Implementation of QAston proc system
This commit is contained in:
@@ -604,7 +604,7 @@ namespace Framework.Constants
|
||||
CommandReloadSpellLootTemplate = 695,
|
||||
CommandReloadSpellLinkedSpell = 696,
|
||||
CommandReloadSpellPetAuras = 697,
|
||||
CommandReloadSpellProcEvent = 698,
|
||||
// 698 - reuse
|
||||
CommandReloadSpellProc = 699,
|
||||
CommandReloadSpellScripts = 700,
|
||||
CommandReloadSpellTargetPosition = 701,
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace Framework.Constants
|
||||
AfterDispel,
|
||||
// Spell Proc Hooks
|
||||
CheckProc,
|
||||
CheckEffectProc,
|
||||
PrepareProc,
|
||||
Proc,
|
||||
EffectProc,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
+137
-794
File diff suppressed because it is too large
Load Diff
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user