Files
CypherCore/Source/Game/Spells/SpellEffects.cs
T

5563 lines
219 KiB
C#

/*
* Copyright (C) 2012-2020 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 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.Networking.Packets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
namespace Game.Spells
{
public partial class Spell
{
[SpellEffectHandler(SpellEffectName.None)]
[SpellEffectHandler(SpellEffectName.Portal)]
[SpellEffectHandler(SpellEffectName.BindSight)]
[SpellEffectHandler(SpellEffectName.CallPet)]
[SpellEffectHandler(SpellEffectName.PortalTeleport)]
[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() { }
void EffectResurrectNew()
{
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(effectInfo.Effect, target);
target.SetResurrectRequestData(m_caster, (uint)health, (uint)mana, 0);
SendResurrectRequest(target);
}
[SpellEffectHandler(SpellEffectName.Instakill)]
void EffectInstaKill()
{
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();
data.Target = unitTarget.GetGUID();
data.Caster = m_caster.GetGUID();
data.SpellID = m_spellInfo.Id;
m_caster.SendMessageToSet(data, true);
Unit.Kill(unitCaster, unitTarget, false);
}
[SpellEffectHandler(SpellEffectName.EnvironmentalDamage)]
void EffectEnvironmentalDMG()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null || !unitTarget.IsAlive())
return;
// CalcAbsorbResist already in Player::EnvironmentalDamage
if (unitTarget.IsTypeId(TypeId.Player))
unitTarget.ToPlayer().EnvironmentalDamage(EnviromentalDamage.Fire, (uint)damage);
else
{
DamageInfo damageInfo = new(unitCaster, unitTarget, (uint)damage, m_spellInfo, m_spellInfo.GetSchoolMask(), DamageEffectType.SpellDirect, WeaponAttackType.BaseAttack);
Unit.CalcAbsorbResist(damageInfo);
SpellNonMeleeDamage log = new(unitCaster, unitTarget, m_spellInfo, m_SpellVisual, m_spellInfo.GetSchoolMask(), m_castId);
log.damage = damageInfo.GetDamage();
log.originalDamage = (uint)damage;
log.absorb = damageInfo.GetAbsorb();
log.resist = damageInfo.GetResist();
if (unitCaster != null)
unitCaster.SendSpellNonMeleeDamageLog(log);
}
}
[SpellEffectHandler(SpellEffectName.SchoolDamage)]
void EffectSchoolDmg()
{
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))
{
long count = GetUnitTargetCountForEffect(effectInfo.EffectIndex);
// divide to all targets
if (count != 0)
damage /= (int)count;
}
if (unitCaster != null && apply_direct_bonus)
{
uint bonus = unitCaster.SpellDamageBonusDone(unitTarget, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect, effectInfo);
damage = (int)(bonus + (bonus * _variance));
damage = (int)unitTarget.SpellDamageBonusTaken(unitCaster, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect);
}
m_damage += damage;
}
}
[SpellEffectHandler(SpellEffectName.Dummy)]
void EffectDummy()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget && !gameObjTarget && !itemTarget)
return;
// pet auras
if (m_caster.GetTypeId() == TypeId.Player)
{
PetAura petSpell = Global.SpellMgr.GetPetAura(m_spellInfo.Id, (byte)effectInfo.EffectIndex);
if (petSpell != null)
{
m_caster.ToPlayer().AddPetAura(petSpell);
return;
}
}
// normal DB scripted effect
Log.outDebug(LogFilter.Spells, "Spell ScriptStart spellid {0} in EffectDummy({1})", m_spellInfo.Id, effectInfo.EffectIndex);
m_caster.GetMap().ScriptsStart(ScriptsType.Spell, (uint)((int)m_spellInfo.Id | (int)(effectInfo.EffectIndex << 24)), m_caster, unitTarget);
}
[SpellEffectHandler(SpellEffectName.TriggerSpell)]
[SpellEffectHandler(SpellEffectName.TriggerSpellWithValue)]
void EffectTriggerSpell()
{
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)
{
// Demonic Empowerment -- succubus
case 54437:
{
unitTarget.RemoveMovementImpairingAuras(true);
unitTarget.RemoveAurasByType(AuraType.ModStalked);
unitTarget.RemoveAurasByType(AuraType.ModStun);
// Cast Lesser Invisibility
unitTarget.CastSpell(unitTarget, 7870, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetOriginalCastId(m_castId));
return;
}
// Brittle Armor - (need add max stack of 24575 Brittle Armor)
case 29284:
{
// Brittle Armor
SpellInfo spell = Global.SpellMgr.GetSpellInfo(24575, GetCastDifficulty());
if (spell == null)
return;
for (uint j = 0; j < spell.StackAmount; ++j)
m_caster.CastSpell(unitTarget, spell.Id, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetOriginalCastId(m_castId));
return;
}
// Mercurial Shield - (need add max stack of 26464 Mercurial Shield)
case 29286:
{
// Mercurial Shield
SpellInfo spell = Global.SpellMgr.GetSpellInfo(26464, GetCastDifficulty());
if (spell == null)
return;
for (uint j = 0; j < spell.StackAmount; ++j)
m_caster.CastSpell(unitTarget, spell.Id, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetOriginalCastId(m_castId));
return;
}
}
}
if (triggered_spell_id == 0)
{
Log.outWarn(LogFilter.Spells, $"Spell::EffectTriggerSpell: Spell {m_spellInfo.Id} [EffectIndex: {effectInfo.EffectIndex}] does not have triggered spell.");
return;
}
// normal case
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(triggered_spell_id, GetCastDifficulty());
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();
if (effectHandleMode == SpellEffectHandleMode.LaunchTarget)
{
if (!spellInfo.NeedsToBeTriggeredByCaster(m_spellInfo))
return;
targets.SetUnitTarget(unitTarget);
}
else //if (effectHandleMode == SpellEffectHandleMode.Launch)
{
if (spellInfo.NeedsToBeTriggeredByCaster(m_spellInfo) && effectInfo.GetProvidedTargetMask().HasAnyFlag(SpellCastTargetFlags.UnitMask))
return;
if (spellInfo.GetExplicitTargetMask().HasAnyFlag(SpellCastTargetFlags.DestLocation))
targets.SetDst(m_targets);
Unit target = m_targets.GetUnitTarget();
if (target != null)
targets.SetUnitTarget(target);
else
{
Unit unit = m_caster.ToUnit();
if (unit != null)
targets.SetUnitTarget(unit);
else
{
GameObject go = m_caster.ToGameObject();
if (go != null)
targets.SetGOTarget(go);
}
}
}
CastSpellExtraArgs args = new(TriggerCastFlags.FullMask);
args.SetOriginalCaster(m_originalCasterGUID);
args.SetOriginalCastId(m_castId);
// set basepoints for trigger with value effect
if (effectInfo.Effect == SpellEffectName.TriggerSpellWithValue)
for (int i = 0; i < SpellConst.MaxEffects; ++i)
args.AddSpellMod(SpellValueMod.BasePoint0 + i, damage);
// original caster guid only for GO cast
m_caster.CastSpell(targets, spellInfo.Id, args);
}
[SpellEffectHandler(SpellEffectName.TriggerMissile)]
[SpellEffectHandler(SpellEffectName.TriggerMissileSpellWithValue)]
void EffectTriggerMissileSpell()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget
&& effectHandleMode != SpellEffectHandleMode.Hit)
return;
uint triggered_spell_id = effectInfo.TriggerSpell;
if (triggered_spell_id == 0)
{
Log.outWarn(LogFilter.Spells, $"Spell::EffectTriggerMissileSpell: Spell {m_spellInfo.Id} [EffectIndex: {effectInfo.EffectIndex}] does not have triggered spell.");
return;
}
// normal case
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(triggered_spell_id, GetCastDifficulty());
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();
if (effectHandleMode == SpellEffectHandleMode.HitTarget)
{
if (!spellInfo.NeedsToBeTriggeredByCaster(m_spellInfo))
return;
targets.SetUnitTarget(unitTarget);
}
else //if (effectHandleMode == SpellEffectHandleMode.Hit)
{
if (spellInfo.NeedsToBeTriggeredByCaster(m_spellInfo) && effectInfo.GetProvidedTargetMask().HasAnyFlag(SpellCastTargetFlags.UnitMask))
return;
if (spellInfo.GetExplicitTargetMask().HasAnyFlag(SpellCastTargetFlags.DestLocation))
targets.SetDst(m_targets);
Unit unit = m_caster.ToUnit();
if (unit != null)
targets.SetUnitTarget(unit);
else
{
GameObject go = m_caster.ToGameObject();
if (go != null)
targets.SetGOTarget(go);
}
}
CastSpellExtraArgs args = new(TriggerCastFlags.FullMask);
args.SetOriginalCaster(m_originalCasterGUID);
args.SetOriginalCastId(m_castId);
// set basepoints for trigger with value effect
if (effectInfo.Effect == SpellEffectName.TriggerMissileSpellWithValue)
for (int i = 0; i < SpellConst.MaxEffects; ++i)
args.AddSpellMod(SpellValueMod.BasePoint0 + i, damage);
// original caster guid only for GO cast
m_caster.CastSpell(targets, spellInfo.Id, args);
}
[SpellEffectHandler(SpellEffectName.ForceCast)]
[SpellEffectHandler(SpellEffectName.ForceCastWithValue)]
[SpellEffectHandler(SpellEffectName.ForceCast2)]
void EffectForceCast()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null)
return;
uint triggered_spell_id = effectInfo.TriggerSpell;
if (triggered_spell_id == 0)
{
Log.outWarn(LogFilter.Spells, $"Spell::EffectForceCast: Spell {m_spellInfo.Id} [EffectIndex: {effectInfo.EffectIndex}] does not have triggered spell.");
return;
}
// normal case
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(triggered_spell_id, GetCastDifficulty());
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
{
CastSpellExtraArgs args1 = new(TriggerCastFlags.FullMask);
args1.SetOriginalCaster(m_originalCasterGUID);
args1.SetOriginalCastId(m_castId);
args1.AddSpellMod(SpellValueMod.BasePoint0, damage);
unitTarget.CastSpell(unitTarget, spellInfo.Id, args1);
return;
}
}
}
switch (spellInfo.Id)
{
case 72298: // Malleable Goo Summon
unitTarget.CastSpell(unitTarget, spellInfo.Id, new CastSpellExtraArgs(TriggerCastFlags.FullMask)
.SetOriginalCaster(m_originalCasterGUID)
.SetOriginalCastId(m_castId));
return;
}
CastSpellExtraArgs args = new(TriggerCastFlags.FullMask);
args.SetOriginalCastId(m_castId);
// set basepoints for trigger with value effect
if (effectInfo.Effect == SpellEffectName.ForceCastWithValue)
for (int i = 0; i < SpellConst.MaxEffects; ++i)
args.AddSpellMod(SpellValueMod.BasePoint0 + i, damage);
unitTarget.CastSpell(m_caster, spellInfo.Id, args);
}
[SpellEffectHandler(SpellEffectName.TriggerSpell2)]
void EffectTriggerRitualOfSummoning()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
uint triggered_spell_id = effectInfo.TriggerSpell;
if (triggered_spell_id == 0)
{
Log.outWarn(LogFilter.Spells, $"Spell::EffectTriggerRitualOfSummoning: Spell {m_spellInfo.Id} [EffectIndex: {effectInfo.EffectIndex}] does not have triggered spell.");
return;
}
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(triggered_spell_id, GetCastDifficulty());
if (spellInfo == null)
{
Log.outError(LogFilter.Spells, $"EffectTriggerRitualOfSummoning of spell {m_spellInfo.Id}: triggering unknown spell id {triggered_spell_id}");
return;
}
Finish();
m_caster.CastSpell((Unit)null, spellInfo.Id, new CastSpellExtraArgs().SetOriginalCastId(m_castId));
}
void CalculateJumpSpeeds(SpellEffectInfo effInfo, float dist, out float speedXY, out float speedZ)
{
float runSpeed = unitCaster.IsControlledByPlayer() ? SharedConst.playerBaseMoveSpeed[(int)UnitMoveType.Run] : SharedConst.baseMoveSpeed[(int)UnitMoveType.Run];
Creature creature = unitCaster.ToCreature();
if (creature != null)
runSpeed *= creature.GetCreatureTemplate().SpeedRun;
float multiplier = effInfo.Amplitude;
if (multiplier <= 0.0f)
multiplier = 1.0f;
speedXY = Math.Min(runSpeed * 3.0f * multiplier, Math.Max(28.0f, unitCaster.GetSpeed(UnitMoveType.Run) * 4.0f));
float duration = dist / speedXY;
float durationSqr = duration * duration;
float minHeight = effInfo.MiscValue != 0 ? effInfo.MiscValue / 10.0f : 0.5f; // Lower bound is blizzlike
float maxHeight = effInfo.MiscValueB != 0 ? effInfo.MiscValueB / 10.0f : 1000.0f; // Upper bound is unknown
float height;
if (durationSqr < minHeight * 8 / MotionMaster.gravity)
height = minHeight;
else if (durationSqr > maxHeight * 8 / MotionMaster.gravity)
height = maxHeight;
else
height = (float)(MotionMaster.gravity * durationSqr / 8);
speedZ = MathF.Sqrt((float)(2 * MotionMaster.gravity * height));
}
[SpellEffectHandler(SpellEffectName.Jump)]
void EffectJump()
{
if (effectHandleMode != SpellEffectHandleMode.LaunchTarget)
return;
if (unitCaster == null)
return;
if (unitCaster.IsInFlight())
return;
if (unitTarget == null)
return;
float speedXY, speedZ;
CalculateJumpSpeeds(effectInfo, unitCaster.GetExactDist2d(unitTarget), out speedXY, out speedZ);
JumpArrivalCastArgs arrivalCast = new();
arrivalCast.SpellId = effectInfo.TriggerSpell;
arrivalCast.Target = unitTarget.GetGUID();
unitCaster.GetMotionMaster().MoveJump(unitTarget, speedXY, speedZ, EventId.Jump, false, arrivalCast);
}
[SpellEffectHandler(SpellEffectName.JumpDest)]
void EffectJumpDest()
{
if (effectHandleMode != SpellEffectHandleMode.Launch)
return;
if (unitCaster == null)
return;
if (unitCaster.IsInFlight())
return;
if (!m_targets.HasDst())
return;
float speedXY, speedZ;
CalculateJumpSpeeds(effectInfo, unitCaster.GetExactDist2d(destTarget), out speedXY, out speedZ);
JumpArrivalCastArgs arrivalCast = new();
arrivalCast.SpellId = effectInfo.TriggerSpell;
unitCaster.GetMotionMaster().MoveJump(destTarget, speedXY, speedZ, EventId.Jump, !m_targets.GetObjectTargetGUID().IsEmpty(), arrivalCast);
}
[SpellEffectHandler(SpellEffectName.TeleportUnits)]
void EffectTeleportUnits()
{
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
WorldLocation targetDest = new(destTarget);
if (targetDest.GetMapId() == 0xFFFFFFFF)
targetDest.SetMapId(unitTarget.GetMapId());
if (targetDest.GetOrientation() == 0 && m_targets.GetUnitTarget())
targetDest.SetOrientation(m_targets.GetUnitTarget().GetOrientation());
Player player = unitTarget.ToPlayer();
if (player != null)
{
// Custom loading screen
uint customLoadingScreenId = (uint)effectInfo.MiscValue;
if (customLoadingScreenId != 0)
player.SendPacket(new CustomLoadScreen(m_spellInfo.Id, customLoadingScreenId));
}
if (targetDest.GetMapId() == unitTarget.GetMapId())
unitTarget.NearTeleportTo(targetDest, unitTarget == m_caster);
else if (player != null)
player.TeleportTo(targetDest, unitTarget == m_caster ? TeleportToOptions.Spell : 0);
else
{
Log.outError(LogFilter.Spells, "Spell.EffectTeleportUnits - spellId {0} attempted to teleport creature to a different map.", m_spellInfo.Id);
return;
}
}
[SpellEffectHandler(SpellEffectName.TeleportWithSpellVisualKitLoadingScreen)]
void EffectTeleportUnitsWithVisualLoadingScreen()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget)
return;
// If not exist data for dest location - return
if (!m_targets.HasDst())
{
Log.outError(LogFilter.Spells, $"Spell::EffectTeleportUnitsWithVisualLoadingScreen - does not have a destination for spellId {m_spellInfo.Id}.");
return;
}
// Init dest coordinates
WorldLocation targetDest = new(destTarget);
if (targetDest.GetMapId() == 0xFFFFFFFF)
targetDest.SetMapId(unitTarget.GetMapId());
if (targetDest.GetOrientation() == 0 && m_targets.GetUnitTarget())
targetDest.SetOrientation(m_targets.GetUnitTarget().GetOrientation());
if (effectInfo.MiscValueB != 0)
{
Player playerTarget = unitTarget.ToPlayer();
if (playerTarget != null)
playerTarget.SendPacket(new SpellVisualLoadScreen(effectInfo.MiscValueB, effectInfo.MiscValue));
}
unitTarget.m_Events.AddEventAtOffset(new DelayedSpellTeleportEvent(unitTarget, targetDest, unitTarget == m_caster ? TeleportToOptions.Spell : 0, m_spellInfo.Id), TimeSpan.FromMilliseconds(effectInfo.MiscValue));
}
[SpellEffectHandler(SpellEffectName.ApplyAura)]
[SpellEffectHandler(SpellEffectName.ApplyAuraOnPet)]
void EffectApplyAura()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (spellAura == null || unitTarget == null)
return;
// register target/effect on aura
AuraApplication aurApp = spellAura.GetApplicationOfTarget(unitTarget.GetGUID());
if (aurApp == null)
aurApp = unitTarget._CreateAuraApplication(spellAura, 1u << (int)effectInfo.EffectIndex);
else
aurApp.UpdateApplyEffectMask(aurApp.GetEffectsToApply() | 1u << (int)effectInfo.EffectIndex);
}
[SpellEffectHandler(SpellEffectName.UnlearnSpecialization)]
void EffectUnlearnSpecialization()
{
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()
{
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
if (unitCaster != null)
{
uint bonus = unitCaster.SpellDamageBonusDone(unitTarget, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect, effectInfo);
damage = (int)(bonus + (bonus * _variance));
damage = (int)unitTarget.SpellDamageBonusTaken(unitCaster, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect);
}
int newDamage = -(unitTarget.ModifyPower(powerType, -damage));
// Don't restore from self drain
float gainMultiplier = 0.0f;
if (unitCaster != null && unitCaster != unitTarget)
{
gainMultiplier = effectInfo.CalcValueMultiplier(unitCaster, this);
int gain = (int)(newDamage * gainMultiplier);
unitCaster.EnergizeBySpell(unitCaster, m_spellInfo, gain, powerType);
}
ExecuteLogEffectTakeTargetPower(effectInfo.Effect, unitTarget, powerType, (uint)newDamage, gainMultiplier);
}
[SpellEffectHandler(SpellEffectName.SendEvent)]
void EffectSendEvent()
{
// 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()
{
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;
int newDamage = -(unitTarget.ModifyPower(powerType, -damage));
// NO - Not a typo - EffectPowerBurn uses effect value multiplier - not effect damage multiplier
float dmgMultiplier = effectInfo.CalcValueMultiplier(unitCaster, this);
// add log data before multiplication (need power amount, not damage)
ExecuteLogEffectTakeTargetPower(effectInfo.Effect, unitTarget, powerType, (uint)newDamage, 0.0f);
newDamage = (int)(newDamage * dmgMultiplier);
m_damage += newDamage;
}
[SpellEffectHandler(SpellEffectName.Heal)]
void EffectHeal()
{
if (effectHandleMode != SpellEffectHandleMode.LaunchTarget)
return;
if (unitTarget == null || !unitTarget.IsAlive() || damage < 0)
return;
// Skip if m_originalCaster not available
if (unitCaster == null)
return;
int addhealth = damage;
// Vessel of the Naaru (Vial of the Sunwell trinket)
///@todo: move this to scripts
if (m_spellInfo.Id == 45064)
{
// Amount of heal - depends from stacked Holy Energy
int damageAmount = 0;
AuraEffect aurEff = unitCaster.GetAuraEffect(45062, 0);
if (aurEff != null)
{
damageAmount += aurEff.GetAmount();
unitCaster.RemoveAurasDueToSpell(45062);
}
addhealth += damageAmount;
}
// Death Pact - return pct of max health to caster
else if (m_spellInfo.SpellFamilyName == SpellFamilyNames.Deathknight && m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x00080000u))
addhealth = (int)unitCaster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)unitCaster.CountPctFromMaxHealth(damage), DamageEffectType.Heal, effectInfo);
else
{
uint bonus = unitCaster.SpellHealingBonusDone(unitTarget, m_spellInfo, (uint)addhealth, DamageEffectType.Heal, effectInfo);
addhealth = (int)(bonus + (uint)(bonus * _variance));
}
addhealth = (int)unitTarget.SpellHealingBonusTaken(unitCaster, m_spellInfo, (uint)addhealth, DamageEffectType.Heal);
// Remove Grievious bite if fully healed
if (unitTarget.HasAura(48920) && ((uint)(unitTarget.GetHealth() + (ulong)addhealth) >= unitTarget.GetMaxHealth()))
unitTarget.RemoveAura(48920);
m_healing += addhealth;
}
[SpellEffectHandler(SpellEffectName.HealPct)]
void EffectHealPct()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null || !unitTarget.IsAlive() || damage < 0)
return;
uint heal = (uint)unitTarget.CountPctFromMaxHealth(damage);
if (unitCaster)
{
heal = unitCaster.SpellHealingBonusDone(unitTarget, m_spellInfo, heal, DamageEffectType.Heal, effectInfo);
heal = unitTarget.SpellHealingBonusTaken(unitCaster, m_spellInfo, heal, DamageEffectType.Heal);
}
m_healing += (int)heal;
}
[SpellEffectHandler(SpellEffectName.HealMechanical)]
void EffectHealMechanical()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null || !unitTarget.IsAlive() || damage < 0)
return;
uint heal = (uint)damage;
if (unitCaster)
heal = unitCaster.SpellHealingBonusDone(unitTarget, m_spellInfo, heal, DamageEffectType.Heal, effectInfo);
heal += (uint)(heal * _variance);
if (unitCaster)
heal = unitTarget.SpellHealingBonusTaken(unitCaster, m_spellInfo, heal, DamageEffectType.Heal);
m_healing += (int)heal;
}
[SpellEffectHandler(SpellEffectName.HealthLeech)]
void EffectHealthLeech()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null || !unitTarget.IsAlive() || damage < 0)
return;
uint bonus = 0;
if (unitCaster != null)
unitCaster.SpellDamageBonusDone(unitTarget, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect, effectInfo);
damage = (int)(bonus + (bonus * _variance));
if (unitCaster != null)
damage = (int)unitTarget.SpellDamageBonusTaken(unitCaster, m_spellInfo, (uint)damage, DamageEffectType.SpellDirect);
Log.outDebug(LogFilter.Spells, "HealthLeech :{0}", damage);
float healMultiplier = effectInfo.CalcValueMultiplier(unitCaster, this);
m_damage += damage;
DamageInfo damageInfo = new(unitCaster, unitTarget, (uint)damage, m_spellInfo, m_spellInfo.GetSchoolMask(), DamageEffectType.Direct, WeaponAttackType.BaseAttack);
Unit.CalcAbsorbResist(damageInfo);
uint absorb = damageInfo.GetAbsorb();
damage -= (int)absorb;
// get max possible damage, don't count overkill for heal
uint healthGain = (uint)(-unitTarget.GetHealthGain(-damage) * healMultiplier);
if (unitCaster != null && unitCaster.IsAlive())
{
healthGain = unitCaster.SpellHealingBonusDone(unitCaster, m_spellInfo, healthGain, DamageEffectType.Heal, effectInfo);
healthGain = unitCaster.SpellHealingBonusTaken(unitCaster, m_spellInfo, healthGain, DamageEffectType.Heal);
HealInfo healInfo = new(unitCaster, unitCaster, healthGain, m_spellInfo, m_spellSchoolMask);
unitCaster.HealBySpell(healInfo);
}
}
public void DoCreateItem(uint itemId, ItemContext context = 0, List<uint> bonusListIds = null)
{
if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
return;
Player player = unitTarget.ToPlayer();
uint newitemid = itemId;
ItemTemplate pProto = Global.ObjectMgr.GetItemTemplate(newitemid);
if (pProto == null)
{
player.SendEquipError(InventoryResult.ItemNotFound);
return;
}
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 = itemId;
// 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();
uint no_space;
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, ItemEnchantmentManager.GenerateItemRandomBonusListId(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().HasSignature())
pItem.SetCreator(player.GetGUID());
// send info to the client
player.SendNewItem(pItem, num_to_add, true, true);
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
player.UpdateCraftSkill(m_spellInfo.Id);
}
}
[SpellEffectHandler(SpellEffectName.CreateItem)]
void EffectCreateItem()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
DoCreateItem(effectInfo.ItemType, m_spellInfo.HasAttribute(SpellAttr0.Tradespell) ? ItemContext.TradeSkill : ItemContext.None);
ExecuteLogEffectCreateItem(effectInfo.Effect, effectInfo.ItemType);
}
[SpellEffectHandler(SpellEffectName.CreateLoot)]
void EffectCreateItem2()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
return;
Player player = unitTarget.ToPlayer();
ItemContext context = m_spellInfo.HasAttribute(SpellAttr0.Tradespell) ? ItemContext.TradeSkill : ItemContext.None;
// Pick a random item from spell_loot_template
if (m_spellInfo.IsLootCrafting())
{
player.AutoStoreLoot(m_spellInfo.Id, LootStorage.Spell, context, false, true);
player.UpdateCraftSkill(m_spellInfo.Id);
}
else // If there's no random loot entries for this spell, pick the item associated with this spell
{
uint itemId = effectInfo.ItemType;
if (itemId != 0)
DoCreateItem(itemId, context);
}
// @todo ExecuteLogEffectCreateItem(i, GetEffect(i].ItemType);
}
[SpellEffectHandler(SpellEffectName.CreateRandomItem)]
void EffectCreateRandomItem()
{
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, m_spellInfo.HasAttribute(SpellAttr0.Tradespell) ? ItemContext.TradeSkill : ItemContext.None);
// @todo ExecuteLogEffectCreateItem(i, GetEffect(i].ItemType);
}
[SpellEffectHandler(SpellEffectName.PersistentAreaAura)]
void EffectPersistentAA()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (unitCaster == null)
return;
// only handle at last effect
for (uint i = effectInfo.EffectIndex + 1; i < m_spellInfo.GetEffects().Count; ++i)
if (m_spellInfo.GetEffect(i).IsEffect(SpellEffectName.PersistentAreaAura))
return;
Cypher.Assert(dynObjAura == null);
float radius = effectInfo.CalcRadius(unitCaster);
// Caster not in world, might be spell triggered from aura removal
if (!unitCaster.IsInWorld)
return;
DynamicObject dynObj = new(false);
if (!dynObj.CreateDynamicObject(unitCaster.GetMap().GenerateLowGuid(HighGuid.DynamicObject), unitCaster, m_spellInfo, destTarget, radius, DynamicObjectType.AreaSpell, m_SpellVisual))
{
dynObj.Dispose();
return;
}
AuraCreateInfo createInfo = new(m_castId, m_spellInfo, GetCastDifficulty(), SpellConst.MaxEffectMask, dynObj);
createInfo.SetCaster(unitCaster);
createInfo.SetBaseAmount(m_spellValue.EffectBasePoints);
createInfo.SetCastItem(m_castItemGUID, m_castItemEntry, m_castItemLevel);
Aura aura = Aura.TryCreate(createInfo);
if (aura != null)
{
dynObjAura = aura.ToDynObjAura();
dynObjAura._RegisterForTargets();
}
else
return;
Cypher.Assert(dynObjAura.GetDynobjOwner());
dynObjAura._ApplyEffectForTargets(effectInfo.EffectIndex);
}
[SpellEffectHandler(SpellEffectName.Energize)]
void EffectEnergize()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitCaster == null || 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
switch (m_spellInfo.Id)
{
case 24571: // Blood Fury
// Instantly increases your rage by ${(300-10*$max(0,$PL-60))/10}.
damage -= 10 * (int)Math.Max(0, Math.Min(30, unitCaster.GetLevel() - 60));
break;
case 24532: // Burst of Energy
// Instantly increases your energy by ${60-4*$max(0,$min(15,$PL-60))}.
damage -= 4 * (int)Math.Max(0, Math.Min(15, unitCaster.GetLevel() - 60));
break;
case 67490: // Runic Mana Injector (mana gain increased by 25% for engineers - 3.2.0 patch change)
{
Player player = unitCaster.ToPlayer();
if (player != null)
if (player.HasSkill(SkillType.Engineering))
MathFunctions.AddPct(ref damage, 25);
break;
}
default:
break;
}
unitCaster.EnergizeBySpell(unitTarget, m_spellInfo, damage, power);
}
[SpellEffectHandler(SpellEffectName.EnergizePct)]
void EffectEnergizePct()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitCaster == null || 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);
unitCaster.EnergizeBySpell(unitTarget, m_spellInfo, 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(player);
return;
}
player.PlayerTalkClass.ClearMenus();
if (gameObjTarget.GetAI().GossipHello(player))
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, player);
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, player);
break;
// Don't return, let loots been taken
default:
break;
}
}
// Send loot
player.SendLoot(guid, loottype);
}
[SpellEffectHandler(SpellEffectName.OpenLock)]
void EffectOpenLock()
{
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;
ObjectGuid guid;
// Get lockId
if (gameObjTarget != null)
{
GameObjectTemplate goInfo = gameObjTarget.GetGoInfo();
if (goInfo.GetNoDamageImmune() != 0 && player.HasUnitFlag(UnitFlags.Immune))
return;
// 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 (goInfo.type == GameObjectTypes.NewFlag)
{
gameObjTarget.Use(player);
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(effectInfo, lockId, ref skillId, ref reqSkillValue, ref skillValue);
if (res != SpellCastResult.SpellCastOk)
{
SendCastResult(res);
return;
}
if (gameObjTarget != null)
SendLoot(guid, LootType.Skinning);
else if (itemTarget != null)
{
itemTarget.AddItemFlag(ItemFieldFlags.Unlocked);
itemTarget.SetState(ItemUpdateState.Changed, itemTarget.GetOwner());
}
// 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(effectInfo.Effect, gameObjTarget != null ? gameObjTarget : (WorldObject)itemTarget);
}
[SpellEffectHandler(SpellEffectName.SummonChangeItem)]
void EffectSummonChangeItem()
{
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, m_CastItem.GetContext(), 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), (uint)m_CastItem.GetEnchantmentCharges(j));
if (m_CastItem.m_itemData.Durability < m_CastItem.m_itemData.MaxDurability)
{
double lossPercent = 1 - m_CastItem.m_itemData.Durability / m_CastItem.m_itemData.MaxDurability;
player.DurabilityLoss(pNewItem, lossPercent);
}
if (player.IsInventoryPos(pos))
{
List<ItemPosCount> dest = new();
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);
player.SendNewItem(pNewItem, 1, true, false);
player.ItemAddedQuestCheck(newitemid, 1);
return;
}
}
else if (Player.IsBankPos(pos))
{
List<ItemPosCount> dest = new();
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();
player.SendNewItem(pNewItem, 1, true, false);
player.ItemAddedQuestCheck(newitemid, 1);
return;
}
}
}
[SpellEffectHandler(SpellEffectName.Proficiency)]
void EffectProficiency()
{
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()
{
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;
}
WorldObject caster = m_caster;
if (m_originalCaster)
caster = m_originalCaster;
ObjectGuid privateObjectOwner = caster.GetGUID();
if (!properties.GetFlags().HasAnyFlag(SummonPropertiesFlags.OnlyVisibleToSummoner | SummonPropertiesFlags.OnlyVisibleToSummonerGroup))
privateObjectOwner = ObjectGuid.Empty;
if (caster.IsPrivateObject())
privateObjectOwner = caster.GetPrivateObjectOwner();
if (properties.GetFlags().HasFlag(SummonPropertiesFlags.OnlyVisibleToSummonerGroup))
if (caster.IsPlayer() && m_originalCaster.ToPlayer().GetGroup())
privateObjectOwner = caster.ToPlayer().GetGroup().GetGUID();
int duration = m_spellInfo.CalcDuration(caster);
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.Control)
{
case SummonCategory.Wild:
case SummonCategory.Ally:
case SummonCategory.Unk:
if (properties.GetFlags().HasFlag(SummonPropertiesFlags.JoinSummonerSpawnGroup))
{
SummonGuardian(effectInfo, entry, properties, numSummons, privateObjectOwner);
break;
}
switch (properties.Title)
{
case SummonTitle.Pet:
case SummonTitle.Guardian:
case SummonTitle.Runeblade:
case SummonTitle.Minion:
SummonGuardian(effectInfo, entry, properties, numSummons, privateObjectOwner);
break;
// Summons a vehicle, but doesn't force anyone to enter it (see SUMMON_CATEGORY_VEHICLE)
case SummonTitle.Vehicle:
case SummonTitle.Mount:
{
if (unitCaster == null)
return;
summon = unitCaster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, unitCaster, m_spellInfo.Id);
break;
}
case SummonTitle.LightWell:
case SummonTitle.Totem:
{
if (unitCaster == null)
return;
summon = unitCaster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, unitCaster, m_spellInfo.Id, 0, privateObjectOwner);
if (summon == null || !summon.IsTotem())
return;
if (damage != 0) // if not spell info, DB values used
{
summon.SetMaxHealth((uint)damage);
summon.SetHealth((uint)damage);
}
break;
}
case SummonTitle.Companion:
{
if (unitCaster == null)
return;
summon = unitCaster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, unitCaster, m_spellInfo.Id, 0, privateObjectOwner);
if (summon == null || !summon.HasUnitTypeMask(UnitTypeMask.Minion))
return;
summon.SelectLevel(); // some summoned creaters have different from 1 DB data for level/hp
summon.SetNpcFlags((NPCFlags)((int)summon.GetCreatureTemplate().Npcflag & 0xFFFFFFFF));
summon.SetNpcFlags2((NPCFlags2)((int)summon.GetCreatureTemplate().Npcflag >> 32));
summon.SetImmuneToAll(true);
break;
}
default:
{
float radius = effectInfo.CalcRadius();
TempSummonType summonType = (duration == 0) ? TempSummonType.DeadDespawn : TempSummonType.TimedDespawn;
for (uint count = 0; count < numSummons; ++count)
{
Position pos;
if (count == 0)
pos = destTarget;
else
// randomize position for multiple summons
pos = caster.GetRandomPoint(destTarget, radius);
summon = caster.SummonCreature(entry, pos, summonType, (uint)duration, 0, m_spellInfo.Id, privateObjectOwner);
if (summon == null)
continue;
if (properties.Control == SummonCategory.Ally)
summon.SetOwnerGUID(caster.GetGUID());
uint faction = properties.Faction;
if (properties.GetFlags().HasFlag(SummonPropertiesFlags.UseSummonerFaction)) // TODO: Determine priority between faction and flag
{
WorldObject summoner = summon.GetSummoner();
if (summoner != null)
faction = summoner.GetFaction();
}
if (faction != 0)
summon.SetFaction(faction);
ExecuteLogEffectSummonObject(effectInfo.Effect, summon);
}
return;
}
}//switch
break;
case SummonCategory.Pet:
SummonGuardian(effectInfo, entry, properties, numSummons, privateObjectOwner);
break;
case SummonCategory.Puppet:
{
if (unitCaster == null)
return;
summon = unitCaster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, unitCaster, m_spellInfo.Id, 0, privateObjectOwner);
break;
}
case SummonCategory.Vehicle:
{
if (unitCaster == null)
return;
// Summoning spells (usually triggered by npc_spellclick) that spawn a vehicle and that cause the clicker
// to cast a ride vehicle spell on the summoned unit.
summon = unitCaster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, unitCaster, 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;
int basePoints = effectInfo.CalcValue();
if (basePoints > SharedConst.MaxVehicleSeats)
{
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo((uint)basePoints, GetCastDifficulty());
if (spellInfo != null && spellInfo.HasAura(AuraType.ControlVehicle))
spellId = spellInfo.Id;
}
CastSpellExtraArgs args = new(TriggerCastFlags.FullMask);
args.SetOriginalCastId(m_castId);
// if we have small value, it indicates seat position
if (basePoints > 0 && basePoints < SharedConst.MaxVehicleSeats)
args.AddSpellMod(SpellValueMod.BasePoint0, basePoints);
unitCaster.CastSpell(summon, spellId, args);
break;
}
}
if (summon != null)
{
summon.SetCreatorGUID(caster.GetGUID());
ExecuteLogEffectSummonObject(effectInfo.Effect, summon);
}
}
[SpellEffectHandler(SpellEffectName.LearnSpell)]
void EffectLearnSpell()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null)
return;
if (!unitTarget.IsTypeId(TypeId.Player))
{
if (unitTarget.IsPet())
EffectLearnPetSpell();
return;
}
Player player = unitTarget.ToPlayer();
if (m_CastItem != null && effectInfo.TriggerSpell == 0)
{
foreach (var itemEffect in m_CastItem.GetEffects())
{
if (itemEffect.TriggerType != ItemSpelltriggerType.LearnSpellId)
continue;
bool dependent = false;
var speciesEntry = Global.SpellMgr.GetBattlePetSpecies((uint)itemEffect.SpellID);
if (speciesEntry != null)
{
player.GetSession().GetBattlePetMgr().AddPet(speciesEntry.Id, BattlePetMgr.SelectPetDisplay(speciesEntry), BattlePetMgr.RollPetBreed(speciesEntry.Id), BattlePetMgr.GetDefaultPetQuality(speciesEntry.Id));
// If the spell summons a battle pet, we fake that it has been learned and the battle pet is added
// marking as dependent prevents saving the spell to database (intended)
dependent = true;
}
player.LearnSpell((uint)itemEffect.SpellID, dependent);
}
}
if (effectInfo.TriggerSpell != 0)
{
player.LearnSpell(effectInfo.TriggerSpell, false);
Log.outDebug(LogFilter.Spells, $"Spell: {player.GetGUID()} has learned spell {effectInfo.TriggerSpell} from {m_caster.GetGUID()}");
}
}
[SpellEffectHandler(SpellEffectName.Dispel)]
void EffectDispel()
{
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<DispelableAura> dispelList = unitTarget.GetDispellableAuraList(m_caster, dispelMask, targetMissInfo == SpellMissInfo.Reflect);
if (dispelList.Empty())
return;
int remaining = dispelList.Count;
// Ok if exist some buffs for dispel try dispel it
List<DispelableAura> successList = new();
DispelFailed dispelFailed = new();
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 && remaining > 0;)
{
// Random select buff for dispel
var dispelableAura = dispelList[RandomHelper.IRand(0, remaining - 1)];
if (dispelableAura.RollDispel())
{
var successAura = successList.Find(dispelAura =>
{
if (dispelAura.GetAura().GetId() == dispelableAura.GetAura().GetId() && dispelAura.GetAura().GetCaster() == dispelableAura.GetAura().GetCaster())
return true;
return false;
});
if (successAura == null)
successList.Add(new DispelableAura(dispelableAura.GetAura(), 0, 1));
else
successAura.IncrementCharges();
if (!dispelableAura.DecrementCharge())
{
--remaining;
dispelList[remaining] = dispelableAura;
}
}
else
{
dispelFailed.FailedSpells.Add(dispelableAura.GetAura().GetId());
}
++count;
}
if (!dispelFailed.FailedSpells.Empty())
m_caster.SendMessageToSet(dispelFailed, true);
if (successList.Empty())
return;
SpellDispellLog spellDispellLog = new();
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 dispelableAura in successList)
{
var dispellData = new SpellDispellData();
dispellData.SpellID = dispelableAura.GetAura().GetId();
dispellData.Harmful = false; // TODO: use me
//dispellData.Rolled = none; // TODO: use me
//dispellData.Needed = none; // TODO: use me
unitTarget.RemoveAurasDueToSpellByDispel(dispelableAura.GetAura().GetId(), m_spellInfo.Id, dispelableAura.GetAura().GetCasterGUID(), m_caster, dispelableAura.GetDispelCharges());
spellDispellLog.DispellData.Add(dispellData);
}
m_caster.SendMessageToSet(spellDispellLog, true);
CallScriptSuccessfulDispel(effectInfo.EffectIndex);
}
[SpellEffectHandler(SpellEffectName.DualWield)]
void EffectDualWield()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
unitTarget.SetCanDualWield(true);
if (unitTarget.IsTypeId(TypeId.Unit))
unitTarget.ToCreature().UpdateDamagePhysical(WeaponAttackType.OffAttack);
}
[SpellEffectHandler(SpellEffectName.Distract)]
void EffectDistract()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
// Check for possible target
if (unitTarget == null || unitTarget.IsEngaged())
return;
// target must be OK to do this
if (unitTarget.HasUnitState(UnitState.Confused | UnitState.Stunned | UnitState.Fleeing))
return;
unitTarget.GetMotionMaster().MoveDistract((uint)(damage * Time.InMilliseconds), unitTarget.GetAbsoluteAngle(destTarget));
}
[SpellEffectHandler(SpellEffectName.Pickpocket)]
void EffectPickPocket()
{
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()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
Player player = m_caster.ToPlayer();
if (player == null)
return;
float radius = effectInfo.CalcRadius();
int duration = m_spellInfo.CalcDuration(m_caster);
// Caster not in world, might be spell triggered from aura removal
if (!player.IsInWorld)
return;
DynamicObject dynObj = new(true);
if (!dynObj.CreateDynamicObject(player.GetMap().GenerateLowGuid(HighGuid.DynamicObject), player, m_spellInfo, destTarget, radius, DynamicObjectType.FarsightFocus, m_SpellVisual))
return;
dynObj.SetDuration(duration);
dynObj.SetCasterViewpoint();
}
[SpellEffectHandler(SpellEffectName.UntrainTalents)]
void EffectUntrainTalents()
{
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()
{
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.GetCombatReach(), dis);
unitTarget.NearTeleportTo(fx, fy, fz, -m_caster.GetOrientation(), unitTarget == m_caster);
}
[SpellEffectHandler(SpellEffectName.SkillStep)]
void EffectLearnSkill()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget.IsTypeId(TypeId.Player))
return;
if (damage < 1)
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)damage, Math.Max(skillval, (ushort)1), tier.Value[damage - 1]);
}
[SpellEffectHandler(SpellEffectName.PlayMovie)]
void EffectPlayMovie()
{
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()
{
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()
{
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
damage = 1;
DoCreateItem(effectInfo.ItemType, m_spellInfo.HasAttribute(SpellAttr0.Tradespell) ? ItemContext.TradeSkill : ItemContext.None);
itemTarget = null;
m_targets.SetItemTarget(null);
}
else
{
// do not increase skill if vellum used
if (!(m_CastItem && m_CastItem.GetTemplate().HasFlag(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()
{
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()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (itemTarget == null)
return;
Player player = m_caster.ToPlayer();
if (player == 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, effectInfo.EffectIndex);
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, effectInfo.EffectIndex, 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()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitCaster == null || !unitCaster.GetPetGUID().IsEmpty())
return;
if (unitTarget == null)
return;
if (!unitTarget.IsTypeId(TypeId.Unit))
return;
Creature creatureTarget = unitTarget.ToCreature();
if (creatureTarget.IsPet())
return;
if (unitCaster.GetClass() != Class.Hunter)
return;
// cast finish successfully
Finish();
Pet pet = unitCaster.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.GetLevelForTarget(m_caster) < (m_caster.GetLevelForTarget(creatureTarget) - 5)) ? (m_caster.GetLevelForTarget(creatureTarget) - 5) : creatureTarget.GetLevelForTarget(m_caster);
// prepare visual effect for levelup
pet.SetLevel(level - 1);
// add to world
pet.GetMap().AddToMap(pet.ToCreature());
// visual effect for levelup
pet.SetLevel(level);
// caster have pet now
unitCaster.SetMinion(pet, true);
if (m_caster.IsTypeId(TypeId.Player))
{
pet.SavePetToDB(PetSaveMode.AsCurrent);
unitCaster.ToPlayer().PetSpellInitialize();
}
}
[SpellEffectHandler(SpellEffectName.SummonPet)]
void EffectSummonPet()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
Player owner = null;
if (unitCaster != null)
{
owner = unitCaster.ToPlayer();
if (owner == null && unitCaster.IsTotem())
owner = unitCaster.GetCharmerOrOwnerPlayerOrPlayerItself();
}
uint petentry = (uint)effectInfo.MiscValue;
if (owner == null)
{
SummonPropertiesRecord properties = CliDB.SummonPropertiesStorage.LookupByKey(67);
if (properties != null)
SummonGuardian(effectInfo, petentry, properties, 1, ObjectGuid.Empty);
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;
Cypher.Assert(OldSummon.GetMap() == owner.GetMap());
float px, py, pz;
owner.GetClosePoint(out px, out py, out pz, OldSummon.GetCombatReach());
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.GetCombatReach());
Pet pet = owner.SummonPet(petentry, x, y, z, owner.Orientation, PetType.Summon, 0, true);
if (!pet)
return;
if (m_caster.IsTypeId(TypeId.Unit))
{
if (m_caster.ToCreature().IsTotem())
pet.SetReactState(ReactStates.Aggressive);
else
pet.SetReactState(ReactStates.Defensive);
}
pet.SetCreatedBySpell(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(effectInfo.Effect, pet);
}
[SpellEffectHandler(SpellEffectName.LearnPetSpell)]
void EffectLearnPetSpell()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null)
return;
if (unitTarget.ToPlayer() != null)
{
EffectLearnSpell();
return;
}
Pet pet = unitTarget.ToPet();
if (pet == null)
return;
SpellInfo learn_spellproto = Global.SpellMgr.GetSpellInfo(effectInfo.TriggerSpell, Difficulty.None);
if (learn_spellproto == null)
return;
pet.LearnSpell(learn_spellproto.Id);
pet.SavePetToDB(PetSaveMode.AsCurrent);
pet.GetOwner().PetSpellInitialize();
}
[SpellEffectHandler(SpellEffectName.AttackMe)]
void EffectTaunt()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitCaster == 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 || unitTarget.IsTotem())
{
SendCastResult(SpellCastResult.DontReport);
return;
}
// Hand of Reckoning can hit some entities that can't have a threat list (including players' pets)
if (m_spellInfo.Id == 62124)
if (!unitTarget.IsPlayer() && unitTarget.GetTarget() != unitCaster.GetGUID())
unitCaster.CastSpell(unitTarget, 67485, true);
if (!unitTarget.CanHaveThreatList())
{
SendCastResult(SpellCastResult.DontReport);
return;
}
ThreatManager mgr = unitTarget.GetThreatManager();
if (mgr.GetCurrentVictim() == unitCaster)
{
SendCastResult(SpellCastResult.DontReport);
return;
}
if (!mgr.IsThreatListEmpty())
// Set threat equal to highest threat currently on target
mgr.MatchUnitThreatToHighestThreat(unitCaster);
}
[SpellEffectHandler(SpellEffectName.WeaponDamageNoSchool)]
[SpellEffectHandler(SpellEffectName.WeaponPercentDamage)]
[SpellEffectHandler(SpellEffectName.WeaponDamage)]
[SpellEffectHandler(SpellEffectName.NormalizedWeaponDmg)]
void EffectWeaponDmg()
{
if (effectHandleMode != SpellEffectHandleMode.LaunchTarget)
return;
if (unitCaster == null)
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 j = effectInfo.EffectIndex + 1; j < m_spellInfo.GetEffects().Count; ++j)
{
switch (m_spellInfo.GetEffect(j).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.Shaman:
{
// Skyshatter Harness item set bonus
// Stormstrike
AuraEffect aurEff = unitCaster.IsScriptOverriden(m_spellInfo, 5634);
if (aurEff != null)
unitCaster.CastSpell((WorldObject)null, 38430, new CastSpellExtraArgs(aurEff));
break;
}
}
bool normalized = false;
float weaponDamagePercentMod = 1.0f;
foreach (var spellEffectInfo in m_spellInfo.GetEffects())
{
switch (spellEffectInfo.Effect)
{
case SpellEffectName.WeaponDamage:
case SpellEffectName.WeaponDamageNoSchool:
fixed_bonus += CalculateDamage(spellEffectInfo, unitTarget);
break;
case SpellEffectName.NormalizedWeaponDmg:
fixed_bonus += CalculateDamage(spellEffectInfo, unitTarget);
normalized = true;
break;
case SpellEffectName.WeaponPercentDamage:
MathFunctions.ApplyPct(ref weaponDamagePercentMod, CalculateDamage(spellEffectInfo, unitTarget));
break;
default:
break; // not weapon damage effect, just skip
}
}
// if (addPctMods) { percent mods are added in Unit::CalculateDamage } else { percent mods are added in Unit::MeleeDamageBonusDone }
// this distinction is neccessary to properly inform the client about his autoattack damage values from Script_UnitDamage
bool addPctMods = !m_spellInfo.HasAttribute(SpellAttr6.IgnoreCasterDamageModifiers) && 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 = unitCaster.GetPctModifierValue(unitMod, UnitModifierPctType.Total);
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 = unitCaster.CalculateDamage(m_attackType, normalized, addPctMods);
// Sequence is important
foreach (var spellEffectInfo in m_spellInfo.GetEffects())
{
// We assume that a spell have at most one fixed_bonus
// and at most one weaponDamagePercentMod
switch (spellEffectInfo.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
weaponDamage = Math.Max(weaponDamage, 0);
// Add melee damage bonuses (also check for negative)
weaponDamage = unitCaster.MeleeDamageBonusDone(unitTarget, weaponDamage, m_attackType, DamageEffectType.SpellDirect, m_spellInfo);
m_damage += (int)unitTarget.MeleeDamageBonusTaken(unitCaster, weaponDamage, m_attackType, DamageEffectType.SpellDirect, m_spellInfo);
}
[SpellEffectHandler(SpellEffectName.Threat)]
void EffectThreat()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitCaster == null || !unitCaster.IsAlive())
return;
if (unitTarget == null)
return;
if (!unitTarget.CanHaveThreatList())
return;
unitTarget.GetThreatManager().AddThreat(unitCaster, damage, m_spellInfo, true);
}
[SpellEffectHandler(SpellEffectName.HealMaxHealth)]
void EffectHealMaxHealth()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitCaster == null)
return;
if (unitTarget == null || !unitTarget.IsAlive())
return;
int addhealth;
// damage == 0 - heal for caster max health
if (damage == 0)
addhealth = (int)unitCaster.GetMaxHealth();
else
addhealth = (int)(unitTarget.GetMaxHealth() - unitTarget.GetHealth());
m_healing += addhealth;
}
[SpellEffectHandler(SpellEffectName.InterruptCast)]
void EffectInterruptCast()
{
if (effectHandleMode != SpellEffectHandleMode.LaunchTarget)
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.CanBeInterrupted(m_caster, unitTarget))
{
if (unitCaster != null)
{
int duration = m_spellInfo.GetDuration();
duration = unitTarget.ModSpellDuration(m_spellInfo, unitTarget, duration, false, 1u << (int)effectInfo.EffectIndex);
unitTarget.GetSpellHistory().LockSpellSchool(curSpellInfo.GetSchoolMask(), TimeSpan.FromMilliseconds(duration));
if (m_spellInfo.DmgClass == SpellDmgClass.Magic)
Unit.ProcSkillsAndAuras(unitCaster, unitTarget, ProcFlags.DoneSpellMagicDmgClassNeg, ProcFlags.TakenSpellMagicDmgClassNeg, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Hit, ProcFlagsHit.Interrupt, null, null, null);
else if (m_spellInfo.DmgClass == SpellDmgClass.Melee)
Unit.ProcSkillsAndAuras(unitCaster, unitTarget, ProcFlags.DoneSpellMeleeDmgClass, ProcFlags.TakenSpellMeleeDmgClass, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Hit, ProcFlagsHit.Interrupt, null, null, null);
}
SendSpellInterruptLog(unitTarget, curSpellInfo.Id);
unitTarget.InterruptSpell(i, false);
}
}
}
}
[SpellEffectHandler(SpellEffectName.SummonObjectWild)]
void EffectSummonObjectWild()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
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.DefaultPlayerBoundingRadius);
Map map = target.GetMap();
Position pos = new(x, y, z, target.GetOrientation());
Quaternion rotation = Quaternion.CreateFromRotationMatrix(Extensions.fromEulerAnglesZYX(target.GetOrientation(), 0.0f, 0.0f));
GameObject go = GameObject.CreateGameObject((uint)effectInfo.MiscValue, map, pos, rotation, 255, GameObjectState.Ready);
if (!go)
return;
PhasingHandler.InheritPhaseShift(go, m_caster);
int duration = m_spellInfo.CalcDuration(m_caster);
go.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0);
go.SetSpellId(m_spellInfo.Id);
ExecuteLogEffectSummonObject(effectInfo.Effect, go);
// Wild object not have owner and check clickable by players
map.AddToMap(go);
if (go.GetGoType() == GameObjectTypes.FlagDrop)
{
Player player = m_caster.ToPlayer();
if (player != null)
{
Battleground bg = player.GetBattleground();
if (bg)
bg.SetDroppedFlagGUID(go.GetGUID(), (player.GetTeam() == Team.Alliance ? TeamId.Horde : TeamId.Alliance));
}
}
GameObject linkedTrap = go.GetLinkedTrap();
if (linkedTrap)
{
PhasingHandler.InheritPhaseShift(linkedTrap, m_caster);
linkedTrap.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0);
linkedTrap.SetSpellId(m_spellInfo.Id);
ExecuteLogEffectSummonObject(effectInfo.Effect, linkedTrap);
}
}
[SpellEffectHandler(SpellEffectName.ScriptEffect)]
void EffectScriptEffect()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
// @todo we must implement hunter pet summon at login there (spell 6962)
/// @todo: move this to scripts
switch (m_spellInfo.SpellFamilyName)
{
case SpellFamilyNames.Generic:
{
switch (m_spellInfo.Id)
{
case 45204: // Clone Me!
m_caster.CastSpell(unitTarget, (uint)damage, new CastSpellExtraArgs(true));
break;
// 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, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetOriginalCastId(m_castId));
return;
}
// Mug Transformation
case 41931:
{
if (!m_caster.IsTypeId(TypeId.Player))
return;
byte bag = 19;
byte slot = 0;
Item item;
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, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetOriginalCastId(m_castId));
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, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetOriginalCastId(m_castId));
break;
}
// Emblazon Runeblade
case 51770:
{
if (m_originalCaster == null)
return;
m_originalCaster.CastSpell(m_originalCaster, (uint)damage, new CastSpellExtraArgs(false));
break;
}
// Summon Ghouls On Scarlet Crusade
case 51904:
{
if (!m_targets.HasDst())
return;
float radius = effectInfo.CalcRadius();
for (byte i = 0; i < 15; ++i)
m_caster.CastSpell(m_caster.GetRandomPoint(destTarget, radius), 54522, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetOriginalCastId(m_castId));
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 && unitCaster != null)
unitCaster.CastSpell(unitTarget, Convert.ToBoolean(RandomHelper.IRand(0, 1)) ? (uint)damage : 52505, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetOriginalCastId(m_castId));
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 58941: // Rock Shards
if (unitTarget != null && m_originalCaster != null)
{
for (uint i = 0; i < 3; ++i)
{
m_originalCaster.CastSpell(unitTarget, 58689, new CastSpellExtraArgs(true));
m_originalCaster.CastSpell(unitTarget, 58692, new CastSpellExtraArgs(true));
}
if (m_originalCaster.GetMap().GetDifficultyID() == Difficulty.None)
{
m_originalCaster.CastSpell(unitTarget, 58695, new CastSpellExtraArgs(true));
m_originalCaster.CastSpell(unitTarget, 58696, new CastSpellExtraArgs(true));
}
else
{
m_originalCaster.CastSpell(unitTarget, 60883, new CastSpellExtraArgs(true));
m_originalCaster.CastSpell(unitTarget, 60884, new CastSpellExtraArgs(true));
}
}
return;
case 62482: // Grab Crate
{
if (unitCaster == null)
return;
if (unitTarget != null)
{
Unit seat = unitCaster.GetVehicleBase();
if (seat != null)
{
Unit parent = seat.GetVehicleBase();
if (parent != null)
{
// @todo a hack, range = 11, should after some time cast, otherwise too far
unitCaster.CastSpell(parent, 62496, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetOriginalCastId(m_castId));
unitTarget.CastSpell(parent, (uint)damage, new CastSpellExtraArgs().SetOriginalCastId(m_castId)); // DIFFICULTY_NONE, so effect always valid
}
}
}
return;
}
}
break;
}
}
// normal DB scripted effect
Log.outDebug(LogFilter.Spells, "Spell ScriptStart spellid {0} in EffectScriptEffect({1})", m_spellInfo.Id, effectInfo.EffectIndex);
m_caster.GetMap().ScriptsStart(ScriptsType.Spell, (uint)((int)m_spellInfo.Id | (int)(effectInfo.EffectIndex << 24)), m_caster, unitTarget);
}
[SpellEffectHandler(SpellEffectName.Sanctuary)]
[SpellEffectHandler(SpellEffectName.Sanctuary2)]
void EffectSanctuary()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null)
return;
if (unitTarget.IsPlayer() && !unitTarget.GetMap().IsDungeon())
{
// stop all pve combat for players outside dungeons, suppress pvp combat
unitTarget.CombatStop(false, false);
}
else
{
// in dungeons (or for nonplayers), reset this unit on all enemies' threat lists
foreach (var pair in unitTarget.GetThreatManager().GetThreatenedByMeList())
pair.Value.ScaleThreat(0.0f);
}
// makes spells cast before this time fizzle
unitTarget.LastSanctuaryTime = GameTime.GetGameTimeMS();
}
[SpellEffectHandler(SpellEffectName.Duel)]
void EffectDuel()
{
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(), caster.GetSession().GetAccountGUID()))
return;
// Players can only fight a duel in zones with this flag
AreaTableRecord casterAreaEntry = CliDB.AreaTableStorage.LookupByKey(caster.GetAreaId());
if (casterAreaEntry != null && !casterAreaEntry.HasFlag(AreaFlags.AllowDuels))
{
SendCastResult(SpellCastResult.NoDueling); // Dueling isn't allowed here
return;
}
AreaTableRecord targetAreaEntry = CliDB.AreaTableStorage.LookupByKey(target.GetAreaId());
if (targetAreaEntry != null && !targetAreaEntry.HasFlag(AreaFlags.AllowDuels))
{
SendCastResult(SpellCastResult.NoDueling); // Dueling isn't allowed here
return;
}
//CREATE DUEL FLAG OBJECT
Map map = caster.GetMap();
Position pos = new()
{
posX = caster.GetPositionX() + (unitTarget.GetPositionX() - caster.GetPositionX()) / 2,
posY = caster.GetPositionY() + (unitTarget.GetPositionY() - caster.GetPositionY()) / 2,
posZ = caster.GetPositionZ(),
Orientation = caster.GetOrientation()
};
Quaternion rotation = Quaternion.CreateFromRotationMatrix(Extensions.fromEulerAnglesZYX(pos.GetOrientation(), 0.0f, 0.0f));
GameObject go = GameObject.CreateGameObject((uint)effectInfo.MiscValue, map, pos, rotation, 0, GameObjectState.Ready);
if (!go)
return;
PhasingHandler.InheritPhaseShift(go, caster);
go.SetFaction(caster.GetFaction());
go.SetLevel(caster.GetLevel() + 1);
int duration = m_spellInfo.CalcDuration(caster);
go.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0);
go.SetSpellId(m_spellInfo.Id);
ExecuteLogEffectSummonObject(effectInfo.Effect, go);
caster.AddGameObject(go);
map.AddToMap(go);
//END
// Send request
DuelRequested packet = new();
packet.ArbiterGUID = go.GetGUID();
packet.RequestedByGUID = caster.GetGUID();
packet.RequestedByWowAccount = caster.GetSession().GetAccountGUID();
caster.SendPacket(packet);
target.SendPacket(packet);
// create duel-info
bool isMounted = (GetSpellInfo().Id == 62875);
caster.duel = new(target, caster, isMounted);
target.duel = new(caster, caster, isMounted);
caster.SetDuelArbiter(go.GetGUID());
target.SetDuelArbiter(go.GetGUID());
Global.ScriptMgr.OnPlayerDuelRequest(target, caster);
}
[SpellEffectHandler(SpellEffectName.Stuck)]
void EffectStuck()
{
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, GetCastDifficulty());
if (spellInfo == null)
return;
Spell spell = new(player, spellInfo, TriggerCastFlags.FullMask);
spell.SendSpellCooldown();
}
[SpellEffectHandler(SpellEffectName.SummonPlayer)]
void EffectSummonPlayer()
{
// workaround - this effect should not use target map
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitCaster == null)
return;
if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
return;
unitTarget.ToPlayer().SendSummonRequestFrom(unitCaster);
}
[SpellEffectHandler(SpellEffectName.ActivateObject)]
void EffectActivateObject()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (gameObjTarget == null)
return;
ScriptInfo activateCommand = new();
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()
{
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);
player.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags2.ChangeGlyph);
GlyphPropertiesRecord glyphProperties = CliDB.GlyphPropertiesStorage.LookupByKey(glyphId);
if (glyphProperties != null)
player.CastSpell(player, glyphProperties.SpellID, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetOriginalCastId(m_castId));
ActiveGlyphs activeGlyphs = new();
activeGlyphs.Glyphs.Add(new GlyphBinding(m_misc.SpellId, (ushort)glyphId));
activeGlyphs.IsFullUpdate = false;
player.SendPacket(activeGlyphs);
}
[SpellEffectHandler(SpellEffectName.EnchantHeldItem)]
void EffectEnchantHeldItem()
{
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()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
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()
{
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, new CastSpellExtraArgs().SetOriginalCastId(m_castId)); // Drunken Vomit
}
else
currentDrunk += (byte)drunkMod;
player.SetDrunkValue(currentDrunk, m_CastItem != null ? m_CastItem.GetEntry() : 0);
}
[SpellEffectHandler(SpellEffectName.FeedPet)]
void EffectFeedPet()
{
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;
ExecuteLogEffectDestroyItem(effectInfo.Effect, foodItem.GetEntry());
int pct;
int levelDiff = (int)pet.GetLevel() - (int)foodItem.GetTemplate().GetBaseItemLevel();
if (levelDiff >= 30)
return;
else if (levelDiff >= 20)
pct = (int)12.5; // we can't pass double so keeping the cast here for future references
else if (levelDiff >= 10)
pct = 25;
else
pct = 50;
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
CastSpellExtraArgs args = new(TriggerCastFlags.FullMask);
args.SetOriginalCastId(m_castId);
args.AddSpellMod(SpellValueMod.BasePoint0, pct);
m_caster.CastSpell(pet, effectInfo.TriggerSpell, args);
}
[SpellEffectHandler(SpellEffectName.DismissPet)]
void EffectDismissPet()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null || !unitTarget.IsPet())
return;
Pet pet = unitTarget.ToPet();
ExecuteLogEffectUnsummonObject(effectInfo.Effect, pet);
pet.GetOwner().RemovePet(pet, PetSaveMode.NotInSlot);
}
[SpellEffectHandler(SpellEffectName.SummonObjectSlot1)]
void EffectSummonObject()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (unitCaster == null)
return;
byte slot = (byte)(effectInfo.Effect - SpellEffectName.SummonObjectSlot1);
ObjectGuid guid = unitCaster.m_ObjectSlot[slot];
if (!guid.IsEmpty())
{
GameObject obj = unitCaster.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);
unitCaster.RemoveGameObject(obj, true);
}
unitCaster.m_ObjectSlot[slot].Clear();
}
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
unitCaster.GetClosePoint(out x, out y, out z, SharedConst.DefaultPlayerBoundingRadius);
Map map = m_caster.GetMap();
Position pos = new(x, y, z, m_caster.GetOrientation());
Quaternion rotation = Quaternion.CreateFromRotationMatrix(Extensions.fromEulerAnglesZYX(m_caster.GetOrientation(), 0.0f, 0.0f));
GameObject go = GameObject.CreateGameObject((uint)effectInfo.MiscValue, map, pos, rotation, 255, GameObjectState.Ready);
if (!go)
return;
PhasingHandler.InheritPhaseShift(go, m_caster);
go.SetFaction(unitCaster.GetFaction());
go.SetLevel(unitCaster.GetLevel());
int duration = m_spellInfo.CalcDuration(m_caster);
go.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0);
go.SetSpellId(m_spellInfo.Id);
unitCaster.AddGameObject(go);
ExecuteLogEffectSummonObject(effectInfo.Effect, go);
map.AddToMap(go);
unitCaster.m_ObjectSlot[slot] = go.GetGUID();
}
[SpellEffectHandler(SpellEffectName.Resurrect)]
void EffectResurrect()
{
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(effectInfo.Effect, target);
target.SetResurrectRequestData(m_caster, health, mana, 0);
SendResurrectRequest(target);
}
[SpellEffectHandler(SpellEffectName.AddExtraAttacks)]
void EffectAddExtraAttacks()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget || !unitTarget.IsAlive())
return;
if (unitTarget.ExtraAttacks != 0)
return;
unitTarget.ExtraAttacks = (uint)damage;
ExecuteLogEffectExtraAttacks(effectInfo.Effect, unitTarget, (uint)damage);
}
[SpellEffectHandler(SpellEffectName.Parry)]
void EffectParry()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (m_caster.IsTypeId(TypeId.Player))
m_caster.ToPlayer().SetCanParry(true);
}
[SpellEffectHandler(SpellEffectName.Block)]
void EffectBlock()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (m_caster.IsTypeId(TypeId.Player))
m_caster.ToPlayer().SetCanBlock(true);
}
[SpellEffectHandler(SpellEffectName.Leap)]
void EffectLeap()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null || unitTarget.IsInFlight())
return;
if (!m_targets.HasDst())
return;
Position pos = destTarget.GetPosition();
unitTarget.NearTeleportTo(pos.posX, pos.posY, pos.posZ, pos.Orientation, unitTarget == m_caster);
}
[SpellEffectHandler(SpellEffectName.Reputation)]
void EffectReputation()
{
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()
{
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 (quest.HasFlag(QuestFlags.Tracking)) // Check if the quest is used as a serverside flag.
player.SetRewardedQuest(questId); // If so, set status to rewarded without broadcasting it to client.
}
}
[SpellEffectHandler(SpellEffectName.ForceDeselect)]
void EffectForceDeselect()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (unitCaster == null)
return;
float dist = m_caster.GetVisibilityRange();
// clear focus
PacketSenderOwning<BreakTarget> breakTarget = new();
breakTarget.Data.UnitGUID = m_caster.GetGUID();
breakTarget.Data.Write();
var notifierBreak = new MessageDistDelivererToHostile<PacketSenderOwning<BreakTarget>>(unitCaster, breakTarget, dist);
Cell.VisitWorldObjects(m_caster, notifierBreak, dist);
// and selection
PacketSenderOwning<ClearTarget> clearTarget = new();
clearTarget.Data.Guid = m_caster.GetGUID();
clearTarget.Data.Write();
var notifierClear = new MessageDistDelivererToHostile<PacketSenderOwning<ClearTarget>>(unitCaster, clearTarget, dist);
Cell.VisitWorldObjects(m_caster, notifierClear, dist);
// we should also force pets to remove us from current target
List<Unit> attackerSet = new();
foreach (var unit in unitCaster.GetAttackers())
if (unit.GetTypeId() == TypeId.Unit && !unit.CanHaveThreatList())
attackerSet.Add(unit);
foreach (var unit in attackerSet)
unit.AttackStop();
}
[SpellEffectHandler(SpellEffectName.SelfResurrect)]
void EffectSelfResurrect()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
Player player = m_caster.ToPlayer();
if (player == null || !player.IsInWorld || player.IsAlive())
return;
uint health;
int mana = 0;
// flat case
if (damage < 0)
{
health = (uint)-damage;
mana = effectInfo.MiscValue;
}
// percent case
else
{
health = (uint)player.CountPctFromMaxHealth(damage);
if (player.GetMaxPower(PowerType.Mana) > 0)
mana = MathFunctions.CalculatePct(player.GetMaxPower(PowerType.Mana), damage);
}
player.ResurrectPlayer(0.0f);
player.SetHealth(health);
player.SetPower(PowerType.Mana, mana);
player.SetPower(PowerType.Rage, 0);
player.SetFullPower(PowerType.Energy);
player.SetPower(PowerType.Focus, 0);
player.SpawnCorpseBones();
}
[SpellEffectHandler(SpellEffectName.Skinning)]
void EffectSkinning()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget.IsTypeId(TypeId.Unit))
return;
Player player = m_caster.ToPlayer();
if (player == null)
return;
Creature creature = unitTarget.ToCreature();
int targetLevel = (int)creature.GetLevelForTarget(m_caster);
SkillType skill = creature.GetCreatureTemplate().GetRequiredLootSkill();
creature.RemoveUnitFlag(UnitFlags.Skinnable);
creature.AddDynamicFlag(UnitDynFlags.Lootable);
player.SendLoot(creature.GetGUID(), LootType.Skinning);
if (skill == SkillType.Skinning)
{
int reqValue;
if (targetLevel <= 10)
reqValue = 1;
else if (targetLevel < 20)
reqValue = (targetLevel - 10) * 10;
else if (targetLevel <= 73)
reqValue = targetLevel * 5;
else if (targetLevel < 80)
reqValue = targetLevel * 10 - 365;
else if (targetLevel <= 84)
reqValue = targetLevel * 5 + 35;
else if (targetLevel <= 87)
reqValue = targetLevel * 15 - 805;
else if (targetLevel <= 92)
reqValue = (targetLevel - 62) * 20;
else if (targetLevel <= 104)
reqValue = targetLevel * 5 + 175;
else if (targetLevel <= 107)
reqValue = targetLevel * 15 - 905;
else if (targetLevel <= 112)
reqValue = (targetLevel - 72) * 20;
else if (targetLevel <= 122)
reqValue = (targetLevel - 32) * 10;
else
reqValue = 900;
// TODO: Specialize skillid for each expansion
// new db field?
// tied to one of existing expansion fields in creature_template?
// Double chances for elites
m_caster.ToPlayer().UpdateGatherSkill(skill, (uint)damage, (uint)reqValue, (uint)(creature.IsElite() ? 2 : 1));
}
}
[SpellEffectHandler(SpellEffectName.Charge)]
void EffectCharge()
{
if (unitTarget == null)
return;
if (unitCaster == null)
return;
if (effectHandleMode == SpellEffectHandleMode.LaunchTarget)
{
// charge changes fall time
if (unitCaster.IsPlayer())
unitCaster.ToPlayer().SetFallInformation(0, m_caster.GetPositionZ());
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 == null)
{
Position pos = unitTarget.GetFirstCollisionPosition(unitTarget.GetCombatReach(), unitTarget.GetRelativeAngle(m_caster.GetPosition()));
if (MathFunctions.fuzzyGt(m_spellInfo.Speed, 0.0f) && m_spellInfo.HasAttribute(SpellAttr9.SpecialDelayCalculation))
speed = pos.GetExactDist(m_caster) / speed;
unitCaster.GetMotionMaster().MoveCharge(pos.posX, pos.posY, pos.posZ, speed, EventId.Charge, false, unitTarget, spellEffectExtraData);
}
else
{
if (MathFunctions.fuzzyGt(m_spellInfo.Speed, 0.0f) && m_spellInfo.HasAttribute(SpellAttr9.SpecialDelayCalculation))
{
Vector3 pos = m_preGeneratedPath.GetActualEndPosition();
speed = new Position(pos.X, pos.Y, pos.Z).GetExactDist(m_caster) / speed;
}
unitCaster.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))
unitCaster.Attack(unitTarget, true);
if (effectInfo.TriggerSpell != 0)
m_caster.CastSpell(unitTarget, effectInfo.TriggerSpell, new CastSpellExtraArgs(TriggerCastFlags.FullMask)
.SetOriginalCaster(m_originalCasterGUID)
.SetOriginalCastId(m_castId));
}
}
[SpellEffectHandler(SpellEffectName.ChargeDest)]
void EffectChargeDest()
{
if (destTarget == null)
return;
if (unitCaster == null)
return;
if (effectHandleMode == SpellEffectHandleMode.Launch)
{
Position pos = destTarget.GetPosition();
if (!unitCaster.IsWithinLOS(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()))
{
float angle = unitCaster.GetRelativeAngle(pos.posX, pos.posY);
float dist = unitCaster.GetDistance(pos);
pos = unitCaster.GetFirstCollisionPosition(dist, angle);
}
unitCaster.GetMotionMaster().MoveCharge(pos.posX, pos.posY, pos.posZ);
}
else if (effectHandleMode == SpellEffectHandleMode.Hit)
{
if (effectInfo.TriggerSpell != 0)
m_caster.CastSpell(destTarget, effectInfo.TriggerSpell, new CastSpellExtraArgs(TriggerCastFlags.FullMask)
.SetOriginalCaster(m_originalCasterGUID)
.SetOriginalCastId(m_castId));
}
}
[SpellEffectHandler(SpellEffectName.KnockBack)]
[SpellEffectHandler(SpellEffectName.KnockBackDest)]
void EffectKnockBack()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget)
return;
if (m_caster.GetAffectingPlayer())
{
Creature creatureTarget = unitTarget.ToCreature();
if (creatureTarget != null)
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.01f && speedz < 0.01f)
return;
Position origin;
if (effectInfo.Effect == SpellEffectName.KnockBackDest)
{
if (m_targets.HasDst())
origin = new(destTarget.GetPosition());
else
return;
}
else //if (effectInfo.Effect == SPELL_EFFECT_KNOCK_BACK)
origin = new(m_caster.GetPosition());
unitTarget.KnockbackFrom(origin, speedxy, speedz);
}
[SpellEffectHandler(SpellEffectName.LeapBack)]
void EffectLeapBack()
{
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);
// changes fall time
if (m_caster.GetTypeId() == TypeId.Player)
m_caster.ToPlayer().SetFallInformation(0, m_caster.GetPositionZ());
}
[SpellEffectHandler(SpellEffectName.ClearQuest)]
void EffectQuestClear()
{
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;
QuestStatus oldStatus = player.GetQuestStatus(quest_id);
// Player has never done this quest
if (oldStatus == 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);
Global.ScriptMgr.OnQuestStatusChange(player, quest, oldStatus, QuestStatus.None);
}
[SpellEffectHandler(SpellEffectName.SendTaxi)]
void EffectSendTaxi()
{
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()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget)
return;
Position pos = new();
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()
{
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()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null)
return;
int mechanic = effectInfo.MiscValue;
List<KeyValuePair<uint, ObjectGuid>> dispel_list = new();
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()
{
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.SetDynamicFlags(UnitDynFlags.HideModel);
pet.RemoveUnitFlag(UnitFlags.Skinnable);
pet.SetDeathState(DeathState.Alive);
pet.ClearUnitState(UnitState.AllErasable);
pet.SetHealth(pet.CountPctFromMaxHealth(damage));
pet.InitializeAI();
player.PetSpellInitialize();
pet.SavePetToDB(PetSaveMode.AsCurrent);
}
[SpellEffectHandler(SpellEffectName.DestroyAllTotems)]
void EffectDestroyAllTotems()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (unitCaster == null)
return;
int mana = 0;
for (byte slot = (int)SummonSlot.Totem; slot < SharedConst.MaxTotemSlot; ++slot)
{
if (unitCaster.m_SummonSlot[slot].IsEmpty())
continue;
Creature totem = unitCaster.GetMap().GetCreature(unitCaster.m_SummonSlot[slot]);
if (totem != null && totem.IsTotem())
{
uint spell_id = totem.m_unitData.CreatedBySpell;
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spell_id, GetCastDifficulty());
if (spellInfo != null)
{
var costs = spellInfo.CalcPowerCost(unitCaster, 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)
{
CastSpellExtraArgs args = new(TriggerCastFlags.FullMask);
args.SetOriginalCastId(m_castId);
args.AddSpellMod(SpellValueMod.BasePoint0, mana);
unitCaster.CastSpell(m_caster, 39104, args);
}
}
[SpellEffectHandler(SpellEffectName.DurabilityDamage)]
void EffectDurabilityDamage()
{
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(effectInfo.Effect, 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(effectInfo.Effect, unitTarget, (int)item.GetEntry(), slot);
}
}
[SpellEffectHandler(SpellEffectName.DurabilityDamagePct)]
void EffectDurabilityDamagePCT()
{
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()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitCaster == null || unitTarget == null)
return;
unitTarget.GetThreatManager().ModifyThreatByPercent(unitCaster, damage);
}
[SpellEffectHandler(SpellEffectName.TransDoor)]
void EffectTransmitted()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (unitCaster == null)
return;
uint name_id = (uint)effectInfo.MiscValue;
var overrideSummonedGameObjects = unitCaster.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(unitCaster);
unitCaster.GetClosePoint(out fx, out fy, out fz, SharedConst.DefaultPlayerBoundingRadius, 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;
unitCaster.GetClosePoint(out fx, out fy, out fz, SharedConst.DefaultPlayerBoundingRadius, dis);
}
Map cMap = unitCaster.GetMap();
// if gameobject is summoning object, it should be spawned right on caster's position
if (goinfo.type == GameObjectTypes.Ritual)
unitCaster.GetPosition(out fx, out fy, out fz);
Position pos = new(fx, fy, fz, unitCaster.GetOrientation());
Quaternion rotation = Quaternion.CreateFromRotationMatrix(Extensions.fromEulerAnglesZYX(unitCaster.GetOrientation(), 0.0f, 0.0f));
GameObject go = GameObject.CreateGameObject(name_id, cMap, pos, rotation, 255, GameObjectState.Ready);
if (!go)
return;
PhasingHandler.InheritPhaseShift(go, m_caster);
int duration = m_spellInfo.CalcDuration(m_caster);
switch (goinfo.type)
{
case GameObjectTypes.FishingNode:
{
go.SetFaction(unitCaster.GetFaction());
ObjectGuid bobberGuid = go.GetGUID();
// client requires fishing bobber guid in channel object slot 0 to be usable
unitCaster.SetChannelObject(0, bobberGuid);
unitCaster.AddGameObject(go); // will removed at spell cancel
// end time of range when possible catch fish (FISHING_BOBBER_READY_TIME..GetDuration(m_spellInfo))
// start time == fish-FISHING_BOBBER_READY_TIME (0..GetDuration(m_spellInfo)-FISHING_BOBBER_READY_TIME)
int lastSec = 0;
switch (RandomHelper.IRand(0, 2))
{
case 0: lastSec = 3; break;
case 1: lastSec = 7; break;
case 2: lastSec = 13; break;
}
// Duration of the fishing bobber can't be higher than the Fishing channeling duration
duration = Math.Min(duration, duration - lastSec * Time.InMilliseconds + 5 * Time.InMilliseconds);
break;
}
case GameObjectTypes.Ritual:
{
if (unitCaster.IsPlayer())
{
go.AddUniqueUse(unitCaster.ToPlayer());
unitCaster.AddGameObject(go); // will be removed at spell cancel
}
break;
}
case GameObjectTypes.DuelArbiter: // 52991
unitCaster.AddGameObject(go);
break;
case GameObjectTypes.FishingHole:
case GameObjectTypes.Chest:
default:
break;
}
go.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0);
go.SetOwnerGUID(unitCaster.GetGUID());
go.SetSpellId(m_spellInfo.Id);
ExecuteLogEffectSummonObject(effectInfo.Effect, go);
Log.outDebug(LogFilter.Spells, "AddObject at SpellEfects.cpp EffectTransmitted");
cMap.AddToMap(go);
GameObject linkedTrap = go.GetLinkedTrap();
if (linkedTrap != null)
{
PhasingHandler.InheritPhaseShift(linkedTrap, m_caster);
linkedTrap.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0);
linkedTrap.SetSpellId(m_spellInfo.Id);
linkedTrap.SetOwnerGUID(unitCaster.GetGUID());
ExecuteLogEffectSummonObject(effectInfo.Effect, linkedTrap);
}
}
[SpellEffectHandler(SpellEffectName.Prospecting)]
void EffectProspecting()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
Player player = m_caster.ToPlayer();
if (player == null)
return;
if (itemTarget == null || !itemTarget.GetTemplate().HasFlag(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()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
Player player = m_caster.ToPlayer();
if (player == null)
return;
if (itemTarget == null || !itemTarget.GetTemplate().HasFlag(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()
{
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()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
}
// remove insignia spell effect
[SpellEffectHandler(SpellEffectName.SkinPlayerCorpse)]
void EffectSkinPlayerCorpse()
{
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()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
Log.outDebug(LogFilter.Spells, "Effect: StealBeneficialBuff");
if (unitTarget == null || unitTarget == m_caster) // can't steal from self
return;
List<DispelableAura> stealList = new();
// 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;
// 2.4.3 Patch Notes: "Dispel effects will no longer attempt to remove effects that have 100% dispel resistance."
int chance = aura.CalcDispelChance(unitTarget, !unitTarget.IsFriendlyTo(m_caster));
if (chance == 0)
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)
stealList.Add(new DispelableAura(aura, chance, charges));
}
}
if (stealList.Empty())
return;
int remaining = stealList.Count;
// Ok if exist some buffs for dispel try dispel it
List<Tuple<uint, ObjectGuid>> successList = new();
DispelFailed dispelFailed = new();
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 && remaining > 0;)
{
// Random select buff for dispel
var dispelableAura = stealList[RandomHelper.IRand(0, remaining - 1)];
if (dispelableAura.RollDispel())
{
successList.Add(Tuple.Create(dispelableAura.GetAura().GetId(), dispelableAura.GetAura().GetCasterGUID()));
if (!dispelableAura.DecrementCharge())
{
--remaining;
stealList[remaining] = dispelableAura;
}
}
else
{
dispelFailed.FailedSpells.Add(dispelableAura.GetAura().GetId());
}
++count;
}
if (!dispelFailed.FailedSpells.Empty())
m_caster.SendMessageToSet(dispelFailed, true);
if (successList.Empty())
return;
SpellDispellLog spellDispellLog = new();
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 successList)
{
var dispellData = new SpellDispellData();
dispellData.SpellID = dispell.Item1;
dispellData.Harmful = false; // TODO: use me
//dispellData.Rolled = none; // TODO: use me
//dispellData.Needed = none; // TODO: use me
unitTarget.RemoveAurasDueToSpellBySteal(dispell.Item1, dispell.Item2, m_caster);
spellDispellLog.DispellData.Add(dispellData);
}
m_caster.SendMessageToSet(spellDispellLog, true);
}
[SpellEffectHandler(SpellEffectName.KillCredit)]
void EffectKillCreditPersonal()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
return;
unitTarget.ToPlayer().KilledMonsterCredit((uint)effectInfo.MiscValue);
}
[SpellEffectHandler(SpellEffectName.KillCredit2)]
void EffectKillCredit()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
return;
int creatureEntry = effectInfo.MiscValue;
if (creatureEntry != 0)
unitTarget.ToPlayer().RewardPlayerAndGroupAtEvent((uint)creatureEntry, unitTarget);
}
[SpellEffectHandler(SpellEffectName.QuestFail)]
void EffectQuestFail()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
return;
unitTarget.ToPlayer().FailQuest((uint)effectInfo.MiscValue);
}
[SpellEffectHandler(SpellEffectName.QuestStart)]
void EffectQuestStart()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget)
return;
Player player = unitTarget.ToPlayer();
if (!player)
return;
Quest quest = Global.ObjectMgr.GetQuestTemplate((uint)effectInfo.MiscValue);
if (quest != null)
{
if (!player.CanTakeQuest(quest, false))
return;
if (quest.IsAutoAccept() && player.CanAddQuest(quest, false))
{
player.AddQuestAndCheckCompletion(quest, null);
player.PlayerTalkClass.SendQuestGiverQuestDetails(quest, player.GetGUID(), true, true);
}
else
player.PlayerTalkClass.SendQuestGiverQuestDetails(quest, player.GetGUID(), true, false);
}
}
[SpellEffectHandler(SpellEffectName.CreateTamedPet)]
void EffectCreateTamedPet()
{
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.GetCombatReach(), 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()
{
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()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (m_caster.IsTypeId(TypeId.Player))
m_caster.ToPlayer().SetCanTitanGrip(true, (uint)effectInfo.MiscValue);
}
[SpellEffectHandler(SpellEffectName.RedirectThreat)]
void EffectRedirectThreat()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitCaster == null)
return;
if (unitTarget != null)
unitCaster.GetThreatManager().RegisterRedirectThreat(m_spellInfo.Id, unitTarget.GetGUID(), (uint)damage);
}
[SpellEffectHandler(SpellEffectName.GameObjectDamage)]
void EffectGameObjectDamage()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (gameObjTarget == null)
return;
FactionTemplateRecord casterFaction = m_caster.GetFactionTemplateEntry();
FactionTemplateRecord targetFaction = CliDB.FactionTemplateStorage.LookupByKey(gameObjTarget.GetFaction());
// Do not allow to damage GO's of friendly factions (ie: Wintergrasp Walls/Ulduar Storm Beacons)
if (targetFaction == null || (casterFaction != null && !casterFaction.IsFriendlyTo(targetFaction)))
gameObjTarget.ModifyHealth(-damage, m_caster, GetSpellInfo().Id);
}
[SpellEffectHandler(SpellEffectName.GameobjectRepair)]
void EffectGameObjectRepair()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (gameObjTarget == null)
return;
gameObjTarget.ModifyHealth(damage, m_caster);
}
[SpellEffectHandler(SpellEffectName.GameobjectSetDestructionState)]
void EffectGameObjectSetDestructionState()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (gameObjTarget == null)
return;
gameObjTarget.SetDestructibleState((GameObjectDestructibleState)effectInfo.MiscValue, m_caster, true);
}
void SummonGuardian(SpellEffectInfo effect, uint entry, SummonPropertiesRecord properties, uint numGuardians, ObjectGuid privateObjectOwner)
{
if (unitCaster == null)
return;
if (unitCaster.IsTotem())
unitCaster = unitCaster.ToTotem().GetOwner();
// in another case summon new
uint level = unitCaster.GetLevel();
// level of pet summoned using engineering item based at engineering skill level
if (m_CastItem != null && unitCaster.IsPlayer())
{
ItemTemplate proto = m_CastItem.GetTemplate();
if (proto != null)
if (proto.GetRequiredSkill() == (uint)SkillType.Engineering)
{
ushort skill202 = unitCaster.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 = unitCaster.GetMap();
for (uint count = 0; count < numGuardians; ++count)
{
Position pos;
if (count == 0)
pos = destTarget;
else
// randomize position for multiple summons
pos = unitCaster.GetRandomPoint(destTarget, radius);
TempSummon summon = map.SummonCreature(entry, pos, properties, (uint)duration, unitCaster, m_spellInfo.Id, 0, privateObjectOwner);
if (summon == null)
return;
if (summon.HasUnitTypeMask(UnitTypeMask.Guardian))
((Guardian)summon).InitStatsForLevel(level);
if (summon.HasUnitTypeMask(UnitTypeMask.Minion) && m_targets.HasDst())
((Minion)summon).SetFollowAngle(unitCaster.GetAbsoluteAngle(summon.GetPosition()));
if (summon.GetEntry() == 27893)
{
VisibleItem weapon = m_caster.ToPlayer().m_playerData.VisibleItems[EquipmentSlot.MainHand];
if (weapon.ItemID != 0)
{
summon.SetDisplayId(11686);
summon.SetVirtualItem(0, weapon.ItemID, weapon.ItemAppearanceModID, weapon.ItemVisual);
}
else
summon.SetDisplayId(1126);
}
ExecuteLogEffectSummonObject(effect.Effect, summon);
}
}
[SpellEffectHandler(SpellEffectName.AllowRenamePet)]
void EffectRenamePet()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Unit) ||
!unitTarget.IsPet() || unitTarget.ToPet().GetPetType() != PetType.Hunter)
return;
unitTarget.AddPetFlag(UnitPetFlags.CanBeRenamed);
}
[SpellEffectHandler(SpellEffectName.PlayMusic)]
void EffectPlayMusic()
{
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()
{
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()
{
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)]
[SpellEffectHandler(SpellEffectName.RemoveAura2)]
void EffectRemoveAura()
{
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()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null)
return;
m_damage += (int)unitTarget.CountPctFromMaxHealth(damage);
}
[SpellEffectHandler(SpellEffectName.GiveCurrency)]
void EffectGiveCurrency()
{
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()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
Player player = m_caster.ToPlayer();
if (player == null)
return;
int button_id = effectInfo.MiscValue + 132;
int n_buttons = effectInfo.MiscValueB;
for (; n_buttons != 0; --n_buttons, ++button_id)
{
ActionButton ab = player.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, GetCastDifficulty());
if (spellInfo == null)
continue;
if (!player.HasSpell(spell_id) || player.GetSpellHistory().HasCooldown(spell_id))
continue;
if (!spellInfo.HasAttribute(SpellAttr9.SummonPlayerTotem))
continue;
CastSpellExtraArgs args = new(TriggerCastFlags.IgnoreGCD | TriggerCastFlags.IgnoreCastInProgress | TriggerCastFlags.CastDirectly | TriggerCastFlags.DontReportCastError);
args.OriginalCastId = m_castId;
args.CastDifficulty = GetCastDifficulty();
m_caster.CastSpell(m_caster, spellInfo.Id, args);
}
}
[SpellEffectHandler(SpellEffectName.RechargeItem)]
void EffectRechargeItem()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null)
return;
Player player = unitTarget.ToPlayer();
if (player == null)
return;
Item item = player.GetItemByEntry(effectInfo.ItemType);
if (item != null)
{
foreach (ItemEffectRecord itemEffect in item.GetEffects())
if (itemEffect.LegacySlotIndex <= item.m_itemData.SpellCharges.GetSize())
item.SetSpellCharges(itemEffect.LegacySlotIndex, itemEffect.Charges);
item.SetState(ItemUpdateState.Changed, player);
}
}
[SpellEffectHandler(SpellEffectName.Bind)]
void EffectBind()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
return;
Player player = unitTarget.ToPlayer();
WorldLocation homeLoc = new();
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: {homeLoc}, AreaId: {areaId}");
// zone update
player.SendPlayerBound(m_caster.GetGUID(), areaId);
}
[SpellEffectHandler(SpellEffectName.TeleportToReturnPoint)]
void EffectTeleportToReturnPoint()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
Player player = unitTarget.ToPlayer();
if (player != null)
{
WorldLocation dest = player.GetStoredAuraTeleportLocation((uint)effectInfo.MiscValue);
if (dest != null)
player.TeleportTo(dest, unitTarget == m_caster ? TeleportToOptions.Spell | TeleportToOptions.NotLeaveCombat : 0);
}
}
[SpellEffectHandler(SpellEffectName.SummonRafFriend)]
void EffectSummonRaFFriend()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!m_caster.IsTypeId(TypeId.Player) || unitTarget == null || !unitTarget.IsTypeId(TypeId.Player))
return;
m_caster.CastSpell(unitTarget, effectInfo.TriggerSpell, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetOriginalCastId(m_castId));
}
[SpellEffectHandler(SpellEffectName.UnlockGuildVaultTab)]
void EffectUnlockGuildVaultTab()
{
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)(damage - 1)); // Bank tabs start at zero internally
}
[SpellEffectHandler(SpellEffectName.SummonPersonalGameobject)]
void EffectSummonPersonalGameObject()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
uint goId = (uint)effectInfo.MiscValue;
if (goId == 0)
return;
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.DefaultPlayerBoundingRadius);
Map map = m_caster.GetMap();
Position pos = new(x, y, z, m_caster.GetOrientation());
Quaternion rot = Quaternion.CreateFromRotationMatrix(Extensions.fromEulerAnglesZYX(m_caster.GetOrientation(), 0.0f, 0.0f));
GameObject go = GameObject.CreateGameObject(goId, map, pos, rot, 255, GameObjectState.Ready);
if (!go)
{
Log.outWarn(LogFilter.Spells, $"SpellEffect Failed to summon personal gameobject. SpellId {m_spellInfo.Id}, effect {effectInfo.EffectIndex}");
return;
}
PhasingHandler.InheritPhaseShift(go, m_caster);
int duration = m_spellInfo.CalcDuration(m_caster);
go.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0);
go.SetSpellId(m_spellInfo.Id);
go.SetPrivateObjectOwner(m_caster.GetGUID());
ExecuteLogEffectSummonObject(effectInfo.Effect, go);
map.AddToMap(go);
GameObject linkedTrap = go.GetLinkedTrap();
if (linkedTrap != null)
{
PhasingHandler.InheritPhaseShift(linkedTrap, m_caster);
linkedTrap.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0);
linkedTrap.SetSpellId(m_spellInfo.Id);
ExecuteLogEffectSummonObject(effectInfo.Effect, linkedTrap);
}
}
[SpellEffectHandler(SpellEffectName.ResurrectWithAura)]
void EffectResurrectWithAura()
{
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.HasSpellInfo(effectInfo.TriggerSpell, Difficulty.None))
resurrectAura = effectInfo.TriggerSpell;
if (resurrectAura != 0 && target.HasAura(resurrectAura))
return;
ExecuteLogEffectResurrect(effectInfo.Effect, target);
target.SetResurrectRequestData(m_caster, health, mana, resurrectAura);
SendResurrectRequest(target);
}
[SpellEffectHandler(SpellEffectName.CreateAreaTrigger)]
void EffectCreateAreaTrigger()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (unitCaster == null || !m_targets.HasDst())
return;
int duration = GetSpellInfo().CalcDuration(GetCaster());
AreaTrigger.CreateAreaTrigger((uint)effectInfo.MiscValue, unitCaster, null, GetSpellInfo(), destTarget.GetPosition(), duration, m_SpellVisual, m_castId);
}
[SpellEffectHandler(SpellEffectName.RemoveTalent)]
void EffectRemoveTalent()
{
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()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
return;
Player player = unitTarget.ToPlayer();
Item item = player.GetItemByEntry(effectInfo.ItemType);
if (item)
player.DestroyItem(item.GetBagSlot(), item.GetSlot(), true);
}
[SpellEffectHandler(SpellEffectName.LearnGarrisonBuilding)]
void EffectLearnGarrisonBuilding()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
return;
Garrison garrison = unitTarget.ToPlayer().GetGarrison();
if (garrison != null)
garrison.LearnBlueprint((uint)effectInfo.MiscValue);
}
[SpellEffectHandler(SpellEffectName.CreateGarrison)]
void EffectCreateGarrison()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
return;
unitTarget.ToPlayer().CreateGarrison((uint)effectInfo.MiscValue);
}
[SpellEffectHandler(SpellEffectName.CreateConversation)]
void EffectCreateConversation()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (unitCaster == null || !m_targets.HasDst())
return;
Conversation.CreateConversation((uint)effectInfo.MiscValue, unitCaster, destTarget.GetPosition(), ObjectGuid.Empty, GetSpellInfo());
}
[SpellEffectHandler(SpellEffectName.CancelConversation)]
void EffectCancelConversation()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget)
return;
List<WorldObject> objs = new();
ObjectEntryAndPrivateOwnerIfExistsCheck check = new(unitTarget.GetGUID(), (uint)effectInfo.MiscValue);
WorldObjectListSearcher checker = new(unitTarget, objs, check, GridMapTypeMask.Conversation);
Cell.VisitGridObjects(unitTarget, checker, 100.0f);
foreach (WorldObject obj in objs)
{
Conversation convo = obj.ToConversation();
if (convo != null)
convo.Remove();
}
}
[SpellEffectHandler(SpellEffectName.AddGarrisonFollower)]
void EffectAddGarrisonFollower()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
return;
Garrison garrison = unitTarget.ToPlayer().GetGarrison();
if (garrison != null)
garrison.AddFollower((uint)effectInfo.MiscValue);
}
[SpellEffectHandler(SpellEffectName.CreateHeirloomItem)]
void EffectCreateHeirloomItem()
{
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();
bonusList.Add(collectionMgr.GetHeirloomBonus(m_misc.Data0));
DoCreateItem(m_misc.Data0, ItemContext.None, bonusList);
ExecuteLogEffectCreateItem(effectInfo.Effect, m_misc.Data0);
}
[SpellEffectHandler(SpellEffectName.ActivateGarrisonBuilding)]
void EffectActivateGarrisonBuilding()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
return;
Garrison garrison = unitTarget.ToPlayer().GetGarrison();
if (garrison != null)
garrison.ActivateBuilding((uint)effectInfo.MiscValue);
}
[SpellEffectHandler(SpellEffectName.HealBattlepetPct)]
void EffectHealBattlePetPct()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
return;
BattlePetMgr battlePetMgr = unitTarget.ToPlayer().GetSession().GetBattlePetMgr();
if (battlePetMgr != null)
battlePetMgr.HealBattlePetsPct((byte)damage);
}
[SpellEffectHandler(SpellEffectName.EnableBattlePets)]
void EffectEnableBattlePets()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (unitTarget == null || !unitTarget.IsPlayer())
return;
Player player = unitTarget.ToPlayer();
player.AddPlayerFlag(PlayerFlags.PetBattlesUnlocked);
player.GetSession().GetBattlePetMgr().UnlockSlot(BattlePetSlots.Slot0);
}
[SpellEffectHandler(SpellEffectName.ChangeBattlepetQuality)]
void EffectChangeBattlePetQuality()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
Player playerCaster = m_caster.ToPlayer();
if (playerCaster == null)
return;
if (unitTarget == null || !unitTarget.IsCreature())
return;
var quality = BattlePetBreedQuality.Poor;
switch (damage)
{
case 85:
quality = BattlePetBreedQuality.Rare;
break;
case 75:
quality = BattlePetBreedQuality.Uncommon;
break;
default:
// Ignore Epic Battle-Stones
break;
}
playerCaster.GetSession().GetBattlePetMgr().ChangeBattlePetQuality(unitTarget.GetBattlePetCompanionGUID(), quality);
}
[SpellEffectHandler(SpellEffectName.LaunchQuestChoice)]
void EffectLaunchQuestChoice()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget || !unitTarget.IsPlayer())
return;
unitTarget.ToPlayer().SendPlayerChoice(GetCaster().GetGUID(), effectInfo.MiscValue);
}
[SpellEffectHandler(SpellEffectName.UncageBattlepet)]
void EffectUncageBattlePet()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (!m_CastItem || !m_caster || !m_caster.IsTypeId(TypeId.Player))
return;
uint speciesId = m_CastItem.GetModifier(ItemModifier.BattlePetSpeciesId);
ushort breed = (ushort)(m_CastItem.GetModifier(ItemModifier.BattlePetBreedData) & 0xFFFFFF);
BattlePetBreedQuality quality = (BattlePetBreedQuality)((m_CastItem.GetModifier(ItemModifier.BattlePetBreedData) >> 24) & 0xFF);
ushort level = (ushort)m_CastItem.GetModifier(ItemModifier.BattlePetLevel);
uint displayId = m_CastItem.GetModifier(ItemModifier.BattlePetDisplayId);
BattlePetSpeciesRecord speciesEntry = CliDB.BattlePetSpeciesStorage.LookupByKey(speciesId);
if (speciesEntry == null)
return;
Player player = m_caster.ToPlayer();
BattlePetMgr battlePetMgr = player.GetSession().GetBattlePetMgr();
if (battlePetMgr == null)
return;
if (battlePetMgr.GetMaxPetLevel() < level)
{
battlePetMgr.SendError(BattlePetError.TooHighLevelToUncage, speciesEntry.CreatureID);
SendCastResult(SpellCastResult.CantAddBattlePet);
return;
}
if (battlePetMgr.HasMaxPetCount(speciesEntry, player.GetGUID()))
{
battlePetMgr.SendError(BattlePetError.CantHaveMorePetsOfThatType, speciesEntry.CreatureID);
SendCastResult(SpellCastResult.CantAddBattlePet);
return;
}
battlePetMgr.AddPet(speciesId, displayId, breed, quality, level);
player.SendPlaySpellVisual(player, SharedConst.SpellVisualUncagePet, 0, 0, 0.0f, false);
player.DestroyItem(m_CastItem.GetBagSlot(), m_CastItem.GetSlot(), true);
m_CastItem = null;
}
[SpellEffectHandler(SpellEffectName.UpgradeHeirloom)]
void EffectUpgradeHeirloom()
{
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()
{
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.SetVisibleItemSlot(itemTarget.GetSlot(), itemTarget);
player.RemoveTradeableItem(itemTarget);
itemTarget.ClearSoulboundTradeable(player);
}
[SpellEffectHandler(SpellEffectName.UpdatePlayerPhase)]
void EffectUpdatePlayerPhase()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
return;
PhasingHandler.OnConditionChange(unitTarget);
}
[SpellEffectHandler(SpellEffectName.UpdateZoneAurasPhases)]
void EffectUpdateZoneAurasAndPhases()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player))
return;
unitTarget.ToPlayer().UpdateAreaDependentAuras(unitTarget.GetAreaId());
}
[SpellEffectHandler(SpellEffectName.GiveArtifactPower)]
void EffectGiveArtifactPower()
{
if (effectHandleMode != SpellEffectHandleMode.LaunchTarget)
return;
Player playerCaster = m_caster.ToPlayer();
if (playerCaster == null)
return;
Aura artifactAura = playerCaster.GetAura(PlayerConst.ArtifactsAllWeaponsGeneralWeaponEquippedPassive);
if (artifactAura != null)
{
Item artifact = playerCaster.GetItemByGuid(artifactAura.GetCastItemGUID());
if (artifact)
artifact.GiveArtifactXp((ulong)damage, m_CastItem, (ArtifactCategory)effectInfo.MiscValue);
}
}
[SpellEffectHandler(SpellEffectName.GiveArtifactPowerNoBonus)]
void EffectGiveArtifactPowerNoBonus()
{
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.PlaySceneScriptPackage)]
void EffectPlaySceneScriptPackage()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (!m_caster.IsTypeId(TypeId.Player))
return;
m_caster.ToPlayer().GetSceneMgr().PlaySceneByPackageId((uint)effectInfo.MiscValue, SceneFlags.PlayerNonInteractablePhased, destTarget);
}
bool IsUnitTargetSceneObjectAura(Spell spell, TargetInfo target)
{
if (target.TargetGUID != spell.GetCaster().GetGUID())
return false;
foreach (SpellEffectInfo spellEffectInfo in spell.GetSpellInfo().GetEffects())
if ((target.EffectMask & (1 << (int)spellEffectInfo.EffectIndex)) != 0 && spellEffectInfo.IsUnitOwnedAuraEffect())
return true;
return false;
}
[SpellEffectHandler(SpellEffectName.CreateSceneObject)]
void EffectCreateSceneObject()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (!unitCaster || !m_targets.HasDst())
return;
SceneObject sceneObject = SceneObject.CreateSceneObject((uint)effectInfo.MiscValue, unitCaster, destTarget.GetPosition(), ObjectGuid.Empty);
if (sceneObject != null)
{
bool hasAuraTargetingCaster = m_UniqueTargetInfo.Any(target => IsUnitTargetSceneObjectAura(this, target));
if (hasAuraTargetingCaster)
sceneObject.SetCreatedBySpellCast(m_castId);
}
}
[SpellEffectHandler(SpellEffectName.CreatePersonalSceneObject)]
void EffectCreatePrivateSceneObject()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (!unitCaster || !m_targets.HasDst())
return;
SceneObject sceneObject = SceneObject.CreateSceneObject((uint)effectInfo.MiscValue, unitCaster, destTarget.GetPosition(), unitCaster.GetGUID());
if (sceneObject != null)
{
bool hasAuraTargetingCaster = m_UniqueTargetInfo.Any(target => IsUnitTargetSceneObjectAura(this, target));
if (hasAuraTargetingCaster)
sceneObject.SetCreatedBySpellCast(m_castId);
}
}
[SpellEffectHandler(SpellEffectName.PlayScene)]
void EffectPlayScene()
{
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()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget || unitTarget.GetTypeId() != TypeId.Player)
return;
PvPCredit packet = new();
packet.Honor = damage;
packet.OriginalHonor = damage;
Player playerTarget = unitTarget.ToPlayer();
playerTarget.AddHonorXP((uint)damage);
playerTarget.SendPacket(packet);
}
[SpellEffectHandler(SpellEffectName.JumpCharge)]
void EffectJumpCharge()
{
if (effectHandleMode != SpellEffectHandleMode.Launch)
return;
if (unitCaster == null)
return;
if (unitCaster.IsInFlight())
return;
JumpChargeParams jumpParams = Global.ObjectMgr.GetJumpChargeParams(effectInfo.MiscValue);
if (jumpParams == null)
return;
float speed = jumpParams.Speed;
if (jumpParams.TreatSpeedAsMoveTimeSeconds)
speed = unitCaster.GetExactDist(destTarget) / jumpParams.Speed;
Optional<JumpArrivalCastArgs> arrivalCast = new();
if (effectInfo.TriggerSpell != 0)
{
arrivalCast.HasValue = true;
arrivalCast.Value.SpellId = effectInfo.TriggerSpell;
}
Optional<SpellEffectExtraData> effectExtra = new();
if (jumpParams.SpellVisualId.HasValue || jumpParams.ProgressCurveId.HasValue || jumpParams.ParabolicCurveId.HasValue)
{
effectExtra.HasValue = true;
if (jumpParams.SpellVisualId.HasValue)
effectExtra.Value.SpellVisualId = jumpParams.SpellVisualId.Value;
if (jumpParams.ProgressCurveId.HasValue)
effectExtra.Value.ProgressCurveId = jumpParams.ProgressCurveId.Value;
if (jumpParams.ParabolicCurveId.HasValue)
effectExtra.Value.ParabolicCurveId = jumpParams.ParabolicCurveId.Value;
}
unitCaster.GetMotionMaster().MoveJumpWithGravity(destTarget, speed, jumpParams.JumpGravity, EventId.Jump, false, arrivalCast.Value, effectExtra.Value);
}
[SpellEffectHandler(SpellEffectName.LearnTransmogSet)]
void EffectLearnTransmogSet()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
if (!unitTarget || !unitTarget.IsPlayer())
return;
unitTarget.ToPlayer().GetSession().GetCollectionMgr().AddTransmogSet((uint)effectInfo.MiscValue);
}
[SpellEffectHandler(SpellEffectName.LearnAzeriteEssencePower)]
void EffectLearnAzeriteEssencePower()
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
return;
Player playerTarget = unitTarget != null ? unitTarget.ToPlayer() : null;
if (!playerTarget)
return;
Item heartOfAzeroth = playerTarget.GetItemByEntry(PlayerConst.ItemIdHeartOfAzeroth, ItemSearchLocation.Everywhere);
if (heartOfAzeroth == null)
return;
AzeriteItem azeriteItem = heartOfAzeroth.ToAzeriteItem();
if (azeriteItem == null)
return;
// remove old rank and apply new one
if (azeriteItem.IsEquipped())
{
SelectedAzeriteEssences selectedEssences = azeriteItem.GetSelectedAzeriteEssences();
if (selectedEssences != null)
{
for (int slot = 0; slot < SharedConst.MaxAzeriteEssenceSlot; ++slot)
{
if (selectedEssences.AzeriteEssenceID[slot] == effectInfo.MiscValue)
{
bool major = (AzeriteItemMilestoneType)Global.DB2Mgr.GetAzeriteItemMilestonePower(slot).Type == AzeriteItemMilestoneType.MajorEssence;
playerTarget.ApplyAzeriteEssence(azeriteItem, (uint)effectInfo.MiscValue, SharedConst.MaxAzeriteEssenceRank, major, false);
playerTarget.ApplyAzeriteEssence(azeriteItem, (uint)effectInfo.MiscValue, (uint)effectInfo.MiscValueB, major, false);
break;
}
}
}
}
azeriteItem.SetEssenceRank((uint)effectInfo.MiscValue, (uint)effectInfo.MiscValueB);
azeriteItem.SetState(ItemUpdateState.Changed, playerTarget);
}
[SpellEffectHandler(SpellEffectName.CreatePrivateConversation)]
void EffectCreatePrivateConversation()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (unitCaster == null || unitTarget == null || !unitTarget.IsPlayer())
return;
Conversation.CreateConversation((uint)effectInfo.MiscValue, unitCaster, unitTarget.GetPosition(), unitTarget.GetGUID(), GetSpellInfo());
}
[SpellEffectHandler(SpellEffectName.SendChatMessage)]
void EffectSendChatMessage()
{
if (effectHandleMode != SpellEffectHandleMode.Hit)
return;
if (unitCaster == null)
return;
uint broadcastTextId = (uint)effectInfo.MiscValue;
if (!CliDB.BroadcastTextStorage.ContainsKey(broadcastTextId))
return;
ChatMsg chatType = (ChatMsg)effectInfo.MiscValueB;
unitCaster.Talk(broadcastTextId, chatType, Global.CreatureTextMgr.GetRangeForChatType(chatType), unitTarget);
}
}
public class DispelableAura
{
public DispelableAura(Aura aura, int dispelChance, byte dispelCharges)
{
_aura = aura;
_chance = dispelChance;
_charges = dispelCharges;
}
public bool RollDispel()
{
return RandomHelper.randChance(_chance);
}
public Aura GetAura()
{
return _aura;
}
public byte GetDispelCharges()
{
return _charges;
}
public void IncrementCharges()
{
++_charges;
}
public bool DecrementCharge()
{
if (_charges == 0)
return false;
--_charges;
return _charges > 0;
}
Aura _aura;
int _chance;
byte _charges;
}
class DelayedSpellTeleportEvent : BasicEvent
{
Unit _target;
WorldLocation _targetDest;
TeleportToOptions _options;
uint _spellId;
public DelayedSpellTeleportEvent(Unit target, WorldLocation targetDest, TeleportToOptions options, uint spellId)
{
_target = target;
_targetDest = targetDest;
_options = options;
_spellId = spellId;
}
public override bool Execute(ulong e_time, uint p_time)
{
if (_targetDest.GetMapId() == _target.GetMapId())
_target.NearTeleportTo(_targetDest, (_options & TeleportToOptions.Spell) != 0);
else
{
Player player = _target.ToPlayer();
if (player != null)
player.TeleportTo(_targetDest, _options);
else
Log.outError(LogFilter.Spells, $"Spell::EffectTeleportUnitsWithVisualLoadingScreen - spellId {_spellId} attempted to teleport creature to a different map.");
}
return true;
}
}
}