Implementation of QAston proc system

This commit is contained in:
hondacrx
2017-08-14 08:12:31 -04:00
parent df8f6133b3
commit 9a039f35a2
23 changed files with 1083 additions and 1730 deletions
+1 -1
View File
@@ -604,7 +604,7 @@ namespace Framework.Constants
CommandReloadSpellLootTemplate = 695,
CommandReloadSpellLinkedSpell = 696,
CommandReloadSpellPetAuras = 697,
CommandReloadSpellProcEvent = 698,
// 698 - reuse
CommandReloadSpellProc = 699,
CommandReloadSpellScripts = 700,
CommandReloadSpellTargetPosition = 701,
+1
View File
@@ -43,6 +43,7 @@ namespace Framework.Constants
AfterDispel,
// Spell Proc Hooks
CheckProc,
CheckEffectProc,
PrepareProc,
Proc,
EffectProc,
+5 -5
View File
@@ -244,9 +244,9 @@ namespace Framework.Constants
ModRatingFromStat = 220,
ModDetaunt = 221,
Unk222 = 222,
RaidProcFromCharge = 223,
Unk223 = 223,
Unk224 = 224,
RaidProcFromChargeWithValue = 225,
ModVisibilityRange = 225,
PeriodicDummy = 226,
PeriodicTriggerSpellWithValue = 227,
DetectStealth = 228,
@@ -349,7 +349,7 @@ namespace Framework.Constants
Unk325 = 325, // Not Used In 4.3.4
PhaseGroup = 326, // Phase Related
Unk327 = 327, // Not Used In 4.3.4
ProcOnPowerAmount = 328,
TriggerSpellOnPowerPct = 328,
ModPowerGainPct = 329, // Nyi
CastWhileWalking = 330,
ForceWeather = 331,
@@ -417,7 +417,7 @@ namespace Framework.Constants
Unk393 = 393,
ShowConfirmationPrompt = 394,
AreaTrigger = 395, // NYI
ProcOnPowerAmount2 = 396,
TriggerSpellOnPowerAmount = 396,
Unk397 = 397,
Unk398 = 398,
Unk399 = 399,
@@ -489,7 +489,7 @@ namespace Framework.Constants
ModBonusArmor = 465, // NYI
ModBonusArmorPct = 466,
ModStatBonusPct = 467,
TriggerSpellOnHealthBelowPct = 468,
TriggerSpellOnHealthPct = 468,
ShowConfirmationPromptWithDifficulty = 469,
Unk470 = 470,
ModVersatility = 471, // NYI
+11 -34
View File
@@ -341,7 +341,9 @@ namespace Framework.Constants
Effect5 = 33,
SpellCost2 = 34, // Used when SpellPowerEntry.PowerIndex == 1
JumpDistance = 35,
// spellmod 36
StackAmount2 = 37, // same as SPELLMOD_STACK_AMOUNT but affects tooltips
// spellmod 38
Max = 39
}
@@ -1171,7 +1173,7 @@ namespace Framework.Constants
IgnoreCasterAurastate = 0x800, //! Will Ignore Caster Aura States Including Combat Requirements And Death State
IgnoreCasterMountedOrOnVehicle = 0x2000, //! Will Ignore Mounted/On Vehicle Restrictions
IgnoreCasterAuras = 0x10000, //! Will Ignore Caster Aura Restrictions Or Requirements
DisallowProcEvents = 0x20000, //! Disallows Proc Events From Triggered Spell (Default)
DontResetPeriodicTimer = 0x20000, //! Will allow periodic aura timers to keep ticking (instead of resetting)
DontReportCastError = 0x40000, //! Will Return SpellFailedDontReport In Checkcast Functions
IgnoreEquippedItemRequirement = 0x80000,
IgnoreTargetCheck = 0x100000,
@@ -2116,39 +2118,6 @@ namespace Framework.Constants
MeleeBasedTriggerMask = (DoneMeleeAutoAttack | TakenMeleeAutoAttack | DoneSpellMeleeDmgClass | TakenSpellMeleeDmgClass |
DoneRangedAutoAttack | TakenRangedAutoAttack | DoneSpellRangedDmgClass | TakenSpellRangedDmgClass)
}
public enum ProcFlagsExLegacy
{
None = 0x0, // If none can tigger on Hit/Crit only (passive spells MUST defined by SpellFamily flag)
NormalHit = 0x01, // If set only from normal hit (only damage spells)
CriticalHit = 0x02,
Miss = 0x04,
Resist = 0x08,
Dodge = 0x10,
Parry = 0x20,
Block = 0x40,
Evade = 0x80,
Immune = 0x100,
Deflect = 0x200,
Absorb = 0x400,
Reflect = 0x800,
Interrupt = 0x1000, // Melee hit result can be Interrupt (not used)
FullBlock = 0x2000, // block al attack damage
Reserved2 = 0x4000,
NotActiveSpell = 0x8000, // Spell mustn't do damage/heal to proc
ExTriggerAlways = 0x10000, // If set trigger always no matter of hit result
ExOneTimeTrigger = 0x20000, // If set trigger always but only one time (not implemented yet)
OnlyActiveSpell = 0x40000, // Spell has to do damage/heal to proc
// Flags for internal use - do not use these in db!
InternalCantProc = 0x800000,
InternalDot = 0x1000000,
InternalHot = 0x2000000,
InternalTriggered = 0x4000000,
InternalReqFamily = 0x8000000,
AuraProcMask = (NormalHit | CriticalHit | Miss | Resist | Dodge | Parry
| Block | Evade | Immune | Deflect | Absorb | Reflect | Interrupt)
}
public enum ProcFlagsSpellPhase
{
None = 0x0,
@@ -2177,6 +2146,14 @@ namespace Framework.Constants
MaskAll = 0x0003FFF
}
public enum ProcAttributes
{
ReqExpOrHonor = 0x01, // requires proc target to give exp or honor for aura proc
TriggeredCanProc = 0x02, // aura can proc even with triggered spells
ReqPowerCost = 0x04, // requires triggering spell to have a power cost for aura proc
ReqSpellmod = 0x08 // requires triggering spell to be affected by proccing aura to drop charges
}
// Spell aura states
public enum AuraStateType
{ // (C) used in caster aura state (T) used in target aura state
-10
View File
@@ -800,15 +800,6 @@ namespace Game.Chat
return true;
}
[Command("spell_proc_event", RBACPermissions.CommandReloadSpellLinkedSpell, true)]
static bool HandleReloadSpellProcEventCommand(StringArguments args, CommandHandler handler)
{
Log.outInfo(LogFilter.Server, "Re-Loading Spell Proc Event conditions...");
Global.SpellMgr.LoadSpellProcEvents();
handler.SendGlobalGMSysMessage("DB table `spell_proc_event` (spell proc trigger requirements) reloaded.");
return true;
}
[Command("spell_proc", RBACPermissions.CommandReloadSpellProc, true)]
static bool HandleReloadSpellProcsCommand(StringArguments args, CommandHandler handler)
{
@@ -1103,7 +1094,6 @@ namespace Game.Chat
HandleReloadSpellGroupsCommand(null, handler);
HandleReloadSpellLearnSpellCommand(null, handler);
HandleReloadSpellLinkedSpellCommand(null, handler);
HandleReloadSpellProcEventCommand(null, handler);
HandleReloadSpellProcsCommand(null, handler);
HandleReloadSpellTargetPositionCommand(null, handler);
HandleReloadSpellThreatsCommand(null, handler);
+9
View File
@@ -332,6 +332,9 @@ namespace Game.DataStorage
foreach (var specSpells in CliDB.SpecializationSpellsStorage.Values)
_specializationSpellsBySpec.Add(specSpells.SpecID, specSpells);
foreach (SpellClassOptionsRecord classOption in CliDB.SpellClassOptionsStorage.Values)
_spellFamilyNames.Add(classOption.SpellClassSet);
foreach (var power in CliDB.SpellPowerStorage.Values)
{
SpellPowerDifficultyRecord powerDifficulty = CliDB.SpellPowerDifficultyStorage.LookupByKey(power.Id);
@@ -1109,6 +1112,11 @@ namespace Game.DataStorage
return _specializationSpellsBySpec.LookupByKey(specId);
}
bool IsValidSpellFamiliyName(SpellFamilyNames family)
{
return _spellFamilyNames.Contains((byte)family);
}
public List<SpellPowerRecord> GetSpellPowers(uint spellId, Difficulty difficulty = Difficulty.None)
{
bool notUsed;
@@ -1334,6 +1342,7 @@ namespace Game.DataStorage
Dictionary<uint, uint> _rulesetItemUpgrade = new Dictionary<uint, uint>();
MultiMap<uint, SkillRaceClassInfoRecord> _skillRaceClassInfoBySkill = new MultiMap<uint, SkillRaceClassInfoRecord>();
MultiMap<uint, SpecializationSpellsRecord> _specializationSpellsBySpec = new MultiMap<uint, SpecializationSpellsRecord>();
List<byte> _spellFamilyNames = new List<byte>();
Dictionary<uint, List<SpellPowerRecord>> _spellPowers = new Dictionary<uint, List<SpellPowerRecord>>();
Dictionary<uint, Dictionary<uint, List<SpellPowerRecord>>> _spellPowerDifficulties = new Dictionary<uint, Dictionary<uint, List<SpellPowerRecord>>>();
MultiMap<uint, SpellProcsPerMinuteModRecord> _spellProcsPerMinuteMods = new MultiMap<uint, SpellProcsPerMinuteModRecord>();
+14
View File
@@ -4289,10 +4289,24 @@ namespace Game.Entities
if (proto.GetFlags().HasAnyFlag(ItemFlags.NoEquipCooldown))
return;
DateTime now = DateTime.Now;
for (byte i = 0; i < proto.Effects.Count; ++i)
{
var effectData = proto.Effects[i];
// apply proc cooldown to equip auras if we have any
if (effectData.Trigger == ItemSpelltriggerType.OnEquip)
{
SpellProcEntry procEntry = Global.SpellMgr.GetSpellProcEntry(effectData.SpellID);
if (procEntry == null)
continue;
Aura itemAura = GetAura(effectData.SpellID, GetGUID(), pItem.GetGUID());
if (itemAura != null)
itemAura.AddProcCooldown(now + TimeSpan.FromMilliseconds(procEntry.Cooldown));
continue;
}
// no spell
if (effectData.SpellID == 0)
continue;
+140 -133
View File
@@ -1797,19 +1797,18 @@ namespace Game.Entities
return;
List<Aura> aurasQueue = new List<Aura>();
for (var i = 0; i < (int)SpellModOp.Max; ++i)
{
for (var j = 0; j < (int)SpellModType.End; ++j)
{
foreach (var mod in m_spellMods[i][j])
{
// spellmods without aura set cannot be charged
if (mod.ownerAura == null || !mod.ownerAura.IsUsingCharges())
// Spellmods without charged aura set cannot be charged
if (!mod.ownerAura.IsUsingCharges())
continue;
// Restore only specific owner aura mods
if (ownerAuraId != 0 && (ownerAuraId != mod.ownerAura.GetSpellInfo().Id))
if (ownerAuraId != 0 && mod.spellId != ownerAuraId)
continue;
if (aura != null && mod.ownerAura != aura)
@@ -1831,70 +1830,37 @@ namespace Game.Entities
// only see the first of its modifier restored)
aurasQueue.Add(mod.ownerAura);
// add mod charges back to mod
if (mod.charges == -1)
mod.charges = 1;
else
mod.charges++;
// Do not set more spellmods than avalible
if (mod.ownerAura.GetCharges() < mod.charges)
mod.charges = mod.ownerAura.GetCharges();
// Skip this check for now - aura charges may change due to various reason
/// @todo track these changes correctly
//ASSERT (mod->ownerAura->GetCharges() <= mod->charges);
// add charges back to aura
mod.ownerAura.ModCharges(1);
}
}
}
foreach (var removeAura in aurasQueue)
{
var appliedMod = spell.m_appliedMods.Find(p => p == removeAura);
if (appliedMod != null)
spell.m_appliedMods.Remove(appliedMod);
}
spell.m_appliedMods.Remove(aura);
}
public void RestoreAllSpellMods(uint ownerAuraId, Aura aura)
public void RestoreAllSpellMods(uint ownerAuraId = 0, Aura aura = null)
{
for (CurrentSpellTypes i = 0; i < CurrentSpellTypes.Max; ++i)
if (GetCurrentSpell(i) != null)
RestoreSpellMods(m_currentSpells[i], ownerAuraId, aura);
{
Spell spell = m_currentSpells[i];
if (spell != null)
RestoreSpellMods(spell, ownerAuraId, aura);
}
}
public void RemoveSpellMods(Spell spell)
public void ApplyModToSpell(SpellModifier mod, Spell spell)
{
if (spell == null)
return;
if (spell.m_appliedMods.Empty())
// don't do anything with no charges
if (mod.ownerAura.IsUsingCharges() && mod.ownerAura.GetCharges() == 0)
return;
for (var i = 0; i < (int)SpellModOp.Max; ++i)
{
for (var j = 0; j < (int)SpellModType.End; ++j)
{
for (var c = 0; c < m_spellMods[i][j].Count; c++)
{
SpellModifier mod = m_spellMods[i][j][c];
// spellmods without aura set cannot be charged
if (mod.ownerAura == null || !mod.ownerAura.IsUsingCharges())
continue;
// check if mod affected this spell
var iterMod = spell.m_appliedMods.Find(p => p == mod.ownerAura);
if (iterMod == null)
continue;
// remove from list
spell.m_appliedMods.Remove(iterMod);
if (mod.ownerAura.DropCharge(AuraRemoveMode.Expire))
c = 0;
}
}
}
// register inside spell, proc system uses this to drop charges
spell.m_appliedMods.Add(mod.ownerAura);
}
public void LearnCustomSpells()
@@ -2643,25 +2609,71 @@ namespace Game.Entities
if (m_spellModTakingSpell != null)
spell = m_spellModTakingSpell;
switch (op)
{
// special case, if a mod makes spell instant, only consume that mod
case SpellModOp.CastingTime:
{
SpellModifier modInstantSpell = null;
foreach (SpellModifier mod in m_spellMods[(int)op][(int)SpellModType.Pct])
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
if (Convert.ToInt64(basevalue) < 10000 && mod.value <= -100)
{
modInstantSpell = mod;
break;
}
}
if (modInstantSpell != null)
{
ApplyModToSpell(modInstantSpell, spell);
basevalue = default(T);
return;
}
break;
}
// special case if two mods apply 100% critical chance, only consume one
case SpellModOp.CriticalChance:
{
SpellModifier modCritical = null;
foreach (SpellModifier mod in m_spellMods[(int)op][(int)SpellModType.Flat])
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
if (mod.value >= 100)
{
modCritical = mod;
break;
}
}
if (modCritical != null)
{
ApplyModToSpell(modCritical, spell);
basevalue = (T)Convert.ChangeType(100, typeof(T));
return;
}
break;
}
default:
break;
}
foreach (var mod in m_spellMods[(int)op][(int)SpellModType.Flat])
{
// Charges can be set only for mods with auras
if (mod.ownerAura == null)
Contract.Assert(mod.charges == 0);
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
totalflat += mod.value;
DropModCharge(mod, spell);
ApplyModToSpell(mod, spell);
}
foreach (var mod in m_spellMods[(int)op][(int)SpellModType.Pct])
{
// Charges can be set only for mods with auras
if (mod.ownerAura == null)
Contract.Assert(mod.charges == 0);
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
@@ -2670,22 +2682,26 @@ namespace Game.Entities
continue;
// special case (skip > 10sec spell casts for instant cast setting)
if (mod.op == SpellModOp.CastingTime && Convert.ToInt64(basevalue) >= 10000 && mod.value <= -100)
if (op == SpellModOp.CastingTime)
{
if (Convert.ToInt32(basevalue) >= 10000 && mod.value <= -100)
continue;
}
totalmul *= 1.0f + MathFunctions.CalculatePct(1.0f, mod.value);
DropModCharge(mod, spell);
ApplyModToSpell(mod, spell);
}
basevalue = (T)Convert.ChangeType(((Convert.ToSingle(basevalue) + totalflat) * totalmul), typeof(T));
}
bool IsAffectedBySpellmod(SpellInfo spellInfo, SpellModifier mod, Spell spell)
{
if (mod == null || spellInfo == null)
return false;
// Mod out of charges
if (spell != null && mod.charges == -1 && !spell.m_appliedMods.Contains(mod.ownerAura))
// First time this aura applies a mod to us and is out of charges
if (spell && mod.ownerAura.IsUsingCharges() && mod.ownerAura.GetCharges() == 0 && !spell.m_appliedMods.Contains(mod.ownerAura))
return false;
// +duration to infinite duration spells making them limited
@@ -2694,21 +2710,6 @@ namespace Game.Entities
return spellInfo.IsAffectedBySpellMod(mod);
}
void DropModCharge(SpellModifier mod, Spell spell)
{
// don't handle spells with proc_event entry defined
// this is a temporary workaround, because all spellmods should be handled like that
if (Global.SpellMgr.GetSpellProcEvent(mod.spellId) != null)
return;
if (spell != null && mod.ownerAura != null && mod.charges > 0)
{
if (--mod.charges == 0)
mod.charges = -1;
spell.m_appliedMods.Add(mod.ownerAura);
}
}
public void SetSpellModTakingSpell(Spell spell, bool apply)
{
@@ -2992,10 +2993,62 @@ namespace Game.Entities
return false;
}
public void CastItemCombatSpell(Unit target, WeaponAttackType attType, ProcFlags procVictim, ProcFlagsExLegacy procEx, Item item, ItemTemplate proto)
public void CastItemCombatSpell(DamageInfo damageInfo)
{
Unit target = damageInfo.GetVictim();
if (target == null || !target.IsAlive() || target == this)
return;
for (byte i = EquipmentSlot.Start; i < EquipmentSlot.End; ++i)
{
// If usable, try to cast item spell
Item item = GetItemByPos(InventorySlots.Bag0, i);
if (item != null)
{
if (!item.IsBroken() && CanUseAttackType(damageInfo.GetAttackType()))
{
ItemTemplate proto = item.GetTemplate();
if (proto != null)
{
// Additional check for weapons
if (proto.GetClass() == ItemClass.Weapon)
{
// offhand item cannot proc from main hand hit etc
byte slot;
switch (damageInfo.GetAttackType())
{
case WeaponAttackType.BaseAttack:
case WeaponAttackType.RangedAttack:
slot = EquipmentSlot.MainHand;
break;
case WeaponAttackType.OffAttack:
slot = EquipmentSlot.OffHand;
break;
default:
slot = EquipmentSlot.End;
break;
}
if (slot != i)
continue;
// Check if item is useable (forms or disarm)
if (damageInfo.GetAttackType() == WeaponAttackType.BaseAttack)
if (!IsUseEquipedWeapon(true) && !IsInFeralForm())
continue;
}
CastItemCombatSpell(damageInfo, item, proto);
}
}
}
}
}
public void CastItemCombatSpell(DamageInfo damageInfo, Item item, ItemTemplate proto)
{
// Can do effect if any damage done to target
if (procVictim.HasAnyFlag(ProcFlags.TakenDamage))
// for done procs allow normal + critical + absorbs by default
bool canTrigger = damageInfo.GetHitMask().HasAnyFlag(ProcFlagsHit.Normal | ProcFlagsHit.Critical | ProcFlagsHit.Absorb);
if (canTrigger)
{
for (byte i = 0; i < proto.Effects.Count; ++i)
{
@@ -3024,14 +3077,14 @@ namespace Game.Entities
if (proto.SpellPPMRate != 0)
{
uint WeaponSpeed = GetBaseAttackTime(attType);
uint WeaponSpeed = GetBaseAttackTime(damageInfo.GetAttackType());
chance = GetPPMProcChance(WeaponSpeed, proto.SpellPPMRate, spellInfo);
}
else if (chance > 100.0f)
chance = GetWeaponProcChance();
if (RandomHelper.randChance(chance))
CastSpell(target, spellInfo.Id, true, item);
CastSpell(damageInfo.GetVictim(), spellInfo.Id, true, item);
}
}
@@ -3053,13 +3106,13 @@ namespace Game.Entities
if (entry != null && entry.procEx != 0)
{
// Check hit/crit/dodge/parry requirement
if ((entry.procEx & procEx) == 0)
if (((uint)entry.procEx & (uint)damageInfo.GetHitMask()) == 0)
continue;
}
else
{
// Can do effect if any damage done to target
if (!Convert.ToBoolean(procVictim & ProcFlags.TakenDamage))
// for done procs allow normal + critical + absorbs by default
if (!canTrigger)
continue;
}
@@ -3093,53 +3146,7 @@ namespace Game.Entities
if (spellInfo.IsPositive())
CastSpell(this, spellInfo, true, item);
else
CastSpell(target, spellInfo, true, item);
}
}
}
}
public void CastItemCombatSpell(Unit target, WeaponAttackType attType, ProcFlags procVictim, ProcFlagsExLegacy procEx)
{
if (target == null || !target.IsAlive() || target == this)
return;
for (byte i = EquipmentSlot.Start; i < EquipmentSlot.End; ++i)
{
// If usable, try to cast item spell
Item item = GetItemByPos(InventorySlots.Bag0, i);
if (item != null)
if (!item.IsBroken() && CanUseAttackType(attType))
{
ItemTemplate proto = item.GetTemplate();
if (proto != null)
{
// Additional check for weapons
if (proto.GetClass() == ItemClass.Weapon)
{
// offhand item cannot proc from main hand hit etc
byte slot;
switch (attType)
{
case WeaponAttackType.BaseAttack:
case WeaponAttackType.RangedAttack:
slot = EquipmentSlot.MainHand;
break;
case WeaponAttackType.OffAttack:
slot = EquipmentSlot.OffHand;
break;
default:
slot = EquipmentSlot.End;
break;
}
if (slot != i)
continue;
// Check if item is useable (forms or disarm)
if (attType == WeaponAttackType.BaseAttack)
if (!IsUseEquipedWeapon(true) && !IsInFeralForm())
continue;
}
CastItemCombatSpell(target, attType, procVictim, procEx, item, proto);
CastSpell(damageInfo.GetVictim(), spellInfo, true, item);
}
}
}
+25 -39
View File
@@ -595,7 +595,8 @@ namespace Game.Entities
DealDamageMods(victim, ref damageInfo.damage, ref damageInfo.absorb);
SendAttackStateUpdate(damageInfo);
ProcDamageAndSpell(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, damageInfo.procEx, damageInfo.damage, damageInfo.attackType);
DamageInfo dmgInfo = new DamageInfo(damageInfo);
ProcSkillsAndAuras(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, ProcFlagsSpellType.None, ProcFlagsSpellPhase.None, dmgInfo.GetHitMask(), null, dmgInfo, null);
DealMeleeDamage(damageInfo, true);
@@ -646,7 +647,6 @@ namespace Game.Entities
damageInfo.procAttacker = ProcFlags.None;
damageInfo.procVictim = ProcFlags.None;
damageInfo.procEx = ProcFlagsExLegacy.None;
damageInfo.hitOutCome = MeleeHitOutcome.Normal;
SendAttackStateUpdate(damageInfo);
@@ -808,7 +808,10 @@ namespace Game.Entities
}
if (IsTypeId(TypeId.Player))
ToPlayer().CastItemCombatSpell(victim, damageInfo.attackType, damageInfo.procVictim, damageInfo.procEx);
{
DamageInfo dmgInfo = new DamageInfo(damageInfo);
ToPlayer().CastItemCombatSpell(dmgInfo);
}
// Do effect if any damage done to target
if (damageInfo.damage != 0)
@@ -1431,16 +1434,17 @@ namespace Game.Entities
// Do KILL and KILLED procs. KILL proc is called only for the unit who landed the killing blow (and its owner - for pets and totems) regardless of who tapped the victim
if (IsPet() || IsTotem())
{
// proc only once for victim
Unit owner = GetOwner();
if (owner != null)
owner.ProcDamageAndSpell(victim, ProcFlags.Kill, ProcFlags.None, ProcFlagsExLegacy.None, 0);
owner.ProcSkillsAndAuras(victim, ProcFlags.Kill, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
}
if (!victim.IsCritter())
ProcDamageAndSpell(victim, ProcFlags.Kill, ProcFlags.Killed, ProcFlagsExLegacy.None, 0);
ProcSkillsAndAuras(victim, ProcFlags.Kill, ProcFlags.Killed, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
// Proc auras on death - must be before aura/combat remove
victim.ProcDamageAndSpell(null, ProcFlags.Death, ProcFlags.None, ProcFlagsExLegacy.None, 0, WeaponAttackType.BaseAttack, null);
victim.ProcSkillsAndAuras(victim, ProcFlags.None, ProcFlags.Death, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
// update get killing blow achievements, must be done before setDeathState to be able to require auras on target
// and before Spirit of Redemption as it also removes auras
@@ -1808,7 +1812,6 @@ namespace Game.Entities
damageInfo.HitInfo = 0;
damageInfo.procAttacker = ProcFlags.None;
damageInfo.procVictim = ProcFlags.None;
damageInfo.procEx = ProcFlagsExLegacy.None;
damageInfo.hitOutCome = MeleeHitOutcome.Evade;
if (victim == null)
@@ -1839,7 +1842,6 @@ namespace Game.Entities
damageInfo.HitInfo |= HitInfo.NormalSwing;
damageInfo.TargetState = VictimState.Immune;
damageInfo.procEx |= ProcFlagsExLegacy.Immune;
damageInfo.damage = 0;
damageInfo.cleanDamage = 0;
return;
@@ -1869,25 +1871,22 @@ namespace Game.Entities
case MeleeHitOutcome.Evade:
damageInfo.HitInfo |= HitInfo.Miss | HitInfo.SwingNoHitSound;
damageInfo.TargetState = VictimState.Evades;
damageInfo.procEx |= ProcFlagsExLegacy.Evade;
damageInfo.damage = 0;
damageInfo.cleanDamage = 0;
return;
case MeleeHitOutcome.Miss:
damageInfo.HitInfo |= HitInfo.Miss;
damageInfo.TargetState = VictimState.Intact;
damageInfo.procEx |= ProcFlagsExLegacy.Miss;
damageInfo.damage = 0;
damageInfo.cleanDamage = 0;
break;
case MeleeHitOutcome.Normal:
damageInfo.TargetState = VictimState.Hit;
damageInfo.procEx |= ProcFlagsExLegacy.NormalHit;
break;
case MeleeHitOutcome.Crit:
damageInfo.HitInfo |= HitInfo.CriticalHit;
damageInfo.TargetState = VictimState.Hit;
damageInfo.procEx |= ProcFlagsExLegacy.CriticalHit;
// Crit bonus calc
damageInfo.damage += damageInfo.damage;
float mod = 0.0f;
@@ -1905,19 +1904,16 @@ namespace Game.Entities
break;
case MeleeHitOutcome.Parry:
damageInfo.TargetState = VictimState.Parry;
damageInfo.procEx |= ProcFlagsExLegacy.Parry;
damageInfo.cleanDamage += damageInfo.damage;
damageInfo.damage = 0;
break;
case MeleeHitOutcome.Dodge:
damageInfo.TargetState = VictimState.Dodge;
damageInfo.procEx |= ProcFlagsExLegacy.Dodge;
damageInfo.cleanDamage += damageInfo.damage;
damageInfo.damage = 0;
break;
case MeleeHitOutcome.Block:
damageInfo.TargetState = VictimState.Hit;
damageInfo.procEx |= ProcFlagsExLegacy.Block | ProcFlagsExLegacy.NormalHit;
// 30% damage blocked, double blocked amount if block is critical
damageInfo.blocked_amount = MathFunctions.CalculatePct(damageInfo.damage, damageInfo.target.isBlockCritical() ? damageInfo.target.GetBlockPercent() * 2 : damageInfo.target.GetBlockPercent());
damageInfo.damage -= damageInfo.blocked_amount;
@@ -1926,7 +1922,6 @@ namespace Game.Entities
case MeleeHitOutcome.Glancing:
damageInfo.HitInfo |= HitInfo.Glancing;
damageInfo.TargetState = VictimState.Hit;
damageInfo.procEx |= ProcFlagsExLegacy.NormalHit;
int leveldif = (int)victim.getLevel() - (int)getLevel();
if (leveldif > 3)
leveldif = 3;
@@ -1938,7 +1933,6 @@ namespace Game.Entities
case MeleeHitOutcome.Crushing:
damageInfo.HitInfo |= HitInfo.Crushing;
damageInfo.TargetState = VictimState.Hit;
damageInfo.procEx |= ProcFlagsExLegacy.NormalHit;
// 150% normal damage
damageInfo.damage += (damageInfo.damage / 2);
break;
@@ -1965,10 +1959,7 @@ namespace Game.Entities
CalcAbsorbResist(damageInfo.target, (SpellSchoolMask)damageInfo.damageSchoolMask, DamageEffectType.Direct, damageInfo.damage, ref damageInfo.absorb, ref damageInfo.resist);
if (damageInfo.absorb != 0)
{
damageInfo.HitInfo |= (damageInfo.damage - damageInfo.absorb == 0 ? HitInfo.FullAbsorb : HitInfo.PartialAbsorb);
damageInfo.procEx |= ProcFlagsExLegacy.Absorb;
}
if (damageInfo.resist != 0)
damageInfo.HitInfo |= (damageInfo.damage - damageInfo.resist == 0 ? HitInfo.FullResist : HitInfo.PartialResist);
@@ -2437,7 +2428,7 @@ namespace Game.Entities
if (eff.GetAmount() >= 0)
{
// Reduce shield amount
eff.SetAmount(eff.GetAmount() - currentAbsorb);
eff.ChangeAmount(eff.GetAmount() - currentAbsorb);
// Aura cannot absorb anything more - remove it
if (eff.GetAmount() <= 0)
eff.GetBase().Remove(AuraRemoveMode.EnemySpell);
@@ -2498,7 +2489,7 @@ namespace Game.Entities
// Check if our aura is using amount to count damage
if (absorbAurEff.GetAmount() >= 0)
{
absorbAurEff.SetAmount(absorbAurEff.GetAmount() - currentAbsorb);
absorbAurEff.ChangeAmount(absorbAurEff.GetAmount() - currentAbsorb);
if ((absorbAurEff.GetAmount() <= 0))
absorbAurEff.GetBase().Remove(AuraRemoveMode.EnemySpell);
}
@@ -2558,7 +2549,8 @@ namespace Game.Entities
SendSpellNonMeleeDamageLog(log);
// break 'Fear' and similar auras
caster.ProcDamageAndSpellFor(true, this, ProcFlags.TakenSpellMagicDmgClassNeg, ProcFlagsExLegacy.NormalHit, WeaponAttackType.BaseAttack, eff.GetSpellInfo(), splitDamage);
DamageInfo damageInfo = new DamageInfo(caster, this, splitDamage, eff.GetSpellInfo(), schoolMask, DamageEffectType.Direct, WeaponAttackType.BaseAttack);
ProcSkillsAndAuras(caster, ProcFlags.None, ProcFlags.TakenSpellMagicDmgClassNeg, ProcFlagsSpellType.Damage, ProcFlagsSpellPhase.Hit, ProcFlagsHit.None, null, damageInfo, null);
}
}
@@ -2566,24 +2558,22 @@ namespace Game.Entities
absorb = dmgInfo.GetAbsorb();
}
public void CalcHealAbsorb(Unit victim, SpellInfo healSpell, ref uint healAmount, ref uint absorb)
public void CalcHealAbsorb(HealInfo healInfo)
{
if (healAmount == 0)
if (healInfo.GetHeal() == 0)
return;
int RemainingHeal = (int)healAmount;
// Need remove expired auras after
bool existExpired = false;
// absorb without mana cost
var vHealAbsorb = victim.GetAuraEffectsByType(AuraType.SchoolHealAbsorb);
var vHealAbsorb = healInfo.GetTarget().GetAuraEffectsByType(AuraType.SchoolHealAbsorb);
foreach (var eff in vHealAbsorb)
{
if (RemainingHeal <= 0)
if (healInfo.GetHeal() <= 0)
break;
if (!Convert.ToBoolean(eff.GetMiscValue() & (int)healSpell.SchoolMask))
if (!Convert.ToBoolean(eff.GetMiscValue() & (int)healInfo.GetSpellInfo().SchoolMask))
continue;
// Max Amount can be absorbed by this aura
@@ -2598,13 +2588,12 @@ namespace Game.Entities
// currentAbsorb - damage can be absorbed by shield
// If need absorb less damage
if (RemainingHeal < currentAbsorb)
currentAbsorb = RemainingHeal;
currentAbsorb = (int)Math.Min(healInfo.GetHeal(), currentAbsorb);
RemainingHeal -= currentAbsorb;
healInfo.AbsorbHeal((uint)currentAbsorb);
// Reduce shield amount
eff.SetAmount(eff.GetAmount() - currentAbsorb);
eff.ChangeAmount(eff.GetAmount() - currentAbsorb);
// Need remove it later
if (eff.GetAmount() <= 0)
existExpired = true;
@@ -2619,16 +2608,13 @@ namespace Game.Entities
++i;
if (auraEff.GetAmount() <= 0)
{
uint removedAuras = victim.m_removedAurasCount;
uint removedAuras = healInfo.GetTarget().m_removedAurasCount;
auraEff.GetBase().Remove(AuraRemoveMode.EnemySpell);
if (removedAuras + 1 < victim.m_removedAurasCount)
if (removedAuras + 1 < healInfo.GetTarget().m_removedAurasCount)
i = 0;
}
}
}
absorb = (uint)(RemainingHeal > 0 ? (healAmount - RemainingHeal) : healAmount);
healAmount = (uint)RemainingHeal;
}
public uint CalcArmorReducedDamage(Unit attacker, Unit victim, uint damage, SpellInfo spellInfo, WeaponAttackType attackType = WeaponAttackType.Max)
+26 -27
View File
@@ -154,23 +154,10 @@ namespace Game.Entities
public DiminishingLevels hitCount;
}
public class ProcTriggeredData
{
public ProcTriggeredData(Aura _aura)
{
aura = _aura;
effMask = 0;
spellProcEvent = null;
}
public SpellProcEventEntry spellProcEvent;
public Aura aura;
public uint effMask;
}
public class ProcEventInfo
{
public ProcEventInfo(Unit actor, Unit actionTarget, Unit procTarget, uint typeMask, uint spellTypeMask,
uint spellPhaseMask, uint hitMask, Spell spell, DamageInfo damageInfo, HealInfo healInfo)
public ProcEventInfo(Unit actor, Unit actionTarget, Unit procTarget, ProcFlags typeMask, ProcFlagsSpellType spellTypeMask,
ProcFlagsSpellPhase spellPhaseMask, ProcFlagsHit hitMask, Spell spell, DamageInfo damageInfo, HealInfo healInfo)
{
_actor = actor;
_actionTarget = actionTarget;
@@ -188,10 +175,10 @@ namespace Game.Entities
public Unit GetActionTarget() { return _actionTarget; }
public Unit GetProcTarget() { return _procTarget; }
public ProcFlags GetTypeMask() { return (ProcFlags)_typeMask; }
public ProcFlagsSpellType GetSpellTypeMask() { return (ProcFlagsSpellType)_spellTypeMask; }
public ProcFlagsSpellPhase GetSpellPhaseMask() { return (ProcFlagsSpellPhase)_spellPhaseMask; }
public ProcFlagsHit GetHitMask() { return (ProcFlagsHit)_hitMask; }
public ProcFlags GetTypeMask() { return _typeMask; }
public ProcFlagsSpellType GetSpellTypeMask() { return _spellTypeMask; }
public ProcFlagsSpellPhase GetSpellPhaseMask() { return _spellPhaseMask; }
public ProcFlagsHit GetHitMask() { return _hitMask; }
public SpellInfo GetSpellInfo()
{
@@ -221,13 +208,15 @@ namespace Game.Entities
public DamageInfo GetDamageInfo() { return _damageInfo; }
public HealInfo GetHealInfo() { return _healInfo; }
public Spell GetProcSpell() { return _spell; }
Unit _actor;
Unit _actionTarget;
Unit _procTarget;
uint _typeMask;
uint _spellTypeMask;
uint _spellPhaseMask;
uint _hitMask;
ProcFlags _typeMask;
ProcFlagsSpellType _spellTypeMask;
ProcFlagsSpellPhase _spellPhaseMask;
ProcFlagsHit _hitMask;
Spell _spell;
DamageInfo _damageInfo;
HealInfo _healInfo;
@@ -245,6 +234,7 @@ namespace Game.Entities
m_damageType = damageType;
m_attackType = attackType;
}
public DamageInfo(CalcDamageInfo dmgInfo)
{
m_attacker = dmgInfo.attacker;
@@ -291,11 +281,20 @@ namespace Game.Entities
case MeleeHitOutcome.Evade:
m_hitMask |= ProcFlagsHit.Evade;
break;
case MeleeHitOutcome.Crushing:
case MeleeHitOutcome.Glancing:
case MeleeHitOutcome.Normal:
m_hitMask |= ProcFlagsHit.Normal;
break;
case MeleeHitOutcome.Crit:
m_hitMask |= ProcFlagsHit.Critical;
break;
default:
break;
}
}
public DamageInfo(SpellNonMeleeDamage spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType)
public DamageInfo(SpellNonMeleeDamage spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType, ProcFlagsHit hitMask)
{
m_attacker = spellNonMeleeDamage.attacker;
m_victim = spellNonMeleeDamage.target;
@@ -307,6 +306,7 @@ namespace Game.Entities
m_absorb = spellNonMeleeDamage.absorb;
m_resist = spellNonMeleeDamage.resist;
m_block = spellNonMeleeDamage.blocked;
m_hitMask = hitMask;
if (spellNonMeleeDamage.blocked != 0)
m_hitMask |= ProcFlagsHit.Block;
@@ -354,7 +354,7 @@ namespace Game.Entities
public uint GetAbsorb() { return m_absorb; }
public uint GetResist() { return m_resist; }
uint GetBlock() { return m_block; }
ProcFlagsHit GetHitMask() { return m_hitMask; }
public ProcFlagsHit GetHitMask() { return m_hitMask; }
Unit m_attacker;
Unit m_victim;
@@ -380,7 +380,7 @@ namespace Game.Entities
_schoolMask = schoolMask;
}
void AbsorbHeal(uint amount)
public void AbsorbHeal(uint amount)
{
amount = Math.Min(amount, GetHeal());
_absorb += amount;
@@ -425,7 +425,6 @@ namespace Game.Entities
public WeaponAttackType attackType { get; set; }
public ProcFlags procAttacker { get; set; }
public ProcFlags procVictim { get; set; }
public ProcFlagsExLegacy procEx { get; set; }
public uint cleanDamage { get; set; } // Used only for rage calculation
public MeleeHitOutcome hitOutCome { get; set; } // TODO: remove this field (need use TargetState)
}
File diff suppressed because it is too large Load Diff
+27
View File
@@ -775,6 +775,7 @@ namespace Game.Scripting
public delegate void AuraEffectAbsorbDelegate(AuraEffect aura, DamageInfo damageInfo, ref uint absorbAmount);
public delegate void AuraEffectSplitDelegate(AuraEffect aura, DamageInfo damageInfo, uint splitAmount);
public delegate bool AuraCheckProcDelegate(ProcEventInfo info);
public delegate bool AuraCheckEffectProcDelegate(AuraEffect aura , ProcEventInfo info);
public delegate void AuraProcDelegate(ProcEventInfo info);
public delegate void AuraEffectProcDelegate(AuraEffect aura, ProcEventInfo info);
@@ -961,12 +962,28 @@ namespace Game.Scripting
AuraCheckProcDelegate _HandlerScript;
}
public class CheckEffectProcHandler : EffectBase
{
public CheckEffectProcHandler(AuraCheckEffectProcDelegate handlerScript, uint effIndex, AuraType effName) : base(effIndex, effName)
{
_HandlerScript = handlerScript;
}
public bool Call(AuraEffect aurEff, ProcEventInfo eventInfo)
{
return _HandlerScript(aurEff, eventInfo);
}
AuraCheckEffectProcDelegate _HandlerScript;
}
public class AuraProcHandler
{
public AuraProcHandler(AuraProcDelegate handlerScript)
{
_HandlerScript = handlerScript;
}
public void Call(ProcEventInfo eventInfo)
{
_HandlerScript(eventInfo);
@@ -1069,6 +1086,10 @@ namespace Game.Scripting
if (!entry.HasEffect(SpellEffectName.ApplyAura) && !entry.HasAreaAuraEffect())
Log.outError(LogFilter.Scripts, "Spell `{0}` of script `{1}` does not have apply aura effect - handler bound to hook `DoCheckProc` of AuraScript won't be executed", entry.Id, m_scriptName);
foreach (var eff in DoCheckEffectProc)
if (eff.GetAffectedEffectsMask(entry) == 0)
Log.outError(LogFilter.Scripts, "Spell `{0}` Effect `{1}` of script `{2}` did not match dbc effect data - handler bound to hook `DoCheckEffectProc` of AuraScript won't be executed", entry.Id, eff.ToString(), m_scriptName);
foreach (var eff in DoPrepareProc)
if (!entry.HasEffect(SpellEffectName.ApplyAura) && !entry.HasAreaAuraEffect())
Log.outError(LogFilter.Scripts, "Spell `{0}` of script `{1}` does not have apply aura effect - handler bound to hook `DoPrepareProc` of AuraScript won't be executed", entry.Id, m_scriptName);
@@ -1246,6 +1267,11 @@ namespace Game.Scripting
// where function is: bool function (ProcEventInfo& eventInfo);
public List<CheckProcHandler> DoCheckProc = new List<CheckProcHandler>();
// executed when aura effect checks if it can proc the aura
// example: DoCheckEffectProc += AuraCheckEffectProcFn(class::function, EffectIndexSpecifier, EffectAuraNameSpecifier);
// where function is bool function (AuraEffect const* aurEff, ProcEventInfo& eventInfo);
public List<CheckEffectProcHandler> DoCheckEffectProc = new List<CheckEffectProcHandler>();
// executed before aura procs (possibility to prevent charge drop/cooldown)
// example: DoPrepareProc += AuraProcFn(class.function);
// where function is: void function (ProcEventInfo& eventInfo);
@@ -1383,6 +1409,7 @@ namespace Game.Scripting
case AuraScriptHookType.EffectAfterManaShield:
case AuraScriptHookType.EffectSplit:
case AuraScriptHookType.CheckProc:
case AuraScriptHookType.CheckEffectProc:
case AuraScriptHookType.PrepareProc:
case AuraScriptHookType.Proc:
case AuraScriptHookType.AfterProc:
-3
View File
@@ -433,9 +433,6 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loading Spell Learn Spells...");
Global.SpellMgr.LoadSpellLearnSpells();
Log.outInfo(LogFilter.ServerLoading, "Loading Spell Proc Event conditions...");
Global.SpellMgr.LoadSpellProcEvents();
Log.outInfo(LogFilter.ServerLoading, "Loading Spell Proc conditions and data...");
Global.SpellMgr.LoadSpellProcs();
+107 -47
View File
@@ -742,10 +742,9 @@ namespace Game.Spells
m_timeCla = 1 * Time.InMilliseconds;
}
void RefreshTimers()
void RefreshTimers(bool resetPeriodicTimer)
{
m_maxDuration = CalcMaxDuration();
bool resetPeriodic = true;
if (m_spellInfo.HasAttribute(SpellAttr8.DontResetPeriodicTimer))
{
int minPeriod = m_maxDuration;
@@ -764,15 +763,18 @@ namespace Game.Spells
if (GetDuration() <= minPeriod)
{
m_maxDuration += GetDuration();
resetPeriodic = false;
resetPeriodicTimer = false;
}
}
RefreshDuration();
Unit caster = GetCaster();
for (byte i = 0; i < SpellConst.MaxEffects; ++i)
if (HasEffect(i))
GetEffect(i).CalculatePeriodic(caster, resetPeriodic, false);
{
AuraEffect aurEff = GetEffect(i);
if (aurEff != null)
aurEff.CalculatePeriodic(caster, resetPeriodicTimer, false);
}
}
public void SetCharges(int charges)
@@ -853,7 +855,7 @@ namespace Game.Spells
SetNeedClientUpdateForTargets();
}
public bool ModStackAmount(int num, AuraRemoveMode removeMode = AuraRemoveMode.Default)
public bool ModStackAmount(int num, AuraRemoveMode removeMode = AuraRemoveMode.Default, bool resetPeriodicTimer = true)
{
int stackAmount = m_stackAmount + num;
@@ -881,22 +883,10 @@ namespace Game.Spells
if (refresh)
{
RefreshSpellMods();
RefreshTimers();
RefreshTimers(resetPeriodicTimer);
// reset charges
SetCharges(CalcMaxCharges());
// FIXME: not a best way to synchronize charges, but works
for (byte i = 0; i < SpellConst.MaxEffects; ++i)
{
AuraEffect aurEff = GetEffect(i);
if (aurEff != null)
if (aurEff.GetAuraType() == AuraType.AddFlatModifier || aurEff.GetAuraType() == AuraType.AddPctModifier)
{
SpellModifier mod = aurEff.GetSpellModifier();
if (mod != null)
mod.charges = GetCharges();
}
}
}
SetNeedClientUpdateForTargets();
return false;
@@ -1668,7 +1658,7 @@ namespace Game.Spells
m_procCooldown = cooldownEnd;
}
void PrepareProcToTrigger(AuraApplication aurApp, ProcEventInfo eventInfo, DateTime now)
public void PrepareProcToTrigger(AuraApplication aurApp, ProcEventInfo eventInfo, DateTime now)
{
bool prepare = CallScriptPrepareProcHandlers(aurApp, eventInfo);
if (!prepare)
@@ -1687,44 +1677,81 @@ namespace Game.Spells
// cooldowns should be added to the whole aura (see 51698 area aura)
AddProcCooldown(now + TimeSpan.FromMilliseconds(procEntry.Cooldown));
SetLastProcSuccessTime(now);
}
bool IsProcTriggeredOnEvent(AuraApplication aurApp, ProcEventInfo eventInfo, DateTime now)
public uint IsProcTriggeredOnEvent(AuraApplication aurApp, ProcEventInfo eventInfo, DateTime now)
{
SpellProcEntry procEntry = Global.SpellMgr.GetSpellProcEntry(GetId());
// only auras with spell proc entry can trigger proc
if (procEntry == null)
return false;
return 0;
// check if we have charges to proc with
if (IsUsingCharges() && GetCharges() == 0)
return false;
if (IsUsingCharges())
{
if (GetCharges() == 0)
return 0;
if (procEntry.AttributesMask.HasAnyFlag(ProcAttributes.ReqSpellmod))
{
Spell spell = eventInfo.GetProcSpell();
if (spell != null)
if (!spell.m_appliedMods.Contains(this))
return 0;
}
}
// check proc cooldown
if (IsProcOnCooldown(now))
return false;
/// @todo
// something about triggered spells triggering, and add extra attack effect
return 0;
// do checks against db data
if (!Global.SpellMgr.CanSpellTriggerProcOnEvent(procEntry, eventInfo))
return false;
return 0;
// check if aura can proc when spell is triggered
if (!procEntry.AttributesMask.HasAnyFlag(ProcAttributes.TriggeredCanProc))
{
Spell spell = eventInfo.GetProcSpell();
if (spell)
if (spell.IsTriggered())
if (!GetSpellInfo().HasAttribute(SpellAttr3.CanProcWithTriggered))
return 0;
}
// do checks using conditions table
if (!Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.SpellProc, GetId(), eventInfo.GetActor(), eventInfo.GetActionTarget()))
return false;
return 0;
// AuraScript Hook
bool check = CallScriptCheckProcHandlers(aurApp, eventInfo);
if (!check)
return false;
return 0;
// At least one effect has to pass checks to proc aura
uint procEffectMask = 0;
for (byte i = 0; i < SpellConst.MaxEffects; ++i)
if (aurApp.HasEffect(i))
if (GetEffect(i).CheckEffectProc(aurApp, eventInfo))
procEffectMask |= (1u << i);
if (procEffectMask == 0)
return 0;
/// @todo
// do allow additional requirements for procs
// this is needed because this is the last moment in which you can prevent aura charge drop on proc
// and possibly a way to prevent default checks (if there're going to be any)
// Aura added by spell can't trigger from self (prevent drop charges/do triggers)
// But except periodic and kill triggers (can triggered from self)
SpellInfo spellInfo = eventInfo.GetSpellInfo();
if (spellInfo != null)
if (spellInfo.Id == GetId() && !eventInfo.GetTypeMask().HasAnyFlag(ProcFlags.TakenPeriodic | ProcFlags.Kill))
return 0;
// Check if current equipment meets aura requirements
// do that only for passive spells
/// @todo this needs to be unified for all kinds of auras
@@ -1734,11 +1761,12 @@ namespace Game.Spells
if (GetSpellInfo().EquippedItemClass == ItemClass.Weapon)
{
if (target.ToPlayer().IsInFeralForm())
return false;
return 0;
if (eventInfo.GetDamageInfo() != null)
DamageInfo damageInfo = eventInfo.GetDamageInfo();
if (damageInfo != null)
{
WeaponAttackType attType = eventInfo.GetDamageInfo().GetAttackType();
WeaponAttackType attType = damageInfo.GetAttackType();
Item item = null;
if (attType == WeaponAttackType.BaseAttack || attType == WeaponAttackType.RangedAttack)
item = target.ToPlayer().GetUseableItemByPos(InventorySlots.Bag0, EquipmentSlot.MainHand);
@@ -1746,7 +1774,7 @@ namespace Game.Spells
item = target.ToPlayer().GetUseableItemByPos(InventorySlots.Bag0, EquipmentSlot.OffHand);
if (item == null || item.IsBroken() || item.GetTemplate().GetClass() != ItemClass.Weapon || !Convert.ToBoolean((1 << (int)item.GetTemplate().GetSubClass()) & GetSpellInfo().EquippedItemSubClassMask))
return false;
return 0;
}
}
else if (GetSpellInfo().EquippedItemClass == ItemClass.Armor)
@@ -1754,11 +1782,16 @@ namespace Game.Spells
// Check if player is wearing shield
Item item = target.ToPlayer().GetUseableItemByPos(InventorySlots.Bag0, EquipmentSlot.OffHand);
if (item == null || item.IsBroken() || item.GetTemplate().GetClass() != ItemClass.Armor || !Convert.ToBoolean((1 << (int)item.GetTemplate().GetSubClass()) & GetSpellInfo().EquippedItemSubClassMask))
return false;
return 0;
}
}
return RandomHelper.randChance(CalcProcChance(procEntry, eventInfo));
SetLastProcAttemptTime(now);
if (RandomHelper.randChance(CalcProcChance(procEntry, eventInfo)))
return procEffectMask;
return 0;
}
float CalcProcChance(SpellProcEntry procEntry, ProcEventInfo eventInfo)
@@ -1775,6 +1808,10 @@ namespace Game.Spells
uint WeaponSpeed = caster.GetBaseAttackTime(eventInfo.GetDamageInfo().GetAttackType());
chance = caster.GetPPMProcChance(WeaponSpeed, procEntry.ProcsPerMinute, GetSpellInfo());
}
if (GetSpellInfo().ProcBasePPM > 0.0f)
chance = CalcPPMProcChance(caster);
// apply chance modifer aura, applies also to ppm chance (see improved judgement of light spell)
Player modOwner = caster.GetSpellModOwner();
if (modOwner != null)
@@ -1783,18 +1820,23 @@ namespace Game.Spells
return chance;
}
void TriggerProcOnEvent(AuraApplication aurApp, ProcEventInfo eventInfo)
public void TriggerProcOnEvent(uint procEffectMask, AuraApplication aurApp, ProcEventInfo eventInfo)
{
bool prevented = CallScriptProcHandlers(aurApp, eventInfo);
if (!prevented)
{
CallScriptProcHandlers(aurApp, eventInfo);
for (byte i = 0; i < SpellConst.MaxEffects; ++i)
{
if (aurApp.HasEffect(i))
if (!Convert.ToBoolean(procEffectMask & (1 << i)))
continue;
// OnEffectProc / AfterEffectProc hooks handled in AuraEffect.HandleProc()
if (aurApp.HasEffect(i))
GetEffect(i).HandleProc(aurApp, eventInfo);
}
CallScriptAfterProcHandlers(aurApp, eventInfo);
}
// Remove aura if we've used last charge to proc
if (IsUsingCharges() && GetCharges() == 0)
@@ -2157,6 +2199,23 @@ namespace Game.Spells
}
}
public bool CallScriptCheckEffectProcHandlers(AuraEffect aurEff, AuraApplication aurApp, ProcEventInfo eventInfo)
{
bool result = true;
foreach (var auraScript in m_loadedScripts)
{
auraScript._PrepareScriptCall(AuraScriptHookType.CheckEffectProc, aurApp);
foreach (var hook in auraScript.DoCheckEffectProc)
if (hook.IsEffectAffected(m_spellInfo, aurEff.GetEffIndex()))
result &= hook.Call(aurEff, eventInfo);
auraScript._FinishScriptCall();
}
return result;
}
public bool CallScriptEffectProcHandlers(AuraEffect aurEff, AuraApplication aurApp, ProcEventInfo eventInfo)
{
bool preventDefault = false;
@@ -2308,7 +2367,7 @@ namespace Game.Spells
public void SetLastProcSuccessTime(DateTime lastProcSuccessTime) { m_lastProcSuccessTime = lastProcSuccessTime; }
//Static Methods
public static uint BuildEffectMaskForOwner(SpellInfo spellProto, uint avalibleEffectMask, WorldObject owner)
public static uint BuildEffectMaskForOwner(SpellInfo spellProto, uint availableEffectMask, WorldObject owner)
{
Contract.Assert(spellProto != null);
Contract.Assert(owner != null);
@@ -2333,24 +2392,25 @@ namespace Game.Spells
default:
break;
}
return (effMask & avalibleEffectMask);
return (effMask & availableEffectMask);
}
public static Aura TryRefreshStackOrCreate(SpellInfo spellproto, ObjectGuid castId, uint tryEffMask, WorldObject owner, Unit caster, int[] baseAmount = null, Item castItem = null, ObjectGuid casterGUID = default(ObjectGuid), int castItemLevel = -1)
public static Aura TryRefreshStackOrCreate(SpellInfo spellproto, ObjectGuid castId, uint tryEffMask, WorldObject owner, Unit caster, int[] baseAmount = null, Item castItem = null, ObjectGuid casterGUID = default(ObjectGuid), bool resetPeriodicTimer = true, int castItemLevel = -1)
{
bool throwway;
return TryRefreshStackOrCreate(spellproto, castId, tryEffMask, owner, caster, out throwway, baseAmount, castItem, casterGUID);
return TryRefreshStackOrCreate(spellproto, castId, tryEffMask, owner, caster, out throwway, baseAmount, castItem, casterGUID, resetPeriodicTimer, castItemLevel);
}
public static Aura TryRefreshStackOrCreate(SpellInfo spellproto, ObjectGuid castId, uint tryEffMask, WorldObject owner, Unit caster, out bool refresh, int[] baseAmount, Item castItem = null, ObjectGuid casterGUID = default(ObjectGuid), int castItemLevel = -1)
public static Aura TryRefreshStackOrCreate(SpellInfo spellproto, ObjectGuid castId, uint tryEffMask, WorldObject owner, Unit caster, out bool refresh, int[] baseAmount, Item castItem = null, ObjectGuid casterGUID = default(ObjectGuid), bool resetPeriodicTimer = true, int castItemLevel = -1)
{
Contract.Assert(spellproto != null);
Contract.Assert(owner != null);
Contract.Assert(caster || !casterGUID.IsEmpty());
Contract.Assert(tryEffMask <= SpellConst.MaxEffectMask);
refresh = false;
uint effMask = BuildEffectMaskForOwner(spellproto, tryEffMask, owner);
if (effMask == 0)
return null;
Aura foundAura = owner.ToUnit()._TryStackingOrRefreshingExistingAura(spellproto, effMask, caster, baseAmount, castItem, casterGUID, castItemLevel);
Aura foundAura = owner.ToUnit()._TryStackingOrRefreshingExistingAura(spellproto, effMask, caster, baseAmount, castItem, casterGUID, resetPeriodicTimer, castItemLevel);
if (foundAura != null)
{
// we've here aura, which script triggered removal after modding stack amount
+129 -156
View File
@@ -232,9 +232,9 @@ namespace Game.Spells
else // aura just created or reapplied
{
m_tickNumber = 0;
// reset periodic timer on aura create or on reapply when aura isn't dot
// possibly we should not reset periodic timers only when aura is triggered by proc
// or maybe there's a spell attribute somewhere
// reset periodic timer on aura create or reapply
// we don't reset periodic timers when aura is triggered by proc
if (resetPeriodicTimer)
{
m_periodicTimer = 0;
@@ -258,7 +258,6 @@ namespace Game.Spells
m_spellmod.type = GetAuraType() == AuraType.AddPctModifier ? SpellModType.Pct : SpellModType.Flat;
m_spellmod.spellId = GetId();
m_spellmod.mask = GetSpellEffectInfo().SpellClassMask;
m_spellmod.charges = GetBase().GetCharges();
}
m_spellmod.value = GetAmount();
break;
@@ -655,6 +654,61 @@ namespace Game.Spells
}
}
public bool CheckEffectProc(AuraApplication aurApp, ProcEventInfo eventInfo)
{
bool result = GetBase().CallScriptCheckEffectProcHandlers(this, aurApp, eventInfo);
if (!result)
return false;
SpellInfo spellInfo = eventInfo.GetSpellInfo();
switch (GetAuraType())
{
case AuraType.MechanicImmunity:
case AuraType.ModMechanicResistance:
// compare mechanic
if (spellInfo == null || (int)spellInfo.Mechanic != GetMiscValue())
result = false;
break;
case AuraType.ModCastingSpeedNotStack:
// skip melee hits and instant cast spells
if (spellInfo == null || spellInfo.CalcCastTime() == 0)
result = false;
break;
case AuraType.ModSpellDamageFromCaster:
// Compare casters
if (GetCasterGUID() != eventInfo.GetActor().GetGUID())
result = false;
break;
case AuraType.ModPowerCostSchool:
case AuraType.ModPowerCostSchoolPct:
{
// Skip melee hits and spells with wrong school or zero cost
if (spellInfo == null || !Convert.ToBoolean((int)spellInfo.GetSchoolMask() & GetMiscValue()) // School Check
|| !eventInfo.GetProcSpell())
{
result = false;
break;
}
// Costs Check
var costs = eventInfo.GetProcSpell().GetPowerCost();
var m = costs.Find(cost => { return cost.Amount > 0; });
if (m == null)
result = false;
break;
}
case AuraType.ReflectSpellsSchool:
// Skip melee hits and spells with wrong school
if (spellInfo == null || !Convert.ToBoolean((int)spellInfo.GetSchoolMask() & GetMiscValue()))
result = false;
break;
default:
break;
}
return result;
}
public void HandleProc(AuraApplication aurApp, ProcEventInfo eventInfo)
{
bool prevented = GetBase().CallScriptEffectProcHandlers(this, aurApp, eventInfo);
@@ -663,6 +717,16 @@ namespace Game.Spells
switch (GetAuraType())
{
// CC Auras which use their amount to drop
// Are there any more auras which need this?
case AuraType.ModConfuse:
case AuraType.ModFear:
case AuraType.ModStun:
case AuraType.ModRoot:
case AuraType.Transform:
HandleBreakableCCAuraProc(aurApp, eventInfo);
break;
case AuraType.Dummy:
case AuraType.ProcTriggerSpell:
HandleProcTriggerSpellAuraProc(aurApp, eventInfo);
break;
@@ -672,16 +736,6 @@ namespace Game.Spells
case AuraType.ProcTriggerDamage:
HandleProcTriggerDamageAuraProc(aurApp, eventInfo);
break;
case AuraType.RaidProcFromCharge:
HandleRaidProcFromChargeAuraProc(aurApp, eventInfo);
break;
case AuraType.RaidProcFromChargeWithValue:
HandleRaidProcFromChargeWithValueAuraProc(aurApp, eventInfo);
break;
case AuraType.ProcOnPowerAmount:
case AuraType.ProcOnPowerAmount2:
HandleProcTriggerSpellOnPowerAmountAuraProc(aurApp, eventInfo);
break;
default:
break;
}
@@ -829,7 +883,6 @@ namespace Game.Spells
public Unit GetCaster() { return auraBase.GetCaster(); }
public ObjectGuid GetCasterGUID() { return auraBase.GetCasterGUID(); }
public Aura GetBase() { return auraBase; }
public SpellModifier GetSpellModifier() { return m_spellmod; }
public SpellInfo GetSpellInfo() { return m_spellInfo; }
public uint GetId() { return m_spellInfo.Id; }
@@ -5131,7 +5184,10 @@ namespace Game.Spells
if (caster != null)
{
uint heal = (uint)caster.CountPctFromMaxHealth(10);
caster.HealBySpell(target, auraSpellInfo, heal);
HealInfo healInfo = new HealInfo(caster, target, heal, auraSpellInfo, auraSpellInfo.GetSchoolMask());
caster.HealBySpell(healInfo);
/// @todo: should proc other auras?
int mana = caster.GetMaxPower(PowerType.Mana);
if (mana != 0)
{
@@ -5535,7 +5591,7 @@ namespace Game.Spells
// Set trigger flag
ProcFlags procAttacker = ProcFlags.DonePeriodic;
ProcFlags procVictim = ProcFlags.TakenPeriodic;
ProcFlagsExLegacy procEx = (crit ? ProcFlagsExLegacy.CriticalHit : ProcFlagsExLegacy.NormalHit) | ProcFlagsExLegacy.InternalDot;
ProcFlagsHit hitMask = crit ? ProcFlagsHit.Critical : ProcFlagsHit.Normal;
damage = (damage <= absorb + resist) ? 0 : (damage - absorb - resist);
if (damage != 0)
procVictim |= ProcFlags.TakenDamage;
@@ -5546,7 +5602,8 @@ namespace Game.Spells
SpellPeriodicAuraLogInfo pInfo = new SpellPeriodicAuraLogInfo(this, damage, overkill, absorb, resist, 0.0f, crit);
caster.ProcDamageAndSpell(target, procAttacker, procVictim, procEx, damage, WeaponAttackType.BaseAttack, GetSpellInfo());
DamageInfo damageInfo = new DamageInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetSchoolMask(), DamageEffectType.DOT, WeaponAttackType.BaseAttack);
caster.ProcSkillsAndAuras(target, procAttacker, procVictim, ProcFlagsSpellType.Damage, ProcFlagsSpellPhase.None, hitMask, null, damageInfo, null);
caster.DealDamage(target, damage, cleanDamage, DamageEffectType.DOT, GetSpellInfo().GetSchoolMask(), GetSpellInfo(), true);
target.SendPeriodicAuraLog(pInfo);
@@ -5624,12 +5681,17 @@ namespace Game.Spells
// Set trigger flag
ProcFlags procAttacker = ProcFlags.DonePeriodic;
ProcFlags procVictim = ProcFlags.TakenPeriodic;
ProcFlagsExLegacy procEx = (crit ? ProcFlagsExLegacy.CriticalHit : ProcFlagsExLegacy.NormalHit) | ProcFlagsExLegacy.InternalDot;
ProcFlagsHit hitMask = crit ? ProcFlagsHit.Critical : ProcFlagsHit.Normal;
damage = (damage <= absorb + resist) ? 0 : (damage - absorb - resist);
if (damage != 0)
procVictim |= ProcFlags.TakenDamage;
if (caster.IsAlive())
caster.ProcDamageAndSpell(target, procAttacker, procVictim, procEx, damage, WeaponAttackType.BaseAttack, GetSpellInfo());
{
DamageInfo damageInfo = new DamageInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetSchoolMask(), DamageEffectType.DOT, WeaponAttackType.BaseAttack);
caster.ProcSkillsAndAuras(target, procAttacker, procVictim, ProcFlagsSpellType.Damage, ProcFlagsSpellPhase.None, hitMask, null, damageInfo, null);
}
int new_damage = (int)caster.DealDamage(target, damage, cleanDamage, DamageEffectType.DOT, GetSpellInfo().GetSchoolMask(), GetSpellInfo(), false);
if (caster.IsAlive())
{
@@ -5638,8 +5700,11 @@ namespace Game.Spells
uint heal = (caster.SpellHealingBonusDone(caster, GetSpellInfo(), (uint)(new_damage * gainMultiplier), DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()));
heal = (caster.SpellHealingBonusTaken(caster, GetSpellInfo(), heal, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()));
int gain = caster.HealBySpell(caster, GetSpellInfo(), heal);
caster.getHostileRefManager().threatAssist(caster, gain * 0.5f, GetSpellInfo());
HealInfo healInfo = new HealInfo(caster, caster, heal, GetSpellInfo(), GetSpellInfo().GetSchoolMask());
caster.HealBySpell(healInfo);
caster.getHostileRefManager().threatAssist(caster, healInfo.GetEffectiveHeal() * 0.5f, GetSpellInfo());
caster.ProcSkillsAndAuras(caster, ProcFlags.DonePeriodic, ProcFlags.TakenPeriodic, ProcFlagsSpellType.Heal, ProcFlagsSpellPhase.None, hitMask, null, null, healInfo);
}
caster.SendSpellNonMeleeDamageLog(log);
@@ -5670,7 +5735,9 @@ namespace Game.Spells
damage = (uint)(damage * gainMultiplier);
caster.HealBySpell(target, GetSpellInfo(), damage);
HealInfo healInfo = new HealInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo().GetSchoolMask());
caster.HealBySpell(healInfo);
caster.ProcSkillsAndAuras(target, ProcFlags.DonePeriodic, ProcFlags.TakenPeriodic, ProcFlagsSpellType.Heal, ProcFlagsSpellPhase.None, ProcFlagsHit.Normal, null, null, healInfo);
}
void HandlePeriodicHealAurasTick(Unit target, Unit caster)
@@ -5750,22 +5817,23 @@ namespace Game.Spells
Log.outDebug(LogFilter.Spells, "PeriodicTick: {0} (TypeId: {1}) heal of {2} (TypeId: {3}) for {4} health inflicted by {5}",
GetCasterGUID().ToString(), GetCaster().GetTypeId(), target.GetGUID().ToString(), target.GetTypeId(), damage, GetId());
uint absorb = 0;
uint heal = (uint)damage;
caster.CalcHealAbsorb(target, GetSpellInfo(), ref heal, ref absorb);
int gain = caster.DealHeal(target, heal);
SpellPeriodicAuraLogInfo pInfo = new SpellPeriodicAuraLogInfo(this, heal, (int)(heal - gain), absorb, 0, 0.0f, crit);
HealInfo healInfo = new HealInfo(caster, target, heal, GetSpellInfo(), GetSpellInfo().GetSchoolMask());
caster.CalcHealAbsorb(healInfo);
caster.DealHeal(healInfo);
SpellPeriodicAuraLogInfo pInfo = new SpellPeriodicAuraLogInfo(this, heal, (int)(heal - healInfo.GetEffectiveHeal()), healInfo.GetAbsorb(), 0, 0.0f, crit);
target.SendPeriodicAuraLog(pInfo);
target.getHostileRefManager().threatAssist(caster, gain * 0.5f, GetSpellInfo());
target.getHostileRefManager().threatAssist(caster, healInfo.GetEffectiveHeal() * 0.5f, GetSpellInfo());
ProcFlags procAttacker = ProcFlags.DonePeriodic;
ProcFlags procVictim = ProcFlags.TakenPeriodic;
ProcFlagsExLegacy procEx = (crit ? ProcFlagsExLegacy.CriticalHit : ProcFlagsExLegacy.NormalHit) | ProcFlagsExLegacy.InternalHot;
ProcFlagsHit hitMask = crit ? ProcFlagsHit.Critical : ProcFlagsHit.Normal;
// ignore item heals
if (GetBase().GetCastItemGUID().IsEmpty())
caster.ProcDamageAndSpell(target, procAttacker, procVictim, procEx, (uint)damage, WeaponAttackType.BaseAttack, GetSpellInfo());
caster.ProcSkillsAndAuras(target, procAttacker, procVictim, ProcFlagsSpellType.Heal, ProcFlagsSpellPhase.None, hitMask, null, null, healInfo);
}
void HandlePeriodicManaLeechAuraTick(Unit target, Unit caster)
@@ -5912,16 +5980,43 @@ namespace Game.Spells
// Set trigger flag
ProcFlags procAttacker = ProcFlags.DonePeriodic;
ProcFlags procVictim = ProcFlags.TakenPeriodic;
ProcFlagsExLegacy procEx = Unit.createProcExtendMask(damageInfo, SpellMissInfo.None) | ProcFlagsExLegacy.InternalDot;
ProcFlagsHit hitMask = Unit.createProcHitMask(damageInfo, SpellMissInfo.None);
ProcFlagsSpellType spellTypeMask = ProcFlagsSpellType.NoDmgHeal;
if (damageInfo.damage != 0)
{
procVictim |= ProcFlags.TakenDamage;
spellTypeMask |= ProcFlagsSpellType.Damage;
}
caster.ProcDamageAndSpell(damageInfo.target, procAttacker, procVictim, procEx, damageInfo.damage, WeaponAttackType.BaseAttack, spellProto);
DamageInfo dotDamageInfo = new DamageInfo(damageInfo, DamageEffectType.DOT, WeaponAttackType.BaseAttack, hitMask);
caster.ProcSkillsAndAuras(target, procAttacker, procVictim, spellTypeMask, ProcFlagsSpellPhase.None, hitMask, null, dotDamageInfo, null);
caster.DealSpellDamage(damageInfo, true);
caster.SendSpellNonMeleeDamageLog(damageInfo);
}
void HandleBreakableCCAuraProc(AuraApplication aurApp, ProcEventInfo eventInfo)
{
DamageInfo damageInfo = eventInfo.GetDamageInfo();
if (damageInfo == null)
return;
// aura own damage at apply won't break CC
if (eventInfo.GetSpellPhaseMask().HasAnyFlag(ProcFlagsSpellPhase.Cast))
{
SpellInfo spellInfo = eventInfo.GetSpellInfo();
if (spellInfo != null)
if (spellInfo == GetSpellInfo())
return;
}
int damageLeft = GetAmount();
if (damageLeft < damageInfo.GetDamage())
aurApp.GetTarget().RemoveAura(aurApp);
else
ChangeAmount((int)(damageLeft - damageInfo.GetDamage()));
}
void HandleProcTriggerSpellAuraProc(AuraApplication aurApp, ProcEventInfo eventInfo)
{
Unit triggerCaster = aurApp.GetTarget();
@@ -5935,7 +6030,7 @@ namespace Game.Spells
triggerCaster.CastSpell(triggerTarget, triggeredSpellInfo, true, null, this);
}
else
Log.outDebug(LogFilter.Spells, "AuraEffect.HandleProcTriggerSpellAuraProc: Could not trigger spell {0} from aura {1} proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId());
Log.outError(LogFilter.Spells, "AuraEffect.HandleProcTriggerSpellAuraProc: Could not trigger spell {0} from aura {1} proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId());
}
void HandleProcTriggerSpellWithValueAuraProc(AuraApplication aurApp, ProcEventInfo eventInfo)
@@ -5952,7 +6047,7 @@ namespace Game.Spells
triggerCaster.CastCustomSpell(triggerTarget, triggerSpellId, basepoints0, 0, 0, true, null, this);
}
else
Log.outDebug(LogFilter.Spells, "AuraEffect.HandleProcTriggerSpellWithValueAuraProc: Could not trigger spell {0} from aura {1} proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId());
Log.outError(LogFilter.Spells, "AuraEffect.HandleProcTriggerSpellWithValueAuraProc: Could not trigger spell {0} from aura {1} proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId());
}
public void HandleProcTriggerDamageAuraProc(AuraApplication aurApp, ProcEventInfo eventInfo)
@@ -5968,128 +6063,6 @@ namespace Game.Spells
target.SendSpellNonMeleeDamageLog(damageInfo);
}
void HandleRaidProcFromChargeAuraProc(AuraApplication aurApp, ProcEventInfo eventInfo)
{
Unit target = aurApp.GetTarget();
uint triggerSpellId;
switch (GetId())
{
case 57949: // Shiver
triggerSpellId = 57952;
//animationSpellId = 57951; dummy effects for jump spell have unknown use (see also 41637)
break;
case 59978: // Shiver
triggerSpellId = 59979;
break;
case 43593: // Cold Stare
triggerSpellId = 43594;
break;
default:
Log.outDebug(LogFilter.Spells, "AuraEffect.HandleRaidProcFromChargeAuraProc: received not handled spell: {0}", GetId());
return;
}
int jumps = GetBase().GetCharges();
// current aura expire on proc finish
GetBase().SetCharges(0);
GetBase().SetUsingCharges(true);
// next target selection
if (jumps > 0)
{
Unit caster = GetCaster();
if (caster != null)
{
float radius = GetSpellEffectInfo().CalcRadius(caster);
Unit triggerTarget = target.GetNextRandomRaidMemberOrPet(radius);
if (triggerTarget != null)
{
target.CastSpell(triggerTarget, GetSpellInfo(), true, null, this, GetCasterGUID());
Aura aura = triggerTarget.GetAura(GetId(), GetCasterGUID());
if (aura != null)
aura.SetCharges((byte)jumps);
}
}
}
Log.outDebug(LogFilter.Spells, "AuraEffect.HandleRaidProcFromChargeAuraProc: Triggering spell {0} from aura {1} proc", triggerSpellId, GetId());
target.CastSpell(target, triggerSpellId, true, null, this, GetCasterGUID());
}
void HandleRaidProcFromChargeWithValueAuraProc(AuraApplication aurApp, ProcEventInfo eventInfo)
{
Unit target = aurApp.GetTarget();
// Currently only Prayer of Mending
if (!(GetSpellInfo().SpellFamilyName == SpellFamilyNames.Priest) && GetSpellInfo().SpellFamilyFlags[1].HasAnyFlag<uint>(0x20))
{
Log.outDebug(LogFilter.Spells, "AuraEffect.HandleRaidProcFromChargeWithValueAuraProc: received not handled spell: {0}", GetId());
return;
}
uint triggerSpellId = 33110;
int value = GetAmount();
int jumps = GetBase().GetCharges();
// current aura expire on proc finish
GetBase().SetCharges(0);
GetBase().SetUsingCharges(true);
// next target selection
if (jumps > 0)
{
Unit caster = GetCaster();
if (caster != null)
{
float radius = GetSpellEffectInfo().CalcRadius(caster);
Unit triggerTarget = target.GetNextRandomRaidMemberOrPet(radius);
if (triggerTarget != null)
{
target.CastCustomSpell(triggerTarget, GetId(), value, 0, 0, true, null, this, GetCasterGUID());
Aura aura = triggerTarget.GetAura(GetId(), GetCasterGUID());
if (aura != null)
aura.SetCharges((byte)jumps);
}
}
}
Log.outDebug(LogFilter.Spells, "AuraEffect.HandleRaidProcFromChargeWithValueAuraProc: Triggering spell {0} from aura {1} proc", triggerSpellId, GetId());
target.CastCustomSpell(target, triggerSpellId, value, 0, 0, true, null, this, GetCasterGUID());
}
public void HandleProcTriggerSpellOnPowerAmountAuraProc(AuraApplication aurApp, ProcEventInfo eventInfo)
{
// Power amount required to proc the spell
int powerAmountRequired = GetAmount();
// Power type required to proc
PowerType powerRequired = (PowerType)GetSpellInfo().GetEffect(GetEffIndex()).MiscValue;
if (powerRequired == 0 || powerAmountRequired == 0)
{
Log.outError(LogFilter.Spells, "AuraEffect.HandleProcTriggerSpellOnPowerAmountAuraProc: Spell {0} have 0 PowerAmountRequired in EffectAmount[{1}] or 0 PowerRequired in EffectMiscValue", GetId(), GetEffIndex());
return /*false*/;
}
Unit triggerCaster = aurApp.GetTarget();
Unit triggerTarget = eventInfo.GetProcTarget();
if (triggerCaster.GetPower(powerRequired) != powerAmountRequired)
return /*false*/;
uint triggerSpellId = GetSpellInfo().GetEffect(GetEffIndex()).TriggerSpell;
SpellInfo triggeredSpellInfo = Global.SpellMgr.GetSpellInfo(triggerSpellId);
if (triggeredSpellInfo != null)
{
Log.outDebug(LogFilter.Spells, "AuraEffect.HandleProcTriggerSpellOnPowerAmountAuraProc: Triggering spell {0} from aura {1} proc", triggeredSpellInfo.Id, GetId());
triggerCaster.CastSpell(triggerTarget, triggeredSpellInfo, true, null, this);
}
else
Log.outDebug(LogFilter.Spells, "AuraEffect.HandleProcTriggerSpellOnPowerAmountAuraProc: Could not trigger spell {0} from aura {1} proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId());
}
[AuraEffectHandler(AuraType.ForceWeather)]
void HandleAuraForceWeather(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply)
{
+123 -96
View File
@@ -1504,12 +1504,11 @@ namespace Game.Spells
return searcher.GetTarget();
}
void prepareDataForTriggerSystem(AuraEffect triggeredByAura)
void prepareDataForTriggerSystem()
{
//==========================================================================================
// Now fill data for trigger system, need know:
// can spell trigger another or not (m_canTrigger)
// Create base triggers flags for Attacker and Victim (m_procAttacker, m_procVictim and m_procEx)
// Create base triggers flags for Attacker and Victim (m_procAttacker, m_procVictim and m_hitMask)
//==========================================================================================
m_procVictim = m_procAttacker = 0;
@@ -1549,7 +1548,7 @@ namespace Game.Spells
// For other spells trigger procflags are set in Spell.DoAllEffectOnTarget
// Because spell positivity is dependant on target
}
m_procEx = ProcFlagsExLegacy.None;
m_hitMask = ProcFlagsHit.None;
// Hunter trap spells - activation proc for Lock and Load, Entrapment and Misdirection
if (m_spellInfo.SpellFamilyName == SpellFamilyNames.Hunter &&
@@ -1559,28 +1558,12 @@ namespace Game.Spells
m_procAttacker |= ProcFlags.DoneTrapActivation;
//Effects which are result of aura proc from triggered spell cannot proc to prevent chain proc of these spells
// Hellfire Effect - trigger as DOT
if (m_spellInfo.SpellFamilyName == SpellFamilyNames.Warlock && m_spellInfo.SpellFamilyFlags[0].HasAnyFlag(0x00000040u))
{
m_procAttacker = ProcFlags.DonePeriodic;
m_procVictim = ProcFlags.TakenPeriodic;
}
// Ranged autorepeat attack is set as triggered spell - ignore it
if (!Convert.ToBoolean(m_procAttacker & ProcFlags.DoneRangedAutoAttack))
{
if (_triggeredCastFlags.HasAnyFlag(TriggerCastFlags.DisallowProcEvents) &&
(m_spellInfo.HasAttribute(SpellAttr2.TriggeredCanTriggerProc) ||
m_spellInfo.HasAttribute(SpellAttr3.TriggeredCanTriggerProc2)))
m_procEx |= ProcFlagsExLegacy.InternalCantProc;
else if (_triggeredCastFlags.HasAnyFlag(TriggerCastFlags.DisallowProcEvents))
m_procEx |= ProcFlagsExLegacy.InternalTriggered;
}
// Totem casts require spellfamilymask defined in spell_proc_event to proc
if (m_originalCaster != null && m_caster != m_originalCaster && m_caster.IsTypeId(TypeId.Unit) && m_caster.IsTotem() && m_caster.IsControlledByPlayer())
m_procEx |= ProcFlagsExLegacy.InternalReqFamily;
}
public void CleanupTargetList()
@@ -1687,10 +1670,10 @@ namespace Game.Spells
if (targetInfo.missCondition == SpellMissInfo.Reflect)
{
// Calculate reflected spell result on caster
targetInfo.reflectResult = m_caster.SpellHitResult(m_caster, m_spellInfo, m_canReflect);
targetInfo.reflectResult = m_caster.SpellHitResult(m_caster, m_spellInfo, false);
if (targetInfo.reflectResult == SpellMissInfo.Reflect) // Impossible reflect again, so simply deflect spell
targetInfo.reflectResult = SpellMissInfo.Parry;
// Proc spell reflect aura when missile hits the original target
target.m_Events.AddEvent(new ProcReflectDelayed(target, m_originalCasterGUID), target.m_Events.CalculateTime(targetInfo.timeDelay));
// Increase time interval for reflected spells by 1.5
targetInfo.timeDelay += targetInfo.timeDelay >> 1;
@@ -1858,13 +1841,12 @@ namespace Game.Spells
// Fill base trigger info
ProcFlags procAttacker = m_procAttacker;
ProcFlags procVictim = m_procVictim;
ProcFlagsExLegacy procEx = m_procEx;
ProcFlagsHit hitMask = m_hitMask;
m_spellAura = null;
//Spells with this flag cannot trigger if effect is cast on self
bool canEffectTrigger = !m_spellInfo.HasAttribute(SpellAttr3.CantTriggerProc) && unitTarget.CanProc()
&& (CanExecuteTriggersOnHit(mask) || missInfo == SpellMissInfo.Immune || missInfo == SpellMissInfo.Immune2);
bool canEffectTrigger = !m_spellInfo.HasAttribute(SpellAttr3.CantTriggerProc) && (CanExecuteTriggersOnHit(mask) || missInfo == SpellMissInfo.Immune || missInfo == SpellMissInfo.Immune2);
Unit spellHitTarget = null;
@@ -1901,16 +1883,8 @@ namespace Game.Spells
}
// Do not take combo points on dodge and miss
if (missInfo != SpellMissInfo.None && m_needComboPoints &&
m_targets.GetUnitTargetGUID() == target.targetGUID)
{
if (missInfo != SpellMissInfo.None && m_needComboPoints && m_targets.GetUnitTargetGUID() == target.targetGUID)
m_needComboPoints = false;
// Restore spell mods for a miss/dodge/parry Cold Blood
/// @todo check how broad this rule should be
if (m_caster.IsTypeId(TypeId.Player) && (missInfo == SpellMissInfo.Miss ||
missInfo == SpellMissInfo.Dodge || missInfo == SpellMissInfo.Parry))
m_caster.ToPlayer().RestoreSpellMods(this, 14177);
}
// Trigger info was not filled in spell.preparedatafortriggersystem - we do it now
if (canEffectTrigger && procAttacker == 0 && procVictim == 0)
@@ -1959,19 +1933,20 @@ namespace Game.Spells
uint addhealth = (uint)m_healing;
if (crit)
{
procEx |= ProcFlagsExLegacy.CriticalHit;
hitMask |= ProcFlagsHit.Critical;
addhealth = (uint)caster.SpellCriticalHealingBonus(m_spellInfo, (int)addhealth, null);
}
else
procEx |= ProcFlagsExLegacy.NormalHit;
hitMask |= ProcFlagsHit.Normal;
int gain = caster.HealBySpell(unitTarget, m_spellInfo, addhealth, crit);
unitTarget.getHostileRefManager().threatAssist(caster, gain * 0.5f, m_spellInfo);
m_healing = gain;
HealInfo healInfo = new HealInfo(caster, unitTarget, addhealth, m_spellInfo, m_spellInfo.GetSchoolMask());
caster.HealBySpell(healInfo, crit);
unitTarget.getHostileRefManager().threatAssist(caster, healInfo.GetEffectiveHeal() * 0.5f, m_spellInfo);
m_healing = (int)healInfo.GetEffectiveHeal();
// Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
if (canEffectTrigger && missInfo != SpellMissInfo.Reflect)
caster.ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, addhealth, m_attackType, m_spellInfo, m_triggeredByAuraSpell);
// Do triggers for unit
if (canEffectTrigger)
caster.ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, ProcFlagsSpellType.Heal, ProcFlagsSpellPhase.Hit, hitMask, this, null, healInfo);
}
// Do damage and triggers
else if (m_damage > 0)
@@ -1983,16 +1958,18 @@ namespace Game.Spells
caster.CalculateSpellDamageTaken(damageInfo, m_damage, m_spellInfo, m_attackType, target.crit);
caster.DealDamageMods(damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb);
procEx |= Unit.createProcExtendMask(damageInfo, missInfo);
hitMask |= Unit.createProcHitMask(damageInfo, missInfo);
procVictim |= ProcFlags.TakenDamage;
// Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
if (canEffectTrigger && missInfo != SpellMissInfo.Reflect)
// Do triggers for unit
if (canEffectTrigger)
{
caster.ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell);
if (caster.IsTypeId(TypeId.Player) && !m_spellInfo.HasAttribute(SpellAttr0.StopAttackTarget) &&
DamageInfo spellDamageInfo = new DamageInfo(damageInfo, DamageEffectType.SpellDirect, m_attackType, hitMask);
caster.ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, ProcFlagsSpellType.Damage, ProcFlagsSpellPhase.Hit, hitMask, this, spellDamageInfo, null);
if (caster.IsPlayer() && !m_spellInfo.HasAttribute(SpellAttr0.StopAttackTarget) &&
(m_spellInfo.DmgClass == SpellDmgClass.Melee || m_spellInfo.DmgClass == SpellDmgClass.Ranged))
caster.ToPlayer().CastItemCombatSpell(unitTarget, m_attackType, procVictim, procEx);
caster.ToPlayer().CastItemCombatSpell(spellDamageInfo);
}
m_damage = (int)damageInfo.damage;
@@ -2007,10 +1984,17 @@ namespace Game.Spells
{
// Fill base damage struct (unitTarget - is real spell target)
SpellNonMeleeDamage damageInfo = new SpellNonMeleeDamage(caster, unitTarget, m_spellInfo.Id, m_SpellVisual, m_spellSchoolMask);
procEx |= Unit.createProcExtendMask(damageInfo, missInfo);
// Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
if (canEffectTrigger && missInfo != SpellMissInfo.Reflect)
caster.ProcDamageAndSpell(unit, procAttacker, procVictim, procEx, 0, m_attackType, m_spellInfo, m_triggeredByAuraSpell);
hitMask |= Unit.createProcHitMask(damageInfo, missInfo);
// Do triggers for unit
if (canEffectTrigger)
{
DamageInfo spellNoDamageInfo = new DamageInfo(damageInfo, DamageEffectType.NoDamage, m_attackType, hitMask);
caster.ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, ProcFlagsSpellType.NoDmgHeal, ProcFlagsSpellPhase.Hit, hitMask, this, spellNoDamageInfo, null);
if (caster.IsPlayer() && !m_spellInfo.HasAttribute(SpellAttr0.StopAttackTarget) &&
(m_spellInfo.DmgClass == SpellDmgClass.Melee || m_spellInfo.DmgClass == SpellDmgClass.Ranged))
caster.ToPlayer().CastItemCombatSpell(spellNoDamageInfo);
}
// Failed Pickpocket, reveal rogue
if (missInfo == SpellMissInfo.Resist && m_spellInfo.HasAttribute(SpellCustomAttributes.PickPocket) && unitTarget.IsTypeId(TypeId.Unit))
@@ -2168,8 +2152,9 @@ namespace Game.Spells
if (m_originalCaster != null)
{
bool refresh = false;
bool resetPeriodicTimer = !_triggeredCastFlags.HasAnyFlag(TriggerCastFlags.DontResetPeriodicTimer);
m_spellAura = Aura.TryRefreshStackOrCreate(aurSpellInfo, m_castId, (byte)effectMask, unit,
m_originalCaster, out refresh, (aurSpellInfo == m_spellInfo) ? m_spellValue.EffectBasePoints : basePoints, m_CastItem, ObjectGuid.Empty, m_castItemLevel);
m_originalCaster, out refresh, (aurSpellInfo == m_spellInfo) ? m_spellValue.EffectBasePoints : basePoints, m_CastItem, ObjectGuid.Empty, resetPeriodicTimer, m_castItemLevel);
if (m_spellAura != null)
{
// Set aura stack amount to desired value
@@ -2548,7 +2533,7 @@ namespace Game.Spells
}
// Prepare data for triggers
prepareDataForTriggerSystem(triggeredByAura);
prepareDataForTriggerSystem();
if (m_caster.IsTypeId(TypeId.Player))
{
@@ -2667,10 +2652,6 @@ namespace Game.Spells
SendInterrupted(0);
SendCastResult(SpellCastResult.Interrupted);
// spell is canceled-take mods and clear list
if (m_caster.IsTypeId(TypeId.Player))
m_caster.ToPlayer().RemoveSpellMods(this);
m_appliedMods.Clear();
break;
@@ -2923,6 +2904,25 @@ namespace Game.Spells
Creature creatureCaster = m_caster.ToCreature();
if (creatureCaster)
creatureCaster.ReleaseFocus(this);
if (!m_originalCaster)
return;
// Handle procs on cast
ProcFlags procAttacker = m_procAttacker;
if (procAttacker == 0)
{
if (m_spellInfo.DmgClass == SpellDmgClass.Magic)
procAttacker = m_spellInfo.IsPositive() ? ProcFlags.DoneSpellMagicDmgClassPos : ProcFlags.DoneSpellMagicDmgClassNeg;
else
procAttacker = m_spellInfo.IsPositive() ? ProcFlags.DoneSpellNoneDmgClassPos : ProcFlags.DoneSpellNoneDmgClassNeg;
}
ProcFlagsHit hitMask = m_hitMask;
if (!hitMask.HasAnyFlag(ProcFlagsHit.Critical))
hitMask |= ProcFlagsHit.Normal;
m_originalCaster.ProcSkillsAndAuras(null, procAttacker, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Cast, hitMask, this, null, null);
}
void handle_immediate()
@@ -3076,20 +3076,6 @@ namespace Game.Spells
// process items
foreach (var ihit in m_UniqueItemInfo)
DoAllEffectOnTarget(ihit);
if (m_originalCaster == null)
return;
// Handle procs on cast
// @todo finish new proc system:P
if (m_UniqueTargetInfo.Empty() && m_targets.HasDst())
{
var procAttacker = m_procAttacker;
if (procAttacker == 0)
procAttacker |= ProcFlags.DoneSpellMagicDmgClassPos;
// Proc the spells that have DEST target
m_originalCaster.ProcDamageAndSpell(null, procAttacker, 0, m_procEx | ProcFlagsExLegacy.NormalHit, 0, WeaponAttackType.BaseAttack, m_spellInfo, m_triggeredByAuraSpell);
}
}
void _handle_finish_phase()
@@ -3113,7 +3099,25 @@ namespace Game.Spells
else
m_caster.m_extraAttacks = 0;
}
// @todo trigger proc phase finish here
// Handle procs on finish
if (!m_originalCaster)
return;
ProcFlags procAttacker = m_procAttacker;
if (procAttacker == 0)
{
if (m_spellInfo.DmgClass == SpellDmgClass.Magic)
procAttacker = m_spellInfo.IsPositive() ? ProcFlags.DoneSpellMagicDmgClassPos : ProcFlags.DoneSpellMagicDmgClassNeg;
else
procAttacker = m_spellInfo.IsPositive() ? ProcFlags.DoneSpellNoneDmgClassPos : ProcFlags.DoneSpellNoneDmgClassNeg;
}
ProcFlagsHit hitMask = m_hitMask;
if (!hitMask.HasAnyFlag(ProcFlagsHit.Critical))
hitMask |= ProcFlagsHit.Normal;
m_originalCaster.ProcSkillsAndAuras(null, procAttacker, ProcFlags.None, ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Finish, hitMask, this, null, null);
}
void SendSpellCooldown()
@@ -3281,19 +3285,6 @@ namespace Game.Spells
m_caster.ToPlayer().UpdatePotionCooldown(this);
}
Player modOwner = m_caster.GetSpellModOwner();
if (modOwner)
{
// triggered spell pointer can be not set in some cases
// this is needed for proper apply of triggered spell mods
modOwner.SetSpellModTakingSpell(this, true);
// Take mods after trigger spell (needed for 14177 to affect 48664)
// mods are taken only on succesfull cast and independantly from targets of the spell
modOwner.RemoveSpellMods(this);
modOwner.SetSpellModTakingSpell(this, false);
}
// Stop Attack for some spells
if (m_spellInfo.HasAttribute(SpellAttr0.StopAttackTarget))
m_caster.AttackStop();
@@ -6470,7 +6461,7 @@ namespace Game.Spells
if (effect != null && Convert.ToBoolean(m_applyMultiplierMask & (1 << (int)effect.EffectIndex)))
multiplier[effect.EffectIndex] = effect.CalcDamageMultiplier(m_originalCaster, this);
bool usesAmmo = m_spellInfo.HasAttribute(SpellCustomAttributes.DirectDamage);
PrepareTargetProcessing();
foreach (var ihit in m_UniqueTargetInfo)
{
@@ -6482,6 +6473,8 @@ namespace Game.Spells
DoAllEffectOnLaunchTarget(target, multiplier);
}
FinishTargetProcessing();
}
void DoAllEffectOnLaunchTarget(TargetInfo targetInfo, float[] multiplier)
@@ -6994,18 +6987,25 @@ namespace Game.Spells
if (m_spellInfo.StartRecoveryTime >= 750 && m_spellInfo.StartRecoveryTime <= 1500)
{
// gcd modifier auras are applied only to own spells and only players have such mods
if (m_caster.IsTypeId(TypeId.Player))
m_caster.ToPlayer().ApplySpellMod(m_spellInfo.Id, SpellModOp.GlobalCooldown, ref gcd, this);
Player modOwner = m_caster.GetSpellModOwner();
if (modOwner)
modOwner.ApplySpellMod(m_spellInfo.Id, SpellModOp.GlobalCooldown, ref gcd, this);
bool isMeleeOrRangedSpell = m_spellInfo.DmgClass == SpellDmgClass.Melee || m_spellInfo.DmgClass == SpellDmgClass.Ranged ||
m_spellInfo.HasAttribute(SpellAttr0.ReqAmmo) || m_spellInfo.HasAttribute(SpellAttr0.Ability);
// Apply haste rating
if (gcd > 750 && ((m_spellInfo.StartRecoveryCategory == 133 && !isMeleeOrRangedSpell) || m_caster.HasAuraTypeWithAffectMask(AuraType.ModGlobalCooldownByHaste, m_spellInfo)))
gcd = Math.Min(Math.Max((int)(gcd * m_caster.GetFloatValue(UnitFields.ModCastHaste)), 750), 1500);
{
gcd = (int)(gcd * m_caster.GetFloatValue(UnitFields.ModCastHaste));
MathFunctions.RoundToInterval(ref gcd, 750, 1500);
}
if (gcd > 750 && m_caster.HasAuraTypeWithAffectMask(AuraType.ModGlobalCooldownByHasteRegen, m_spellInfo))
gcd = Math.Min(Math.Max((int)(gcd * m_caster.GetFloatValue(UnitFields.ModHasteRegen)), 750), 1500);
{
gcd = (int)(gcd * m_caster.GetFloatValue(UnitFields.ModHasteRegen));
MathFunctions.RoundToInterval(ref gcd, 750, 1500);
}
}
m_caster.GetSpellHistory().AddGlobalCooldown(m_spellInfo, (uint)gcd);
@@ -7246,7 +7246,7 @@ namespace Game.Spells
// ******************************************
ProcFlags m_procAttacker; // Attacker trigger flags
ProcFlags m_procVictim; // Victim trigger flags
ProcFlagsExLegacy m_procEx;
ProcFlagsHit m_hitMask;
// *****************************************
// Spell target subsystem
@@ -7442,6 +7442,7 @@ namespace Game.Spells
public bool scaleAura;
public int damage;
}
public class GOTargetInfo
{
public ObjectGuid targetGUID;
@@ -7480,11 +7481,10 @@ namespace Game.Spells
// Spell modifier (used for modify other spells)
public class SpellModifier
{
public SpellModifier(Aura _ownerAura = null)
public SpellModifier(Aura _ownerAura)
{
op = SpellModOp.Damage;
type = SpellModType.Flat;
charges = 0;
value = 0;
mask = new FlagArray128();
spellId = 0;
@@ -7493,7 +7493,6 @@ namespace Game.Spells
public SpellModOp op { get; set; }
public SpellModType type { get; set; }
public short charges { get; set; }
public int value { get; set; }
public FlagArray128 mask { get; set; }
public uint spellId { get; set; }
@@ -7763,4 +7762,32 @@ namespace Game.Spells
Spell m_Spell;
}
class ProcReflectDelayed : BasicEvent
{
public ProcReflectDelayed(Unit owner, ObjectGuid casterGuid)
{
_victim = owner;
_casterGuid = casterGuid;
}
public override bool Execute(ulong e_time, uint p_time)
{
Unit caster = Global.ObjAccessor.GetUnit(_victim, _casterGuid);
if (!caster)
return true;
ProcFlags typeMaskActor = ProcFlags.None;
ProcFlags typeMaskActionTarget = ProcFlags.TakenSpellMagicDmgClassNeg | ProcFlags.TakenSpellNoneDmgClassNeg;
ProcFlagsSpellType spellTypeMask = ProcFlagsSpellType.Damage | ProcFlagsSpellType.NoDmgHeal;
ProcFlagsSpellPhase spellPhaseMask = ProcFlagsSpellPhase.None;
ProcFlagsHit hitMask = ProcFlagsHit.Reflect;
caster.ProcSkillsAndAuras(_victim, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, null, null, null);
return true;
}
Unit _victim;
ObjectGuid _casterGuid;
}
}
+3 -2
View File
@@ -1089,7 +1089,8 @@ namespace Game.Spells
healthGain = m_caster.SpellHealingBonusDone(m_caster, m_spellInfo, healthGain, DamageEffectType.Heal, effectInfo);
healthGain = m_caster.SpellHealingBonusTaken(m_caster, m_spellInfo, healthGain, DamageEffectType.Heal, effectInfo);
m_caster.HealBySpell(m_caster, m_spellInfo, healthGain);
HealInfo healInfo = new HealInfo(m_caster, m_caster, healthGain, m_spellInfo, m_spellSchoolMask);
m_caster.HealBySpell(healInfo);
}
}
@@ -2877,7 +2878,7 @@ namespace Game.Spells
[SpellEffectHandler(SpellEffectName.InterruptCast)]
void EffectInterruptCast(uint effIndex)
{
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
if (effectHandleMode != SpellEffectHandleMode.LaunchTarget)
return;
if (unitTarget == null || !unitTarget.IsAlive())
+179 -255
View File
@@ -445,142 +445,6 @@ namespace Game.Entities
return SpellGroupStackRule.Default;
}
public SpellProcEventEntry GetSpellProcEvent(uint spellId)
{
return mSpellProcEventMap.LookupByKey(spellId);
}
public bool IsSpellProcEventCanTriggeredBy(SpellInfo spellProto, SpellProcEventEntry spellProcEvent, ProcFlags EventProcFlag, SpellInfo procSpell, ProcFlags procFlags, ProcFlagsExLegacy procExtra, bool active)
{
// No extra req need
ProcFlagsExLegacy procEvent_procEx = ProcFlagsExLegacy.None;
// check prockFlags for condition
if (!procFlags.HasAnyFlag(EventProcFlag))
return false;
bool hasFamilyMask = false;
// Quick Check - If PROC_FLAG_TAKEN_DAMAGE is set for aura and procSpell dealt damage, proc no matter what kind of spell that deals the damage.
if (procFlags.HasAnyFlag(ProcFlags.TakenDamage) && EventProcFlag.HasAnyFlag(ProcFlags.TakenDamage))
return true;
if (procFlags.HasAnyFlag(ProcFlags.DonePeriodic) && EventProcFlag.HasAnyFlag(ProcFlags.DonePeriodic))
{
if (procExtra.HasAnyFlag(ProcFlagsExLegacy.InternalHot))
{
if (EventProcFlag == ProcFlags.DonePeriodic)
{
// no aura with only PROC_FLAG_DONE_PERIODIC and spellFamilyName == 0 can proc from a HOT.
if (spellProto.SpellFamilyName == 0)
return false;
}
// Aura must have positive procflags for a HOT to proc
else if (!EventProcFlag.HasAnyFlag(ProcFlags.DoneSpellMagicDmgClassPos | ProcFlags.DoneSpellNoneDmgClassPos))
return false;
}
// Aura must have negative or neutral(PROC_FLAG_DONE_PERIODIC only) procflags for a DOT to proc
else if (EventProcFlag != ProcFlags.DonePeriodic)
if (!EventProcFlag.HasAnyFlag(ProcFlags.DoneSpellMagicDmgClassNeg | ProcFlags.DoneSpellNoneDmgClassNeg))
return false;
}
if (procFlags.HasAnyFlag(ProcFlags.TakenPeriodic) && EventProcFlag.HasAnyFlag(ProcFlags.TakenPeriodic))
{
if (procExtra.HasAnyFlag(ProcFlagsExLegacy.InternalHot))
{
// No aura that only has PROC_FLAG_TAKEN_PERIODIC can proc from a HOT.
if (EventProcFlag == ProcFlags.TakenPeriodic)
return false;
// Aura must have positive procflags for a HOT to proc
if (!EventProcFlag.HasAnyFlag(ProcFlags.TakenSpellMagicDmgClassPos | ProcFlags.TakenSpellNoneDmgClassPos))
return false;
}
// Aura must have negative or neutral(PROC_FLAG_TAKEN_PERIODIC only) procflags for a DOT to proc
else if (EventProcFlag != ProcFlags.TakenPeriodic)
if (!EventProcFlag.HasAnyFlag(ProcFlags.TakenSpellMagicDmgClassNeg | ProcFlags.TakenSpellNoneDmgClassNeg))
return false;
}
// Trap casts are active by default
if (procFlags.HasAnyFlag(ProcFlags.DoneTrapActivation))
active = true;
// Always trigger for this
if (procFlags.HasAnyFlag(ProcFlags.Killed | ProcFlags.Kill | ProcFlags.Death))
return true;
if (spellProcEvent != null) // Exist event data
{
// Store extra req
procEvent_procEx = (ProcFlagsExLegacy)spellProcEvent.procEx;
// For melee triggers
if (procSpell == null)
{
// Check (if set) for school (melee attack have Normal school)
if (spellProcEvent.schoolMask != 0 && (spellProcEvent.schoolMask & SpellSchoolMask.Normal) == 0)
return false;
}
else // For spells need check school/spell family/family mask
{
// Check (if set) for school
if (spellProcEvent.schoolMask != 0 && (spellProcEvent.schoolMask & procSpell.SchoolMask) == 0)
return false;
// Check (if set) for spellFamilyName
if (spellProcEvent.spellFamilyName != 0 && (spellProcEvent.spellFamilyName != procSpell.SpellFamilyName))
return false;
// spellFamilyName is Ok need check for spellFamilyMask if present
if (spellProcEvent.spellFamilyMask != null)
{
if (!(spellProcEvent.spellFamilyMask & procSpell.SpellFamilyFlags))
return false;
hasFamilyMask = true;
// Some spells are not considered as active even with have spellfamilyflags
if (!procEvent_procEx.HasAnyFlag(ProcFlagsExLegacy.OnlyActiveSpell))
active = true;
}
}
}
if (procExtra.HasAnyFlag(ProcFlagsExLegacy.InternalReqFamily))
{
if (!hasFamilyMask)
return false;
}
// Check for extra req (if none) and hit/crit
if (procEvent_procEx == ProcFlagsExLegacy.None)
{
// No extra req, so can trigger only for hit/crit - spell has to be active
if (procExtra.HasAnyFlag(ProcFlagsExLegacy.NormalHit | ProcFlagsExLegacy.CriticalHit) && active)
return true;
}
else // Passive spells hits here only if resist/reflect/immune/evade
{
if (procExtra.HasAnyFlag(ProcFlagsExLegacy.AuraProcMask))
{
// if spell marked as procing only from not active spells
if (active && procEvent_procEx.HasAnyFlag(ProcFlagsExLegacy.NotActiveSpell))
return false;
// if spell marked as procing only from active spells
if (!active && procEvent_procEx.HasAnyFlag(ProcFlagsExLegacy.OnlyActiveSpell))
return false;
// Exist req for PROC_EX_EX_TRIGGER_ALWAYS
if (procEvent_procEx.HasAnyFlag(ProcFlagsExLegacy.ExTriggerAlways))
return true;
// PROC_EX_NOT_ACTIVE_SPELL and PROC_EX_ONLY_ACTIVE_SPELL flags handle: if passed checks before
if ((procExtra.HasAnyFlag(ProcFlagsExLegacy.NormalHit | ProcFlagsExLegacy.CriticalHit) && (procEvent_procEx & ProcFlagsExLegacy.AuraProcMask) == 0))
return true;
}
// Check Extra Requirement like (hit/crit/miss/resist/parry/dodge/block/immune/reflect/absorb and other)
if (procEvent_procEx.HasAnyFlag(procExtra))
return true;
}
return false;
}
public SpellProcEntry GetSpellProcEntry(uint spellId)
{
return mSpellProcMap.LookupByKey(spellId);
@@ -593,7 +457,7 @@ namespace Game.Entities
return false;
// check XP or honor target requirement
if ((procEntry.AttributesMask & 0x0000010) != 0)
if (((uint)procEntry.AttributesMask & 0x0000010) != 0)
{
Player actor = eventInfo.GetActor().ToPlayer();
if (actor)
@@ -601,10 +465,38 @@ namespace Game.Entities
return false;
}
// check power requirement
if (procEntry.AttributesMask.HasAnyFlag(ProcAttributes.ReqPowerCost))
{
if (!eventInfo.GetProcSpell())
return false;
var costs = eventInfo.GetProcSpell().GetPowerCost();
var m = costs.Find(cost => { return cost.Amount > 0; });
if (m == null)
return false;
}
// always trigger for these types
if ((eventInfo.GetTypeMask() & (ProcFlags.Killed | ProcFlags.Kill | ProcFlags.Death)) != 0)
return true;
// do triggered cast checks
if (!procEntry.AttributesMask.HasAnyFlag(ProcAttributes.TriggeredCanProc))
{
Spell spell = eventInfo.GetProcSpell();
if (spell)
{
if (spell.IsTriggered())
{
SpellInfo spellInfo = spell.GetSpellInfo();
if (!spellInfo.HasAttribute(SpellAttr3.TriggeredCanTriggerProc2) &&
!spellInfo.HasAttribute(SpellAttr2.TriggeredCanTriggerProc))
return false;
}
}
}
// check school mask (if set) for other trigger types
if (procEntry.SchoolMask != 0 && !Convert.ToBoolean(eventInfo.GetSchoolMask() & procEntry.SchoolMask))
return false;
@@ -1631,87 +1523,6 @@ namespace Game.Entities
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} spell group stack rules in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public void LoadSpellProcEvents()
{
uint oldMSTime = Time.GetMSTime();
mSpellProcEventMap.Clear(); // need for reload case
// 0 1 2 3 4 5 6 7 8 9 10 11
SQLResult result = DB.World.Query("SELECT entry, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, SpellFamilyMask3, procFlags, procEx, ppmRate, CustomChance, Cooldown FROM spell_proc_event");
if (result.IsEmpty())
{
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 spell proc event conditions. DB table `spell_proc_event` is empty.");
return;
}
uint count = 0;
do
{
int spellId = result.Read<int>(0);
bool allRanks = false;
if (spellId < 0)
{
allRanks = true;
spellId = -spellId;
}
SpellInfo spellInfo = GetSpellInfo((uint)spellId);
if (spellInfo == null)
{
Log.outError(LogFilter.Sql, "Spell {0} listed in `spell_proc_event` does not exist", spellId);
continue;
}
if (allRanks)
{
if (!spellInfo.IsRanked())
Log.outError(LogFilter.Sql, "Spell {0} listed in `spell_proc_event` with all ranks, but spell has no ranks.", spellId);
if (spellInfo.GetFirstRankSpell().Id != spellId)
{
Log.outError(LogFilter.Sql, "Spell {0} listed in `spell_proc_event` is not first rank of spell.", spellId);
continue;
}
}
SpellProcEventEntry spellProcEvent = new SpellProcEventEntry();
spellProcEvent.schoolMask = (SpellSchoolMask)result.Read<uint>(1);
spellProcEvent.spellFamilyName = (SpellFamilyNames)result.Read<uint>(2);
spellProcEvent.spellFamilyMask = new FlagArray128(result.Read<uint>(3), result.Read<uint>(4), result.Read<uint>(5), result.Read<uint>(6));
spellProcEvent.procFlags = result.Read<uint>(7);
spellProcEvent.procEx = result.Read<uint>(8);
spellProcEvent.ppmRate = result.Read<float>(9);
spellProcEvent.customChance = result.Read<float>(10);
spellProcEvent.cooldown = result.Read<uint>(11);
while (spellInfo != null)
{
if (mSpellProcEventMap.ContainsKey(spellInfo.Id))
{
Log.outError(LogFilter.Sql, "Spell {0} listed in `spell_proc_event` already has its first rank in table.", spellInfo.Id);
break;
}
if (spellInfo.ProcFlags == 0 && spellProcEvent.procFlags == 0)
Log.outError(LogFilter.Sql, "Spell {0} listed in `spell_proc_event` probably not triggered spell", spellInfo.Id);
mSpellProcEventMap[spellInfo.Id] = spellProcEvent;
if (allRanks)
spellInfo = spellInfo.GetNextRankSpell();
else
break;
}
++count;
} while (result.NextRow());
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} extra spell proc event conditions in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public void LoadSpellProcs()
{
uint oldMSTime = Time.GetMSTime();
@@ -1722,13 +1533,10 @@ namespace Game.Entities
SQLResult result = DB.World.Query("SELECT SpellId, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, SpellFamilyMask3, " +
// 7 8 9 10 11 12 13 14 15
"ProcFlags, SpellTypeMask, SpellPhaseMask, HitMask, AttributesMask, ProcsPerMinute, Chance, Cooldown, Charges FROM spell_proc");
if (result.IsEmpty())
{
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 spell proc conditions and data. DB table `spell_proc` is empty.");
return;
}
uint count = 0;
if (!result.IsEmpty())
{
do
{
int spellId = result.Read<int>(0);
@@ -1765,7 +1573,7 @@ namespace Game.Entities
baseProcEntry.SpellTypeMask = (ProcFlagsSpellType)result.Read<uint>(8);
baseProcEntry.SpellPhaseMask = (ProcFlagsSpellPhase)result.Read<uint>(9);
baseProcEntry.HitMask = (ProcFlagsHit)result.Read<uint>(10);
baseProcEntry.AttributesMask = result.Read<uint>(11);
baseProcEntry.AttributesMask = (ProcAttributes)result.Read<uint>(11);
baseProcEntry.ProcsPerMinute = result.Read<float>(12);
baseProcEntry.Chance = result.Read<float>(13);
baseProcEntry.Cooldown = result.Read<uint>(14);
@@ -1834,6 +1642,87 @@ namespace Game.Entities
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} spell proc conditions and data in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
else
Log.outInfo(LogFilter.ServerLoading, ">> Loaded 0 spell proc conditions and data. DB table `spell_proc` is empty.");
// This generates default procs to retain compatibility with previous proc system
Log.outInfo(LogFilter.ServerLoading, "Generating spell proc data from SpellMap...");
count = 0;
oldMSTime = Time.GetMSTime();
foreach (SpellInfo spellInfo in mSpellInfoMap.Values)
{
if (spellInfo == null)
continue;
if (mSpellProcMap.ContainsKey(spellInfo.Id))
continue;
bool found = false, addTriggerFlag = false;
foreach (SpellEffectInfo effect in spellInfo.GetEffectsForDifficulty(Difficulty.None))
{
if (effect == null || !effect.IsEffect())
continue;
AuraType auraName = effect.ApplyAuraName;
if (auraName == 0)
continue;
if (!isTriggerAura(auraName))
continue;
found = true;
if (!addTriggerFlag && isAlwaysTriggeredAura(auraName))
addTriggerFlag = true;
break;
}
if (!found)
continue;
if (spellInfo.ProcFlags == 0)
continue;
SpellProcEntry procEntry = new SpellProcEntry();
procEntry.SchoolMask = 0;
procEntry.SpellFamilyName = spellInfo.SpellFamilyName;
procEntry.ProcFlags = spellInfo.ProcFlags;
foreach (SpellEffectInfo effect in spellInfo.GetEffectsForDifficulty(Difficulty.None))
if (effect != null && effect.IsEffect() && isTriggerAura(effect.ApplyAuraName))
procEntry.SpellFamilyMask |= effect.SpellClassMask;
procEntry.SpellTypeMask = ProcFlagsSpellType.MaskAll;
procEntry.SpellPhaseMask = ProcFlagsSpellPhase.Hit;
procEntry.HitMask = ProcFlagsHit.None; // uses default proc @see SpellMgr::CanSpellTriggerProcOnEvent
// Reflect auras should only proc off reflects
foreach (SpellEffectInfo effect in spellInfo.GetEffectsForDifficulty(Difficulty.None))
{
if (effect != null && (effect.IsAura(AuraType.ReflectSpells) || effect.IsAura(AuraType.ReflectSpellsSchool)))
{
procEntry.HitMask = ProcFlagsHit.Reflect;
break;
}
}
procEntry.AttributesMask = 0;
if (spellInfo.ProcFlags.HasAnyFlag(ProcFlags.Kill))
procEntry.AttributesMask |= ProcAttributes.ReqExpOrHonor;
if (addTriggerFlag)
procEntry.AttributesMask |= ProcAttributes.TriggeredCanProc;
procEntry.ProcsPerMinute = 0;
procEntry.Chance = spellInfo.ProcChance;
procEntry.Cooldown = 0;
procEntry.Charges = spellInfo.ProcCharges;
mSpellProcMap[spellInfo.Id] = procEntry;
++count;
}
Log.outInfo(LogFilter.ServerLoading, "Generated spell proc data for {0} spells in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public void LoadSpellThreats()
{
@@ -2004,7 +1893,7 @@ namespace Game.Entities
SpellEnchantProcEntry spe = new SpellEnchantProcEntry();
spe.customChance = result.Read<uint>(1);
spe.PPMChance = result.Read<float>(2);
spe.procEx = (ProcFlagsExLegacy)result.Read<uint>(3);
spe.procEx = result.Read<uint>(3);
mSpellEnchantProcEventMap[enchantId] = spe;
@@ -2153,6 +2042,7 @@ namespace Game.Entities
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} summonable creature templates in {1} ms", countCreature, Time.GetMSTimeDiffToNow(oldMSTime));
}
bool LoadPetDefaultSpells_helper(CreatureTemplate cInfo, PetDefaultSpellsEntry petDefSpells)
{
// skip empty list;
@@ -2812,20 +2702,9 @@ namespace Game.Entities
spellInfo.MaxAffectedTargets = 1;
spellInfo.GetEffect(0).TriggerSpell = 33760;
break;
case 17941: // Shadow Trance
case 22008: // Netherwind Focus
case 34477: // Misdirection
case 48108: // Hot Streak
case 51124: // Killing Machine
case 64823: // Item - Druid T8 Balance 4P Bonus
spellInfo.ProcCharges = 1;
break;
case 44544: // Fingers of Frost
spellInfo.GetEffect(0).SpellClassMask = new FlagArray128(685904631, 1151048, 0, 0);
break;
case 28200: // Ascendance (Talisman of Ascendance trinket)
spellInfo.ProcCharges = 6;
break;
case 37408: // Oscillation Field
spellInfo.AttributesEx3 |= SpellAttr3.StackForDiffCasters;
break;
@@ -3172,10 +3051,6 @@ namespace Game.Entities
// Little hack, Increase the radius so it can hit the Cave In Stalkers in the platform.
spellInfo.GetEffect(0).MaxRadiusEntry = CliDB.SpellRadiusStorage.LookupByKey(EffectRadiusIndex.Yards45);
break;
case 75323: // Reverberating Hymn
// Aura is refreshed at 3 seconds, and the tick should happen at the fourth.
spellInfo.AttributesEx8 |= SpellAttr8.DontResetPeriodicTimer;
break;
case 24314: // Threatening Gaze
spellInfo.AuraInterruptFlags |= SpellAuraInterruptFlags.Cast | SpellAuraInterruptFlags.Move | SpellAuraInterruptFlags.Jump;
break;
@@ -3254,6 +3129,68 @@ namespace Game.Entities
}
#endregion
public bool isTriggerAura(AuraType type)
{
switch (type)
{
case AuraType.Dummy:
case AuraType.ModConfuse:
case AuraType.ModThreat:
case AuraType.ModStun:
case AuraType.ModDamageDone:
case AuraType.ModDamageTaken:
case AuraType.ModResistance:
case AuraType.ModStealth:
case AuraType.ModFear:
case AuraType.ModRoot:
case AuraType.Transform:
case AuraType.ReflectSpells:
case AuraType.DamageImmunity:
case AuraType.ProcTriggerSpell:
case AuraType.ProcTriggerDamage:
case AuraType.ModCastingSpeedNotStack:
case AuraType.SchoolAbsorb:
case AuraType.ModPowerCostSchoolPct:
case AuraType.ModPowerCostSchool:
case AuraType.ReflectSpellsSchool:
case AuraType.MechanicImmunity:
case AuraType.ModDamagePercentTaken:
case AuraType.SpellMagnet:
case AuraType.ModAttackPower:
case AuraType.ModPowerRegenPercent:
case AuraType.AddCasterHitTrigger:
case AuraType.OverrideClassScripts:
case AuraType.ModMechanicResistance:
case AuraType.MeleeAttackPowerAttackerBonus:
case AuraType.ModMeleeHaste:
case AuraType.ModMeleeHaste3:
case AuraType.ModAttackerMeleeHitChance:
case AuraType.ProcTriggerSpellWithValue:
case AuraType.ModSpellDamageFromCaster:
case AuraType.AbilityIgnoreAurastate:
case AuraType.ModRoot2:
return true;
}
return false;
}
public bool isAlwaysTriggeredAura(AuraType type)
{
switch (type)
{
case AuraType.OverrideClassScripts:
case AuraType.ModFear:
case AuraType.ModRoot:
case AuraType.ModStun:
case AuraType.Transform:
case AuraType.SpellMagnet:
case AuraType.SchoolAbsorb:
case AuraType.ModStealth:
case AuraType.ModRoot2:
return true;
}
return false;
}
// SpellInfo object management
public SpellInfo GetSpellInfo(uint spellId)
{
@@ -3368,7 +3305,6 @@ namespace Game.Entities
MultiMap<uint, SpellGroup> mSpellSpellGroup = new MultiMap<uint, SpellGroup>();
MultiMap<SpellGroup, int> mSpellGroupSpell = new MultiMap<SpellGroup, int>();
Dictionary<SpellGroup, SpellGroupStackRule> mSpellGroupStack = new Dictionary<SpellGroup, SpellGroupStackRule>();
Dictionary<uint, SpellProcEventEntry> mSpellProcEventMap = new Dictionary<uint, SpellProcEventEntry>();
Dictionary<uint, SpellProcEntry> mSpellProcMap = new Dictionary<uint, SpellProcEntry>();
Dictionary<uint, SpellThreatEntry> mSpellThreatMap = new Dictionary<uint, SpellThreatEntry>();
Dictionary<uint, PetAura> mSpellPetAuraMap = new Dictionary<uint, PetAura>();
@@ -3454,25 +3390,13 @@ namespace Game.Entities
public ProcFlagsSpellType SpellTypeMask { get; set; } // if nonzero - bitmask for matching proc condition based on candidate spell's damage/heal effects, see enum ProcFlagsSpellType
public ProcFlagsSpellPhase SpellPhaseMask { get; set; } // if nonzero - bitmask for matching phase of a spellcast on which proc occurs, see enum ProcFlagsSpellPhase
public ProcFlagsHit HitMask { get; set; } // if nonzero - bitmask for matching proc condition based on hit result, see enum ProcFlagsHit
public uint AttributesMask { get; set; } // bitmask, see ProcAttributes
public ProcAttributes AttributesMask { get; set; } // bitmask, see ProcAttributes
public float ProcsPerMinute { get; set; } // if nonzero - chance to proc is equal to value * aura caster's weapon speed / 60
public float Chance { get; set; } // if nonzero - owerwrite procChance field for given Spell.dbc entry, defines chance of proc to occur, not used if ProcsPerMinute set
public uint Cooldown { get; set; } // if nonzero - cooldown in secs for aura proc, applied to aura
public uint Charges { get; set; } // if nonzero - owerwrite procCharges field for given Spell.dbc entry, defines how many times proc can occur before aura remove, 0 - infinite
}
public class SpellProcEventEntry
{
public SpellSchoolMask schoolMask; // if nonzero - bit mask for matching proc condition based on spell candidate's school: Fire=2, Mask=1<<(2-1)=2
public SpellFamilyNames spellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyNamer value
public FlagArray128 spellFamilyMask; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyFlags (like auras 107 and 108 do)
public uint procFlags; // bitmask for matching proc event
public uint procEx; // proc Extend info (see ProcFlagsEx)
public float ppmRate; // for melee (ranged?) damage spells - proc rate per minute. if zero, falls back to flat chance from Spell.dbc
public float customChance; // Owerride chance (in most cases for debug only)
public uint cooldown; // hidden cooldown used for some spell proc events, applied to _triggered_spell_
}
public class PetDefaultSpellsEntry
{
public uint[] spellid = new uint[4];
@@ -3637,7 +3561,7 @@ namespace Game.Entities
{
public uint customChance;
public float PPMChance;
public ProcFlagsExLegacy procEx;
public uint procEx;
}
public class SpellTargetPosition
+3 -3
View File
@@ -643,12 +643,12 @@ namespace Scripts.Spells.Druid
void HandleProc(AuraEffect aurEff, ProcEventInfo eventInfo)
{
PreventDefaultAction();
SpellInfo spellInfo = eventInfo.GetSpellInfo();
if (spellInfo == null)
Spell spell = eventInfo.GetProcSpell();
if (spell == null)
return;
Unit caster = eventInfo.GetActor();
var costs = spellInfo.CalcPowerCost(caster, spellInfo.GetSchoolMask());
var costs = spell.GetPowerCost();
var m = costs.First(cost => { return cost.Power == PowerType.Mana; });
if (m == null)
return;
+28 -7
View File
@@ -2536,30 +2536,51 @@ namespace Scripts.Spells.Generic
}
}
[Script]
[Script("spell_pvp_trinket_shared_cd", SpellIds.WillOfTheForsakenCooldownTrigger)]
[Script("spell_wotf_shared_cd", SpellIds.WillOfTheForsakenCooldownTriggerWotf)]
class spell_pvp_trinket_wotf_shared_cd : SpellScript
{
public override bool Load()
public spell_pvp_trinket_wotf_shared_cd(uint triggered)
{
return GetCaster().IsTypeId(TypeId.Player);
_triggered = triggered;
}
public override bool Validate(SpellInfo spellInfo)
{
return ValidateSpellInfo(SpellIds.WillOfTheForsakenCooldownTrigger, SpellIds.WillOfTheForsakenCooldownTriggerWotf);
return ValidateSpellInfo(_triggered);
}
void HandleScript()
{
// This is only needed because spells cast from spell_linked_spell are triggered by default
// Spell.SendSpellCooldown() skips all spells with TRIGGERED_IGNORE_SPELL_AND_CATEGORY_CD
GetCaster().GetSpellHistory().StartCooldown(GetSpellInfo(), 0, GetSpell());
/*
* @workaround: PendingCast flag normally means 'triggered' spell, however
* if the spell is cast triggered, the core won't send SMSG_SPELL_GO packet
* so client never registers the cooldown (see Spell::IsNeedSendToClient)
*
* ServerToClient: SMSG_SPELL_GO (0x0132) Length: 42 ConnIdx: 0 Time: 07/19/2010 02:32:35.000 Number: 362675
* Caster GUID: Full: Player
* Caster Unit GUID: Full: Player
* Cast Count: 0
* Spell ID: 72752 (72752)
* Cast Flags: PendingCast, Unknown3, Unknown7 (265)
* Time: 3901468825
* Hit Count: 1
* [0] Hit GUID: Player
* Miss Count: 0
* Target Flags: Unit (2)
* Target GUID: 0x0
*/
// Spell flags need further research, until then just cast not triggered
GetCaster().CastSpell((Unit)null, _triggered, false);
}
public override void Register()
{
AfterCast.Add(new CastHandler(HandleScript));
}
uint _triggered;
}
[Script]
+3 -3
View File
@@ -1444,10 +1444,10 @@ namespace Scripts.Spells.Items
{
bool CheckProc(ProcEventInfo eventInfo)
{
SpellInfo spellInfo = eventInfo.GetSpellInfo();
if (spellInfo != null)
Spell spell = eventInfo.GetProcSpell();
if (spell != null)
{
var costs = spellInfo.CalcPowerCost(GetTarget(), spellInfo.GetSchoolMask());
var costs = spell.GetPowerCost();
var m = costs.FirstOrDefault(cost => { return cost.Power == PowerType.Mana && cost.Amount > 0; });
if (m != null)
return true;
+2 -2
View File
@@ -417,14 +417,14 @@ namespace Scripts.Spells.Mage
bool CheckProc(ProcEventInfo eventInfo)
{
return eventInfo.GetDamageInfo().GetSpellInfo() != null; // eventInfo.GetSpellInfo()
return eventInfo.GetProcSpell() != null;
}
void HandleProc(AuraEffect aurEff, ProcEventInfo eventInfo)
{
PreventDefaultAction();
var costs = eventInfo.GetDamageInfo().GetSpellInfo().CalcPowerCost(GetTarget(), eventInfo.GetDamageInfo().GetSchoolMask());
var costs = eventInfo.GetProcSpell().GetPowerCost();
var m = costs.Find(cost => cost.Power == PowerType.Mana);
if (m != null)
{
+2 -5
View File
@@ -442,17 +442,14 @@ namespace Scripts.Spells.Shaman
bool CheckProc(ProcEventInfo eventInfo)
{
return eventInfo.GetSpellInfo() != null;
return eventInfo.GetProcSpell() != null;
}
void HandleProc(AuraEffect aurEff, ProcEventInfo eventInfo)
{
PreventDefaultAction();
SpellInfo spellInfo = eventInfo.GetSpellInfo();
if (spellInfo == null)
return;
var costs = eventInfo.GetDamageInfo().GetSpellInfo().CalcPowerCost(GetTarget(), eventInfo.GetDamageInfo().GetSchoolMask());
var costs = eventInfo.GetProcSpell().GetPowerCost();
var m = costs.Find(cost => cost.Power == PowerType.Mana);
if (m != null)
{