diff options
author | ariel- <ariel-@users.noreply.github.com> | 2016-09-25 20:47:28 -0300 |
---|---|---|
committer | ariel- <ariel-@users.noreply.github.com> | 2016-10-04 20:21:55 -0300 |
commit | e641d0c7d71bdb21e443bf52bf508b1581e07839 (patch) | |
tree | 764e1afadfa69a8c1d1a9c709498c017d12409b4 | |
parent | 23902ff266c28fa83f3bab5646b3e5b93ab55380 (diff) |
Core/Spells: Implementation of QAston proc system
- Move checks from Unit::IsTriggeredAtSpellProcEvent (old system) to Aura::IsProcTriggeredOnEvent (new system)
- Templatize SpellModOp param of Player::ApplySpellMod, also killed charge counter from SpellModifier and Player system for handling charges... no point in having 3 different systems doing the same thing
- Automatically add default entries to spellProcMap, based on spellinfo (else auras won't proc without an entry) Based on old Unit::ProcDamageAndSpellFor
- Old Unit::ProcDamageAndSpellFor renamed to Unit::ProcSkillsAndReactives and made private, will no longer handle auras.
- Start making use of HealInfo::AbsorbHeal in unit calculations, add effective healing info to HealInfo struct
- Changes in spell reflection system, emulates old behaviour, delaying aura drop
- Removed old charge count hacks in SpellMgr::LoadSpellInfoCorrections
- Removed bogus error log when procChance is 0: Some auras have initial 0 procChance but modified by SPELLMOD_CHANCE_OF_SUCCESS
- Fixed TriggerAurasProcOnEvent logic that tried to trigger twice from actor.
- Allow non damaging spells with DamageClass Melee or Ranged to proc character enchants. Ref issue #17034:
* http://web.archive.org/web/20110309092008/http://elitistjerks.com/f47/t49865-paladin_retribution_pve/
* When an auto-attack lands (does not dodge/parry/miss) that can proc a seal the of the following things happen independently of each other (see 2 roll system).
* 1) A "hidden strike" which uses melee combat mechanics occurs. If it lands it refreshes/stacks SoV DoT. Only white swings can trigger a refresh or stack. (This hidden strike mechanic can also proc things like berserking..)
* 2) A weapon damage based proc will occur if you used a special (CS/DS/judge) or if you have a 5 stack (from auto attacks). This attack can not be avoided.
* Holy Vengeance is the "hidden strike" it has an apply aura effect and damage class melee.
- Fixed Blood Tap interaction with Death Runes (btw, don't know what was going on with those MiscValueB, spell 45529 doesn't have any MiscValueB in SPELL_EFFECT_ACTIVATE_RUNE)
- Ported some AuraEffect checks from old Unit.cpp function. added new AuraScript hook to check procs of an specific effect
- Allow only AuraEffects that passed the check to proc, this won't block whole aura from proccing (and lose charges) if at least one of the effects procs, though
- Changes in spell mod system (for SPELLMOD_CASTING_TIME). fixes #17558.
- Added an exception for SPELLMOD_CRITICAL_CHANCE too, fixes #15193
24 files changed, 1204 insertions, 4718 deletions
diff --git a/sql/base/auth_database.sql b/sql/base/auth_database.sql index 05d2acbe0e8..9d659382ed4 100644 --- a/sql/base/auth_database.sql +++ b/sql/base/auth_database.sql @@ -486,7 +486,7 @@ CREATE TABLE `updates` ( LOCK TABLES `updates` WRITE; /*!40000 ALTER TABLE `updates` DISABLE KEYS */; -INSERT INTO `updates` VALUES ('2014_11_10_00_auth.sql','0E3CB119442D09DD88E967015319BBC8DAFBBFE0','ARCHIVED','2015-03-21 21:44:12',0),('2014_11_10_01_auth.sql','327E77A1DA3546D5275AB249915DD57EDD6FDD3D','ARCHIVED','2015-03-21 21:44:12',0),('2014_12_10_00_auth.sql','821703A96D80F9080074852B5A46E2909C9562EA','ARCHIVED','2015-03-21 21:44:12',0),('2014_12_21_00_auth.sql','CE2E5D2CD82E79C25294539ADED27A1429105B43','ARCHIVED','2015-03-21 21:44:12',0),('2015_03_20_00_auth.sql','E8C5B74BB45F0F35DEC182C72BACF435C7066FB0','ARCHIVED','2015-03-21 21:44:12',0),('2015_03_20_01_auth.sql','862961815354DA2746F5F71FBC8155F57CBE75AB','ARCHIVED','2015-03-21 21:44:12',0),('2015_03_20_02_auth.sql','33E4F94086590768EF5D4855DD43D7DE7C06ADA4','ARCHIVED','2015-03-21 21:44:51',0),('2015_08_21_00_auth.sql','C31A9E1D28E11B60BE8F8198637DD51F6D75123F','ARCHIVED','2015-10-05 23:16:19',0),('2015_11_07_00_auth.sql','0ACDD35EC9745231BCFA701B78056DEF94D0CC53','ARCHIVED','2016-04-11 00:42:36',92),('2016_01_13_00_auth.sql','24615CC69B3CD7BB4699874647C35BA86E8A93FD','ARCHIVED','2016-01-13 00:00:00',0),('2016_04_11_00_auth.sql','0ACDD35EC9745231BCFA701B78056DEF94D0CC53','RELEASED','2016-04-11 03:18:17',0),('2016_05_11_00_auth.sql','95B66235B8D67BF1CA216EB09F313C1F8F393B47','RELEASED','2016-04-16 13:17:11',0),('2016_06_06_00_auth.sql','57603BB68C340A8C919FB424452C282C47A193F7','RELEASED','2016-06-06 18:20',0); +INSERT INTO `updates` VALUES ('2014_11_10_00_auth.sql','0E3CB119442D09DD88E967015319BBC8DAFBBFE0','ARCHIVED','2015-03-21 21:44:12',0),('2014_11_10_01_auth.sql','327E77A1DA3546D5275AB249915DD57EDD6FDD3D','ARCHIVED','2015-03-21 21:44:12',0),('2014_12_10_00_auth.sql','821703A96D80F9080074852B5A46E2909C9562EA','ARCHIVED','2015-03-21 21:44:12',0),('2014_12_21_00_auth.sql','CE2E5D2CD82E79C25294539ADED27A1429105B43','ARCHIVED','2015-03-21 21:44:12',0),('2015_03_20_00_auth.sql','E8C5B74BB45F0F35DEC182C72BACF435C7066FB0','ARCHIVED','2015-03-21 21:44:12',0),('2015_03_20_01_auth.sql','862961815354DA2746F5F71FBC8155F57CBE75AB','ARCHIVED','2015-03-21 21:44:12',0),('2015_03_20_02_auth.sql','33E4F94086590768EF5D4855DD43D7DE7C06ADA4','ARCHIVED','2015-03-21 21:44:51',0),('2015_08_21_00_auth.sql','C31A9E1D28E11B60BE8F8198637DD51F6D75123F','ARCHIVED','2015-10-05 23:16:19',0),('2015_11_07_00_auth.sql','0ACDD35EC9745231BCFA701B78056DEF94D0CC53','ARCHIVED','2016-04-11 00:42:36',92),('2016_01_13_00_auth.sql','24615CC69B3CD7BB4699874647C35BA86E8A93FD','ARCHIVED','2016-01-13 00:00:00',0),('2016_04_11_00_auth.sql','0ACDD35EC9745231BCFA701B78056DEF94D0CC53','RELEASED','2016-04-11 03:18:17',0),('2016_05_11_00_auth.sql','95B66235B8D67BF1CA216EB09F313C1F8F393B47','RELEASED','2016-04-16 13:17:11',0),('2016_06_06_00_auth.sql','57603BB68C340A8C919FB424452C282C47A193F7','RELEASED','2016-06-06 18:20',0),('2016_09_22_00_auth.sql','70047954E3556BFA430ADD5680EF8797F74A4B9E','RELEASED','2016-09-22 00:00:00',0); /*!40000 ALTER TABLE `updates` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/updates/auth/3.3.5/2016_09_22_00_auth.sql b/sql/updates/auth/3.3.5/2016_09_22_00_auth.sql new file mode 100644 index 00000000000..324bd800ad2 --- /dev/null +++ b/sql/updates/auth/3.3.5/2016_09_22_00_auth.sql @@ -0,0 +1 @@ +DELETE FROM `rbac_permissions` WHERE `id` = 698; diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index 80fc0b237b1..907a650e602 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -602,7 +602,7 @@ enum RBACPermissions RBAC_PERM_COMMAND_RELOAD_SPELL_LOOT_TEMPLATE = 695, RBAC_PERM_COMMAND_RELOAD_SPELL_LINKED_SPELL = 696, RBAC_PERM_COMMAND_RELOAD_SPELL_PET_AURAS = 697, - RBAC_PERM_COMMAND_RELOAD_SPELL_PROC_EVENT = 698, + // 698 - reuse RBAC_PERM_COMMAND_RELOAD_SPELL_PROC = 699, RBAC_PERM_COMMAND_RELOAD_SPELL_SCRIPTS = 700, RBAC_PERM_COMMAND_RELOAD_SPELL_TARGET_POSITION = 701, diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp index 9767d69e2aa..e04e1485d64 100644 --- a/src/server/game/Combat/ThreatManager.cpp +++ b/src/server/game/Combat/ThreatManager.cpp @@ -44,7 +44,7 @@ float ThreatCalcHelper::calcThreat(Unit* hatedUnit, Unit* /*hatingUnit*/, float return threat; if (Player* modOwner = hatedUnit->GetSpellModOwner()) - modOwner->ApplySpellMod(threatSpell->Id, SPELLMOD_THREAT, threat); + modOwner->ApplySpellMod<SPELLMOD_THREAT>(threatSpell->Id, threat); } return hatedUnit->ApplyTotalThreatModifier(threat, schoolMask); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index cdd9da88d7b..a00bd9e4d47 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -7923,8 +7923,10 @@ void Player::UpdateEquipSpellsAtFormChange() } } } -void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx) + +void Player::CastItemCombatSpell(DamageInfo const& damageInfo) { + Unit* target = damageInfo.GetVictim(); if (!target || !target->IsAlive() || target == this) return; @@ -7932,7 +7934,9 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 { // If usable, try to cast item spell if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - if (!item->IsBroken() && CanUseAttackType(attType)) + { + if (!item->IsBroken() && CanUseAttackType(damageInfo.GetAttackType())) + { if (ItemTemplate const* proto = item->GetTemplate()) { // Additional check for weapons @@ -7940,30 +7944,42 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 { // offhand item cannot proc from main hand hit etc EquipmentSlots slot; - switch (attType) + switch (damageInfo.GetAttackType()) { - case BASE_ATTACK: slot = EQUIPMENT_SLOT_MAINHAND; break; - case OFF_ATTACK: slot = EQUIPMENT_SLOT_OFFHAND; break; - case RANGED_ATTACK: slot = EQUIPMENT_SLOT_RANGED; break; - default: slot = EQUIPMENT_SLOT_END; break; + case BASE_ATTACK: + slot = EQUIPMENT_SLOT_MAINHAND; + break; + case OFF_ATTACK: + slot = EQUIPMENT_SLOT_OFFHAND; + break; + case RANGED_ATTACK: + slot = EQUIPMENT_SLOT_RANGED; + break; + default: + slot = EQUIPMENT_SLOT_END; + break; } if (slot != i) continue; // Check if item is useable (forms or disarm) - if (attType == BASE_ATTACK) + if (damageInfo.GetAttackType() == BASE_ATTACK) if (!IsUseEquipedWeapon(true) && !IsInFeralForm()) continue; } - CastItemCombatSpell(target, attType, procVictim, procEx, item, proto); + + CastItemCombatSpell(damageInfo, item, proto); } + } + } } } -void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx, Item* item, ItemTemplate const* proto) +void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemTemplate const* proto) { // Can do effect if any damage done to target - if (procVictim & PROC_FLAG_TAKEN_DAMAGE) - //if (damageInfo->procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE) + // for done procs allow normal + critical + absorbs by default + bool canTrigger = (damageInfo.GetHitMask() & (PROC_HIT_NORMAL | PROC_HIT_CRITICAL | PROC_HIT_ABSORB)) != 0; + if (canTrigger) { for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) { @@ -7993,14 +8009,14 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 if (spellData.SpellPPMRate) { - uint32 WeaponSpeed = GetAttackTime(attType); + uint32 WeaponSpeed = GetAttackTime(damageInfo.GetAttackType()); chance = GetPPMProcChance(WeaponSpeed, spellData.SpellPPMRate, spellInfo); } else if (chance > 100.0f) chance = GetWeaponProcChance(); if (roll_chance_f(chance)) - CastSpell(target, spellInfo->Id, true, item); + CastSpell(damageInfo.GetVictim(), spellInfo->Id, true, item); } } @@ -8018,18 +8034,17 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 continue; SpellEnchantProcEntry const* entry = sSpellMgr->GetSpellEnchantProcEvent(enchant_id); - if (entry && entry->procEx) { // Check hit/crit/dodge/parry requirement - if ((entry->procEx & procEx) == 0) + if ((entry->procEx & damageInfo.GetHitMask()) == 0) continue; } else { // Can do effect if any damage done to target - if (!(procVictim & PROC_FLAG_TAKEN_DAMAGE)) - //if (!(damageInfo->procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE)) + // for done procs allow normal + critical + absorbs by default + if (!canTrigger) continue; } @@ -8042,7 +8057,6 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 } float chance = pEnchant->amount[s] != 0 ? float(pEnchant->amount[s]) : GetWeaponProcChance(); - if (entry) { if (entry->PPMChance) @@ -8052,7 +8066,7 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 } // Apply spell mods - ApplySpellMod(pEnchant->spellid[s], SPELLMOD_CHANCE_OF_SUCCESS, chance); + ApplySpellMod<SPELLMOD_CHANCE_OF_SUCCESS>(pEnchant->spellid[s], chance); // Shiv has 100% chance to apply the poison if (FindCurrentSpellBySpellId(5938) && e_slot == TEMP_ENCHANTMENT_SLOT) @@ -8063,7 +8077,7 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 if (spellInfo->IsPositive()) CastSpell(this, spellInfo, true, item); else - CastSpell(target, spellInfo, true, item); + CastSpell(damageInfo.GetVictim(), spellInfo, true, item); } } } @@ -20819,13 +20833,13 @@ void Player::SendRemoveControlBar() const GetSession()->SendPacket(&data); } -bool Player::IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell) const +bool Player::IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell) { if (!mod || !spellInfo) return false; - // Mod out of charges - if (spell && mod->charges == -1 && spell->m_appliedMods.find(mod->ownerAura) == spell->m_appliedMods.end()) + // First time this aura applies a mod to us and is out of charges + if (spell && mod->ownerAura->IsUsingCharges() && !mod->ownerAura->GetCharges() && !spell->m_appliedMods.count(mod->ownerAura)) return false; // +duration to infinite duration spells making them limited @@ -20842,22 +20856,22 @@ void Player::AddSpellMod(SpellModifier* mod, bool apply) int i = 0; flag96 _mask = 0; - for (int eff = 0; eff < 96; ++eff) + for (int32 eff = 0; eff < 96; ++eff) { - if (eff != 0 && eff%32 == 0) + if (eff != 0 && eff % 32 == 0) _mask[i++] = 0; - _mask[i] = uint32(1) << (eff-(32*i)); - if (mod->mask & _mask) + _mask[i] = uint32(1) << (eff - (32 * i)); + if ((mod->mask & _mask)) { int32 val = 0; - for (SpellModList::iterator itr = m_spellMods[mod->op].begin(); itr != m_spellMods[mod->op].end(); ++itr) + for (SpellModifier* spellMod : m_spellMods[mod->op]) { - if ((*itr)->type == mod->type && (*itr)->mask & _mask) - val += (*itr)->value; + if (spellMod->type == mod->type && (spellMod->mask & _mask)) + val += spellMod->value; } val += apply ? mod->value : -(mod->value); - WorldPacket data(Opcode, (1+1+4)); + WorldPacket data(Opcode, (1 + 1 + 4)); data << uint8(eff); data << uint8(mod->op); data << int32(val); @@ -20866,36 +20880,30 @@ void Player::AddSpellMod(SpellModifier* mod, bool apply) } if (apply) - m_spellMods[mod->op].push_back(mod); + m_spellMods[mod->op].insert(mod); else - { - m_spellMods[mod->op].remove(mod); - // mods bound to aura will be removed in AuraEffect::~AuraEffect - if (!mod->ownerAura) - delete mod; - } + m_spellMods[mod->op].erase(mod); } // Restore spellmods in case of failed cast -void Player::RestoreSpellMods(Spell* spell, uint32 ownerAuraId, Aura* aura) +void Player::RestoreSpellMods(Spell* spell, uint32 ownerAuraId /*= 0*/, Aura* aura /*= nullptr*/) { if (!spell || spell->m_appliedMods.empty()) return; std::list<Aura*> aurasQueue; - - for (uint8 i=0; i<MAX_SPELLMOD; ++i) + for (uint8 i = 0; i < MAX_SPELLMOD; ++i) { - for (SpellModList::iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end(); ++itr) + for (auto itr = m_spellMods[i].begin(); itr != m_spellMods[i].end(); ++itr) { SpellModifier* mod = *itr; - // Spellmods without aura set cannot be charged - if (!mod->ownerAura || !mod->ownerAura->IsUsingCharges()) + // Spellmods without charged aura cannot be charged + if (!mod->ownerAura->IsUsingCharges()) continue; // Restore only specific owner aura mods - if (ownerAuraId && (ownerAuraId != mod->ownerAura->GetSpellInfo()->Id)) + if (ownerAuraId && mod->spellId != ownerAuraId) continue; if (aura && mod->ownerAura != aura) @@ -20916,84 +20924,33 @@ void Player::RestoreSpellMods(Spell* spell, uint32 ownerAuraId, Aura* aura) // only see the first of its modifier restored) aurasQueue.push_back(mod->ownerAura); - // add mod charges back to mod - if (mod->charges == -1) - mod->charges = 1; - else - mod->charges++; - - // Do not set more spellmods than available - 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); } } - for (std::list<Aura*>::iterator itr = aurasQueue.begin(); itr != aurasQueue.end(); ++itr) - { - Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(*itr); - if (iterMod != spell->m_appliedMods.end()) - spell->m_appliedMods.erase(iterMod); - } + for (Aura* aura : aurasQueue) + spell->m_appliedMods.erase(aura); } -void Player::RestoreAllSpellMods(uint32 ownerAuraId, Aura* aura) +void Player::RestoreAllSpellMods(uint32 ownerAuraId /*= 0*/, Aura* aura /*= nullptr*/) { for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i) - if (m_currentSpells[i]) - RestoreSpellMods(m_currentSpells[i], ownerAuraId, aura); + if (Spell* spell = m_currentSpells[i]) + RestoreSpellMods(spell, ownerAuraId, aura); } -void Player::RemoveSpellMods(Spell* spell) +void Player::ApplyModToSpell(SpellModifier* mod, Spell* spell) { if (!spell) return; - if (spell->m_appliedMods.empty()) + // don't do anything with no charges + if (mod->ownerAura->IsUsingCharges() && !mod->ownerAura->GetCharges()) return; - for (uint8 i=0; i<MAX_SPELLMOD; ++i) - { - for (SpellModList::const_iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end();) - { - SpellModifier* mod = *itr; - ++itr; - - // spellmods without aura set cannot be charged - if (!mod->ownerAura || !mod->ownerAura->IsUsingCharges()) - continue; - - // check if mod affected this spell - Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(mod->ownerAura); - if (iterMod == spell->m_appliedMods.end()) - continue; - - // remove from list - spell->m_appliedMods.erase(iterMod); - - if (mod->ownerAura->DropCharge(AURA_REMOVE_BY_EXPIRE)) - itr = m_spellMods[i].begin(); - } - } -} - -void Player::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 (sSpellMgr->GetSpellProcEvent(mod->spellId)) - return; - - if (spell && mod->ownerAura && mod->charges > 0) - { - if (--mod->charges == 0) - mod->charges = -1; - - spell->m_appliedMods.insert(mod->ownerAura); - } + // register inside spell, proc system uses this to drop charges + spell->m_appliedMods.insert(mod->ownerAura); } void Player::SetSpellModTakingSpell(Spell* spell, bool apply) @@ -21004,7 +20961,7 @@ void Player::SetSpellModTakingSpell(Spell* spell, bool apply) if (apply && spell->getState() == SPELL_STATE_FINISHED) return; - m_spellModTakingSpell = apply ? spell : NULL; + m_spellModTakingSpell = apply ? spell : nullptr; } // send Proficiency diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 269cbb4a9ef..2f580908e2b 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -66,10 +66,10 @@ typedef std::deque<Mail*> PlayerMails; #define PLAYER_EXPLORED_ZONES_SIZE 128 // Note: SPELLMOD_* values is aura types in fact -enum SpellModType +enum SpellModType : uint8 { - SPELLMOD_FLAT = 107, // SPELL_AURA_ADD_FLAT_MODIFIER - SPELLMOD_PCT = 108 // SPELL_AURA_ADD_PCT_MODIFIER + SPELLMOD_FLAT = SPELL_AURA_ADD_FLAT_MODIFIER, + SPELLMOD_PCT = SPELL_AURA_ADD_PCT_MODIFIER }; // 2^n values, Player::m_isunderwater is a bitmask. These are Trinity internal values, they are never send to any client @@ -118,10 +118,11 @@ struct PlayerTalent // Spell modifier (used for modify other spells) struct SpellModifier { - SpellModifier(Aura* _ownerAura = nullptr) : op(SPELLMOD_DAMAGE), type(SPELLMOD_FLAT), charges(0), value(0), mask(), spellId(0), ownerAura(_ownerAura) { } - SpellModOp op : 8; - SpellModType type : 8; - int16 charges : 16; + SpellModifier(Aura* _ownerAura) : op(SPELLMOD_DAMAGE), type(SPELLMOD_FLAT), value(0), mask(), spellId(0), ownerAura(_ownerAura) { } + + SpellModOp op; + SpellModType type; + int32 value; flag96 mask; uint32 spellId; @@ -130,7 +131,7 @@ struct SpellModifier typedef std::unordered_map<uint32, PlayerTalent*> PlayerTalentMap; typedef std::unordered_map<uint32, PlayerSpell*> PlayerSpellMap; -typedef std::list<SpellModifier*> SpellModList; +typedef std::unordered_set<SpellModifier*> SpellModContainer; typedef std::unordered_map<uint32 /*instanceId*/, time_t/*releaseTime*/> InstanceTimeMap; @@ -1602,13 +1603,12 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> PlayerSpellMap & GetSpellMap() { return m_spells; } void AddSpellMod(SpellModifier* mod, bool apply); - bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = nullptr) const; - template <class T> - void ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell = nullptr); - void RemoveSpellMods(Spell* spell); + static bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = nullptr); + template <SpellModOp op, class T> + void ApplySpellMod(uint32 spellId, T& basevalue, Spell* spell = nullptr) const; void RestoreSpellMods(Spell* spell, uint32 ownerAuraId = 0, Aura* aura = nullptr); void RestoreAllSpellMods(uint32 ownerAuraId = 0, Aura* aura = nullptr); - void DropModCharge(SpellModifier* mod, Spell* spell); + static void ApplyModToSpell(SpellModifier* mod, Spell* spell); void SetSpellModTakingSpell(Spell* spell, bool apply); void RemoveArenaSpellCooldowns(bool removeActivePetCooldowns = false); @@ -1973,9 +1973,9 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void ApplyItemEquipSpell(Item* item, bool apply, bool form_change = false); void ApplyEquipSpell(SpellInfo const* spellInfo, Item* item, bool apply, bool form_change = false); void UpdateEquipSpellsAtFormChange(); - void CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx); + void CastItemCombatSpell(DamageInfo const& damageInfo); + void CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemTemplate const* proto); void CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8 cast_count, uint32 glyphIndex); - void CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx, Item* item, ItemTemplate const* proto); void SendEquipmentSetList(); void SetEquipmentSet(uint32 index, EquipmentSet eqset); @@ -2441,7 +2441,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> uint32 m_baseHealthRegen; int32 m_spellPenetrationItemMod; - SpellModList m_spellMods[MAX_SPELLMOD]; + SpellModContainer m_spellMods[MAX_SPELLMOD]; EnchantDurationList m_enchantDuration; ItemDurationList m_itemDuration; @@ -2614,8 +2614,8 @@ TC_GAME_API void AddItemsSetItem(Player* player, Item* item); TC_GAME_API void RemoveItemsSetItem(Player* player, ItemTemplate const* proto); // "the bodies of template functions must be made available in a header file" -template <class T> -void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell /*= nullptr*/) +template <SpellModOp op, class T> +void Player::ApplySpellMod(uint32 spellId, T& basevalue, Spell* spell /*= nullptr*/) const { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) @@ -2628,33 +2628,89 @@ void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* s if (m_spellModTakingSpell) spell = m_spellModTakingSpell; - for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr) + switch (op) { - SpellModifier* mod = *itr; + // special case, if a mod makes spell instant, only consume that mod + case SPELLMOD_CASTING_TIME: + { + SpellModifier* modInstantSpell = nullptr; + for (SpellModifier* mod : m_spellMods[SPELLMOD_CASTING_TIME]) + { + if (!IsAffectedBySpellmod(spellInfo, mod, spell)) + continue; + + if (mod->type == SPELLMOD_PCT && basevalue < T(10000) && mod->value <= -100) + { + modInstantSpell = mod; + break; + } + } - // Charges can be set only for mods with auras - if (!mod->ownerAura) - ASSERT(mod->charges == 0); + if (modInstantSpell) + { + Player::ApplyModToSpell(modInstantSpell, spell); + basevalue = T(0); + return; + } + break; + } + // special case if two mods apply 100% critical chance, only consume one + case SPELLMOD_CRITICAL_CHANCE: + { + SpellModifier* modCritical = nullptr; + for (SpellModifier* mod : m_spellMods[SPELLMOD_CRITICAL_CHANCE]) + { + if (!IsAffectedBySpellmod(spellInfo, mod, spell)) + continue; + + if (mod->type == SPELLMOD_FLAT && mod->value >= 100) + { + modCritical = mod; + break; + } + } + if (modCritical) + { + Player::ApplyModToSpell(modCritical, spell); + basevalue = T(100); + return; + } + break; + } + default: + break; + } + + for (SpellModifier* mod : m_spellMods[op]) + { if (!IsAffectedBySpellmod(spellInfo, mod, spell)) continue; - if (mod->type == SPELLMOD_FLAT) - totalflat += mod->value; - else if (mod->type == SPELLMOD_PCT) + switch (mod->type) { - // skip percent mods for null basevalue (most important for spell mods with charges) - if (basevalue == T(0)) - continue; - - // special case (skip > 10sec spell casts for instant cast setting) - if (mod->op == SPELLMOD_CASTING_TIME && basevalue >= T(10000) && mod->value <= -100) - continue; - - totalmul += CalculatePct(1.0f, mod->value); + case SPELLMOD_FLAT: + totalflat += mod->value; + break; + case SPELLMOD_PCT: + { + // skip percent mods for null basevalue (most important for spell mods with charges) + if (basevalue == T(0)) + continue; + + // special case (skip > 10sec spell casts for instant cast setting) + if (op == SPELLMOD_CASTING_TIME) + { + if (mod->value <= -100 && basevalue >= T(10000)) + continue; + } + + totalmul += CalculatePct(1.0f, mod->value); + break; + } } - DropModCharge(mod, spell); + Player::ApplyModToSpell(mod, spell); } basevalue = T(float(basevalue + totalflat) * totalmul); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 2dcac43b828..ea16e542ff7 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -92,17 +92,6 @@ float playerBaseMoveSpeed[MAX_MOVE_TYPE] = 3.14f // MOVE_PITCH_RATE }; -// Used for prepare can/can`t triggr aura -static bool InitTriggerAuraData(); -// Define can trigger auras -static bool isTriggerAura[TOTAL_AURAS]; -// Define can't trigger auras (need for disable second trigger) -static bool isNonTriggerAura[TOTAL_AURAS]; -// Triggered always, even from triggered spells -static bool isAlwaysTriggeredAura[TOTAL_AURAS]; -// Prepare lists -static bool procPrepared = InitTriggerAuraData(); - DamageInfo::DamageInfo(Unit* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, SpellSchoolMask schoolMask, DamageEffectType damageType, WeaponAttackType attackType) : m_attacker(attacker), m_victim(victim), m_damage(damage), m_spellInfo(spellInfo), m_schoolMask(schoolMask), m_damageType(damageType), m_attackType(attackType), m_absorb(0), m_resist(0), m_block(0), m_hitMask(0) @@ -146,15 +135,23 @@ DamageInfo::DamageInfo(CalcDamageInfo const& dmgInfo) case MELEE_HIT_EVADE: m_hitMask |= PROC_HIT_EVADE; break; + case MELEE_HIT_CRUSHING: + case MELEE_HIT_GLANCING: + case MELEE_HIT_NORMAL: + m_hitMask |= PROC_HIT_NORMAL; + break; + case MELEE_HIT_CRIT: + m_hitMask |= PROC_HIT_CRITICAL; + break; default: break; } } -DamageInfo::DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType) +DamageInfo::DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType, uint32 hitMask) : m_attacker(spellNonMeleeDamage.attacker), m_victim(spellNonMeleeDamage.target), m_damage(spellNonMeleeDamage.damage), m_spellInfo(sSpellMgr->GetSpellInfo(spellNonMeleeDamage.SpellID)), m_schoolMask(SpellSchoolMask(spellNonMeleeDamage.schoolMask)), m_damageType(damageType), - m_attackType(attackType), m_absorb(spellNonMeleeDamage.absorb), m_resist(spellNonMeleeDamage.resist), m_block(spellNonMeleeDamage.blocked), m_hitMask(0) + m_attackType(attackType), m_absorb(spellNonMeleeDamage.absorb), m_resist(spellNonMeleeDamage.resist), m_block(spellNonMeleeDamage.blocked), m_hitMask(hitMask) { if (spellNonMeleeDamage.blocked) m_hitMask |= PROC_HIT_BLOCK; @@ -201,7 +198,7 @@ uint32 DamageInfo::GetHitMask() const } HealInfo::HealInfo(Unit* healer, Unit* target, uint32 heal, SpellInfo const* spellInfo, SpellSchoolMask schoolMask) - : _healer(healer), _target(target), _heal(heal), _absorb(0), _spellInfo(spellInfo), _schoolMask(schoolMask), _hitMask(0) + : _healer(healer), _target(target), _heal(heal), _effectiveHeal(0), _absorb(0), _spellInfo(spellInfo), _schoolMask(schoolMask), _hitMask(0) { } @@ -210,6 +207,8 @@ void HealInfo::AbsorbHeal(uint32 amount) amount = std::min(amount, GetHeal()); _absorb += amount; _heal -= amount; + amount = std::min(amount, GetEffectiveHeal()); + _effectiveHeal -= amount; _hitMask |= PROC_HIT_ABSORB; } @@ -1118,7 +1117,7 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama uint32 crit_bonus = damage; // Apply crit_damage bonus for melee spells if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus); + modOwner->ApplySpellMod<SPELLMOD_CRIT_DAMAGE_BONUS>(spellInfo->Id, crit_bonus); damage += crit_bonus; // Apply SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE or SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE @@ -1232,7 +1231,6 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam damageInfo->HitInfo = 0; damageInfo->procAttacker = PROC_FLAG_NONE; damageInfo->procVictim = PROC_FLAG_NONE; - damageInfo->procEx = PROC_EX_NONE; damageInfo->hitOutCome = MELEE_HIT_EVADE; if (!victim) @@ -1263,7 +1261,6 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam damageInfo->HitInfo |= HITINFO_NORMALSWING; damageInfo->TargetState = VICTIMSTATE_IS_IMMUNE; - damageInfo->procEx |= PROC_EX_IMMUNE; damageInfo->damage = 0; damageInfo->cleanDamage = 0; return; @@ -1293,27 +1290,23 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam case MELEE_HIT_EVADE: damageInfo->HitInfo |= HITINFO_MISS | HITINFO_SWINGNOHITSOUND; damageInfo->TargetState = VICTIMSTATE_EVADES; - damageInfo->procEx |= PROC_EX_EVADE; damageInfo->damage = 0; damageInfo->cleanDamage = 0; return; case MELEE_HIT_MISS: damageInfo->HitInfo |= HITINFO_MISS; damageInfo->TargetState = VICTIMSTATE_INTACT; - damageInfo->procEx |= PROC_EX_MISS; damageInfo->damage = 0; damageInfo->cleanDamage = 0; break; case MELEE_HIT_NORMAL: damageInfo->TargetState = VICTIMSTATE_HIT; - damageInfo->procEx |= PROC_EX_NORMAL_HIT; break; case MELEE_HIT_CRIT: { damageInfo->HitInfo |= HITINFO_CRITICALHIT; damageInfo->TargetState = VICTIMSTATE_HIT; - damageInfo->procEx |= PROC_EX_CRITICAL_HIT; // Crit bonus calc damageInfo->damage += damageInfo->damage; float mod = 0.0f; @@ -1336,32 +1329,27 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam } case MELEE_HIT_PARRY: damageInfo->TargetState = VICTIMSTATE_PARRY; - damageInfo->procEx |= PROC_EX_PARRY; damageInfo->cleanDamage += damageInfo->damage; damageInfo->damage = 0; break; case MELEE_HIT_DODGE: damageInfo->TargetState = VICTIMSTATE_DODGE; - damageInfo->procEx |= PROC_EX_DODGE; damageInfo->cleanDamage += damageInfo->damage; damageInfo->damage = 0; break; case MELEE_HIT_BLOCK: damageInfo->TargetState = VICTIMSTATE_HIT; damageInfo->HitInfo |= HITINFO_BLOCK; - damageInfo->procEx |= PROC_EX_BLOCK; damageInfo->blocked_amount = damageInfo->target->GetShieldBlockValue(); // double blocked amount if block is critical if (damageInfo->target->isBlockCritical()) - damageInfo->blocked_amount+=damageInfo->blocked_amount; + damageInfo->blocked_amount += damageInfo->blocked_amount; if (damageInfo->blocked_amount >= damageInfo->damage) { damageInfo->TargetState = VICTIMSTATE_BLOCKS; damageInfo->blocked_amount = damageInfo->damage; - damageInfo->procEx |= PROC_EX_FULL_BLOCK; } - else - damageInfo->procEx |= PROC_EX_NORMAL_HIT; + damageInfo->damage -= damageInfo->blocked_amount; damageInfo->cleanDamage += damageInfo->blocked_amount; break; @@ -1369,7 +1357,6 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam { damageInfo->HitInfo |= HITINFO_GLANCING; damageInfo->TargetState = VICTIMSTATE_HIT; - damageInfo->procEx |= PROC_EX_NORMAL_HIT; int32 leveldif = int32(victim->getLevel()) - int32(getLevel()); if (leveldif > 3) leveldif = 3; @@ -1386,7 +1373,6 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam case MELEE_HIT_CRUSHING: damageInfo->HitInfo |= HITINFO_CRUSHING; damageInfo->TargetState = VICTIMSTATE_HIT; - damageInfo->procEx |= PROC_EX_NORMAL_HIT; // 150% normal damage damageInfo->damage += (damageInfo->damage / 2); break; @@ -1413,10 +1399,7 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam CalcAbsorbResist(damageInfo->target, SpellSchoolMask(damageInfo->damageSchoolMask), DIRECT_DAMAGE, damageInfo->damage, &damageInfo->absorb, &damageInfo->resist); if (damageInfo->absorb) - { damageInfo->HitInfo |= (damageInfo->damage - damageInfo->absorb == 0 ? HITINFO_FULL_ABSORB : HITINFO_PARTIAL_ABSORB); - damageInfo->procEx |= PROC_EX_ABSORB; - } if (damageInfo->resist) damageInfo->HitInfo |= (damageInfo->damage - damageInfo->resist == 0 ? HITINFO_FULL_RESIST : HITINFO_PARTIAL_RESIST); @@ -1506,7 +1489,10 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss) } if (GetTypeId() == TYPEID_PLAYER) - ToPlayer()->CastItemCombatSpell(victim, damageInfo->attackType, damageInfo->procVictim, damageInfo->procEx); + { + DamageInfo dmgInfo(*damageInfo); + ToPlayer()->CastItemCombatSpell(dmgInfo); + } // Do effect if any damage done to target if (damageInfo->damage) @@ -1598,7 +1584,7 @@ uint32 Unit::CalcArmorReducedDamage(Unit* victim, const uint32 damage, SpellInfo if (spellInfo) if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_IGNORE_ARMOR, armor); + modOwner->ApplySpellMod<SPELLMOD_IGNORE_ARMOR>(spellInfo->Id, armor); AuraEffectList const& resIgnoreAurasAb = GetAuraEffectsByType(SPELL_AURA_MOD_ABILITY_IGNORE_TARGET_RESIST); for (AuraEffectList::const_iterator j = resIgnoreAurasAb.begin(); j != resIgnoreAurasAb.end(); ++j) @@ -1978,8 +1964,10 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe CleanDamage cleanDamage = CleanDamage(splitDamage, 0, BASE_ATTACK, MELEE_HIT_NORMAL); DealDamage(caster, splitDamage, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*itr)->GetSpellInfo(), false); + // break 'Fear' and similar auras - caster->ProcDamageAndSpellFor(true, this, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_EX_NORMAL_HIT, BASE_ATTACK, (*itr)->GetSpellInfo(), splitDamage); + DamageInfo damageInfo(caster, this, splitDamage, (*itr)->GetSpellInfo(), schoolMask, DIRECT_DAMAGE, BASE_ATTACK); + ProcSkillsAndAuras(caster, PROC_FLAG_NONE, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_HIT, PROC_HIT_NONE, nullptr, &damageInfo, nullptr); } } @@ -1987,21 +1975,22 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe *absorb = dmgInfo.GetAbsorb(); } -void Unit::CalcHealAbsorb(Unit* victim, SpellInfo const* healSpell, uint32 &healAmount, uint32 &absorb) +void Unit::CalcHealAbsorb(HealInfo& healInfo) { - if (!healAmount) + if (!healInfo.GetHeal()) return; - int32 RemainingHeal = healAmount; + int32 const healing = static_cast<int32>(healInfo.GetHeal()); + int32 absorbAmount = 0; // Need remove expired auras after bool existExpired = false; // absorb without mana cost - AuraEffectList const& vHealAbsorb = victim->GetAuraEffectsByType(SPELL_AURA_SCHOOL_HEAL_ABSORB); - for (AuraEffectList::const_iterator i = vHealAbsorb.begin(); i != vHealAbsorb.end() && RemainingHeal > 0; ++i) + AuraEffectList const& vHealAbsorb = healInfo.GetTarget()->GetAuraEffectsByType(SPELL_AURA_SCHOOL_HEAL_ABSORB); + for (AuraEffectList::const_iterator i = vHealAbsorb.begin(); i != vHealAbsorb.end() && absorbAmount <= healing; ++i) { - if (!((*i)->GetMiscValue() & healSpell->SchoolMask)) + if (!((*i)->GetMiscValue() & healInfo.GetSpellInfo()->SchoolMask)) continue; // Max Amount can be absorbed by this aura @@ -2016,10 +2005,10 @@ void Unit::CalcHealAbsorb(Unit* victim, SpellInfo const* healSpell, uint32 &heal // currentAbsorb - damage can be absorbed by shield // If need absorb less damage - if (RemainingHeal < currentAbsorb) - currentAbsorb = RemainingHeal; + if (healing < currentAbsorb + absorbAmount) + currentAbsorb = healing - absorbAmount; - RemainingHeal -= currentAbsorb; + absorbAmount += currentAbsorb; // Reduce shield amount (*i)->SetAmount((*i)->GetAmount() - currentAbsorb); @@ -2037,19 +2026,19 @@ void Unit::CalcHealAbsorb(Unit* victim, SpellInfo const* healSpell, uint32 &heal ++i; if (auraEff->GetAmount() <= 0) { - uint32 removedAuras = victim->m_removedAurasCount; + uint32 removedAuras = healInfo.GetTarget()->m_removedAurasCount; auraEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL); - if (removedAuras+1 < victim->m_removedAurasCount) + if (removedAuras + 1 < healInfo.GetTarget()->m_removedAurasCount) i = vHealAbsorb.begin(); } } } - absorb = RemainingHeal > 0 ? (healAmount - RemainingHeal) : healAmount; - healAmount = RemainingHeal; + if (absorbAmount > 0) + healInfo.AbsorbHeal(absorbAmount); } -void Unit::AttackerStateUpdate (Unit* victim, WeaponAttackType attType, bool extra) +void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extra) { if (HasUnitState(UNIT_STATE_CANNOT_AUTOATTACK) || HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED)) return; @@ -2083,8 +2072,7 @@ void Unit::AttackerStateUpdate (Unit* victim, WeaponAttackType attType, bool ext DealDamageMods(victim, damageInfo.damage, &damageInfo.absorb); SendAttackStateUpdate(&damageInfo); - //TriggerAurasProcOnEvent(damageInfo); - ProcDamageAndSpell(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, damageInfo.procEx, damageInfo.damage, damageInfo.attackType); + TriggerAurasProcOnEvent(damageInfo); DealMeleeDamage(&damageInfo, true); @@ -2577,7 +2565,7 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo // Spellmod from SPELLMOD_RESIST_MISS_CHANCE if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RESIST_MISS_CHANCE, modHitChance); + modOwner->ApplySpellMod<SPELLMOD_RESIST_MISS_CHANCE>(spellInfo->Id, modHitChance); // Increase from attacker SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT auras modHitChance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT, schoolMask); @@ -2683,12 +2671,9 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, boo for (Unit::AuraEffectList::const_iterator i = mReflectSpellsSchool.begin(); i != mReflectSpellsSchool.end(); ++i) if ((*i)->GetMiscValue() & spellInfo->GetSchoolMask()) reflectchance += (*i)->GetAmount(); + if (reflectchance > 0 && roll_chance_i(reflectchance)) - { - // Start triggers for remove charges if need (trigger only for victim, and mark as active spell) - ProcDamageAndSpell(victim, PROC_FLAG_NONE, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_EX_REFLECT, 1, BASE_ATTACK, spellInfo); return SPELL_MISS_REFLECT; - } } switch (spellInfo->DmgClass) @@ -5214,15 +5199,16 @@ void Unit::SendSpellNonMeleeDamageLog(Unit* target, uint32 SpellID, uint32 Damag SendSpellNonMeleeDamageLog(&log); } -void Unit::ProcDamageAndSpell(Unit* victim, uint32 procAttacker, uint32 procVictim, uint32 procExtra, uint32 amount, WeaponAttackType attType, SpellInfo const* procSpell, SpellInfo const* procAura) +void Unit::ProcSkillsAndAuras(Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo) { - // Not much to do if no flags are set. - if (procAttacker) - ProcDamageAndSpellFor(false, victim, procAttacker, procExtra, attType, procSpell, amount, procAura); - // Now go on with a victim's events'n'auras - // Not much to do if no flags are set or there is no victim - if (victim && victim->IsAlive() && procVictim) - victim->ProcDamageAndSpellFor(true, this, procVictim, procExtra, attType, procSpell, amount, procAura); + WeaponAttackType attType = damageInfo ? damageInfo->GetAttackType() : BASE_ATTACK; + if (typeMaskActor && CanProc()) + ProcSkillsAndReactives(false, actionTarget, typeMaskActor, hitMask, attType); + + if (typeMaskActionTarget && actionTarget && actionTarget->CanProc()) + actionTarget->ProcSkillsAndReactives(true, this, typeMaskActionTarget, hitMask, attType); + + TriggerAurasProcOnEvent(actionTarget, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); } void Unit::SendPeriodicAuraLog(SpellPeriodicAuraLogInfo* pInfo) @@ -5384,2970 +5370,6 @@ void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit* target, uint8 /*SwingType SendAttackStateUpdate(&dmgInfo); } -//victim may be NULL -bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, Milliseconds& cooldown) -{ - SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo(); - uint32 effIndex = triggeredByAura->GetEffIndex(); - int32 triggerAmount = triggeredByAura->GetAmount(); - - Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER - ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; - - uint32 triggered_spell_id = 0; - Unit* target = victim; - int32 basepoints0 = 0; - ObjectGuid originalCaster; - - switch (dummySpell->SpellFamilyName) - { - case SPELLFAMILY_GENERIC: - { - switch (dummySpell->Id) - { - // Unstable Power - case 24658: - { - if (!procSpell || procSpell->Id == 24659) - return false; - // Need remove one 24659 aura - RemoveAuraFromStack(24659); - return true; - } - // Restless Strength - case 24661: - { - // Need remove one 24662 aura - RemoveAuraFromStack(24662); - return true; - } - // Mark of Malice - case 33493: - { - // Cast finish spell at last charge - if (triggeredByAura->GetBase()->GetCharges() > 1) - return false; - - target = this; - triggered_spell_id = 33494; - break; - } - // Twisted Reflection (boss spell) - case 21063: - triggered_spell_id = 21064; - break; - // Vampiric Aura (boss spell) - case 38196: - { - basepoints0 = 3 * damage; // 300% - if (basepoints0 < 0) - return false; - - triggered_spell_id = 31285; - target = this; - break; - } - // Aura of Madness (Darkmoon Card: Madness trinket) - //===================================================== - // 39511 Sociopath: +35 strength (Paladin, Rogue, Druid, Warrior) - // 40997 Delusional: +70 attack power (Rogue, Hunter, Paladin, Warrior, Druid) - // 40998 Kleptomania: +35 agility (Warrior, Rogue, Paladin, Hunter, Druid) - // 40999 Megalomania: +41 damage/healing (Druid, Shaman, Priest, Warlock, Mage, Paladin) - // 41002 Paranoia: +35 spell/melee/ranged crit strike rating (All classes) - // 41005 Manic: +35 haste (spell, melee and ranged) (All classes) - // 41009 Narcissism: +35 intellect (Druid, Shaman, Priest, Warlock, Mage, Paladin, Hunter) - // 41011 Martyr Complex: +35 stamina (All classes) - // 41406 Dementia: Every 5 seconds either gives you +5% damage/healing. (Druid, Shaman, Priest, Warlock, Mage, Paladin) - // 41409 Dementia: Every 5 seconds either gives you -5% damage/healing. (Druid, Shaman, Priest, Warlock, Mage, Paladin) - case 39446: - { - if (GetTypeId() != TYPEID_PLAYER || !IsAlive()) - return false; - - // Select class defined buff - switch (getClass()) - { - case CLASS_PALADIN: // 39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409 - case CLASS_DRUID: // 39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409 - triggered_spell_id = RAND(39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409); - break; - case CLASS_ROGUE: // 39511, 40997, 40998, 41002, 41005, 41011 - case CLASS_WARRIOR: // 39511, 40997, 40998, 41002, 41005, 41011 - case CLASS_DEATH_KNIGHT: - triggered_spell_id = RAND(39511, 40997, 40998, 41002, 41005, 41011); - break; - case CLASS_PRIEST: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 - case CLASS_SHAMAN: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 - case CLASS_MAGE: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 - case CLASS_WARLOCK: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 - triggered_spell_id = RAND(40999, 41002, 41005, 41009, 41011, 41406, 41409); - break; - case CLASS_HUNTER: // 40997, 40999, 41002, 41005, 41009, 41011, 41406, 41409 - triggered_spell_id = RAND(40997, 40999, 41002, 41005, 41009, 41011, 41406, 41409); - break; - default: - return false; - } - - target = this; - if (roll_chance_i(10)) - ToPlayer()->Say("This is Madness!", LANG_UNIVERSAL); /// @todo It should be moved to database, shouldn't it? - break; - } - // Sunwell Exalted Caster Neck (??? neck) - // cast ??? Light's Wrath if Exalted by Aldor - // cast ??? Arcane Bolt if Exalted by Scryers - case 46569: - return false; // old unused version - // Sunwell Exalted Caster Neck (Shattered Sun Pendant of Acumen neck) - // cast 45479 Light's Wrath if Exalted by Aldor - // cast 45429 Arcane Bolt if Exalted by Scryers - case 45481: - { - Player* player = ToPlayer(); - if (!player) - return false; - - // Get Aldor reputation rank - if (player->GetReputationRank(932) == REP_EXALTED) - { - target = this; - triggered_spell_id = 45479; - break; - } - // Get Scryers reputation rank - if (player->GetReputationRank(934) == REP_EXALTED) - { - // triggered at positive/self casts also, current attack target used then - if (target && IsFriendlyTo(target)) - { - target = GetVictim(); - if (!target) - { - target = player->GetSelectedUnit(); - if (!target) - return false; - } - if (IsFriendlyTo(target)) - return false; - } - - triggered_spell_id = 45429; - break; - } - return false; - } - // Sunwell Exalted Melee Neck (Shattered Sun Pendant of Might neck) - // cast 45480 Light's Strength if Exalted by Aldor - // cast 45428 Arcane Strike if Exalted by Scryers - case 45482: - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - // Get Aldor reputation rank - if (ToPlayer()->GetReputationRank(932) == REP_EXALTED) - { - target = this; - triggered_spell_id = 45480; - break; - } - // Get Scryers reputation rank - if (ToPlayer()->GetReputationRank(934) == REP_EXALTED) - { - triggered_spell_id = 45428; - break; - } - return false; - } - // Sunwell Exalted Tank Neck (Shattered Sun Pendant of Resolve neck) - // cast 45431 Arcane Insight if Exalted by Aldor - // cast 45432 Light's Ward if Exalted by Scryers - case 45483: - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - // Get Aldor reputation rank - if (ToPlayer()->GetReputationRank(932) == REP_EXALTED) - { - target = this; - triggered_spell_id = 45432; - break; - } - // Get Scryers reputation rank - if (ToPlayer()->GetReputationRank(934) == REP_EXALTED) - { - target = this; - triggered_spell_id = 45431; - break; - } - return false; - } - // Sunwell Exalted Healer Neck (Shattered Sun Pendant of Restoration neck) - // cast 45478 Light's Salvation if Exalted by Aldor - // cast 45430 Arcane Surge if Exalted by Scryers - case 45484: - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - // Get Aldor reputation rank - if (ToPlayer()->GetReputationRank(932) == REP_EXALTED) - { - target = this; - triggered_spell_id = 45478; - break; - } - // Get Scryers reputation rank - if (ToPlayer()->GetReputationRank(934) == REP_EXALTED) - { - triggered_spell_id = 45430; - break; - } - return false; - } - // Kill command - case 58914: - { - // Remove aura stack from pet - RemoveAuraFromStack(58914); - Unit* owner = GetOwner(); - if (!owner) - return true; - // reduce the owner's aura stack - owner->RemoveAuraFromStack(34027); - return true; - } - // Vampiric Touch (generic, used by some boss) - case 52723: - case 60501: - { - triggered_spell_id = 52724; - basepoints0 = damage / 2; - target = this; - break; - } - // Shadowfiend Death (Gain mana if pet dies with Glyph of Shadowfiend) - case 57989: - { - Unit* owner = GetOwner(); - if (!owner || owner->GetTypeId() != TYPEID_PLAYER) - return false; - // Glyph of Shadowfiend (need cast as self cast for owner, no hidden cooldown) - owner->CastSpell(owner, 58227, true, castItem, triggeredByAura); - return true; - } - // Divine purpose - case 31871: - case 31872: - { - // Roll chane - if (!victim || !victim->IsAlive() || !roll_chance_i(triggerAmount)) - return false; - - // Remove any stun effect on target - victim->RemoveAurasWithMechanic(1<<MECHANIC_STUN, AURA_REMOVE_BY_ENEMY_SPELL); - return true; - } - // Glyph of Scourge Strike - case 58642: - { - triggered_spell_id = 69961; // Glyph of Scourge Strike - break; - } - // Glyph of Life Tap - case 63320: - { - triggered_spell_id = 63321; // Life Tap - break; - } - // Purified Shard of the Scale - Onyxia 10 Caster Trinket - case 69755: - { - triggered_spell_id = (procFlag & PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS) ? 69733 : 69729; - break; - } - // Shiny Shard of the Scale - Onyxia 25 Caster Trinket - case 69739: - { - triggered_spell_id = (procFlag & PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS) ? 69734 : 69730; - break; - } - case 71519: // Deathbringer's Will Normal - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - std::vector<uint32> RandomSpells; - switch (getClass()) - { - case CLASS_WARRIOR: - case CLASS_PALADIN: - case CLASS_DEATH_KNIGHT: - RandomSpells.push_back(71484); - RandomSpells.push_back(71491); - RandomSpells.push_back(71492); - break; - case CLASS_SHAMAN: - case CLASS_ROGUE: - RandomSpells.push_back(71486); - RandomSpells.push_back(71485); - RandomSpells.push_back(71492); - break; - case CLASS_DRUID: - RandomSpells.push_back(71484); - RandomSpells.push_back(71485); - RandomSpells.push_back(71492); - break; - case CLASS_HUNTER: - RandomSpells.push_back(71486); - RandomSpells.push_back(71491); - RandomSpells.push_back(71485); - break; - default: - return false; - } - if (RandomSpells.empty()) // shouldn't happen - return false; - - uint8 rand_spell = urand(0, (RandomSpells.size() - 1)); - CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster); - break; - } - case 71562: // Deathbringer's Will Heroic - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - std::vector<uint32> RandomSpells; - switch (getClass()) - { - case CLASS_WARRIOR: - case CLASS_PALADIN: - case CLASS_DEATH_KNIGHT: - RandomSpells.push_back(71561); - RandomSpells.push_back(71559); - RandomSpells.push_back(71560); - break; - case CLASS_SHAMAN: - case CLASS_ROGUE: - RandomSpells.push_back(71558); - RandomSpells.push_back(71556); - RandomSpells.push_back(71560); - break; - case CLASS_DRUID: - RandomSpells.push_back(71561); - RandomSpells.push_back(71556); - RandomSpells.push_back(71560); - break; - case CLASS_HUNTER: - RandomSpells.push_back(71558); - RandomSpells.push_back(71559); - RandomSpells.push_back(71556); - break; - default: - return false; - } - if (RandomSpells.empty()) // shouldn't happen - return false; - - uint8 rand_spell = urand(0, (RandomSpells.size() - 1)); - CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster); - break; - } - case 65032: // Boom aura (321 Boombot) - { - if (victim->GetEntry() != 33343) // Scrapbot - return false; - - InstanceScript* instance = GetInstanceScript(); - if (!instance) - return false; - - instance->DoCastSpellOnPlayers(65037); // Achievement criteria marker - break; - } - } - break; - } - case SPELLFAMILY_MAGE: - { - // Magic Absorption - if (dummySpell->SpellIconID == 459) // only this spell has SpellIconID == 459 and dummy aura - { - if (getPowerType() != POWER_MANA) - return false; - - // mana reward - basepoints0 = CalculatePct(int32(GetMaxPower(POWER_MANA)), triggerAmount); - target = this; - triggered_spell_id = 29442; - break; - } - // Arcane Potency - if (dummySpell->SpellIconID == 2120) - { - if (!procSpell) - return false; - - target = this; - switch (dummySpell->Id) - { - case 31571: triggered_spell_id = 57529; break; - case 31572: triggered_spell_id = 57531; break; - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleDummyAuraProc: non handled spell id: %u", dummySpell->Id); - return false; - } - break; - } - // Hot Streak - if (dummySpell->SpellIconID == 2999) - { - if (effIndex != 0) - return false; - AuraEffect* counter = triggeredByAura->GetBase()->GetEffect(EFFECT_1); - if (!counter) - return true; - - // Count spell criticals in a row in second aura - if (procEx & PROC_EX_CRITICAL_HIT) - { - counter->SetAmount(counter->GetAmount() * 2); - if (counter->GetAmount() < 100) // not enough - return true; - // Crititcal counted -> roll chance - if (roll_chance_i(triggerAmount)) - CastSpell(this, 48108, true, castItem, triggeredByAura); - } - counter->SetAmount(25); - return true; - } - // Incanter's Regalia set (add trigger chance to Mana Shield) - if (dummySpell->SpellFamilyFlags[0] & 0x8000) - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - target = this; - triggered_spell_id = 37436; - break; - } - switch (dummySpell->Id) - { - // Glyph of Polymorph - case 56375: - { - if (!target) - return false; - target->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE, ObjectGuid::Empty, target->GetAura(32409)); // SW:D shall not be removed. - target->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT); - target->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH); - return true; - } - // Glyph of Icy Veins - case 56374: - { - RemoveAurasByType(SPELL_AURA_HASTE_SPELLS, ObjectGuid::Empty, 0, true, false); - RemoveAurasByType(SPELL_AURA_MOD_DECREASE_SPEED); - return true; - } - // Glyph of Ice Block - case 56372: - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool - { - SpellInfo const* cdSpell = sSpellMgr->GetSpellInfo(itr->first); - if (!cdSpell || cdSpell->SpellFamilyName != SPELLFAMILY_MAGE - || !(cdSpell->SpellFamilyFlags[0] & 0x00000040)) - return false; - return true; - }, true); - break; - } - case 47020: // Enter vehicle XT-002 (Scrapbot) - { - if (GetTypeId() != TYPEID_UNIT) - return false; - - Unit* vehicleBase = GetVehicleBase(); - if (!vehicleBase) - return false; - - /// @todo Check if this amount is blizzlike - vehicleBase->ModifyHealth(int32(vehicleBase->CountPctFromMaxHealth(1))); - break; - } - } - break; - } - case SPELLFAMILY_WARRIOR: - { - switch (dummySpell->Id) - { - // Victorious - case 32216: - { - RemoveAura(dummySpell->Id); - return false; - } - // Improved Spell Reflection - case 59088: - case 59089: - { - triggered_spell_id = 59725; - target = this; - break; - } - } - // Second Wind - if (dummySpell->SpellIconID == 1697) - { - // only for spells and hit/crit (trigger start always) and not start from self cast spells (5530 Mace Stun Effect for example) - if (!procSpell || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == victim) - return false; - // Need stun or root mechanic - if (!(procSpell->GetAllEffectsMechanicMask() & ((1<<MECHANIC_ROOT)|(1<<MECHANIC_STUN)))) - return false; - - switch (dummySpell->Id) - { - case 29838: triggered_spell_id=29842; break; - case 29834: triggered_spell_id=29841; break; - case 42770: triggered_spell_id=42771; break; - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleDummyAuraProc: non handled spell id: %u (SW)", dummySpell->Id); - return false; - } - - target = this; - break; - } - // Glyph of Blocking - if (dummySpell->Id == 58375) - { - triggered_spell_id = 58374; - break; - } - break; - } - case SPELLFAMILY_WARLOCK: - { - // Seed of Corruption - if (dummySpell->SpellFamilyFlags[1] & 0x00000010) - { - if (procSpell && procSpell->SpellFamilyFlags[1] & 0x8000) - return false; - // if damage is more than need or target die from damage deal finish spell - if (triggeredByAura->GetAmount() <= int32(damage) || GetHealth() <= damage) - { - // remember caster before aura delete - Unit* caster = triggeredByAura->GetCaster(); - - // Remove aura (before cast for prevent infinite loop handlers) - RemoveAurasDueToSpell(triggeredByAura->GetId()); - - uint32 spell = sSpellMgr->GetSpellWithRank(27285, dummySpell->GetRank()); - - // Cast finish spell (triggeredByAura already not exist!) - if (caster) - caster->CastSpell(this, spell, true, castItem); - return true; // no hidden cooldown - } - - // Damage counting - triggeredByAura->SetAmount(triggeredByAura->GetAmount() - damage); - return true; - } - // Seed of Corruption (Mobs cast) - no die req - if (dummySpell->SpellFamilyFlags.IsEqual(0, 0, 0) && dummySpell->SpellIconID == 1932) - { - // if damage is more than need deal finish spell - if (triggeredByAura->GetAmount() <= int32(damage)) - { - // remember caster before aura delete - Unit* caster = triggeredByAura->GetCaster(); - - // Remove aura (before cast for prevent infinite loop handlers) - RemoveAurasDueToSpell(triggeredByAura->GetId()); - - // Cast finish spell (triggeredByAura already not exist!) - if (caster) - caster->CastSpell(this, 32865, true, castItem); - return true; // no hidden cooldown - } - // Damage counting - triggeredByAura->SetAmount(triggeredByAura->GetAmount() - damage); - return true; - } - switch (dummySpell->Id) - { - // Nightfall - case 18094: - case 18095: - // Glyph of corruption - case 56218: - { - target = this; - triggered_spell_id = 17941; - break; - } - // Soul Leech - case 30293: - case 30295: - case 30296: - { - // Improved Soul Leech - AuraEffectList const& SoulLeechAuras = GetAuraEffectsByType(SPELL_AURA_DUMMY); - for (Unit::AuraEffectList::const_iterator i = SoulLeechAuras.begin(); i != SoulLeechAuras.end(); ++i) - { - if ((*i)->GetId() == 54117 || (*i)->GetId() == 54118) - { - if ((*i)->GetEffIndex() != 0) - continue; - basepoints0 = int32((*i)->GetAmount()); - target = GetGuardianPet(); - if (target) - { - // regen mana for pet - CastCustomSpell(target, 54607, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - } - // regen mana for caster - CastCustomSpell(this, 59117, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - // Get second aura of spell for replenishment effect on party - if (AuraEffect const* aurEff = (*i)->GetBase()->GetEffect(EFFECT_1)) - { - // Replenishment - roll chance - if (roll_chance_i(aurEff->GetAmount())) - CastSpell(this, 57669, true, castItem, triggeredByAura); - } - break; - } - } - // health - basepoints0 = CalculatePct(int32(damage), triggerAmount); - target = this; - triggered_spell_id = 30294; - break; - } - // Shadowflame (Voidheart Raiment set bonus) - case 37377: - { - triggered_spell_id = 37379; - break; - } - // Pet Healing (Corruptor Raiment or Rift Stalker Armor) - case 37381: - { - target = GetGuardianPet(); - if (!target) - return false; - - // heal amount - basepoints0 = CalculatePct(int32(damage), triggerAmount); - triggered_spell_id = 37382; - break; - } - // Shadowflame Hellfire (Voidheart Raiment set bonus) - case 39437: - { - triggered_spell_id = 37378; - break; - } - // Glyph of Succubus - case 56250: - { - if (!target) - return false; - target->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE, ObjectGuid::Empty, target->GetAura(32409)); // SW:D shall not be removed. - target->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT); - target->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH); - return true; - } - } - break; - } - case SPELLFAMILY_PRIEST: - { - // Vampiric Touch - if (dummySpell->SpellFamilyFlags[1] & 0x00000400) - { - if (!victim || !victim->IsAlive()) - return false; - - if (effIndex != 0) - return false; - - // victim is caster of aura - if (triggeredByAura->GetCasterGUID() != victim->GetGUID()) - return false; - - // Energize 0.25% of max. mana - victim->CastSpell(victim, 57669, true, castItem, triggeredByAura); - return true; // no hidden cooldown - } - // Body and Soul - if (dummySpell->SpellIconID == 2218) - { - // Proc only from Abolish desease on self cast - if (!procSpell || procSpell->Id != 552 || victim != this || !roll_chance_i(triggerAmount)) - return false; - triggered_spell_id = 64136; - target = this; - break; - } - switch (dummySpell->Id) - { - // Vampiric Embrace - case 15286: - { - if (!victim || !victim->IsAlive() || !procSpell || procSpell->SpellFamilyFlags[1] & 0x80000) - return false; - - // heal amount - int32 total = CalculatePct(int32(damage), triggerAmount); - int32 team = total / 5; - int32 self = total - team; - CastCustomSpell(this, 15290, &team, &self, NULL, true, castItem, triggeredByAura); - return true; // no hidden cooldown - } - // Priest Tier 6 Trinket (Ashtongue Talisman of Acumen) - case 40438: - { - // Shadow Word: Pain - if (!procSpell) - return false; - else if (procSpell->SpellFamilyFlags[0] & 0x8000) - triggered_spell_id = 40441; - // Renew - else if (procSpell->SpellFamilyFlags[0] & 0x40) - triggered_spell_id = 40440; - else - return false; - - target = this; - break; - } - // Improved Shadowform - case 47570: - case 47569: - { - if (!roll_chance_i(triggerAmount)) - return false; - - RemoveMovementImpairingAuras(); - break; - } - // Glyph of Dispel Magic - case 55677: - { - // Dispel Magic shares spellfamilyflag with abolish disease - if (!procSpell || procSpell->SpellIconID != 74) - return false; - if (!target || !target->IsFriendlyTo(this)) - return false; - - basepoints0 = int32(target->CountPctFromMaxHealth(triggerAmount)); - triggered_spell_id = 56131; - break; - } - // Oracle Healing Bonus ("Garments of the Oracle" set) - case 26169: - { - // heal amount - basepoints0 = int32(CalculatePct(damage, 10)); - target = this; - triggered_spell_id = 26170; - break; - } - // Frozen Shadoweave (Shadow's Embrace set) warning! its not only priest set - case 39372: - { - if (!procSpell || (procSpell->GetSchoolMask() & (SPELL_SCHOOL_MASK_FROST | SPELL_SCHOOL_MASK_SHADOW)) == 0) - return false; - - // heal amount - basepoints0 = CalculatePct(int32(damage), triggerAmount); - target = this; - triggered_spell_id = 39373; - break; - } - // Greater Heal (Vestments of Faith (Priest Tier 3) - 4 pieces bonus) - case 28809: - { - triggered_spell_id = 28810; - break; - } - // Priest T10 Healer 2P Bonus - case 70770: - // Flash Heal - if (procSpell && procSpell->SpellFamilyFlags[0] & 0x800) - { - triggered_spell_id = 70772; - SpellInfo const* blessHealing = sSpellMgr->GetSpellInfo(triggered_spell_id); - if (!blessHealing) - return false; - basepoints0 = int32(CalculatePct(damage, triggerAmount) / (blessHealing->GetMaxDuration() / blessHealing->Effects[0].Amplitude)); - } - break; - } - break; - } - case SPELLFAMILY_DRUID: - { - switch (dummySpell->Id) - { - // Glyph of Innervate - case 54832: - { - if (!procSpell || procSpell->SpellIconID != 62) - return false; - - int32 mana_perc = triggeredByAura->GetSpellInfo()->Effects[triggeredByAura->GetEffIndex()].CalcValue(); - basepoints0 = int32(CalculatePct(GetCreatePowers(POWER_MANA), mana_perc) / 10); - triggered_spell_id = 54833; - target = this; - break; - } - // Glyph of Starfire - case 54845: - { - triggered_spell_id = 54846; - break; - } - // Glyph of Shred - case 54815: - { - if (!target) - return false; - - // try to find spell Rip on the target - if (AuraEffect const* AurEff = target->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DRUID, 0x00800000, 0x0, 0x0, GetGUID())) - { - // Rip's max duration, note: spells which modifies Rip's duration also counted like Glyph of Rip - uint32 CountMin = AurEff->GetBase()->GetMaxDuration(); - - // just Rip's max duration without other spells - uint32 CountMax = AurEff->GetSpellInfo()->GetMaxDuration(); - - // add possible auras' and Glyph of Shred's max duration - CountMax += 3 * triggerAmount * IN_MILLISECONDS; // Glyph of Shred -> +6 seconds - CountMax += HasAura(54818) ? 4 * IN_MILLISECONDS : 0; // Glyph of Rip -> +4 seconds - CountMax += HasAura(60141) ? 4 * IN_MILLISECONDS : 0; // Rip Duration/Lacerate Damage -> +4 seconds - - // if min < max -> that means caster didn't cast 3 shred yet - // so set Rip's duration and max duration - if (CountMin < CountMax) - { - AurEff->GetBase()->SetDuration(AurEff->GetBase()->GetDuration() + triggerAmount * IN_MILLISECONDS); - AurEff->GetBase()->SetMaxDuration(CountMin + triggerAmount * IN_MILLISECONDS); - return true; - } - } - // if not found Rip - return false; - } - // Glyph of Rake - case 54821: - { - if (procSpell && procSpell->SpellVisual[0] == 750 && procSpell->Effects[1].ApplyAuraName == 3) - { - if (target && target->GetTypeId() == TYPEID_UNIT) - { - triggered_spell_id = 54820; - break; - } - } - return false; - } - // Leader of the Pack - case 24932: - { - if (triggerAmount <= 0) - return false; - basepoints0 = int32(CountPctFromMaxHealth(triggerAmount)); - target = this; - triggered_spell_id = 34299; - if (triggeredByAura->GetCasterGUID() != GetGUID()) - break; - int32 basepoints1 = CalculatePct(GetMaxPower(Powers(POWER_MANA)), triggerAmount * 2); - // Improved Leader of the Pack - // Check cooldown of heal spell cooldown - if (!GetSpellHistory()->HasCooldown(34299)) - CastCustomSpell(this, 68285, &basepoints1, 0, 0, true, 0, triggeredByAura); - break; - } - // Healing Touch (Dreamwalker Raiment set) - case 28719: - { - if (procSpell) - { - // mana back - basepoints0 = int32(CalculatePct(procSpell->ManaCost, 30)); - target = this; - triggered_spell_id = 28742; - } - break; - } - // Glyph of Rejuvenation - case 54754: - { - if (!victim || !victim->HealthBelowPct(uint32(triggerAmount))) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - triggered_spell_id = 54755; - break; - } - // Healing Touch Refund (Idol of Longevity trinket) - case 28847: - { - target = this; - triggered_spell_id = 28848; - break; - } - // Mana Restore (Malorne Raiment set / Malorne Regalia set) - case 37288: - case 37295: - { - target = this; - triggered_spell_id = 37238; - break; - } - // Druid Tier 6 Trinket - case 40442: - { - float chance; - - if (!procSpell) - return false; - // Starfire - else if (procSpell->SpellFamilyFlags[0] & 0x4) - { - triggered_spell_id = 40445; - chance = 25.0f; - } - // Rejuvenation - else if (procSpell->SpellFamilyFlags[0] & 0x10) - { - triggered_spell_id = 40446; - chance = 25.0f; - } - // Mangle (Bear) and Mangle (Cat) - else if (procSpell->SpellFamilyFlags[1] & 0x00000440) - { - triggered_spell_id = 40452; - chance = 40.0f; - } - else - return false; - - if (!roll_chance_f(chance)) - return false; - - target = this; - break; - } - // Maim Interrupt - case 44835: - { - // Deadly Interrupt Effect - triggered_spell_id = 32747; - break; - } - // Item - Druid T10 Balance 4P Bonus - case 70723: - { - // Wrath & Starfire - if (procSpell && (procSpell->SpellFamilyFlags[0] & 0x5) && (procEx & PROC_EX_CRITICAL_HIT)) - { - triggered_spell_id = 71023; - SpellInfo const* triggeredSpell = sSpellMgr->GetSpellInfo(triggered_spell_id); - if (!triggeredSpell) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount) / (triggeredSpell->GetMaxDuration() / triggeredSpell->Effects[0].Amplitude); - // Add remaining ticks to damage done - basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_DAMAGE); - } - break; - } - // Item - Druid T10 Restoration 4P Bonus (Rejuvenation) - case 70664: - { - // Proc only from normal Rejuvenation - if (!procSpell || procSpell->SpellVisual[0] != 32) - return false; - - Player* caster = ToPlayer(); - if (!caster) - return false; - if (!caster->GetGroup() && victim == this) - return false; - - CastCustomSpell(70691, SPELLVALUE_BASE_POINT0, damage, victim, true); - return true; - } - } - break; - } - case SPELLFAMILY_ROGUE: - { - switch (dummySpell->Id) - { - case 56800: // Glyph of Backstab - { - triggered_spell_id = 63975; - break; - } - case 32748: // Deadly Throw Interrupt - { - // Prevent cast Deadly Throw Interrupt on self from last effect (apply dummy) of Deadly Throw - if (this == victim) - return false; - - triggered_spell_id = 32747; - break; - } - } - - switch (dummySpell->SpellIconID) - { - case 2116: // Quick Recovery - { - if (!procSpell) - return false; - - // energy cost save - basepoints0 = CalculatePct(int32(procSpell->ManaCost), triggerAmount); - if (basepoints0 <= 0) - return false; - - target = this; - triggered_spell_id = 31663; - break; - } - case 2909: // Cut to the Chase - { - // "refresh your Slice and Dice duration to its 5 combo point maximum" - // lookup Slice and Dice - if (AuraEffect const* aur = GetAuraEffect(SPELL_AURA_MOD_MELEE_HASTE, SPELLFAMILY_ROGUE, 0x40000, 0, 0)) - { - aur->GetBase()->SetDuration(aur->GetSpellInfo()->GetMaxDuration(), true); - return true; - } - return false; - } - case 2963: // Deadly Brew - { - triggered_spell_id = 3409; - break; - } - } - break; - } - case SPELLFAMILY_HUNTER: - { - switch (dummySpell->SpellIconID) - { - case 2236: // Thrill of the Hunt - { - if (!procSpell) - return false; - - Spell* spell = ToPlayer()->m_spellModTakingSpell; - - // Disable charge drop because of Lock and Load - ToPlayer()->SetSpellModTakingSpell(spell, false); - - // Explosive Shot - if (procSpell->SpellFamilyFlags[2] & 0x200) - { - if (!victim) - return false; - if (AuraEffect const* pEff = victim->GetAuraEffect(SPELL_AURA_PERIODIC_DUMMY, SPELLFAMILY_HUNTER, 0x0, 0x80000000, 0x0, GetGUID())) - basepoints0 = pEff->GetSpellInfo()->CalcPowerCost(this, SpellSchoolMask(pEff->GetSpellInfo()->SchoolMask)) * 4/10/3; - } - else - basepoints0 = procSpell->CalcPowerCost(this, SpellSchoolMask(procSpell->SchoolMask)) * 4/10; - - ToPlayer()->SetSpellModTakingSpell(spell, true); - - if (basepoints0 <= 0) - return false; - - target = this; - triggered_spell_id = 34720; - break; - } - case 3406: // Hunting Party - { - triggered_spell_id = 57669; - target = this; - break; - } - case 3560: // Rapid Recuperation - { - // This effect only from Rapid Killing (mana regen) - if (!procSpell || !(procSpell->SpellFamilyFlags[1] & 0x01000000)) - return false; - - target = this; - - switch (dummySpell->Id) - { - case 53228: // Rank 1 - triggered_spell_id = 56654; - break; - case 53232: // Rank 2 - triggered_spell_id = 58882; - break; - } - break; - } - } - - switch (dummySpell->Id) - { - case 57870: // Glyph of Mend Pet - { - victim->CastSpell(victim, 57894, true, NULL, NULL, GetGUID()); - return true; - } - } - break; - } - case SPELLFAMILY_PALADIN: - { - // Judgements of the Wise - if (dummySpell->SpellIconID == 3017) - { - target = this; - triggered_spell_id = 31930; - // replenishment - CastSpell(this, 57669, true, castItem, triggeredByAura); - break; - } - // Righteous Vengeance - if (dummySpell->SpellIconID == 3025) - { - // 4 damage tick - basepoints0 = triggerAmount * damage / 400; - triggered_spell_id = 61840; - // Add remaining ticks to damage done - basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_DAMAGE); - break; - } - // Sheath of Light - if (dummySpell->SpellIconID == 3030) - { - // 4 healing tick - basepoints0 = triggerAmount * damage / 400; - triggered_spell_id = 54203; - // Add remaining ticks to healing done - basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_HEAL); - break; - } - switch (dummySpell->Id) - { - // Sacred Shield - case 53601: - { - if (procFlag & PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS) - return false; - - if (damage > 0) - triggered_spell_id = 58597; - - // Item - Paladin T8 Holy 4P Bonus - if (Unit* caster = triggeredByAura->GetCaster()) - if (AuraEffect const* aurEff = caster->GetAuraEffect(64895, 0)) - cooldown = Milliseconds(aurEff->GetAmount()); - - target = this; - break; - } - // Heart of the Crusader - case 20335: // rank 1 - triggered_spell_id = 21183; - break; - case 20336: // rank 2 - triggered_spell_id = 54498; - break; - case 20337: // rank 3 - triggered_spell_id = 54499; - break; - // Judgement of Light - case 20185: - { - if (!victim) - return false; - - // 2% of maximum health - basepoints0 = int32(victim->CountPctFromMaxHealth(2)); - victim->CastCustomSpell(victim, 20267, &basepoints0, 0, 0, true, 0, triggeredByAura); - return true; - } - // Judgement of Wisdom - case 20186: - { - if (victim && victim->IsAlive() && victim->getPowerType() == POWER_MANA) - { - // 2% of base mana - basepoints0 = int32(CalculatePct(victim->GetCreateMana(), 2)); - victim->CastCustomSpell(victim, 20268, &basepoints0, NULL, NULL, true, 0, triggeredByAura); - } - return true; - } - // Holy Power (Redemption Armor set) - case 28789: - { - if (!victim) - return false; - - // Set class defined buff - switch (victim->getClass()) - { - case CLASS_PALADIN: - case CLASS_PRIEST: - case CLASS_SHAMAN: - case CLASS_DRUID: - triggered_spell_id = 28795; // Increases the friendly target's mana regeneration by $s1 per 5 sec. for $d. - break; - case CLASS_MAGE: - case CLASS_WARLOCK: - triggered_spell_id = 28793; // Increases the friendly target's spell damage and healing by up to $s1 for $d. - break; - case CLASS_HUNTER: - case CLASS_ROGUE: - triggered_spell_id = 28791; // Increases the friendly target's attack power by $s1 for $d. - break; - case CLASS_WARRIOR: - triggered_spell_id = 28790; // Increases the friendly target's armor - break; - default: - return false; - } - break; - } - // Seal of Vengeance (damage calc on apply aura) - case 31801: - { - if (effIndex != 0) // effect 1, 2 used by seal unleashing code - return false; - - // At melee attack or Hammer of the Righteous spell damage considered as melee attack - bool stacker = procSpell ? procSpell->Id == 53595 : true; - // spells with SPELL_DAMAGE_CLASS_MELEE excluding Judgements - bool damager = procSpell ? (procSpell->EquippedItemClass != -1 || (procSpell->SpellIconID == 243 && procSpell->SpellVisual[0] == 39)) : false; - - if (!stacker && !damager) - return false; - - triggered_spell_id = 31803; - - // On target with 5 stacks of Holy Vengeance direct damage is done - if (Aura* aur = victim->GetAura(triggered_spell_id, GetGUID())) - { - if (aur->GetStackAmount() == 5) - { - if (stacker) - aur->RefreshDuration(); - CastSpell(victim, 42463, true); - return true; - } - } - - if (!stacker) - return false; - break; - } - // Seal of Corruption - case 53736: - { - if (effIndex != 0) // effect 1, 2 used by seal unleashing code - return false; - - // At melee attack or Hammer of the Righteous spell damage considered as melee attack - bool stacker = procSpell ? procSpell->Id == 53595 : true; - // spells with SPELL_DAMAGE_CLASS_MELEE excluding Judgements - bool damager = procSpell ? (procSpell->EquippedItemClass != -1 || (procSpell->SpellIconID == 243 && procSpell->SpellVisual[0] == 39)) : false; - - if (!stacker && !damager) - return false; - - triggered_spell_id = 53742; - - // On target with 5 stacks of Blood Corruption direct damage is done - if (Aura* aur = victim->GetAura(triggered_spell_id, GetGUID())) - { - if (aur->GetStackAmount() == 5) - { - if (stacker) - aur->RefreshDuration(); - CastSpell(victim, 53739, true); - return true; - } - } - - if (!stacker) - return false; - break; - } - // Spiritual Attunement - case 31785: - case 33776: - { - // if healed by another unit (victim) - if (this == victim) - return false; - - // heal amount - basepoints0 = int32(CalculatePct(std::min(damage, GetMaxHealth() - GetHealth()), triggerAmount)); - target = this; - - if (basepoints0) - triggered_spell_id = 31786; - break; - } - // Paladin Tier 6 Trinket (Ashtongue Talisman of Zeal) - case 40470: - { - if (!procSpell) - return false; - - float chance; - - // Flash of light/Holy light - if (procSpell->SpellFamilyFlags[0] & 0xC0000000) - { - triggered_spell_id = 40471; - chance = 15.0f; - } - // Judgement (any) - else if (procSpell->GetSpellSpecific() == SPELL_SPECIFIC_JUDGEMENT) - { - triggered_spell_id = 40472; - chance = 50.0f; - } - else - return false; - - if (!roll_chance_f(chance)) - return false; - - break; - } - // Glyph of Holy Light - case 54937: - { - triggered_spell_id = 54968; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - break; - } - // Item - Paladin T8 Holy 2P Bonus - case 64890: - { - triggered_spell_id = 64891; - basepoints0 = triggerAmount * damage / 300; - break; - } - case 71406: // Tiny Abomination in a Jar - case 71545: // Tiny Abomination in a Jar (Heroic) - { - if (!victim || !victim->IsAlive()) - return false; - - CastSpell(this, 71432, true, NULL, triggeredByAura); - - Aura const* dummy = GetAura(71432); - if (!dummy || dummy->GetStackAmount() < (dummySpell->Id == 71406 ? 8 : 7)) - return false; - - RemoveAurasDueToSpell(71432); - triggered_spell_id = 71433; // default main hand attack - // roll if offhand - if (Player const* player = ToPlayer()) - if (player->GetWeaponForAttack(OFF_ATTACK, true) && urand(0, 1)) - triggered_spell_id = 71434; - target = victim; - break; - } - // Item - Icecrown 25 Normal Dagger Proc - case 71880: - { - switch (getPowerType()) - { - case POWER_MANA: - triggered_spell_id = 71881; - break; - case POWER_RAGE: - triggered_spell_id = 71883; - break; - case POWER_ENERGY: - triggered_spell_id = 71882; - break; - case POWER_RUNIC_POWER: - triggered_spell_id = 71884; - break; - default: - return false; - } - break; - } - // Item - Icecrown 25 Heroic Dagger Proc - case 71892: - { - switch (getPowerType()) - { - case POWER_MANA: - triggered_spell_id = 71888; - break; - case POWER_RAGE: - triggered_spell_id = 71886; - break; - case POWER_ENERGY: - triggered_spell_id = 71887; - break; - case POWER_RUNIC_POWER: - triggered_spell_id = 71885; - break; - default: - return false; - } - break; - } - } - break; - } - case SPELLFAMILY_SHAMAN: - { - switch (dummySpell->Id) - { - // Tidal Force - case 55198: - { - // Remove aura stack from caster - RemoveAuraFromStack(55166); - // drop charges - return false; - } - // Totemic Power (The Earthshatterer set) - case 28823: - { - if (!victim) - return false; - - // Set class defined buff - switch (victim->getClass()) - { - case CLASS_PALADIN: - case CLASS_PRIEST: - case CLASS_SHAMAN: - case CLASS_DRUID: - triggered_spell_id = 28824; // Increases the friendly target's mana regeneration by $s1 per 5 sec. for $d. - break; - case CLASS_MAGE: - case CLASS_WARLOCK: - triggered_spell_id = 28825; // Increases the friendly target's spell damage and healing by up to $s1 for $d. - break; - case CLASS_HUNTER: - case CLASS_ROGUE: - triggered_spell_id = 28826; // Increases the friendly target's attack power by $s1 for $d. - break; - case CLASS_WARRIOR: - triggered_spell_id = 28827; // Increases the friendly target's armor - break; - default: - return false; - } - break; - } - // Lesser Healing Wave (Totem of Flowing Water Relic) - case 28849: - { - target = this; - triggered_spell_id = 28850; - break; - } - // Windfury Weapon (Passive) 1-8 Ranks - case 33757: - { - Player* player = ToPlayer(); - if (!player || !castItem || !castItem->IsEquipped() || !victim || !victim->IsAlive()) - return false; - - if (triggeredByAura->GetBase() && castItem->GetGUID() != triggeredByAura->GetBase()->GetCastItemGUID()) - return false; - - WeaponAttackType attType = WeaponAttackType(player->GetAttackBySlot(castItem->GetSlot())); - if ((attType != BASE_ATTACK && attType != OFF_ATTACK) - || (attType == BASE_ATTACK && procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) - || (attType == OFF_ATTACK && procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK)) - return false; - - // Now compute real proc chance... - uint32 chance = 20; - player->ApplySpellMod(dummySpell->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance); - - Item* addWeapon = player->GetWeaponForAttack(attType == BASE_ATTACK ? OFF_ATTACK : BASE_ATTACK, true); - uint32 enchant_id_add = addWeapon ? addWeapon->GetEnchantmentId(EnchantmentSlot(TEMP_ENCHANTMENT_SLOT)) : 0; - SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id_add); - if (pEnchant && pEnchant->spellid[0] == dummySpell->Id) - chance += 14; - - if (!roll_chance_i(chance)) - return false; - - // Now amount of extra power stored in 1 effect of Enchant spell - // Get it by item enchant id - uint32 spellId; - switch (castItem->GetEnchantmentId(EnchantmentSlot(TEMP_ENCHANTMENT_SLOT))) - { - case 283: spellId = 8232; break; // 1 Rank - case 284: spellId = 8235; break; // 2 Rank - case 525: spellId = 10486; break; // 3 Rank - case 1669:spellId = 16362; break; // 4 Rank - case 2636:spellId = 25505; break; // 5 Rank - case 3785:spellId = 58801; break; // 6 Rank - case 3786:spellId = 58803; break; // 7 Rank - case 3787:spellId = 58804; break; // 8 Rank - default: - { - TC_LOG_ERROR("entities.unit", "Unit::HandleDummyAuraProc: non handled item enchantment (rank?) %u for spell id: %u (Windfury)", - castItem->GetEnchantmentId(EnchantmentSlot(TEMP_ENCHANTMENT_SLOT)), dummySpell->Id); - return false; - } - } - - SpellInfo const* windfurySpellInfo = sSpellMgr->GetSpellInfo(spellId); - if (!windfurySpellInfo) - { - TC_LOG_ERROR("entities.unit", "Unit::HandleDummyAuraProc: non-existing spell id: %u (Windfury)", spellId); - return false; - } - - int32 extra_attack_power = CalculateSpellDamage(victim, windfurySpellInfo, 1); - - // Value gained from additional AP - basepoints0 = int32(extra_attack_power / 14.0f * GetAttackTime(attType) / 1000); - - if (procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK) - triggered_spell_id = 25504; - - if (procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) - triggered_spell_id = 33750; - - // apply cooldown before cast to prevent processing itself - triggeredByAura->GetBase()->AddProcCooldown(std::chrono::steady_clock::now() + cooldown); - cooldown = Milliseconds::zero(); - - // Attack Twice - for (uint32 i = 0; i < 2; ++i) - CastCustomSpell(victim, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - - return true; - } - // Shaman Tier 6 Trinket - case 40463: - { - if (!procSpell) - return false; - - float chance; - if (procSpell->SpellFamilyFlags[0] & 0x1) - { - triggered_spell_id = 40465; // Lightning Bolt - chance = 15.0f; - } - else if (procSpell->SpellFamilyFlags[0] & 0x80) - { - triggered_spell_id = 40465; // Lesser Healing Wave - chance = 10.0f; - } - else if (procSpell->SpellFamilyFlags[1] & 0x00000010) - { - triggered_spell_id = 40466; // Stormstrike - chance = 50.0f; - } - else - return false; - - if (!roll_chance_f(chance)) - return false; - - target = this; - break; - } - // Glyph of Healing Wave - case 55440: - { - // Not proc from self heals - if (this == victim) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - target = this; - triggered_spell_id = 55533; - break; - } - // Spirit Hunt - case 58877: - { - // Cast on owner - target = GetOwner(); - if (!target) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - triggered_spell_id = 58879; - // Cast on spirit wolf - CastCustomSpell(this, triggered_spell_id, &basepoints0, NULL, NULL, true, NULL, triggeredByAura); - break; - } - // Shaman T8 Elemental 4P Bonus - case 64928: - { - basepoints0 = CalculatePct(int32(damage), triggerAmount); - triggered_spell_id = 64930; // Electrified - break; - } - // Shaman T9 Elemental 4P Bonus - case 67228: - { - // Lava Burst - if (procSpell && procSpell->SpellFamilyFlags[1] & 0x1000) - { - triggered_spell_id = 71824; - SpellInfo const* triggeredSpell = sSpellMgr->GetSpellInfo(triggered_spell_id); - if (!triggeredSpell) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount) / (triggeredSpell->GetMaxDuration() / triggeredSpell->Effects[0].Amplitude); - } - break; - } - // Item - Shaman T10 Restoration 4P Bonus - case 70808: - { - // Chain Heal - if (procSpell && (procSpell->SpellFamilyFlags[0] & 0x100) && (procEx & PROC_EX_CRITICAL_HIT)) - { - triggered_spell_id = 70809; - SpellInfo const* triggeredSpell = sSpellMgr->GetSpellInfo(triggered_spell_id); - if (!triggeredSpell) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount) / (triggeredSpell->GetMaxDuration() / triggeredSpell->Effects[0].Amplitude); - // Add remaining ticks to healing done - basepoints0 += GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_HEAL); - } - break; - } - // Item - Shaman T10 Elemental 4P Bonus - case 70817: - { - if (!target) - return false; - // try to find spell Flame Shock on the target - if (AuraEffect const* aurEff = target->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_SHAMAN, 0x10000000, 0x0, 0x0, GetGUID())) - { - Aura* flameShock = aurEff->GetBase(); - int32 maxDuration = flameShock->GetMaxDuration(); - int32 newDuration = flameShock->GetDuration() + 2 * aurEff->GetAmplitude(); - - flameShock->SetDuration(newDuration); - // is it blizzlike to change max duration for FS? - if (newDuration > maxDuration) - flameShock->SetMaxDuration(newDuration); - - return true; - } - // if not found Flame Shock - return false; - } - case 63280: // Glyph of Totem of Wrath - { - if (!procSpell || procSpell->SpellIconID != 2019) - return false; - - if (Creature* totem = GetMap()->GetCreature(m_SummonSlot[1])) // Fire totem summon slot - { - if (SpellInfo const* totemSpell = sSpellMgr->GetSpellInfo(totem->m_spells[0])) - { - int32 bp0 = CalculatePct(totemSpell->Effects[EFFECT_0].CalcValue(), triggerAmount); - int32 bp1 = CalculatePct(totemSpell->Effects[EFFECT_1].CalcValue(), triggerAmount); - CastCustomSpell(this, 63283, &bp0, &bp1, NULL, true); - return true; - } - } - return false; - } - break; - } - // Frozen Power - if (dummySpell->SpellIconID == 3780) - { - if (!target) - return false; - if (GetDistance(target) < 15.0f) - return false; - float chance = (float)triggerAmount; - if (!roll_chance_f(chance)) - return false; - - triggered_spell_id = 63685; - break; - } - // Ancestral Awakening - if (dummySpell->SpellIconID == 3065) - { - triggered_spell_id = 52759; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - target = this; - break; - } - // Flametongue Weapon (Passive) - if (dummySpell->SpellFamilyFlags[0] & 0x200000) - { - if (GetTypeId() != TYPEID_PLAYER || !victim || !victim->IsAlive() || !castItem || !castItem->IsEquipped()) - return false; - - WeaponAttackType attType = WeaponAttackType(Player::GetAttackBySlot(castItem->GetSlot())); - if ((attType != BASE_ATTACK && attType != OFF_ATTACK) - || (attType == BASE_ATTACK && procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) - || (attType == OFF_ATTACK && procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK)) - return false; - - float fire_onhit = float(CalculatePct(dummySpell->Effects[EFFECT_0]. CalcValue(), 1.0f)); - - float add_spellpower = (float)(SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_FIRE) - + victim->SpellBaseDamageBonusTaken(SPELL_SCHOOL_MASK_FIRE)); - - // 1.3speed = 5%, 2.6speed = 10%, 4.0 speed = 15%, so, 1.0speed = 3.84% - ApplyPct(add_spellpower, 3.84f); - - // Enchant on Off-Hand and ready? - if (castItem->GetSlot() == EQUIPMENT_SLOT_OFFHAND && procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) - { - float BaseWeaponSpeed = GetAttackTime(OFF_ATTACK) / 1000.0f; - - // Value1: add the tooltip damage by swingspeed + Value2: add spelldmg by swingspeed - basepoints0 = int32((fire_onhit * BaseWeaponSpeed) + (add_spellpower * BaseWeaponSpeed)); - triggered_spell_id = 10444; - } - - // Enchant on Main-Hand and ready? - else if (castItem->GetSlot() == EQUIPMENT_SLOT_MAINHAND && procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK) - { - float BaseWeaponSpeed = GetAttackTime(BASE_ATTACK) / 1000.0f; - - // Value1: add the tooltip damage by swingspeed + Value2: add spelldmg by swingspeed - basepoints0 = int32((fire_onhit * BaseWeaponSpeed) + (add_spellpower * BaseWeaponSpeed)); - triggered_spell_id = 10444; - } - - // If not ready, we should return, shouldn't we?! - else - return false; - - CastCustomSpell(victim, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - return true; - } - // Improved Water Shield - if (dummySpell->SpellIconID == 2287) - { - // Default chance for Healing Wave and Riptide - float chance = (float)triggeredByAura->GetAmount(); - - if (!procSpell) - return false; //This is the same than putting chance *= 0.0f; - else if (procSpell->SpellFamilyFlags[0] & 0x80) - // Lesser Healing Wave - 0.6 of default - chance *= 0.6f; - else if (procSpell->SpellFamilyFlags[0] & 0x100) - // Chain heal - 0.3 of default - chance *= 0.3f; - - if (!roll_chance_f(chance)) - return false; - - // Water Shield - if (AuraEffect const* aurEff = GetAuraEffect(SPELL_AURA_PROC_TRIGGER_SPELL, SPELLFAMILY_SHAMAN, 0, 0x00000020, 0)) - { - uint32 spell = aurEff->GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell; - CastSpell(this, spell, true, castItem, triggeredByAura); - return true; - } - return false; - } - // Lightning Overload - if (dummySpell->SpellIconID == 2018) // only this spell has SpellFamily Shaman SpellIconID == 2018 and dummy aura - { - if (!procSpell || GetTypeId() != TYPEID_PLAYER || !victim) - return false; - - uint32 spellId = 0; - // Every Lightning Bolt and Chain Lightning spell have duplicate vs half damage and zero cost - switch (procSpell->Id) - { - // Lightning Bolt - case 403: spellId = 45284; break; // Rank 1 - case 529: spellId = 45286; break; // Rank 2 - case 548: spellId = 45287; break; // Rank 3 - case 915: spellId = 45288; break; // Rank 4 - case 943: spellId = 45289; break; // Rank 5 - case 6041: spellId = 45290; break; // Rank 6 - case 10391: spellId = 45291; break; // Rank 7 - case 10392: spellId = 45292; break; // Rank 8 - case 15207: spellId = 45293; break; // Rank 9 - case 15208: spellId = 45294; break; // Rank 10 - case 25448: spellId = 45295; break; // Rank 11 - case 25449: spellId = 45296; break; // Rank 12 - case 49237: spellId = 49239; break; // Rank 13 - case 49238: spellId = 49240; break; // Rank 14 - // Chain Lightning - case 421: spellId = 45297; break; // Rank 1 - case 930: spellId = 45298; break; // Rank 2 - case 2860: spellId = 45299; break; // Rank 3 - case 10605: spellId = 45300; break; // Rank 4 - case 25439: spellId = 45301; break; // Rank 5 - case 25442: spellId = 45302; break; // Rank 6 - case 49270: spellId = 49268; break; // Rank 7 - case 49271: spellId = 49269; break; // Rank 8 - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleDummyAuraProc: non handled spell id: %u (LO)", procSpell->Id); - return false; - } - - // Chain Lightning - if (procSpell->SpellFamilyFlags[0] & 0x2) - { - // Chain lightning has [LightOverload_Proc_Chance] / [Max_Number_of_Targets] chance to proc of each individual target hit. - // A maxed LO would have a 33% / 3 = 11% chance to proc of each target. - // LO chance was already "accounted" at the proc chance roll, now need to divide the chance by [Max_Number_of_Targets] - float chance = 100.0f / procSpell->Effects[effIndex].ChainTarget; - if (!roll_chance_f(chance)) - return false; - } - - CastSpell(victim, spellId, true, castItem, triggeredByAura); - return true; - } - // Static Shock - if (dummySpell->SpellIconID == 3059) - { - // Lightning Shield - if (AuraEffect const* aurEff = GetAuraEffect(SPELL_AURA_PROC_TRIGGER_SPELL, SPELLFAMILY_SHAMAN, 0x400, 0, 0)) - { - uint32 spell = sSpellMgr->GetSpellWithRank(26364, aurEff->GetSpellInfo()->GetRank()); - - // custom cooldown processing case - if (GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(spell)) - GetSpellHistory()->ResetCooldown(spell); - - CastSpell(target, spell, true, castItem, triggeredByAura); - aurEff->GetBase()->DropCharge(); - return true; - } - return false; - } - break; - } - case SPELLFAMILY_DEATHKNIGHT: - { - // Blood-Caked Strike - Blood-Caked Blade - if (dummySpell->SpellIconID == 138) - { - if (!target || !target->IsAlive()) - return false; - - triggered_spell_id = dummySpell->Effects[effIndex].TriggerSpell; - break; - } - // Improved Blood Presence - if (dummySpell->SpellIconID == 2636) - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - break; - } - // Butchery - if (dummySpell->SpellIconID == 2664) - { - basepoints0 = triggerAmount; - triggered_spell_id = 50163; - target = this; - break; - } - // Dancing Rune Weapon - if (dummySpell->Id == 49028) - { - // 1 dummy aura for dismiss rune blade - if (effIndex != 1) - return false; - - Unit* pPet = NULL; - for (ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) // Find Rune Weapon - if ((*itr)->GetEntry() == 27893) - { - pPet = *itr; - break; - } - - if (pPet && pPet->GetVictim() && damage && procSpell) - { - uint32 procDmg = damage / 2; - pPet->SendSpellNonMeleeDamageLog(pPet->GetVictim(), procSpell->Id, procDmg, procSpell->GetSchoolMask(), 0, 0, false, 0, false); - pPet->DealDamage(pPet->GetVictim(), procDmg, NULL, SPELL_DIRECT_DAMAGE, procSpell->GetSchoolMask(), procSpell, true); - break; - } - else - return false; - } - // Mark of Blood - if (dummySpell->Id == 49005) - { - /// @todo need more info (cooldowns/PPM) - triggered_spell_id = 61607; - break; - } - // Unholy Blight - if (dummySpell->Id == 49194) - { - triggered_spell_id = 50536; - SpellInfo const* unholyBlight = sSpellMgr->GetSpellInfo(triggered_spell_id); - if (!unholyBlight) - return false; - - basepoints0 = CalculatePct(int32(damage), triggerAmount); - - //Glyph of Unholy Blight - if (AuraEffect* glyph=GetAuraEffect(63332, 0)) - AddPct(basepoints0, glyph->GetAmount()); - - basepoints0 = basepoints0 / (unholyBlight->GetMaxDuration() / unholyBlight->Effects[0].Amplitude); - basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_DAMAGE); - break; - } - // Vendetta - if (dummySpell->SpellFamilyFlags[0] & 0x10000) - { - basepoints0 = int32(CountPctFromMaxHealth(triggerAmount)); - triggered_spell_id = 50181; - target = this; - break; - } - // Necrosis - if (dummySpell->SpellIconID == 2709) - { - basepoints0 = CalculatePct(int32(damage), triggerAmount); - triggered_spell_id = 51460; - break; - } - // Threat of Thassarian - if (dummySpell->SpellIconID == 2023) - { - // Must Dual Wield - if (!procSpell || !haveOffhandWeapon()) - return false; - // Chance as basepoints for dummy aura - if (!roll_chance_i(triggerAmount)) - return false; - - switch (procSpell->Id) - { - // Obliterate - case 49020: triggered_spell_id = 66198; break; // Rank 1 - case 51423: triggered_spell_id = 66972; break; // Rank 2 - case 51424: triggered_spell_id = 66973; break; // Rank 3 - case 51425: triggered_spell_id = 66974; break; // Rank 4 - - // Frost Strike - case 49143: triggered_spell_id = 66196; break; // Rank 1 - case 51416: triggered_spell_id = 66958; break; // Rank 2 - case 51417: triggered_spell_id = 66959; break; // Rank 3 - case 51418: triggered_spell_id = 66960; break; // Rank 4 - case 51419: triggered_spell_id = 66961; break; // Rank 5 - case 55268: triggered_spell_id = 66962; break; // Rank 6 - - // Plague Strike - case 45462: triggered_spell_id = 66216; break; // Rank 1 - case 49917: triggered_spell_id = 66988; break; // Rank 2 - case 49918: triggered_spell_id = 66989; break; // Rank 3 - case 49919: triggered_spell_id = 66990; break; // Rank 4 - case 49920: triggered_spell_id = 66991; break; // Rank 5 - case 49921: triggered_spell_id = 66992; break; // Rank 6 - - // Death Strike - case 49998: triggered_spell_id = 66188; break; // Rank 1 - case 49999: triggered_spell_id = 66950; break; // Rank 2 - case 45463: triggered_spell_id = 66951; break; // Rank 3 - case 49923: triggered_spell_id = 66952; break; // Rank 4 - case 49924: triggered_spell_id = 66953; break; // Rank 5 - - // Rune Strike - case 56815: triggered_spell_id = 66217; break; // Rank 1 - - // Blood Strike - case 45902: triggered_spell_id = 66215; break; // Rank 1 - case 49926: triggered_spell_id = 66975; break; // Rank 2 - case 49927: triggered_spell_id = 66976; break; // Rank 3 - case 49928: triggered_spell_id = 66977; break; // Rank 4 - case 49929: triggered_spell_id = 66978; break; // Rank 5 - case 49930: triggered_spell_id = 66979; break; // Rank 6 - default: - return false; - } - break; - } - // Runic Power Back on Snare/Root - if (dummySpell->Id == 61257) - { - // only for spells and hit/crit (trigger start always) and not start from self cast spells - if (!procSpell || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == victim) - return false; - // Need snare or root mechanic - if (!(procSpell->GetAllEffectsMechanicMask() & ((1<<MECHANIC_ROOT)|(1<<MECHANIC_SNARE)))) - return false; - triggered_spell_id = 61258; - target = this; - break; - } - // Wandering Plague - if (dummySpell->SpellIconID == 1614) - { - if (!roll_chance_f(GetUnitCriticalChance(BASE_ATTACK, victim))) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - triggered_spell_id = 50526; - break; - } - // Sudden Doom - if (dummySpell->SpellIconID == 1939 && GetTypeId() == TYPEID_PLAYER) - { - SpellChainNode const* chain = NULL; - // get highest rank of the Death Coil spell - PlayerSpellMap const& sp_list = ToPlayer()->GetSpellMap(); - for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) - { - // check if shown in spell book - if (!itr->second->active || itr->second->disabled || itr->second->state == PLAYERSPELL_REMOVED) - continue; - - SpellInfo const* spellProto = sSpellMgr->GetSpellInfo(itr->first); - if (!spellProto) - continue; - - if (spellProto->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT - && spellProto->SpellFamilyFlags[0] & 0x2000) - { - SpellChainNode const* newChain = sSpellMgr->GetSpellChainNode(itr->first); - - // No chain entry or entry lower than found entry - if (!chain || !newChain || (chain->rank < newChain->rank)) - { - triggered_spell_id = itr->first; - chain = newChain; - } - else - continue; - // Found spell is last in chain - do not need to look more - // Optimisation for most common case - if (chain && chain->last->Id == itr->first) - break; - } - } - } - break; - } - case SPELLFAMILY_POTION: - { - // alchemist's stone - if (procSpell && dummySpell->Id == 17619) - { - if (procSpell->SpellFamilyName == SPELLFAMILY_POTION) - { - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; i++) - { - if (procSpell->Effects[i].Effect == SPELL_EFFECT_HEAL) - { - triggered_spell_id = 21399; - } - else if (procSpell->Effects[i].Effect == SPELL_EFFECT_ENERGIZE) - { - triggered_spell_id = 21400; - } - else - continue; - - basepoints0 = int32(CalculateSpellDamage(this, procSpell, i) * 0.4f); - CastCustomSpell(this, triggered_spell_id, &basepoints0, NULL, NULL, true, NULL, triggeredByAura); - } - return true; - } - } - break; - } - case SPELLFAMILY_PET: - { - switch (dummySpell->SpellIconID) - { - // Guard Dog - case 201: - { - if (!victim || !procSpell) - return false; - - triggered_spell_id = 54445; - target = this; - float addThreat = float(CalculatePct(procSpell->Effects[0].CalcValue(this), triggerAmount)); - victim->AddThreat(this, addThreat); - break; - } - // Silverback - case 1582: - triggered_spell_id = dummySpell->Id == 62765 ? 62801 : 62800; - target = this; - break; - } - break; - } - default: - break; - } - - // if not handled by custom case, get triggered spell from dummySpell proto - if (!triggered_spell_id) - triggered_spell_id = dummySpell->Effects[triggeredByAura->GetEffIndex()].TriggerSpell; - - // processed charge only counting case - if (!triggered_spell_id) - return true; - - SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id); - if (!triggerEntry) - { - TC_LOG_ERROR("entities.unit", "Unit::HandleDummyAuraProc: Spell %u has non-existing triggered spell %u", dummySpell->Id, triggered_spell_id); - return false; - } - - if (basepoints0) - CastCustomSpell(target, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura, originalCaster); - else - CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura, originalCaster); - - return true; -} - -// Used in case when access to whole aura is needed -// All procs should be handled like this... -bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 /*procFlag*/, uint32 procEx, bool* handled) -{ - SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo(); - - switch (dummySpell->SpellFamilyName) - { - case SPELLFAMILY_GENERIC: - switch (dummySpell->Id) - { - // Nevermelting Ice Crystal - case 71564: - RemoveAuraFromStack(71564); - *handled = true; - break; - // Gaseous Bloat - case 70672: - case 72455: - case 72832: - case 72833: - { - *handled = true; - uint32 stack = triggeredByAura->GetStackAmount(); - int32 const mod = (GetMap()->GetSpawnMode() & 1) ? 1500 : 1250; - int32 dmg = 0; - for (uint8 i = 1; i <= stack; ++i) - dmg += mod * i; - if (Unit* caster = triggeredByAura->GetCaster()) - caster->CastCustomSpell(70701, SPELLVALUE_BASE_POINT0, dmg); - break; - } - // Ball of Flames Proc - case 71756: - case 72782: - case 72783: - case 72784: - RemoveAuraFromStack(dummySpell->Id); - *handled = true; - break; - // Discerning Eye of the Beast - case 59915: - { - CastSpell(this, 59914, true); // 59914 already has correct basepoints in DBC, no need for custom bp - *handled = true; - break; - } - // Swift Hand of Justice - case 59906: - { - int32 bp0 = CalculatePct(GetMaxHealth(), dummySpell->Effects[EFFECT_0]. CalcValue()); - CastCustomSpell(this, 59913, &bp0, NULL, NULL, true); - *handled = true; - break; - } - } - - break; - case SPELLFAMILY_PALADIN: - { - // Infusion of Light - if (procSpell && dummySpell->SpellIconID == 3021) - { - // Flash of Light HoT on Flash of Light when Sacred Shield active - if (procSpell->SpellFamilyFlags[0] & 0x40000000 && procSpell->SpellIconID == 242) - { - *handled = true; - if (victim && victim->HasAura(53601)) - { - int32 bp0 = CalculatePct(int32(damage / 12), dummySpell->Effects[EFFECT_2].CalcValue()); - // Item - Paladin T9 Holy 4P Bonus - if (AuraEffect const* aurEff = GetAuraEffect(67191, 0)) - AddPct(bp0, aurEff->GetAmount()); - CastCustomSpell(victim, 66922, &bp0, NULL, NULL, true); - return true; - } - } - // but should not proc on non-critical Holy Shocks - else if ((procSpell->SpellFamilyFlags[0] & 0x200000 || procSpell->SpellFamilyFlags[1] & 0x10000) && !(procEx & PROC_EX_CRITICAL_HIT)) - *handled = true; - break; - } - // Judgements of the Just - else if (dummySpell->SpellIconID == 3015) - { - *handled = true; - CastSpell(victim, 68055, true); - return true; - } - // Glyph of Divinity - else if (dummySpell->Id == 54939) - { - if (!procSpell) - return false; - *handled = true; - // Check if we are the target and prevent mana gain - if (victim && triggeredByAura->GetCasterGUID() == victim->GetGUID()) - return false; - // Lookup base amount mana restore - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; i++) - { - if (procSpell->Effects[i].Effect == SPELL_EFFECT_ENERGIZE) - { - // value multiplied by 2 because you should get twice amount - int32 mana = procSpell->Effects[i].CalcValue() * 2; - CastCustomSpell(this, 54986, 0, &mana, NULL, true); - } - } - return true; - } - break; - } - case SPELLFAMILY_MAGE: - { - // Combustion - switch (dummySpell->Id) - { - case 11129: - { - *handled = true; - Unit* caster = triggeredByAura->GetCaster(); - if (!caster || !damage) - return false; - - // last charge and crit - if (triggeredByAura->GetCharges() <= 1 && (procEx & PROC_EX_CRITICAL_HIT)) - return true; // charge counting (will removed) - - CastSpell(this, 28682, true); - - return (procEx & PROC_EX_CRITICAL_HIT) != 0; - } - // Empowered Fire - case 31656: - case 31657: - case 31658: - { - *handled = true; - - SpellInfo const* spInfo = sSpellMgr->GetSpellInfo(67545); - if (!spInfo) - return false; - - int32 bp0 = int32(CalculatePct(GetCreateMana(), spInfo->Effects[0].CalcValue())); - CastCustomSpell(this, 67545, &bp0, NULL, NULL, true, NULL, triggeredByAura->GetEffect(EFFECT_0), GetGUID()); - return true; - } - case 44401: //Missile Barrage - case 48108: //Hot Streak - case 57761: //Fireball! - { - *handled = true; - - // Prevent double proc for Arcane missiles - if (this == victim) - return false; - - if (HasAura(70752)) // Item - Mage T10 2P Bonus - CastSpell((Unit*)nullptr, 70753, true); - - // Proc chance is unknown, we'll just use dummy aura amount - if (AuraEffect const* aurEff = GetAuraEffect(64869, EFFECT_0)) // Item - Mage T8 4P Bonus - if (roll_chance_i(aurEff->GetAmount())) // do not proc charges - return false; - - // @workaround: We'll take care of removing the aura on next update tick - // This is needed for 44401 to affect Arcane Missiles, else the spellmod will not be applied - // it only works because EventProcessor will always update first scheduled event, - // as cast is already in progress the SpellEvent for Arcane Missiles is already in queue. - triggeredByAura->DropChargeDelayed(1); - return false; - } - } - break; - } - case SPELLFAMILY_DEATHKNIGHT: - { - // Blood of the North - // Reaping - // Death Rune Mastery - /// @todo move those to spell scripts - if (dummySpell->SpellIconID == 3041 || (dummySpell->SpellIconID == 22 && dummySpell->Id != 62459) || dummySpell->SpellIconID == 2622) - { - *handled = true; - // Convert recently used Blood Rune to Death Rune - if (Player* player = ToPlayer()) - { - if (player->getClass() != CLASS_DEATH_KNIGHT) - return false; - - RuneType rune = ToPlayer()->GetLastUsedRune(); - // can't proc from death rune use - if (rune == RUNE_DEATH) - return false; - AuraEffect* aurEff = triggeredByAura->GetEffect(EFFECT_0); - if (!aurEff) - return false; - - // Reset amplitude - set death rune remove timer to 30s - aurEff->ResetPeriodic(true); - uint32 runesLeft; - - if (dummySpell->SpellIconID == 2622) - runesLeft = 2; - else - runesLeft = 1; - - for (uint8 i = 0; i < MAX_RUNES && runesLeft; ++i) - { - if (dummySpell->SpellIconID == 2622) - { - if (player->GetCurrentRune(i) == RUNE_DEATH || - player->GetBaseRune(i) == RUNE_BLOOD) - continue; - } - else - { - if (player->GetCurrentRune(i) == RUNE_DEATH || - player->GetBaseRune(i) != RUNE_BLOOD) - continue; - } - if (player->GetRuneCooldown(i) != (player->GetRuneBaseCooldown(i) - player->GetLastRuneGraceTimer(i))) - continue; - - --runesLeft; - // Mark aura as used - player->AddRuneByAuraEffect(i, RUNE_DEATH, aurEff); - } - return true; - } - return false; - } - - switch (dummySpell->Id) - { - // Bone Shield cooldown - case 49222: - { - *handled = true; - return true; - } - // Hungering Cold aura drop - case 51209: - *handled = true; - // Drop only in not disease case - if (procSpell && procSpell->Dispel == DISPEL_DISEASE) - return false; - return true; - } - break; - } - case SPELLFAMILY_WARRIOR: - { - switch (dummySpell->Id) - { - // Item - Warrior T10 Protection 4P Bonus - case 70844: - { - int32 basepoints0 = CalculatePct(GetMaxHealth(), dummySpell->Effects[EFFECT_1]. CalcValue()); - CastCustomSpell(this, 70845, &basepoints0, NULL, NULL, true); - break; - } - // Recklessness - case 1719: - { - //! Possible hack alert - //! Don't drop charges on proc, they will be dropped on SpellMod removal - //! Before this change, it was dropping two charges per attack, one in ProcDamageAndSpellFor, and one in RemoveSpellMods. - //! The reason of this behaviour is Recklessness having three auras, 2 of them can not proc (isTriggeredAura array) but the other one can, making the whole spell proc. - *handled = true; - break; - } - default: - break; - } - break; - } - } - return false; -} - -bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx) -{ - // Get triggered aura spell info - SpellInfo const* auraSpellInfo = triggeredByAura->GetSpellInfo(); - - // Basepoints of trigger aura - int32 triggerAmount = triggeredByAura->GetAmount(); - - // Set trigger spell id, target, custom basepoints - uint32 trigger_spell_id = auraSpellInfo->Effects[triggeredByAura->GetEffIndex()].TriggerSpell; - - Unit* target = NULL; - int32 basepoints0 = 0; - - if (triggeredByAura->GetAuraType() == SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE) - basepoints0 = triggerAmount; - - Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER - ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; - - // Try handle unknown trigger spells - // triggered spells exists only in serverside spell_dbc - /// @todo: reverify and move these spells to spellscripts - { - switch (auraSpellInfo->SpellFamilyName) - { - case SPELLFAMILY_WARLOCK: - { - // Drain Soul - if (auraSpellInfo->SpellFamilyFlags[0] & 0x4000) - { - // Improved Drain Soul - Unit::AuraEffectList const& mAddFlatModifier = GetAuraEffectsByType(SPELL_AURA_DUMMY); - for (Unit::AuraEffectList::const_iterator i = mAddFlatModifier.begin(); i != mAddFlatModifier.end(); ++i) - { - if ((*i)->GetMiscValue() == SPELLMOD_CHANCE_OF_SUCCESS && (*i)->GetSpellInfo()->SpellIconID == 113) - { - int32 value2 = CalculateSpellDamage(this, (*i)->GetSpellInfo(), 2); - basepoints0 = int32(CalculatePct(GetMaxPower(POWER_MANA), value2)); - // Drain Soul - CastCustomSpell(this, 18371, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - break; - } - } - // Not remove charge (aura removed on death in any cases) - // Need for correct work Drain Soul SPELL_AURA_CHANNEL_DEATH_ITEM aura - return false; - } - } - default: - break; - } - } - - // All ok. Check current trigger spell - SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(trigger_spell_id); - if (triggerEntry == NULL) - { - // Don't cast unknown spell - TC_LOG_ERROR("entities.unit.handleproctriggerspell", "Unit::HandleProcTriggerSpell: Spell %u (effIndex: %u) has unknown TriggerSpell %u. Unhandled custom case?", auraSpellInfo->Id, triggeredByAura->GetEffIndex(), trigger_spell_id); - return false; - } - - // not allow proc extra attack spell at extra attack - if (m_extraAttacks && triggerEntry->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)) - return false; - - // Custom requirements (not listed in procEx) Warning! damage dealing after this - // Custom triggered spells - switch (auraSpellInfo->Id) - { - // Deep Wounds - case 12834: - case 12849: - case 12867: - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - float averageDmg = 0; - // now compute approximate weapon damage by formula from wowwiki.com - if (procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) - averageDmg = (GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE) + GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE)) / 2.f; - else - averageDmg = (GetFloatValue(UNIT_FIELD_MINDAMAGE) + GetFloatValue(UNIT_FIELD_MAXDAMAGE)) / 2.f; - - basepoints0 = int32(averageDmg); - break; - } - // Persistent Shield (Scarab Brooch trinket) - // This spell originally trigger 13567 - Dummy Trigger (vs dummy efect) - case 26467: - { - basepoints0 = int32(CalculatePct(damage, 15)); - target = victim; - trigger_spell_id = 26470; - break; - } - // Unyielding Knights (item exploit 29108\29109) - case 38164: - { - if (!victim || victim->GetEntry() != 19457) // Proc only if your target is Grillok - return false; - break; - } - // Deflection - case 52420: - { - if (!HealthBelowPct(35)) - return false; - break; - } - - // Cheat Death - case 28845: - { - // When your health drops below 20% - if (HealthBelowPctDamaged(20, damage) || HealthBelowPct(20)) - return false; - break; - } - // Deadly Swiftness (Rank 1) - case 31255: - { - // whenever you deal damage to a target who is below 20% health. - if (!victim || !victim->IsAlive() || victim->HealthAbovePct(20)) - return false; - - target = this; - trigger_spell_id = 22588; - } - // Greater Heal Refund (Avatar Raiment set) - case 37594: - { - if (!victim || !victim->IsAlive()) - return false; - - // Doesn't proc if target already has full health - if (victim->IsFullHealth()) - return false; - // If your Greater Heal brings the target to full health, you gain $37595s1 mana. - if (victim->GetHealth() + damage < victim->GetMaxHealth()) - return false; - break; - } - // Bonus Healing (Crystal Spire of Karabor mace) - case 40971: - { - // If your target is below $s1% health - if (!victim || !victim->IsAlive() || victim->HealthAbovePct(triggerAmount)) - return false; - break; - } - // Rapid Recuperation - case 53228: - case 53232: - { - // This effect only from Rapid Fire (ability cast) - if (!procSpell || !(procSpell->SpellFamilyFlags[0] & 0x20)) - return false; - break; - } - // Decimation - case 63156: - case 63158: - // Can proc only if target has hp below 35% - if (!victim || !victim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, procSpell, this)) - return false; - break; - // Deathbringer Saurfang - Blood Beast's Blood Link - case 72176: - basepoints0 = 3; - break; - case 15337: // Improved Spirit Tap (Rank 1) - case 15338: // Improved Spirit Tap (Rank 2) - { - if (!procSpell) - return false; - - if (procSpell->SpellFamilyFlags[0] & 0x800000) - if ((procSpell->Id != 58381) || !roll_chance_i(50)) - return false; - - target = victim; - break; - } - // Professor Putricide - Ooze Spell Tank Protection - case 71770: - if (victim) - victim->CastSpell(victim, trigger_spell_id, true); // EffectImplicitTarget is self - return true; - case 45057: // Evasive Maneuvers (Commendation of Kael`thas trinket) - case 71634: // Item - Icecrown 25 Normal Tank Trinket 1 - case 71640: // Item - Icecrown 25 Heroic Tank Trinket 1 - case 75475: // Item - Chamber of Aspects 25 Normal Tank Trinket - case 75481: // Item - Chamber of Aspects 25 Heroic Tank Trinket - { - // Procs only if damage takes health below $s1% - if (!HealthBelowPctDamaged(triggerAmount, damage)) - return false; - break; - } - default: - break; - } - - // Blade Barrier - if (auraSpellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && auraSpellInfo->SpellIconID == 85 && procSpell) - { - Player* player = ToPlayer(); - if (!player || player->getClass() != CLASS_DEATH_KNIGHT) - return false; - - if (!player->IsBaseRuneSlotsOnCooldown(RUNE_BLOOD)) - return false; - } - - // Rime - else if (auraSpellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && auraSpellInfo->SpellIconID == 56) - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - // Howling Blast - GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool - { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); - return spellInfo && spellInfo->GetCategory() == 1248; - }, true); - } - - // Custom basepoints/target for exist spell - // dummy basepoints or other customs - switch (trigger_spell_id) - { - // Auras which should proc on area aura source (caster in this case): - // Turn the Tables - case 52914: - case 52915: - case 52910: - { - target = triggeredByAura->GetBase()->GetCaster(); - if (!target) - return false; - - target->CastSpell(target, trigger_spell_id, true, castItem, triggeredByAura); - return true; - } - // Cast positive spell on enemy target - case 7099: // Curse of Mending - case 39703: // Curse of Mending - case 29494: // Temptation - case 20233: // Improved Lay on Hands (cast on target) - { - target = victim; - break; - } - // Combo points add triggers (need add combopoint only for main target, and after possible combopoints reset) - case 15250: // Rogue Setup - { - // applied only for main target - if (!victim || (GetTypeId() == TYPEID_PLAYER && victim != ToPlayer()->GetSelectedUnit())) - return false; - break; // continue normal case - } - // Finish movies that add combo - case 14189: // Seal Fate (Netherblade set) - case 14157: // Ruthlessness - { - if (!victim || victim == this) - return false; - // Need add combopoint AFTER finish movie (or they dropped in finish phase) - break; - } - // Item - Druid T10 Balance 2P Bonus - case 16870: - { - if (HasAura(70718)) - CastSpell(this, 70721, true); - break; - } - // Shamanistic Rage triggered spell - case 30824: - { - basepoints0 = int32(CalculatePct(GetTotalAttackPowerValue(BASE_ATTACK), triggerAmount)); - break; - } - // Enlightenment (trigger only from mana cost spells) - case 35095: - { - if (!procSpell || procSpell->PowerType != POWER_MANA || (procSpell->ManaCost == 0 && procSpell->ManaCostPercentage == 0 && procSpell->ManaCostPerlevel == 0)) - return false; - break; - } - // Demonic Pact - case 48090: - { - // Get talent aura from owner - if (IsPet()) - if (Unit* owner = GetOwner()) - { - if (AuraEffect* aurEff = owner->GetDummyAuraEffect(SPELLFAMILY_WARLOCK, 3220, 0)) - { - basepoints0 = int32((aurEff->GetAmount() * owner->SpellBaseDamageBonusDone(SpellSchoolMask(SPELL_SCHOOL_MASK_MAGIC)) + 100.0f) / 100.0f); /// @todo Is it right? - CastCustomSpell(this, trigger_spell_id, &basepoints0, &basepoints0, NULL, true, castItem, triggeredByAura); - return true; - } - } - break; - } - case 46916: // Slam! (Bloodsurge proc) - case 52437: // Sudden Death - { - // Item - Warrior T10 Melee 4P Bonus - if (AuraEffect const* aurEff = GetAuraEffect(70847, 0)) - { - if (!roll_chance_i(aurEff->GetAmount())) - break; - CastSpell(this, 70849, true, castItem, triggeredByAura); // Extra Charge! - CastSpell(this, 71072, true, castItem, triggeredByAura); // Slam GCD Reduced - CastSpell(this, 71069, true, castItem, triggeredByAura); // Execute GCD Reduced - } - break; - } - // Sword and Board - case 50227: - { - // Remove cooldown on Shield Slam - GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool - { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); - return spellInfo && spellInfo->GetCategory() == 1209; - }, true); - break; - } - // Maelstrom Weapon - case 53817: - { - // has rank dependant proc chance, ignore too often cases - // PPM = 2.5 * (rank of talent), - uint32 rank = auraSpellInfo->GetRank(); - // 5 rank -> 100% 4 rank -> 80% and etc from full rate - if (!roll_chance_i(20*rank)) - return false; - // Item - Shaman T10 Enhancement 4P Bonus - if (AuraEffect const* aurEff = GetAuraEffect(70832, 0)) - if (Aura const* maelstrom = GetAura(53817)) - if ((maelstrom->GetStackAmount() == maelstrom->GetSpellInfo()->StackAmount) && roll_chance_i(aurEff->GetAmount())) - CastSpell(this, 70831, true, castItem, triggeredByAura); - break; - } - // Astral Shift - case 52179: - { - if (!procSpell || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == victim) - return false; - - // Need stun, fear or silence mechanic - if (!(procSpell->GetAllEffectsMechanicMask() & ((1<<MECHANIC_SILENCE)|(1<<MECHANIC_STUN)|(1<<MECHANIC_FEAR)))) - return false; - break; - } - // Burning Determination - case 54748: - { - if (!procSpell) - return false; - // Need Interrupt or Silenced mechanic - if (!(procSpell->GetAllEffectsMechanicMask() & ((1<<MECHANIC_INTERRUPT)|(1<<MECHANIC_SILENCE)))) - return false; - break; - } - // Glyph of Death's Embrace - case 58679: - { - // Proc only from healing part of Death Coil. Check is essential as all Death Coil spells have 0x2000 mask in SpellFamilyFlags - if (!procSpell || !(procSpell->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && procSpell->SpellFamilyFlags[0] == 0x80002000)) - return false; - break; - } - // Glyph of Death Grip - case 58628: - { - // remove cooldown of Death Grip - GetSpellHistory()->ResetCooldown(49576, true); - return true; - } - // Savage Defense - case 62606: - { - basepoints0 = CalculatePct(triggerAmount, GetTotalAttackPowerValue(BASE_ATTACK)); - break; - } - // Body and Soul - case 64128: - case 65081: - { - // Proc only from PW:S cast - if (!procSpell || !(procSpell->SpellFamilyFlags[0] & 0x00000001)) - return false; - break; - } - // Culling the Herd - case 70893: - { - if (!procSpell) - return false; - // check if we're doing a critical hit - if (!(procSpell->SpellFamilyFlags[1] & 0x10000000) && (procEx != PROC_EX_CRITICAL_HIT)) - return false; - // check if we're procced by Claw, Bite or Smack (need to use the spell icon ID to detect it) - if (!(procSpell->SpellIconID == 262 || procSpell->SpellIconID == 1680 || procSpell->SpellIconID == 473)) - return false; - break; - } - } - - // extra attack should hit same target - if (triggerEntry->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)) - target = victim; - - // try detect target manually if not set - if (target == NULL) - target = !(procFlag & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS)) && triggerEntry->IsPositive() ? this : victim; - - if (basepoints0) - CastCustomSpell(target, trigger_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - else - CastSpell(target, trigger_spell_id, true, castItem, triggeredByAura); - - return true; -} - -bool Unit::HandleOverrideClassScriptAuraProc(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* procSpell) -{ - int32 scriptId = triggeredByAura->GetMiscValue(); - - if (!victim || !victim->IsAlive()) - return false; - - Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER - ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; - - uint32 triggered_spell_id = 0; - - switch (scriptId) - { - case 836: // Improved Blizzard (Rank 1) - { - if (!procSpell || procSpell->SpellVisual[0] != 9487) - return false; - triggered_spell_id = 12484; - break; - } - case 988: // Improved Blizzard (Rank 2) - { - if (!procSpell || procSpell->SpellVisual[0] != 9487) - return false; - triggered_spell_id = 12485; - break; - } - case 989: // Improved Blizzard (Rank 3) - { - if (!procSpell || procSpell->SpellVisual[0] != 9487) - return false; - triggered_spell_id = 12486; - break; - } - case 4533: // Dreamwalker Raiment 2 pieces bonus - { - // Chance 50% - if (!roll_chance_i(50)) - return false; - - switch (victim->getPowerType()) - { - case POWER_MANA: triggered_spell_id = 28722; break; - case POWER_RAGE: triggered_spell_id = 28723; break; - case POWER_ENERGY: triggered_spell_id = 28724; break; - default: - return false; - } - break; - } - case 4537: // Dreamwalker Raiment 6 pieces bonus - triggered_spell_id = 28750; // Blessing of the Claw - break; - case 5497: // Improved Mana Gems - triggered_spell_id = 37445; // Mana Surge - break; - case 7010: // Revitalize - can proc on full hp target - case 7011: - case 7012: - { - if (!roll_chance_i(triggeredByAura->GetAmount())) - return false; - switch (victim->getPowerType()) - { - case POWER_MANA: triggered_spell_id = 48542; break; - case POWER_RAGE: triggered_spell_id = 48541; break; - case POWER_ENERGY: triggered_spell_id = 48540; break; - case POWER_RUNIC_POWER: triggered_spell_id = 48543; break; - default: - break; - } - break; - } - default: - break; - } - - // not processed - if (!triggered_spell_id) - return false; - - // standard non-dummy case - SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id); - - if (!triggerEntry) - { - TC_LOG_ERROR("entities.unit", "Unit::HandleOverrideClassScriptAuraProc: Spell %u triggering for class script id %u", triggered_spell_id, scriptId); - return false; - } - - CastSpell(victim, triggered_spell_id, true, castItem, triggeredByAura); - return true; -} - void Unit::setPowerType(Powers new_powertype) { if (getPowerType() == new_powertype) @@ -8863,9 +5885,9 @@ uint32 Unit::BuildAuraStateUpdateForTarget(Unit* target) const { uint32 auraStates = GetUInt32Value(UNIT_FIELD_AURASTATE) &~(PER_CASTER_AURA_STATE_MASK); for (AuraStateAurasMap::const_iterator itr = m_auraStateAuras.begin(); itr != m_auraStateAuras.end(); ++itr) - if ((1<<(itr->first-1)) & PER_CASTER_AURA_STATE_MASK) + if ((1 << (itr->first - 1)) & PER_CASTER_AURA_STATE_MASK) if (itr->second->GetBase()->GetCasterGUID() == target->GetGUID()) - auraStates |= (1<<(itr->first-1)); + auraStates |= (1 << (itr->first - 1)); return auraStates; } @@ -8877,13 +5899,14 @@ bool Unit::HasAuraState(AuraStateType flag, SpellInfo const* spellProto, Unit co if (spellProto) { AuraEffectList const& stateAuras = Caster->GetAuraEffectsByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE); - for (AuraEffectList::const_iterator j = stateAuras.begin(); j != stateAuras.end(); ++j) - if ((*j)->IsAffectedOnSpell(spellProto)) + for (AuraEffect const* aurEff : stateAuras) + if (aurEff->IsAffectedOnSpell(spellProto)) return true; } + // Check per caster aura state // If aura with aurastate by caster not found return false - if ((1<<(flag-1)) & PER_CASTER_AURA_STATE_MASK) + if ((1 << (flag - 1)) & PER_CASTER_AURA_STATE_MASK) { AuraStateAurasMapBounds range = m_auraStateAuras.equal_range(flag); for (AuraStateAurasMap::const_iterator itr = range.first; itr != range.second; ++itr) @@ -8893,7 +5916,7 @@ bool Unit::HasAuraState(AuraStateType flag, SpellInfo const* spellProto, Unit co } } - return HasFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)); + return HasFlag(UNIT_FIELD_AURASTATE, 1 << (flag - 1)); } void Unit::SetOwnerGUID(ObjectGuid owner) @@ -9267,9 +6290,11 @@ void Unit::SetCharm(Unit* charm, bool apply) } } -int32 Unit::DealHeal(Unit* victim, uint32 addhealth) +void Unit::DealHeal(HealInfo& healInfo) { int32 gain = 0; + Unit* victim = healInfo.GetTarget(); + uint32 addhealth = healInfo.GetHeal(); if (victim->IsAIEnabled) victim->GetAI()->HealReceived(this, addhealth); @@ -9306,7 +6331,8 @@ int32 Unit::DealHeal(Unit* victim, uint32 addhealth) player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED, addhealth); } - return gain; + if (gain) + healInfo.SetEffectiveHeal(gain > 0 ? static_cast<uint32>(gain) : 0UL); } bool Unit::IsMagnet() const @@ -9519,30 +6545,29 @@ void Unit::UnsummonAllTotems() } } -void Unit::SendHealSpellLog(Unit* victim, uint32 SpellID, uint32 Damage, uint32 OverHeal, uint32 Absorb, bool critical) +void Unit::SendHealSpellLog(HealInfo& healInfo, bool critical /*= false*/) { // we guess size WorldPacket data(SMSG_SPELLHEALLOG, 8 + 8 + 4 + 4 + 4 + 4 + 1 + 1); - data << victim->GetPackGUID(); + data << healInfo.GetTarget()->GetPackGUID(); data << GetPackGUID(); - data << uint32(SpellID); - data << uint32(Damage); - data << uint32(OverHeal); - data << uint32(Absorb); // Absorb amount + data << uint32(healInfo.GetSpellInfo()->Id); + data << uint32(healInfo.GetHeal()); + data << uint32(healInfo.GetHeal() - healInfo.GetEffectiveHeal()); + data << uint32(healInfo.GetAbsorb()); // Absorb amount data << uint8(critical ? 1 : 0); data << uint8(0); // unused SendMessageToSet(&data, true); } -int32 Unit::HealBySpell(Unit* victim, SpellInfo const* spellInfo, uint32 addHealth, bool critical) +int32 Unit::HealBySpell(HealInfo& healInfo, bool critical /*= false*/) { - uint32 absorb = 0; // calculate heal absorb and reduce healing - CalcHealAbsorb(victim, spellInfo, addHealth, absorb); + CalcHealAbsorb(healInfo); - int32 gain = DealHeal(victim, addHealth); - SendHealSpellLog(victim, spellInfo->Id, addHealth, uint32(addHealth - gain), absorb, critical); - return gain; + DealHeal(healInfo); + SendHealSpellLog(healInfo, critical); + return healInfo.GetEffectiveHeal(); } void Unit::SendEnergizeSpellLog(Unit* victim, uint32 spellId, int32 damage, Powers powerType) @@ -9682,7 +6707,7 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin if (Player* modOwner = GetSpellModOwner()) { coeff *= 100.0f; - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, coeff); + modOwner->ApplySpellMod<SPELLMOD_BONUS_MULTIPLIER>(spellProto->Id, coeff); coeff /= 100.0f; } DoneTotal += int32(DoneAdvertisedBenefit * coeff * factorMod); @@ -9692,7 +6717,12 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin float tmpDamage = (int32(pdamage) + DoneTotal) * (damagetype == DOT ? 1.0f : SpellDamagePctDone(victim, spellProto, damagetype)); // apply spellmod to Done damage (flat and pct) if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, tmpDamage); + { + if (damagetype == DOT) + modOwner->ApplySpellMod<SPELLMOD_DOT>(spellProto->Id, tmpDamage); + else + modOwner->ApplySpellMod<SPELLMOD_DAMAGE>(spellProto->Id, tmpDamage); + } return uint32(std::max(tmpDamage, 0.0f)); } @@ -10081,7 +7111,7 @@ uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, ui if (Player* modOwner = GetSpellModOwner()) { coeff *= 100.0f; - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, coeff); + modOwner->ApplySpellMod<SPELLMOD_BONUS_MULTIPLIER>(spellProto->Id, coeff); coeff /= 100.0f; } TakenTotal += int32(TakenAdvertisedBenefit * coeff * factorMod); @@ -10222,28 +7252,28 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto if (!((*i)->IsAffectedOnSpell(spellProto))) continue; + float modChance = 0.f; switch ((*i)->GetMiscValue()) { + case 911: // Shatter (Rank 3) + modChance += 16.f; + case 910: // Shatter (Rank 2) + modChance += 17.f; case 849: // Shatter (Rank 1) - if (victim->HasAuraState(AURA_STATE_FROZEN, spellProto, this)) - crit_chance += 17; + modChance += 17.f; + if (!victim->HasAuraState(AURA_STATE_FROZEN, spellProto, this)) + break; + + crit_chance += modChance; break; - case 910: // Shatter (Rank 2) - if (victim->HasAuraState(AURA_STATE_FROZEN, spellProto, this)) - crit_chance += 34; - break; - case 911: // Shatter (Rank 3) - if (victim->HasAuraState(AURA_STATE_FROZEN, spellProto, this)) - crit_chance += 50; - break; case 7917: // Glyph of Shadowburn if (victim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, spellProto, this)) - crit_chance+=(*i)->GetAmount(); + crit_chance += (*i)->GetAmount(); break; case 7997: // Renewed Hope case 7998: if (victim->HasAura(6788)) - crit_chance+=(*i)->GetAmount(); + crit_chance += (*i)->GetAmount(); break; default: break; @@ -10358,7 +7388,7 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto // percent done // only players use intelligence for critical chance computations if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRITICAL_CHANCE, crit_chance); + modOwner->ApplySpellMod<SPELLMOD_CRITICAL_CHANCE>(spellProto->Id, crit_chance); // for this types the bonus was already added in GetUnitCriticalChance, do not add twice if (spellProto->DmgClass != SPELL_DAMAGE_CLASS_MELEE && spellProto->DmgClass != SPELL_DAMAGE_CLASS_RANGED) @@ -10408,7 +7438,7 @@ uint32 Unit::SpellCriticalDamageBonus(SpellInfo const* spellProto, uint32 damage { // adds additional damage to critBonus (from talents) if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus); + modOwner->ApplySpellMod<SPELLMOD_CRIT_DAMAGE_BONUS>(spellProto->Id, crit_bonus); } crit_bonus += damage; @@ -10520,7 +7550,7 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui if (Player* modOwner = GetSpellModOwner()) { coeff *= 100.0f; - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, coeff); + modOwner->ApplySpellMod<SPELLMOD_BONUS_MULTIPLIER>(spellProto->Id, coeff); coeff /= 100.0f; } @@ -10549,7 +7579,12 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui float heal = float(int32(healamount) + DoneTotal) * (damagetype == DOT ? 1.0f : SpellHealingPctDone(victim, spellProto)); // apply spellmod to Done amount if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, heal); + { + if (damagetype == DOT) + modOwner->ApplySpellMod<SPELLMOD_DOT>(spellProto->Id, heal); + else + modOwner->ApplySpellMod<SPELLMOD_DAMAGE>(spellProto->Id, heal); + } return uint32(std::max(heal, 0.0f)); } @@ -10695,7 +7730,7 @@ uint32 Unit::SpellHealingBonusTaken(Unit* caster, SpellInfo const* spellProto, u if (Player* modOwner = GetSpellModOwner()) { coeff *= 100.0f; - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, coeff); + modOwner->ApplySpellMod<SPELLMOD_BONUS_MULTIPLIER>(spellProto->Id, coeff); coeff /= 100.0f; } @@ -11111,7 +8146,7 @@ uint32 Unit::MeleeDamageBonusDone(Unit* victim, uint32 pdamage, WeaponAttackType // apply spellmod to Done damage if (spellProto) if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_DAMAGE, tmpDamage); + modOwner->ApplySpellMod<SPELLMOD_DAMAGE>(spellProto->Id, tmpDamage); // bonus result can be negative return uint32(std::max(tmpDamage, 0.0f)); @@ -11314,7 +8349,7 @@ float Unit::GetPPMProcChance(uint32 WeaponSpeed, float PPM, const SpellInfo* spe // Apply chance modifer aura if (spellProto) if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_PROC_PER_MINUTE, PPM); + modOwner->ApplySpellMod<SPELLMOD_PROC_PER_MINUTE>(spellProto->Id, PPM); return std::floor((WeaponSpeed * PPM) / 600.0f); // result is chance in percents (probability = Speed_in_sec * (PPM / 60)) } @@ -12494,17 +9529,17 @@ float Unit::ApplyEffectModifiers(SpellInfo const* spellProto, uint8 effect_index { if (Player* modOwner = GetSpellModOwner()) { - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_ALL_EFFECTS, value); + modOwner->ApplySpellMod<SPELLMOD_ALL_EFFECTS>(spellProto->Id, value); switch (effect_index) { - case 0: - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT1, value); + case EFFECT_0: + modOwner->ApplySpellMod<SPELLMOD_EFFECT1>(spellProto->Id, value); break; - case 1: - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT2, value); + case EFFECT_1: + modOwner->ApplySpellMod<SPELLMOD_EFFECT2>(spellProto->Id, value); break; - case 2: - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT3, value); + case EFFECT_2: + modOwner->ApplySpellMod<SPELLMOD_EFFECT3>(spellProto->Id, value); break; } } @@ -12647,7 +9682,7 @@ void Unit::ModSpellCastTime(SpellInfo const* spellInfo, int32 & castTime, Spell* // called from caster if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, castTime, spell); + modOwner->ApplySpellMod<SPELLMOD_CASTING_TIME>(spellInfo->Id, castTime, spell); if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) @@ -12668,7 +9703,7 @@ void Unit::ModSpellDurationTime(SpellInfo const* spellInfo, int32 & duration, Sp // called from caster if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, duration, spell); + modOwner->ApplySpellMod<SPELLMOD_CASTING_TIME>(spellInfo->Id, duration, spell); if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) @@ -13709,152 +10744,96 @@ bool Unit::isFrozen() const return HasAuraState(AURA_STATE_FROZEN); } -struct ProcTriggeredData +uint32 createProcHitMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition) { - ProcTriggeredData(Aura* _aura) - : aura(_aura) - { - effMask = 0; - spellProcEvent = NULL; - } - SpellProcEventEntry const* spellProcEvent; - Aura* aura; - uint32 effMask; -}; - -typedef std::list< ProcTriggeredData > ProcTriggeredList; - -// List of auras that CAN be trigger but may not exist in spell_proc_event -// in most case need for drop charges -// in some types of aura need do additional check -// for example SPELL_AURA_MECHANIC_IMMUNITY - need check for mechanic -bool InitTriggerAuraData() -{ - for (uint16 i = 0; i < TOTAL_AURAS; ++i) - { - isTriggerAura[i] = false; - isNonTriggerAura[i] = false; - isAlwaysTriggeredAura[i] = false; - } - isTriggerAura[SPELL_AURA_DUMMY] = true; - isTriggerAura[SPELL_AURA_MOD_CONFUSE] = true; - isTriggerAura[SPELL_AURA_MOD_THREAT] = true; - isTriggerAura[SPELL_AURA_MOD_STUN] = true; // Aura does not have charges but needs to be removed on trigger - isTriggerAura[SPELL_AURA_MOD_DAMAGE_DONE] = true; - isTriggerAura[SPELL_AURA_MOD_DAMAGE_TAKEN] = true; - isTriggerAura[SPELL_AURA_MOD_RESISTANCE] = true; - isTriggerAura[SPELL_AURA_MOD_STEALTH] = true; - isTriggerAura[SPELL_AURA_MOD_FEAR] = true; // Aura does not have charges but needs to be removed on trigger - isTriggerAura[SPELL_AURA_MOD_ROOT] = true; - isTriggerAura[SPELL_AURA_TRANSFORM] = true; - isTriggerAura[SPELL_AURA_REFLECT_SPELLS] = true; - isTriggerAura[SPELL_AURA_DAMAGE_IMMUNITY] = true; - isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL] = true; - isTriggerAura[SPELL_AURA_PROC_TRIGGER_DAMAGE] = true; - isTriggerAura[SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK] = true; - isTriggerAura[SPELL_AURA_SCHOOL_ABSORB] = true; // Savage Defense untested - isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT] = true; - isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL] = true; - isTriggerAura[SPELL_AURA_REFLECT_SPELLS_SCHOOL] = true; - isTriggerAura[SPELL_AURA_MECHANIC_IMMUNITY] = true; - isTriggerAura[SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN] = true; - isTriggerAura[SPELL_AURA_SPELL_MAGNET] = true; - isTriggerAura[SPELL_AURA_MOD_ATTACK_POWER] = true; - isTriggerAura[SPELL_AURA_ADD_CASTER_HIT_TRIGGER] = true; - isTriggerAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true; - isTriggerAura[SPELL_AURA_MOD_MECHANIC_RESISTANCE] = true; - isTriggerAura[SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS] = true; - isTriggerAura[SPELL_AURA_MOD_MELEE_HASTE] = true; - isTriggerAura[SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE] = true; - isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE] = true; - isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE] = true; - isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE] = true; - isTriggerAura[SPELL_AURA_MOD_DAMAGE_FROM_CASTER] = true; - isTriggerAura[SPELL_AURA_MOD_SPELL_CRIT_CHANCE] = true; - isTriggerAura[SPELL_AURA_ABILITY_IGNORE_AURASTATE] = true; - - isNonTriggerAura[SPELL_AURA_MOD_POWER_REGEN] = true; - isNonTriggerAura[SPELL_AURA_REDUCE_PUSHBACK] = true; - - isAlwaysTriggeredAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true; - isAlwaysTriggeredAura[SPELL_AURA_MOD_FEAR] = true; - isAlwaysTriggeredAura[SPELL_AURA_MOD_ROOT] = true; - isAlwaysTriggeredAura[SPELL_AURA_MOD_STUN] = true; - isAlwaysTriggeredAura[SPELL_AURA_TRANSFORM] = true; - isAlwaysTriggeredAura[SPELL_AURA_SPELL_MAGNET] = true; - isAlwaysTriggeredAura[SPELL_AURA_SCHOOL_ABSORB] = true; - isAlwaysTriggeredAura[SPELL_AURA_MOD_STEALTH] = true; - - return true; -} - -uint32 createProcExtendMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition) -{ - uint32 procEx = PROC_EX_NONE; + uint32 hitMask = PROC_HIT_NONE; // Check victim state if (missCondition != SPELL_MISS_NONE) + { switch (missCondition) { - case SPELL_MISS_MISS: procEx|=PROC_EX_MISS; break; - case SPELL_MISS_RESIST: procEx|=PROC_EX_RESIST; break; - case SPELL_MISS_DODGE: procEx|=PROC_EX_DODGE; break; - case SPELL_MISS_PARRY: procEx|=PROC_EX_PARRY; break; - case SPELL_MISS_BLOCK: procEx|=PROC_EX_BLOCK; break; - case SPELL_MISS_EVADE: procEx|=PROC_EX_EVADE; break; - case SPELL_MISS_IMMUNE: procEx|=PROC_EX_IMMUNE; break; - case SPELL_MISS_IMMUNE2: procEx|=PROC_EX_IMMUNE; break; - case SPELL_MISS_DEFLECT: procEx|=PROC_EX_DEFLECT;break; - case SPELL_MISS_ABSORB: procEx|=PROC_EX_ABSORB; break; - case SPELL_MISS_REFLECT: procEx|=PROC_EX_REFLECT;break; + case SPELL_MISS_MISS: + hitMask |= PROC_HIT_MISS; + break; + case SPELL_MISS_DODGE: + hitMask |= PROC_HIT_DODGE; + break; + case SPELL_MISS_PARRY: + hitMask |= PROC_HIT_PARRY; + break; + case SPELL_MISS_BLOCK: + hitMask |= PROC_HIT_BLOCK; + break; + case SPELL_MISS_EVADE: + hitMask |= PROC_HIT_EVADE; + break; + case SPELL_MISS_IMMUNE: + hitMask |= PROC_HIT_IMMUNE; + break; + case SPELL_MISS_IMMUNE2: + hitMask |= PROC_HIT_IMMUNE; + break; + case SPELL_MISS_DEFLECT: + hitMask |= PROC_HIT_DEFLECT; + break; + case SPELL_MISS_ABSORB: + hitMask |= PROC_HIT_ABSORB; + break; + case SPELL_MISS_REFLECT: + hitMask |= PROC_HIT_REFLECT; + break; default: break; } + } else { // On block if (damageInfo->blocked) - procEx|=PROC_EX_BLOCK; + hitMask |= PROC_HIT_BLOCK; // On absorb if (damageInfo->absorb) - procEx|=PROC_EX_ABSORB; + hitMask |= PROC_HIT_ABSORB; // On crit if (damageInfo->HitInfo & SPELL_HIT_TYPE_CRIT) - procEx|=PROC_EX_CRITICAL_HIT; + hitMask |= PROC_HIT_CRITICAL; else - procEx|=PROC_EX_NORMAL_HIT; + hitMask |= PROC_HIT_NORMAL; } - return procEx; + + return hitMask; } -void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellInfo const* procSpell, uint32 damage, SpellInfo const* procAura) +void Unit::ProcSkillsAndReactives(bool isVictim, Unit* procTarget, uint32 typeMask, uint32 hitMask, WeaponAttackType attType) { // Player is loaded now - do not allow passive spell casts to proc if (GetTypeId() == TYPEID_PLAYER && ToPlayer()->GetSession()->PlayerLoading()) return; + // For melee/ranged based attack need update skills and set some Aura states if victim present - if (procFlag & MELEE_BASED_TRIGGER_MASK && target) + if (typeMask & MELEE_BASED_TRIGGER_MASK && procTarget) { // Update skills here for players if (GetTypeId() == TYPEID_PLAYER) { // On melee based hit/miss/resist need update skill (for victim and attacker) - if (procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_MISS|PROC_EX_RESIST)) + if (hitMask & (PROC_HIT_NORMAL | PROC_HIT_MISS | PROC_HIT_FULL_RESIST)) { - if (target->GetTypeId() != TYPEID_PLAYER && !target->IsCritter()) - ToPlayer()->UpdateCombatSkills(target, attType, isVictim); + if (procTarget->GetTypeId() != TYPEID_PLAYER && !procTarget->IsCritter()) + ToPlayer()->UpdateCombatSkills(procTarget, attType, isVictim); } // Update defense if player is victim and parry/dodge/block - else if (isVictim && procExtra & (PROC_EX_DODGE|PROC_EX_PARRY|PROC_EX_BLOCK)) - ToPlayer()->UpdateCombatSkills(target, attType, true); + else if (isVictim && (hitMask & (PROC_HIT_DODGE | PROC_HIT_PARRY | PROC_HIT_BLOCK))) + ToPlayer()->UpdateCombatSkills(procTarget, attType, true); } // If exist crit/parry/dodge/block need update aura state (for victim and attacker) - if (procExtra & (PROC_EX_CRITICAL_HIT|PROC_EX_PARRY|PROC_EX_DODGE|PROC_EX_BLOCK)) + if (hitMask & (PROC_HIT_CRITICAL | PROC_HIT_PARRY | PROC_HIT_DODGE | PROC_HIT_BLOCK)) { // for victim if (isVictim) { // if victim and dodge attack - if (procExtra & PROC_EX_DODGE) + if (hitMask & PROC_HIT_DODGE) { // Update AURA_STATE on dodge if (getClass() != CLASS_ROGUE) // skip Rogue Riposte @@ -13864,7 +10843,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u } } // if victim and parry attack - if (procExtra & PROC_EX_PARRY) + if (hitMask & PROC_HIT_PARRY) { // For Hunters only Counterattack (skip Mongoose bite) if (getClass() == CLASS_HUNTER) @@ -13879,7 +10858,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u } } // if and victim block attack - if (procExtra & PROC_EX_BLOCK) + if (hitMask & PROC_HIT_BLOCK) { ModifyAuraState(AURA_STATE_DEFENSE, true); StartReactiveTimer(REACTIVE_DEFENSE); @@ -13888,362 +10867,42 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u else // For attacker { // Overpower on victim dodge - if (procExtra & PROC_EX_DODGE && GetTypeId() == TYPEID_PLAYER && getClass() == CLASS_WARRIOR) + if ((hitMask & PROC_HIT_DODGE) && GetTypeId() == TYPEID_PLAYER && getClass() == CLASS_WARRIOR) { - ToPlayer()->AddComboPoints(target, 1); + ToPlayer()->AddComboPoints(procTarget, 1); StartReactiveTimer(REACTIVE_OVERPOWER); } } } } - - Unit* actor = isVictim ? target : this; - Unit* actionTarget = !isVictim ? target : this; - - DamageInfo damageInfo = DamageInfo(actor, actionTarget, damage, procSpell, procSpell ? SpellSchoolMask(procSpell->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL, SPELL_DIRECT_DAMAGE, attType); - HealInfo healInfo = HealInfo(actor, actionTarget, damage, procSpell, procSpell ? SpellSchoolMask(procSpell->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL); - ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, 0, procExtra, nullptr, &damageInfo, &healInfo); - - std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); - ProcTriggeredList procTriggered; - // Fill procTriggered list - for (AuraApplicationMap::const_iterator itr = GetAppliedAuras().begin(); itr!= GetAppliedAuras().end(); ++itr) - { - // Do not allow auras to proc from effect triggered by itself - if (procAura && procAura->Id == itr->first) - continue; - - if (itr->second->GetBase()->IsProcOnCooldown(now)) - continue; - - ProcTriggeredData triggerData(itr->second->GetBase()); - // Defensive procs are active on absorbs (so absorption effects are not a hindrance) - bool active = damage || (procExtra & PROC_EX_BLOCK && isVictim); - if (isVictim) - procExtra &= ~PROC_EX_INTERNAL_REQ_FAMILY; - - SpellInfo const* spellProto = itr->second->GetBase()->GetSpellInfo(); - - // only auras that has triggered spell should proc from fully absorbed damage - if (procExtra & PROC_EX_ABSORB && isVictim) - if (damage || spellProto->Effects[EFFECT_0].TriggerSpell || spellProto->Effects[EFFECT_1].TriggerSpell || spellProto->Effects[EFFECT_2].TriggerSpell) - active = true; - - if (!IsTriggeredAtSpellProcEvent(target, triggerData.aura, procSpell, procFlag, procExtra, attType, isVictim, active, triggerData.spellProcEvent)) - continue; - - // do checks using conditions table - if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_SPELL_PROC, spellProto->Id, eventInfo.GetActor(), eventInfo.GetActionTarget())) - continue; - - // AuraScript Hook - if (!triggerData.aura->CallScriptCheckProcHandlers(itr->second, eventInfo)) - continue; - - bool procSuccess = RollProcResult(target, triggerData.aura, attType, isVictim, triggerData.spellProcEvent); - if (!procSuccess) - continue; - - bool triggeredCanProcAura = true; - // Additional checks for triggered spells (ignore trap casts) - if (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION)) - { - if (!spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)) - triggeredCanProcAura = false; - } - - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (itr->second->HasEffect(i)) - { - AuraEffect* aurEff = itr->second->GetBase()->GetEffect(i); - // Skip this auras - if (isNonTriggerAura[aurEff->GetAuraType()]) - continue; - // If not trigger by default and spellProcEvent == NULL - skip - if (!isTriggerAura[aurEff->GetAuraType()] && triggerData.spellProcEvent == NULL) - continue; - // Some spells must always trigger - if (triggeredCanProcAura || isAlwaysTriggeredAura[aurEff->GetAuraType()]) - triggerData.effMask |= 1<<i; - } - } - if (triggerData.effMask) - procTriggered.push_front(triggerData); - } - - // Nothing found - if (procTriggered.empty()) - return; - - // Note: must SetCantProc(false) before return - if (procExtra & (PROC_EX_INTERNAL_TRIGGERED | PROC_EX_INTERNAL_CANT_PROC)) - SetCantProc(true); - - // Handle effects proceed this time - for (ProcTriggeredList::const_iterator i = procTriggered.begin(); i != procTriggered.end(); ++i) - { - // look for aura in auras list, it may be removed while proc event processing - if (i->aura->IsRemoved()) - continue; - - bool useCharges = i->aura->IsUsingCharges(); - // no more charges to use, prevent proc - if (useCharges && !i->aura->GetCharges()) - continue; - - bool takeCharges = false; - SpellInfo const* spellInfo = i->aura->GetSpellInfo(); - uint32 Id = i->aura->GetId(); - - AuraApplication* aurApp = i->aura->GetApplicationOfTarget(GetGUID()); - - bool prepare = i->aura->CallScriptPrepareProcHandlers(aurApp, eventInfo); - - Milliseconds cooldown = Milliseconds::zero(); - if (prepare && i->spellProcEvent && i->spellProcEvent->cooldown) - cooldown = Seconds(i->spellProcEvent->cooldown); - - // Note: must SetCantProc(false) before return - if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) - SetCantProc(true); - - bool handled = i->aura->CallScriptProcHandlers(aurApp, eventInfo); - - // "handled" is needed as long as proc can be handled in multiple places - if (!handled && HandleAuraProc(target, damage, i->aura, procSpell, procFlag, procExtra, &handled)) - { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), Id); - takeCharges = true; - } - - if (!handled) - { - for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) - { - if (!(i->effMask & (1 << effIndex))) - continue; - - AuraEffect* triggeredByAura = i->aura->GetEffect(effIndex); - ASSERT(triggeredByAura); - - bool prevented = i->aura->CallScriptEffectProcHandlers(triggeredByAura, aurApp, eventInfo); - if (prevented) - { - takeCharges = true; - continue; - } - - switch (triggeredByAura->GetAuraType()) - { - case SPELL_AURA_PROC_TRIGGER_SPELL: - { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", - spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); - // Don`t drop charge or add cooldown for not started trigger - if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra)) - takeCharges = true; - break; - } - case SPELL_AURA_PROC_TRIGGER_DAMAGE: - { - // target has to be valid - if (!eventInfo.GetProcTarget()) - break; - - triggeredByAura->HandleProcTriggerDamageAuraProc(aurApp, eventInfo); // this function is part of the new proc system - takeCharges = true; - break; - } - case SPELL_AURA_MANA_SHIELD: - case SPELL_AURA_DUMMY: - { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", - spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); - if (HandleDummyAuraProc(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) - takeCharges = true; - break; - } - case SPELL_AURA_OBS_MOD_POWER: - case SPELL_AURA_MOD_SPELL_CRIT_CHANCE: - case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN: - case SPELL_AURA_MOD_MELEE_HASTE: - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", - spellInfo->Id, isVictim ? "a victim's" : "an attacker's", triggeredByAura->GetId()); - takeCharges = true; - break; - case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS: - { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", - spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); - if (HandleOverrideClassScriptAuraProc(target, damage, triggeredByAura, procSpell)) - takeCharges = true; - break; - } - case SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE: - { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)", - (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); - - HandleAuraRaidProcFromChargeWithValue(triggeredByAura); - takeCharges = true; - break; - } - case SPELL_AURA_RAID_PROC_FROM_CHARGE: - { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)", - (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); - - HandleAuraRaidProcFromCharge(triggeredByAura); - takeCharges = true; - break; - } - case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE: - { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", - spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); - - if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra)) - takeCharges = true; - break; - } - case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK: - // Skip melee hits or instant cast spells - if (procSpell && procSpell->CalcCastTime() != 0) - takeCharges = true; - break; - case SPELL_AURA_REFLECT_SPELLS_SCHOOL: - // Skip Melee hits and spells ws wrong school - if (procSpell && (triggeredByAura->GetMiscValue() & procSpell->SchoolMask)) // School check - takeCharges = true; - break; - case SPELL_AURA_SPELL_MAGNET: - // Skip Melee hits and targets with magnet aura - if (procSpell && (triggeredByAura->GetBase()->GetUnitOwner()->ToUnit() == ToUnit())) // Magnet - takeCharges = true; - break; - case SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT: - case SPELL_AURA_MOD_POWER_COST_SCHOOL: - // Skip melee hits and spells ws wrong school or zero cost - if (procSpell && - (procSpell->ManaCost != 0 || procSpell->ManaCostPercentage != 0) && // Cost check - (triggeredByAura->GetMiscValue() & procSpell->SchoolMask)) // School check - takeCharges = true; - break; - case SPELL_AURA_MECHANIC_IMMUNITY: - // Compare mechanic - if (procSpell && procSpell->Mechanic == uint32(triggeredByAura->GetMiscValue())) - takeCharges = true; - break; - case SPELL_AURA_MOD_MECHANIC_RESISTANCE: - // Compare mechanic - if (procSpell && procSpell->Mechanic == uint32(triggeredByAura->GetMiscValue())) - takeCharges = true; - break; - case SPELL_AURA_MOD_DAMAGE_FROM_CASTER: - // Compare casters - if (triggeredByAura->GetCasterGUID() == target->GetGUID()) - takeCharges = true; - break; - // CC Auras which use their amount amount to drop - // Are there any more auras which need this? - case SPELL_AURA_MOD_CONFUSE: - case SPELL_AURA_MOD_FEAR: - case SPELL_AURA_MOD_STUN: - case SPELL_AURA_MOD_ROOT: - case SPELL_AURA_TRANSFORM: - { - // chargeable mods are breaking on hit - if (useCharges) - takeCharges = true; - else - { - // Spell own direct damage at apply wont break the CC - if (procSpell && (procSpell->Id == triggeredByAura->GetId())) - { - Aura* aura = triggeredByAura->GetBase(); - // called from spellcast, should not have ticked yet - if (aura->GetDuration() == aura->GetMaxDuration()) - break; - } - int32 damageLeft = triggeredByAura->GetAmount(); - // No damage left - if (damageLeft < int32(damage)) - i->aura->Remove(); - else - triggeredByAura->SetAmount(damageLeft - damage); - } - break; - } - //case SPELL_AURA_ADD_FLAT_MODIFIER: - //case SPELL_AURA_ADD_PCT_MODIFIER: - // HandleSpellModAuraProc - //break; - default: - // nothing do, just charges counter - takeCharges = true; - break; - } // switch (triggeredByAura->GetAuraType()) - i->aura->CallScriptAfterEffectProcHandlers(triggeredByAura, aurApp, eventInfo); - } // for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) - } // if (!handled) - - if (prepare && takeCharges && cooldown != Milliseconds::zero()) - i->aura->AddProcCooldown(now + cooldown); - - // Remove charge (aura can be removed by triggers) - if (prepare && useCharges && takeCharges) - { - // Set charge drop delay (only for missiles) - if ((procExtra & PROC_EX_REFLECT) && target && procSpell && procSpell->Speed > 0.0f) - { - // Set up missile speed based delay (from Spell.cpp: Spell::AddUnitTarget()::L2237) - uint32 delay = uint32(std::floor(std::max<float>(target->GetDistance(this), 5.0f) / procSpell->Speed * 1000.0f)); - // Schedule charge drop - i->aura->DropChargeDelayed(delay); - } - else - i->aura->DropCharge(); - } - - i->aura->CallScriptAfterProcHandlers(aurApp, eventInfo); - - if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) - SetCantProc(false); - } - - // Cleanup proc requirements - if (procExtra & (PROC_EX_INTERNAL_TRIGGERED | PROC_EX_INTERNAL_CANT_PROC)) - SetCantProc(false); } -void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTriggeringProc, std::list<AuraApplication*>* procAuras, ProcEventInfo eventInfo) +void Unit::GetProcAurasTriggeredOnEvent(AuraApplicationProcContainer& aurasTriggeringProc, AuraApplicationList* procAuras, ProcEventInfo& eventInfo) { std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); // use provided list of auras which can proc if (procAuras) { - for (std::list<AuraApplication*>::iterator itr = procAuras->begin(); itr!= procAuras->end(); ++itr) + for (AuraApplication* aurApp : *procAuras) { - ASSERT((*itr)->GetTarget() == this); - if (!(*itr)->GetRemoveMode()) - if ((*itr)->GetBase()->IsProcTriggeredOnEvent(*itr, eventInfo, now)) - { - (*itr)->GetBase()->PrepareProcToTrigger(*itr, eventInfo, now); - aurasTriggeringProc.push_back(*itr); - } + ASSERT(aurApp->GetTarget() == this); + if (uint8 procEffectMask = aurApp->GetBase()->IsProcTriggeredOnEvent(aurApp, eventInfo, now)) + { + aurApp->GetBase()->PrepareProcToTrigger(aurApp, eventInfo, now); + aurasTriggeringProc.emplace_back(std::make_pair(procEffectMask, aurApp)); + } } } // or generate one on our own else { - for (AuraApplicationMap::iterator itr = GetAppliedAuras().begin(); itr!= GetAppliedAuras().end(); ++itr) + for (AuraApplicationMap::iterator itr = GetAppliedAuras().begin(); itr != GetAppliedAuras().end(); ++itr) { - if (itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second, eventInfo, now)) + if (uint8 procEffectMask = itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second, eventInfo, now)) { itr->second->GetBase()->PrepareProcToTrigger(itr->second, eventInfo, now); - aurasTriggeringProc.push_back(itr->second); + aurasTriggeringProc.emplace_back(std::make_pair(procEffectMask, itr->second)); } } } @@ -14252,34 +10911,49 @@ void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTrigge void Unit::TriggerAurasProcOnEvent(CalcDamageInfo& damageInfo) { DamageInfo dmgInfo = DamageInfo(damageInfo); - TriggerAurasProcOnEvent(NULL, NULL, damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, 0, 0, damageInfo.procEx, NULL, &dmgInfo, NULL); + TriggerAurasProcOnEvent(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, PROC_SPELL_TYPE_NONE, PROC_SPELL_PHASE_NONE, dmgInfo.GetHitMask(), nullptr, &dmgInfo, nullptr); } -void Unit::TriggerAurasProcOnEvent(std::list<AuraApplication*>* myProcAuras, std::list<AuraApplication*>* targetProcAuras, Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo) +void Unit::TriggerAurasProcOnEvent(Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo) { // prepare data for self trigger - ProcEventInfo myProcEventInfo = ProcEventInfo(this, actionTarget, actionTarget, typeMaskActor, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); - std::list<AuraApplication*> myAurasTriggeringProc; - GetProcAurasTriggeredOnEvent(myAurasTriggeringProc, myProcAuras, myProcEventInfo); + ProcEventInfo myProcEventInfo(this, actionTarget, actionTarget, typeMaskActor, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); + if (typeMaskActor && CanProc()) + { + AuraApplicationProcContainer myAurasTriggeringProc; + GetProcAurasTriggeredOnEvent(myAurasTriggeringProc, nullptr, myProcEventInfo); + TriggerAurasProcOnEvent(myProcEventInfo, myAurasTriggeringProc); + } // prepare data for target trigger - ProcEventInfo targetProcEventInfo = ProcEventInfo(this, actionTarget, this, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); - std::list<AuraApplication*> targetAurasTriggeringProc; - if (typeMaskActionTarget) - GetProcAurasTriggeredOnEvent(targetAurasTriggeringProc, targetProcAuras, targetProcEventInfo); - - TriggerAurasProcOnEvent(myProcEventInfo, myAurasTriggeringProc); - - if (typeMaskActionTarget) - TriggerAurasProcOnEvent(targetProcEventInfo, targetAurasTriggeringProc); + ProcEventInfo targetProcEventInfo(this, actionTarget, this, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); + if (typeMaskActionTarget && actionTarget && actionTarget->CanProc()) + { + AuraApplicationProcContainer targetAurasTriggeringProc; + actionTarget->GetProcAurasTriggeredOnEvent(targetAurasTriggeringProc, nullptr, targetProcEventInfo); + actionTarget->TriggerAurasProcOnEvent(targetProcEventInfo, targetAurasTriggeringProc); + } } -void Unit::TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, std::list<AuraApplication*>& aurasTriggeringProc) +void Unit::TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationProcContainer& aurasTriggeringProc) { - for (std::list<AuraApplication*>::iterator itr = aurasTriggeringProc.begin(); itr != aurasTriggeringProc.end(); ++itr) + for (auto const& aurAppProc : aurasTriggeringProc) { - if (!(*itr)->GetRemoveMode()) - (*itr)->GetBase()->TriggerProcOnEvent(*itr, eventInfo); + AuraApplication* aurApp; + uint8 procEffectMask; + std::tie(procEffectMask, aurApp) = aurAppProc; + + if (aurApp->GetRemoveMode()) + continue; + + SpellInfo const* spellInfo = aurApp->GetBase()->GetSpellInfo(); + if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) + SetCantProc(true); + + aurApp->GetBase()->TriggerProcOnEvent(procEffectMask, aurApp, eventInfo); + + if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) + SetCantProc(false); } } @@ -14864,203 +11538,6 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) return true; } -bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent) -{ - SpellInfo const* spellInfo = aura->GetSpellInfo(); - - // let the aura be handled by new proc system if it has new entry - if (sSpellMgr->GetSpellProcEntry(spellInfo->Id)) - return false; - - // Get proc Event Entry - spellProcEvent = sSpellMgr->GetSpellProcEvent(spellInfo->Id); - - // Get EventProcFlag - uint32 EventProcFlag; - if (spellProcEvent && spellProcEvent->procFlags) // if exist get custom spellProcEvent->procFlags - EventProcFlag = spellProcEvent->procFlags; - else - EventProcFlag = spellInfo->ProcFlags; // else get from spell proto - // Continue if no trigger exist - if (!EventProcFlag) - return false; - - // Check spellProcEvent data requirements - if (!sSpellMgr->IsSpellProcEventCanTriggeredBy(spellInfo, spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active)) - return false; - // In most cases req get honor or XP from kill - if (EventProcFlag & PROC_FLAG_KILL && GetTypeId() == TYPEID_PLAYER) - { - bool allow = false; - - if (victim) - allow = ToPlayer()->isHonorOrXPTarget(victim); - - // Shadow Word: Death - can trigger from every kill - if (aura->GetId() == 32409) - allow = true; - if (!allow) - return false; - } - // Aura added by spell can`t trigger from self (prevent drop charges/do triggers) - // But except periodic and kill triggers (can triggered from self) - if (procSpell && procSpell->Id == spellInfo->Id - && !(spellInfo->ProcFlags&(PROC_FLAG_TAKEN_PERIODIC | PROC_FLAG_KILL))) - return false; - - // Check if current equipment allows aura to proc - if (!isVictim && GetTypeId() == TYPEID_PLAYER) - { - Player* player = ToPlayer(); - if (spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON) - { - Item* item = NULL; - if (attType == BASE_ATTACK) - item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); - else if (attType == OFF_ATTACK) - item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); - else - item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); - - if (player->IsInFeralForm()) - return false; - - if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_WEAPON || !((1<<item->GetTemplate()->SubClass) & spellInfo->EquippedItemSubClassMask)) - return false; - } - else if (spellInfo->EquippedItemClass == ITEM_CLASS_ARMOR) - { - // Check if player is wearing shield - Item* item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); - if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_ARMOR || !((1<<item->GetTemplate()->SubClass) & spellInfo->EquippedItemSubClassMask)) - return false; - } - } - - return true; -} - -bool Unit::RollProcResult(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, SpellProcEventEntry const* spellProcEvent) -{ - SpellInfo const* spellInfo = aura->GetSpellInfo(); - // Get chance from spell - float chance = float(spellInfo->ProcChance); - // If in spellProcEvent exist custom chance, chance = spellProcEvent->customChance; - if (spellProcEvent && spellProcEvent->customChance) - chance = spellProcEvent->customChance; - // If PPM exist calculate chance from PPM - if (spellProcEvent && spellProcEvent->ppmRate != 0) - { - if (!isVictim) - { - uint32 weaponSpeed = GetAttackTime(attType); - chance = GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellInfo); - } - else if (victim) - { - uint32 weaponSpeed = victim->GetAttackTime(attType); - chance = victim->GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellInfo); - } - } - // Apply chance modifer aura - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance); - - return roll_chance_f(chance); -} - -bool Unit::HandleAuraRaidProcFromChargeWithValue(AuraEffect* triggeredByAura) -{ - // aura can be deleted at casts - SpellInfo const* spellProto = triggeredByAura->GetSpellInfo(); - int32 heal = triggeredByAura->GetAmount(); - ObjectGuid caster_guid = triggeredByAura->GetCasterGUID(); - - // Currently only Prayer of Mending - if (!(spellProto->SpellFamilyName == SPELLFAMILY_PRIEST && spellProto->SpellFamilyFlags[1] & 0x20)) - { - TC_LOG_DEBUG("spells", "Unit::HandleAuraRaidProcFromChargeWithValue, received not handled spell: %u", spellProto->Id); - return false; - } - - // jumps - int32 jumps = triggeredByAura->GetBase()->GetCharges()-1; - - // current aura expire - triggeredByAura->GetBase()->SetCharges(1); // will removed at next charges decrease - - // next target selection - if (jumps > 0) - { - if (Unit* caster = triggeredByAura->GetCaster()) - { - float radius = triggeredByAura->GetSpellInfo()->Effects[triggeredByAura->GetEffIndex()].CalcRadius(caster); - - if (Unit* target = GetNextRandomRaidMemberOrPet(radius)) - { - CastCustomSpell(target, spellProto->Id, &heal, NULL, NULL, true, NULL, triggeredByAura, caster_guid); - if (Aura* aura = target->GetAura(spellProto->Id, caster->GetGUID())) - aura->SetCharges(jumps); - } - } - } - - // heal - CastCustomSpell(this, 33110, &heal, NULL, NULL, true, NULL, NULL, caster_guid); - return true; - -} -bool Unit::HandleAuraRaidProcFromCharge(AuraEffect* triggeredByAura) -{ - // aura can be deleted at casts - SpellInfo const* spellProto = triggeredByAura->GetSpellInfo(); - - uint32 damageSpellId; - switch (spellProto->Id) - { - case 57949: // shiver - damageSpellId = 57952; - //animationSpellId = 57951; dummy effects for jump spell have unknown use (see also 41637) - break; - case 59978: // shiver - damageSpellId = 59979; - break; - case 43593: // Cold Stare - damageSpellId = 43594; - break; - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleAuraRaidProcFromCharge, received unhandled spell: %u", spellProto->Id); - return false; - } - - ObjectGuid caster_guid = triggeredByAura->GetCasterGUID(); - - // jumps - int32 jumps = triggeredByAura->GetBase()->GetCharges()-1; - - // current aura expire - triggeredByAura->GetBase()->SetCharges(1); // will removed at next charges decrease - - // next target selection - if (jumps > 0) - { - if (Unit* caster = triggeredByAura->GetCaster()) - { - float radius = triggeredByAura->GetSpellInfo()->Effects[triggeredByAura->GetEffIndex()].CalcRadius(caster); - if (Unit* target= GetNextRandomRaidMemberOrPet(radius)) - { - CastSpell(target, spellProto, true, NULL, triggeredByAura, caster_guid); - if (Aura* aura = target->GetAura(spellProto->Id, caster->GetGUID())) - aura->SetCharges(jumps); - } - } - } - - CastSpell(this, damageSpellId, true, NULL, triggeredByAura, caster_guid); - - return true; -} - void Unit::Kill(Unit* victim, bool durabilityLoss) { // Prevent killing unit twice (and giving reward from kill twice) @@ -15155,14 +11632,17 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) // 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 if (Unit* owner = GetOwner()) - owner->ProcDamageAndSpell(victim, PROC_FLAG_KILL, PROC_FLAG_NONE, PROC_EX_NONE, 0); + owner->ProcSkillsAndAuras(victim, PROC_FLAG_KILL, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); + } if (!victim->IsCritter()) - ProcDamageAndSpell(victim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_EX_NONE, 0); + ProcSkillsAndAuras(victim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); // Proc auras on death - must be before aura/combat remove - victim->ProcDamageAndSpell(NULL, PROC_FLAG_DEATH, PROC_FLAG_NONE, PROC_EX_NONE, 0, BASE_ATTACK, 0); + victim->ProcSkillsAndAuras(victim, PROC_FLAG_NONE, PROC_FLAG_DEATH, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); // 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 @@ -16197,7 +12677,7 @@ float Unit::MeleeSpellMissChance(const Unit* victim, WeaponAttackType attType, i if (spellId) { if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_MISS_CHANCE, hitChance); + modOwner->ApplySpellMod<SPELLMOD_RESIST_MISS_CHANCE>(spellId, hitChance); } missChance += hitChance - 100.0f; diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 1a782c3e89f..b5081fcdab6 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -81,42 +81,42 @@ enum SpellAuraInterruptFlags AURA_INTERRUPT_FLAG_NOT_VICTIM = (AURA_INTERRUPT_FLAG_HITBYSPELL | AURA_INTERRUPT_FLAG_TAKE_DAMAGE | AURA_INTERRUPT_FLAG_DIRECT_DAMAGE) }; -enum SpellModOp -{ - SPELLMOD_DAMAGE = 0, - SPELLMOD_DURATION = 1, - SPELLMOD_THREAT = 2, - SPELLMOD_EFFECT1 = 3, - SPELLMOD_CHARGES = 4, - SPELLMOD_RANGE = 5, - SPELLMOD_RADIUS = 6, - SPELLMOD_CRITICAL_CHANCE = 7, - SPELLMOD_ALL_EFFECTS = 8, - SPELLMOD_NOT_LOSE_CASTING_TIME = 9, - SPELLMOD_CASTING_TIME = 10, - SPELLMOD_COOLDOWN = 11, - SPELLMOD_EFFECT2 = 12, - SPELLMOD_IGNORE_ARMOR = 13, - SPELLMOD_COST = 14, - SPELLMOD_CRIT_DAMAGE_BONUS = 15, - SPELLMOD_RESIST_MISS_CHANCE = 16, - SPELLMOD_JUMP_TARGETS = 17, - SPELLMOD_CHANCE_OF_SUCCESS = 18, - SPELLMOD_ACTIVATION_TIME = 19, - SPELLMOD_DAMAGE_MULTIPLIER = 20, - SPELLMOD_GLOBAL_COOLDOWN = 21, - SPELLMOD_DOT = 22, - SPELLMOD_EFFECT3 = 23, - SPELLMOD_BONUS_MULTIPLIER = 24, +enum SpellModOp : uint8 +{ + SPELLMOD_DAMAGE = 0, + SPELLMOD_DURATION = 1, + SPELLMOD_THREAT = 2, + SPELLMOD_EFFECT1 = 3, + SPELLMOD_CHARGES = 4, + SPELLMOD_RANGE = 5, + SPELLMOD_RADIUS = 6, + SPELLMOD_CRITICAL_CHANCE = 7, + SPELLMOD_ALL_EFFECTS = 8, + SPELLMOD_NOT_LOSE_CASTING_TIME = 9, + SPELLMOD_CASTING_TIME = 10, + SPELLMOD_COOLDOWN = 11, + SPELLMOD_EFFECT2 = 12, + SPELLMOD_IGNORE_ARMOR = 13, + SPELLMOD_COST = 14, + SPELLMOD_CRIT_DAMAGE_BONUS = 15, + SPELLMOD_RESIST_MISS_CHANCE = 16, + SPELLMOD_JUMP_TARGETS = 17, + SPELLMOD_CHANCE_OF_SUCCESS = 18, + SPELLMOD_ACTIVATION_TIME = 19, + SPELLMOD_DAMAGE_MULTIPLIER = 20, + SPELLMOD_GLOBAL_COOLDOWN = 21, + SPELLMOD_DOT = 22, + SPELLMOD_EFFECT3 = 23, + SPELLMOD_BONUS_MULTIPLIER = 24, // spellmod 25 - SPELLMOD_PROC_PER_MINUTE = 26, - SPELLMOD_VALUE_MULTIPLIER = 27, - SPELLMOD_RESIST_DISPEL_CHANCE = 28, - SPELLMOD_CRIT_DAMAGE_BONUS_2 = 29, //one not used spell - SPELLMOD_SPELL_COST_REFUND_ON_FAIL = 30 -}; + SPELLMOD_PROC_PER_MINUTE = 26, + SPELLMOD_VALUE_MULTIPLIER = 27, + SPELLMOD_RESIST_DISPEL_CHANCE = 28, + SPELLMOD_CRIT_DAMAGE_BONUS_2 = 29, //one not used spell + SPELLMOD_SPELL_COST_REFUND_ON_FAIL = 30, -#define MAX_SPELLMOD 32 + MAX_SPELLMOD +}; enum SpellValueMod { @@ -426,7 +426,7 @@ enum TriggerCastFlags TRIGGERED_IGNORE_CASTER_AURASTATE = 0x00000800, //! Will ignore caster aura states including combat requirements and death state TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE = 0x00002000, //! Will ignore mounted/on vehicle restrictions TRIGGERED_IGNORE_CASTER_AURAS = 0x00010000, //! Will ignore caster aura restrictions or requirements - TRIGGERED_DISALLOW_PROC_EVENTS = 0x00020000, //! Disallows proc events from triggered spell (default) + // reuse 0x00020000 TRIGGERED_DONT_REPORT_CAST_ERROR = 0x00040000, //! Will return SPELL_FAILED_DONT_REPORT in CheckCast functions TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT = 0x00080000, //! Will ignore equipped item requirements TRIGGERED_FULL_MASK = 0xFFFFFFFF @@ -805,13 +805,13 @@ struct DiminishingReturn : DRGroup(group), stack(0), hitTime(t), hitCount(count) { } - DiminishingGroup DRGroup:16; - uint16 stack:16; + DiminishingGroup DRGroup; + uint16 stack; uint32 hitTime; uint32 hitCount; }; -enum MeleeHitOutcome +enum MeleeHitOutcome : uint8 { MELEE_HIT_EVADE, MELEE_HIT_MISS, MELEE_HIT_DODGE, MELEE_HIT_BLOCK, MELEE_HIT_PARRY, MELEE_HIT_GLANCING, MELEE_HIT_CRIT, MELEE_HIT_CRUSHING, MELEE_HIT_NORMAL @@ -819,21 +819,18 @@ enum MeleeHitOutcome class DispelInfo { -public: - explicit DispelInfo(Unit* dispeller, uint32 dispellerSpellId, uint8 chargesRemoved) : - _dispellerUnit(dispeller), _dispellerSpell(dispellerSpellId), _chargesRemoved(chargesRemoved) { } + public: + explicit DispelInfo(Unit* dispeller, uint32 dispellerSpellId, uint8 chargesRemoved) : + _dispellerUnit(dispeller), _dispellerSpell(dispellerSpellId), _chargesRemoved(chargesRemoved) { } - Unit* GetDispeller() const { return _dispellerUnit; } - uint32 GetDispellerSpellId() const { return _dispellerSpell; } - uint8 GetRemovedCharges() const { return _chargesRemoved; } - void SetRemovedCharges(uint8 amount) - { - _chargesRemoved = amount; - } -private: - Unit* _dispellerUnit; - uint32 _dispellerSpell; - uint8 _chargesRemoved; + Unit* GetDispeller() const { return _dispellerUnit; } + uint32 GetDispellerSpellId() const { return _dispellerSpell; } + uint8 GetRemovedCharges() const { return _chargesRemoved; } + void SetRemovedCharges(uint8 amount) { _chargesRemoved = amount; } + private: + Unit* _dispellerUnit; + uint32 _dispellerSpell; + uint8 _chargesRemoved; }; struct CleanDamage @@ -849,104 +846,110 @@ struct CleanDamage }; struct CalcDamageInfo; +struct SpellNonMeleeDamage; class TC_GAME_API DamageInfo { -private: - Unit* const m_attacker; - Unit* const m_victim; - uint32 m_damage; - SpellInfo const* const m_spellInfo; - SpellSchoolMask const m_schoolMask; - DamageEffectType const m_damageType; - WeaponAttackType m_attackType; - uint32 m_absorb; - uint32 m_resist; - uint32 m_block; - uint32 m_hitMask; -public: - DamageInfo(Unit* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, SpellSchoolMask schoolMask, DamageEffectType damageType, WeaponAttackType attackType); - explicit DamageInfo(CalcDamageInfo const& dmgInfo); - DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType); - - void ModifyDamage(int32 amount); - void AbsorbDamage(uint32 amount); - void ResistDamage(uint32 amount); - void BlockDamage(uint32 amount); - - Unit* GetAttacker() const { return m_attacker; } - Unit* GetVictim() const { return m_victim; } - SpellInfo const* GetSpellInfo() const { return m_spellInfo; } - SpellSchoolMask GetSchoolMask() const { return m_schoolMask; } - DamageEffectType GetDamageType() const { return m_damageType; } - WeaponAttackType GetAttackType() const { return m_attackType; } - uint32 GetDamage() const { return m_damage; } - uint32 GetAbsorb() const { return m_absorb; } - uint32 GetResist() const { return m_resist; } - uint32 GetBlock() const { return m_block; } - - uint32 GetHitMask() const; + private: + Unit* const m_attacker; + Unit* const m_victim; + uint32 m_damage; + SpellInfo const* const m_spellInfo; + SpellSchoolMask const m_schoolMask; + DamageEffectType const m_damageType; + WeaponAttackType m_attackType; + uint32 m_absorb; + uint32 m_resist; + uint32 m_block; + uint32 m_hitMask; + public: + DamageInfo(Unit* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, SpellSchoolMask schoolMask, DamageEffectType damageType, WeaponAttackType attackType); + explicit DamageInfo(CalcDamageInfo const& dmgInfo); + DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType, uint32 hitMask); + + void ModifyDamage(int32 amount); + void AbsorbDamage(uint32 amount); + void ResistDamage(uint32 amount); + void BlockDamage(uint32 amount); + + Unit* GetAttacker() const { return m_attacker; } + Unit* GetVictim() const { return m_victim; } + SpellInfo const* GetSpellInfo() const { return m_spellInfo; } + SpellSchoolMask GetSchoolMask() const { return m_schoolMask; } + DamageEffectType GetDamageType() const { return m_damageType; } + WeaponAttackType GetAttackType() const { return m_attackType; } + uint32 GetDamage() const { return m_damage; } + uint32 GetAbsorb() const { return m_absorb; } + uint32 GetResist() const { return m_resist; } + uint32 GetBlock() const { return m_block; } + + uint32 GetHitMask() const; }; class TC_GAME_API HealInfo { -private: - Unit* const _healer; - Unit* const _target; - uint32 _heal; - uint32 _absorb; - SpellInfo const* const _spellInfo; - SpellSchoolMask const _schoolMask; - uint32 _hitMask; + private: + Unit* const _healer; + Unit* const _target; + uint32 _heal; + uint32 _effectiveHeal; + uint32 _absorb; + SpellInfo const* const _spellInfo; + SpellSchoolMask const _schoolMask; + uint32 _hitMask; -public: - explicit HealInfo(Unit* healer, Unit* target, uint32 heal, SpellInfo const* spellInfo, SpellSchoolMask schoolMask); + public: + HealInfo(Unit* healer, Unit* target, uint32 heal, SpellInfo const* spellInfo, SpellSchoolMask schoolMask); - void AbsorbHeal(uint32 amount); + void AbsorbHeal(uint32 amount); + void SetEffectiveHeal(uint32 amount) { _effectiveHeal = amount; } - Unit* GetHealer() const { return _healer; } - Unit* GetTarget() const { return _target; } - uint32 GetHeal() const { return _heal; } - uint32 GetAbsorb() const { return _absorb; } - SpellInfo const* GetSpellInfo() const { return _spellInfo; }; - SpellSchoolMask GetSchoolMask() const { return _schoolMask; }; + Unit* GetHealer() const { return _healer; } + Unit* GetTarget() const { return _target; } + uint32 GetHeal() const { return _heal; } + uint32 GetEffectiveHeal() const { return _effectiveHeal; } + uint32 GetAbsorb() const { return _absorb; } + SpellInfo const* GetSpellInfo() const { return _spellInfo; }; + SpellSchoolMask GetSchoolMask() const { return _schoolMask; }; - uint32 GetHitMask() const; + uint32 GetHitMask() const; }; class TC_GAME_API ProcEventInfo { -public: - ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, uint32 typeMask, - uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, - Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo); + public: + ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, uint32 typeMask, + uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, + Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo); - Unit* GetActor() { return _actor; } - Unit* GetActionTarget() const { return _actionTarget; } - Unit* GetProcTarget() const { return _procTarget; } + Unit* GetActor() { return _actor; } + Unit* GetActionTarget() const { return _actionTarget; } + Unit* GetProcTarget() const { return _procTarget; } - uint32 GetTypeMask() const { return _typeMask; } - uint32 GetSpellTypeMask() const { return _spellTypeMask; } - uint32 GetSpellPhaseMask() const { return _spellPhaseMask; } - uint32 GetHitMask() const { return _hitMask; } + uint32 GetTypeMask() const { return _typeMask; } + uint32 GetSpellTypeMask() const { return _spellTypeMask; } + uint32 GetSpellPhaseMask() const { return _spellPhaseMask; } + uint32 GetHitMask() const { return _hitMask; } - SpellInfo const* GetSpellInfo() const; - SpellSchoolMask GetSchoolMask() const; + SpellInfo const* GetSpellInfo() const; + SpellSchoolMask GetSchoolMask() const; - DamageInfo* GetDamageInfo() const { return _damageInfo; } - HealInfo* GetHealInfo() const { return _healInfo; } + DamageInfo* GetDamageInfo() const { return _damageInfo; } + HealInfo* GetHealInfo() const { return _healInfo; } -private: - Unit* const _actor; - Unit* const _actionTarget; - Unit* const _procTarget; - uint32 _typeMask; - uint32 _spellTypeMask; - uint32 _spellPhaseMask; - uint32 _hitMask; - Spell* _spell; - DamageInfo* _damageInfo; - HealInfo* _healInfo; + Spell const* GetProcSpell() const { return _spell; } + + private: + Unit* const _actor; + Unit* const _actionTarget; + Unit* const _procTarget; + uint32 _typeMask; + uint32 _spellTypeMask; + uint32 _spellPhaseMask; + uint32 _hitMask; + Spell* _spell; + DamageInfo* _damageInfo; + HealInfo* _healInfo; }; // Struct for use in Unit::CalculateMeleeDamage @@ -966,7 +969,6 @@ struct CalcDamageInfo WeaponAttackType attackType; // uint32 procAttacker; uint32 procVictim; - uint32 procEx; uint32 cleanDamage; // Used only for rage calculation MeleeHitOutcome hitOutCome; /// @todo remove this field (need use TargetState) }; @@ -1009,7 +1011,7 @@ struct SpellPeriodicAuraLogInfo bool critical; }; -uint32 createProcExtendMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition); +uint32 createProcHitMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition); struct RedirectThreatInfo { @@ -1233,8 +1235,6 @@ enum PlayerTotemType #define ATTACK_DISPLAY_DELAY 200 #define MAX_PLAYER_STEALTH_DETECT_RANGE 30.0f // max distance for detection targets by player -struct SpellProcEventEntry; // used only privately - class TC_GAME_API Unit : public WorldObject { public: @@ -1257,6 +1257,8 @@ class TC_GAME_API Unit : public WorldObject typedef std::list<AuraApplication *> AuraApplicationList; typedef std::list<DiminishingReturn> Diminishing; + typedef std::deque<std::pair<uint8 /*procEffectMask*/, AuraApplication*>> AuraApplicationProcContainer; + typedef std::map<uint8, AuraApplication*> VisibleAuraMap; virtual ~Unit(); @@ -1430,18 +1432,18 @@ class TC_GAME_API Unit : public WorldObject uint32 DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDamage = NULL, DamageEffectType damagetype = DIRECT_DAMAGE, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* spellProto = NULL, bool durabilityLoss = true); void Kill(Unit* victim, bool durabilityLoss = true); void KillSelf(bool durabilityLoss = true) { Kill(this, durabilityLoss); } - int32 DealHeal(Unit* victim, uint32 addhealth); + void DealHeal(HealInfo& healInfo); - void ProcDamageAndSpell(Unit* victim, uint32 procAttacker, uint32 procVictim, uint32 procEx, uint32 amount, WeaponAttackType attType = BASE_ATTACK, SpellInfo const* procSpell = NULL, SpellInfo const* procAura = NULL); - void ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellInfo const* procSpell, uint32 damage, SpellInfo const* procAura = NULL); + void ProcSkillsAndAuras(Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, + uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, + DamageInfo* damageInfo, HealInfo* healInfo); - void GetProcAurasTriggeredOnEvent(AuraApplicationList& aurasTriggeringProc, AuraApplicationList* procAuras, ProcEventInfo eventInfo); + void GetProcAurasTriggeredOnEvent(AuraApplicationProcContainer& aurasTriggeringProc, AuraApplicationList* procAuras, ProcEventInfo& eventInfo); void TriggerAurasProcOnEvent(CalcDamageInfo& damageInfo); - void TriggerAurasProcOnEvent(AuraApplicationList* myProcAuras, AuraApplicationList* targetProcAuras, - Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, + void TriggerAurasProcOnEvent(Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo); - void TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationList& procAuras); + void TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationProcContainer& procAuras); void HandleEmoteCommand(uint32 anim_id); void AttackerStateUpdate (Unit* victim, WeaponAttackType attType = BASE_ATTACK, bool extra = false); @@ -1548,8 +1550,8 @@ class TC_GAME_API Unit : public WorldObject virtual void UpdateUnderwaterState(Map* m, float x, float y, float z); bool isInAccessiblePlaceFor(Creature const* c) const; - void SendHealSpellLog(Unit* victim, uint32 SpellID, uint32 Damage, uint32 OverHeal, uint32 Absorb, bool critical = false); - int32 HealBySpell(Unit* victim, SpellInfo const* spellInfo, uint32 addHealth, bool critical = false); + void SendHealSpellLog(HealInfo& healInfo, bool critical = false); + int32 HealBySpell(HealInfo& healInfo, bool critical = false); void SendEnergizeSpellLog(Unit* victim, uint32 spellID, int32 damage, Powers powerType); void EnergizeBySpell(Unit* victim, uint32 SpellID, int32 Damage, Powers powertype); uint32 SpellNonMeleeDamageLog(Unit* victim, uint32 spellID, uint32 damage); @@ -2028,7 +2030,7 @@ class TC_GAME_API Unit : public WorldObject uint32 CalcArmorReducedDamage(Unit* victim, const uint32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType = MAX_ATTACK); uint32 CalcSpellResistance(Unit* victim, SpellSchoolMask schoolMask, SpellInfo const* spellInfo) const; void CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffectType damagetype, uint32 const damage, uint32* absorb, uint32* resist, SpellInfo const* spellInfo = NULL); - void CalcHealAbsorb(Unit* victim, SpellInfo const* spellInfo, uint32& healAmount, uint32& absorb); + void CalcHealAbsorb(HealInfo& healInfo); void UpdateSpeed(UnitMoveType mtype); float GetSpeed(UnitMoveType mtype) const; @@ -2090,7 +2092,7 @@ class TC_GAME_API Unit : public WorldObject void UpdateAuraForGroup(uint8 slot); // proc trigger system - bool CanProc() const {return !m_procDeep;} + bool CanProc() const { return !m_procDeep; } void SetCantProc(bool apply); // pet auras @@ -2261,15 +2263,8 @@ class TC_GAME_API Unit : public WorldObject bool IsAlwaysDetectableFor(WorldObject const* seer) const override; void DisableSpline(); + private: - bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent); - bool RollProcResult(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, SpellProcEventEntry const* spellProcEvent); - bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, Milliseconds& cooldown); - bool HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, bool* handled); - bool HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx); - bool HandleOverrideClassScriptAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell); - bool HandleAuraRaidProcFromChargeWithValue(AuraEffect* triggeredByAura); - bool HandleAuraRaidProcFromCharge(AuraEffect* triggeredByAura); void UpdateSplineMovement(uint32 t_diff); void UpdateSplinePosition(); @@ -2278,6 +2273,8 @@ class TC_GAME_API Unit : public WorldObject float GetCombatRatingReduction(CombatRating cr) const; uint32 GetCombatRatingDamageReduction(CombatRating cr, float rate, float cap, uint32 damage) const; + void ProcSkillsAndReactives(bool isVictim, Unit* procTarget, uint32 typeMask, uint32 hitMask, WeaponAttackType attType); + void SetFeared(bool apply); void SetConfused(bool apply); void SetStunned(bool apply); diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 0475cfff28c..f7c440ce8bc 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -3114,7 +3114,7 @@ enum DiminishingReturnsType }; // Diminishing Return Groups -enum DiminishingGroup +enum DiminishingGroup : uint16 { DIMINISHING_NONE = 0, DIMINISHING_BANISH = 1, diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index ed49ed197cc..346ee22dcee 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -99,8 +99,8 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleAuraModSchoolImmunity, // 39 SPELL_AURA_SCHOOL_IMMUNITY &AuraEffect::HandleAuraModDmgImmunity, // 40 SPELL_AURA_DAMAGE_IMMUNITY &AuraEffect::HandleAuraModDispelImmunity, // 41 SPELL_AURA_DISPEL_IMMUNITY - &AuraEffect::HandleNoImmediateEffect, // 42 SPELL_AURA_PROC_TRIGGER_SPELL implemented in Unit::ProcDamageAndSpellFor and Unit::HandleProcTriggerSpell - &AuraEffect::HandleNoImmediateEffect, // 43 SPELL_AURA_PROC_TRIGGER_DAMAGE implemented in Unit::ProcDamageAndSpellFor + &AuraEffect::HandleNoImmediateEffect, // 42 SPELL_AURA_PROC_TRIGGER_SPELL implemented in AuraEffect::HandleProc + &AuraEffect::HandleNoImmediateEffect, // 43 SPELL_AURA_PROC_TRIGGER_DAMAGE implemented in AuraEffect::HandleProc &AuraEffect::HandleAuraTrackCreatures, // 44 SPELL_AURA_TRACK_CREATURES &AuraEffect::HandleAuraTrackResources, // 45 SPELL_AURA_TRACK_RESOURCES &AuraEffect::HandleNULL, // 46 SPELL_AURA_46 (used in test spells 54054 and 54058, and spell 48050) (3.0.8a) @@ -319,7 +319,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleNoImmediateEffect, //259 SPELL_AURA_MOD_HOT_PCT implemented in Unit::SpellHealingBonus &AuraEffect::HandleNoImmediateEffect, //260 SPELL_AURA_SCREEN_EFFECT (miscvalue = id in ScreenEffect.dbc) not required any code &AuraEffect::HandlePhase, //261 SPELL_AURA_PHASE - &AuraEffect::HandleNoImmediateEffect, //262 SPELL_AURA_ABILITY_IGNORE_AURASTATE implemented in spell::cancast + &AuraEffect::HandleNoImmediateEffect, //262 SPELL_AURA_ABILITY_IGNORE_AURASTATE implemented in Spell::CheckCast &AuraEffect::HandleAuraAllowOnlyAbility, //263 SPELL_AURA_ALLOW_ONLY_ABILITY player can use only abilities set in SpellClassMask &AuraEffect::HandleUnused, //264 unused (3.2.0) &AuraEffect::HandleUnused, //265 unused (3.2.0) @@ -535,7 +535,7 @@ void AuraEffect::CalculatePeriodic(Unit* caster, bool create, bool load) { // Apply periodic time mod if (modOwner) - modOwner->ApplySpellMod(GetId(), SPELLMOD_ACTIVATION_TIME, m_amplitude); + modOwner->ApplySpellMod<SPELLMOD_ACTIVATION_TIME>(GetId(), m_amplitude); if (caster) { @@ -588,7 +588,6 @@ void AuraEffect::CalculateSpellMod() m_spellmod->type = SpellModType(uint32(GetAuraType())); // SpellModType value == spell aura types m_spellmod->spellId = GetId(); m_spellmod->mask = GetSpellInfo()->Effects[GetEffIndex()].SpellClassMask; - m_spellmod->charges = GetBase()->GetCharges(); } m_spellmod->value = GetAmount(); break; @@ -961,6 +960,50 @@ void AuraEffect::PeriodicTick(AuraApplication * aurApp, Unit* caster) const } } +bool AuraEffect::CheckEffectProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) const +{ + bool result = GetBase()->CallScriptCheckEffectProcHandlers(this, aurApp, eventInfo); + if (!result) + return false; + + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + switch (GetAuraType()) + { + case SPELL_AURA_MECHANIC_IMMUNITY: + case SPELL_AURA_MOD_MECHANIC_RESISTANCE: + // compare mechanic + if (!spellInfo || static_cast<int32>(spellInfo->Mechanic) != GetMiscValue()) + result = false; + break; + case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK: + // skip melee hits and instant cast spells + if (!spellInfo || !spellInfo->CalcCastTime()) + result = false; + break; + case SPELL_AURA_MOD_DAMAGE_FROM_CASTER: + // Compare casters + if (GetCasterGUID() != eventInfo.GetActor()->GetGUID()) + result = false; + break; + case SPELL_AURA_MOD_POWER_COST_SCHOOL: + case SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT: + // Skip melee hits and spells with wrong school or zero cost + if (!spellInfo || (!spellInfo->ManaCost && !spellInfo->ManaCostPercentage) || // Cost Check + !(spellInfo->GetSchoolMask() & GetMiscValue())) // School Check + result = false; + break; + case SPELL_AURA_REFLECT_SPELLS_SCHOOL: + // Skip melee hits and spells with wrong school + if (!spellInfo || !(spellInfo->GetSchoolMask() & GetMiscValue())) + result = false; + break; + default: + break; + } + + return result; +} + void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) { bool prevented = GetBase()->CallScriptEffectProcHandlers(this, aurApp, eventInfo); @@ -969,6 +1012,16 @@ void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) switch (GetAuraType()) { + // CC Auras which use their amount to drop + // Are there any more auras which need this? + case SPELL_AURA_MOD_CONFUSE: + case SPELL_AURA_MOD_FEAR: + case SPELL_AURA_MOD_STUN: + case SPELL_AURA_MOD_ROOT: + case SPELL_AURA_TRANSFORM: + HandleBreakableCCAuraProc(aurApp, eventInfo); + break; + case SPELL_AURA_DUMMY: case SPELL_AURA_PROC_TRIGGER_SPELL: HandleProcTriggerSpellAuraProc(aurApp, eventInfo); break; @@ -4667,10 +4720,6 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool if (target->GetTypeId() == TYPEID_PLAYER) target->ToPlayer()->RemoveAmmo(); // not use ammo and not allow use break; - case 71563: - if (Aura* newAura = target->AddAura(71564, target)) - newAura->SetStackAmount(newAura->GetSpellInfo()->StackAmount); - break; } } // AT REMOVE @@ -5470,8 +5519,10 @@ void AuraEffect::HandlePeriodicTriggerSpellAuraTick(Unit* target, Unit* caster) if (caster) { int32 heal = caster->CountPctFromMaxHealth(10); - caster->HealBySpell(target, auraSpellInfo, heal); + HealInfo healInfo(caster, target, heal, auraSpellInfo, auraSpellInfo->GetSchoolMask()); + caster->HealBySpell(healInfo); + /// @todo: should proc other auras? if (int32 mana = caster->GetMaxPower(POWER_MANA)) { mana /= 10; @@ -5789,7 +5840,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const damage = std::max(int32(damage * GetDonePct()), 0); if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetSpellInfo()->Id, SPELLMOD_DOT, damage); + modOwner->ApplySpellMod<SPELLMOD_DOT>(GetSpellInfo()->Id, damage); damage = target->SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DOT, GetBase()->GetStackAmount()); @@ -5847,12 +5898,14 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const damage = uint32(target->CountPctFromMaxHealth(damage)); if (!m_spellInfo->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) + { if (m_spellInfo->Effects[m_effIndex].IsTargetingArea() || isAreaAura) { damage = int32(float(damage) * target->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE, m_spellInfo->SchoolMask)); if (caster->GetTypeId() != TYPEID_PLAYER) damage = int32(float(damage) * target->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CREATURE_AOE_DAMAGE_AVOIDANCE, m_spellInfo->SchoolMask)); } + } bool crit = false; @@ -5878,7 +5931,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const // Set trigger flag uint32 procAttacker = PROC_FLAG_DONE_PERIODIC; uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC; - uint32 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_DOT; + uint32 hitMask = crit ? PROC_HIT_CRITICAL : PROC_HIT_NORMAL; damage = (damage <= absorb+resist) ? 0 : (damage-absorb-resist); if (damage) procVictim |= PROC_FLAG_TAKEN_DAMAGE; @@ -5890,7 +5943,8 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const SpellPeriodicAuraLogInfo pInfo(this, damage, overkill, absorb, resist, 0.0f, crit); target->SendPeriodicAuraLog(&pInfo); - caster->ProcDamageAndSpell(target, procAttacker, procVictim, procEx, damage, BASE_ATTACK, GetSpellInfo()); + DamageInfo damageInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask(), DOT, BASE_ATTACK); + caster->ProcSkillsAndAuras(target, procAttacker, procVictim, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_NONE, hitMask, nullptr, &damageInfo, nullptr); caster->DealDamage(target, damage, &cleanDamage, DOT, GetSpellInfo()->GetSchoolMask(), GetSpellInfo(), true); } @@ -5967,12 +6021,17 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c // Set trigger flag uint32 procAttacker = PROC_FLAG_DONE_PERIODIC; uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC; - uint32 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_DOT; + uint32 hitMask = crit ? PROC_HIT_CRITICAL : PROC_HIT_NORMAL; damage = (damage <= absorb+resist) ? 0 : (damage-absorb-resist); if (damage) procVictim |= PROC_FLAG_TAKEN_DAMAGE; + if (caster->IsAlive()) - caster->ProcDamageAndSpell(target, procAttacker, procVictim, procEx, damage, BASE_ATTACK, GetSpellInfo()); + { + DamageInfo damageInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask(), DOT, BASE_ATTACK); + caster->ProcSkillsAndAuras(target, procAttacker, procVictim, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_NONE, hitMask, nullptr, &damageInfo, nullptr); + } + int32 new_damage = caster->DealDamage(target, damage, &cleanDamage, DOT, GetSpellInfo()->GetSchoolMask(), GetSpellInfo(), false); if (caster->IsAlive()) { @@ -5981,8 +6040,11 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c uint32 heal = uint32(caster->SpellHealingBonusDone(caster, GetSpellInfo(), uint32(new_damage * gainMultiplier), DOT, GetBase()->GetStackAmount())); heal = uint32(caster->SpellHealingBonusTaken(caster, GetSpellInfo(), heal, DOT, GetBase()->GetStackAmount())); - int32 gain = caster->HealBySpell(caster, GetSpellInfo(), heal); - caster->getHostileRefManager().threatAssist(caster, gain * 0.5f, GetSpellInfo()); + HealInfo healInfo(caster, caster, heal, GetSpellInfo(), GetSpellInfo()->GetSchoolMask()); + caster->HealBySpell(healInfo); + + caster->getHostileRefManager().threatAssist(caster, healInfo.GetEffectiveHeal() * 0.5f, GetSpellInfo()); + caster->ProcSkillsAndAuras(caster, PROC_FLAG_DONE_PERIODIC, PROC_FLAG_TAKEN_PERIODIC, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_NONE, hitMask, nullptr, nullptr, &healInfo); } } @@ -6011,7 +6073,9 @@ void AuraEffect::HandlePeriodicHealthFunnelAuraTick(Unit* target, Unit* caster) damage = int32(damage * gainMultiplier); - caster->HealBySpell(target, GetSpellInfo(), damage); + HealInfo healInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask()); + caster->HealBySpell(healInfo); + caster->ProcSkillsAndAuras(target, PROC_FLAG_DONE_PERIODIC, PROC_FLAG_TAKEN_PERIODIC, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_NONE, PROC_HIT_NORMAL, nullptr, nullptr, &healInfo); } void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const @@ -6093,7 +6157,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const damage = std::max(int32(damage * GetDonePct()), 0); if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetSpellInfo()->Id, SPELLMOD_DOT, damage); + modOwner->ApplySpellMod<SPELLMOD_DOT>(GetSpellInfo()->Id, damage); damage = target->SpellHealingBonusTaken(caster, GetSpellInfo(), damage, DOT, GetBase()->GetStackAmount()); } @@ -6109,15 +6173,16 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const TC_LOG_DEBUG("spells.periodic", "PeriodicTick: %s heal of %s for %u health inflicted by %u", GetCasterGUID().ToString().c_str(), target->GetGUID().ToString().c_str(), damage, GetId()); - uint32 absorb = 0; uint32 heal = damage; - caster->CalcHealAbsorb(target, GetSpellInfo(), heal, absorb); - int32 gain = caster->DealHeal(target, heal); - SpellPeriodicAuraLogInfo pInfo(this, heal, heal - gain, absorb, 0, 0.0f, crit); + HealInfo healInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask()); + caster->CalcHealAbsorb(healInfo); + caster->DealHeal(healInfo); + + SpellPeriodicAuraLogInfo pInfo(this, heal, heal - healInfo.GetEffectiveHeal(), healInfo.GetAbsorb(), 0, 0.0f, crit); target->SendPeriodicAuraLog(&pInfo); - target->getHostileRefManager().threatAssist(caster, float(gain) * 0.5f, GetSpellInfo()); + target->getHostileRefManager().threatAssist(caster, float(healInfo.GetEffectiveHeal()) * 0.5f, GetSpellInfo()); bool haveCastItem = !GetBase()->GetCastItemGUID().IsEmpty(); @@ -6127,8 +6192,8 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const { uint32 funnelDamage = GetSpellInfo()->ManaPerSecond; // damage is not affected by spell power - if ((int32)funnelDamage > gain && gain > 0) - funnelDamage = gain; + if (funnelDamage > healInfo.GetEffectiveHeal() && healInfo.GetEffectiveHeal()) + funnelDamage = healInfo.GetEffectiveHeal(); uint32 funnelAbsorb = 0; caster->DealDamageMods(caster, funnelDamage, &funnelAbsorb); @@ -6140,10 +6205,10 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const uint32 procAttacker = PROC_FLAG_DONE_PERIODIC; uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC; - uint32 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_HOT; + uint32 hitMask = crit ? PROC_HIT_CRITICAL : PROC_HIT_NORMAL; // ignore item heals if (!haveCastItem) - caster->ProcDamageAndSpell(target, procAttacker, procVictim, procEx, damage, BASE_ATTACK, GetSpellInfo()); + caster->ProcSkillsAndAuras(target, procAttacker, procVictim, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_NONE, hitMask, nullptr, nullptr, &healInfo); } void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) const @@ -6322,15 +6387,41 @@ void AuraEffect::HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) con // Set trigger flag uint32 procAttacker = PROC_FLAG_DONE_PERIODIC; uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC; - uint32 procEx = createProcExtendMask(&damageInfo, SPELL_MISS_NONE) | PROC_EX_INTERNAL_DOT; + uint32 hitMask = createProcHitMask(&damageInfo, SPELL_MISS_NONE); + uint32 spellTypeMask = PROC_SPELL_TYPE_NO_DMG_HEAL; if (damageInfo.damage) + { procVictim |= PROC_FLAG_TAKEN_DAMAGE; + spellTypeMask |= PROC_SPELL_TYPE_DAMAGE; + } - caster->ProcDamageAndSpell(damageInfo.target, procAttacker, procVictim, procEx, damageInfo.damage, BASE_ATTACK, spellProto); + DamageInfo dotDamageInfo(damageInfo, DOT, BASE_ATTACK, hitMask); + caster->ProcSkillsAndAuras(target, procAttacker, procVictim, spellTypeMask, PROC_SPELL_PHASE_NONE, hitMask, nullptr, &dotDamageInfo, nullptr); caster->DealSpellDamage(&damageInfo, true); } +void AuraEffect::HandleBreakableCCAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) +{ + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo) + return; + + // aura own damage at apply won't break CC + if (eventInfo.GetSpellPhaseMask() & PROC_SPELL_PHASE_CAST) + { + if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) + if (spellInfo == GetSpellInfo()) + return; + } + + int32 damageLeft = GetAmount(); + if (damageLeft < int32(damageInfo->GetDamage())) + aurApp->GetTarget()->RemoveAura(aurApp); + else + SetAmount(damageLeft - damageInfo->GetDamage()); +} + void AuraEffect::HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) { Unit* triggerCaster = aurApp->GetTarget(); @@ -6343,7 +6434,7 @@ void AuraEffect::HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEve triggerCaster->CastSpell(triggerTarget, triggeredSpellInfo, true, NULL, this); } else - TC_LOG_DEBUG("spells","AuraEffect::HandleProcTriggerSpellAuraProc: Could not trigger spell %u from aura %u proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId()); + TC_LOG_ERROR("spells","AuraEffect::HandleProcTriggerSpellAuraProc: Could not trigger spell %u from aura %u proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId()); } void AuraEffect::HandleProcTriggerSpellWithValueAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) @@ -6359,7 +6450,7 @@ void AuraEffect::HandleProcTriggerSpellWithValueAuraProc(AuraApplication* aurApp triggerCaster->CastCustomSpell(triggerTarget, triggerSpellId, &basepoints0, NULL, NULL, true, NULL, this); } else - TC_LOG_DEBUG("spells","AuraEffect::HandleProcTriggerSpellWithValueAuraProc: Could not trigger spell %u from aura %u proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId()); + TC_LOG_ERROR("spells","AuraEffect::HandleProcTriggerSpellWithValueAuraProc: Could not trigger spell %u from aura %u proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId()); } void AuraEffect::HandleProcTriggerDamageAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h index 32a7b97fff2..aae860ce6db 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.h +++ b/src/server/game/Spells/Auras/SpellAuraEffects.h @@ -41,7 +41,6 @@ class TC_GAME_API AuraEffect Aura* GetBase() const { return m_base; } void GetTargetList(std::list<Unit*> & targetList) const; void GetApplicationList(std::list<AuraApplication*> & applicationList) const; - SpellModifier* GetSpellModifier() const { return m_spellmod; } SpellInfo const* GetSpellInfo() const { return m_spellInfo; } uint32 GetId() const { return m_spellInfo->Id; } @@ -90,8 +89,9 @@ class TC_GAME_API AuraEffect bool HasSpellClassMask() const { return m_spellInfo->Effects[m_effIndex].SpellClassMask; } void SendTickImmune(Unit* target, Unit* caster) const; - void PeriodicTick(AuraApplication * aurApp, Unit* caster) const; + void PeriodicTick(AuraApplication* aurApp, Unit* caster) const; + bool CheckEffectProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) const; void HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); void CleanupTriggeredSpells(Unit* target); @@ -305,6 +305,7 @@ class TC_GAME_API AuraEffect void HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) const; // aura effect proc handlers + void HandleBreakableCCAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); void HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); void HandleProcTriggerSpellWithValueAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); void HandleProcTriggerDamageAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 23ebc741b37..b2052dbe5da 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -730,7 +730,7 @@ int32 Aura::CalcMaxDuration(Unit* caster) const // IsPermanent() checks max duration (which we are supposed to calculate here) if (maxDuration != -1 && modOwner) - modOwner->ApplySpellMod(GetId(), SPELLMOD_DURATION, maxDuration); + modOwner->ApplySpellMod<SPELLMOD_DURATION>(GetId(), maxDuration); return maxDuration; } @@ -740,7 +740,7 @@ void Aura::SetDuration(int32 duration, bool withMods) if (withMods) if (Unit* caster = GetCaster()) if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetId(), SPELLMOD_DURATION, duration); + modOwner->ApplySpellMod<SPELLMOD_DURATION>(GetId(), duration); m_duration = duration; SetNeedClientUpdateForTargets(); @@ -794,7 +794,7 @@ uint8 Aura::CalcMaxCharges(Unit* caster) const if (caster) if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetId(), SPELLMOD_CHARGES, maxProcCharges); + modOwner->ApplySpellMod<SPELLMOD_CHARGES>(GetId(), maxProcCharges); return maxProcCharges; } @@ -900,12 +900,6 @@ bool Aura::ModStackAmount(int32 num, AuraRemoveMode removeMode) // reset charges SetCharges(CalcMaxCharges()); - // FIXME: not a best way to synchronize charges, but works - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (AuraEffect* aurEff = GetEffect(i)) - if (aurEff->GetAuraType() == SPELL_AURA_ADD_FLAT_MODIFIER || aurEff->GetAuraType() == SPELL_AURA_ADD_PCT_MODIFIER) - if (SpellModifier* mod = aurEff->GetSpellModifier()) - mod->charges = GetCharges(); } SetNeedClientUpdateForTargets(); @@ -1037,7 +1031,7 @@ int32 Aura::CalcDispelChance(Unit* auraTarget, bool offensive) const // Apply dispel mod from aura caster if (Unit* caster = GetCaster()) if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetId(), SPELLMOD_RESIST_DISPEL_CHANCE, resistChance); + modOwner->ApplySpellMod<SPELLMOD_RESIST_DISPEL_CHANCE>(GetId(), resistChance); // Dispel resistance from target SPELL_AURA_MOD_DISPEL_RESIST // Only affects offensive dispels @@ -1094,7 +1088,7 @@ void Aura::HandleAllEffects(AuraApplication * aurApp, uint8 mode, bool apply) m_effects[i]->HandleEffect(aurApp, mode, apply); } -void Aura::GetApplicationList(std::list<AuraApplication*> & applicationList) const +void Aura::GetApplicationList(Unit::AuraApplicationList& applicationList) const { for (Aura::ApplicationMap::const_iterator appIter = m_applications.begin(); appIter != m_applications.end(); ++appIter) { @@ -1278,17 +1272,9 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b } break; case 44544: // Fingers of Frost - { - // See if we already have the indicator aura. If not, create one. - if (Aura* aur = target->GetAura(74396)) - { - // Aura already there. Refresh duration and set original charges - aur->SetCharges(2); - aur->RefreshDuration(); - } - else - target->AddAura(74396, target); - } + // Refresh or add visual aura + target->CastCustomSpell(74396, SPELLVALUE_AURA_STACK, sSpellMgr->AssertSpellInfo(74396)->StackAmount, (Unit*)nullptr, true); + break; default: break; } @@ -1402,10 +1388,6 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b target->CastSpell(target, 32612, true, NULL, GetEffect(1)); target->CombatStop(); break; - case 74396: // Fingers of Frost - // Remove the IGNORE_AURASTATE aura - target->RemoveAurasDueToSpell(44544); - break; default: break; } @@ -1868,49 +1850,76 @@ void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInf } SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId()); - ASSERT(procEntry); // cooldowns should be added to the whole aura (see 51698 area aura) AddProcCooldown(now + procEntry->Cooldown); } -bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const +uint8 Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const { SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId()); // only auras with spell proc entry can trigger proc if (!procEntry) - return false; + return 0; // check if we have charges to proc with - if (IsUsingCharges() && !GetCharges()) - return false; + if (IsUsingCharges()) + { + if (!GetCharges()) + return 0; + + if (procEntry->AttributesMask & PROC_ATTR_REQ_SPELLMOD) + if (Spell const* spell = eventInfo.GetProcSpell()) + if (!spell->m_appliedMods.count(const_cast<Aura*>(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 (!sSpellMgr->CanSpellTriggerProcOnEvent(*procEntry, eventInfo)) - return false; + return 0; + + // check if aura can proc when spell is triggered + if (!(procEntry->AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC)) + if (Spell const* spell = eventInfo.GetProcSpell()) + if (spell->IsTriggered()) + if (!GetSpellInfo()->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)) + return 0; // do checks using conditions table if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_SPELL_PROC, GetId(), eventInfo.GetActor(), eventInfo.GetActionTarget())) - return false; + return 0; // AuraScript Hook bool check = const_cast<Aura*>(this)->CallScriptCheckProcHandlers(aurApp, eventInfo); if (!check) - return false; + return 0; + + // At least one effect has to pass checks to proc aura + uint8 procEffectMask = 0; + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (aurApp->HasEffect(i)) + if (GetEffect(i)->CheckEffectProc(aurApp, eventInfo)) + procEffectMask |= (1 << i); + + if (!procEffectMask) + 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) + if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) + if (spellInfo->Id == GetId() && !(eventInfo.GetTypeMask() & (PROC_FLAG_TAKEN_PERIODIC | PROC_FLAG_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 @@ -1920,12 +1929,12 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI if (GetSpellInfo()->EquippedItemClass == ITEM_CLASS_WEAPON) { if (target->ToPlayer()->IsInFeralForm()) - return false; + return 0; - if (eventInfo.GetDamageInfo()) + if (DamageInfo* damageInfo = eventInfo.GetDamageInfo()) { - WeaponAttackType attType = eventInfo.GetDamageInfo()->GetAttackType(); - Item* item = NULL; + WeaponAttackType attType = damageInfo->GetAttackType(); + Item* item = nullptr; if (attType == BASE_ATTACK) item = target->ToPlayer()->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); else if (attType == OFF_ATTACK) @@ -1933,20 +1942,23 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI else item = target->ToPlayer()->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); - if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_WEAPON || !((1<<item->GetTemplate()->SubClass) & GetSpellInfo()->EquippedItemSubClassMask)) - return false; + if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_WEAPON || !((1 << item->GetTemplate()->SubClass) & GetSpellInfo()->EquippedItemSubClassMask)) + return 0; } } else if (GetSpellInfo()->EquippedItemClass == ITEM_CLASS_ARMOR) { // Check if player is wearing shield Item* item = target->ToPlayer()->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); - if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_ARMOR || !((1<<item->GetTemplate()->SubClass) & GetSpellInfo()->EquippedItemSubClassMask)) - return false; + if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_ARMOR || !((1 << item->GetTemplate()->SubClass) & GetSpellInfo()->EquippedItemSubClassMask)) + return 0; } } - return roll_chance_f(CalcProcChance(*procEntry, eventInfo)); + if (roll_chance_f(CalcProcChance(*procEntry, eventInfo))) + return procEffectMask; + + return 0; } float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const @@ -1964,21 +1976,28 @@ float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& event } // apply chance modifer aura, applies also to ppm chance (see improved judgement of light spell) if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetId(), SPELLMOD_CHANCE_OF_SUCCESS, chance); + modOwner->ApplySpellMod<SPELLMOD_CHANCE_OF_SUCCESS>(GetId(), chance); } return chance; } -void Aura::TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) +void Aura::TriggerProcOnEvent(uint8 procEffectMask, AuraApplication* aurApp, ProcEventInfo& eventInfo) { - CallScriptProcHandlers(aurApp, eventInfo); + bool prevented = CallScriptProcHandlers(aurApp, eventInfo); + if (!prevented) + { + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (!(procEffectMask & (1 << i))) + continue; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (aurApp->HasEffect(i)) // OnEffectProc / AfterEffectProc hooks handled in AuraEffect::HandleProc() - GetEffect(i)->HandleProc(aurApp, eventInfo); + if (aurApp->HasEffect(i)) + GetEffect(i)->HandleProc(aurApp, eventInfo); + } - CallScriptAfterProcHandlers(aurApp, eventInfo); + CallScriptAfterProcHandlers(aurApp, eventInfo); + } // Remove aura if we've used last charge to proc if (IsUsingCharges() && !GetCharges()) @@ -2336,6 +2355,23 @@ void Aura::CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventI } } +bool Aura::CallScriptCheckEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo) +{ + bool result = true; + for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + { + (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_CHECK_EFFECT_PROC, aurApp); + std::list<AuraScript::CheckEffectProcHandler>::iterator hookItrEnd = (*scritr)->DoCheckEffectProc.end(), hookItr = (*scritr)->DoCheckEffectProc.begin(); + for (; hookItr != hookItrEnd; ++hookItr) + if (hookItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + result &= hookItr->Call(*scritr, aurEff, eventInfo); + + (*scritr)->_FinishScriptCall(); + } + + return result; +} + bool Aura::CallScriptEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo) { bool preventDefault = false; diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index a147957f258..750787fc918 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -199,18 +199,14 @@ class TC_GAME_API Aura bool CheckAreaTarget(Unit* target); bool CanStackWith(Aura const* existingAura) const; - // Proc system - // this subsystem is not yet in use - the core of it is functional, but still some research has to be done - // and some dependant problems fixed before it can replace old proc system (for example cooldown handling) - // currently proc system functionality is implemented in Unit::ProcDamageAndSpell bool IsProcOnCooldown(std::chrono::steady_clock::time_point now) const; void AddProcCooldown(std::chrono::steady_clock::time_point cooldownEnd); bool IsUsingCharges() const { return m_isUsingCharges; } void SetUsingCharges(bool val) { m_isUsingCharges = val; } void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now); - bool IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const; + uint8 IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const; float CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const; - void TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo); + void TriggerProcOnEvent(uint8 procEffectMask, AuraApplication* aurApp, ProcEventInfo& eventInfo); // AuraScript void LoadScripts(); @@ -233,6 +229,7 @@ class TC_GAME_API Aura void CallScriptEffectSplitHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & splitAmount); // Spell Proc Hooks bool CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); + bool CallScriptCheckEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo); bool CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); bool CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); void CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 2a7b550392a..b37a44d6e70 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -592,7 +592,7 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO m_healing = 0; m_procAttacker = 0; m_procVictim = 0; - m_procEx = 0; + m_hitMask = 0; focusObject = NULL; m_cast_count = 0; m_glyphIndex = 0; @@ -1481,7 +1481,7 @@ void Spell::SelectImplicitChainTargets(SpellEffIndex effIndex, SpellImplicitTarg { uint32 maxTargets = m_spellInfo->Effects[effIndex].ChainTarget; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, maxTargets, this); + modOwner->ApplySpellMod<SPELLMOD_JUMP_TARGETS>(m_spellInfo->Id, maxTargets, this); if (maxTargets > 1) { @@ -1947,12 +1947,11 @@ GameObject* Spell::SearchSpellFocus() return focus; } -void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/) +void Spell::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; @@ -1982,7 +1981,7 @@ void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/) break; default: if (m_spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON && - m_spellInfo->EquippedItemSubClassMask & (1<<ITEM_SUBCLASS_WEAPON_WAND) + m_spellInfo->EquippedItemSubClassMask & (1 << ITEM_SUBCLASS_WEAPON_WAND) && m_spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) // Wands auto attack { m_procAttacker = PROC_FLAG_DONE_RANGED_AUTO_ATTACK; @@ -1991,7 +1990,7 @@ void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/) // For other spells trigger procflags are set in Spell::DoAllEffectOnTarget // Because spell positivity is dependant on target } - m_procEx = PROC_EX_NONE; + m_hitMask = PROC_HIT_NONE; // Hunter trap spells - activation proc for Lock and Load, Entrapment and Misdirection if (m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && @@ -2000,29 +1999,12 @@ void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/) m_spellInfo->SpellFamilyFlags[2] & 0x00024000)) // Explosive and Immolation Trap m_procAttacker |= PROC_FLAG_DONE_TRAP_ACTIVATION; - /* 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 == SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags[0] & 0x00000040) { m_procAttacker = PROC_FLAG_DONE_PERIODIC; m_procVictim = PROC_FLAG_TAKEN_PERIODIC; } - - // Ranged autorepeat attack is set as triggered spell - ignore it - if (!(m_procAttacker & PROC_FLAG_DONE_RANGED_AUTO_ATTACK)) - { - if (_triggeredCastFlags & TRIGGERED_DISALLOW_PROC_EVENTS && - (m_spellInfo->HasAttribute(SPELL_ATTR2_TRIGGERED_CAN_TRIGGER_PROC) || - m_spellInfo->HasAttribute(SPELL_ATTR3_TRIGGERED_CAN_TRIGGER_PROC_2))) - m_procEx |= PROC_EX_INTERNAL_CANT_PROC; - else if (_triggeredCastFlags & TRIGGERED_DISALLOW_PROC_EVENTS) - m_procEx |= PROC_EX_INTERNAL_TRIGGERED; - } - // Totem casts require spellfamilymask defined in spell_proc_event to proc - if (m_originalCaster && m_caster != m_originalCaster && m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsTotem() && m_caster->IsControlledByPlayer()) - m_procEx |= PROC_EX_INTERNAL_REQ_FAMILY; } void Spell::CleanupTargetList() @@ -2033,6 +2015,32 @@ void Spell::CleanupTargetList() m_delayMoment = 0; } +class ProcReflectDelayed : public BasicEvent +{ + public: + ProcReflectDelayed(Unit* owner, ObjectGuid casterGuid) : _victim(owner), _casterGuid(casterGuid) { } + + bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) override + { + Unit* caster = ObjectAccessor::GetUnit(*_victim, _casterGuid); + if (!caster) + return true; + + uint32 const typeMaskActor = PROC_FLAG_NONE; + uint32 const typeMaskActionTarget = PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG; + uint32 const spellTypeMask = PROC_SPELL_TYPE_DAMAGE | PROC_SPELL_TYPE_NO_DMG_HEAL; + uint32 const spellPhaseMask = PROC_SPELL_PHASE_NONE; + uint32 const hitMask = PROC_HIT_REFLECT; + + caster->ProcSkillsAndAuras(_victim, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, nullptr, nullptr, nullptr); + return true; + } + + private: + Unit* _victim; + ObjectGuid _casterGuid; +}; + void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= true*/, bool implicit /*= true*/, Position const* losPosition /*= nullptr*/) { for (uint32 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) @@ -2112,20 +2120,20 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= targetInfo.timeDelay = (uint64)std::floor(dist / m_spellInfo->Speed * 1000.0f); // Calculate minimum incoming time - if (m_delayMoment == 0 || m_delayMoment > targetInfo.timeDelay) + if (!m_delayMoment || m_delayMoment > targetInfo.timeDelay) m_delayMoment = targetInfo.timeDelay; } else - targetInfo.timeDelay = 0LL; + targetInfo.timeDelay = 0ULL; // If target reflect spell back to caster if (targetInfo.missCondition == SPELL_MISS_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); // can't reflect twice - if (targetInfo.reflectResult == SPELL_MISS_REFLECT) // Impossible reflect again, so simply deflect spell - targetInfo.reflectResult = SPELL_MISS_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; @@ -2147,14 +2155,14 @@ void Spell::AddGOTarget(GameObject* go, uint32 effectMask) { switch (m_spellInfo->Effects[effIndex].Effect) { - case SPELL_EFFECT_GAMEOBJECT_DAMAGE: - case SPELL_EFFECT_GAMEOBJECT_REPAIR: - case SPELL_EFFECT_GAMEOBJECT_SET_DESTRUCTION_STATE: - if (go->GetGoType() != GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING) - effectMask &= ~(1 << effIndex); - break; - default: - break; + case SPELL_EFFECT_GAMEOBJECT_DAMAGE: + case SPELL_EFFECT_GAMEOBJECT_REPAIR: + case SPELL_EFFECT_GAMEOBJECT_SET_DESTRUCTION_STATE: + if (go->GetGoType() != GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING) + effectMask &= ~(1 << effIndex); + break; + default: + break; } } } @@ -2189,7 +2197,7 @@ void Spell::AddGOTarget(GameObject* go, uint32 effectMask) if (dist < 5.0f) dist = 5.0f; target.timeDelay = uint64(floor(dist / m_spellInfo->Speed * 1000.0f)); - if (m_delayMoment == 0 || m_delayMoment > target.timeDelay) + if (!m_delayMoment || m_delayMoment > target.timeDelay) m_delayMoment = target.timeDelay; } else @@ -2297,13 +2305,13 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) // Fill base trigger info uint32 procAttacker = m_procAttacker; uint32 procVictim = m_procVictim; - uint32 procEx = m_procEx; + uint32 hitMask = m_hitMask; - m_spellAura = NULL; // Set aura to null for every target-make sure that pointer is not used for unit without aura applied + m_spellAura = nullptr; // Set aura to null for every target-make sure that pointer is not used for unit without aura applied - //Spells with this flag cannot trigger if effect is cast on self - bool canEffectTrigger = !m_spellInfo->HasAttribute(SPELL_ATTR3_CANT_TRIGGER_PROC) && unitTarget->CanProc() && (CanExecuteTriggersOnHit(mask) || missInfo == SPELL_MISS_IMMUNE || missInfo == SPELL_MISS_IMMUNE2); - Unit* spellHitTarget = NULL; + // Spells with this flag cannot trigger if effect is cast on self + bool canEffectTrigger = !m_spellInfo->HasAttribute(SPELL_ATTR3_CANT_TRIGGER_PROC) && (CanExecuteTriggersOnHit(mask) || missInfo == SPELL_MISS_IMMUNE || missInfo == SPELL_MISS_IMMUNE2); + Unit* spellHitTarget = nullptr; if (missInfo == SPELL_MISS_NONE) // In case spell hit target, do all effect on that target spellHitTarget = unit; @@ -2331,23 +2339,15 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) if (missInfo2 != SPELL_MISS_MISS) m_caster->SendSpellMiss(unit, m_spellInfo->Id, missInfo2); m_damage = 0; - spellHitTarget = NULL; + spellHitTarget = nullptr; } } // Do not take combo points on dodge and miss - if (missInfo != SPELL_MISS_NONE && m_needComboPoints && - m_targets.GetUnitTargetGUID() == target->targetGUID) - { + if (missInfo != SPELL_MISS_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->GetTypeId() == TYPEID_PLAYER && (missInfo == SPELL_MISS_MISS || - missInfo == SPELL_MISS_DODGE || missInfo == SPELL_MISS_PARRY)) - m_caster->ToPlayer()->RestoreSpellMods(this, 14177); - } - // Trigger info was not filled in spell::preparedatafortriggersystem - we do it now + // Trigger info was not filled in Spell::prepareDataForTriggerSystem - we do it now if (canEffectTrigger && !procAttacker && !procVictim) { bool positive = true; @@ -2394,19 +2394,20 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) uint32 addhealth = m_healing; if (crit) { - procEx |= PROC_EX_CRITICAL_HIT; - addhealth = caster->SpellCriticalHealingBonus(m_spellInfo, addhealth, NULL); + hitMask |= PROC_HIT_CRITICAL; + addhealth = caster->SpellCriticalHealingBonus(m_spellInfo, addhealth, nullptr); } else - procEx |= PROC_EX_NORMAL_HIT; + hitMask |= PROC_HIT_NORMAL; - int32 gain = caster->HealBySpell(unitTarget, m_spellInfo, addhealth, crit); - unitTarget->getHostileRefManager().threatAssist(caster, float(gain) * 0.5f, m_spellInfo); - m_healing = gain; + HealInfo healInfo(caster, unitTarget, addhealth, m_spellInfo, m_spellInfo->GetSchoolMask()); + caster->HealBySpell(healInfo, crit); + unitTarget->getHostileRefManager().threatAssist(caster, float(healInfo.GetEffectiveHeal()) * 0.5f, m_spellInfo); + m_healing = healInfo.GetEffectiveHeal(); - // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) - if (canEffectTrigger && missInfo != SPELL_MISS_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, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_HIT, hitMask, this, nullptr, &healInfo); } // Do damage and triggers else if (m_damage > 0) @@ -2421,16 +2422,18 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) // Send log damage message to client caster->SendSpellNonMeleeDamageLog(&damageInfo); - procEx |= createProcExtendMask(&damageInfo, missInfo); + hitMask |= createProcHitMask(&damageInfo, missInfo); procVictim |= PROC_FLAG_TAKEN_DAMAGE; - // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) - if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT) + // Do triggers for unit + if (canEffectTrigger) { - caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell); - if (caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET) == 0 && - (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) - caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim, procEx); + DamageInfo spellDamageInfo(damageInfo, SPELL_DIRECT_DAMAGE, m_attackType, hitMask); + caster->ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_HIT, hitMask, this, &spellDamageInfo, nullptr); + + if (caster->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET) && + (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) + caster->ToPlayer()->CastItemCombatSpell(spellDamageInfo); } m_damage = damageInfo.damage; @@ -2442,10 +2445,17 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) { // Fill base damage struct (unitTarget - is real spell target) SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, m_spellSchoolMask); - procEx |= createProcExtendMask(&damageInfo, missInfo); - // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) - if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT) - caster->ProcDamageAndSpell(unit, procAttacker, procVictim, procEx, 0, m_attackType, m_spellInfo, m_triggeredByAuraSpell); + hitMask |= createProcHitMask(&damageInfo, missInfo); + // Do triggers for unit + if (canEffectTrigger) + { + DamageInfo spellNoDamageInfo(damageInfo, NODAMAGE, m_attackType, hitMask); + caster->ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, PROC_SPELL_TYPE_NO_DMG_HEAL, PROC_SPELL_PHASE_HIT, hitMask, this, &spellNoDamageInfo, nullptr); + + if (caster->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET) && + (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) + caster->ToPlayer()->CastItemCombatSpell(spellNoDamageInfo); + } // Failed Pickpocket, reveal rogue if (missInfo == SPELL_MISS_RESIST && m_spellInfo->HasAttribute(SPELL_ATTR0_CU_PICKPOCKET) && unitTarget->GetTypeId() == TYPEID_UNIT) @@ -2815,7 +2825,7 @@ bool Spell::UpdateChanneledTargetList() { range = m_spellInfo->GetMaxRange(m_spellInfo->IsPositive()); if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this); + modOwner->ApplySpellMod<SPELLMOD_RANGE>(m_spellInfo->Id, range, this); } for (std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) @@ -2953,7 +2963,7 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered } // Prepare data for triggers - prepareDataForTriggerSystem(triggeredByAura); + prepareDataForTriggerSystem(); if (Player* player = m_caster->ToPlayer()) { @@ -3061,10 +3071,6 @@ void Spell::cancel() SendInterrupted(0); SendCastResult(SPELL_FAILED_INTERRUPTED); - // spell is canceled-take mods and clear list - if (m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->ToPlayer()->RemoveSpellMods(this); - m_appliedMods.clear(); break; @@ -3299,6 +3305,25 @@ void Spell::cast(bool skipCheck) if (Creature* creatureCaster = m_caster->ToCreature()) creatureCaster->ReleaseFocus(this); + + if (!m_originalCaster) + return; + + // Handle procs on cast + uint32 procAttacker = m_procAttacker; + if (!procAttacker) + { + if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC) + procAttacker = m_spellInfo->IsPositive() ? PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG; + else + procAttacker = m_spellInfo->IsPositive() ? PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG; + } + + uint32 hitMask = m_hitMask; + if (!(hitMask & PROC_HIT_CRITICAL)) + hitMask |= PROC_HIT_NORMAL; + + m_originalCaster->ProcSkillsAndAuras(nullptr, procAttacker, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_CAST, hitMask, this, nullptr, nullptr); } void Spell::handle_immediate() @@ -3312,7 +3337,7 @@ void Spell::handle_immediate() // First mod_duration then haste - see Missile Barrage // Apply duration mod if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); + modOwner->ApplySpellMod<SPELLMOD_DURATION>(m_spellInfo->Id, duration); // Apply haste mods m_caster->ModSpellDurationTime(m_spellInfo, duration, this); @@ -3455,22 +3480,8 @@ void Spell::_handle_immediate_phase() } // process items - for (std::list<ItemTargetInfo>::iterator ihit= m_UniqueItemInfo.begin(); ihit != m_UniqueItemInfo.end(); ++ihit) + for (std::list<ItemTargetInfo>::iterator ihit = m_UniqueItemInfo.begin(); ihit != m_UniqueItemInfo.end(); ++ihit) DoAllEffectOnTarget(&(*ihit)); - - if (!m_originalCaster) - return; - // Handle procs on cast - /// @todo finish new proc system:P - if (m_UniqueTargetInfo.empty() && m_targets.HasDst()) - { - uint32 procAttacker = m_procAttacker; - if (!procAttacker) - procAttacker |= PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS; - - // Proc the spells that have DEST target - m_originalCaster->ProcDamageAndSpell(NULL, procAttacker, 0, m_procEx | PROC_EX_NORMAL_HIT, 0, BASE_ATTACK, m_spellInfo, m_triggeredByAuraSpell); - } } void Spell::_handle_finish_phase() @@ -3494,7 +3505,24 @@ void Spell::_handle_finish_phase() m_caster->m_extraAttacks = 0; } - /// @todo trigger proc phase finish here + // Handle procs on finish + if (!m_originalCaster) + return; + + uint32 procAttacker = m_procAttacker; + if (!procAttacker) + { + if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC) + procAttacker = m_spellInfo->IsPositive() ? PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG; + else + procAttacker = m_spellInfo->IsPositive() ? PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG; + } + + uint32 hitMask = m_hitMask; + if (!(hitMask & PROC_HIT_CRITICAL)) + hitMask |= PROC_HIT_NORMAL; + + m_originalCaster->ProcSkillsAndAuras(nullptr, procAttacker, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_FINISH, hitMask, this, nullptr, nullptr); } void Spell::SendSpellCooldown() @@ -3658,18 +3686,6 @@ void Spell::finish(bool ok) m_caster->ToPlayer()->UpdatePotionCooldown(this); } - if (Player* modOwner = m_caster->GetSpellModOwner()) - { - // 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(SPELL_ATTR0_STOP_ATTACK_TARGET)) m_caster->AttackStop(); @@ -4387,7 +4403,7 @@ void Spell::TakePower() hit = false; //lower spell cost on fail (by talent aura) if (Player* modOwner = m_caster->ToPlayer()->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, m_powerCost); + modOwner->ApplySpellMod<SPELLMOD_SPELL_COST_REFUND_ON_FAIL>(m_spellInfo->Id, m_powerCost); } break; } @@ -4479,7 +4495,7 @@ SpellCastResult Spell::CheckRuneCost(uint32 runeCostID) { runeCost[i] = src->RuneCost[i]; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, runeCost[i], this); + modOwner->ApplySpellMod<SPELLMOD_COST>(m_spellInfo->Id, runeCost[i], this); } runeCost[RUNE_DEATH] = MAX_RUNES; // calculated later @@ -4519,7 +4535,7 @@ void Spell::TakeRunePower(bool didHit) { runeCost[i] = runeCostData->RuneCost[i]; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, runeCost[i], this); + modOwner->ApplySpellMod<SPELLMOD_COST>(m_spellInfo->Id, runeCost[i], this); } // Let's say we use a skill that requires a Frost rune. This is the order: @@ -5896,7 +5912,7 @@ std::pair<float, float> Spell::GetMinMaxRange(bool strict) maxRange *= ranged->GetTemplate()->RangedModRange * 0.01f; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, maxRange, this); + modOwner->ApplySpellMod<SPELLMOD_RANGE>(m_spellInfo->Id, maxRange, this); maxRange += rangeMod; @@ -6465,7 +6481,7 @@ void Spell::Delayed() // only called in DealDamage() //check pushback reduce int32 delaytime = 500; // spellcasting delay is normally 500ms int32 delayReduce = 100; // must be initialized to 100 for percent modifiers - m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); + m_caster->ToPlayer()->ApplySpellMod<SPELLMOD_NOT_LOSE_CASTING_TIME>(m_spellInfo->Id, delayReduce, this); delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; if (delayReduce >= 100) return; @@ -6503,7 +6519,7 @@ void Spell::DelayedChannel() int32 delaytime = CalculatePct(duration, 25); // channeling delay is normally 25% of its time per hit int32 delayReduce = 100; // must be initialized to 100 for percent modifiers - m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); + m_caster->ToPlayer()->ApplySpellMod<SPELLMOD_NOT_LOSE_CASTING_TIME>(m_spellInfo->Id, delayReduce, this); delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; if (delayReduce >= 100) return; @@ -7428,15 +7444,12 @@ void Spell::TriggerGlobalCooldown() if (m_spellInfo->StartRecoveryTime >= MIN_GCD && m_spellInfo->StartRecoveryTime <= MAX_GCD) { // gcd modifier auras are applied only to own spells and only players have such mods - if (m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_GLOBAL_COOLDOWN, gcd, this); + if (Player* modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod<SPELLMOD_GLOBAL_COOLDOWN>(m_spellInfo->Id, gcd, this); // Apply haste rating gcd = int32(float(gcd) * m_caster->GetFloatValue(UNIT_MOD_CAST_SPEED)); - if (gcd < MIN_GCD) - gcd = MIN_GCD; - else if (gcd > MAX_GCD) - gcd = MAX_GCD; + RoundToInterval<int32>(gcd, MIN_GCD, MAX_GCD); } m_caster->GetSpellHistory()->AddGlobalCooldown(m_spellInfo, gcd); diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 016af275141..5823ea72dbd 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -352,7 +352,7 @@ class TC_GAME_API Spell void EffectCastButtons(SpellEffIndex effIndex); void EffectRechargeManaGem(SpellEffIndex effIndex); - typedef std::set<Aura*> UsedSpellMods; + typedef std::unordered_set<Aura*> UsedSpellMods; Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID = ObjectGuid::Empty, bool skipCheck = false); ~Spell(); @@ -577,8 +577,8 @@ class TC_GAME_API Spell // ****************************************** uint32 m_procAttacker; // Attacker trigger flags uint32 m_procVictim; // Victim trigger flags - uint32 m_procEx; - void prepareDataForTriggerSystem(AuraEffect const* triggeredByAura); + uint32 m_hitMask; + void prepareDataForTriggerSystem(); // ***************************************** // Spell target subsystem diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 630236f05ac..93a0ec2ac56 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1507,7 +1507,8 @@ void Spell::EffectHealthLeech(SpellEffIndex effIndex) healthGain = m_caster->SpellHealingBonusDone(m_caster, m_spellInfo, healthGain, HEAL); healthGain = m_caster->SpellHealingBonusTaken(m_caster, m_spellInfo, healthGain, HEAL); - m_caster->HealBySpell(m_caster, m_spellInfo, uint32(healthGain)); + HealInfo healInfo(m_caster, m_caster, healthGain, m_spellInfo, m_spellSchoolMask); + m_caster->HealBySpell(healInfo); } } @@ -2197,7 +2198,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) int32 duration = m_spellInfo->GetDuration(); if (Player* modOwner = m_originalCaster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); + modOwner->ApplySpellMod<SPELLMOD_DURATION>(m_spellInfo->Id, duration); TempSummon* summon = NULL; @@ -5442,18 +5443,8 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) m_runesState = m_caster->ToPlayer()->GetRunesState(); uint32 count = damage; - if (count == 0) count = 1; - for (uint32 j = 0; j < MAX_RUNES && count > 0; ++j) - { - if (player->GetRuneCooldown(j) && player->GetCurrentRune(j) == RuneType(m_spellInfo->Effects[effIndex].MiscValue)) - { - if (m_spellInfo->Id == 45529) - if (player->GetBaseRune(j) != RuneType(m_spellInfo->Effects[effIndex].MiscValueB)) - continue; - player->SetRuneCooldown(j, 0); - --count; - } - } + if (count == 0) + count = 1; // Blood Tap if (m_spellInfo->Id == 45529 && count > 0) @@ -5461,11 +5452,12 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) for (uint32 l = 0; l + 1 < MAX_RUNES && count > 0; ++l) { // Check if both runes are on cd as that is the only time when this needs to come into effect - if ((player->GetRuneCooldown(l) && player->GetBaseRune(l) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB)) && (player->GetRuneCooldown(l+1) && player->GetBaseRune(l+1) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB))) + if ((player->GetRuneCooldown(l) && player->GetBaseRune(l) == RUNE_BLOOD) && (player->GetRuneCooldown(l + 1) && player->GetBaseRune(l + 1) == RUNE_BLOOD)) { // Should always update the rune with the lowest cd - if (l + 1 < MAX_RUNES && player->GetRuneCooldown(l) >= player->GetRuneCooldown(l+1)) - l++; + if (l + 1 < MAX_RUNES && player->GetRuneCooldown(l) >= player->GetRuneCooldown(l + 1)) + ++l; + player->SetRuneCooldown(l, 0); --count; // is needed to push through to the client that the rune is active @@ -5476,6 +5468,15 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) } } + for (uint32 j = 0; j < MAX_RUNES && count > 0; ++j) + { + if (player->GetRuneCooldown(j) && player->GetCurrentRune(j) == RuneType(m_spellInfo->Effects[effIndex].MiscValue)) + { + player->SetRuneCooldown(j, 0); + --count; + } + } + // Empower rune weapon if (m_spellInfo->Id == 47568) { @@ -5485,7 +5486,7 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) for (uint32 i = 0; i < MAX_RUNES; ++i) { - if (player->GetRuneCooldown(i) && (player->GetCurrentRune(i) == RUNE_FROST || player->GetCurrentRune(i) == RUNE_DEATH)) + if (player->GetRuneCooldown(i) && (player->GetBaseRune(i) == RUNE_FROST)) player->SetRuneCooldown(i, 0); } } @@ -5614,7 +5615,7 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* int32 duration = m_spellInfo->GetDuration(); if (Player* modOwner = m_originalCaster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); + modOwner->ApplySpellMod<SPELLMOD_DURATION>(m_spellInfo->Id, duration); //TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN; Map* map = caster->GetMap(); diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp index 31490bea29b..3925ffe552e 100644 --- a/src/server/game/Spells/SpellHistory.cpp +++ b/src/server/game/Spells/SpellHistory.cpp @@ -307,10 +307,10 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel if (Player* modOwner = _owner->GetSpellModOwner()) { if (cooldown > 0) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown, spell); + modOwner->ApplySpellMod<SPELLMOD_COOLDOWN>(spellInfo->Id, cooldown, spell); if (categoryCooldown > 0 && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS)) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, categoryCooldown, spell); + modOwner->ApplySpellMod<SPELLMOD_COOLDOWN>(spellInfo->Id, categoryCooldown, spell); } if (int32 cooldownMod = _owner->GetTotalAuraModifier(SPELL_AURA_MOD_COOLDOWN)) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 56399529651..f69f49eb509 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -519,7 +519,7 @@ float SpellEffectInfo::CalcValueMultiplier(Unit* caster, Spell* spell) const { float multiplier = ValueMultiplier; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : NULL)) - modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_VALUE_MULTIPLIER, multiplier, spell); + modOwner->ApplySpellMod<SPELLMOD_VALUE_MULTIPLIER>(_spellInfo->Id, multiplier, spell); return multiplier; } @@ -527,7 +527,7 @@ float SpellEffectInfo::CalcDamageMultiplier(Unit* caster, Spell* spell) const { float multiplierPercent = DamageMultiplier * 100.0f; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : NULL)) - modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_DAMAGE_MULTIPLIER, multiplierPercent, spell); + modOwner->ApplySpellMod<SPELLMOD_DAMAGE_MULTIPLIER>(_spellInfo->Id, multiplierPercent, spell); return multiplierPercent / 100.0f; } @@ -547,7 +547,7 @@ float SpellEffectInfo::CalcRadius(Unit* caster, Spell* spell) const radius += RadiusEntry->RadiusPerLevel * caster->getLevel(); radius = std::min(radius, RadiusEntry->RadiusMax); if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_RADIUS, radius, spell); + modOwner->ApplySpellMod<SPELLMOD_RADIUS>(_spellInfo->Id, radius, spell); } return radius; @@ -2109,7 +2109,7 @@ float SpellInfo::GetMaxRange(bool positive, Unit* caster, Spell* spell) const range = RangeEntry->maxRangeHostile; if (caster) if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(Id, SPELLMOD_RANGE, range, spell); + modOwner->ApplySpellMod<SPELLMOD_RANGE>(Id, range, spell); return range; } @@ -2255,7 +2255,7 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) c // Apply cost mod by spell if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(Id, SPELLMOD_COST, powerCost); + modOwner->ApplySpellMod<SPELLMOD_COST>(Id, powerCost); if (!caster->IsControlledByPlayer()) { @@ -2433,7 +2433,7 @@ bool SpellInfo::_IsPositiveEffect(uint8 effIndex, bool deep) const // Amplify Magic, Dampen Magic if (SpellFamilyFlags[0] == 0x00002000) return true; - // Ignite + // Impact if (SpellIconID == 45) return true; break; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 54684f78186..bab1a34c038 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -18,6 +18,7 @@ #include "SpellMgr.h" #include "SpellInfo.h" +#include "Spell.h" #include "ObjectMgr.h" #include "SpellAuraDefines.h" #include "SharedDefines.h" @@ -779,172 +780,6 @@ SpellGroupStackRule SpellMgr::GetSpellGroupStackRule(SpellGroup group) const return SPELL_GROUP_STACK_RULE_DEFAULT; } -SpellProcEventEntry const* SpellMgr::GetSpellProcEvent(uint32 spellId) const -{ - SpellProcEventMap::const_iterator itr = mSpellProcEventMap.find(spellId); - if (itr != mSpellProcEventMap.end()) - return &itr->second; - return NULL; -} - -bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellInfo const* procSpell, uint32 procFlags, uint32 procExtra, bool active) const -{ - // No extra req need - uint32 procEvent_procEx = PROC_EX_NONE; - - // check prockFlags for condition - if ((procFlags & EventProcFlag) == 0) - return false; - - bool hasFamilyMask = false; - - /** - - * @brief Check auras procced by periodics - - *Only damaging Dots can proc auras with PROC_FLAG_TAKEN_DAMAGE - - *Only Dots can proc if ONLY has PROC_FLAG_DONE_PERIODIC or PROC_FLAG_TAKEN_PERIODIC. - - *Hots can proc if ONLY has PROC_FLAG_DONE_PERIODIC and spellfamily != 0 - - *Only Dots can proc auras with PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG or PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG - - *Only Hots can proc auras with PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS or PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS - - *Only Dots can proc auras with PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG or PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG - - *Only Hots can proc auras with PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS or PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_POS - - * @param procSpell the spell proccing the aura - * @param procFlags proc_flags of spellProc - * @param procExtra proc_EX of procSpell - * @param EventProcFlag proc_flags of aura to be procced - * @param spellProto SpellInfo of aura to be procced - - */ - - /// 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 & PROC_FLAG_TAKEN_DAMAGE && EventProcFlag & PROC_FLAG_TAKEN_DAMAGE) - return true; - - if (procFlags & PROC_FLAG_DONE_PERIODIC && EventProcFlag & PROC_FLAG_DONE_PERIODIC) - { - if (procExtra & PROC_EX_INTERNAL_HOT) - { - if (EventProcFlag == PROC_FLAG_DONE_PERIODIC) - { - /// no aura with only PROC_FLAG_DONE_PERIODIC and spellFamilyName == 0 can proc from a HOT. - if (!spellProto->SpellFamilyName) - return false; - } - /// Aura must have positive procflags for a HOT to proc - else if (!(EventProcFlag & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS))) - return false; - } - /// Aura must have negative or neutral(PROC_FLAG_DONE_PERIODIC only) procflags for a DOT to proc - /// Traps are negative spells but not always do damage (only hunter traps set PROC_FLAG_DONE_TRAP_ACTIVATION) - else if (EventProcFlag != PROC_FLAG_DONE_PERIODIC) - if (!(EventProcFlag & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG | PROC_FLAG_DONE_TRAP_ACTIVATION))) - return false; - } - - if (procFlags & PROC_FLAG_TAKEN_PERIODIC && EventProcFlag & PROC_FLAG_TAKEN_PERIODIC) - { - if (procExtra & PROC_EX_INTERNAL_HOT) - { - /// No aura that only has PROC_FLAG_TAKEN_PERIODIC can proc from a HOT. - if (EventProcFlag == PROC_FLAG_TAKEN_PERIODIC) - return false; - /// Aura must have positive procflags for a HOT to proc - if (!(EventProcFlag & (PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_POS))) - return false; - } - /// Aura must have negative or neutral(PROC_FLAG_TAKEN_PERIODIC only) procflags for a DOT to proc - else if (EventProcFlag != PROC_FLAG_TAKEN_PERIODIC) - if (!(EventProcFlag & (PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG))) - return false; - } - // Trap casts are active by default - if (procFlags & PROC_FLAG_DONE_TRAP_ACTIVATION) - active = true; - - // Always trigger for this - if (procFlags & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH)) - return true; - - if (spellProcEvent) // Exist event data - { - // Store extra req - procEvent_procEx = spellProcEvent->procEx; - - // For melee triggers - if (procSpell == NULL) - { - // Check (if set) for school (melee attack has Normal school) - if (spellProcEvent->schoolMask && (spellProcEvent->schoolMask & SPELL_SCHOOL_MASK_NORMAL) == 0) - return false; - } - else // For spells need check school/spell family/family mask - { - // Check (if set) for school - if (spellProcEvent->schoolMask && (spellProcEvent->schoolMask & procSpell->SchoolMask) == 0) - return false; - - // Check (if set) for spellFamilyName - if (spellProcEvent->spellFamilyName && (spellProcEvent->spellFamilyName != procSpell->SpellFamilyName)) - return false; - - // spellFamilyName is Ok need check for spellFamilyMask if present - if (spellProcEvent->spellFamilyMask) - { - if (!(spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags)) - return false; - hasFamilyMask = true; - // Some spells are not considered as active even with spellfamilyflags set - if (!(procEvent_procEx & PROC_EX_ONLY_ACTIVE_SPELL)) - active = true; - } - } - } - - if (procExtra & (PROC_EX_INTERNAL_REQ_FAMILY)) - { - if (!hasFamilyMask) - return false; - } - - // Check for extra req (if none) and hit/crit - if (procEvent_procEx == PROC_EX_NONE) - { - // No extra req, so can trigger only for hit/crit - spell has to be active - if ((procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) && active) - return true; - } - else // Passive spells hits here only if resist/reflect/immune/evade - { - if (procExtra & AURA_SPELL_PROC_EX_MASK) - { - // if spell marked as procing only from not active spells - if (active && procEvent_procEx & PROC_EX_NOT_ACTIVE_SPELL) - return false; - // if spell marked as procing only from active spells - if (!active && procEvent_procEx & PROC_EX_ONLY_ACTIVE_SPELL) - return false; - // Exist req for PROC_EX_EX_TRIGGER_ALWAYS - if (procEvent_procEx & PROC_EX_EX_TRIGGER_ALWAYS) - return true; - // PROC_EX_NOT_ACTIVE_SPELL and PROC_EX_ONLY_ACTIVE_SPELL flags handle: if passed checks before - if ((procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) && ((procEvent_procEx & (AURA_SPELL_PROC_EX_MASK)) == 0)) - return true; - } - // Check Extra Requirement like (hit/crit/miss/resist/parry/dodge/block/immune/reflect/absorb and other) - if (procEvent_procEx & procExtra) - return true; - } - return false; -} - SpellProcEntry const* SpellMgr::GetSpellProcEntry(uint32 spellId) const { SpellProcMap::const_iterator itr = mSpellProcMap.find(spellId); @@ -965,10 +800,31 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE if (eventInfo.GetActionTarget() && !actor->isHonorOrXPTarget(eventInfo.GetActionTarget())) return false; + // check mana requirement + if (procEntry.AttributesMask & PROC_ATTR_REQ_MANA_COST) + if (SpellInfo const* eventSpellInfo = eventInfo.GetSpellInfo()) + if (!eventSpellInfo->ManaCost && !eventSpellInfo->ManaCostPercentage) + return false; + // always trigger for these types if (eventInfo.GetTypeMask() & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH)) return true; + // do triggered cast checks + if (!(procEntry.AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC)) + { + if (Spell const* spell = eventInfo.GetProcSpell()) + { + if (spell->IsTriggered()) + { + SpellInfo const* spellInfo = spell->GetSpellInfo(); + if (!spellInfo->HasAttribute(SPELL_ATTR3_TRIGGERED_CAN_TRIGGER_PROC_2) && + !spellInfo->HasAttribute(SPELL_ATTR2_TRIGGERED_CAN_TRIGGER_PROC)) + return false; + } + } + } + // check school mask (if set) for other trigger types if (procEntry.SchoolMask && !(eventInfo.GetSchoolMask() & procEntry.SchoolMask)) return false; @@ -1851,224 +1707,276 @@ void SpellMgr::LoadSpellGroupStackRules() TC_LOG_INFO("server.loading", ">> Loaded %u spell group stack rules in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } -void SpellMgr::LoadSpellProcEvents() +// Used for prepare can/can't triggr aura +static bool InitTriggerAuraData(); +// Define can trigger auras +static bool isTriggerAura[TOTAL_AURAS]; +// Triggered always, even from triggered spells +static bool isAlwaysTriggeredAura[TOTAL_AURAS]; +// Prepare lists +static bool procPrepared = InitTriggerAuraData(); + +// List of auras that CAN be trigger but may not exist in spell_proc_event +// in most case need for drop charges +// in some types of aura need do additional check +// for example SPELL_AURA_MECHANIC_IMMUNITY - need check for mechanic +bool InitTriggerAuraData() +{ + for (uint16 i = 0; i < TOTAL_AURAS; ++i) + { + isTriggerAura[i] = false; + isAlwaysTriggeredAura[i] = false; + } + isTriggerAura[SPELL_AURA_DUMMY] = true; // Most dummy auras should require scripting. Remove? + isTriggerAura[SPELL_AURA_MOD_CONFUSE] = true; // "Any direct damaging attack will revive targets" + isTriggerAura[SPELL_AURA_MOD_THREAT] = true; // Only one spell: 28762 part of Mage T3 8p bonus + isTriggerAura[SPELL_AURA_MOD_STUN] = true; // Aura does not have charges but needs to be removed on trigger + isTriggerAura[SPELL_AURA_MOD_DAMAGE_DONE] = true; + isTriggerAura[SPELL_AURA_MOD_DAMAGE_TAKEN] = true; + isTriggerAura[SPELL_AURA_MOD_RESISTANCE] = true; + isTriggerAura[SPELL_AURA_MOD_STEALTH] = true; + isTriggerAura[SPELL_AURA_MOD_FEAR] = true; // Aura does not have charges but needs to be removed on trigger + isTriggerAura[SPELL_AURA_MOD_ROOT] = true; + isTriggerAura[SPELL_AURA_TRANSFORM] = true; + isTriggerAura[SPELL_AURA_REFLECT_SPELLS] = true; + isTriggerAura[SPELL_AURA_DAMAGE_IMMUNITY] = true; + isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL] = true; + isTriggerAura[SPELL_AURA_PROC_TRIGGER_DAMAGE] = true; + isTriggerAura[SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK] = true; + isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT] = true; + isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL] = true; + isTriggerAura[SPELL_AURA_REFLECT_SPELLS_SCHOOL] = true; + isTriggerAura[SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN] = true; + isTriggerAura[SPELL_AURA_MOD_ATTACK_POWER] = true; + isTriggerAura[SPELL_AURA_ADD_CASTER_HIT_TRIGGER] = true; + isTriggerAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true; + isTriggerAura[SPELL_AURA_MOD_MELEE_HASTE] = true; + isTriggerAura[SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE] = true; + isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE] = true; + isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE] = true; + isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE] = true; + isTriggerAura[SPELL_AURA_MOD_SPELL_CRIT_CHANCE] = true; + isTriggerAura[SPELL_AURA_ADD_FLAT_MODIFIER] = true; + isTriggerAura[SPELL_AURA_ADD_PCT_MODIFIER] = true; + isTriggerAura[SPELL_AURA_ABILITY_IGNORE_AURASTATE] = true; + + isAlwaysTriggeredAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true; + isAlwaysTriggeredAura[SPELL_AURA_MOD_FEAR] = true; + isAlwaysTriggeredAura[SPELL_AURA_MOD_ROOT] = true; + isAlwaysTriggeredAura[SPELL_AURA_MOD_STUN] = true; + isAlwaysTriggeredAura[SPELL_AURA_TRANSFORM] = true; + isAlwaysTriggeredAura[SPELL_AURA_SPELL_MAGNET] = true; + isAlwaysTriggeredAura[SPELL_AURA_SCHOOL_ABSORB] = true; + isAlwaysTriggeredAura[SPELL_AURA_MOD_STEALTH] = true; + + return true; +} + +void SpellMgr::LoadSpellProcs() { uint32 oldMSTime = getMSTime(); - mSpellProcEventMap.clear(); // need for reload case + mSpellProcMap.clear(); // need for reload case - // 0 1 2 3 4 5 6 7 8 9 10 - QueryResult result = WorldDatabase.Query("SELECT entry, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, procFlags, procEx, ppmRate, CustomChance, Cooldown FROM spell_proc_event"); - if (!result) - { - TC_LOG_INFO("server.loading", ">> Loaded 0 spell proc event conditions. DB table `spell_proc_event` is empty."); - return; - } + // 0 1 2 3 4 5 + QueryResult result = WorldDatabase.Query("SELECT SpellId, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, " + // 6 7 8 9 10 11 12 13 14 + "ProcFlags, SpellTypeMask, SpellPhaseMask, HitMask, AttributesMask, ProcsPerMinute, Chance, Cooldown, Charges FROM spell_proc"); uint32 count = 0; - - do + if (result) { - Field* fields = result->Fetch(); - - int32 spellId = fields[0].GetInt32(); - - bool allRanks = false; - if (spellId < 0) - { - allRanks = true; - spellId = -spellId; - } - - SpellInfo const* spellInfo = GetSpellInfo(spellId); - if (!spellInfo) + do { - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` does not exist.", spellId); - continue; - } + Field* fields = result->Fetch(); - if (allRanks) - { - if (!spellInfo->IsRanked()) - TC_LOG_ERROR("sql.sql", "The spell %u is listed in `spell_proc_event` with all ranks, but spell has no ranks.", spellId); + int32 spellId = fields[0].GetInt32(); - if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId)) + bool allRanks = false; + if (spellId < 0) { - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` is not first rank of spell.", spellId); - continue; + allRanks = true; + spellId = -spellId; } - } - - SpellProcEventEntry spellProcEvent; - - spellProcEvent.schoolMask = fields[1].GetInt8(); - spellProcEvent.spellFamilyName = fields[2].GetUInt16(); - spellProcEvent.spellFamilyMask[0] = fields[3].GetUInt32(); - spellProcEvent.spellFamilyMask[1] = fields[4].GetUInt32(); - spellProcEvent.spellFamilyMask[2] = fields[5].GetUInt32(); - spellProcEvent.procFlags = fields[6].GetUInt32(); - spellProcEvent.procEx = fields[7].GetUInt32(); - spellProcEvent.ppmRate = fields[8].GetFloat(); - spellProcEvent.customChance = fields[9].GetFloat(); - spellProcEvent.cooldown = fields[10].GetUInt32(); - while (spellInfo) - { - if (mSpellProcEventMap.find(spellInfo->Id) != mSpellProcEventMap.end()) + SpellInfo const* spellInfo = GetSpellInfo(spellId); + if (!spellInfo) { - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` already has its first rank in table.", spellInfo->Id); - break; + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` does not exist", spellId); + continue; } - if (!spellInfo->ProcFlags && !spellProcEvent.procFlags) - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` is probably not a triggered spell.", spellInfo->Id); + if (allRanks) + { + if (!spellInfo->IsRanked()) + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` with all ranks, but spell has no ranks.", spellId); - mSpellProcEventMap[spellInfo->Id] = spellProcEvent; + if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId)) + { + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` is not the first rank of the spell.", spellId); + continue; + } + } - if (allRanks) - spellInfo = spellInfo->GetNextRankSpell(); - else - break; - } + SpellProcEntry baseProcEntry; + + baseProcEntry.SchoolMask = fields[1].GetInt8(); + baseProcEntry.SpellFamilyName = fields[2].GetUInt16(); + baseProcEntry.SpellFamilyMask[0] = fields[3].GetUInt32(); + baseProcEntry.SpellFamilyMask[1] = fields[4].GetUInt32(); + baseProcEntry.SpellFamilyMask[2] = fields[5].GetUInt32(); + baseProcEntry.ProcFlags = fields[6].GetUInt32(); + baseProcEntry.SpellTypeMask = fields[7].GetUInt32(); + baseProcEntry.SpellPhaseMask = fields[8].GetUInt32(); + baseProcEntry.HitMask = fields[9].GetUInt32(); + baseProcEntry.AttributesMask = fields[10].GetUInt32(); + baseProcEntry.ProcsPerMinute = fields[11].GetFloat(); + baseProcEntry.Chance = fields[12].GetFloat(); + baseProcEntry.Cooldown = Milliseconds(fields[13].GetUInt32()); + baseProcEntry.Charges = fields[14].GetUInt8(); + + while (spellInfo) + { + if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end()) + { + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` already has its first rank in the table.", spellInfo->Id); + break; + } - ++count; + SpellProcEntry procEntry = SpellProcEntry(baseProcEntry); + + // take defaults from dbcs + if (!procEntry.ProcFlags) + procEntry.ProcFlags = spellInfo->ProcFlags; + if (!procEntry.Charges) + procEntry.Charges = spellInfo->ProcCharges; + if (!procEntry.Chance && !procEntry.ProcsPerMinute) + procEntry.Chance = float(spellInfo->ProcChance); + + // validate data + if (procEntry.SchoolMask & ~SPELL_SCHOOL_MASK_ALL) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SchoolMask` set: %u", spellInfo->Id, procEntry.SchoolMask); + if (procEntry.SpellFamilyName && (procEntry.SpellFamilyName < SPELLFAMILY_MAGE || procEntry.SpellFamilyName > SPELLFAMILY_PET || procEntry.SpellFamilyName == 14 || procEntry.SpellFamilyName == 16)) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellFamilyName` set: %u", spellInfo->Id, procEntry.SpellFamilyName); + if (procEntry.Chance < 0) + { + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `Chance` field", spellInfo->Id); + procEntry.Chance = 0; + } + if (procEntry.ProcsPerMinute < 0) + { + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `ProcsPerMinute` field", spellInfo->Id); + procEntry.ProcsPerMinute = 0; + } + if (!procEntry.ProcFlags) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `ProcFlags` value defined, proc will not be triggered.", spellInfo->Id); + if (procEntry.SpellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellTypeMask` set: %u", spellInfo->Id, procEntry.SpellTypeMask); + if (procEntry.SpellTypeMask && !(procEntry.ProcFlags & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `SpellTypeMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); + if (!procEntry.SpellPhaseMask && procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `SpellPhaseMask` value defined, but it is required for the defined `ProcFlags` value. Proc will not be triggered.", spellInfo->Id); + if (procEntry.SpellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `SpellPhaseMask` set: %u", spellInfo->Id, procEntry.SpellPhaseMask); + if (procEntry.SpellPhaseMask && !(procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK)) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a `SpellPhaseMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); + if (procEntry.HitMask & ~PROC_HIT_MASK_ALL) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `HitMask` set: %u", spellInfo->Id, procEntry.HitMask); + if (procEntry.HitMask && !(procEntry.ProcFlags & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.ProcFlags & DONE_HIT_PROC_FLAG_MASK && (!procEntry.SpellPhaseMask || procEntry.SpellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH))))) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `HitMask` value defined, but it will not be used for defined `ProcFlags` and `SpellPhaseMask` values.", spellInfo->Id); + + mSpellProcMap[spellInfo->Id] = procEntry; + + if (allRanks) + spellInfo = spellInfo->GetNextRankSpell(); + else + break; + } + ++count; + } while (result->NextRow()); } - while (result->NextRow()); + else + TC_LOG_INFO("server.loading", ">> Loaded 0 spell proc conditions and data. DB table `spell_proc` is empty."); - TC_LOG_INFO("server.loading", ">> Loaded %u extra spell proc event conditions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); -} + TC_LOG_INFO("server.loading", ">> Loaded %u spell proc conditions and data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); -void SpellMgr::LoadSpellProcs() -{ - uint32 oldMSTime = getMSTime(); + // This generates default procs to retain compatibility with previous proc system + TC_LOG_INFO("server.loading", "Generating spell proc data from SpellMap..."); + count = 0; + oldMSTime = getMSTime(); - mSpellProcMap.clear(); // need for reload case + for (SpellInfo const* spellInfo : mSpellInfoMap) + { + if (!spellInfo) + continue; - // 0 1 2 3 4 5 - QueryResult result = WorldDatabase.Query("SELECT SpellId, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, " - // 6 7 8 9 10 11 12 13 14 - "ProcFlags, SpellTypeMask, SpellPhaseMask, HitMask, AttributesMask, ProcsPerMinute, Chance, Cooldown, Charges FROM spell_proc"); + if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end()) + continue; - if (!result) - { - TC_LOG_INFO("server.loading", ">> Loaded 0 spell proc conditions and data. DB table `spell_proc` is empty."); - return; - } + bool found = false, addTriggerFlag = false; + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (!spellInfo->Effects[i].IsEffect()) + continue; - uint32 count = 0; - do - { - Field* fields = result->Fetch(); + uint32 auraName = spellInfo->Effects[i].ApplyAuraName; + if (!auraName) + continue; - int32 spellId = fields[0].GetInt32(); + if (!isTriggerAura[auraName]) + continue; - bool allRanks = false; - if (spellId < 0) - { - allRanks = true; - spellId = -spellId; - } + found = true; - SpellInfo const* spellInfo = GetSpellInfo(spellId); - if (!spellInfo) - { - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` does not exist", spellId); - continue; + if (!addTriggerFlag && isAlwaysTriggeredAura[auraName]) + addTriggerFlag = true; + break; } - if (allRanks) - { - if (!spellInfo->IsRanked()) - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` with all ranks, but spell has no ranks.", spellId); + if (!found) + continue; - if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId)) - { - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` is not the first rank of the spell.", spellId); - continue; - } - } + if (!spellInfo->ProcFlags) + continue; - SpellProcEntry baseProcEntry; + SpellProcEntry procEntry; + procEntry.SchoolMask = 0; + procEntry.SpellFamilyName = spellInfo->SpellFamilyName; + procEntry.ProcFlags = spellInfo->ProcFlags; + for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (spellInfo->Effects[i].IsEffect() && isTriggerAura[spellInfo->Effects[i].ApplyAuraName]) + procEntry.SpellFamilyMask |= spellInfo->Effects[i].SpellClassMask; - baseProcEntry.SchoolMask = fields[1].GetInt8(); - baseProcEntry.SpellFamilyName = fields[2].GetUInt16(); - baseProcEntry.SpellFamilyMask[0] = fields[3].GetUInt32(); - baseProcEntry.SpellFamilyMask[1] = fields[4].GetUInt32(); - baseProcEntry.SpellFamilyMask[2] = fields[5].GetUInt32(); - baseProcEntry.ProcFlags = fields[6].GetUInt32(); - baseProcEntry.SpellTypeMask = fields[7].GetUInt32(); - baseProcEntry.SpellPhaseMask = fields[8].GetUInt32(); - baseProcEntry.HitMask = fields[9].GetUInt32(); - baseProcEntry.AttributesMask = fields[10].GetUInt32(); - baseProcEntry.ProcsPerMinute = fields[11].GetFloat(); - baseProcEntry.Chance = fields[12].GetFloat(); - baseProcEntry.Cooldown = Milliseconds(fields[13].GetUInt32()); - baseProcEntry.Charges = fields[14].GetUInt8(); + procEntry.SpellTypeMask = PROC_SPELL_TYPE_MASK_ALL; + procEntry.SpellPhaseMask = PROC_SPELL_PHASE_HIT; + procEntry.HitMask = PROC_HIT_NONE; // uses default proc @see SpellMgr::CanSpellTriggerProcOnEvent - while (spellInfo) + // Reflect auras should only proc off reflects + for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end()) + if (spellInfo->Effects[i].IsAura(SPELL_AURA_REFLECT_SPELLS) || spellInfo->Effects[i].IsAura(SPELL_AURA_REFLECT_SPELLS_SCHOOL)) { - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` already has its first rank in the table.", spellInfo->Id); + procEntry.HitMask = PROC_HIT_REFLECT; break; } + } - SpellProcEntry procEntry = SpellProcEntry(baseProcEntry); - - // take defaults from dbcs - if (!procEntry.ProcFlags) - procEntry.ProcFlags = spellInfo->ProcFlags; - if (!procEntry.Charges) - procEntry.Charges = spellInfo->ProcCharges; - if (!procEntry.Chance && !procEntry.ProcsPerMinute) - procEntry.Chance = float(spellInfo->ProcChance); - - // validate data - if (procEntry.SchoolMask & ~SPELL_SCHOOL_MASK_ALL) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SchoolMask` set: %u", spellInfo->Id, procEntry.SchoolMask); - if (procEntry.SpellFamilyName && (procEntry.SpellFamilyName < 3 || procEntry.SpellFamilyName > 17 || procEntry.SpellFamilyName == 14 || procEntry.SpellFamilyName == 16)) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellFamilyName` set: %u", spellInfo->Id, procEntry.SpellFamilyName); - if (procEntry.Chance < 0) - { - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `Chance` field", spellInfo->Id); - procEntry.Chance = 0; - } - if (procEntry.ProcsPerMinute < 0) - { - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `ProcsPerMinute` field", spellInfo->Id); - procEntry.ProcsPerMinute = 0; - } - if (procEntry.Chance == 0 && procEntry.ProcsPerMinute == 0) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u doesn't have any `Chance` and `ProcsPerMinute` values defined, proc will not be triggered", spellInfo->Id); - if (!procEntry.ProcFlags) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `ProcFlags` value defined, proc will not be triggered.", spellInfo->Id); - if (procEntry.SpellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellTypeMask` set: %u", spellInfo->Id, procEntry.SpellTypeMask); - if (procEntry.SpellTypeMask && !(procEntry.ProcFlags & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `SpellTypeMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); - if (!procEntry.SpellPhaseMask && procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `SpellPhaseMask` value defined, but it is required for the defined `ProcFlags` value. Proc will not be triggered.", spellInfo->Id); - if (procEntry.SpellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `SpellPhaseMask` set: %u", spellInfo->Id, procEntry.SpellPhaseMask); - if (procEntry.SpellPhaseMask && !(procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK)) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a `SpellPhaseMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); - if (procEntry.HitMask & ~PROC_HIT_MASK_ALL) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `HitMask` set: %u", spellInfo->Id, procEntry.HitMask); - if (procEntry.HitMask && !(procEntry.ProcFlags & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.ProcFlags & DONE_HIT_PROC_FLAG_MASK && (!procEntry.SpellPhaseMask || procEntry.SpellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH))))) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `HitMask` value defined, but it will not be used for defined `ProcFlags` and `SpellPhaseMask` values.", spellInfo->Id); - - mSpellProcMap[spellInfo->Id] = procEntry; + procEntry.AttributesMask = 0; + if (spellInfo->ProcFlags & PROC_FLAG_KILL) + procEntry.AttributesMask |= PROC_ATTR_REQ_EXP_OR_HONOR; + if (addTriggerFlag) + procEntry.AttributesMask |= PROC_ATTR_TRIGGERED_CAN_PROC; - if (allRanks) - spellInfo = spellInfo->GetNextRankSpell(); - else - break; - } + procEntry.ProcsPerMinute = 0; + procEntry.Chance = spellInfo->ProcChance; + procEntry.Cooldown = Milliseconds::zero(); + procEntry.Charges = spellInfo->ProcCharges; + + mSpellProcMap[spellInfo->Id] = procEntry; ++count; } - while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u spell proc conditions and data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Generated spell proc data for %u spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } void SpellMgr::LoadSpellBonusess() @@ -3037,7 +2945,7 @@ void SpellMgr::LoadSpellInfoCorrections() case 59725: // Improved Spell Reflection - aoe aura // Target entry seems to be wrong for this spell :/ spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_CASTER_AREA_PARTY); - spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_10_YARDS_2); + spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_20_YARDS); break; case 44978: // Wild Magic case 45001: @@ -3130,31 +3038,13 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->MaxAffectedTargets = 1; spellInfo->Effects[EFFECT_0].TriggerSpell = 33760; break; - case 17941: // Shadow Trance - case 22008: // Netherwind Focus - case 31834: // Light's Grace - case 34936: // Backlash - case 48108: // Hot Streak - case 51124: // Killing Machine - case 54741: // Firestarter - case 57761: // Fireball! - case 64823: // Item - Druid T8 Balance 4P Bonus - case 34477: // Misdirection - case 44401: // Missile Barrage - case 18820: // Insight - spellInfo->ProcCharges = 1; - break; case 44544: // Fingers of Frost spellInfo->Effects[EFFECT_0].SpellClassMask = flag96(685904631, 1151048, 0); break; - case 74396: // Fingers of Frost visual buff case 53257: // Cobra Strikes spellInfo->ProcCharges = 2; spellInfo->StackAmount = 0; break; - case 28200: // Ascendance (Talisman of Ascendance trinket) - spellInfo->ProcCharges = 6; - break; case 49224: // Magic Suppression - DK case 49610: // Magic Suppression - DK case 49611: // Magic Suppression - DK diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index 5e3193ccd52..ff328a2ac1f 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -194,43 +194,6 @@ enum ProcFlags PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS | \ PROC_FLAG_TAKEN_SPELL_RANGED_DMG_CLASS) -enum ProcFlagsExLegacy -{ - PROC_EX_NONE = 0x0000000, // If none can tigger on Hit/Crit only (passive spells MUST defined by SpellFamily flag) - PROC_EX_NORMAL_HIT = 0x0000001, // If set only from normal hit (only damage spells) - PROC_EX_CRITICAL_HIT = 0x0000002, - PROC_EX_MISS = 0x0000004, - PROC_EX_RESIST = 0x0000008, - PROC_EX_DODGE = 0x0000010, - PROC_EX_PARRY = 0x0000020, - PROC_EX_BLOCK = 0x0000040, - PROC_EX_EVADE = 0x0000080, - PROC_EX_IMMUNE = 0x0000100, - PROC_EX_DEFLECT = 0x0000200, - PROC_EX_ABSORB = 0x0000400, - PROC_EX_REFLECT = 0x0000800, - PROC_EX_INTERRUPT = 0x0001000, // Melee hit result can be Interrupt (not used) - PROC_EX_FULL_BLOCK = 0x0002000, // block al attack damage - PROC_EX_RESERVED2 = 0x0004000, - PROC_EX_NOT_ACTIVE_SPELL = 0x0008000, // Spell mustn't do damage/heal to proc - PROC_EX_EX_TRIGGER_ALWAYS = 0x0010000, // If set trigger always no matter of hit result - PROC_EX_EX_ONE_TIME_TRIGGER = 0x0020000, // If set trigger always but only one time (not implemented yet) - PROC_EX_ONLY_ACTIVE_SPELL = 0x0040000, // Spell has to do damage/heal to proc - - // Flags for internal use - do not use these in db! - PROC_EX_INTERNAL_CANT_PROC = 0x0800000, - PROC_EX_INTERNAL_DOT = 0x1000000, - PROC_EX_INTERNAL_HOT = 0x2000000, - PROC_EX_INTERNAL_TRIGGERED = 0x4000000, - PROC_EX_INTERNAL_REQ_FAMILY = 0x8000000 -}; - -#define AURA_SPELL_PROC_EX_MASK \ - (PROC_EX_NORMAL_HIT | PROC_EX_CRITICAL_HIT | PROC_EX_MISS | \ - PROC_EX_RESIST | PROC_EX_DODGE | PROC_EX_PARRY | PROC_EX_BLOCK | \ - PROC_EX_EVADE | PROC_EX_IMMUNE | PROC_EX_DEFLECT | \ - PROC_EX_ABSORB | PROC_EX_REFLECT | PROC_EX_INTERRUPT) - enum ProcFlagsSpellType { PROC_SPELL_TYPE_NONE = 0x0000000, @@ -271,23 +234,12 @@ enum ProcFlagsHit enum ProcAttributes { - PROC_ATTR_REQ_EXP_OR_HONOR = 0x0000010 + PROC_ATTR_REQ_EXP_OR_HONOR = 0x0000001, // requires proc target to give exp or honor for aura proc + PROC_ATTR_TRIGGERED_CAN_PROC = 0x0000002, // aura can proc even with triggered spells + PROC_ATTR_REQ_MANA_COST = 0x0000004, // requires triggering spell to have a mana cost for aura proc + PROC_ATTR_REQ_SPELLMOD = 0x0000008 // requires triggering spell to be affected by proccing aura to drop charges }; -struct SpellProcEventEntry -{ - uint32 schoolMask; // if nonzero - bit mask for matching proc condition based on spell candidate's school: Fire=2, Mask=1<<(2-1)=2 - uint32 spellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyNamer value - flag96 spellFamilyMask; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyFlags (like auras 107 and 108 do) - uint32 procFlags; // bitmask for matching proc event - uint32 procEx; // proc Extend info (see ProcFlagsEx) - float ppmRate; // for melee (ranged?) damage spells - proc rate per minute. if zero, falls back to flat chance from Spell.dbc - float customChance; // Owerride chance (in most cases for debug only) - uint32 cooldown; // hidden cooldown used for some spell proc events, applied to _triggered_spell_ -}; - -typedef std::unordered_map<uint32, SpellProcEventEntry> SpellProcEventMap; - struct SpellProcEntry { uint32 SchoolMask; // if nonzero - bitmask for matching proc condition based on spell's school @@ -658,10 +610,6 @@ class TC_GAME_API SpellMgr SpellGroupStackRule CheckSpellGroupStackRules(SpellInfo const* spellInfo1, SpellInfo const* spellInfo2) const; SpellGroupStackRule GetSpellGroupStackRule(SpellGroup groupid) const; - // Spell proc event table - SpellProcEventEntry const* GetSpellProcEvent(uint32 spellId) const; - bool IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellInfo const* procSpell, uint32 procFlags, uint32 procExtra, bool active) const; - // Spell proc table SpellProcEntry const* GetSpellProcEntry(uint32 spellId) const; bool CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const; @@ -720,7 +668,6 @@ class TC_GAME_API SpellMgr void LoadSpellTargetPositions(); void LoadSpellGroups(); void LoadSpellGroupStackRules(); - void LoadSpellProcEvents(); void LoadSpellProcs(); void LoadSpellBonusess(); void LoadSpellThreats(); @@ -749,7 +696,6 @@ class TC_GAME_API SpellMgr SpellSpellGroupMap mSpellSpellGroup; SpellGroupSpellMap mSpellGroupSpell; SpellGroupStackMap mSpellGroupStack; - SpellProcEventMap mSpellProcEventMap; SpellProcMap mSpellProcMap; SpellBonusMap mSpellBonusMap; SpellThreatMap mSpellThreatMap; diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp index e2598386466..67aaa582776 100644 --- a/src/server/game/Spells/SpellScript.cpp +++ b/src/server/game/Spells/SpellScript.cpp @@ -720,6 +720,10 @@ bool AuraScript::_Validate(SpellInfo const* entry) if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) TC_LOG_ERROR("scripts", "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `DoCheckProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); + for (std::list<CheckEffectProcHandler>::iterator itr = DoCheckEffectProc.begin(); itr != DoCheckEffectProc.end(); ++itr) + if (!itr->GetAffectedEffectsMask(entry)) + TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `DoCheckEffectProc` of AuraScript won't be executed", entry->Id, itr->ToString().c_str(), m_scriptName->c_str()); + for (std::list<AuraProcHandler>::iterator itr = DoPrepareProc.begin(); itr != DoPrepareProc.end(); ++itr) if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) TC_LOG_ERROR("scripts", "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `DoPrepareProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); @@ -887,6 +891,17 @@ bool AuraScript::CheckProcHandler::Call(AuraScript* auraScript, ProcEventInfo& e return (auraScript->*_HandlerScript)(eventInfo); } +AuraScript::CheckEffectProcHandler::CheckEffectProcHandler(AuraCheckEffectProcFnType handlerScript, uint8 effIndex, uint16 effName) + : AuraScript::EffectBase(effIndex, effName) +{ + _HandlerScript = handlerScript; +} + +bool AuraScript::CheckEffectProcHandler::Call(AuraScript* auraScript, AuraEffect const* aurEff, ProcEventInfo& eventInfo) +{ + return (auraScript->*_HandlerScript)(aurEff, eventInfo); +} + AuraScript::AuraProcHandler::AuraProcHandler(AuraProcFnType handlerScript) { _HandlerScript = handlerScript; @@ -1148,6 +1163,7 @@ Unit* AuraScript::GetTarget() const case AURA_SCRIPT_HOOK_EFFECT_AFTER_MANASHIELD: case AURA_SCRIPT_HOOK_EFFECT_SPLIT: case AURA_SCRIPT_HOOK_CHECK_PROC: + case AURA_SCRIPT_HOOK_CHECK_EFFECT_PROC: case AURA_SCRIPT_HOOK_PREPARE_PROC: case AURA_SCRIPT_HOOK_PROC: case AURA_SCRIPT_HOOK_AFTER_PROC: diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index 539bc54cc94..1ec10f03820 100644 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -468,6 +468,7 @@ enum AuraScriptHookType AURA_SCRIPT_HOOK_AFTER_DISPEL, // Spell Proc Hooks AURA_SCRIPT_HOOK_CHECK_PROC, + AURA_SCRIPT_HOOK_CHECK_EFFECT_PROC, AURA_SCRIPT_HOOK_PREPARE_PROC, AURA_SCRIPT_HOOK_PROC, AURA_SCRIPT_HOOK_EFFECT_PROC, @@ -499,6 +500,7 @@ class TC_GAME_API AuraScript : public _SpellScript typedef void(CLASSNAME::*AuraEffectAbsorbFnType)(AuraEffect*, DamageInfo &, uint32 &); \ typedef void(CLASSNAME::*AuraEffectSplitFnType)(AuraEffect*, DamageInfo &, uint32 &); \ typedef bool(CLASSNAME::*AuraCheckProcFnType)(ProcEventInfo&); \ + typedef bool(CLASSNAME::*AuraCheckEffectProcFnType)(AuraEffect const*, ProcEventInfo&); \ typedef void(CLASSNAME::*AuraProcFnType)(ProcEventInfo&); \ typedef void(CLASSNAME::*AuraEffectProcFnType)(AuraEffect const*, ProcEventInfo&); \ @@ -608,6 +610,14 @@ class TC_GAME_API AuraScript : public _SpellScript private: AuraCheckProcFnType _HandlerScript; }; + class TC_GAME_API CheckEffectProcHandler : public EffectBase + { + public: + CheckEffectProcHandler(AuraCheckEffectProcFnType handlerScript, uint8 effIndex, uint16 effName); + bool Call(AuraScript* auraScript, AuraEffect const* aurEff, ProcEventInfo& eventInfo); + private: + AuraCheckEffectProcFnType _HandlerScript; + }; class TC_GAME_API AuraProcHandler { public: @@ -638,6 +648,7 @@ class TC_GAME_API AuraScript : public _SpellScript class EffectManaShieldFunction : public AuraScript::EffectManaShieldHandler { public: EffectManaShieldFunction(AuraEffectAbsorbFnType _pEffectHandlerScript, uint8 _effIndex) : AuraScript::EffectManaShieldHandler((AuraScript::AuraEffectAbsorbFnType)_pEffectHandlerScript, _effIndex) { } }; \ class EffectSplitFunction : public AuraScript::EffectSplitHandler { public: EffectSplitFunction(AuraEffectSplitFnType _pEffectHandlerScript, uint8 _effIndex) : AuraScript::EffectSplitHandler((AuraScript::AuraEffectSplitFnType)_pEffectHandlerScript, _effIndex) { } }; \ class CheckProcHandlerFunction : public AuraScript::CheckProcHandler { public: CheckProcHandlerFunction(AuraCheckProcFnType handlerScript) : AuraScript::CheckProcHandler((AuraScript::AuraCheckProcFnType)handlerScript) { } }; \ + class CheckEffectProcHandlerFunction : public AuraScript::CheckEffectProcHandler { public: CheckEffectProcHandlerFunction(AuraCheckEffectProcFnType handlerScript, uint8 effIndex, uint16 effName) : AuraScript::CheckEffectProcHandler((AuraScript::AuraCheckEffectProcFnType)handlerScript, effIndex, effName) { } }; \ class AuraProcHandlerFunction : public AuraScript::AuraProcHandler { public: AuraProcHandlerFunction(AuraProcFnType handlerScript) : AuraScript::AuraProcHandler((AuraScript::AuraProcFnType)handlerScript) { } }; \ class EffectProcHandlerFunction : public AuraScript::EffectProcHandler { public: EffectProcHandlerFunction(AuraEffectProcFnType effectHandlerScript, uint8 effIndex, uint16 effName) : AuraScript::EffectProcHandler((AuraScript::AuraEffectProcFnType)effectHandlerScript, effIndex, effName) { } } @@ -776,6 +787,12 @@ class TC_GAME_API AuraScript : public _SpellScript HookList<CheckProcHandler> DoCheckProc; #define AuraCheckProcFn(F) CheckProcHandlerFunction(&F) + // 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); + HookList<CheckEffectProcHandler> DoCheckEffectProc; + #define AuraCheckEffectProcFn(F, I, N) CheckEffectProcHandlerFunction(&F, I, N) + // executed before aura procs (possibility to prevent charge drop/cooldown) // example: DoPrepareProc += AuraProcFn(class::function); // where function is: void function (ProcEventInfo& eventInfo); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 5110e4b09b2..934b33333f2 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1511,9 +1511,6 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Spell Learn Spells..."); sSpellMgr->LoadSpellLearnSpells(); - TC_LOG_INFO("server.loading", "Loading Spell Proc Event conditions..."); - sSpellMgr->LoadSpellProcEvents(); - TC_LOG_INFO("server.loading", "Loading Spell Proc conditions and data..."); sSpellMgr->LoadSpellProcs(); diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index 4470fa7de42..f44b66c7498 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -144,7 +144,6 @@ public: { "spell_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesSpellCommand, "" }, { "spell_linked_spell", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_LINKED_SPELL, true, &HandleReloadSpellLinkedSpellCommand, "" }, { "spell_pet_auras", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_PET_AURAS, true, &HandleReloadSpellPetAurasCommand, "" }, - { "spell_proc_event", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_PROC_EVENT, true, &HandleReloadSpellProcEventCommand, "" }, { "spell_proc", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_PROC, true, &HandleReloadSpellProcsCommand, "" }, { "spell_scripts", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_SCRIPTS, true, &HandleReloadSpellScriptsCommand, "" }, { "spell_target_position", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_TARGET_POSITION, true, &HandleReloadSpellTargetPositionCommand, "" }, @@ -276,7 +275,6 @@ public: HandleReloadSpellGroupsCommand(handler, "a"); HandleReloadSpellLearnSpellCommand(handler, "a"); HandleReloadSpellLinkedSpellCommand(handler, "a"); - HandleReloadSpellProcEventCommand(handler, "a"); HandleReloadSpellProcsCommand(handler, "a"); HandleReloadSpellBonusesCommand(handler, "a"); HandleReloadSpellTargetPositionCommand(handler, "a"); @@ -811,14 +809,6 @@ public: return true; } - static bool HandleReloadSpellProcEventCommand(ChatHandler* handler, const char* /*args*/) - { - TC_LOG_INFO("misc", "Re-Loading Spell Proc Event conditions..."); - sSpellMgr->LoadSpellProcEvents(); - handler->SendGlobalGMSysMessage("DB table `spell_proc_event` (spell proc trigger requirements) reloaded."); - return true; - } - static bool HandleReloadSpellProcsCommand(ChatHandler* handler, const char* /*args*/) { TC_LOG_INFO("misc", "Re-Loading Spell Proc conditions and data..."); |