/*
 * Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
 * 
 * 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 3 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 <http://www.gnu.org/licenses/>.
 */

using Framework.Constants;
using Framework.Dynamic;
using Framework.GameMath;
using Game.BattleGrounds;
using Game.BattlePets;
using Game.Combat;
using Game.DataStorage;
using Game.Entities;
using Game.Garrisons;
using Game.Groups;
using Game.Guilds;
using Game.Loots;
using Game.Maps;
using Game.Movement;
using Game.Network.Packets;
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;

namespace Game.Spells
{
    public partial class Spell
    {
        [SpellEffectHandler(SpellEffectName.Null)]
        [SpellEffectHandler(SpellEffectName.Portal)]
        [SpellEffectHandler(SpellEffectName.BindSight)]
        [SpellEffectHandler(SpellEffectName.CallPet)]
        [SpellEffectHandler(SpellEffectName.Effect171)]
        [SpellEffectHandler(SpellEffectName.Effect177)]
        [SpellEffectHandler(SpellEffectName.PortalTeleport)]
        [SpellEffectHandler(SpellEffectName.RitualBase)]
        [SpellEffectHandler(SpellEffectName.RitualActivatePortal)]
        [SpellEffectHandler(SpellEffectName.Dodge)]
        [SpellEffectHandler(SpellEffectName.Evade)]
        [SpellEffectHandler(SpellEffectName.Weapon)]
        [SpellEffectHandler(SpellEffectName.Defense)]
        [SpellEffectHandler(SpellEffectName.SpellDefense)]
        [SpellEffectHandler(SpellEffectName.Language)]
        [SpellEffectHandler(SpellEffectName.Spawn)]
        [SpellEffectHandler(SpellEffectName.Stealth)]
        [SpellEffectHandler(SpellEffectName.Detect)]
        [SpellEffectHandler(SpellEffectName.ForceCriticalHit)]
        [SpellEffectHandler(SpellEffectName.Attack)]
        [SpellEffectHandler(SpellEffectName.ThreatAll)]
        [SpellEffectHandler(SpellEffectName.Effect112)]
        [SpellEffectHandler(SpellEffectName.TeleportGraveyard)]
        [SpellEffectHandler(SpellEffectName.Effect122)]
        [SpellEffectHandler(SpellEffectName.Effect175)]
        [SpellEffectHandler(SpellEffectName.Effect178)]
        void EffectUnused(uint effIndex) { }

        void EffectResurrectNew(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || unitTarget.IsAlive())
                return;

            if (!unitTarget.IsTypeId(TypeId.Player))
                return;

            if (!unitTarget.IsInWorld)
                return;

            Player target = unitTarget.ToPlayer();

            if (target.IsResurrectRequested())       // already have one active request
                return;

            int health = damage;
            int mana = effectInfo.MiscValue;
            ExecuteLogEffectResurrect(effIndex, target);
            target.SetResurrectRequestData(m_caster, (uint)health, (uint)mana, 0);
            SendResurrectRequest(target);
        }

        [SpellEffectHandler(SpellEffectName.Instakill)]
        void EffectInstaKill(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsAlive())
                return;

            if (unitTarget.IsTypeId(TypeId.Player))
                if (unitTarget.ToPlayer().GetCommandStatus(PlayerCommandStates.God))
                    return;

            if (m_caster == unitTarget)                              // prevent interrupt message
                finish();

            SpellInstakillLog data = new SpellInstakillLog();
            data.Target = unitTarget.GetGUID();
            data.Caster = m_caster.GetGUID();
            data.SpellID = m_spellInfo.Id;
            m_caster.SendMessageToSet(data, true);

            m_caster.DealDamage(unitTarget, (uint)unitTarget.GetHealth(), null, DamageEffectType.NoDamage, SpellSchoolMask.Normal, null, false);
        }

        void EffectEnvironmentalDMG(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsAlive())
                return;

            uint absorb = 0;
            uint resist = 0;

            m_caster.CalcAbsorbResist(unitTarget, m_spellInfo.GetSchoolMask(), DamageEffectType.SpellDirect, (uint)damage, ref absorb, ref resist, m_spellInfo);

            SpellNonMeleeDamage log = new SpellNonMeleeDamage(m_caster, unitTarget, m_spellInfo.Id, m_SpellVisual, m_spellInfo.GetSchoolMask(), m_castId);
            log.damage = (uint)(damage - absorb - resist);
            log.absorb = absorb;
            log.resist = resist;
            if (unitTarget.IsTypeId(TypeId.Player))
                unitTarget.ToPlayer().EnvironmentalDamage(EnviromentalDamage.Fire, (uint)damage);

            m_caster.SendSpellNonMeleeDamageLog(log);
        }

        [SpellEffectHandler(SpellEffectName.SchoolDamage)]
        void EffectSchoolDmg(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.LaunchTarget)
                return;

            if (unitTarget != null && unitTarget.IsAlive())
            {
                bool apply_direct_bonus = true;

                // Meteor like spells (divided damage to targets)
                if (m_spellInfo.HasAttribute(SpellCustomAttributes.ShareDamage))
                {
                    int count = m_UniqueTargetInfo.Count(targetInfo =>
                    {
                        return Convert.ToBoolean(targetInfo.effectMask & (1 << (int)effIndex));
                    });

                    // divide to all targets
                    if (count != 0)
                        damage /= count;
                }

                switch (m_spellInfo.SpellFamilyName)
                {
                    case SpellFamilyNames.Generic:
                        {
                            switch (m_spellInfo.Id)                     // better way to check unknown
                            {
                                // Consumption
                                case 28865:
                                    damage = m_caster.GetMap().GetDifficultyID() == Difficulty.None ? 2750 : 4250;
                                    break;
                                // percent from health with min
                                case 25599:                             // Thundercrash
                                    {
                                        damage = (int)unitTarget.GetHealth() / 2;
                                        if (damage < 200)
                                            damage = 200;
                                        break;
                                    }
                                // arcane charge. must only affect demons (also undead?)
                                case 45072:
                                    {
                                        if (unitTarget.GetCreatureType() != CreatureType.Demon
                                            && unitTarget.GetCreatureType() != CreatureType.Undead)
                                            return;
                                        break;
                                    }
                                // Gargoyle Strike
                                case 51963:
                                    {
                                        // about +4 base spell dmg per level
                                        damage = (int)(m_caster.getLevel() - 60) * 4 + 60;
                                        break;
                                    }
                            }
                            break;
                        }
                    case SpellFamilyNames.Warrior:
                        {
                            // Victory Rush
                            if (m_spellInfo.Id == 34428)
                                MathFunctions.ApplyPct(ref damage, m_caster.GetTotalAttackPowerValue(WeaponAttackType.BaseAttack));
                            // Shockwave
                            else if (m_spellInfo.Id == 46968)
                            {
                                int pct = m_caster.CalculateSpellDamage(unitTarget, m_spellInfo, 2);
                                if (pct > 0)
                                    damage += (int)MathFunctions.CalculatePct(m_caster.GetTotalAttackPowerValue(WeaponAttackType.BaseAttack), pct);
                                break;
                            }
                            break;
                        }
                    case SpellFamilyNames.Warlock:
                        {
                            break;
                        }
                    case SpellFamilyNames.Priest:
                        {
                            break;
                        }
                    case SpellFamilyNames.Druid:
                        {
                            // Ferocious Bite
                            if (m_caster.IsTypeId(TypeId.Player) && m_spellInfo.SpellFamilyFlags[3].HasAnyFlag(0x1000u))
                            {
                                // converts each extra point of energy ( up to 25 energy ) into additional damage
                                int energy = -(m_caster.ModifyPower(PowerType.Energy, -25));
                                // 25 energy = 100% more damage
                                MathFunctions.AddPct(ref damage, energy * 4);
                            }
                            break;
                        }
                    case SpellFamilyNames.Deathknight:
                        {
                            // Blood Boil - bonus for diseased targets
                            if (m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x00040000u))
                            {
                                if (unitTarget.GetAuraEffect(AuraType.PeriodicDamage, SpellFamilyNames.Deathknight, new FlagArray128(0, 0, 0x00000002), m_caster.GetGUID()) != null)
                                {
                                    damage += m_damage / 2;
                                    damage += (int)(m_caster.GetTotalAttackPowerValue(WeaponAttackType.BaseAttack) * 0.035f);
                                }
                            }
                            break;
                        }
                }

                if (m_originalCaster != null && apply_direct_bonus)
                {
                    uint bonus = m_originalCaster.SpellDamageBonusDone(unitTarget, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect, effectInfo);
                    damage = (int)(bonus + (bonus * variance));
                    damage = (int)unitTarget.SpellDamageBonusTaken(m_originalCaster, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect, effectInfo);
                }

                m_damage += damage;
            }
        }

        [SpellEffectHandler(SpellEffectName.Dummy)]
        void EffectDummy(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget && !gameObjTarget && !itemTarget)
                return;

            // selection by spell family
            switch (m_spellInfo.SpellFamilyName)
            {
                case SpellFamilyNames.Paladin:
                    switch (m_spellInfo.Id)
                    {
                        case 31789:                                 // Righteous Defense (step 1)
                            {
                                // Clear targets for eff 1
                                foreach (var hit in m_UniqueTargetInfo)
                                    hit.effectMask &= ~Convert.ToUInt32(1 << 1);

                                // not empty (checked), copy
                                var attackers = unitTarget.getAttackers();

                                // remove invalid attackers
                                foreach (var att in attackers)
                                    if (!att.IsValidAttackTarget(m_caster))
                                        attackers.Remove(att);

                                // selected from list 3
                                int maxTargets = Math.Min(3, attackers.Count);
                                for (uint i = 0; i < maxTargets; ++i)
                                {
                                    Unit attacker = attackers.PickRandom();
                                    AddUnitTarget(attacker, 1 << 1);
                                    attackers.Remove(attacker);
                                }

                                // now let next effect cast spell at each target.
                                return;
                            }
                    }
                    break;
            }

            // pet auras
            PetAura petSpell = Global.SpellMgr.GetPetAura(m_spellInfo.Id, (byte)effIndex);
            if (petSpell != null)
            {
                m_caster.AddPetAura(petSpell);
                return;
            }

            // normal DB scripted effect
            Log.outDebug(LogFilter.Spells, "Spell ScriptStart spellid {0} in EffectDummy({1})", m_spellInfo.Id, effIndex);
            m_caster.GetMap().ScriptsStart(ScriptsType.Spell, (uint)((int)m_spellInfo.Id | (int)(effIndex << 24)), m_caster, unitTarget);

            // Script based implementation. Must be used only for not good for implementation in core spell effects
            // So called only for not proccessed cases
            if (gameObjTarget)
                Global.ScriptMgr.OnDummyEffect(m_caster, m_spellInfo.Id, effIndex, gameObjTarget);
            else if (unitTarget && unitTarget.IsTypeId(TypeId.Unit))
                Global.ScriptMgr.OnDummyEffect(m_caster, m_spellInfo.Id, effIndex, unitTarget.ToCreature());
            else if (itemTarget)
                Global.ScriptMgr.OnDummyEffect(m_caster, m_spellInfo.Id, effIndex, itemTarget);
        }

        [SpellEffectHandler(SpellEffectName.TriggerSpell)]
        [SpellEffectHandler(SpellEffectName.TriggerSpellWithValue)]
        void EffectTriggerSpell(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.LaunchTarget
                && effectHandleMode != SpellEffectHandleMode.Launch)
                return;

            uint triggered_spell_id = effectInfo.TriggerSpell;

            // @todo move those to spell scripts
            if (effectInfo.Effect == SpellEffectName.TriggerSpell && effectHandleMode == SpellEffectHandleMode.LaunchTarget)
            {
                // special cases
                switch (triggered_spell_id)
                {
                    // Vanish (not exist)
                    case 18461:
                        {
                            unitTarget.RemoveMovementImpairingAuras();
                            unitTarget.RemoveAurasByType(AuraType.ModStalked);
                            return;
                        }
                    // Demonic Empowerment -- succubus
                    case 54437:
                        {
                            unitTarget.RemoveMovementImpairingAuras();
                            unitTarget.RemoveAurasByType(AuraType.ModStalked);
                            unitTarget.RemoveAurasByType(AuraType.ModStun);

                            // Cast Lesser Invisibility
                            unitTarget.CastSpell(unitTarget, 7870, true);
                            return;
                        }
                    // Brittle Armor - (need add max stack of 24575 Brittle Armor)
                    case 29284:
                        {
                            // Brittle Armor
                            SpellInfo spell = Global.SpellMgr.GetSpellInfo(24575);
                            if (spell == null)
                                return;

                            for (uint j = 0; j < spell.StackAmount; ++j)
                                m_caster.CastSpell(unitTarget, spell.Id, true);
                            return;
                        }
                    // Mercurial Shield - (need add max stack of 26464 Mercurial Shield)
                    case 29286:
                        {
                            // Mercurial Shield
                            SpellInfo spell = Global.SpellMgr.GetSpellInfo(26464);
                            if (spell == null)
                                return;

                            for (uint j = 0; j < spell.StackAmount; ++j)
                                m_caster.CastSpell(unitTarget, spell.Id, true);
                            return;
                        }
                    // Cloak of Shadows
                    case 35729:
                        {
                            uint dispelMask = SpellInfo.GetDispelMask(DispelType.ALL);
                            foreach (var iter in unitTarget.GetAppliedAuras())
                            {
                                // remove all harmful spells on you...
                                SpellInfo spell = iter.Value.GetBase().GetSpellInfo();
                                if ((spell.DmgClass == SpellDmgClass.Magic // only affect magic spells
                                    || (Convert.ToBoolean(spell.GetDispelMask() & dispelMask))
                                    // ignore positive and passive auras
                                    && !iter.Value.IsPositive() && !iter.Value.GetBase().IsPassive()))
                                {
                                    m_caster.RemoveAura(iter);
                                }
                            }
                            return;
                        }
                }
            }

            // normal case
            SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(triggered_spell_id);
            if (spellInfo == null)
            {
                Log.outDebug(LogFilter.Spells, "Spell.EffectTriggerSpell spell {0} tried to trigger unknown spell {1}", m_spellInfo.Id, triggered_spell_id);
                return;
            }

            SpellCastTargets targets = new SpellCastTargets();
            if (effectHandleMode == SpellEffectHandleMode.LaunchTarget)
            {
                if (!spellInfo.NeedsToBeTriggeredByCaster(m_spellInfo, m_caster.GetMap().GetDifficultyID()))
                    return;
                targets.SetUnitTarget(unitTarget);
            }
            else //if (effectHandleMode == SpellEffectHandleMode.Launch)
            {
                if (spellInfo.NeedsToBeTriggeredByCaster(m_spellInfo, m_caster.GetMap().GetDifficultyID()) && effectInfo.GetProvidedTargetMask().HasAnyFlag(SpellCastTargetFlags.UnitMask))
                    return;

                if (spellInfo.GetExplicitTargetMask().HasAnyFlag(SpellCastTargetFlags.DestLocation))
                    targets.SetDst(m_targets);

                targets.SetUnitTarget(m_caster);
            }

            Dictionary<SpellValueMod, int> values = new Dictionary<SpellValueMod, int>();
            // set basepoints for trigger with value effect
            if (effectInfo.Effect == SpellEffectName.TriggerSpellWithValue)
            {
                values.Add(SpellValueMod.BasePoint0, damage);
                values.Add(SpellValueMod.BasePoint1, damage);
                values.Add(SpellValueMod.BasePoint2, damage);
            }

            // original caster guid only for GO cast
            m_caster.CastSpell(targets, spellInfo, values, TriggerCastFlags.FullMask, null, null, m_originalCasterGUID);
        }

        [SpellEffectHandler(SpellEffectName.TriggerMissile)]
        [SpellEffectHandler(SpellEffectName.TriggerMissileSpellWithValue)]
        void EffectTriggerMissileSpell(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget
                && effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            uint triggered_spell_id = effectInfo.TriggerSpell;

            // normal case
            SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(triggered_spell_id);
            if (spellInfo == null)
            {
                Log.outDebug(LogFilter.Spells, "Spell.EffectTriggerMissileSpell spell {0} tried to trigger unknown spell {1}", m_spellInfo.Id, triggered_spell_id);
                return;
            }

            SpellCastTargets targets = new SpellCastTargets();
            if (effectHandleMode == SpellEffectHandleMode.HitTarget)
            {
                if (!spellInfo.NeedsToBeTriggeredByCaster(m_spellInfo, m_caster.GetMap().GetDifficultyID()))
                    return;
                targets.SetUnitTarget(unitTarget);
            }
            else //if (effectHandleMode == SpellEffectHandleMode.Hit)
            {
                if (spellInfo.NeedsToBeTriggeredByCaster(m_spellInfo, m_caster.GetMap().GetDifficultyID()) && effectInfo.GetProvidedTargetMask().HasAnyFlag(SpellCastTargetFlags.UnitMask))
                    return;

                if (spellInfo.GetExplicitTargetMask().HasAnyFlag(SpellCastTargetFlags.DestLocation))
                    targets.SetDst(m_targets);

                targets.SetUnitTarget(m_caster);
            }

            Dictionary<SpellValueMod, int> values = new Dictionary<SpellValueMod, int>();
            // set basepoints for trigger with value effect
            if (effectInfo.Effect == SpellEffectName.TriggerMissileSpellWithValue)
            {
                // maybe need to set value only when basepoints == 0?
                values.Add(SpellValueMod.BasePoint0, damage);
                values.Add(SpellValueMod.BasePoint1, damage);
                values.Add(SpellValueMod.BasePoint2, damage);
            }

            // original caster guid only for GO cast
            m_caster.CastSpell(targets, spellInfo, values, TriggerCastFlags.FullMask, null, null, m_originalCasterGUID);
        }

        [SpellEffectHandler(SpellEffectName.ForceCast)]
        [SpellEffectHandler(SpellEffectName.ForceCastWithValue)]
        [SpellEffectHandler(SpellEffectName.ForceCast2)]
        void EffectForceCast(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null)
                return;

            uint triggered_spell_id = effectInfo.TriggerSpell;

            // normal case
            SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(triggered_spell_id);

            if (spellInfo == null)
            {
                Log.outError(LogFilter.Spells, "Spell.EffectForceCast of spell {0}: triggering unknown spell id {1}", m_spellInfo.Id, triggered_spell_id);
                return;
            }

            if (effectInfo.Effect == SpellEffectName.ForceCast && damage != 0)
            {
                switch (m_spellInfo.Id)
                {
                    case 52588: // Skeletal Gryphon Escape
                    case 48598: // Ride Flamebringer Cue
                        unitTarget.RemoveAura((uint)damage);
                        break;
                    case 52463: // Hide In Mine Car
                    case 52349: // Overtake
                        unitTarget.CastCustomSpell(unitTarget, spellInfo.Id, damage, 0, 0, true, null, null, m_originalCasterGUID);
                        return;
                }
            }

            Dictionary<SpellValueMod, int> values = new Dictionary<SpellValueMod, int>();
            // set basepoints for trigger with value effect
            if (effectInfo.Effect == SpellEffectName.ForceCastWithValue)
            {
                // maybe need to set value only when basepoints == 0?
                values.Add(SpellValueMod.BasePoint0, damage);
                values.Add(SpellValueMod.BasePoint1, damage);
                values.Add(SpellValueMod.BasePoint2, damage);
            }

            SpellCastTargets targets = new SpellCastTargets();
            targets.SetUnitTarget(m_caster);

            unitTarget.CastSpell(targets, spellInfo, values, TriggerCastFlags.FullMask);
        }

        [SpellEffectHandler(SpellEffectName.TriggerSpell2)]
        void EffectTriggerRitualOfSummoning(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            uint triggered_spell_id = effectInfo.TriggerSpell;
            SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(triggered_spell_id);
            if (spellInfo == null)
            {
                Log.outError(LogFilter.Spells, "EffectTriggerRitualOfSummoning of spell {0}: triggering unknown spell id {1}", m_spellInfo.Id, triggered_spell_id);
                return;
            }

            finish();

            m_caster.CastSpell(null, spellInfo, false);
        }

        [SpellEffectHandler(SpellEffectName.Jump)]
        void EffectJump(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.LaunchTarget)
                return;

            if (m_caster.IsInFlight())
                return;

            if (unitTarget == null)
                return;

            float x, y, z;
            unitTarget.GetContactPoint(m_caster, out x, out y, out z, SharedConst.ContactDistance);

            float speedXY, speedZ;
            CalculateJumpSpeeds(effectInfo, m_caster.GetExactDist2d(x, y), out speedXY, out speedZ);
            JumpArrivalCastArgs arrivalCast = new JumpArrivalCastArgs();
            arrivalCast.SpellId = effectInfo.TriggerSpell;
            arrivalCast.Target = unitTarget.GetGUID();
            m_caster.GetMotionMaster().MoveJump(x, y, z, 0.0f, speedXY, speedZ, EventId.Jump, false, arrivalCast);
        }

        [SpellEffectHandler(SpellEffectName.JumpDest)]
        void EffectJumpDest(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Launch)
                return;

            if (m_caster.IsInFlight())
                return;

            if (!m_targets.HasDst())
                return;

            float speedXY, speedZ;
            CalculateJumpSpeeds(effectInfo, m_caster.GetExactDist2d(destTarget), out speedXY, out speedZ);
            JumpArrivalCastArgs arrivalCast = new JumpArrivalCastArgs();
            arrivalCast.SpellId = effectInfo.TriggerSpell;
            m_caster.GetMotionMaster().MoveJump(destTarget, speedXY, speedZ, EventId.Jump, !m_targets.GetObjectTargetGUID().IsEmpty(), arrivalCast);
        }

        void CalculateJumpSpeeds(SpellEffectInfo effInfo, float dist, out float speedXY, out float speedZ)
        {
            if (effInfo.MiscValue != 0)
                speedZ = (float)effInfo.MiscValue / 10;
            else if (effInfo.MiscValueB != 0)
                speedZ = (float)effInfo.MiscValueB / 10;
            else
                speedZ = 10.0f;

            speedXY = dist * 10.0f / speedZ;
        }

        [SpellEffectHandler(SpellEffectName.TeleportUnits)]
        void EffectTeleportUnits(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || unitTarget.IsInFlight())
                return;

            // If not exist data for dest location - return
            if (!m_targets.HasDst())
            {
                Log.outError(LogFilter.Spells, "Spell.EffectTeleportUnits - does not have a destination for spellId {0}.", m_spellInfo.Id);
                return;
            }

            // Init dest coordinates
            uint mapid = destTarget.GetMapId();
            if (mapid == 0xFFFFFFFF)
                mapid = unitTarget.GetMapId();
            float x, y, z, orientation;
            destTarget.GetPosition(out x, out y, out z, out orientation);
            if (orientation == 0 && m_targets.GetUnitTarget() != null)
                orientation = m_targets.GetUnitTarget().GetOrientation();
            Log.outDebug(LogFilter.Spells, "Spell.EffectTeleportUnits - teleport unit to {0} {1} {2} {3} {4}\n", mapid, x, y, z, orientation);

            Player player = unitTarget.ToPlayer();
            if (player)
            {
                // Custom loading screen
                uint customLoadingScreenId = (uint)effectInfo.MiscValue;
                if (customLoadingScreenId != 0)
                    player.SendPacket(new CustomLoadScreen(m_spellInfo.Id, customLoadingScreenId));

                player.TeleportTo(mapid, x, y, z, orientation, unitTarget == m_caster ? TeleportToOptions.Spell | TeleportToOptions.NotLeaveCombat : 0);
            }
            else if (mapid == unitTarget.GetMapId())
                unitTarget.NearTeleportTo(x, y, z, orientation, unitTarget == m_caster);
            else
            {
                Log.outError(LogFilter.Spells, "Spell.EffectTeleportUnits - spellId {0} attempted to teleport creature to a different map.", m_spellInfo.Id);
                return;
            }

            // post effects for TARGET_DEST_DB
            switch (m_spellInfo.Id)
            {
                // Dimensional Ripper - Everlook
                case 23442:
                    {
                        int r = RandomHelper.IRand(0, 119);
                        if (r >= 70)                                  // 7/12 success
                        {
                            if (r < 100)                              // 4/12 evil twin
                                m_caster.CastSpell(m_caster, 23445, true);
                            else                                        // 1/12 fire
                                m_caster.CastSpell(m_caster, 23449, true);
                        }
                        return;
                    }
                // Ultrasafe Transporter: Toshley's Station
                case 36941:
                    {
                        if (RandomHelper.randChance(50))                        // 50% success
                        {
                            int rand_eff = RandomHelper.IRand(1, 7);
                            switch (rand_eff)
                            {
                                case 1:
                                    // soul split - evil
                                    m_caster.CastSpell(m_caster, 36900, true);
                                    break;
                                case 2:
                                    // soul split - good
                                    m_caster.CastSpell(m_caster, 36901, true);
                                    break;
                                case 3:
                                    // Increase the size
                                    m_caster.CastSpell(m_caster, 36895, true);
                                    break;
                                case 4:
                                    // Decrease the size
                                    m_caster.CastSpell(m_caster, 36893, true);
                                    break;
                                case 5:
                                    // Transform
                                    {
                                        if (m_caster.ToPlayer().GetTeam() == Team.Alliance)
                                            m_caster.CastSpell(m_caster, 36897, true);
                                        else
                                            m_caster.CastSpell(m_caster, 36899, true);
                                        break;
                                    }
                                case 6:
                                    // chicken
                                    m_caster.CastSpell(m_caster, 36940, true);
                                    break;
                                case 7:
                                    // evil twin
                                    m_caster.CastSpell(m_caster, 23445, true);
                                    break;
                            }
                        }
                        return;
                    }
                // Dimensional Ripper - Area 52
                case 36890:
                    {
                        if (RandomHelper.randChance(50))                        // 50% success
                        {
                            int rand_eff = RandomHelper.IRand(1, 4);
                            switch (rand_eff)
                            {
                                case 1:
                                    // soul split - evil
                                    m_caster.CastSpell(m_caster, 36900, true);
                                    break;
                                case 2:
                                    // soul split - good
                                    m_caster.CastSpell(m_caster, 36901, true);
                                    break;
                                case 3:
                                    // Increase the size
                                    m_caster.CastSpell(m_caster, 36895, true);
                                    break;
                                case 4:
                                    // Transform
                                    {
                                        if (m_caster.ToPlayer().GetTeam() == Team.Alliance)
                                            m_caster.CastSpell(m_caster, 36897, true);
                                        else
                                            m_caster.CastSpell(m_caster, 36899, true);
                                        break;
                                    }
                            }
                        }
                        return;
                    }
            }
        }

        [SpellEffectHandler(SpellEffectName.ApplyAura)]
        void EffectApplyAura(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (m_spellAura == null || unitTarget == null)
                return;

            Contract.Assert(unitTarget == m_spellAura.GetOwner());
            m_spellAura._ApplyEffectForTargets(effIndex);
        }

        [SpellEffectHandler(SpellEffectName.ApplyAreaAuraEnemy)]
        [SpellEffectHandler(SpellEffectName.ApplyAreaAuraFriend)]
        [SpellEffectHandler(SpellEffectName.ApplyAreaAuraOwner)]
        [SpellEffectHandler(SpellEffectName.ApplyAreaAuraParty)]
        [SpellEffectHandler(SpellEffectName.ApplyAreaAuraPet)]
        [SpellEffectHandler(SpellEffectName.ApplyAreaAuraRaid)]
        void EffectApplyAreaAura(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (m_spellAura == null || unitTarget == null)
                return;
            Contract.Assert(unitTarget == m_spellAura.GetOwner());
            m_spellAura._ApplyEffectForTargets(effIndex);
        }

        [SpellEffectHandler(SpellEffectName.UnlearnSpecialization)]
        void EffectUnlearnSpecialization(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            Player player = unitTarget.ToPlayer();
            uint spellToUnlearn = effectInfo.TriggerSpell;

            player.RemoveSpell(spellToUnlearn);

            Log.outDebug(LogFilter.Spells, "Spell: Player {0} has unlearned spell {1} from NpcGUID: {2}", player.GetGUID().ToString(), spellToUnlearn, m_caster.GetGUID().ToString());
        }

        [SpellEffectHandler(SpellEffectName.PowerDrain)]
        void EffectPowerDrain(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (effectInfo.MiscValue < 0 || effectInfo.MiscValue >= (byte)PowerType.Max)
                return;

            PowerType powerType = (PowerType)effectInfo.MiscValue;

            if (unitTarget == null || !unitTarget.IsAlive() || unitTarget.getPowerType() != powerType || damage < 0)
                return;

            // add spell damage bonus
            uint bonus = m_caster.SpellDamageBonusDone(unitTarget, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect, effectInfo);
            damage = (int)(bonus + (bonus * variance));
            damage = (int)unitTarget.SpellDamageBonusTaken(m_caster, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect, effectInfo);

            int newDamage = -(unitTarget.ModifyPower(powerType, -damage));

            float gainMultiplier = 0.0f;

            // Don't restore from self drain
            if (m_caster != unitTarget)
            {
                gainMultiplier = effectInfo.CalcValueMultiplier(m_originalCaster, this);

                int gain = (int)(newDamage * gainMultiplier);

                m_caster.EnergizeBySpell(m_caster, m_spellInfo.Id, gain, powerType);
            }
            ExecuteLogEffectTakeTargetPower(effIndex, unitTarget, powerType, (uint)newDamage, gainMultiplier);
        }

        [SpellEffectHandler(SpellEffectName.SendEvent)]
        void EffectSendEvent(uint effIndex)
        {
            // we do not handle a flag dropping or clicking on flag in Battlegroundby sendevent system
            if (effectHandleMode != SpellEffectHandleMode.HitTarget
                && effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            WorldObject target = null;

            // call events for object target if present
            if (effectHandleMode == SpellEffectHandleMode.HitTarget)
            {
                if (unitTarget != null)
                    target = unitTarget;
                else if (gameObjTarget != null)
                    target = gameObjTarget;
            }
            else // if (effectHandleMode == SpellEffectHandleMode.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().HasAnyFlag(SpellCastTargetFlags.UnitMask | SpellCastTargetFlags.GameobjectMask))
                    return;
                // some spells have no target entries in dbc and they use focus target
                if (focusObject != null)
                    target = focusObject;
                // @todo there should be a possibility to pass dest target to event script
            }

            Log.outDebug(LogFilter.Spells, "Spell ScriptStart {0} for spellid {1} in EffectSendEvent ", effectInfo.MiscValue, m_spellInfo.Id);

            ZoneScript zoneScript = m_caster.GetZoneScript();
            InstanceScript instanceScript = m_caster.GetInstanceScript();
            if (zoneScript != null)
                zoneScript.ProcessEvent(target, (uint)effectInfo.MiscValue);
            else if (instanceScript != null)    // needed in case Player is the caster
                instanceScript.ProcessEvent(target, (uint)effectInfo.MiscValue);

            m_caster.GetMap().ScriptsStart(ScriptsType.Event, (uint)effectInfo.MiscValue, m_caster, target);
        }

        [SpellEffectHandler(SpellEffectName.PowerBurn)]
        void EffectPowerBurn(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (effectInfo.MiscValue < 0 || effectInfo.MiscValue >= (int)PowerType.Max)
                return;

            PowerType powerType = (PowerType)effectInfo.MiscValue;

            if (unitTarget == null || !unitTarget.IsAlive() || unitTarget.getPowerType() != powerType || damage < 0)
                return;

            // burn x% of target's mana, up to maximum of 2x% of caster's mana (Mana Burn)
            if (m_spellInfo.Id == 8129)
            {
                int maxDamage = MathFunctions.CalculatePct(m_caster.GetMaxPower(powerType), damage * 2);
                damage = MathFunctions.CalculatePct(unitTarget.GetMaxPower(powerType), damage);
                damage = Math.Min(damage, maxDamage);
            }

            int newDamage = -(unitTarget.ModifyPower(powerType, -damage));

            // NO - Not a typo - EffectPowerBurn uses effect value multiplier - not effect damage multiplier
            float dmgMultiplier = effectInfo.CalcValueMultiplier(m_originalCaster, this);

            // add log data before multiplication (need power amount, not damage)
            ExecuteLogEffectTakeTargetPower(effIndex, unitTarget, powerType, (uint)newDamage, 0.0f);

            newDamage = (int)(newDamage * dmgMultiplier);

            m_damage += newDamage;
        }

        [SpellEffectHandler(SpellEffectName.Heal)]
        void EffectHeal(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.LaunchTarget)
                return;

            if (unitTarget != null && unitTarget.IsAlive() && damage >= 0)
            {
                // Try to get original caster
                Unit caster = !m_originalCasterGUID.IsEmpty() ? m_originalCaster : m_caster;

                // Skip if m_originalCaster not available
                if (caster == null)
                    return;

                int addhealth = damage;

                // Vessel of the Naaru (Vial of the Sunwell trinket)
                if (m_spellInfo.Id == 45064)
                {
                    // Amount of heal - depends from stacked Holy Energy
                    int damageAmount = 0;
                    AuraEffect aurEff = m_caster.GetAuraEffect(45062, 0);
                    if (aurEff != null)
                    {
                        damageAmount += aurEff.GetAmount();
                        m_caster.RemoveAurasDueToSpell(45062);
                    }

                    addhealth += damageAmount;
                }
                // Runic Healing Injector (heal increased by 25% for engineers - 3.2.0 patch change)
                else if (m_spellInfo.Id == 67489)
                {
                    Player player = m_caster.ToPlayer();
                    if (player != null)
                        if (player.HasSkill(SkillType.Engineering))
                            MathFunctions.AddPct(ref addhealth, 25);
                }
                // Swiftmend - consumes Regrowth or Rejuvenation
                else if (m_spellInfo.TargetAuraState == AuraStateType.Swiftmend && unitTarget.HasAuraState(AuraStateType.Swiftmend, m_spellInfo, m_caster))
                {
                    var RejorRegr = unitTarget.GetAuraEffectsByType(AuraType.PeriodicHeal);
                    // find most short by duration
                    AuraEffect targetAura = null;
                    foreach (var eff in RejorRegr)
                    {
                        if (eff.GetSpellInfo().SpellFamilyName == SpellFamilyNames.Druid
                            && eff.GetSpellInfo().SpellFamilyFlags[0].HasAnyFlag(0x50u))
                        {
                            if (targetAura == null || eff.GetBase().GetDuration() < targetAura.GetBase().GetDuration())
                                targetAura = eff;
                        }
                    }

                    if (targetAura == null)
                    {
                        Log.outError(LogFilter.Spells, "Target(GUID: {0}) has aurastate AURA_STATE_SWIFTMEND but no matching aura.", unitTarget.GetGUID());
                        return;
                    }

                    int tickheal = targetAura.GetAmount();
                    Unit auraCaster = targetAura.GetCaster();
                    if (auraCaster != null)
                        tickheal = (int)auraCaster.SpellHealingBonusDone(unitTarget, targetAura.GetSpellInfo(), (uint)tickheal, DamageEffectType.DOT, effectInfo);
                    //It is said that talent bonus should not be included

                    int tickcount = 0;
                    // Rejuvenation
                    if (targetAura.GetSpellInfo().SpellFamilyFlags[0].HasAnyFlag(0x10u))
                        tickcount = 4;
                    // Regrowth
                    else
                        tickcount = 6;

                    addhealth += tickheal * tickcount;

                    // Glyph of Swiftmend
                    if (!caster.HasAura(54824))
                        unitTarget.RemoveAura(targetAura.GetId(), targetAura.GetCasterGUID());
                }
                // Death Pact - return pct of max health to caster
                else if (m_spellInfo.SpellFamilyName == SpellFamilyNames.Deathknight && m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x00080000u))
                    addhealth = (int)caster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)caster.CountPctFromMaxHealth(damage), DamageEffectType.Heal, effectInfo);
                else
                {
                    addhealth = (int)caster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)addhealth, DamageEffectType.Heal, effectInfo);
                    uint bonus = caster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)addhealth, DamageEffectType.Heal, effectInfo);
                    damage = (int)(bonus + (bonus * variance));
                }

                addhealth = (int)unitTarget.SpellHealingBonusTaken(caster, m_spellInfo, (uint)addhealth, DamageEffectType.Heal, effectInfo);

                // Remove Grievious bite if fully healed
                if (unitTarget.HasAura(48920) && ((uint)(unitTarget.GetHealth() + (ulong)addhealth) >= unitTarget.GetMaxHealth()))
                    unitTarget.RemoveAura(48920);

                m_damage -= addhealth;
            }
        }

        [SpellEffectHandler(SpellEffectName.HealPct)]
        void EffectHealPct(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsAlive() || damage < 0)
                return;

            // Skip if m_originalCaster not available
            if (m_originalCaster == null)
                return;

            uint heal = m_originalCaster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)unitTarget.CountPctFromMaxHealth(damage), DamageEffectType.Heal, effectInfo);
            heal = unitTarget.SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, DamageEffectType.Heal, effectInfo);

            m_healing += (int)heal;
        }

        [SpellEffectHandler(SpellEffectName.HealMechanical)]
        void EffectHealMechanical(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsAlive() || damage < 0)
                return;

            // Skip if m_originalCaster not available
            if (m_originalCaster == null)
                return;

            uint heal = m_originalCaster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)damage, DamageEffectType.Heal, effectInfo);
            heal += (uint)(heal * variance);

            m_healing += (int)unitTarget.SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, DamageEffectType.Heal, effectInfo);
        }

        [SpellEffectHandler(SpellEffectName.HealthLeech)]
        void EffectHealthLeech(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsAlive() || damage < 0)
                return;

            uint bonus = m_caster.SpellDamageBonusDone(unitTarget, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect, effectInfo);
            damage = (int)(bonus + (bonus * variance));
            damage = (int)unitTarget.SpellDamageBonusTaken(m_caster, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect, effectInfo);

            Log.outDebug(LogFilter.Spells, "HealthLeech :{0}", damage);

            float healMultiplier = effectInfo.CalcValueMultiplier(m_originalCaster, this);

            m_damage += damage;
            // get max possible damage, don't count overkill for heal
            uint healthGain = (uint)(-unitTarget.GetHealthGain(-damage) * healMultiplier);

            if (m_caster.IsAlive())
            {
                healthGain = m_caster.SpellHealingBonusDone(m_caster, m_spellInfo, healthGain, DamageEffectType.Heal, effectInfo);
                healthGain = m_caster.SpellHealingBonusTaken(m_caster, m_spellInfo, healthGain, DamageEffectType.Heal, effectInfo);

                m_caster.HealBySpell(m_caster, m_spellInfo, healthGain);
            }
        }

        public void DoCreateItem(uint i, uint itemtype, byte context = 0, List<uint> bonusListIds = null)
        {
            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            Player player = unitTarget.ToPlayer();

            uint newitemid = itemtype;
            ItemTemplate pProto = Global.ObjectMgr.GetItemTemplate(newitemid);
            if (pProto == null)
            {
                player.SendEquipError(InventoryResult.ItemNotFound);
                return;
            }

            // bg reward have some special in code work
            BattlegroundTypeId bgType = 0;
            switch ((BattlegroundMarks)m_spellInfo.Id)
            {
                case BattlegroundMarks.SpellAvMarkWinner:
                case BattlegroundMarks.SpellAvMarkLoser:
                    bgType = BattlegroundTypeId.AV;
                    break;
                case BattlegroundMarks.SpellWsMarkWinner:
                case BattlegroundMarks.SpellWsMarkLoser:
                    bgType = BattlegroundTypeId.WS;
                    break;
                case BattlegroundMarks.SpellAbMarkWinner:
                case BattlegroundMarks.SpellAbMarkLoser:
                    bgType = BattlegroundTypeId.AB;
                    break;
                default:
                    break;
            }

            uint num_to_add = (uint)damage;

            if (num_to_add < 1)
                num_to_add = 1;
            if (num_to_add > pProto.GetMaxStackSize())
                num_to_add = pProto.GetMaxStackSize();

            // 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
            uint perfectItemType = itemtype;
            // get perfection capability and chance
            if (SkillPerfectItems.CanCreatePerfectItem(player, m_spellInfo.Id, ref perfectCreateChance, ref perfectItemType))
                if (RandomHelper.randChance(perfectCreateChance)) // if the roll succeeds...
                    newitemid = perfectItemType;        // the perfect item replaces the regular one

            // 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
            byte additionalMaxNum = 0;
            // get the chance and maximum number for creating extra items
            if (SkillExtraItems.CanCreateExtraItems(player, m_spellInfo.Id, ref additionalCreateChance, ref additionalMaxNum))
            {
                // roll with this chance till we roll not to create or we create the max num
                while (RandomHelper.randChance(additionalCreateChance) && items_count <= additionalMaxNum)
                    ++items_count;
            }

            // really will be created more items
            num_to_add *= (uint)items_count;

            // can the player store the new item?
            List<ItemPosCount> dest = new List<ItemPosCount>();
            uint no_space = 0;
            InventoryResult msg = player.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, newitemid, num_to_add, out no_space);
            if (msg != InventoryResult.Ok)
            {
                // convert to possible store amount
                if (msg == InventoryResult.InvFull || msg == InventoryResult.ItemMaxCount)
                    num_to_add -= no_space;
                else
                {
                    // if not created by another reason from full inventory or unique items amount limitation
                    player.SendEquipError(msg, null, null, newitemid);
                    return;
                }
            }

            if (num_to_add != 0)
            {
                // create the new item and store it
                Item pItem = player.StoreNewItem(dest, newitemid, true, ItemEnchantment.GenerateItemRandomPropertyId(newitemid), null, context, bonusListIds);

                // was it successful? return error if not
                if (pItem == null)
                {
                    player.SendEquipError(InventoryResult.ItemNotFound);
                    return;
                }

                // set the "Crafted by ..." property of the item
                if (pItem.GetTemplate().GetClass() != ItemClass.Consumable && pItem.GetTemplate().GetClass() != ItemClass.Quest && newitemid != 6265 && newitemid != 6948)
                    pItem.SetGuidValue(ItemFields.Creator, player.GetGUID());

                // send info to the client
                player.SendNewItem(pItem, num_to_add, true, bgType == 0);

                if (pItem.GetQuality() > ItemQuality.Epic || (pItem.GetQuality() == ItemQuality.Epic && pItem.GetItemLevel(player) >= GuildConst.MinNewsItemLevel))
                {
                    Guild guild = player.GetGuild();
                    if (guild != null)
                        guild.AddGuildNews(GuildNews.ItemCrafted, player.GetGUID(), 0, pProto.GetId());
                }

                // we succeeded in creating at least one item, so a levelup is possible
                if (bgType == 0)
                    player.UpdateCraftSkill(m_spellInfo.Id);
            }
        }

        [SpellEffectHandler(SpellEffectName.CreateItem)]
        void EffectCreateItem(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            DoCreateItem(effIndex, effectInfo.ItemType);
            ExecuteLogEffectCreateItem(effIndex, effectInfo.ItemType);
        }

        [SpellEffectHandler(SpellEffectName.CreateLoot)]
        void EffectCreateItem2(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            Player player = unitTarget.ToPlayer();

            uint item_id = effectInfo.ItemType;

            if (item_id != 0)
                DoCreateItem(effIndex, item_id);

            // special case: fake item replaced by generate using spell_loot_template
            if (m_spellInfo.IsLootCrafting())
            {
                if (item_id != 0)
                {
                    if (!player.HasItemCount(item_id))
                        return;

                    // remove reagent
                    uint count = 1;
                    player.DestroyItemCount(item_id, count, true);

                    // create some random items
                    player.AutoStoreLoot(m_spellInfo.Id, LootStorage.Spell);
                }
                else
                    player.AutoStoreLoot(m_spellInfo.Id, LootStorage.Spell);    // create some random items

                player.UpdateCraftSkill(m_spellInfo.Id);
            }
            // @todo ExecuteLogEffectCreateItem(i, GetEffect(i].ItemType);
        }

        [SpellEffectHandler(SpellEffectName.CreateRandomItem)]
        void EffectCreateRandomItem(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;
            Player player = unitTarget.ToPlayer();

            // create some random items
            player.AutoStoreLoot(m_spellInfo.Id, LootStorage.Spell);
            // @todo ExecuteLogEffectCreateItem(i, GetEffect(i].ItemType);
        }

        [SpellEffectHandler(SpellEffectName.PersistentAreaAura)]
        void EffectPersistentAA(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            if (m_spellAura == null)
            {
                Unit caster = m_caster.GetEntry() == SharedConst.WorldTrigger ? m_originalCaster : m_caster;
                float radius = effectInfo.CalcRadius(caster);

                // Caster not in world, might be spell triggered from aura removal
                if (!caster.IsInWorld)
                    return;
                DynamicObject dynObj = new DynamicObject(false);
                if (!dynObj.CreateDynamicObject(caster.GetMap().GenerateLowGuid(HighGuid.DynamicObject), caster, m_spellInfo, destTarget, radius, DynamicObjectType.AreaSpell, m_SpellVisual))
                    return;

                Aura aura = Aura.TryCreate(m_spellInfo, m_castId, SpellConst.MaxEffectMask, dynObj, caster, m_spellValue.EffectBasePoints, null, ObjectGuid.Empty, m_castItemLevel);
                if (aura != null)
                {
                    m_spellAura = aura;
                    m_spellAura._RegisterForTargets();
                }
                else
                    return;
            }

            Contract.Assert(m_spellAura.GetDynobjOwner());
            m_spellAura._ApplyEffectForTargets(effIndex);
        }

        [SpellEffectHandler(SpellEffectName.Energize)]
        void EffectEnergize(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null)
                return;

            if (!unitTarget.IsAlive())
                return;

            if (effectInfo.MiscValue < 0 || effectInfo.MiscValue >= (byte)PowerType.Max)
                return;

            PowerType power = (PowerType)effectInfo.MiscValue;
            if (unitTarget.GetMaxPower(power) == 0)
                return;

            // Some level depends spells
            int level_multiplier = 0;
            int level_diff = 0;
            switch (m_spellInfo.Id)
            {
                case 9512:                                          // Restore Energy
                    level_diff = (int)m_caster.getLevel() - 40;
                    level_multiplier = 2;
                    break;
                case 24571:                                         // Blood Fury
                    level_diff = (int)m_caster.getLevel() - 60;
                    level_multiplier = 10;
                    break;
                case 24532:                                         // Burst of Energy
                    level_diff = (int)m_caster.getLevel() - 60;
                    level_multiplier = 4;
                    break;
                case 31930:                                         // Judgements of the Wise
                case 63375:                                         // Primal Wisdom
                case 68082:                                         // Glyph of Seal of Command
                    damage = (int)MathFunctions.CalculatePct(unitTarget.GetCreateMana(), damage);
                    break;
                case 67490:                                         // Runic Mana Injector (mana gain increased by 25% for engineers - 3.2.0 patch change)
                    {
                        Player player = m_caster.ToPlayer();
                        if (player != null)
                            if (player.HasSkill(SkillType.Engineering))
                                MathFunctions.AddPct(ref damage, 25);
                        break;
                    }
                default:
                    break;
            }

            if (level_diff > 0)
                damage -= level_multiplier * level_diff;

            if (damage < 0 && power != PowerType.LunarPower)
                return;

            m_caster.EnergizeBySpell(unitTarget, m_spellInfo.Id, damage, power);

            // Mad Alchemist's Potion
            if (m_spellInfo.Id == 45051)
            {
                // find elixirs on target
                bool guardianFound = false;
                bool battleFound = false;
                foreach (var app in unitTarget.GetAppliedAuras())
                {
                    uint spell_id = app.Value.GetBase().GetId();
                    if (!guardianFound)
                        if (Global.SpellMgr.IsSpellMemberOfSpellGroup(spell_id, SpellGroup.ElixirGuardian))
                            guardianFound = true;
                    if (!battleFound)
                        if (Global.SpellMgr.IsSpellMemberOfSpellGroup(spell_id, SpellGroup.ElixirBattle))
                            battleFound = true;
                    if (battleFound && guardianFound)
                        break;
                }

                // get all available elixirs by mask and spell level
                List<int> avalibleElixirs = new List<int>();
                if (!guardianFound)
                    Global.SpellMgr.GetSetOfSpellsInSpellGroup(SpellGroup.ElixirGuardian, out avalibleElixirs);
                if (!battleFound)
                    Global.SpellMgr.GetSetOfSpellsInSpellGroup(SpellGroup.ElixirBattle, out avalibleElixirs);
                foreach (int spellId in avalibleElixirs)
                {
                    SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo((uint)spellId);
                    if (spellInfo.SpellLevel < m_spellInfo.SpellLevel || spellInfo.SpellLevel > unitTarget.getLevel())
                        avalibleElixirs.Remove(spellId);
                    else if (Global.SpellMgr.IsSpellMemberOfSpellGroup((uint)spellId, SpellGroup.ElixirShattrath))
                        avalibleElixirs.Remove(spellId);
                    else if (Global.SpellMgr.IsSpellMemberOfSpellGroup((uint)spellId, SpellGroup.ElixirUnstable))
                        avalibleElixirs.Remove(spellId);
                }

                if (!avalibleElixirs.Empty())
                {
                    // cast random elixir on target
                    m_caster.CastSpell(unitTarget, (uint)avalibleElixirs.PickRandom(), true, m_CastItem);
                }
            }
        }

        [SpellEffectHandler(SpellEffectName.EnergizePct)]
        void EffectEnergizePct(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null)
                return;
            if (!unitTarget.IsAlive())
                return;

            if (effectInfo.MiscValue < 0 || effectInfo.MiscValue >= (byte)PowerType.Max)
                return;

            PowerType power = (PowerType)effectInfo.MiscValue;
            uint maxPower = (uint)unitTarget.GetMaxPower(power);
            if (maxPower == 0)
                return;

            int gain = (int)MathFunctions.CalculatePct(maxPower, damage);
            m_caster.EnergizeBySpell(unitTarget, m_spellInfo.Id, gain, power);
        }

        void SendLoot(ObjectGuid guid, LootType loottype)
        {
            Player player = m_caster.ToPlayer();
            if (player == null)
                return;

            if (gameObjTarget != null)
            {
                // Players shouldn't be able to loot gameobjects that are currently despawned
                if (!gameObjTarget.isSpawned() && !player.IsGameMaster())
                {
                    Log.outError(LogFilter.Spells, "Possible hacking attempt: Player {0} [{1}] tried to loot a gameobject [{2}] which is on respawn time without being in GM mode!",
                                    player.GetName(), player.GetGUID().ToString(), gameObjTarget.GetGUID().ToString());
                    return;
                }
                // special case, already has GossipHello inside so return and avoid calling twice
                if (gameObjTarget.GetGoType() == GameObjectTypes.Goober)
                {
                    gameObjTarget.Use(m_caster);
                    return;
                }

                if (Global.ScriptMgr.OnGossipHello(player, gameObjTarget))
                    return;

                if (gameObjTarget.GetAI().GossipHello(player, true))
                    return;

                switch (gameObjTarget.GetGoType())
                {
                    case GameObjectTypes.Door:
                    case GameObjectTypes.Button:
                        gameObjTarget.UseDoorOrButton(0, false, player);
                        return;

                    case GameObjectTypes.QuestGiver:
                        player.PrepareGossipMenu(gameObjTarget, gameObjTarget.GetGoInfo().QuestGiver.gossipID, true);
                        player.SendPreparedGossip(gameObjTarget);
                        return;

                    case GameObjectTypes.SpellFocus:
                        // triggering linked GO
                        uint trapEntry = gameObjTarget.GetGoInfo().SpellFocus.linkedTrap;
                        if (trapEntry != 0)
                            gameObjTarget.TriggeringLinkedGameObject(trapEntry, m_caster);
                        return;
                    case GameObjectTypes.Chest:
                        // @todo possible must be moved to loot release (in different from linked triggering)
                        if (gameObjTarget.GetGoInfo().Chest.triggeredEvent != 0)
                        {
                            Log.outDebug(LogFilter.Spells, "Chest ScriptStart id {0} for GO {1}", gameObjTarget.GetGoInfo().Chest.triggeredEvent, gameObjTarget.GetSpawnId());
                            player.GetMap().ScriptsStart(ScriptsType.Event, gameObjTarget.GetGoInfo().Chest.triggeredEvent, player, gameObjTarget);
                        }

                        // triggering linked GO
                        uint _trapEntry = gameObjTarget.GetGoInfo().Chest.linkedTrap;
                        if (_trapEntry != 0)
                            gameObjTarget.TriggeringLinkedGameObject(_trapEntry, m_caster);
                        break;
                    // Don't return, let loots been taken
                    default:
                        break;
                }
            }

            // Send loot
            player.SendLoot(guid, loottype);
        }

        [SpellEffectHandler(SpellEffectName.OpenLock)]
        void EffectOpenLock(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!m_caster.IsTypeId(TypeId.Player))
            {
                Log.outDebug(LogFilter.Spells, "WORLD: Open Lock - No Player Caster!");
                return;
            }

            Player player = m_caster.ToPlayer();

            uint lockId = 0;
            ObjectGuid guid = ObjectGuid.Empty;

            // Get lockId
            if (gameObjTarget != null)
            {
                GameObjectTemplate goInfo = gameObjTarget.GetGoInfo();
                // Arathi Basin banner opening. // @todo Verify correctness of this check
                if ((goInfo.type == GameObjectTypes.Button && goInfo.Button.noDamageImmune != 0) ||
                    (goInfo.type == GameObjectTypes.Goober && goInfo.Goober.requireLOS != 0))
                {
                    //CanUseBattlegroundObject() already called in CheckCast()
                    // in Battlegroundcheck
                    Battleground bg = player.GetBattleground();
                    if (bg)
                    {
                        bg.EventPlayerClickedOnFlag(player, gameObjTarget);
                        return;
                    }
                }
                else if (goInfo.type == GameObjectTypes.FlagStand)
                {
                    //CanUseBattlegroundObject() already called in CheckCast()
                    // in Battlegroundcheck
                    Battleground bg = player.GetBattleground();
                    if (bg)
                    {
                        if (bg.GetTypeID(true) == BattlegroundTypeId.EY)
                            bg.EventPlayerClickedOnFlag(player, gameObjTarget);
                        return;
                    }
                }
                else if (m_spellInfo.Id == 1842 && gameObjTarget.GetGoInfo().type == GameObjectTypes.Trap && gameObjTarget.GetOwner() != null)
                {
                    gameObjTarget.SetLootState(LootState.JustDeactivated);
                    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 == GameObjectTypes.Goober && Global.OutdoorPvPMgr.HandleOpenGo(player, gameObjTarget))
                    return;
                lockId = goInfo.GetLockId();
                guid = gameObjTarget.GetGUID();
            }
            else if (itemTarget != null)
            {
                lockId = itemTarget.GetTemplate().GetLockID();
                guid = itemTarget.GetGUID();
            }
            else
            {
                Log.outDebug(LogFilter.Spells, "WORLD: Open Lock - No GameObject/Item Target!");
                return;
            }

            SkillType skillId = SkillType.None;
            int reqSkillValue = 0;
            int skillValue = 0;

            SpellCastResult res = CanOpenLock(effIndex, lockId, skillId, ref reqSkillValue, ref skillValue);
            if (res != SpellCastResult.SpellCastOk)
            {
                SendCastResult(res);
                return;
            }

            if (gameObjTarget != null)
                SendLoot(guid, LootType.Skinning);
            else if (itemTarget != null)
                itemTarget.SetFlag(ItemFields.Flags, ItemFieldFlags.Unlocked);

            // not allow use skill grow at item base open
            if (m_CastItem == null && skillId != SkillType.None)
            {
                // update skill if really known
                uint pureSkillValue = player.GetPureSkillValue(skillId);
                if (pureSkillValue != 0)
                {
                    if (gameObjTarget != null)
                    {
                        // Allow one skill-up until respawned
                        if (!gameObjTarget.IsInSkillupList(player.GetGUID()) &&
                            player.UpdateGatherSkill(skillId, pureSkillValue, (uint)reqSkillValue))
                            gameObjTarget.AddToSkillupList(player.GetGUID());
                    }
                    else if (itemTarget != null)
                    {
                        // Do one skill-up
                        player.UpdateGatherSkill(skillId, pureSkillValue, (uint)reqSkillValue);
                    }
                }
            }
            ExecuteLogEffectOpenLock(effIndex, gameObjTarget != null ? gameObjTarget : (WorldObject)itemTarget);
        }

        [SpellEffectHandler(SpellEffectName.SummonChangeItem)]
        void EffectSummonChangeItem(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            if (!m_caster.IsTypeId(TypeId.Player))
                return;

            Player player = m_caster.ToPlayer();

            // applied only to using item
            if (m_CastItem == null)
                return;

            // ... only to item in own inventory/bank/equip_slot
            if (m_CastItem.GetOwnerGUID() != player.GetGUID())
                return;

            uint newitemid = effectInfo.ItemType;
            if (newitemid == 0)
                return;

            ushort pos = m_CastItem.GetPos();

            Item pNewItem = Item.CreateItem(newitemid, 1, player);
            if (pNewItem == null)
                return;

            for (var j = EnchantmentSlot.Perm; j <= EnchantmentSlot.Temp; ++j)
                if (m_CastItem.GetEnchantmentId(j) != 0)
                    pNewItem.SetEnchantment(j, m_CastItem.GetEnchantmentId(j), m_CastItem.GetEnchantmentDuration(j), m_CastItem.GetEnchantmentCharges(j));

            if (m_CastItem.GetUInt32Value(ItemFields.Durability) < m_CastItem.GetUInt32Value(ItemFields.MaxDurability))
            {
                double lossPercent = 1 - m_CastItem.GetUInt32Value(ItemFields.Durability) / (double)m_CastItem.GetUInt32Value(ItemFields.MaxDurability);
                player.DurabilityLoss(pNewItem, lossPercent);
            }

            if (player.IsInventoryPos(pos))
            {
                List<ItemPosCount> dest = new List<ItemPosCount>();
                InventoryResult msg = player.CanStoreItem(m_CastItem.GetBagSlot(), m_CastItem.GetSlot(), dest, pNewItem, true);
                if (msg == InventoryResult.Ok)
                {
                    player.DestroyItem(m_CastItem.GetBagSlot(), m_CastItem.GetSlot(), true);

                    // prevent crash at access and unexpected charges counting with item update queue corrupt
                    if (m_CastItem == m_targets.GetItemTarget())
                        m_targets.SetItemTarget(null);

                    m_CastItem = null;
                    m_castItemGUID.Clear();
                    m_castItemEntry = 0;
                    m_castItemLevel = -1;

                    player.StoreItem(dest, pNewItem, true);
                    return;
                }
            }
            else if (Player.IsBankPos(pos))
            {
                List<ItemPosCount> dest = new List<ItemPosCount>();
                InventoryResult msg = player.CanBankItem(m_CastItem.GetBagSlot(), m_CastItem.GetSlot(), dest, pNewItem, true);
                if (msg == InventoryResult.Ok)
                {
                    player.DestroyItem(m_CastItem.GetBagSlot(), m_CastItem.GetSlot(), true);

                    // prevent crash at access and unexpected charges counting with item update queue corrupt
                    if (m_CastItem == m_targets.GetItemTarget())
                        m_targets.SetItemTarget(null);

                    m_CastItem = null;
                    m_castItemGUID.Clear();
                    m_castItemEntry = 0;
                    m_castItemLevel = -1;

                    player.BankItem(dest, pNewItem, true);
                    return;
                }
            }
            else if (Player.IsEquipmentPos(pos))
            {
                ushort dest;

                player.DestroyItem(m_CastItem.GetBagSlot(), m_CastItem.GetSlot(), true);

                InventoryResult msg = player.CanEquipItem(m_CastItem.GetSlot(), out dest, pNewItem, true);

                if (msg == InventoryResult.Ok || msg == InventoryResult.ClientLockedOut)
                {
                    if (msg == InventoryResult.ClientLockedOut)
                        dest = EquipmentSlot.MainHand;

                    // prevent crash at access and unexpected charges counting with item update queue corrupt
                    if (m_CastItem == m_targets.GetItemTarget())
                        m_targets.SetItemTarget(null);

                    m_CastItem = null;
                    m_castItemGUID.Clear();
                    m_castItemEntry = 0;
                    m_castItemLevel = -1;

                    player.EquipItem(dest, pNewItem, true);
                    player.AutoUnequipOffhandIfNeed();
                    return;
                }
            }
        }

        [SpellEffectHandler(SpellEffectName.Proficiency)]
        void EffectProficiency(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            if (!m_caster.IsTypeId(TypeId.Player))
                return;
            Player p_target = m_caster.ToPlayer();

            uint subClassMask = (uint)m_spellInfo.EquippedItemSubClassMask;
            if (m_spellInfo.EquippedItemClass == ItemClass.Weapon && !Convert.ToBoolean(p_target.GetWeaponProficiency() & subClassMask))
            {
                p_target.AddWeaponProficiency(subClassMask);
                p_target.SendProficiency(ItemClass.Weapon, p_target.GetWeaponProficiency());
            }
            if (m_spellInfo.EquippedItemClass == ItemClass.Armor && !Convert.ToBoolean(p_target.GetArmorProficiency() & subClassMask))
            {
                p_target.AddArmorProficiency(subClassMask);
                p_target.SendProficiency(ItemClass.Armor, p_target.GetArmorProficiency());
            }
        }

        [SpellEffectHandler(SpellEffectName.Summon)]
        void EffectSummonType(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            uint entry = (uint)effectInfo.MiscValue;
            if (entry == 0)
                return;

            SummonPropertiesRecord properties = CliDB.SummonPropertiesStorage.LookupByKey(effectInfo.MiscValueB);
            if (properties == null)
            {
                Log.outError(LogFilter.Spells, "EffectSummonType: Unhandled summon type {0}", effectInfo.MiscValueB);
                return;
            }

            if (m_originalCaster == null)
                return;

            int duration = m_spellInfo.CalcDuration(m_originalCaster);

            TempSummon summon = null;

            // determine how many units should be summoned
            uint 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 = (uint)(damage > 0 ? damage : 1);
                    break;
                default:
                    numSummons = 1;
                    break;
            }

            switch (properties.Category)
            {
                case SummonCategory.Wild:
                case SummonCategory.Ally:
                case SummonCategory.Unk:
                    if (Convert.ToBoolean(properties.Flags & 512))
                    {
                        SummonGuardian(effIndex, entry, properties, numSummons);
                        break;
                    }
                    switch (properties.Type)
                    {
                        case SummonType.Pet:
                        case SummonType.Guardian:
                        case SummonType.Guardian2:
                        case SummonType.Minion:
                            SummonGuardian(effIndex, entry, properties, numSummons);
                            break;
                        // Summons a vehicle, but doesn't force anyone to enter it (see SUMMON_CATEGORY_VEHICLE)
                        case SummonType.Vehicle:
                        case SummonType.Vehicle2:
                            summon = m_caster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_originalCaster, m_spellInfo.Id);
                            break;
                        case SummonType.LightWell:
                        case SummonType.Totem:
                            {
                                summon = m_caster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_originalCaster, m_spellInfo.Id);
                                if (summon == null || !summon.IsTotem())
                                    return;

                                // Mana Tide Totem
                                if (m_spellInfo.Id == 16190)
                                    damage = (int)m_caster.CountPctFromMaxHealth(10);

                                if (damage != 0)                                            // if not spell info, DB values used
                                {
                                    summon.SetMaxHealth((uint)damage);
                                    summon.SetHealth((uint)damage);
                                }
                                break;
                            }
                        case SummonType.Minipet:
                            {
                                summon = m_caster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_originalCaster, m_spellInfo.Id);
                                if (summon == null || !summon.HasUnitTypeMask(UnitTypeMask.Minion))
                                    return;

                                summon.SelectLevel();       // some summoned creaters have different from 1 DB data for level/hp
                                summon.SetUInt64Value(UnitFields.NpcFlags, (ulong)summon.GetCreatureTemplate().Npcflag);

                                summon.SetFlag(UnitFields.Flags, UnitFlags.ImmuneToPc | UnitFlags.ImmuneToNpc);

                                summon.GetAI().EnterEvadeMode();
                                break;
                            }
                        default:
                            {
                                float radius = effectInfo.CalcRadius();

                                TempSummonType summonType = (duration == 0) ? TempSummonType.DeadDespawn : TempSummonType.TimedDespawn;

                                for (uint count = 0; count < numSummons; ++count)
                                {
                                    Position pos = new Position();
                                    if (count == 0)
                                        pos = destTarget;
                                    else
                                        // randomize position for multiple summons
                                        m_caster.GetRandomPoint(destTarget, radius, out pos);

                                    summon = m_originalCaster.SummonCreature(entry, pos, summonType, (uint)duration);
                                    if (summon == null)
                                        continue;

                                    if (properties.Category == SummonCategory.Ally)
                                    {
                                        summon.SetOwnerGUID(m_originalCaster.GetGUID());
                                        summon.SetFaction(m_originalCaster.getFaction());
                                        summon.SetUInt32Value(UnitFields.CreatedBySpell, m_spellInfo.Id);
                                    }

                                    ExecuteLogEffectSummonObject(effIndex, summon);
                                }
                                return;
                            }
                    }//switch
                    break;
                case SummonCategory.Pet:
                    SummonGuardian(effIndex, entry, properties, numSummons);
                    break;
                case SummonCategory.Puppet:
                    summon = m_caster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_originalCaster, m_spellInfo.Id);
                    break;
                case SummonCategory.Vehicle:
                    // 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 = m_originalCaster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_caster, m_spellInfo.Id);
                    if (summon == null || !summon.IsVehicle())
                        return;

                    // The spell that this effect will trigger. It has SPELL_AURA_CONTROL_VEHICLE
                    uint spellId = SharedConst.VehicleSpellRideHardcoded;
                    SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo((uint)effectInfo.CalcValue());
                    if (spellInfo != null && spellInfo.HasAura(m_originalCaster.GetMap().GetDifficultyID(), AuraType.ControlVehicle))
                        spellId = spellInfo.Id;

                    // Hard coded enter vehicle spell
                    m_originalCaster.CastSpell(summon, spellId, true);

                    uint faction = properties.Faction;
                    if (faction == 0)
                        faction = m_originalCaster.getFaction();

                    summon.SetFaction(faction);
                    break;
            }

            if (summon != null)
            {
                summon.SetCreatorGUID(m_originalCaster.GetGUID());
                ExecuteLogEffectSummonObject(effIndex, summon);
            }
        }

        [SpellEffectHandler(SpellEffectName.LearnSpell)]
        void EffectLearnSpell(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null)
                return;

            if (!unitTarget.IsTypeId(TypeId.Player))
            {
                if (unitTarget.IsPet())
                    EffectLearnPetSpell(effIndex);
                return;
            }

            Player player = unitTarget.ToPlayer();

            uint spellToLearn = (m_spellInfo.Id == 483 || m_spellInfo.Id == 55884) ? (uint)damage : effectInfo.TriggerSpell;
            player.LearnSpell(spellToLearn, false);

            if (m_spellInfo.Id == 55884)
            {
                BattlePetMgr battlePetMgr = player.GetSession().GetBattlePetMgr();
                if (battlePetMgr != null)
                {
                    foreach (var entry in CliDB.BattlePetSpeciesStorage.Values)
                    {
                        if (entry.SummonSpellID == spellToLearn)
                        {
                            battlePetMgr.AddPet(entry.Id, entry.CreatureID);
                            player.UpdateCriteria(CriteriaTypes.OwnBattlePetCount);
                            break;
                        }
                    }
                }
            }

            Log.outDebug(LogFilter.Spells, "Spell: Player {0} has learned spell {1} from NpcGUID={2}", player.GetGUID().ToString(), spellToLearn, m_caster.GetGUID().ToString());
        }

        [SpellEffectHandler(SpellEffectName.Dispel)]
        void EffectDispel(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null)
                return;

            // Create dispel mask by dispel type
            uint dispel_type = (uint)effectInfo.MiscValue;
            uint dispelMask = SpellInfo.GetDispelMask((DispelType)dispel_type);

            List<DispelCharges> dispel_list = unitTarget.GetDispellableAuraList(m_caster, dispelMask);
            if (dispel_list.Empty())
                return;

            // Ok if exist some buffs for dispel try dispel it
            List<DispelCharges> success_list = new List<DispelCharges>();

            DispelFailed dispelFailed = new 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 (int count = 0; count < damage && !dispel_list.Empty();)
            {
                // Random select buff for dispel
                var pair = dispel_list[RandomHelper.IRand(0, dispel_list.Count - 1)];

                int chance = pair.aura.CalcDispelChance(unitTarget, !unitTarget.IsFriendlyTo(m_caster));
                // 2.4.3 Patch Notes: "Dispel effects will no longer attempt to remove effects that have 100% dispel resistance."
                if (chance == 0)
                {
                    dispel_list.Remove(pair);
                    continue;
                }
                else
                {
                    if (RandomHelper.randChance(chance))
                    {
                        bool alreadyListed = false;
                        foreach (var successPair in success_list)
                        {
                            var successValue = successPair.value;
                            if (successPair.aura.GetId() == pair.aura.GetId())
                            {
                                ++successPair.value;
                                alreadyListed = true;
                            }
                        }
                        if (!alreadyListed)
                            success_list.Add(new DispelCharges(pair.aura, 1));
                        --pair.value;
                        if (pair.value <= 0)
                            dispel_list.Remove(pair);
                    }
                    else
                        dispelFailed.FailedSpells.Add(pair.aura.GetId());

                    ++count;
                }
            }

            if (!dispelFailed.FailedSpells.Empty())
                m_caster.SendMessageToSet(dispelFailed, true);

            if (success_list.Empty())
                return;

            SpellDispellLog spellDispellLog = new SpellDispellLog();
            spellDispellLog.IsBreak = false; // TODO: use me
            spellDispellLog.IsSteal = false;

            spellDispellLog.TargetGUID = unitTarget.GetGUID();
            spellDispellLog.CasterGUID = m_caster.GetGUID();
            spellDispellLog.DispelledBySpellID = m_spellInfo.Id;

            foreach (var dispellCharge in success_list)
            {
                var dispellData = new SpellDispellData();
                dispellData.SpellID = dispellCharge.aura.GetId();
                dispellData.Harmful = false;      // TODO: use me
                //dispellData.Rolled = none; // TODO: use me
                //dispellData.Needed = none; // TODO: use me

                unitTarget.RemoveAurasDueToSpellByDispel(dispellCharge.aura.GetId(), m_spellInfo.Id, dispellCharge.aura.GetCasterGUID(), m_caster, dispellCharge.value);

                spellDispellLog.DispellData.Add(dispellData);
            }
            m_caster.SendMessageToSet(spellDispellLog, true);

            CallScriptSuccessfulDispel(effIndex);
        }

        [SpellEffectHandler(SpellEffectName.DualWield)]
        void EffectDualWield(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            unitTarget.SetCanDualWield(true);
            if (unitTarget.IsTypeId(TypeId.Unit))
                unitTarget.ToCreature().UpdateDamagePhysical(WeaponAttackType.OffAttack);
        }

        [SpellEffectHandler(SpellEffectName.Pull)]
        void EffectPull(uint effIndex)
        {
            // @todo create a proper pull towards distract spell center for distract
            EffectUnused(effIndex);
        }

        [SpellEffectHandler(SpellEffectName.Distract)]
        void EffectDistract(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            // Check for possible target
            if (unitTarget == null || unitTarget.IsInCombat())
                return;

            // target must be OK to do this
            if (unitTarget.HasUnitState(UnitState.Confused | UnitState.Stunned | UnitState.Fleeing))
                return;

            unitTarget.SetFacingTo(unitTarget.GetAngle(destTarget));
            unitTarget.ClearUnitState(UnitState.Moving);

            if (unitTarget.IsTypeId(TypeId.Unit))
                unitTarget.GetMotionMaster().MoveDistract((uint)(damage * Time.InMilliseconds));
        }

        [SpellEffectHandler(SpellEffectName.Pickpocket)]
        void EffectPickPocket(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!m_caster.IsTypeId(TypeId.Player))
                return;

            // victim must be creature and attackable
            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Unit) || m_caster.IsFriendlyTo(unitTarget))
                return;

            // victim have to be alive and humanoid or undead
            if (unitTarget.IsAlive() && (unitTarget.GetCreatureTypeMask() & (uint)CreatureType.MaskHumanoidOrUndead) != 0)
                m_caster.ToPlayer().SendLoot(unitTarget.GetGUID(), LootType.Pickpocketing);
        }

        [SpellEffectHandler(SpellEffectName.AddFarsight)]
        void EffectAddFarsight(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            if (!m_caster.IsTypeId(TypeId.Player))
                return;

            float radius = effectInfo.CalcRadius();
            int duration = m_spellInfo.CalcDuration(m_caster);
            // Caster not in world, might be spell triggered from aura removal
            if (!m_caster.IsInWorld)
                return;

            DynamicObject dynObj = new DynamicObject(true);
            if (!dynObj.CreateDynamicObject(m_caster.GetMap().GenerateLowGuid(HighGuid.DynamicObject), m_caster, m_spellInfo, destTarget, radius, DynamicObjectType.FarsightFocus, m_SpellVisual))
                return;

            dynObj.SetDuration(duration);
            dynObj.SetCasterViewpoint();
        }

        [SpellEffectHandler(SpellEffectName.UntrainTalents)]
        void EffectUntrainTalents(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || m_caster.IsTypeId(TypeId.Player))
                return;

            ObjectGuid guid = m_caster.GetGUID();
            if (!guid.IsEmpty()) // the trainer is the caster
                unitTarget.ToPlayer().SendRespecWipeConfirm(guid, unitTarget.ToPlayer().GetNextResetTalentsCost());
        }

        [SpellEffectHandler(SpellEffectName.TeleportUnitsFaceCaster)]
        void EffectTeleUnitsFaceCaster(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null)
                return;

            if (unitTarget.IsInFlight())
                return;

            float dis = effectInfo.CalcRadius(m_caster);

            float fx, fy, fz;
            m_caster.GetClosePoint(out fx, out fy, out fz, unitTarget.GetObjectSize(), dis);

            unitTarget.NearTeleportTo(fx, fy, fz, -m_caster.GetOrientation(), unitTarget == m_caster);
        }

        [SpellEffectHandler(SpellEffectName.SkillStep)]
        void EffectLearnSkill(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget.IsTypeId(TypeId.Player))
                return;

            if (damage < 0)
                return;

            uint skillid = (uint)effectInfo.MiscValue;
            SkillRaceClassInfoRecord rcEntry = Global.DB2Mgr.GetSkillRaceClassInfo(skillid, unitTarget.GetRace(), unitTarget.GetClass());
            if (rcEntry == null)
                return;

            SkillTiersEntry tier = Global.ObjectMgr.GetSkillTier(rcEntry.SkillTierID);
            if (tier == null)
                return;
            ushort skillval = unitTarget.ToPlayer().GetPureSkillValue((SkillType)skillid);
            unitTarget.ToPlayer().SetSkill(skillid, (uint)effectInfo.CalcValue(), Math.Max(skillval, (ushort)1), tier.Value[damage - 1]);
        }

        [SpellEffectHandler(SpellEffectName.PlayMovie)]
        void EffectPlayMovie(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget.IsTypeId(TypeId.Player))
                return;

            uint movieId = (uint)effectInfo.MiscValue;
            if (!CliDB.MovieStorage.ContainsKey(movieId))
                return;

            unitTarget.ToPlayer().SendMovieStart(movieId);
        }

        [SpellEffectHandler(SpellEffectName.TradeSkill)]
        void EffectTradeSkill(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            if (!m_caster.IsTypeId(TypeId.Player))
                return;
            // uint skillid =  GetEffect(i].MiscValue;
            // ushort skillmax = unitTarget.ToPlayer().(skillid);
            // m_caster.ToPlayer().SetSkill(skillid, skillval?skillval:1, skillmax+75);
        }

        [SpellEffectHandler(SpellEffectName.EnchantItem)]
        void EffectEnchantItemPerm(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (itemTarget == null)
                return;

            Player player = m_caster.ToPlayer();
            if (player == null)
                return;

            // Handle vellums
            if (itemTarget.IsVellum())
            {
                // destroy one vellum from stack
                uint count = 1;
                player.DestroyItemCount(itemTarget, ref count, true);
                unitTarget = player;
                // and add a scroll
                DoCreateItem(effIndex, effectInfo.ItemType);
                itemTarget = null;
                m_targets.SetItemTarget(null);
            }
            else
            {
                // do not increase skill if vellum used
                if (!(m_CastItem && m_CastItem.GetTemplate().GetFlags().HasAnyFlag(ItemFlags.NoReagentCost)))
                    player.UpdateCraftSkill(m_spellInfo.Id);

                uint enchant_id = (uint)effectInfo.MiscValue;
                if (enchant_id == 0)
                    return;

                SpellItemEnchantmentRecord pEnchant = CliDB.SpellItemEnchantmentStorage.LookupByKey(enchant_id);
                if (pEnchant == null)
                    return;

                // item can be in trade slot and have owner diff. from caster
                Player item_owner = itemTarget.GetOwner();
                if (item_owner == null)
                    return;

                if (item_owner != player && player.GetSession().HasPermission(RBACPermissions.LogGmTrade))
                {
                    Log.outCommand(player.GetSession().GetAccountId(), "GM {0} (Account: {1}) enchanting(perm): {2} (Entry: {3}) for player: {4} (Account: {5})",
                        player.GetName(), player.GetSession().GetAccountId(), itemTarget.GetTemplate().GetName(), itemTarget.GetEntry(), item_owner.GetName(), item_owner.GetSession().GetAccountId());
                }

                // remove old enchanting before applying new if equipped
                item_owner.ApplyEnchantment(itemTarget, EnchantmentSlot.Perm, false);

                itemTarget.SetEnchantment(EnchantmentSlot.Perm, enchant_id, 0, 0, m_caster.GetGUID());

                // add new enchanting if equipped
                item_owner.ApplyEnchantment(itemTarget, EnchantmentSlot.Perm, true);

                item_owner.RemoveTradeableItem(itemTarget);
                itemTarget.ClearSoulboundTradeable(item_owner);
            }
        }

        [SpellEffectHandler(SpellEffectName.EnchantItemPrismatic)]
        void EffectEnchantItemPrismatic(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (itemTarget == null)
                return;

            Player player = m_caster.ToPlayer();
            if (player == null)
                return;

            uint enchantId = (uint)effectInfo.MiscValue;
            if (enchantId == 0)
                return;

            SpellItemEnchantmentRecord enchant = CliDB.SpellItemEnchantmentStorage.LookupByKey(enchantId);
            if (enchant == null)
                return;

            // support only enchantings with add socket in this slot
            {
                bool add_socket = false;
                for (byte i = 0; i < ItemConst.MaxItemEnchantmentEffects; ++i)
                {
                    if (enchant.Effect[i] == ItemEnchantmentType.PrismaticSocket)
                    {
                        add_socket = true;
                        break;
                    }
                }
                if (!add_socket)
                {
                    Log.outError(LogFilter.Spells, "Spell.EffectEnchantItemPrismatic: attempt apply enchant spell {0} with SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC ({1}) but without ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET ({2}), not suppoted yet.",
                        m_spellInfo.Id, SpellEffectName.EnchantItemPrismatic, ItemEnchantmentType.PrismaticSocket);
                    return;
                }
            }

            // item can be in trade slot and have owner diff. from caster
            Player item_owner = itemTarget.GetOwner();
            if (item_owner == null)
                return;

            if (item_owner != player && player.GetSession().HasPermission(RBACPermissions.LogGmTrade))
            {
                Log.outCommand(player.GetSession().GetAccountId(), "GM {0} (Account: {1}) enchanting(perm): {2} (Entry: {3}) for player: {4} (Account: {5})",
                    player.GetName(), player.GetSession().GetAccountId(), itemTarget.GetTemplate().GetName(), itemTarget.GetEntry(), item_owner.GetName(), item_owner.GetSession().GetAccountId());
            }

            // remove old enchanting before applying new if equipped
            item_owner.ApplyEnchantment(itemTarget, EnchantmentSlot.Prismatic, false);

            itemTarget.SetEnchantment(EnchantmentSlot.Prismatic, enchantId, 0, 0, m_caster.GetGUID());

            // add new enchanting if equipped
            item_owner.ApplyEnchantment(itemTarget, EnchantmentSlot.Prismatic, true);

            item_owner.RemoveTradeableItem(itemTarget);
            itemTarget.ClearSoulboundTradeable(item_owner);
        }

        [SpellEffectHandler(SpellEffectName.EnchantItemTemporary)]
        void EffectEnchantItemTmp(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            Player player = m_caster.ToPlayer();
            if (player == null)
                return;

            if (itemTarget == null)
                return;

            uint enchant_id = (uint)effectInfo.MiscValue;

            if (enchant_id == 0)
            {
                Log.outError(LogFilter.Spells, "Spell {0} Effect {1} (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) have 0 as enchanting id", m_spellInfo.Id, effIndex);
                return;
            }

            SpellItemEnchantmentRecord pEnchant = CliDB.SpellItemEnchantmentStorage.LookupByKey(enchant_id);
            if (pEnchant == null)
            {
                Log.outError(LogFilter.Spells, "Spell {0} Effect {1} (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) have not existed enchanting id {2}", m_spellInfo.Id, effIndex, enchant_id);
                return;
            }

            // select enchantment duration
            uint duration;

            // rogue family enchantments exception by duration
            if (m_spellInfo.Id == 38615)
                duration = 1800;                                    // 30 mins
            // other rogue family enchantments always 1 hour (some have spell damage=0, but some have wrong data in EffBasePoints)
            else if (m_spellInfo.SpellFamilyName == SpellFamilyNames.Rogue)
                duration = 3600;                                    // 1 hour
            // shaman family enchantments
            else if (m_spellInfo.SpellFamilyName == SpellFamilyNames.Shaman)
                duration = 3600;                                    // 30 mins
            // other cases with this SpellVisual already selected
            else if (m_spellInfo.GetSpellVisual() == 215)
                duration = 1800;                                    // 30 mins
            // some fishing pole bonuses except Glow Worm which lasts full hour
            else if (m_spellInfo.GetSpellVisual() == 563 && m_spellInfo.Id != 64401)
                duration = 600;                                     // 10 mins
            else if (m_spellInfo.Id == 29702)
                duration = 300;                                     // 5 mins
            else if (m_spellInfo.Id == 37360)
                duration = 300;                                     // 5 mins
            // default case
            else
                duration = 3600;                                    // 1 hour

            // item can be in trade slot and have owner diff. from caster
            Player item_owner = itemTarget.GetOwner();
            if (item_owner == null)
                return;

            if (item_owner != player && player.GetSession().HasPermission(RBACPermissions.LogGmTrade))
            {
                Log.outCommand(player.GetSession().GetAccountId(), "GM {0} (Account: {1}) enchanting(temp): {2} (Entry: {3}) for player: {4} (Account: {5})",
                    player.GetName(), player.GetSession().GetAccountId(), itemTarget.GetTemplate().GetName(), itemTarget.GetEntry(), item_owner.GetName(), item_owner.GetSession().GetAccountId());
            }

            // remove old enchanting before applying new if equipped
            item_owner.ApplyEnchantment(itemTarget, EnchantmentSlot.Temp, false);

            itemTarget.SetEnchantment(EnchantmentSlot.Temp, enchant_id, duration * 1000, 0, m_caster.GetGUID());

            // add new enchanting if equipped
            item_owner.ApplyEnchantment(itemTarget, EnchantmentSlot.Temp, true);
        }

        [SpellEffectHandler(SpellEffectName.Tamecreature)]
        void EffectTameCreature(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!m_caster.GetPetGUID().IsEmpty())
                return;

            if (unitTarget == null)
                return;

            if (!unitTarget.IsTypeId(TypeId.Unit))
                return;

            Creature creatureTarget = unitTarget.ToCreature();

            if (creatureTarget.IsPet())
                return;

            if (m_caster.GetClass() != Class.Hunter)
                return;

            // cast finish successfully
            finish();

            Pet pet = m_caster.CreateTamedPetFrom(creatureTarget, m_spellInfo.Id);
            if (pet == null)                                               // in very specific state like near world end/etc.
                return;

            // "kill" original creature
            creatureTarget.DespawnOrUnsummon();

            uint level = (creatureTarget.getLevel() < (m_caster.getLevel() - 5)) ? (m_caster.getLevel() - 5) : creatureTarget.getLevel();

            // prepare visual effect for levelup
            pet.SetUInt32Value(UnitFields.Level, level - 1);

            // add to world
            pet.GetMap().AddToMap(pet.ToCreature());

            // visual effect for levelup
            pet.SetUInt32Value(UnitFields.Level, level);

            // caster have pet now
            m_caster.SetMinion(pet, true);

            if (m_caster.IsTypeId(TypeId.Player))
            {
                pet.SavePetToDB(PetSaveMode.AsCurrent);
                m_caster.ToPlayer().PetSpellInitialize();
            }
        }

        [SpellEffectHandler(SpellEffectName.SummonPet)]
        void EffectSummonPet(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            Player owner = null;
            if (m_originalCaster != null)
            {
                owner = m_originalCaster.ToPlayer();
                if (owner == null && m_originalCaster.IsTotem())
                    owner = m_originalCaster.GetCharmerOrOwnerPlayerOrPlayerItself();
            }

            uint petentry = (uint)effectInfo.MiscValue;

            if (owner == null)
            {
                SummonPropertiesRecord properties = CliDB.SummonPropertiesStorage.LookupByKey(67);
                if (properties != null)
                    SummonGuardian(effIndex, petentry, properties, 1);
                return;
            }

            Pet OldSummon = owner.GetPet();

            // if pet requested type already exist
            if (OldSummon != null)
            {
                if (petentry == 0 || OldSummon.GetEntry() == petentry)
                {
                    // pet in corpse state can't be summoned
                    if (OldSummon.IsDead())
                        return;

                    Contract.Assert(OldSummon.GetMap() == owner.GetMap());

                    float px, py, pz;
                    owner.GetClosePoint(out px, out py, out pz, OldSummon.GetObjectSize());

                    OldSummon.NearTeleportTo(px, py, pz, OldSummon.GetOrientation());

                    if (owner.IsTypeId(TypeId.Player) && OldSummon.isControlled())
                        owner.ToPlayer().PetSpellInitialize();

                    return;
                }

                if (owner.IsTypeId(TypeId.Player))
                    owner.ToPlayer().RemovePet(OldSummon, (OldSummon.getPetType() == PetType.Hunter ? PetSaveMode.AsDeleted : PetSaveMode.NotInSlot), false);
                else
                    return;
            }

            float x, y, z;
            owner.GetClosePoint(out x, out y, out z, owner.GetObjectSize());
            Pet pet = owner.SummonPet(petentry, x, y, z, owner.Orientation, PetType.Summon, 0);
            if (!pet)
                return;

            if (m_caster.IsTypeId(TypeId.Unit))
            {
                if (m_caster.IsTotem())
                    pet.SetReactState(ReactStates.Aggressive);
                else
                    pet.SetReactState(ReactStates.Defensive);
            }

            pet.SetUInt32Value(UnitFields.CreatedBySpell, m_spellInfo.Id);

            // generate new name for summon pet
            string new_name = Global.ObjectMgr.GeneratePetName(petentry);
            if (!string.IsNullOrEmpty(new_name))
                pet.SetName(new_name);

            ExecuteLogEffectSummonObject(effIndex, pet);
        }

        [SpellEffectHandler(SpellEffectName.LearnPetSpell)]
        void EffectLearnPetSpell(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null)
                return;

            if (unitTarget.ToPlayer() != null)
            {
                EffectLearnSpell(effIndex);
                return;
            }
            Pet pet = unitTarget.ToPet();
            if (pet == null)
                return;

            SpellInfo learn_spellproto = Global.SpellMgr.GetSpellInfo(effectInfo.TriggerSpell);
            if (learn_spellproto == null)
                return;

            pet.learnSpell(learn_spellproto.Id);
            pet.SavePetToDB(PetSaveMode.AsCurrent);
            pet.GetOwner().PetSpellInitialize();
        }

        [SpellEffectHandler(SpellEffectName.AttackMe)]
        void EffectTaunt(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null)
                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 == null || !unitTarget.CanHaveThreatList()
                || unitTarget.GetVictim() == m_caster)
            {
                SendCastResult(SpellCastResult.DontReport);
                return;
            }

            if (!unitTarget.GetThreatManager().getOnlineContainer().empty())
            {
                // Also use this effect to set the taunter's threat to the taunted creature's highest value
                float myThreat = unitTarget.GetThreatManager().getThreat(m_caster);
                float topThreat = unitTarget.GetThreatManager().getOnlineContainer().getMostHated().getThreat();
                if (topThreat > myThreat)
                    unitTarget.GetThreatManager().addThreat(m_caster, topThreat - myThreat);

                //Set aggro victim to caster
                HostileReference forcedVictim = unitTarget.GetThreatManager().getOnlineContainer().getReferenceByTarget(m_caster);
                if (forcedVictim != null)
                    unitTarget.GetThreatManager().setCurrentVictim(forcedVictim);
            }

            if (unitTarget.ToCreature().IsAIEnabled && !unitTarget.ToCreature().HasReactState(ReactStates.Passive))
                unitTarget.ToCreature().GetAI().AttackStart(m_caster);
        }

        [SpellEffectHandler(SpellEffectName.WeaponDamageNoschool)]
        [SpellEffectHandler(SpellEffectName.WeaponPercentDamage)]
        [SpellEffectHandler(SpellEffectName.WeaponDamage)]
        [SpellEffectHandler(SpellEffectName.NormalizedWeaponDmg)]
        void EffectWeaponDmg(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.LaunchTarget)
                return;

            if (unitTarget == null || !unitTarget.IsAlive())
                return;

            // multiple weapon dmg effect workaround
            // execute only the last weapon damage
            // and handle all effects at once
            for (var i = effIndex + 1; i < SpellConst.MaxEffects; ++i)
            {
                var effect = GetEffect(i);
                if (effect == null)
                    continue;

                switch (effect.Effect)
                {
                    case SpellEffectName.WeaponDamage:
                    case SpellEffectName.WeaponDamageNoschool:
                    case SpellEffectName.NormalizedWeaponDmg:
                    case SpellEffectName.WeaponPercentDamage:
                        return;     // we must calculate only at last weapon effect
                }
            }

            // some spell specific modifiers
            float totalDamagePercentMod = 1.0f;                    // applied to final bonus+weapon damage
            int fixed_bonus = 0;
            int spell_bonus = 0;                                  // bonus specific for spell

            switch (m_spellInfo.SpellFamilyName)
            {
                case SpellFamilyNames.Warrior:
                    {
                        // Devastate (player ones)
                        if (m_spellInfo.SpellFamilyFlags[1].HasAnyFlag(0x40u))
                        {
                            // Player can apply only 58567 Sunder Armor effect.
                            bool needCast = !unitTarget.HasAura(58567, m_caster.GetGUID());
                            if (needCast)
                                m_caster.CastSpell(unitTarget, 58567, true);

                            Aura aur = unitTarget.GetAura(58567, m_caster.GetGUID());
                            if (aur != null)
                            {
                                int num = (needCast ? 0 : 1);
                                if (num != 0)
                                    aur.ModStackAmount(num);
                                fixed_bonus += (aur.GetStackAmount() - 1) * CalculateDamage(2, unitTarget);
                            }
                        }
                        break;
                    }
                case SpellFamilyNames.Rogue:
                    {
                        // Hemorrhage
                        if (m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x2000000u))
                        {
                            if (m_caster.IsTypeId(TypeId.Player))
                                m_caster.ToPlayer().AddComboPoints(1, this);
                            // 50% more damage with daggers
                            if (m_caster.IsTypeId(TypeId.Player))
                            {
                                Item item = m_caster.ToPlayer().GetWeaponForAttack(m_attackType, true);
                                if (item != null)
                                    if (item.GetTemplate().GetSubClass() == (uint)ItemSubClassWeapon.Dagger)
                                        totalDamagePercentMod *= 1.5f;
                            }
                        }
                        break;
                    }
                case SpellFamilyNames.Shaman:
                    {
                        // Skyshatter Harness item set bonus
                        // Stormstrike
                        AuraEffect aurEff = m_caster.IsScriptOverriden(m_spellInfo, 5634);
                        if (aurEff != null)
                            m_caster.CastSpell(m_caster, 38430, true, null, aurEff);
                        break;
                    }
                case SpellFamilyNames.Druid:
                    {
                        // Mangle (Cat): CP
                        if (m_spellInfo.SpellFamilyFlags[1].HasAnyFlag(0x400u))
                        {
                            if (m_caster.IsTypeId(TypeId.Player))
                                m_caster.ToPlayer().AddComboPoints(1, this);
                        }
                        break;
                    }
                case SpellFamilyNames.Hunter:
                    {
                        // Kill Shot - bonus damage from Ranged Attack Power
                        if (m_spellInfo.SpellFamilyFlags[1].HasAnyFlag(0x800000u))
                            spell_bonus += (int)(0.45f * m_caster.GetTotalAttackPowerValue(WeaponAttackType.RangedAttack));
                        break;
                    }
                case SpellFamilyNames.Deathknight:
                    {
                        // Blood Strike
                        if (m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x400000u))
                        {
                            SpellEffectInfo effect = GetEffect(2);
                            if (effect != null)
                            {
                                float bonusPct = effect.CalcValue(m_caster) * unitTarget.GetDiseasesByCaster(m_caster.GetGUID()) / 2.0f;
                                // Death Knight T8 Melee 4P Bonus
                                AuraEffect aurEff = m_caster.GetAuraEffect(64736, 0);
                                if (aurEff != null)
                                    MathFunctions.AddPct(ref bonusPct, aurEff.GetAmount());
                                MathFunctions.AddPct(ref totalDamagePercentMod, bonusPct);
                            }
                            break;
                        }
                        break;
                    }
            }

            bool normalized = false;
            float weaponDamagePercentMod = 1.0f;
            foreach (SpellEffectInfo effect in GetEffects())
            {
                if (effect == null)
                    continue;

                switch (effect.Effect)
                {
                    case SpellEffectName.WeaponDamage:
                    case SpellEffectName.WeaponDamageNoschool:
                        fixed_bonus += CalculateDamage(effect.EffectIndex, unitTarget);
                        break;
                    case SpellEffectName.NormalizedWeaponDmg:
                        fixed_bonus += CalculateDamage(effect.EffectIndex, unitTarget);
                        normalized = true;
                        break;
                    case SpellEffectName.WeaponPercentDamage:
                        MathFunctions.ApplyPct(ref weaponDamagePercentMod, CalculateDamage(effect.EffectIndex, 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 addPctMods = !m_spellInfo.HasAttribute(SpellAttr6.NoDonePctDamageMods) && m_spellSchoolMask.HasAnyFlag(SpellSchoolMask.Normal);
            if (addPctMods)
            {
                UnitMods unitMod;
                switch (m_attackType)
                {
                    default:
                    case WeaponAttackType.BaseAttack:
                        unitMod = UnitMods.DamageMainHand;
                        break;
                    case WeaponAttackType.OffAttack:
                        unitMod = UnitMods.DamageOffHand;
                        break;
                    case WeaponAttackType.RangedAttack:
                        unitMod = UnitMods.DamageRanged;
                        break;
                }

                float weapon_total_pct = m_caster.GetModifierValue(unitMod, UnitModifierType.TotalPCT);
                if (fixed_bonus != 0)
                    fixed_bonus = (int)(fixed_bonus * weapon_total_pct);
                if (spell_bonus != 0)
                    spell_bonus = (int)(spell_bonus * weapon_total_pct);
            }

            uint weaponDamage = m_caster.CalculateDamage(m_attackType, normalized, addPctMods);

            // Sequence is important
            foreach (SpellEffectInfo effect in GetEffects())
            {
                if (effect == null)
                    continue;

                // We assume that a spell have at most one fixed_bonus
                // and at most one weaponDamagePercentMod
                switch (effect.Effect)
                {
                    case SpellEffectName.WeaponDamage:
                    case SpellEffectName.WeaponDamageNoschool:
                    case SpellEffectName.NormalizedWeaponDmg:
                        weaponDamage += (uint)fixed_bonus;
                        break;
                    case SpellEffectName.WeaponPercentDamage:
                        weaponDamage = (uint)(weaponDamage * weaponDamagePercentMod);
                        break;
                    default:
                        break;                                      // not weapon damage effect, just skip
                }
            }

            weaponDamage += (uint)spell_bonus;
            weaponDamage = (uint)(weaponDamage * totalDamagePercentMod);

            // prevent negative damage
            uint eff_damage = Math.Max(weaponDamage, 0);

            // Add melee damage bonuses (also check for negative)
            uint damage = m_caster.MeleeDamageBonusDone(unitTarget, eff_damage, m_attackType, m_spellInfo);

            m_damage += (int)unitTarget.MeleeDamageBonusTaken(m_caster, damage, m_attackType, m_spellInfo);
        }

        [SpellEffectHandler(SpellEffectName.Threat)]
        void EffectThreat(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsAlive() || !m_caster.IsAlive())
                return;

            if (!unitTarget.CanHaveThreatList())
                return;

            unitTarget.AddThreat(m_caster, damage);
        }

        [SpellEffectHandler(SpellEffectName.HealMaxHealth)]
        void EffectHealMaxHealth(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsAlive())
                return;

            int addhealth = 0;

            // damage == 0 - heal for caster max health
            if (damage == 0)
                addhealth = (int)m_caster.GetMaxHealth();
            else
                addhealth = (int)(unitTarget.GetMaxHealth() - unitTarget.GetHealth());

            m_healing += addhealth;
        }

        [SpellEffectHandler(SpellEffectName.InterruptCast)]
        void EffectInterruptCast(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !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 (var i = CurrentSpellTypes.Generic; i < CurrentSpellTypes.AutoRepeat; ++i)
            {
                Spell spell = unitTarget.GetCurrentSpell(i);
                if (spell != null)
                {
                    SpellInfo curSpellInfo = spell.m_spellInfo;
                    // check if we can interrupt spell
                    if ((spell.getState() == SpellState.Casting
                        || (spell.getState() == SpellState.Preparing && spell.GetCastTime() > 0.0f))
                        && (curSpellInfo.PreventionType.HasAnyFlag(SpellPreventionType.Silence))
                        && ((i == CurrentSpellTypes.Generic && curSpellInfo.InterruptFlags.HasAnyFlag(SpellInterruptFlags.Interrupt))
                        || (i == CurrentSpellTypes.Channeled && curSpellInfo.ChannelInterruptFlags.HasAnyFlag(SpellChannelInterruptFlags.Interrupt))))
                    {
                        if (m_originalCaster != null)
                        {
                            int duration = m_spellInfo.GetDuration();
                            unitTarget.GetSpellHistory().LockSpellSchool(curSpellInfo.GetSchoolMask(), (uint)unitTarget.ModSpellDuration(m_spellInfo, unitTarget, duration, false, (uint)(1 << (int)effIndex)));
                        }
                        ExecuteLogEffectInterruptCast(effIndex, unitTarget, curSpellInfo.Id);
                        unitTarget.InterruptSpell(i, false);
                    }
                }
            }
        }

        [SpellEffectHandler(SpellEffectName.SummonObjectWild)]
        void EffectSummonObjectWild(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            uint gameobject_id = (uint)effectInfo.MiscValue;

            GameObject pGameObj = new GameObject();

            WorldObject target = focusObject;
            if (target == null)
                target = m_caster;

            float x, y, z;
            if (m_targets.HasDst())
                destTarget.GetPosition(out x, out y, out z);
            else
                m_caster.GetClosePoint(out x, out y, out z, SharedConst.DefaultWorldObjectSize);

            Map map = target.GetMap();
            Quaternion rotation = new Quaternion(Matrix3.fromEulerAnglesZYX(target.GetOrientation(), 0.0f, 0.0f));
            if (!pGameObj.Create(gameobject_id, map, m_caster.GetPhaseMask(), new Position(x, y, z, target.GetOrientation()), rotation, 255, GameObjectState.Ready))
                return;

            pGameObj.CopyPhaseFrom(m_caster);

            int duration = m_spellInfo.CalcDuration(m_caster);

            pGameObj.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0);
            pGameObj.SetSpellId(m_spellInfo.Id);

            ExecuteLogEffectSummonObject(effIndex, pGameObj);

            // Wild object not have owner and check clickable by players
            map.AddToMap(pGameObj);

            if (pGameObj.GetGoType() == GameObjectTypes.FlagDrop)
            {
                Player player = m_caster.ToPlayer();
                if (player != null)
                {
                    Battleground bg = player.GetBattleground();
                    if (bg)
                        bg.SetDroppedFlagGUID(pGameObj.GetGUID(), (int)(player.GetTeam() == Team.Alliance ? TeamId.Horde : TeamId.Alliance));
                }
            }

            uint linkedEntry = pGameObj.GetGoInfo().GetLinkedGameObjectEntry();
            if (linkedEntry != 0)
            {
                GameObject linkedGO = new GameObject();
                if (linkedGO.Create(linkedEntry, map, m_caster.GetPhaseMask(), new Position(x, y, z, target.GetOrientation()), rotation, 255, GameObjectState.Ready))
                {
                    linkedGO.CopyPhaseFrom(m_caster);

                    linkedGO.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0);
                    linkedGO.SetSpellId(m_spellInfo.Id);

                    ExecuteLogEffectSummonObject(effIndex, linkedGO);

                    // Wild object not have owner and check clickable by players
                    map.AddToMap(linkedGO);
                }
                else
                    linkedGO.Dispose();
            }
        }

        [SpellEffectHandler(SpellEffectName.ScriptEffect)]
        void EffectScriptEffect(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            // @todo we must implement hunter pet summon at login there (spell 6962)

            switch (m_spellInfo.SpellFamilyName)
            {
                case SpellFamilyNames.Generic:
                    {
                        switch (m_spellInfo.Id)
                        {
                            case 45204: // Clone Me!
                                m_caster.CastSpell(unitTarget, (uint)damage, true);
                                break;
                            case 55693:                                 // Remove Collapsing Cave Aura
                                if (unitTarget == null)
                                    return;
                                unitTarget.RemoveAurasDueToSpell((uint)effectInfo.CalcValue());
                                break;
                            // Bending Shinbone
                            case 8856:
                                {
                                    if (itemTarget == null && !m_caster.IsTypeId(TypeId.Player))
                                        return;

                                    uint spell_id = RandomHelper.Rand32(20) != 0 ? 8854u : 8855u;

                                    m_caster.CastSpell(m_caster, spell_id, true, null);
                                    return;
                                }
                            // Brittle Armor - need remove one 24575 Brittle Armor aura
                            case 24590:
                                unitTarget.RemoveAuraFromStack(24575);
                                return;
                            // Mercurial Shield - need remove one 26464 Mercurial Shield aura
                            case 26465:
                                unitTarget.RemoveAuraFromStack(26464);
                                return;
                            // Shadow Flame (All script effects, not just end ones to prevent player from dodging the last triggered spell)
                            case 22539:
                            case 22972:
                            case 22975:
                            case 22976:
                            case 22977:
                            case 22978:
                            case 22979:
                            case 22980:
                            case 22981:
                            case 22982:
                            case 22983:
                            case 22984:
                            case 22985:
                                {
                                    if (unitTarget == null || !unitTarget.IsAlive())
                                        return;

                                    // Onyxia Scale Cloak
                                    if (unitTarget.HasAura(22683))
                                        return;

                                    // Shadow Flame
                                    m_caster.CastSpell(unitTarget, 22682, true);
                                    return;
                                }
                            // Mirren's Drinking Hat
                            case 29830:
                                {
                                    uint item = 0;
                                    switch (RandomHelper.IRand(1, 6))
                                    {
                                        case 1:
                                        case 2:
                                        case 3:
                                            item = 23584;
                                            break;            // Loch Modan Lager
                                        case 4:
                                        case 5:
                                            item = 23585;
                                            break;            // Stouthammer Lite
                                        case 6:
                                            item = 23586;
                                            break;            // Aerie Peak Pale Ale
                                    }
                                    if (item != 0)
                                        DoCreateItem(effIndex, item);
                                    break;
                                }
                            case 20589: // Escape artist
                            case 30918: // Improved Sprint
                                {
                                    // Removes snares and roots.
                                    unitTarget.RemoveMovementImpairingAuras();
                                    break;
                                }
                            // Plant Warmaul Ogre Banner
                            case 32307:
                                Player caster = m_caster.ToPlayer();
                                if (caster != null)
                                {
                                    caster.RewardPlayerAndGroupAtEvent(18388, unitTarget);
                                    Creature target = unitTarget.ToCreature();
                                    if (target != null)
                                    {
                                        target.setDeathState(DeathState.Corpse);
                                        target.RemoveCorpse();
                                    }
                                }
                                break;
                            // Mug Transformation
                            case 41931:
                                {
                                    if (!m_caster.IsTypeId(TypeId.Player))
                                        return;

                                    byte bag = 19;
                                    byte slot = 0;
                                    Item item = null;

                                    while (bag != 0) // 256 = 0 due to var type
                                    {
                                        item = m_caster.ToPlayer().GetItemByPos(bag, slot);
                                        if (item != null && item.GetEntry() == 38587)
                                            break;

                                        ++slot;
                                        if (slot == 39)
                                        {
                                            slot = 0;
                                            ++bag;
                                        }
                                    }
                                    if (bag != 0)
                                    {
                                        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, true);
                                        return;
                                    }
                                    break;
                                }
                            // Brutallus - Burn
                            case 45141:
                            case 45151:
                                {
                                    //Workaround for Range ... should be global for every ScriptEffect
                                    float radius = effectInfo.CalcRadius();
                                    if (unitTarget != null && unitTarget.IsTypeId(TypeId.Player) && unitTarget.GetDistance(m_caster) >= radius && !unitTarget.HasAura(46394) && unitTarget != m_caster)
                                        unitTarget.CastSpell(unitTarget, 46394, true);

                                    break;
                                }
                            // Goblin Weather Machine
                            case 46203:
                                {
                                    if (unitTarget == null)
                                        return;

                                    uint spellId = 0;
                                    switch (RandomHelper.IRand(1, 4))
                                    {
                                        case 0: spellId = 46740; break;
                                        case 1: spellId = 46739; break;
                                        case 2: spellId = 46738; break;
                                        case 3: spellId = 46736; break;
                                    }
                                    unitTarget.CastSpell(unitTarget, spellId, true);
                                    break;
                                }
                            // 5, 000 Gold
                            case 46642:
                                {
                                    if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                                        return;

                                    unitTarget.ToPlayer().ModifyMoney(5000 * MoneyConstants.Gold);

                                    break;
                                }
                            // Death Knight Initiate Visual
                            case 51519:
                                {
                                    if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Unit))
                                        return;

                                    uint iTmpSpellId = 0;
                                    switch (unitTarget.GetDisplayId())
                                    {
                                        case 25369: iTmpSpellId = 51552; break; // bloodelf female
                                        case 25373: iTmpSpellId = 51551; break; // bloodelf male
                                        case 25363: iTmpSpellId = 51542; break; // draenei female
                                        case 25357: iTmpSpellId = 51541; break; // draenei male
                                        case 25361: iTmpSpellId = 51537; break; // dwarf female
                                        case 25356: iTmpSpellId = 51538; break; // dwarf male
                                        case 25372: iTmpSpellId = 51550; break; // forsaken female
                                        case 25367: iTmpSpellId = 51549; break; // forsaken male
                                        case 25362: iTmpSpellId = 51540; break; // gnome female
                                        case 25359: iTmpSpellId = 51539; break; // gnome male
                                        case 25355: iTmpSpellId = 51534; break; // human female
                                        case 25354: iTmpSpellId = 51520; break; // human male
                                        case 25360: iTmpSpellId = 51536; break; // nightelf female
                                        case 25358: iTmpSpellId = 51535; break; // nightelf male
                                        case 25368: iTmpSpellId = 51544; break; // orc female
                                        case 25364: iTmpSpellId = 51543; break; // orc male
                                        case 25371: iTmpSpellId = 51548; break; // tauren female
                                        case 25366: iTmpSpellId = 51547; break; // tauren male
                                        case 25370: iTmpSpellId = 51545; break; // troll female
                                        case 25365: iTmpSpellId = 51546; break; // troll male
                                        default: return;
                                    }

                                    unitTarget.CastSpell(unitTarget, iTmpSpellId, true);
                                    Creature npc = unitTarget.ToCreature();
                                    npc.LoadEquipment();
                                    return;
                                }
                            // Emblazon Runeblade
                            case 51770:
                                {
                                    if (m_originalCaster == null)
                                        return;

                                    m_originalCaster.CastSpell(m_originalCaster, (uint)damage, false);
                                    break;
                                }
                            // Deathbolt from Thalgran Blightbringer
                            // reflected by Freya's Ward
                            // Retribution by Sevenfold Retribution
                            case 51854:
                                {
                                    if (unitTarget == null)
                                        return;
                                    if (unitTarget.HasAura(51845))
                                        unitTarget.CastSpell(m_caster, 51856, true);
                                    else
                                        m_caster.CastSpell(unitTarget, 51855, true);
                                    break;
                                }
                            // Summon Ghouls On Scarlet Crusade
                            case 51904:
                                {
                                    if (!m_targets.HasDst())
                                        return;

                                    float x, y, z;
                                    float radius = effectInfo.CalcRadius();
                                    for (byte i = 0; i < 15; ++i)
                                    {
                                        m_caster.GetRandomPoint(destTarget, radius, out x, out y, out z);
                                        m_caster.CastSpell(x, y, z, 54522, true);
                                    }
                                    break;
                                }
                            case 52173: // Coyote Spirit Despawn
                            case 60243: // Blood Parrot Despawn
                                if (unitTarget.IsTypeId(TypeId.Unit) && unitTarget.ToCreature().IsSummon())
                                    unitTarget.ToTempSummon().UnSummon();
                                return;
                            case 52479: // Gift of the Harvester
                                if (unitTarget != null && m_originalCaster != null)
                                    m_originalCaster.CastSpell(unitTarget, Convert.ToBoolean(RandomHelper.IRand(0, 1)) ? (uint)damage : 52505, true);
                                return;
                            case 53110: // Devour Humanoid
                                if (unitTarget != null)
                                    unitTarget.CastSpell(m_caster, (uint)damage, true);
                                return;
                            case 57347: // Retrieving (Wintergrasp RP-GG pickup spell)
                                {
                                    if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Unit) || !m_caster.IsTypeId(TypeId.Player))
                                        return;

                                    unitTarget.ToCreature().DespawnOrUnsummon();

                                    return;
                                }
                            case 57349: // Drop RP-GG (Wintergrasp RP-GG at death drop spell)
                                {
                                    if (!m_caster.IsTypeId(TypeId.Player))
                                        return;

                                    // Delete item from inventory at death
                                    m_caster.ToPlayer().DestroyItemCount((uint)damage, 5, true);

                                    return;
                                }
                            case 58418:                                 // Portal to Orgrimmar
                            case 58420:                                 // Portal to Stormwind
                                {
                                    if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player) || effIndex != 0)
                                        return;

                                    uint spellID = (uint)GetEffect(0).CalcValue();
                                    uint questID = (uint)GetEffect(1).CalcValue();

                                    if (unitTarget.ToPlayer().GetQuestStatus(questID) == QuestStatus.Complete)
                                        unitTarget.CastSpell(unitTarget, spellID, true);

                                    return;
                                }
                            case 58941:                                 // Rock Shards
                                if (unitTarget != null && m_originalCaster != null)
                                {
                                    for (uint i = 0; i < 3; ++i)
                                    {
                                        m_originalCaster.CastSpell(unitTarget, 58689, true);
                                        m_originalCaster.CastSpell(unitTarget, 58692, true);
                                    }
                                    if (m_originalCaster.GetMap().GetDifficultyID() == Difficulty.None)
                                    {
                                        m_originalCaster.CastSpell(unitTarget, 58695, true);
                                        m_originalCaster.CastSpell(unitTarget, 58696, true);
                                    }
                                    else
                                    {
                                        m_originalCaster.CastSpell(unitTarget, 60883, true);
                                        m_originalCaster.CastSpell(unitTarget, 60884, true);
                                    }
                                }
                                return;
                            case 58983: // Big Blizzard Bear
                                {
                                    if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                                        return;

                                    // Prevent stacking of mounts and client crashes upon dismounting
                                    unitTarget.RemoveAurasByType(AuraType.Mounted);

                                    // Triggered spell id dependent on riding skilll
                                    ushort skillval = unitTarget.ToPlayer().GetSkillValue(SkillType.Riding);
                                    if (skillval != 0)
                                    {
                                        if (skillval >= 150)
                                            unitTarget.CastSpell(unitTarget, 58999, true);
                                        else
                                            unitTarget.CastSpell(unitTarget, 58997, true);
                                    }
                                    return;
                                }
                            case 59317:                                 // Teleporting
                                {

                                    if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                                        return;

                                    // return from top
                                    if (unitTarget.ToPlayer().GetAreaId() == 4637)
                                        unitTarget.CastSpell(unitTarget, 59316, true);
                                    // teleport atop
                                    else
                                        unitTarget.CastSpell(unitTarget, 59314, true);

                                    return;
                                }
                            case 62482: // Grab Crate
                                {
                                    if (unitTarget != null)
                                    {
                                        Unit seat = m_caster.GetVehicleBase();
                                        if (seat != null)
                                        {
                                            Unit parent = seat.GetVehicleBase();
                                            if (parent != null)
                                            {
                                                // @todo a hack, range = 11, should after some time cast, otherwise too far
                                                m_caster.CastSpell(parent, 62496, true);
                                                unitTarget.CastSpell(parent, (uint)GetEffect(0).CalcValue());
                                            }
                                        }
                                    }
                                    return;
                                }
                            case 60123: // Lightwell
                                {
                                    if (!m_caster.IsTypeId(TypeId.Unit) || !m_caster.ToCreature().IsSummon())
                                        return;

                                    uint spell_heal;

                                    switch (m_caster.GetEntry())
                                    {
                                        case 31897: spell_heal = 7001; break;
                                        case 31896: spell_heal = 27873; break;
                                        case 31895: spell_heal = 27874; break;
                                        case 31894: spell_heal = 28276; break;
                                        case 31893: spell_heal = 48084; break;
                                        case 31883: spell_heal = 48085; break;
                                        default:
                                            Log.outError(LogFilter.Spells, "Unknown Lightwell spell caster {0}", m_caster.GetEntry());
                                            return;
                                    }

                                    // proc a spellcast
                                    Aura chargesAura = m_caster.GetAura(59907);
                                    if (chargesAura != null)
                                    {
                                        m_caster.CastSpell(unitTarget, spell_heal, true, null, null, m_caster.ToTempSummon().GetSummonerGUID());
                                        if (chargesAura.ModCharges(-1))
                                            m_caster.ToTempSummon().UnSummon();
                                    }

                                    return;
                                }
                            // Stoneclaw Totem
                            case 55328: // Rank 1
                            case 55329: // Rank 2
                            case 55330: // Rank 3
                            case 55332: // Rank 4
                            case 55333: // Rank 5
                            case 55335: // Rank 6
                            case 55278: // Rank 7
                            case 58589: // Rank 8
                            case 58590: // Rank 9
                            case 58591: // Rank 10
                                {
                                    int basepoints0 = damage;
                                    // Cast Absorb on totems
                                    for (byte slot = (int)SummonSlot.Totem; slot < SharedConst.MaxTotemSlot; ++slot)
                                    {
                                        if (unitTarget.m_SummonSlot[slot].IsEmpty())
                                            continue;

                                        Creature totem = unitTarget.GetMap().GetCreature(unitTarget.m_SummonSlot[slot]);
                                        if (totem != null && totem.IsTotem())
                                        {
                                            m_caster.CastCustomSpell(totem, 55277, basepoints0, 0, 0, true);
                                        }
                                    }
                                    // Glyph of Stoneclaw Totem
                                    AuraEffect aur = unitTarget.GetAuraEffect(63298, 0);
                                    if (aur != null)
                                    {
                                        basepoints0 *= aur.GetAmount();
                                        m_caster.CastCustomSpell(unitTarget, 55277, basepoints0, 0, 0, true);
                                    }
                                    break;
                                }
                            case 45668:                                 // Ultra-Advanced Proto-Typical Shortening Blaster
                                {
                                    if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Unit))
                                        return;

                                    if (RandomHelper.randChance(50))                  // chance unknown, using 50
                                        return;

                                    uint[] spellPlayer = new uint[5]
                                    {
                                        45674,                            // Bigger!
                                        45675,                            // Shrunk
                                        45678,                            // Yellow
                                        45682,                            // Ghost
                                        45684                             // Polymorph
                                    };

                                    uint[] spellTarget = new uint[5]
                                    {
                                        45673,                            // Bigger!
                                        45672,                            // Shrunk
                                        45677,                            // Yellow
                                        45681,                            // Ghost
                                        45683                             // Polymorph
                                    };

                                    m_caster.CastSpell(m_caster, spellPlayer[RandomHelper.IRand(0, 4)], true);
                                    unitTarget.CastSpell(unitTarget, spellTarget[RandomHelper.IRand(0, 4)], true);
                                    break;
                                }
                        }
                        break;
                    }
                case SpellFamilyNames.Paladin:
                    {
                        // Judgement (seal trigger)
                        if (m_spellInfo.GetCategory() == (int)SpellCategories.Judgement)
                        {
                            if (unitTarget == null || !unitTarget.IsAlive())
                                return;
                            uint spellId1 = 0;
                            uint spellId2 = 0;

                            // Judgement self add switch
                            switch (m_spellInfo.Id)
                            {
                                case 53407: spellId1 = 20184; break;    // Judgement of Justice
                                case 20271:                             // Judgement of Light
                                case 57774: spellId1 = 20185; break;    // Judgement of Light
                                case 53408: spellId1 = 20186; break;    // Judgement of Wisdom
                                default:
                                    Log.outError(LogFilter.Spells, "Unsupported Judgement (seal trigger) spell (Id: {0}) in Spell.EffectScriptEffect", m_spellInfo.Id);
                                    return;
                            }
                            // all seals have aura dummy in 2 effect
                            foreach (var app in m_caster.GetAppliedAuras())
                            {
                                Aura aura = app.Value.GetBase();
                                if (aura.GetSpellInfo().GetSpellSpecific() == SpellSpecificType.Seal)
                                {
                                    AuraEffect aureff = aura.GetEffect(2);
                                    if (aureff != null)
                                        if (aureff.GetAuraType() == AuraType.Dummy)
                                        {
                                            if (Global.SpellMgr.GetSpellInfo((uint)aureff.GetAmount()) != null)
                                                spellId2 = (uint)aureff.GetAmount();
                                            break;
                                        }
                                    if (spellId2 == 0)
                                    {
                                        switch (app.Key)
                                        {
                                            // Seal of light, Seal of wisdom, Seal of justice
                                            case 20165:
                                            case 20166:
                                            case 20164:
                                                spellId2 = 54158;
                                                break;
                                        }
                                    }
                                    break;
                                }
                            }
                            if (spellId1 != 0)
                                m_caster.CastSpell(unitTarget, spellId1, true);
                            if (spellId2 != 0)
                                m_caster.CastSpell(unitTarget, spellId2, true);
                            return;
                        }
                        break;
                    }
            }

            // normal DB scripted effect
            Log.outDebug(LogFilter.Spells, "Spell ScriptStart spellid {0} in EffectScriptEffect({1})", m_spellInfo.Id, effIndex);
            m_caster.GetMap().ScriptsStart(ScriptsType.Spell, (uint)((int)m_spellInfo.Id | (int)(effIndex << 24)), m_caster, unitTarget);
        }

        [SpellEffectHandler(SpellEffectName.Sanctuary)]
        [SpellEffectHandler(SpellEffectName.Sanctuary2)]
        void EffectSanctuary(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null)
                return;

            unitTarget.getHostileRefManager().UpdateVisibility();

            var attackers = unitTarget.getAttackers();
            foreach (var unit in attackers)
            {
                if (!unit.CanSeeOrDetect(unitTarget))
                    unit.AttackStop();
            }

            unitTarget.m_lastSanctuaryTime = Time.GetMSTime();

            // Vanish allows to remove all threat and cast regular stealth so other spells can be used
            if (m_caster.IsTypeId(TypeId.Player)
                && m_spellInfo.SpellFamilyName == SpellFamilyNames.Rogue
                && m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x00000800u))
            {
                m_caster.ToPlayer().RemoveAurasByType(AuraType.ModRoot);
                m_caster.ToPlayer().RemoveAurasByType(AuraType.ModRoot2);
                // Overkill
                if (m_caster.ToPlayer().HasSpell(58426))
                    m_caster.CastSpell(m_caster, 58427, true);
            }
        }

        [SpellEffectHandler(SpellEffectName.AddComboPoints)]
        void EffectAddComboPoints(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null)
                return;

            if (m_caster.m_playerMovingMe == null)
                return;

            if (damage <= 0)
                return;

            m_caster.m_playerMovingMe.AddComboPoints((sbyte)damage, this);
        }

        [SpellEffectHandler(SpellEffectName.Duel)]
        void EffectDuel(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !m_caster.IsTypeId(TypeId.Player) || !unitTarget.IsTypeId(TypeId.Player))
                return;
            Player caster = m_caster.ToPlayer();
            Player target = unitTarget.ToPlayer();

            // caster or target already have requested duel
            if (caster.duel != null || target.duel != null || target.GetSocial() == null || target.GetSocial().HasIgnore(caster.GetGUID()))
                return;

            // Players can only fight a duel in zones with this flag
            AreaTableRecord casterAreaEntry = CliDB.AreaTableStorage.LookupByKey(caster.GetAreaId());
            if (casterAreaEntry != null && !casterAreaEntry.Flags[0].HasAnyFlag(AreaFlags.AllowDuels))
            {
                SendCastResult(SpellCastResult.NoDueling);            // Dueling isn't allowed here
                return;
            }

            AreaTableRecord targetAreaEntry = CliDB.AreaTableStorage.LookupByKey(target.GetAreaId());
            if (targetAreaEntry != null && !targetAreaEntry.Flags[0].HasAnyFlag(AreaFlags.AllowDuels))
            {
                SendCastResult(SpellCastResult.NoDueling);            // Dueling isn't allowed here
                return;
            }

            //CREATE DUEL FLAG OBJECT
            GameObject pGameObj = new GameObject();

            int gameobject_id = effectInfo.MiscValue;

            Position pos = new Position()
            {
                posX = m_caster.GetPositionX() + (unitTarget.GetPositionX() - m_caster.GetPositionX()) / 2,
                posY = m_caster.GetPositionY() + (unitTarget.GetPositionY() - m_caster.GetPositionY()) / 2,
                posZ = m_caster.GetPositionZ(),
                Orientation = m_caster.GetOrientation()
            };

            Map map = m_caster.GetMap();
            Quaternion rotation = new Quaternion(Matrix3.fromEulerAnglesZYX(pos.GetOrientation(), 0.0f, 0.0f));
            if (!pGameObj.Create((uint)gameobject_id, map, m_caster.GetPhaseMask(), pos, rotation, 0, GameObjectState.Ready))
                return;

            pGameObj.CopyPhaseFrom(m_caster);

            pGameObj.SetUInt32Value(GameObjectFields.Faction, m_caster.getFaction());
            pGameObj.SetUInt32Value(GameObjectFields.Level, m_caster.getLevel() + 1);
            int duration = m_spellInfo.CalcDuration(m_caster);
            pGameObj.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0);
            pGameObj.SetSpellId(m_spellInfo.Id);

            ExecuteLogEffectSummonObject(effIndex, pGameObj);

            m_caster.AddGameObject(pGameObj);
            map.AddToMap(pGameObj);
            //END

            // Send request
            DuelRequested packet = new DuelRequested();
            packet.ArbiterGUID = pGameObj.GetGUID();
            packet.RequestedByGUID = caster.GetGUID();
            packet.RequestedByWowAccount = caster.GetSession().GetAccountGUID();
            packet.Write();

            caster.SendPacket(packet, false);
            target.SendPacket(packet, false);

            // create duel-info
            DuelInfo duel = new DuelInfo();
            duel.initiator = caster;
            duel.opponent = target;
            duel.startTime = 0;
            duel.startTimer = 0;
            duel.isMounted = (GetSpellInfo().Id == 62875); // Mounted Duel
            caster.duel = duel;

            DuelInfo duel2 = new DuelInfo();
            duel2.initiator = caster;
            duel2.opponent = caster;
            duel2.startTime = 0;
            duel2.startTimer = 0;
            duel2.isMounted = (GetSpellInfo().Id == 62875); // Mounted Duel
            target.duel = duel2;

            caster.SetGuidValue(PlayerFields.DuelArbiter, pGameObj.GetGUID());
            target.SetGuidValue(PlayerFields.DuelArbiter, pGameObj.GetGUID());

            Global.ScriptMgr.OnPlayerDuelRequest(target, caster);
        }

        [SpellEffectHandler(SpellEffectName.Stuck)]
        void EffectStuck(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            if (!WorldConfig.GetBoolValue(WorldCfg.CastUnstuck))
                return;

            Player player = m_caster.ToPlayer();
            if (player == null)
                return;

            Log.outDebug(LogFilter.Spells, "Spell Effect: Stuck");
            Log.outInfo(LogFilter.Spells, "Player {0} (guid {1}) used auto-unstuck future at map {2} ({3}, {4}, {5})", 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() == 0)
                    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.GetHomebind(), TeleportToOptions.Spell);

            // Stuck spell trigger Hearthstone cooldown
            SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(8690);
            if (spellInfo == null)
                return;

            Spell spell = new Spell(player, spellInfo, TriggerCastFlags.FullMask);
            spell.SendSpellCooldown();
        }

        [SpellEffectHandler(SpellEffectName.SummonPlayer)]
        void EffectSummonPlayer(uint effIndex)
        {
            // workaround - this effect should not use target map
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            unitTarget.ToPlayer().SendSummonRequestFrom(m_caster);
        }

        [SpellEffectHandler(SpellEffectName.ActivateObject)]
        void EffectActivateObject(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (gameObjTarget == null)
                return;

            ScriptInfo activateCommand = new ScriptInfo();
            activateCommand.command = ScriptCommands.ActivateObject;

            // int unk = effectInfo.MiscValue; // This is set for EffectActivateObject spells; needs research

            gameObjTarget.GetMap().ScriptCommandStart(activateCommand, 0, m_caster, gameObjTarget);
        }

        [SpellEffectHandler(SpellEffectName.ApplyGlyph)]
        void EffectApplyGlyph(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            Player player = m_caster.ToPlayer();
            if (player == null)
                return;

            List<uint> glyphs = player.GetGlyphs(player.GetActiveTalentGroup());
            int replacedGlyph = glyphs.Count;
            for (int i = 0; i < glyphs.Count; ++i)
            {
                List<uint> activeGlyphBindableSpells = Global.DB2Mgr.GetGlyphBindableSpells(glyphs[i]);
                if (activeGlyphBindableSpells.Contains(m_misc.SpellId))
                {
                    replacedGlyph = i;
                    player.RemoveAurasDueToSpell(CliDB.GlyphPropertiesStorage.LookupByKey(glyphs[i]).SpellID);
                    break;
                }
            }

            uint glyphId = (uint)effectInfo.MiscValue;
            if (replacedGlyph < glyphs.Count)
            {
                if (glyphId != 0)
                    glyphs[replacedGlyph] = glyphId;
                else
                    glyphs.RemoveAt(replacedGlyph);
            }
            else if (glyphId != 0)
                glyphs.Add(glyphId);

            GlyphPropertiesRecord glyphProperties = CliDB.GlyphPropertiesStorage.LookupByKey(glyphId);
            if (glyphProperties != null)
                player.CastSpell(player, glyphProperties.SpellID, true);

            ActiveGlyphs activeGlyphs = new ActiveGlyphs();
            activeGlyphs.Glyphs.Add(new GlyphBinding(m_misc.SpellId, (ushort)glyphId));
            activeGlyphs.IsFullUpdate = false;
            player.SendPacket(activeGlyphs);
        }

        [SpellEffectHandler(SpellEffectName.EnchantHeldItem)]
        void EffectEnchantHeldItem(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            // this is only item spell effect applied to main-hand weapon of target player (players in area)
            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            Player item_owner = unitTarget.ToPlayer();
            Item item = item_owner.GetItemByPos(InventorySlots.Bag0, EquipmentSlot.MainHand);

            if (item == null)
                return;

            // must be equipped
            if (!item.IsEquipped())
                return;

            if (effectInfo.MiscValue != 0)
            {
                uint enchant_id = (uint)effectInfo.MiscValue;
                int duration = m_spellInfo.GetDuration();          //Try duration index first ..
                if (duration == 0)
                    duration = damage;//+1;            //Base points after ..
                if (duration == 0)
                    duration = 10 * Time.InMilliseconds;                                  //10 seconds for enchants which don't have listed duration

                if (m_spellInfo.Id == 14792) // Venomhide Poison
                    duration = 5 * Time.Minute * Time.InMilliseconds;

                SpellItemEnchantmentRecord pEnchant = CliDB.SpellItemEnchantmentStorage.LookupByKey(enchant_id);
                if (pEnchant == null)
                    return;

                // Always go to temp enchantment slot
                EnchantmentSlot slot = EnchantmentSlot.Temp;

                // Enchantment will not be applied if a different one already exists
                if (item.GetEnchantmentId(slot) != 0 && item.GetEnchantmentId(slot) != enchant_id)
                    return;

                // Apply the temporary enchantment
                item.SetEnchantment(slot, enchant_id, (uint)duration, 0, m_caster.GetGUID());
                item_owner.ApplyEnchantment(item, slot, true);
            }
        }

        [SpellEffectHandler(SpellEffectName.Disenchant)]
        void EffectDisEnchant(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (itemTarget == null || itemTarget.GetTemplate().DisenchantID == 0)
                return;

            Player caster = m_caster.ToPlayer();
            if (caster != null)
            {
                caster.UpdateCraftSkill(m_spellInfo.Id);
                caster.SendLoot(itemTarget.GetGUID(), LootType.Disenchanting);
            }

            // item will be removed at disenchanting end
        }

        [SpellEffectHandler(SpellEffectName.Inebriate)]
        void EffectInebriate(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            Player player = unitTarget.ToPlayer();
            byte currentDrunk = player.GetDrunkValue();
            int drunkMod = damage;
            if (currentDrunk + drunkMod > 100)
            {
                currentDrunk = 100;
                if (RandomHelper.randChance() < 25.0f)
                    player.CastSpell(player, 67468, false);    // Drunken Vomit
            }
            else
                currentDrunk += (byte)drunkMod;

            player.SetDrunkValue(currentDrunk, m_CastItem != null ? m_CastItem.GetEntry() : 0);
        }

        [SpellEffectHandler(SpellEffectName.FeedPet)]
        void EffectFeedPet(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            Player player = m_caster.ToPlayer();
            if (player == null)
                return;

            Item foodItem = itemTarget;
            if (foodItem == null)
                return;

            Pet pet = player.GetPet();
            if (pet == null)
                return;

            if (!pet.IsAlive())
                return;

            int benefit = (int)pet.GetCurrentFoodBenefitLevel(foodItem.GetTemplate().GetBaseItemLevel());
            if (benefit <= 0)
                return;

            ExecuteLogEffectDestroyItem(effIndex, foodItem.GetEntry());

            uint count = 1;
            player.DestroyItemCount(foodItem, ref count, true);
            // @todo fix crash when a spell has two effects, both pointed at the same item target

            m_caster.CastCustomSpell(pet, effectInfo.TriggerSpell, benefit, 0, 0, true);
        }

        [SpellEffectHandler(SpellEffectName.DismissPet)]
        void EffectDismissPet(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsPet())
                return;

            Pet pet = unitTarget.ToPet();

            ExecuteLogEffectUnsummonObject(effIndex, pet);
            pet.GetOwner().RemovePet(pet, PetSaveMode.NotInSlot);
        }

        [SpellEffectHandler(SpellEffectName.SummonObjectSlot1)]
        void EffectSummonObject(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            uint go_id = (uint)effectInfo.MiscValue;
            byte slot = (byte)(effectInfo.Effect - SpellEffectName.SummonObjectSlot1);
            ObjectGuid guid = m_caster.m_ObjectSlot[slot];
            if (!guid.IsEmpty())
            {
                GameObject obj = m_caster.GetMap().GetGameObject(guid);
                if (obj != null)
                {
                    // 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);
                    m_caster.RemoveGameObject(obj, true);
                }
                m_caster.m_ObjectSlot[slot].Clear();
            }

            GameObject go = new GameObject();

            float x, y, z;
            // If dest location if present
            if (m_targets.HasDst())
                destTarget.GetPosition(out x, out y, out z);
            // Summon in random point all other units if location present
            else
                m_caster.GetClosePoint(out x, out y, out z, SharedConst.DefaultWorldObjectSize);

            Map map = m_caster.GetMap();
            Quaternion rotation = new Quaternion(Matrix3.fromEulerAnglesZYX(m_caster.GetOrientation(), 0.0f, 0.0f));
            if (!go.Create(go_id, map, m_caster.GetPhaseMask(), new Position(x, y, z, m_caster.GetOrientation()), rotation, 255, GameObjectState.Ready))
                return;

            go.CopyPhaseFrom(m_caster);

            int duration = m_spellInfo.CalcDuration(m_caster);
            go.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0);
            go.SetSpellId(m_spellInfo.Id);
            m_caster.AddGameObject(go);

            ExecuteLogEffectSummonObject(effIndex, go);

            map.AddToMap(go);

            m_caster.m_ObjectSlot[slot] = go.GetGUID();
        }

        [SpellEffectHandler(SpellEffectName.Resurrect)]
        void EffectResurrect(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            if (unitTarget.IsAlive() || !unitTarget.IsInWorld)
                return;

            Player target = unitTarget.ToPlayer();

            if (target.IsResurrectRequested())       // already have one active request
                return;

            uint health = (uint)target.CountPctFromMaxHealth(damage);
            uint mana = (uint)MathFunctions.CalculatePct(target.GetMaxPower(PowerType.Mana), damage);

            ExecuteLogEffectResurrect(effIndex, target);

            target.SetResurrectRequestData(m_caster, health, mana, 0);
            SendResurrectRequest(target);
        }

        [SpellEffectHandler(SpellEffectName.AddExtraAttacks)]
        void EffectAddExtraAttacks(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget || !unitTarget.IsAlive())
                return;

            if (unitTarget.m_extraAttacks != 0)
                return;

            unitTarget.m_extraAttacks = (uint)damage;

            ExecuteLogEffectExtraAttacks(effIndex, unitTarget, (uint)damage);
        }

        [SpellEffectHandler(SpellEffectName.Parry)]
        void EffectParry(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            if (m_caster.IsTypeId(TypeId.Player))
                m_caster.ToPlayer().SetCanParry(true);
        }

        [SpellEffectHandler(SpellEffectName.Block)]
        void EffectBlock(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            if (m_caster.IsTypeId(TypeId.Player))
                m_caster.ToPlayer().SetCanBlock(true);
        }

        [SpellEffectHandler(SpellEffectName.Leap)]
        void EffectLeap(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || unitTarget.IsInFlight())
                return;

            if (!m_targets.HasDst())
                return;

            Position pos = destTarget.GetPosition();
            pos = unitTarget.GetFirstCollisionPosition(unitTarget.GetDistance(pos.posX, pos.posY, pos.posZ + 2.0f), 0.0f);
            unitTarget.NearTeleportTo(pos.posX, pos.posY, pos.posZ, pos.Orientation, unitTarget == m_caster);
        }

        [SpellEffectHandler(SpellEffectName.Reputation)]
        void EffectReputation(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            Player player = unitTarget.ToPlayer();

            int repChange = damage;

            int factionId = effectInfo.MiscValue;

            FactionRecord factionEntry = CliDB.FactionStorage.LookupByKey(factionId);
            if (factionEntry == null)
                return;

            repChange = player.CalculateReputationGain(ReputationSource.Spell, 0, repChange, factionId);

            player.GetReputationMgr().ModifyReputation(factionEntry, repChange);
        }

        [SpellEffectHandler(SpellEffectName.QuestComplete)]
        void EffectQuestComplete(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;
            Player player = unitTarget.ToPlayer();

            uint questId = (uint)effectInfo.MiscValue;
            if (questId != 0)
            {
                Quest quest = Global.ObjectMgr.GetQuestTemplate(questId);
                if (quest == null)
                    return;

                ushort logSlot = player.FindQuestSlot(questId);
                if (logSlot < SharedConst.MaxQuestLogSize)
                    player.AreaExploredOrEventHappens(questId);
                else if (player.CanTakeQuest(quest, false))    // Check if the quest has already been turned in.
                    player.SetRewardedQuest(questId);          // If not, set status to rewarded without broadcasting it to client.
            }
        }

        [SpellEffectHandler(SpellEffectName.ForceDeselect)]
        void EffectForceDeselect(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            ClearTarget clearTarget = new ClearTarget();
            clearTarget.Guid = m_caster.GetGUID();
            m_caster.SendMessageToSet(clearTarget, true);
        }

        [SpellEffectHandler(SpellEffectName.SelfResurrect)]
        void EffectSelfResurrect(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            if (m_caster == null || m_caster.IsAlive())
                return;
            if (!m_caster.IsTypeId(TypeId.Player))
                return;
            if (!m_caster.IsInWorld)
                return;

            uint health = 0;
            int mana = 0;

            // flat case
            if (damage < 0)
            {
                health = (uint)-damage;
                mana = effectInfo.MiscValue;
            }
            // percent case
            else
            {
                health = (uint)m_caster.CountPctFromMaxHealth(damage);
                if (m_caster.GetMaxPower(PowerType.Mana) > 0)
                    mana = MathFunctions.CalculatePct(m_caster.GetMaxPower(PowerType.Mana), damage);
            }

            Player player = m_caster.ToPlayer();
            player.ResurrectPlayer(0.0f);

            player.SetHealth(health);
            player.SetPower(PowerType.Mana, mana);
            player.SetPower(PowerType.Rage, 0);
            player.SetPower(PowerType.Energy, player.GetMaxPower(PowerType.Energy));
            player.SetPower(PowerType.Focus, 0);

            player.SpawnCorpseBones();
        }

        [SpellEffectHandler(SpellEffectName.Skinning)]
        void EffectSkinning(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget.IsTypeId(TypeId.Unit))
                return;
            if (!m_caster.IsTypeId(TypeId.Player))
                return;

            Creature creature = unitTarget.ToCreature();
            int targetLevel = (int)creature.getLevel();

            SkillType skill = creature.GetCreatureTemplate().GetRequiredLootSkill();

            m_caster.ToPlayer().SendLoot(creature.GetGUID(), LootType.Skinning);
            creature.RemoveFlag(UnitFields.Flags, UnitFlags.Skinnable);
            creature.SetFlag(ObjectFields.DynamicFlags, UnitDynFlags.Lootable);

            uint reqValue = (uint)(targetLevel < 10 ? 0 : targetLevel < 20 ? (targetLevel - 10) * 10 : targetLevel * 5);

            uint skillValue = m_caster.ToPlayer().GetPureSkillValue(skill);

            // Double chances for elites
            m_caster.ToPlayer().UpdateGatherSkill(skill, skillValue, reqValue, (uint)(creature.isElite() ? 2 : 1));
        }

        [SpellEffectHandler(SpellEffectName.Charge)]
        void EffectCharge(uint effIndex)
        {
            if (unitTarget == null)
                return;

            if (effectHandleMode == SpellEffectHandleMode.LaunchTarget)
            {
                float speed = MathFunctions.fuzzyGt(m_spellInfo.Speed, 0.0f) ? m_spellInfo.Speed : MotionMaster.SPEED_CHARGE;
                SpellEffectExtraData spellEffectExtraData = null;
                if (effectInfo.MiscValueB != 0)
                {
                    spellEffectExtraData = new SpellEffectExtraData();
                    spellEffectExtraData.Target = unitTarget.GetGUID();
                    spellEffectExtraData.SpellVisualId = (uint)effectInfo.MiscValueB;
                }
                // Spell is not using explicit target - no generated path
                if (m_preGeneratedPath.GetPathType() == PathType.Blank)
                {
                    Position pos = unitTarget.GetFirstCollisionPosition(unitTarget.GetObjectSize(), unitTarget.GetRelativeAngle(m_caster.GetPosition()));
                    m_caster.GetMotionMaster().MoveCharge(pos.posX, pos.posY, pos.posZ, speed, EventId.Charge, false, unitTarget, spellEffectExtraData);
                }
                else
                    m_caster.GetMotionMaster().MoveCharge(m_preGeneratedPath, speed, unitTarget, spellEffectExtraData);
            }

            if (effectHandleMode == SpellEffectHandleMode.HitTarget)
            {
                // not all charge effects used in negative spells
                if (!m_spellInfo.IsPositive() && m_caster.IsTypeId(TypeId.Player))
                    m_caster.Attack(unitTarget, true);

                if (effectInfo.TriggerSpell != 0)
                    m_caster.CastSpell(unitTarget, effectInfo.TriggerSpell, true, null, null, m_originalCasterGUID);
            }
        }

        [SpellEffectHandler(SpellEffectName.ChargeDest)]
        void EffectChargeDest(uint effIndex)
        {
            if (destTarget == null)
                return;

            if (effectHandleMode == SpellEffectHandleMode.Launch)
            {
                Position pos = destTarget.GetPosition();
                float angle = m_caster.GetRelativeAngle(pos.posX, pos.posY);
                float dist = m_caster.GetDistance(pos);
                pos = m_caster.GetFirstCollisionPosition(dist, angle);

                m_caster.GetMotionMaster().MoveCharge(pos.posX, pos.posY, pos.posZ);
            }
            else if (effectHandleMode == SpellEffectHandleMode.Hit)
            {
                if (effectInfo.TriggerSpell != 0)
                    m_caster.CastSpell(destTarget.GetPositionX(), destTarget.GetPositionY(), destTarget.GetPositionZ(), effectInfo.TriggerSpell, true, null, null, m_originalCasterGUID);
            }
        }

        [SpellEffectHandler(SpellEffectName.KnockBack)]
        [SpellEffectHandler(SpellEffectName.KnockBackDest)]
        void EffectKnockBack(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget)
                return;

            Creature creatureTarget = unitTarget.ToCreature();
            if (creatureTarget)
                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(UnitState.Root | UnitState.Stunned))
                return;

            // Instantly interrupt non melee spells being casted
            if (unitTarget.IsNonMeleeSpellCast(true))
                unitTarget.InterruptNonMeleeSpells(true);

            float ratio = 0.1f;
            float speedxy = effectInfo.MiscValue * ratio;
            float speedz = damage * ratio;
            if (speedxy < 0.1f && speedz < 0.1f)
                return;

            float x, y;
            if (effectInfo.Effect == SpellEffectName.KnockBackDest)
            {
                if (m_targets.HasDst())
                    destTarget.GetPosition(out x, out y);
                else
                    return;
            }
            else //if (GetEffect(i].Effect == SPELL_EFFECT_KNOCK_BACK)
            {
                m_caster.GetPosition(out x, out y);
            }

            unitTarget.KnockbackFrom(x, y, speedxy, speedz);
        }

        [SpellEffectHandler(SpellEffectName.LeapBack)]
        void EffectLeapBack(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.LaunchTarget)
                return;

            if (unitTarget == null)
                return;

            float speedxy = effectInfo.MiscValue / 10.0f;
            float speedz = damage / 10.0f;
            // Disengage
            unitTarget.JumpTo(speedxy, speedz, m_spellInfo.IconFileDataId != 132572);
        }

        [SpellEffectHandler(SpellEffectName.ClearQuest)]
        void EffectQuestClear(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;
            Player player = unitTarget.ToPlayer();

            uint quest_id = (uint)effectInfo.MiscValue;

            Quest quest = Global.ObjectMgr.GetQuestTemplate(quest_id);

            if (quest == null)
                return;

            // Player has never done this quest
            if (player.GetQuestStatus(quest_id) == QuestStatus.None)
                return;

            // remove all quest entries for 'entry' from quest log
            for (byte slot = 0; slot < SharedConst.MaxQuestLogSize; ++slot)
            {
                uint 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(QuestFlags.Pvp))
                    {
                        player.pvpInfo.IsHostile = player.pvpInfo.IsInHostileArea || player.HasPvPForcingQuest();
                        player.UpdatePvPState();
                    }
                }
            }

            player.RemoveActiveQuest(quest_id, false);
            player.RemoveRewardedQuest(quest_id);

            Global.ScriptMgr.OnQuestStatusChange(player, quest_id);
        }

        [SpellEffectHandler(SpellEffectName.SendTaxi)]
        void EffectSendTaxi(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            unitTarget.ToPlayer().ActivateTaxiPathTo((uint)effectInfo.MiscValue, m_spellInfo.Id);
        }

        [SpellEffectHandler(SpellEffectName.PullTowards)]
        [SpellEffectHandler(SpellEffectName.PullTowardsDest)]
        void EffectPullTowards(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget)
                return;

            Position pos = new Position();
            if (effectInfo.Effect == SpellEffectName.PullTowardsDest)
            {
                if (m_targets.HasDst())
                    pos.Relocate(destTarget);
                else
                    return;
            }
            else
            {
                pos.Relocate(m_caster.GetPosition());
            }

            float speedXY = effectInfo.MiscValue * 0.1f;
            float speedZ = (float)(unitTarget.GetDistance(pos) / speedXY * 0.5f * MotionMaster.gravity);

            unitTarget.GetMotionMaster().MoveJump(pos, speedXY, speedZ);
        }

        [SpellEffectHandler(SpellEffectName.ChangeRaidMarker)]
        void EffectChangeRaidMarker(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.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(out x, out y, out z);

            group.AddRaidMarker((byte)damage, player.GetMapId(), x, y, z);
        }

        [SpellEffectHandler(SpellEffectName.DispelMechanic)]
        void EffectDispelMechanic(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null)
                return;

            int mechanic = effectInfo.MiscValue;

            List<KeyValuePair<uint, ObjectGuid>> dispel_list = new List<KeyValuePair<uint, ObjectGuid>>();

            var auras = unitTarget.GetOwnedAuras();
            foreach (var pair in auras)
            {
                Aura aura = pair.Value;
                if (aura.GetApplicationOfTarget(unitTarget.GetGUID()) == null)
                    continue;

                if (RandomHelper.randChance(aura.CalcDispelChance(unitTarget, !unitTarget.IsFriendlyTo(m_caster))))
                    if (Convert.ToBoolean(aura.GetSpellInfo().GetAllEffectsMechanicMask() & (1 << mechanic)))
                        dispel_list.Add(new KeyValuePair<uint, ObjectGuid>(aura.GetId(), aura.GetCasterGUID()));
            }

            while (!dispel_list.Empty())
            {
                unitTarget.RemoveAura(dispel_list[0].Key, dispel_list[0].Value, 0, AuraRemoveMode.EnemySpell);
                dispel_list.RemoveAt(0);
            }
        }

        [SpellEffectHandler(SpellEffectName.ResurrectPet)]
        void EffectSummonDeadPet(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            Player player = m_caster.ToPlayer();
            if (player == null)
                return;

            Pet pet = player.GetPet();
            if (pet != null && pet.IsAlive())
                return;

            if (damage < 0)
                return;

            float x, y, z;
            player.GetPosition(out x, out y, out z);
            if (pet == null)
            {
                player.SummonPet(0, x, y, z, player.Orientation, PetType.Summon, 0);
                pet = player.GetPet();
            }
            if (pet == null)
                return;

            player.GetMap().CreatureRelocation(pet, x, y, z, player.GetOrientation());

            pet.SetUInt32Value(ObjectFields.DynamicFlags, (uint)UnitDynFlags.HideModel);
            pet.RemoveFlag(UnitFields.Flags, UnitFlags.Skinnable);
            pet.setDeathState(DeathState.Alive);
            pet.ClearUnitState(UnitState.AllState);
            pet.SetHealth(pet.CountPctFromMaxHealth(damage));

            pet.InitializeAI();
            player.PetSpellInitialize();
            pet.SavePetToDB(PetSaveMode.AsCurrent);
        }

        [SpellEffectHandler(SpellEffectName.DestroyAllTotems)]
        void EffectDestroyAllTotems(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            int mana = 0;
            for (byte slot = (int)SummonSlot.Totem; slot < SharedConst.MaxTotemSlot; ++slot)
            {
                if (m_caster.m_SummonSlot[slot].IsEmpty())
                    continue;

                Creature totem = m_caster.GetMap().GetCreature(m_caster.m_SummonSlot[slot]);
                if (totem != null && totem.IsTotem())
                {
                    uint spell_id = totem.GetUInt32Value(UnitFields.CreatedBySpell);
                    SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spell_id);
                    if (spellInfo != null)
                    {
                        var costs = spellInfo.CalcPowerCost(m_caster, spellInfo.GetSchoolMask());
                        var m = costs.Find(cost => cost.Power == PowerType.Mana);
                        if (m != null)
                            mana += m.Amount;
                    }
                    totem.ToTotem().UnSummon();
                }
            }
            MathFunctions.ApplyPct(ref mana, damage);
            if (mana != 0)
                m_caster.CastCustomSpell(m_caster, 39104, mana, 0, 0, true);
        }

        [SpellEffectHandler(SpellEffectName.DurabilityDamage)]
        void EffectDurabilityDamage(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            int slot = effectInfo.MiscValue;

            // -1 means all player equipped items and -2 all items
            if (slot < 0)
            {
                unitTarget.ToPlayer().DurabilityPointsLossAll(damage, (slot < -1));
                ExecuteLogEffectDurabilityDamage(effIndex, unitTarget, -1, -1);
                return;
            }

            // invalid slot value
            if (slot >= InventorySlots.BagEnd)
                return;

            Item item = unitTarget.ToPlayer().GetItemByPos(InventorySlots.Bag0, (byte)slot);
            if (item != null)
            {
                unitTarget.ToPlayer().DurabilityPointsLoss(item, damage);
                ExecuteLogEffectDurabilityDamage(effIndex, unitTarget, (int)item.GetEntry(), slot);
            }
        }

        [SpellEffectHandler(SpellEffectName.DurabilityDamagePct)]
        void EffectDurabilityDamagePCT(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            int 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(damage / 100.0f, (slot < -1));
                return;
            }

            // invalid slot value
            if (slot >= InventorySlots.BagEnd)
                return;

            if (damage <= 0)
                return;

            Item item = unitTarget.ToPlayer().GetItemByPos(InventorySlots.Bag0, (byte)slot);
            if (item != null)
                unitTarget.ToPlayer().DurabilityLoss(item, damage / 100.0f);
        }

        [SpellEffectHandler(SpellEffectName.ModifyThreatPercent)]
        void EffectModifyThreatPercent(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null)
                return;

            unitTarget.GetThreatManager().modifyThreatPercent(m_caster, damage);
        }

        [SpellEffectHandler(SpellEffectName.TransDoor)]
        void EffectTransmitted(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            uint name_id = (uint)effectInfo.MiscValue;

            var overrideSummonedGameObjects = m_caster.GetAuraEffectsByType(AuraType.OverrideSummonedObject);
            foreach (AuraEffect aurEff in overrideSummonedGameObjects)
            {
                if (aurEff.GetMiscValue() == name_id)
                {
                    name_id = (uint)aurEff.GetMiscValueB();
                    break;
                }
            }

            GameObjectTemplate goinfo = Global.ObjectMgr.GetGameObjectTemplate(name_id);
            if (goinfo == null)
            {
                Log.outError(LogFilter.Sql, "Gameobject (Entry: {0}) not exist and not created at spell (ID: {1}) cast", name_id, m_spellInfo.Id);
                return;
            }

            float fx, fy, fz;

            if (m_targets.HasDst())
                destTarget.GetPosition(out fx, out fy, out fz);
            //FIXME: this can be better check for most objects but still hack
            else if (effectInfo.HasRadius() && m_spellInfo.Speed == 0)
            {
                float dis = effectInfo.CalcRadius(m_originalCaster);
                m_caster.GetClosePoint(out fx, out fy, out fz, SharedConst.DefaultWorldObjectSize, dis);
            }
            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)RandomHelper.NextDouble() * (max_dis - min_dis) + min_dis;

                m_caster.GetClosePoint(out fx, out fy, out fz, SharedConst.DefaultWorldObjectSize, dis);
            }

            Map cMap = m_caster.GetMap();
            // if gameobject is summoning object, it should be spawned right on caster's position
            if (goinfo.type == GameObjectTypes.Ritual)
                m_caster.GetPosition(out fx, out fy, out fz);

            GameObject pGameObj = new GameObject();

            Position pos = new Position(fx, fy, fz, m_caster.GetOrientation());
            Quaternion rotation = new Quaternion(Matrix3.fromEulerAnglesZYX(m_caster.GetOrientation(), 0.0f, 0.0f));
            if (!pGameObj.Create(name_id, cMap, m_caster.GetPhaseMask(), pos, rotation, 255, GameObjectState.Ready))
                return;

            pGameObj.CopyPhaseFrom(m_caster);

            int duration = m_spellInfo.CalcDuration(m_caster);

            switch (goinfo.type)
            {
                case GameObjectTypes.FishingNode:
                    {
                        m_caster.AddChannelObject(pGameObj.GetGUID());
                        m_caster.AddGameObject(pGameObj);              // will removed at spell cancel

                        // end time of range when possible catch fish (FISHING_BOBBER_READY_TIME..GetDuration(m_spellInfo))
                        // start time == fish-FISHING_BOBBER_READY_TIME (0..GetDuration(m_spellInfo)-FISHING_BOBBER_READY_TIME)
                        int lastSec = 0;
                        switch (RandomHelper.IRand(0, 3))
                        {
                            case 0: lastSec = 3; break;
                            case 1: lastSec = 7; break;
                            case 2: lastSec = 13; break;
                            case 3: lastSec = 17; break;
                        }

                        duration = duration - lastSec * Time.InMilliseconds + 5 * Time.InMilliseconds;
                        break;
                    }
                case GameObjectTypes.Ritual:
                    {
                        if (m_caster.IsTypeId(TypeId.Player))
                        {
                            pGameObj.AddUniqueUse(m_caster.ToPlayer());
                            m_caster.AddGameObject(pGameObj);      // will be removed at spell cancel
                        }
                        break;
                    }
                case GameObjectTypes.DuelArbiter: // 52991
                    m_caster.AddGameObject(pGameObj);
                    break;
                case GameObjectTypes.FishingHole:
                case GameObjectTypes.Chest:
                default:
                    break;
            }

            pGameObj.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0);
            pGameObj.SetOwnerGUID(m_caster.GetGUID());
            pGameObj.SetSpellId(m_spellInfo.Id);

            ExecuteLogEffectSummonObject(effIndex, pGameObj);

            Log.outDebug(LogFilter.Spells, "AddObject at SpellEfects.cpp EffectTransmitted");

            cMap.AddToMap(pGameObj);
            uint linkedEntry = pGameObj.GetGoInfo().GetLinkedGameObjectEntry();
            if (linkedEntry != 0)
            {
                GameObject linkedGO = new GameObject();
                if (linkedGO.Create(linkedEntry, cMap, m_caster.GetPhaseMask(), pos, rotation, 255, GameObjectState.Ready))
                {
                    linkedGO.CopyPhaseFrom(m_caster);

                    linkedGO.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0);
                    linkedGO.SetSpellId(m_spellInfo.Id);
                    linkedGO.SetOwnerGUID(m_caster.GetGUID());
                    ExecuteLogEffectSummonObject(effIndex, linkedGO);

                    linkedGO.GetMap().AddToMap(linkedGO);
                }
                else
                {
                    linkedGO = null;
                    return;
                }
            }
        }

        [SpellEffectHandler(SpellEffectName.Prospecting)]
        void EffectProspecting(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            Player player = m_caster.ToPlayer();
            if (player == null)
                return;

            if (itemTarget == null || !itemTarget.GetTemplate().GetFlags().HasAnyFlag(ItemFlags.IsProspectable))
                return;

            if (itemTarget.GetCount() < 5)
                return;

            if (WorldConfig.GetBoolValue(WorldCfg.SkillProspecting))
            {
                uint SkillValue = player.GetPureSkillValue(SkillType.Jewelcrafting);
                uint reqSkillValue = itemTarget.GetTemplate().GetRequiredSkillRank();
                player.UpdateGatherSkill(SkillType.Jewelcrafting, SkillValue, reqSkillValue);
            }

            player.SendLoot(itemTarget.GetGUID(), LootType.Prospecting);
        }

        [SpellEffectHandler(SpellEffectName.Milling)]
        void EffectMilling(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            Player player = m_caster.ToPlayer();
            if (player == null)
                return;

            if (itemTarget == null || !itemTarget.GetTemplate().GetFlags().HasAnyFlag(ItemFlags.IsMillable))
                return;

            if (itemTarget.GetCount() < 5)
                return;

            if (WorldConfig.GetBoolValue(WorldCfg.SkillMilling))
            {
                uint SkillValue = player.GetPureSkillValue(SkillType.Inscription);
                uint reqSkillValue = itemTarget.GetTemplate().GetRequiredSkillRank();
                player.UpdateGatherSkill(SkillType.Inscription, SkillValue, reqSkillValue);
            }

            player.SendLoot(itemTarget.GetGUID(), LootType.Milling);
        }

        [SpellEffectHandler(SpellEffectName.Skill)]
        void EffectSkill(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            Log.outDebug(LogFilter.Spells, "WORLD: SkillEFFECT");
        }

        /* There is currently no need for this effect. We handle it in Battleground.cpp
           If we would handle the resurrection here, the spiritguide would instantly disappear as the
           player revives, and so we wouldn't see the spirit heal visual effect on the npc.
           This is why we use a half sec delay between the visual effect and the resurrection itself */
        void EffectSpiritHeal(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;
        }

        // remove insignia spell effect
        [SpellEffectHandler(SpellEffectName.SkinPlayerCorpse)]
        void EffectSkinPlayerCorpse(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            Log.outDebug(LogFilter.Spells, "Effect: SkinPlayerCorpse");

            Player player = m_caster.ToPlayer();
            Player target = unitTarget.ToPlayer();
            if (player == null || target == null || target.IsAlive())
                return;

            target.RemovedInsignia(player);
        }

        [SpellEffectHandler(SpellEffectName.StealBeneficialBuff)]
        void EffectStealBeneficialBuff(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            Log.outDebug(LogFilter.Spells, "Effect: StealBeneficialBuff");

            if (unitTarget == null || unitTarget == m_caster)                 // can't steal from self
                return;

            List<Tuple<Aura, byte>> steal_list = new List<Tuple<Aura, byte>>();

            // Create dispel mask by dispel type
            uint dispelMask = SpellInfo.GetDispelMask((DispelType)effectInfo.MiscValue);
            var auras = unitTarget.GetOwnedAuras();
            foreach (var map in auras)
            {
                Aura aura = map.Value;
                AuraApplication aurApp = aura.GetApplicationOfTarget(unitTarget.GetGUID());
                if (aurApp == null)
                    continue;

                if (Convert.ToBoolean(aura.GetSpellInfo().GetDispelMask() & dispelMask))
                {
                    // Need check for passive? this
                    if (!aurApp.IsPositive() || aura.IsPassive() || aura.GetSpellInfo().HasAttribute(SpellAttr4.NotStealable))
                        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(SpellAttr7.DispelCharges);
                    byte charges = dispelCharges ? aura.GetCharges() : aura.GetStackAmount();
                    if (charges > 0)
                        steal_list.Add(Tuple.Create(aura, charges));
                }
            }

            if (steal_list.Empty())
                return;

            // Ok if exist some buffs for dispel try dispel it
            List<KeyValuePair<uint, ObjectGuid>> success_list = new List<KeyValuePair<uint, ObjectGuid>>();

            DispelFailed dispelFailed = new 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 (int count = 0; count < damage && !steal_list.Empty();)
            {
                // Random select buff for dispel
                var pair = steal_list[RandomHelper.IRand(0, steal_list.Count - 1)];

                int chance = pair.Item1.CalcDispelChance(unitTarget, !unitTarget.IsFriendlyTo(m_caster));
                // 2.4.3 Patch Notes: "Dispel effects will no longer attempt to remove effects that have 100% dispel resistance."
                if (chance == 0)
                {
                    steal_list.Remove(pair);
                    continue;
                }
                else
                {
                    if (RandomHelper.randChance(chance))
                    {
                        success_list.Add(new KeyValuePair<uint, ObjectGuid>(pair.Item1.GetId(), pair.Item1.GetCasterGUID()));
                        var temp = pair.Item2;
                        --temp;
                        pair = Tuple.Create(pair.Item1, temp);
                        if (pair.Item2 <= 0)
                            steal_list.Remove(pair);
                    }
                    else
                        dispelFailed.FailedSpells.Add(pair.Item1.GetId());

                    ++count;
                }
            }

            if (!dispelFailed.FailedSpells.Empty())
                m_caster.SendMessageToSet(dispelFailed, true);

            if (success_list.Empty())
                return;

            SpellDispellLog spellDispellLog = new SpellDispellLog();
            spellDispellLog.IsBreak = false; // TODO: use me
            spellDispellLog.IsSteal = true;

            spellDispellLog.TargetGUID = unitTarget.GetGUID();
            spellDispellLog.CasterGUID = m_caster.GetGUID();
            spellDispellLog.DispelledBySpellID = m_spellInfo.Id;

            foreach (var dispell in success_list)
            {
                var dispellData = new SpellDispellData();
                dispellData.SpellID = dispell.Key;
                dispellData.Harmful = false;      // TODO: use me
                //dispellData.Rolled = none;        // TODO: use me
                //dispellData.Needed = none;        // TODO: use me

                unitTarget.RemoveAurasDueToSpellBySteal(dispell.Key, dispell.Value, m_caster);

                spellDispellLog.DispellData.Add(dispellData);
            }
            m_caster.SendMessageToSet(spellDispellLog, true);
        }

        [SpellEffectHandler(SpellEffectName.KillCredit)]
        void EffectKillCreditPersonal(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            unitTarget.ToPlayer().KilledMonsterCredit((uint)effectInfo.MiscValue);
        }

        [SpellEffectHandler(SpellEffectName.KillCredit2)]
        void EffectKillCredit(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            int creatureEntry = effectInfo.MiscValue;
            if (creatureEntry == 0)
            {
                if (m_spellInfo.Id == 42793) // Burn Body
                    creatureEntry = 24008; // Fallen Combatant
            }

            if (creatureEntry != 0)
                unitTarget.ToPlayer().RewardPlayerAndGroupAtEvent((uint)creatureEntry, unitTarget);
        }

        [SpellEffectHandler(SpellEffectName.QuestFail)]
        void EffectQuestFail(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            unitTarget.ToPlayer().FailQuest((uint)effectInfo.MiscValue);
        }

        [SpellEffectHandler(SpellEffectName.QuestStart)]
        void EffectQuestStart(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget)
                return;

            Player player = unitTarget.ToPlayer();
            if (!player)
                return;

            Quest qInfo = Global.ObjectMgr.GetQuestTemplate((uint)effectInfo.MiscValue);
            if (qInfo != null)
            {
                if (!player.CanTakeQuest(qInfo, false))
                    return;

                if (qInfo.IsAutoAccept() && player.CanAddQuest(qInfo, false))
                    player.AddQuestAndCheckCompletion(qInfo, null);

                player.PlayerTalkClass.SendQuestGiverQuestDetails(qInfo, player.GetGUID(), true);
            }
        }

        [SpellEffectHandler(SpellEffectName.ActivateRune)]
        void EffectActivateRune(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Launch)
                return;

            if (!m_caster.IsTypeId(TypeId.Player))
                return;

            Player player = m_caster.ToPlayer();

            if (player.GetClass() != Class.Deathknight)
                return;

            // needed later
            m_runesState = m_caster.ToPlayer().GetRunesState();

            uint count = (uint)damage;
            if (count == 0)
                count = 1;

            // first restore fully depleted runes
            for (byte j = 0; j < player.GetMaxPower(PowerType.Runes) && count > 0; ++j)
            {
                if (player.GetRuneCooldown(j) == player.GetRuneBaseCooldown())
                {
                    player.SetRuneCooldown(j, 0);
                    --count;
                }
            }

            // then the rest if we still got something left
            for (byte j = 0; j < player.GetMaxPower(PowerType.Runes) && count > 0; ++j)
            {
                player.SetRuneCooldown(j, 0);
                --count;
            }
        }

        [SpellEffectHandler(SpellEffectName.CreateTamedPet)]
        void EffectCreateTamedPet(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player) || !unitTarget.GetPetGUID().IsEmpty() || unitTarget.GetClass() != Class.Hunter)
                return;

            uint creatureEntry = (uint)effectInfo.MiscValue;
            Pet pet = unitTarget.CreateTamedPetFrom(creatureEntry, m_spellInfo.Id);
            if (pet == null)
                return;

            // relocate
            float px, py, pz;
            unitTarget.GetClosePoint(out px, out py, out pz, pet.GetObjectSize(), SharedConst.PetFollowDist, 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.IsTypeId(TypeId.Player))
            {
                pet.SavePetToDB(PetSaveMode.AsCurrent);
                unitTarget.ToPlayer().PetSpellInitialize();
            }
        }

        [SpellEffectHandler(SpellEffectName.DiscoverTaxi)]
        void EffectDiscoverTaxi(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;
            uint nodeid = (uint)effectInfo.MiscValue;
            if (CliDB.TaxiNodesStorage.ContainsKey(nodeid))
                unitTarget.ToPlayer().GetSession().SendDiscoverNewTaxiNode(nodeid);
        }

        [SpellEffectHandler(SpellEffectName.TitanGrip)]
        void EffectTitanGrip(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            if (m_caster.IsTypeId(TypeId.Player))
                m_caster.ToPlayer().SetCanTitanGrip(true, (uint)effectInfo.MiscValue);
        }

        [SpellEffectHandler(SpellEffectName.RedirectThreat)]
        void EffectRedirectThreat(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget != null)
                m_caster.SetRedirectThreat(unitTarget.GetGUID(), (uint)damage);
        }

        [SpellEffectHandler(SpellEffectName.GameObjectDamage)]
        void EffectGameObjectDamage(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (gameObjTarget == null)
                return;

            Unit caster = m_originalCaster;
            if (caster == null)
                return;

            FactionTemplateRecord casterFaction = caster.GetFactionTemplateEntry();
            FactionTemplateRecord targetFaction = CliDB.FactionTemplateStorage.LookupByKey(gameObjTarget.GetUInt32Value(GameObjectFields.Faction));
            // Do not allow to damage GO's of friendly factions (ie: Wintergrasp Walls/Ulduar Storm Beacons)
            if (targetFaction == null || (casterFaction != null && targetFaction != null && !casterFaction.IsFriendlyTo(targetFaction)))
                gameObjTarget.ModifyHealth(-damage, caster, GetSpellInfo().Id);
        }

        [SpellEffectHandler(SpellEffectName.GameobjectRepair)]
        void EffectGameObjectRepair(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (gameObjTarget == null)
                return;

            gameObjTarget.ModifyHealth(damage, m_caster);
        }

        [SpellEffectHandler(SpellEffectName.GameobjectSetDestructionState)]
        void EffectGameObjectSetDestructionState(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (gameObjTarget == null || m_originalCaster == null)
                return;

            Player player = m_originalCaster.GetCharmerOrOwnerPlayerOrPlayerItself();
            gameObjTarget.SetDestructibleState((GameObjectDestructibleState)effectInfo.MiscValue, player, true);
        }

        void SummonGuardian(uint i, uint entry, SummonPropertiesRecord properties, uint numGuardians)
        {
            Unit caster = m_originalCaster;
            if (caster == null)
                return;

            if (caster.IsTotem())
                caster = caster.ToTotem().GetOwner();

            // in another case summon new
            uint level = caster.getLevel();

            // level of pet summoned using engineering item based at engineering skill level
            if (m_CastItem != null && caster.IsTypeId(TypeId.Player))
            {
                ItemTemplate proto = m_CastItem.GetTemplate();
                if (proto != null)
                    if (proto.GetRequiredSkill() == (uint)SkillType.Engineering)
                    {
                        ushort skill202 = caster.ToPlayer().GetSkillValue(SkillType.Engineering);
                        if (skill202 != 0)
                            level = (uint)(skill202 / 5);
                    }
            }

            float radius = 5.0f;
            int duration = m_spellInfo.CalcDuration(m_originalCaster);

            TempSummonType summonType = (duration == 0) ? TempSummonType.DeadDespawn : TempSummonType.TimedDespawn;
            Map map = caster.GetMap();

            for (uint count = 0; count < numGuardians; ++count)
            {
                Position pos = new Position();
                if (count == 0)
                    pos = destTarget;
                else
                    // randomize position for multiple summons
                    m_caster.GetRandomPoint(destTarget, radius, out pos);

                TempSummon summon = map.SummonCreature(entry, pos, properties, (uint)duration, caster, m_spellInfo.Id);
                if (summon == null)
                    return;
                if (summon.HasUnitTypeMask(UnitTypeMask.Guardian))
                    ((Guardian)summon).InitStatsForLevel(level);

                if (properties != null && properties.Category == SummonCategory.Ally)
                    summon.SetFaction(caster.getFaction());

                if (summon.HasUnitTypeMask(UnitTypeMask.Minion) && m_targets.HasDst())
                    ((Minion)summon).SetFollowAngle(m_caster.GetAngle(summon.GetPosition()));

                if (summon.GetEntry() == 27893)
                {
                    uint weapon = m_caster.GetUInt32Value(PlayerFields.VisibleItem + (EquipmentSlot.MainHand * 2));
                    if (weapon != 0)
                    {
                        summon.SetDisplayId(11686);
                        summon.SetVirtualItem(0, weapon);
                    }
                    else
                        summon.SetDisplayId(1126);
                }

                summon.GetAI().EnterEvadeMode();

                ExecuteLogEffectSummonObject(i, summon);
            }
        }

        [SpellEffectHandler(SpellEffectName.AllowRenamePet)]
        void EffectRenamePet(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Unit) ||
                !unitTarget.IsPet() || unitTarget.ToPet().getPetType() != PetType.Hunter)
                return;

            unitTarget.SetByteFlag(UnitFields.Bytes2, UnitBytes2Offsets.PetFlags, UnitPetFlags.CanBeRenamed);
        }

        [SpellEffectHandler(SpellEffectName.PlayMusic)]
        void EffectPlayMusic(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            uint soundid = (uint)effectInfo.MiscValue;

            if (!CliDB.SoundKitStorage.ContainsKey(soundid))
            {
                Log.outError(LogFilter.Spells, "EffectPlayMusic: Sound (Id: {0}) not exist in spell {1}.", soundid, m_spellInfo.Id);
                return;
            }

            unitTarget.ToPlayer().SendPacket(new PlayMusic(soundid));
        }

        [SpellEffectHandler(SpellEffectName.TalentSpecSelect)]
        void EffectActivateSpec(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            Player player = unitTarget.ToPlayer();
            uint specID = m_misc.SpecializationId;
            ChrSpecializationRecord spec = CliDB.ChrSpecializationStorage.LookupByKey(specID);

            // Safety checks done in Spell::CheckCast
            if (!spec.IsPetSpecialization())
                player.ActivateTalentGroup(spec);
            else
                player.GetPet().SetSpecialization(specID);
        }

        [SpellEffectHandler(SpellEffectName.PlaySound)]
        void EffectPlaySound(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget)
                return;

            Player player = unitTarget.ToPlayer();
            if (!player)
                return;

            switch (m_spellInfo.Id)
            {
                case 91604: // Restricted Flight Area
                    player.GetSession().SendNotification(CypherStrings.ZoneNoflyzone);
                    break;
                default:
                    break;
            }

            uint soundId = (uint)effectInfo.MiscValue;

            if (!CliDB.SoundKitStorage.ContainsKey(soundId))
            {
                Log.outError(LogFilter.Spells, "EffectPlaySound: Sound (Id: {0}) not exist in spell {1}.", soundId, m_spellInfo.Id);
                return;
            }

            player.PlayDirectSound(soundId, player);
        }

        [SpellEffectHandler(SpellEffectName.RemoveAura)]
        void EffectRemoveAura(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null)
                return;
            // there may be need of specifying casterguid of removed auras
            unitTarget.RemoveAurasDueToSpell(effectInfo.TriggerSpell);
        }

        [SpellEffectHandler(SpellEffectName.DamageFromMaxHealthPCT)]
        void EffectDamageFromMaxHealthPCT(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null)
                return;

            m_damage += (int)unitTarget.CountPctFromMaxHealth(damage);
        }

        [SpellEffectHandler(SpellEffectName.GiveCurrency)]
        void EffectGiveCurrency(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            if (!CliDB.CurrencyTypesStorage.ContainsKey(effectInfo.MiscValue))
                return;

            unitTarget.ToPlayer().ModifyCurrency((CurrencyTypes)effectInfo.MiscValue, damage);
        }

        [SpellEffectHandler(SpellEffectName.CastButton)]
        void EffectCastButtons(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            if (!m_caster.IsTypeId(TypeId.Player))
                return;

            Player p_caster = m_caster.ToPlayer();
            int button_id = effectInfo.MiscValue + 132;
            int n_buttons = effectInfo.MiscValueB;

            for (; n_buttons != 0; --n_buttons, ++button_id)
            {
                ActionButton ab = p_caster.GetActionButton((byte)button_id);
                if (ab == null || ab.GetButtonType() != ActionButtonType.Spell)
                    continue;

                //! Action button data is unverified when it's set so it can be "hacked"
                //! to contain invalid spells, so filter here.
                uint spell_id = ab.GetAction();
                if (spell_id == 0)
                    continue;

                SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spell_id);
                if (spellInfo == null)
                    continue;

                if (!p_caster.HasSpell(spell_id) || p_caster.GetSpellHistory().HasCooldown(spell_id))
                    continue;

                if (!spellInfo.HasAttribute(SpellAttr9.SummonPlayerTotem))
                    continue;

                TriggerCastFlags triggerFlags = (TriggerCastFlags.IgnoreGCD | TriggerCastFlags.IgnoreCastInProgress | TriggerCastFlags.CastDirectly | TriggerCastFlags.DontReportCastError);
                m_caster.CastSpell(m_caster, spell_id, triggerFlags);
            }
        }

        [SpellEffectHandler(SpellEffectName.CreateManaGem)]
        void EffectRechargeManaGem(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            Player player = m_caster.ToPlayer();

            if (player == null)
                return;

            uint item_id = GetEffect(0).ItemType;

            ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(item_id);
            if (proto == null)
            {
                player.SendEquipError(InventoryResult.ItemNotFound);
                return;
            }

            Item pItem = player.GetItemByEntry(item_id);
            if (pItem != null)
            {
                for (int x = 0; x < proto.Effects.Count && x < 5; ++x)
                    pItem.SetSpellCharges(x, proto.Effects[x].Charges);
                pItem.SetState(ItemUpdateState.Changed, player);
            }
        }

        [SpellEffectHandler(SpellEffectName.Bind)]
        void EffectBind(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            Player player = unitTarget.ToPlayer();

            WorldLocation homeLoc = new WorldLocation();
            uint areaId = player.GetAreaId();

            if (effectInfo.MiscValue != 0)
                areaId = (uint)effectInfo.MiscValue;

            if (m_targets.HasDst())
                homeLoc.WorldRelocate(destTarget);
            else
            {
                homeLoc.Relocate(player.GetPosition());
                homeLoc.SetMapId(player.GetMapId());
            }

            player.SetHomebind(homeLoc, areaId);
            player.SendBindPointUpdate();

            Log.outDebug(LogFilter.Spells, "EffectBind: New homebind MapId: {0}, AreaId: {1}, {2}, ", homeLoc.GetMapId(), areaId, homeLoc);

            // zone update
            PlayerBound packet = new PlayerBound(m_caster.GetGUID(), areaId);
            player.SendPacket(packet);
        }

        [SpellEffectHandler(SpellEffectName.SummonRafFriend)]
        void EffectSummonRaFFriend(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!m_caster.IsTypeId(TypeId.Player) || unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
                return;

            m_caster.CastSpell(unitTarget, effectInfo.TriggerSpell, true);
        }

        [SpellEffectHandler(SpellEffectName.UnlockGuildVaultTab)]
        void EffectUnlockGuildVaultTab(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            // Safety checks done in Spell.CheckCast
            Player caster = m_caster.ToPlayer();
            Guild guild = caster.GetGuild();
            if (guild != null)
                guild.HandleBuyBankTab(caster.GetSession(), (byte)(effectInfo.BasePoints - 1)); // Bank tabs start at zero internally
        }

        [SpellEffectHandler(SpellEffectName.ResurrectWithAura)]
        void EffectResurrectWithAura(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (unitTarget == null || !unitTarget.IsInWorld)
                return;

            Player target = unitTarget.ToPlayer();
            if (target == null)
                return;

            if (unitTarget.IsAlive())
                return;

            if (target.IsResurrectRequested())       // already have one active request
                return;

            uint health = (uint)target.CountPctFromMaxHealth(damage);
            uint mana = (uint)MathFunctions.CalculatePct(target.GetMaxPower(PowerType.Mana), damage);
            uint resurrectAura = 0;
            if (Global.SpellMgr.GetSpellInfo(effectInfo.TriggerSpell) != null)
                resurrectAura = effectInfo.TriggerSpell;

            if (resurrectAura != 0 && target.HasAura(resurrectAura))
                return;

            ExecuteLogEffectResurrect(effIndex, target);
            target.SetResurrectRequestData(m_caster, health, mana, resurrectAura);
            SendResurrectRequest(target);
        }

        [SpellEffectHandler(SpellEffectName.CreateAreaTrigger)]
        void EffectCreateAreaTrigger(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            if (!m_targets.HasDst())
                return;

            // trigger entry/miscvalue relation is currently unknown, for now use MiscValue as trigger entry
            uint triggerEntry = (uint)effectInfo.MiscValue;

            int duration = GetSpellInfo().CalcDuration(GetCaster());
            AreaTrigger areaTrigger = new AreaTrigger();
            if (!areaTrigger.CreateAreaTrigger((uint)effectInfo.MiscValue, GetCaster(), null, GetSpellInfo(), destTarget.GetPosition(), duration, m_SpellVisual, m_castId))
                areaTrigger.Dispose();
        }

        [SpellEffectHandler(SpellEffectName.RemoveTalent)]
        void EffectRemoveTalent(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            TalentRecord talent = CliDB.TalentStorage.LookupByKey(m_misc.TalentId);
            if (talent == null)
                return;

            Player player = unitTarget ? unitTarget.ToPlayer() : null;
            if (player == null)
                return;

            player.RemoveTalent(talent);
            player.SendTalentsInfoData();
        }

        [SpellEffectHandler(SpellEffectName.DestroyItem)]
        void EffectDestroyItem(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
                return;

            Player player = unitTarget.ToPlayer();
            SpellEffectInfo effect = GetEffect(effIndex);
            uint itemId = effect.ItemType;
            Item item = player.GetItemByEntry(itemId);
            if (item)
                player.DestroyItem(item.GetBagSlot(), item.GetSlot(), true);
        }

        [SpellEffectHandler(SpellEffectName.LearnGarrisonBuilding)]
        void EffectLearnGarrisonBuilding(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
                return;

            Garrison garrison = unitTarget.ToPlayer().GetGarrison();
            if (garrison != null)
                garrison.LearnBlueprint((uint)GetEffect(effIndex).MiscValue);
        }

        [SpellEffectHandler(SpellEffectName.CreateGarrison)]
        void EffectCreateGarrison(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
                return;

            unitTarget.ToPlayer().CreateGarrison((uint)GetEffect(effIndex).MiscValue);
        }

        [SpellEffectHandler(SpellEffectName.CreateConversation)]
        void EffectCreateConversation(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            if (!m_targets.HasDst())
                return;

            Conversation.CreateConversation((uint)effectInfo.MiscValue, GetCaster(), destTarget.GetPosition(), new List<ObjectGuid>() { GetCaster().GetGUID() }, GetSpellInfo());
        }

        [SpellEffectHandler(SpellEffectName.AddGarrisonFollower)]
        void EffectAddGarrisonFollower(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
                return;

            Garrison garrison = unitTarget.ToPlayer().GetGarrison();
            if (garrison != null)
                garrison.AddFollower((uint)GetEffect(effIndex).MiscValue);
        }

        [SpellEffectHandler(SpellEffectName.CreateHeirloomItem)]
        void EffectCreateHeirloomItem(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            Player player = m_caster.ToPlayer();
            if (!player)
                return;

            CollectionMgr collectionMgr = player.GetSession().GetCollectionMgr();
            if (collectionMgr == null)
                return;

            List<uint> bonusList = new List<uint>();
            bonusList.Add(collectionMgr.GetHeirloomBonus(m_misc.Data0));

            DoCreateItem(effIndex, m_misc.Data0, 0, bonusList);
            ExecuteLogEffectCreateItem(effIndex, m_misc.Data0);
        }

        [SpellEffectHandler(SpellEffectName.ActivateGarrisonBuilding)]
        void EffectActivateGarrisonBuilding(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
                return;

            Garrison garrison = unitTarget.ToPlayer().GetGarrison();
            if (garrison != null)
                garrison.ActivateBuilding((uint)GetEffect(effIndex).MiscValue);
        }

        [SpellEffectHandler(SpellEffectName.HealBattlepetPct)]
        void EffectHealBattlePetPct(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
                return;

            BattlePetMgr battlePetMgr = unitTarget.ToPlayer().GetSession().GetBattlePetMgr();
            if (battlePetMgr != null)
                battlePetMgr.HealBattlePetsPct((byte)GetEffect(effIndex).BasePoints);
        }

        [SpellEffectHandler(SpellEffectName.EnableBattlePets)]
        void EffectEnableBattlePets(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
                return;

            Player plr = unitTarget.ToPlayer();
            plr.SetFlag(PlayerFields.Flags, PlayerFlags.PetBattlesUnlocked);
            plr.GetSession().GetBattlePetMgr().UnlockSlot(0);
        }

        [SpellEffectHandler(SpellEffectName.UncageBattlepet)]
        void EffectUncageBattlePet(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            if (!m_CastItem || !m_caster || !m_caster.IsTypeId(TypeId.Player))
                return;

            Player plr = m_caster.ToPlayer();

            // are we allowed to learn battle pets without it?
            /*if (plr.HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_PET_BATTLES_UNLOCKED))
                return; // send some error*/

            uint speciesId = m_CastItem.GetModifier(ItemModifier.BattlePetSpeciesId);
            ushort breed = (ushort)(m_CastItem.GetModifier(ItemModifier.BattlePetBreedData) & 0xFFFFFF);
            byte quality = (byte)((m_CastItem.GetModifier(ItemModifier.BattlePetBreedData) >> 24) & 0xFF);
            ushort level = (ushort)m_CastItem.GetModifier(ItemModifier.BattlePetLevel);
            uint creatureId = m_CastItem.GetModifier(ItemModifier.BattlePetDisplayId);

            BattlePetSpeciesRecord speciesEntry = CliDB.BattlePetSpeciesStorage.LookupByKey(speciesId);
            if (speciesEntry == null)
                return;

            BattlePetMgr battlePetMgr = plr.GetSession().GetBattlePetMgr();
            if (battlePetMgr == null)
                return;

            ushort maxLearnedLevel = 0;

            foreach (var pet in battlePetMgr.GetLearnedPets())
                maxLearnedLevel = Math.Max(pet.PacketInfo.Level, maxLearnedLevel);

            // TODO: This means if you put your highest lvl pet into cage, you won't be able to uncage it again which is probably wrong.
            // We will need to store maxLearnedLevel somewhere to avoid this behaviour.
            if (maxLearnedLevel < level)
            {
                battlePetMgr.SendError(BattlePetError.TooHighLevelToUncage, creatureId); // or speciesEntry.CreatureID
                SendCastResult(SpellCastResult.CantAddBattlePet);
                return;
            }

            if (battlePetMgr.GetPetCount(speciesId) >= SharedConst.MaxBattlePetsPerSpecies)
            {
                battlePetMgr.SendError(BattlePetError.CantHaveMorePetsOfThatType, creatureId); // or speciesEntry.CreatureID
                SendCastResult(SpellCastResult.CantAddBattlePet);
                return;
            }

            if (!plr.HasSpell(speciesEntry.SummonSpellID))
                plr.LearnSpell(speciesEntry.SummonSpellID, false);

            battlePetMgr.AddPet(speciesId, creatureId, breed, quality, level);
            plr.DestroyItem(m_CastItem.GetBagSlot(), m_CastItem.GetSlot(), true);
            m_CastItem = null;
        }

        [SpellEffectHandler(SpellEffectName.UpgradeHeirloom)]
        void EffectUpgradeHeirloom(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            Player player = m_caster.ToPlayer();
            if (player)
            {
                CollectionMgr collectionMgr = player.GetSession().GetCollectionMgr();
                if (collectionMgr != null)
                    collectionMgr.UpgradeHeirloom(m_misc.Data0, m_castItemEntry);
            }
        }

        [SpellEffectHandler(SpellEffectName.ApplyEnchantIllusion)]
        void EffectApplyEnchantIllusion(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!itemTarget)
                return;

            Player player = m_caster.ToPlayer();
            if (!player || player.GetGUID() != itemTarget.GetOwnerGUID())
                return;

            itemTarget.SetState(ItemUpdateState.Changed, player);
            itemTarget.SetModifier(ItemModifier.EnchantIllusionAllSpecs, (uint)effectInfo.MiscValue);
            if (itemTarget.IsEquipped())
                player.SetUInt16Value(PlayerFields.VisibleItem + 1 + (itemTarget.GetSlot() * 2), 1, itemTarget.GetVisibleItemVisual(player));

            player.RemoveTradeableItem(itemTarget);
            itemTarget.ClearSoulboundTradeable(player);
        }

        [SpellEffectHandler(SpellEffectName.UpdatePlayerPhase)]
        void EffectUpdatePlayerPhase(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
                return;

            unitTarget.UpdateAreaAndZonePhase();
        }

        [SpellEffectHandler(SpellEffectName.UpdateZoneAurasPhases)]
        void EffectUpdateZoneAurasAndPhases(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
                return;

            unitTarget.ToPlayer().UpdateAreaDependentAuras(unitTarget.GetAreaId());
        }

        [SpellEffectHandler(SpellEffectName.GiveArtifactPower)]
        void EffectGiveArtifactPower(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.LaunchTarget)
                return;

            if (!m_caster.IsTypeId(TypeId.Player))
                return;

            Aura artifactAura = m_caster.GetAura(PlayerConst.ArtifactsAllWeaponsGeneralWeaponEquippedPassive);
            if (artifactAura != null)
            {
                Item artifact = m_caster.ToPlayer().GetItemByGuid(artifactAura.GetCastItemGUID());
                if (artifact)
                    artifact.GiveArtifactXp((ulong)damage, m_CastItem, (uint)effectInfo.MiscValue);
            }
        }

        [SpellEffectHandler(SpellEffectName.GiveArtifactPowerNoBonus)]
        void EffectGiveArtifactPowerNoBonus(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.LaunchTarget)
                return;

            if (!unitTarget || !m_caster.IsTypeId(TypeId.Player))
                return;

            Aura artifactAura = unitTarget.GetAura(PlayerConst.ArtifactsAllWeaponsGeneralWeaponEquippedPassive);
            if (artifactAura != null)
            {
                Item artifact = unitTarget.ToPlayer().GetItemByGuid(artifactAura.GetCastItemGUID());
                if (artifact)
                    artifact.GiveArtifactXp((ulong)damage, m_CastItem, 0);
            }
        }

        [SpellEffectHandler(SpellEffectName.PlayScene)]
        void EffectPlayScene(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.Hit)
                return;

            if (m_caster.GetTypeId() != TypeId.Player)
                return;

            m_caster.ToPlayer().GetSceneMgr().PlayScene((uint)effectInfo.MiscValue, destTarget);
        }

        [SpellEffectHandler(SpellEffectName.GiveHonor)]
        void EffectGiveHonor(uint effIndex)
        {
            if (effectHandleMode != SpellEffectHandleMode.HitTarget)
                return;

            if (!unitTarget || unitTarget.GetTypeId() != TypeId.Player)
                return;

            PvPCredit packet = new PvPCredit();
            packet.Honor = damage;
            packet.OriginalHonor = damage;

            Player playerTarget = unitTarget.ToPlayer();
            playerTarget.AddHonorXP((uint)damage);
            playerTarget.SendPacket(packet);
        }
    }

    public class DispelCharges
    {
        public DispelCharges(Aura _aura, byte _value)
        {
            aura = _aura;
            value = _value;
        }

        public Aura aura;
        public byte value;
    }
}
