diff options
Diffstat (limited to 'src/server/game/Spells')
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.cpp | 405 | ||||
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.h | 5 | ||||
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.cpp | 128 | ||||
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.h | 3 | ||||
| -rw-r--r-- | src/server/game/Spells/Spell.cpp | 75 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 17 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellInfo.cpp | 179 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellInfo.h | 10 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellInfoCorrections.cpp | 24 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellMgr.cpp | 379 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellMgr.h | 102 |
11 files changed, 639 insertions, 688 deletions
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 06d3166e0a..7ffb4065f8 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -212,7 +212,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS] = &AuraEffect::HandleModStateImmunityMask, //147 SPELL_AURA_MECHANIC_IMMUNITY_MASK &AuraEffect::HandleAuraRetainComboPoints, //148 SPELL_AURA_RETAIN_COMBO_POINTS &AuraEffect::HandleNoImmediateEffect, //149 SPELL_AURA_REDUCE_PUSHBACK - &AuraEffect::HandleShieldBlockValue, //150 SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT + &AuraEffect::HandleShieldBlockValuePercent, //150 SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT &AuraEffect::HandleAuraTrackStealthed, //151 SPELL_AURA_TRACK_STEALTHED &AuraEffect::HandleNoImmediateEffect, //152 SPELL_AURA_MOD_DETECTED_RANGE implemented in Creature::GetAggroRange &AuraEffect::HandleNoImmediateEffect, //153 SPELL_AURA_SPLIT_DAMAGE_FLAT @@ -393,7 +393,6 @@ AuraEffect::AuraEffect(Aura* base, uint8 effIndex, int32* baseAmount, Unit* cast m_amount = CalculateAmount(caster); m_casterLevel = caster ? caster->GetLevel() : 0; m_applyResilience = caster && caster->CanApplyResilience(); - m_auraGroup = sSpellMgr->GetSpellGroup(GetId()); CalculateSpellMod(); @@ -727,9 +726,12 @@ void AuraEffect::ChangeAmount(int32 newAmount, bool mark, bool onStackOrReapply) std::list<AuraApplication*> effectApplications; GetApplicationList(effectApplications); - for (std::list<AuraApplication*>::const_iterator apptItr = effectApplications.begin(); apptItr != effectApplications.end(); ++apptItr) - if ((*apptItr)->HasEffect(GetEffIndex())) - HandleEffect(*apptItr, handleMask, false); + for (AuraApplication* aurApp : effectApplications) + if (aurApp->HasEffect(GetEffIndex())) + { + aurApp->GetTarget()->_RegisterAuraEffect(this, false); + HandleEffect(aurApp, handleMask, false); + } if (handleMask & AURA_EFFECT_HANDLE_CHANGE_AMOUNT) { @@ -740,9 +742,15 @@ void AuraEffect::ChangeAmount(int32 newAmount, bool mark, bool onStackOrReapply) CalculateSpellMod(); } - for (std::list<AuraApplication*>::const_iterator apptItr = effectApplications.begin(); apptItr != effectApplications.end(); ++apptItr) - if ((*apptItr)->HasEffect(GetEffIndex())) - HandleEffect(*apptItr, handleMask, true); + for (AuraApplication* aurApp : effectApplications) + if (aurApp->HasEffect(GetEffIndex())) + { + if (aurApp->GetRemoveMode() != AURA_REMOVE_NONE) + continue; + + aurApp->GetTarget()->_RegisterAuraEffect(this, true); + HandleEffect(aurApp, handleMask, true); + } } void AuraEffect::HandleEffect(AuraApplication* aurApp, uint8 mode, bool apply) @@ -3000,14 +3008,17 @@ void AuraEffect::HandleAuraModDisarm(AuraApplication const* aurApp, uint8 mode, // Handle damage modification, shapeshifted druids are not affected if (target->IsPlayer() && (!target->IsInFeralForm() || target->GetShapeshiftForm() == FORM_GHOSTWOLF)) { - if (Item* pItem = target->ToPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) + Player* player = target->ToPlayer(); + if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) { - WeaponAttackType attacktype = Player::GetAttackBySlot(slot); + WeaponAttackType attackType = Player::GetAttackBySlot(slot); - if (attacktype < MAX_ATTACK) + player->ApplyItemDependentAuras(pItem, !apply); + if (attackType < MAX_ATTACK) { - target->ToPlayer()->_ApplyWeaponDamage(slot, pItem->GetTemplate(), nullptr, !apply); - target->ToPlayer()->_ApplyWeaponDependentAuraMods(pItem, attacktype, !apply); + player->_ApplyWeaponDamage(slot, pItem->GetTemplate(), nullptr, !apply); + if (!apply) // apply case already handled on item dependent aura removal (if any) + player->UpdateWeaponDependentAuras(attackType); } } } @@ -3439,9 +3450,17 @@ void AuraEffect::HandleModThreat(AuraApplication const* aurApp, uint8 mode, bool return; Unit* target = aurApp->GetTarget(); - for (int8 i = 0; i < MAX_SPELL_SCHOOL; ++i) + for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i) if (GetMiscValue() & (1 << i)) - ApplyPercentModFloatVar(target->m_threatModifier[i], float(GetAmount()), apply); + { + if (apply) + AddPct(target->m_threatModifier[i], GetAmount()); + else + { + float amount = target->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_THREAT, 1 << i); + target->m_threatModifier[i] = amount; + } + } } void AuraEffect::HandleAuraModTotalThreat(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -4318,7 +4337,7 @@ void AuraEffect::HandleAuraModResistanceExclusive(AuraApplication const* aurApp, Unit* target = aurApp->GetTarget(); - for (int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL; x++) + for (uint8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL; x++) { if (GetMiscValue() & int32(1 << x)) { @@ -4326,9 +4345,9 @@ void AuraEffect::HandleAuraModResistanceExclusive(AuraApplication const* aurApp, if (amount < GetAmount()) { float value = float(GetAmount() - amount); - target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_VALUE, value, apply); - if (target->IsPlayer()) - target->ApplyResistanceBuffModsMod(SpellSchools(x), aurApp->IsPositive(), value, apply); + target->HandleStatFlatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_VALUE, value, apply); + if (target->IsPlayer() || target->IsPet()) + target->UpdateResistanceBuffModsMod(SpellSchools(x)); } } } @@ -4345,9 +4364,9 @@ void AuraEffect::HandleAuraModResistance(AuraApplication const* aurApp, uint8 mo { if (GetMiscValue() & int32(1 << x)) { - target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), TOTAL_VALUE, float(GetAmount()), apply); + target->HandleStatFlatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), TOTAL_VALUE, float(GetAmount()), apply); if (target->IsPlayer() || target->IsPet()) - target->ApplyResistanceBuffModsMod(SpellSchools(x), GetAmount() > 0, (float)GetAmount(), apply); + target->UpdateResistanceBuffModsMod(SpellSchools(x)); } } } @@ -4358,32 +4377,39 @@ void AuraEffect::HandleAuraModBaseResistancePCT(AuraApplication const* aurApp, u return; Unit* target = aurApp->GetTarget(); - for (int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL; x++) + for (uint8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL; x++) { if (GetMiscValue() & int32(1 << x)) { - target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_PCT, float(GetAmount()), apply); + if (apply) + target->ApplyStatPctModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_PCT, float(GetAmount())); + else + { + float amount = target->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_BASE_RESISTANCE_PCT, 1 << x); + target->SetStatPctModifier(UnitMods(UNIT_MOD_RESISTANCE_START + x), BASE_PCT, amount); + } } } } -void AuraEffect::HandleModResistancePercent(AuraApplication const* aurApp, uint8 mode, bool apply) const +void AuraEffect::HandleModResistancePercent(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const { if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) return; Unit* target = aurApp->GetTarget(); - for (int8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++) + for (uint8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++) { if (GetMiscValue() & int32(1 << i)) { - target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_PCT, float(GetAmount()), apply); + float amount = target->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_RESISTANCE_PCT, 1 << i); + if (target->GetPctModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_PCT) == amount) + continue; + + target->SetStatPctModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_PCT, amount); if (target->IsPlayer() || target->IsPet()) - { - target->ApplyResistanceBuffModsPercentMod(SpellSchools(i), true, (float)GetAmount(), apply); - target->ApplyResistanceBuffModsPercentMod(SpellSchools(i), false, (float)GetAmount(), apply); - } + target->UpdateResistanceBuffModsMod(SpellSchools(i)); } } } @@ -4398,7 +4424,7 @@ void AuraEffect::HandleModBaseResistance(AuraApplication const* aurApp, uint8 mo { if (GetMiscValue() & (1 << i)) { - target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_VALUE, float(GetAmount()), apply); + target->HandleStatFlatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_VALUE, float(GetAmount()), apply); } } } @@ -4430,23 +4456,28 @@ void AuraEffect::HandleAuraModStat(AuraApplication const* aurApp, uint8 mode, bo if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) return; - Unit* target = aurApp->GetTarget(); - if (GetMiscValue() < -2 || GetMiscValue() > 4) { LOG_ERROR("spells.aura.effect", "WARNING: Spell {} effect {} has an unsupported misc value ({}) for SPELL_AURA_MOD_STAT ", GetId(), GetEffIndex(), GetMiscValue()); return; } + Unit* target = aurApp->GetTarget(); + int32 spellGroupVal = target->GetHighestExclusiveSameEffectSpellGroupValue(this, SPELL_AURA_MOD_STAT, true, GetMiscValue()); + if (std::abs(spellGroupVal) >= std::abs(GetAmount())) + return; + for (int32 i = STAT_STRENGTH; i < MAX_STATS; i++) { // -1 or -2 is all stats (misc < -2 checked in function beginning) if (GetMiscValue() < 0 || GetMiscValue() == i) { - //target->ApplyStatMod(Stats(i), m_amount, apply); - target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_VALUE, float(GetAmount()), apply); + if (spellGroupVal) + target->HandleStatFlatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_VALUE, float(GetAmount()), !apply); + + target->HandleStatFlatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_VALUE, float(GetAmount()), apply); if (target->IsPlayer() || target->IsPet()) - target->ApplyStatBuffMod(Stats(i), (float)GetAmount(), apply); + target->UpdateStatBuffMod(Stats(i)); } } } @@ -4470,8 +4501,16 @@ void AuraEffect::HandleModPercentStat(AuraApplication const* aurApp, uint8 mode, for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i) { - if (GetMiscValue() == i || GetMiscValue() == -1) - target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, float(m_amount), apply); + if (apply) + target->ApplyStatPctModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, float(GetAmount())); + else + { + float amount = target->GetTotalAuraMultiplier(SPELL_AURA_MOD_PERCENT_STAT, [i](AuraEffect const* aurEff) + { + return (aurEff->GetMiscValue() == i || aurEff->GetMiscValue() == -1); + }); + target->SetStatPctModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, amount); + } } } @@ -4549,7 +4588,7 @@ void AuraEffect::HandleModHealingDone(AuraApplication const* aurApp, uint8 mode, target->ToPlayer()->UpdateSpellDamageAndHealingBonus(); } -void AuraEffect::HandleModTotalPercentStat(AuraApplication const* aurApp, uint8 mode, bool apply) const +void AuraEffect::HandleModTotalPercentStat(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const { if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) return; @@ -4565,39 +4604,22 @@ void AuraEffect::HandleModTotalPercentStat(AuraApplication const* aurApp, uint8 // save current health state float healthPct = target->GetHealthPct(); bool alive = target->IsAlive(); - float value = GetAmount(); - - if (GetId() == 67480) // xinef: hack fix for blessing of sanctuary stats stack with blessing of kings... - { - if (value) // not turned off - value = 10.0f; - for (int32 i = STAT_STRENGTH; i < MAX_STATS; i++) - { - if (i == STAT_STRENGTH || i == STAT_STAMINA) - { - if (apply && (target->IsPlayer() || target->IsPet())) - target->ApplyStatPercentBuffMod(Stats(i), value, apply); - - target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_PCT, value, apply); - - if (!apply && (target->IsPlayer() || target->IsPet())) - target->ApplyStatPercentBuffMod(Stats(i), value, apply); - } - } - return; - } for (int32 i = STAT_STRENGTH; i < MAX_STATS; i++) { if (GetMiscValue() == i || GetMiscValue() == -1) { - if (apply && (target->IsPlayer() || target->IsPet())) - target->ApplyStatPercentBuffMod(Stats(i), value, apply); + float amount = target->GetTotalAuraMultiplier(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE, [i](AuraEffect const* aurEff) + { + return (aurEff->GetMiscValue() == i || aurEff->GetMiscValue() == -1); + }); - target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_PCT, value, apply); + if (target->GetPctModifierValue(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_PCT) == amount) + continue; - if (!apply && (target->IsPlayer() || target->IsPet())) - target->ApplyStatPercentBuffMod(Stats(i), value, apply); + target->SetStatPctModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_PCT, amount); + if (target->IsPlayer() || target->IsPet()) + target->UpdateStatBuffMod(Stats(i)); } } @@ -4693,7 +4715,7 @@ void AuraEffect::HandleAuraModIncreaseHealth(AuraApplication const* aurApp, uint if (apply) { - target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(GetAmount()), apply); + target->HandleStatFlatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(GetAmount()), apply); target->ModifyHealth(GetAmount()); } else @@ -4702,7 +4724,7 @@ void AuraEffect::HandleAuraModIncreaseHealth(AuraApplication const* aurApp, uint target->ModifyHealth(-GetAmount()); else target->SetHealth(1); - target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(GetAmount()), apply); + target->HandleStatFlatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(GetAmount()), apply); } } @@ -4716,7 +4738,7 @@ void AuraEffect::HandleAuraModIncreaseMaxHealth(AuraApplication const* aurApp, u uint32 oldhealth = target->GetHealth(); double healthPercentage = (double)oldhealth / (double)target->GetMaxHealth(); - target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(GetAmount()), apply); + target->HandleStatFlatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(GetAmount()), apply); // refresh percentage if (oldhealth > 0) @@ -4746,7 +4768,7 @@ void AuraEffect::HandleAuraModIncreaseEnergy(AuraApplication const* aurApp, uint UnitMods unitMod = UnitMods(static_cast<uint16>(UNIT_MOD_POWER_START) + PowerType); - target->HandleStatModifier(unitMod, TOTAL_VALUE, float(GetAmount()), apply); + target->HandleStatFlatModifier(unitMod, TOTAL_VALUE, float(GetAmount()), apply); } void AuraEffect::HandleAuraModIncreaseEnergyPercent(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -4765,17 +4787,16 @@ void AuraEffect::HandleAuraModIncreaseEnergyPercent(AuraApplication const* aurAp // return; UnitMods unitMod = UnitMods(static_cast<uint16>(UNIT_MOD_POWER_START) + PowerType); - float amount = float(GetAmount()); if (apply) { - target->HandleStatModifier(unitMod, TOTAL_PCT, amount, apply); - target->ModifyPowerPct(PowerType, amount, apply); + float amount = float(GetAmount()); + target->ApplyStatPctModifier(unitMod, TOTAL_PCT, amount); } else { - target->ModifyPowerPct(PowerType, amount, apply); - target->HandleStatModifier(unitMod, TOTAL_PCT, amount, apply); + float amount = target->GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_INCREASE_ENERGY_PERCENT, GetMiscValue()); + target->SetStatPctModifier(unitMod, TOTAL_PCT, amount); } } @@ -4788,7 +4809,14 @@ void AuraEffect::HandleAuraModIncreaseHealthPercent(AuraApplication const* aurAp // Unit will keep hp% after MaxHealth being modified if unit is alive. float percent = target->GetHealthPct(); - target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_PCT, float(GetAmount()), apply); + + if (apply) + target->ApplyStatPctModifier(UNIT_MOD_HEALTH, TOTAL_PCT, float(GetAmount())); + else + { + float amount = target->GetTotalAuraMultiplier(SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT); + target->SetStatPctModifier(UNIT_MOD_HEALTH, TOTAL_PCT, amount); + } // Xinef: pct was rounded down and could "kill" creature by setting its health to 0 making npc zombie if (target->IsAlive()) @@ -4803,7 +4831,13 @@ void AuraEffect::HandleAuraIncreaseBaseHealthPercent(AuraApplication const* aurA Unit* target = aurApp->GetTarget(); - target->HandleStatModifier(UNIT_MOD_HEALTH, BASE_PCT, float(GetAmount()), apply); + if (apply) + target->ApplyStatPctModifier(UNIT_MOD_HEALTH, BASE_PCT, float(GetAmount())); + else + { + float amount = target->GetTotalAuraMultiplier(SPELL_AURA_MOD_BASE_HEALTH_PCT); + target->SetStatPctModifier(UNIT_MOD_HEALTH, BASE_PCT, amount); + } } /********************************/ @@ -4857,34 +4891,17 @@ void AuraEffect::HandleAuraModRegenInterrupt(AuraApplication const* aurApp, uint HandleModManaRegen(aurApp, mode, apply); } -void AuraEffect::HandleAuraModWeaponCritPercent(AuraApplication const* aurApp, uint8 mode, bool apply) const +void AuraEffect::HandleAuraModWeaponCritPercent(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const { if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) return; - Unit* target = aurApp->GetTarget(); + Player* target = aurApp->GetTarget()->ToPlayer(); - if (!target->IsPlayer()) + if (!target) return; - for (int i = 0; i < MAX_ATTACK; ++i) - if (Item* pItem = target->ToPlayer()->GetWeaponForAttack(WeaponAttackType(i), true)) - target->ToPlayer()->_ApplyWeaponDependentAuraCritMod(pItem, WeaponAttackType(i), this, apply); - - // mods must be applied base at equipped weapon class and subclass comparison - // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask - // GetMiscValue() comparison with item generated damage types - - if (GetSpellInfo()->EquippedItemClass == -1) - { - target->ToPlayer()->HandleBaseModValue(CRIT_PERCENTAGE, FLAT_MOD, float (GetAmount()), apply); - target->ToPlayer()->HandleBaseModValue(OFFHAND_CRIT_PERCENTAGE, FLAT_MOD, float (GetAmount()), apply); - target->ToPlayer()->HandleBaseModValue(RANGED_CRIT_PERCENTAGE, FLAT_MOD, float (GetAmount()), apply); - } - else - { - // done in Player::_ApplyWeaponDependentAuraMods - } + target->UpdateAllWeaponDependentCritAuras(); } void AuraEffect::HandleModHitChance(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -4960,9 +4977,7 @@ void AuraEffect::HandleAuraModCritPct(AuraApplication const* aurApp, uint8 mode, return; } - target->ToPlayer()->HandleBaseModValue(CRIT_PERCENTAGE, FLAT_MOD, float (GetAmount()), apply); - target->ToPlayer()->HandleBaseModValue(OFFHAND_CRIT_PERCENTAGE, FLAT_MOD, float (GetAmount()), apply); - target->ToPlayer()->HandleBaseModValue(RANGED_CRIT_PERCENTAGE, FLAT_MOD, float (GetAmount()), apply); + target->ToPlayer()->UpdateAllWeaponDependentCritAuras(); // included in Player::UpdateSpellCritChance calculation target->ToPlayer()->UpdateAllSpellCritChances(); @@ -4986,6 +5001,13 @@ void AuraEffect::HandleModCastingSpeed(AuraApplication const* aurApp, uint8 mode return; } + int32 spellGroupVal = target->GetHighestExclusiveSameEffectSpellGroupValue(this, GetAuraType()); + if (std::abs(spellGroupVal) >= std::abs(GetAmount())) + return; + + if (spellGroupVal) + target->ApplyCastTimePercentMod(float(spellGroupVal), !apply); + target->ApplyCastTimePercentMod((float)GetAmount(), apply); } @@ -5007,6 +5029,17 @@ void AuraEffect::HandleModCombatSpeedPct(AuraApplication const* aurApp, uint8 mo return; Unit* target = aurApp->GetTarget(); + int32 spellGroupVal = target->GetHighestExclusiveSameEffectSpellGroupValue(this, SPELL_AURA_MELEE_SLOW); + if (std::abs(spellGroupVal) >= std::abs(GetAmount())) + return; + + if (spellGroupVal) + { + target->ApplyCastTimePercentMod(float(spellGroupVal), !apply); + target->ApplyAttackTimePercentMod(BASE_ATTACK, float(spellGroupVal), !apply); + target->ApplyAttackTimePercentMod(OFF_ATTACK, float(spellGroupVal), !apply); + target->ApplyAttackTimePercentMod(RANGED_ATTACK, float(spellGroupVal), !apply); + } target->ApplyCastTimePercentMod(float(GetAmount()), apply); target->ApplyAttackTimePercentMod(BASE_ATTACK, float(GetAmount()), apply); @@ -5031,7 +5064,15 @@ void AuraEffect::HandleModMeleeSpeedPct(AuraApplication const* aurApp, uint8 mod return; Unit* target = aurApp->GetTarget(); + int32 spellGroupVal = target->GetHighestExclusiveSameEffectSpellGroupValue(this, SPELL_AURA_MOD_MELEE_HASTE); + if (std::abs(spellGroupVal) >= std::abs(GetAmount())) + return; + if (spellGroupVal) + { + target->ApplyAttackTimePercentMod(BASE_ATTACK, float(spellGroupVal), !apply); + target->ApplyAttackTimePercentMod(OFF_ATTACK, float(spellGroupVal), !apply); + } target->ApplyAttackTimePercentMod(BASE_ATTACK, float(GetAmount()), apply); target->ApplyAttackTimePercentMod(OFF_ATTACK, float(GetAmount()), apply); } @@ -5073,7 +5114,7 @@ void AuraEffect::HandleModRating(AuraApplication const* aurApp, uint8 mode, bool if (!target->IsPlayer()) return; - for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating) + for (uint8 rating = 0; rating < MAX_COMBAT_RATING; ++rating) if (GetMiscValue() & (1 << rating)) target->ToPlayer()->ApplyRatingMod(CombatRating(rating), GetAmount(), apply); } @@ -5089,7 +5130,7 @@ void AuraEffect::HandleModRatingFromStat(AuraApplication const* aurApp, uint8 mo return; // Just recalculate ratings - for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating) + for (uint8 rating = 0; rating < MAX_COMBAT_RATING; ++rating) if (GetMiscValue() & (1 << rating)) target->ToPlayer()->ApplyRatingMod(CombatRating(rating), 0, apply); } @@ -5105,7 +5146,7 @@ void AuraEffect::HandleAuraModAttackPower(AuraApplication const* aurApp, uint8 m Unit* target = aurApp->GetTarget(); - target->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(GetAmount()), apply); + target->HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(GetAmount()), apply); } void AuraEffect::HandleAuraModRangedAttackPower(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -5118,7 +5159,7 @@ void AuraEffect::HandleAuraModRangedAttackPower(AuraApplication const* aurApp, u if ((target->getClassMask() & CLASSMASK_WAND_USERS) != 0) return; - target->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(GetAmount()), apply); + target->HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(GetAmount()), apply); } void AuraEffect::HandleAuraModAttackPowerPercent(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -5129,7 +5170,13 @@ void AuraEffect::HandleAuraModAttackPowerPercent(AuraApplication const* aurApp, Unit* target = aurApp->GetTarget(); //UNIT_FIELD_ATTACK_POWER_MULTIPLIER = multiplier - 1 - target->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, float(GetAmount()), apply); + if (apply) + target->ApplyStatPctModifier(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, float(GetAmount())); + else + { + float amount = target->GetTotalAuraMultiplier(SPELL_AURA_MOD_ATTACK_POWER_PCT); + target->SetStatPctModifier(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, amount); + } } void AuraEffect::HandleAuraModRangedAttackPowerPercent(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -5143,7 +5190,13 @@ void AuraEffect::HandleAuraModRangedAttackPowerPercent(AuraApplication const* au return; //UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER = multiplier - 1 - target->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_PCT, float(GetAmount()), apply); + if (apply) + target->ApplyStatPctModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_PCT, float(GetAmount())); + else + { + float amount = target->GetTotalAuraMultiplier(SPELL_AURA_MOD_RANGED_ATTACK_POWER_PCT); + target->SetStatPctModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_PCT, amount); + } } void AuraEffect::HandleAuraModRangedAttackPowerOfStatPercent(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const @@ -5184,141 +5237,89 @@ void AuraEffect::HandleModDamageDone(AuraApplication const* aurApp, uint8 mode, Unit* target = aurApp->GetTarget(); - // apply item specific bonuses for already equipped weapon + if ((GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) != 0) + target->UpdateAllDamageDoneMods(); + + // Magic damage modifiers implemented in Unit::SpellBaseDamageBonus + // This information for client side use only if (target->IsPlayer()) { - for (int i = 0; i < MAX_ATTACK; ++i) - if (Item* pItem = target->ToPlayer()->GetWeaponForAttack(WeaponAttackType(i), true)) - target->ToPlayer()->_ApplyWeaponDependentAuraDamageMod(pItem, WeaponAttackType(i), this, apply); - } - // GetMiscValue() is bitmask of spell schools - // 1 (0-bit) - normal school damage (SPELL_SCHOOL_MASK_NORMAL) - // 126 - full bitmask all magic damages (SPELL_SCHOOL_MASK_MAGIC) including wands - // 127 - full bitmask any damages - // - // mods must be applied base at equipped weapon class and subclass comparison - // with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask - // GetMiscValue() comparison with item generated damage types + uint16 baseField = GetAmount() >= 0 ? PLAYER_FIELD_MOD_DAMAGE_DONE_POS : PLAYER_FIELD_MOD_DAMAGE_DONE_NEG; + for (uint16 i = 0; i < MAX_SPELL_SCHOOL; ++i) + if (GetMiscValue() & (1 << i)) + target->ApplyModUInt32Value(baseField + i, GetAmount(), apply); - if ((GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) != 0 && sScriptMgr->CanModAuraEffectDamageDone(this, target, aurApp, mode, apply)) - { - // apply generic physical damage bonuses including wand case - if (GetSpellInfo()->EquippedItemClass == -1 || !target->IsPlayer()) - { - target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(GetAmount()), apply); - target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(GetAmount()), apply); - target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(GetAmount()), apply); - - if (target->IsPlayer()) - { - if (GetAmount() > 0) - target->ApplyModInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS, GetAmount(), apply); - else - target->ApplyModInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG, GetAmount(), apply); - } - } - else - { - // done in Player::_ApplyWeaponDependentAuraMods - } + if (Guardian* pet = target->ToPlayer()->GetGuardianPet()) + pet->UpdateAttackPowerAndDamage(); } +} - // Skip non magic case for Speedup - if ((GetMiscValue() & SPELL_SCHOOL_MASK_MAGIC) == 0) +void AuraEffect::HandleModDamagePercentDone(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const +{ + if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) return; - if (GetSpellInfo()->EquippedItemClass != -1 || GetSpellInfo()->EquippedItemInventoryTypeMask != 0) - { - // wand magic case (skip generic to all item spell bonuses) - // done in Player::_ApplyWeaponDependentAuraMods - - // Skip item specific requirements for not wand magic damage + Unit* target = aurApp->GetTarget(); + if (!target) return; - } - // Magic damage modifiers implemented in Unit::SpellDamageBonus - // This information for client side use only + if ((GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL)) + target->UpdateAllDamagePctDoneMods(); + if (target->IsPlayer()) { - if (GetAmount() > 0) + for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i) { - for (uint32 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++) + if (GetMiscValue() & (1 << i)) { - if ((GetMiscValue() & (1 << i)) != 0) - target->ApplyModInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + i, GetAmount(), apply); + // only aura type modifying PLAYER_FIELD_MOD_DAMAGE_DONE_PCT + float amount = target->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, 1 << i); + target->SetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT + i, amount); } } - else - { - for (uint32 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++) - { - if ((GetMiscValue() & (1 << i)) != 0) - target->ApplyModInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + i, GetAmount(), apply); - } - } - if (Guardian* pet = target->ToPlayer()->GetGuardianPet()) - pet->UpdateAttackPowerAndDamage(); } } -void AuraEffect::HandleModDamagePercentDone(AuraApplication const* aurApp, uint8 mode, bool apply) const +void AuraEffect::HandleModOffhandDamagePercent(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const { if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) return; Unit* target = aurApp->GetTarget(); - if (!target) - return; - - if (!sScriptMgr->CanModAuraEffectModDamagePercentDone(this, target, aurApp, mode, apply)) - return; - if (target->IsPlayer()) - { - for (int i = 0; i < MAX_ATTACK; ++i) - if (Item* item = target->ToPlayer()->GetWeaponForAttack(WeaponAttackType(i), false)) - target->ToPlayer()->_ApplyWeaponDependentAuraDamageMod(item, WeaponAttackType(i), this, apply); - } - - if ((GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) && (GetSpellInfo()->EquippedItemClass == -1 || !target->IsPlayer())) - { - target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, float(GetAmount()), apply); - target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(GetAmount()), apply); - target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_PCT, float(GetAmount()), apply); - - if (target->IsPlayer()) - target->ToPlayer()->ApplyPercentModFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT, float (GetAmount()), apply); - } - else - { - // done in Player::_ApplyWeaponDependentAuraMods for SPELL_SCHOOL_MASK_NORMAL && EquippedItemClass != -1 and also for wand case - } + // also handles spell group stacks + target->UpdateDamagePctDoneMods(OFF_ATTACK); } -void AuraEffect::HandleModOffhandDamagePercent(AuraApplication const* aurApp, uint8 mode, bool apply) const +void AuraEffect::HandleShieldBlockValue(AuraApplication const* aurApp, uint8 mode, bool apply) const { if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) return; - Unit* target = aurApp->GetTarget(); + Player* target = aurApp->GetTarget()->ToPlayer(); + if (!target) + return; - target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(GetAmount()), apply); + target->HandleBaseModFlatValue(SHIELD_BLOCK_VALUE, float(GetAmount()), apply); } -void AuraEffect::HandleShieldBlockValue(AuraApplication const* aurApp, uint8 mode, bool apply) const +void AuraEffect::HandleShieldBlockValuePercent(AuraApplication const* aurApp, uint8 mode, bool apply) const { if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) return; - Unit* target = aurApp->GetTarget(); - - BaseModType modType = FLAT_MOD; - if (GetAuraType() == SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT) - modType = PCT_MOD; + Player* target = aurApp->GetTarget()->ToPlayer(); + if (!target) + return; - if (target->IsPlayer()) - target->ToPlayer()->HandleBaseModValue(SHIELD_BLOCK_VALUE, modType, float(GetAmount()), apply); + if (apply) + target->ApplyBaseModPctValue(SHIELD_BLOCK_VALUE, float(GetAmount())); + else + { + float amount = target->GetTotalAuraMultiplier(SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT); + target->SetBaseModPctValue(SHIELD_BLOCK_VALUE, amount); + } } /********************************/ diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h index 73640626bc..8349c83fc2 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.h +++ b/src/server/game/Spells/Auras/SpellAuraEffects.h @@ -112,8 +112,6 @@ public: float GetPctMods() const { return m_pctMods; } void SetPctMods(float pctMods) { m_pctMods = pctMods; } - // xinef: stacking - uint32 GetAuraGroup() const { return m_auraGroup; } int32 GetOldAmount() const { return m_oldAmount; } void SetOldAmount(int32 amount) { m_oldAmount = amount; } void SetEnabled(bool enabled) { m_isAuraEnabled = enabled; } @@ -131,8 +129,6 @@ private: float m_critChance; float m_pctMods; - // xinef: stacking - uint32 m_auraGroup; int32 m_oldAmount; bool m_isAuraEnabled; // xinef: channel information for channel triggering @@ -299,6 +295,7 @@ public: void HandleModDamagePercentDone(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleModOffhandDamagePercent(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleShieldBlockValue(AuraApplication const* aurApp, uint8 mode, bool apply) const; + void HandleShieldBlockValuePercent(AuraApplication const* aurApp, uint8 mode, bool apply) const; // power cost void HandleModPowerCostPCT(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleModPowerCost(AuraApplication const* aurApp, uint8 mode, bool apply) const; diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 7d6260e24e..0b3b35d59e 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -181,67 +181,6 @@ void AuraApplication::_HandleEffect(uint8 effIndex, bool apply) // Remove all triggered by aura spells vs unlimited duration aurEff->CleanupTriggeredSpells(GetTarget()); } - - // Stacking! - if (uint32 groupId = aurEff->GetAuraGroup()) - { - SpellGroupStackFlags sFlag = sSpellMgr->GetGroupStackFlags(groupId); - if (!aurEff->IsPeriodic() && (sFlag & SPELL_GROUP_STACK_FLAG_EFFECT_EXCLUSIVE)) - { - AuraApplication* strongestApp = apply ? this : nullptr; - AuraEffect* strongestEff = apply ? aurEff : nullptr; - int32 amount = apply ? std::abs(aurEff->GetAmount()) : 0; - Unit* target = GetTarget(); - Unit::AuraEffectList const& auraList = target->GetAuraEffectsByType(aurEff->GetAuraType()); - for (Unit::AuraEffectList::const_iterator iter = auraList.begin(); iter != auraList.end(); ++iter) - { - if ((*iter)->GetAuraGroup() != groupId || (*iter) == strongestEff || (*iter)->GetBase()->IsRemoved()) - continue; - - // xinef: skip different misc values - if (aurEff->GetAuraType() != SPELL_AURA_230 /*SPELL_AURA_MOD_INCREASE_HEALTH_2*/ && aurEff->GetAuraType() != SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK && - aurEff->GetMiscValue() != (*iter)->GetMiscValue()) - continue; - - // xinef: should not happen - AuraApplication* aurApp = (*iter)->GetBase()->GetApplicationOfTarget(target->GetGUID()); - if (!aurApp) - continue; - - if (amount < std::abs((*iter)->GetForcedAmount())) - { - // xinef: if we have strongest aura and it is active, turn it off - // xinef: otherwise just save new aura; - if (strongestApp && strongestEff && strongestApp->IsActive(strongestEff->GetEffIndex())) - { - strongestEff->HandleEffect(strongestApp, AURA_EFFECT_HANDLE_CHANGE_AMOUNT, false); - if (!strongestEff->GetSpellInfo()->HasAreaAuraEffect()) - strongestEff->SetEnabled(false); - strongestApp->SetDisableMask(strongestEff->GetEffIndex()); - } - strongestApp = aurApp; - strongestEff = (*iter); - amount = std::abs((*iter)->GetAmount()); - } - // xinef: itered aura is weaker, deactivate if active - else if (aurApp->IsActive((*iter)->GetEffIndex())) - { - (*iter)->HandleEffect(aurApp, AURA_EFFECT_HANDLE_CHANGE_AMOUNT, false); - if (!(*iter)->GetSpellInfo()->HasAreaAuraEffect()) - (*iter)->SetEnabled(false); - aurApp->SetDisableMask((*iter)->GetEffIndex()); - } - } - - // xinef: if we have new strongest aura, and it is not active - if (strongestApp && strongestEff && !strongestApp->IsActive(strongestEff->GetEffIndex())) - { - strongestApp->RemoveDisableMask(strongestEff->GetEffIndex()); - strongestEff->SetEnabled(true); - strongestEff->HandleEffect(strongestApp, AURA_EFFECT_HANDLE_CHANGE_AMOUNT, true); - } - } - } SetNeedClientUpdate(); } @@ -661,6 +600,9 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) if (!itr->second || itr->first->IsImmunedToSpell(GetSpellInfo()) || !CanBeAppliedOn(itr->first)) addUnit = false; + if (addUnit && !itr->first->IsHighestExclusiveAura(this, true)) + addUnit = false; + if (addUnit) { // persistent area aura does not hit flying targets @@ -684,7 +626,7 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) for (Unit::AuraApplicationMap::iterator iter = itr->first->GetAppliedAuras().begin(); iter != itr->first->GetAppliedAuras().end(); ++iter) { Aura const* aura = iter->second->GetBase(); - if (!CanStackWith(aura, false)) + if (!CanStackWith(aura)) { addUnit = false; break; @@ -1069,6 +1011,16 @@ void Aura::RefreshSpellMods() player->RestoreAllSpellMods(0, this); } +bool Aura::HasMoreThanOneEffectForType(AuraType auraType) const +{ + uint32 count = 0; + for (SpellEffectInfo const& spellEffectInfo : GetSpellInfo()->GetEffects()) + if (HasEffect(spellEffectInfo.EffectIndex) && spellEffectInfo.ApplyAuraName == auraType) + ++count; + + return count > 1; +} + bool Aura::IsArea() const { for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) @@ -1589,7 +1541,7 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b // Alchemy: Mixology if (caster && caster->HasAura(53042) && caster->IsPlayer() && !caster->ToPlayer()->GetSession()->PlayerLoading()) { - if (sSpellMgr->GetSpellGroup(GetId()) == 1) /*Elixirs*/ + if (sSpellMgr->IsSpellMemberOfSpellGroup(GetId(), SPELL_GROUP_ELIXIR_BATTLE) || sSpellMgr->IsSpellMemberOfSpellGroup(GetId(), SPELL_GROUP_ELIXIR_GUARDIAN)) { if (caster->HasSpell(GetSpellInfo()->Effects[EFFECT_0].TriggerSpell)) { @@ -2018,7 +1970,7 @@ bool Aura::IsAuraStronger(Aura const* newAura) const return false; } -bool Aura::CanStackWith(Aura const* existingAura, bool remove) const +bool Aura::CanStackWith(Aura const* existingAura) const { // Can stack with self if (this == existingAura) @@ -2056,47 +2008,19 @@ bool Aura::CanStackWith(Aura const* existingAura, bool remove) const return false; // check spell group stack rules - // xinef: this assures us that both spells are in same group! - SpellGroupStackFlags stackFlags = sSpellMgr->CheckSpellGroupStackRules(m_spellInfo, existingSpellInfo, remove, IsArea()); - if (stackFlags) + switch (sSpellMgr->CheckSpellGroupStackRules(m_spellInfo, existingSpellInfo)) { - // xinef: same caster rule is bounded by spellfamily - if (sameCaster && m_spellInfo->SpellFamilyName == existingSpellInfo->SpellFamilyName && - (stackFlags & SPELL_GROUP_STACK_FLAG_NOT_SAME_CASTER)) + case SPELL_GROUP_STACK_RULE_EXCLUSIVE: + case SPELL_GROUP_STACK_RULE_EXCLUSIVE_HIGHEST: // if it reaches this point, existing aura is lower/equal return false; - - // xinef: normal exclusive stacking, remove if auras are equal by effects - if (stackFlags & SPELL_GROUP_STACK_FLAG_EXCLUSIVE) - { - if (GetSpellInfo()->IsAuraEffectEqual(existingSpellInfo) || GetSpellInfo()->IsRankOf(existingSpellInfo)) - { - if (remove) - return IsAuraStronger(existingAura); - else - return existingAura->IsAuraStronger(this); - } - } - - // xinef: check priority before effect mask - SpellGroupSpecialFlags thisAuraFlag = sSpellMgr->GetSpellGroupSpecialFlags(GetId()); - SpellGroupSpecialFlags existingAuraFlag = sSpellMgr->GetSpellGroupSpecialFlags(existingSpellInfo->Id); - if (thisAuraFlag >= SPELL_GROUP_SPECIAL_FLAG_PRIORITY1 && thisAuraFlag <= SPELL_GROUP_SPECIAL_FLAG_PRIORITY4 && - existingAuraFlag >= SPELL_GROUP_SPECIAL_FLAG_PRIORITY1 && existingAuraFlag <= SPELL_GROUP_SPECIAL_FLAG_PRIORITY4) - { - if (thisAuraFlag < existingAuraFlag) - { + case SPELL_GROUP_STACK_RULE_EXCLUSIVE_FROM_SAME_CASTER: + if (sameCaster) return false; - } - } - - // xinef: forced strongest aura in group by flag - if (stackFlags & SPELL_GROUP_STACK_FLAG_FORCED_STRONGEST) - return !remove; - if (stackFlags & SPELL_GROUP_STACK_FLAG_FORCED_WEAKEST) - return remove; - - // xinef: forced return, handle all cases using available flags! - return !(stackFlags & SPELL_GROUP_STACK_FLAG_NEVER_STACK); + break; + case SPELL_GROUP_STACK_RULE_DEFAULT: + case SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT: + default: + break; } if (m_spellInfo->SpellFamilyName != existingSpellInfo->SpellFamilyName) diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 7b7ab603ec..8731299f8b 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -153,6 +153,7 @@ public: uint8 GetCasterLevel() const { return m_casterLevel; } + bool HasMoreThanOneEffectForType(AuraType auraType) const; bool IsArea() const; bool IsPassive() const; bool IsDeathPersistent() const; @@ -188,7 +189,7 @@ public: void HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, bool apply, bool onReapply); bool CanBeAppliedOn(Unit* target); bool CheckAreaTarget(Unit* target); - bool CanStackWith(Aura const* checkAura, bool remove) const; + bool CanStackWith(Aura const* existingAura) const; bool IsAuraStronger(Aura const* newAura) const; // Proc system diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 24f1c1ad68..608b9614b8 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -19,7 +19,6 @@ #include "ArenaSpectator.h" #include "BattlefieldMgr.h" #include "Battleground.h" -#include "BattlegroundIC.h" #include "CharmInfo.h" #include "CellImpl.h" #include "Common.h" @@ -33,7 +32,6 @@ #include "InstanceScript.h" #include "Log.h" #include "LootMgr.h" -#include "MapMgr.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Opcodes.h" @@ -41,6 +39,7 @@ #include "Player.h" #include "ScriptMgr.h" #include "SharedDefines.h" +#include "SpellAuraDefines.h" #include "SpellAuraEffects.h" #include "SpellInfo.h" #include "SpellMgr.h" @@ -1240,11 +1239,7 @@ void Spell::SelectImplicitConeTargets(SpellEffIndex effIndex, SpellImplicitTarge // Other special target selection goes here if (uint32 maxTargets = m_spellValue->MaxAffectedTargets) { - Unit::AuraEffectList const& Auras = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS); - for (Unit::AuraEffectList::const_iterator j = Auras.begin(); j != Auras.end(); ++j) - if ((*j)->IsAffectedOnSpell(m_spellInfo)) - maxTargets += (*j)->GetAmount(); - + maxTargets += m_caster->GetTotalAuraModifierByAffectMask(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS, m_spellInfo); Acore::Containers::RandomResize(targets, maxTargets); } @@ -1327,11 +1322,7 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge // Other special target selection goes here if (uint32 maxTargets = m_spellValue->MaxAffectedTargets) { - Unit::AuraEffectList const& Auras = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS); - for (Unit::AuraEffectList::const_iterator j = Auras.begin(); j != Auras.end(); ++j) - if ((*j)->IsAffectedOnSpell(m_spellInfo)) - maxTargets += (*j)->GetAmount(); - + maxTargets += m_caster->GetTotalAuraModifierByAffectMask(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS, m_spellInfo); Acore::Containers::RandomResize(targets, maxTargets); } @@ -6076,6 +6067,8 @@ SpellCastResult Spell::CheckCast(bool strict) } } + uint8 approximateAuraEffectMask = 0; + uint8 nonAuraEffectMask = 0; for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { // for effects of spells that have only one target @@ -6561,6 +6554,11 @@ SpellCastResult Spell::CheckCast(bool strict) default: break; } + + if (m_spellInfo->Effects[i].IsAura()) + approximateAuraEffectMask |= 1 << i; + else if (m_spellInfo->Effects[i].IsEffect()) + nonAuraEffectMask |= 1 << i; } for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) @@ -6609,8 +6607,13 @@ SpellCastResult Spell::CheckCast(bool strict) if (target->IsCreature() && target->ToCreature()->IsVehicle()) return SPELL_FAILED_BAD_IMPLICIT_TARGETS; + // Allow SPELL_AURA_MOD_POSSESS to work on mounted players, + // but keep the old restriction for everything else. if (target->IsMounted()) - return SPELL_FAILED_CANT_BE_CHARMED; + { + if (!(target->IsPlayer() && m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_POSSESS)) + return SPELL_FAILED_CANT_BE_CHARMED; + } if (target->GetCharmerGUID()) return SPELL_FAILED_CHARMED; @@ -6718,6 +6721,13 @@ SpellCastResult Spell::CheckCast(bool strict) default: break; } + + // check if target already has the same type, but more powerful aura + if (!nonAuraEffectMask && (approximateAuraEffectMask & (1 << i)) && !m_spellInfo->IsTargetingArea()) + if (Unit* target = m_targets.GetUnitTarget()) + if (!target->IsHighestExclusiveAuraEffect(m_spellInfo, AuraType(m_spellInfo->Effects[i].ApplyAuraName), + m_spellInfo->Effects[i].CalcValue(m_caster, &m_spellValue->EffectBasePoints[i]), approximateAuraEffectMask, false)) + return SPELL_FAILED_AURA_BOUNCED; } // check trade slot case (last, for allow catch any another cast problems) @@ -6968,27 +6978,36 @@ bool Spell::CanAutoCast(Unit* target) { ObjectGuid targetguid = target->GetGUID(); - for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j) + for (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects()) { - if (m_spellInfo->Effects[j].Effect == SPELL_EFFECT_APPLY_AURA) + if (!spellEffectInfo.IsAura()) + continue; + + AuraType const& auraType = spellEffectInfo.ApplyAuraName; + Unit::AuraEffectList const& auras = target->GetAuraEffectsByType(auraType); + for (Unit::AuraEffectList::const_iterator auraIt = auras.begin(); auraIt != auras.end(); ++auraIt) { - if (m_spellInfo->StackAmount <= 1) + if (GetSpellInfo()->Id == (*auraIt)->GetSpellInfo()->Id) + return false; + + switch (sSpellMgr->CheckSpellGroupStackRules(GetSpellInfo(), (*auraIt)->GetSpellInfo())) { - if (target->HasAuraEffect(m_spellInfo->Id, j)) + case SPELL_GROUP_STACK_RULE_EXCLUSIVE: return false; - } - else - { - if (AuraEffect* aureff = target->GetAuraEffect(m_spellInfo->Id, j)) - if (aureff->GetBase()->GetStackAmount() >= m_spellInfo->StackAmount) + case SPELL_GROUP_STACK_RULE_EXCLUSIVE_FROM_SAME_CASTER: + if (GetCaster() == (*auraIt)->GetCaster()) + return false; + break; + case SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT: // this one has further checks, but i don't think they're necessary for autocast logic + case SPELL_GROUP_STACK_RULE_EXCLUSIVE_HIGHEST: + if (abs(spellEffectInfo.BasePoints) <= abs((*auraIt)->GetAmount())) return false; + break; + case SPELL_GROUP_STACK_RULE_DEFAULT: + default: + break; } } - else if (m_spellInfo->Effects[j].IsAreaAuraEffect()) - { - if (target->HasAuraEffect(m_spellInfo->Id, j)) - return false; - } } SpellCastResult result = CheckPetCast(target); @@ -7905,7 +7924,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const case SPELL_AURA_AOE_CHARM: if (target->IsCreature() && target->IsVehicle()) return false; - if (target->IsMounted()) + if (target->IsMounted() && m_spellInfo->Effects[eff].ApplyAuraName != SPELL_AURA_MOD_POSSESS) return false; if (target->GetCharmerGUID()) return false; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 3343c70595..d725b682ec 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1952,13 +1952,10 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) Unit::AuraApplicationMap& Auras = unitTarget->GetAppliedAuras(); for (Unit::AuraApplicationMap::iterator itr = Auras.begin(); itr != Auras.end(); ++itr) { - SpellGroupSpecialFlags sFlag = sSpellMgr->GetSpellGroupSpecialFlags(itr->second->GetBase()->GetId()); - if (!guardianFound) - if (sFlag & SPELL_GROUP_SPECIAL_FLAG_ELIXIR_GUARDIAN) - guardianFound = true; - if (!battleFound) - if (sFlag & SPELL_GROUP_SPECIAL_FLAG_ELIXIR_BATTLE) - battleFound = true; + if (!guardianFound && sSpellMgr->IsSpellMemberOfSpellGroup(itr->second->GetBase()->GetId(), SPELL_GROUP_ELIXIR_GUARDIAN)) + guardianFound = true; + if (!battleFound && sSpellMgr->IsSpellMemberOfSpellGroup(itr->second->GetBase()->GetId(), SPELL_GROUP_ELIXIR_BATTLE)) + battleFound = true; if (battleFound && guardianFound) break; } @@ -1966,9 +1963,9 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) // get all available elixirs by mask and spell level std::set<uint32> availableElixirs; if (!guardianFound) - sSpellMgr->GetSetOfSpellsInSpellGroupWithFlag(1, SPELL_GROUP_SPECIAL_FLAG_ELIXIR_GUARDIAN, availableElixirs); + sSpellMgr->GetSetOfSpellsInSpellGroup(SPELL_GROUP_ELIXIR_GUARDIAN, availableElixirs); if (!battleFound) - sSpellMgr->GetSetOfSpellsInSpellGroupWithFlag(1, SPELL_GROUP_SPECIAL_FLAG_ELIXIR_BATTLE, availableElixirs); + sSpellMgr->GetSetOfSpellsInSpellGroup(SPELL_GROUP_ELIXIR_BATTLE, availableElixirs); for (std::set<uint32>::iterator itr = availableElixirs.begin(); itr != availableElixirs.end();) { SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(*itr); @@ -3598,7 +3595,7 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) unitMod = UNIT_MOD_DAMAGE_RANGED; break; } - float weapon_total_pct = m_caster->GetModifierValue(unitMod, TOTAL_PCT); + float weapon_total_pct = m_caster->GetPctModifierValue(unitMod, TOTAL_PCT); fixed_bonus = int32(fixed_bonus * weapon_total_pct); spell_bonus = int32(spell_bonus * weapon_total_pct); } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 2a54900431..74b89e99bd 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -326,9 +326,9 @@ std::array<SpellImplicitTargetInfo::StaticData, TOTAL_SPELL_TARGETS> SpellImplic SpellEffectInfo::SpellEffectInfo(SpellEntry const* spellEntry, SpellInfo const* spellInfo, uint8 effIndex) { _spellInfo = spellInfo; - _effIndex = effIndex; + EffectIndex = effIndex; Effect = spellEntry->Effect[effIndex]; - ApplyAuraName = spellEntry->EffectApplyAuraName[effIndex]; + ApplyAuraName = AuraType(spellEntry->EffectApplyAuraName[effIndex]); Amplitude = spellEntry->EffectAmplitude[effIndex]; DieSides = spellEntry->EffectDieSides[effIndex]; RealPointsPerLevel = spellEntry->EffectRealPointsPerLevel[effIndex]; @@ -456,7 +456,7 @@ int32 SpellEffectInfo::CalcValue(Unit const* caster, int32 const* bp, Unit const value += PointsPerComboPoint * comboPoints; } - value = caster->ApplyEffectModifiers(_spellInfo, _effIndex, value); + value = caster->ApplyEffectModifiers(_spellInfo, EffectIndex, value); // amount multiplication based on caster's level if (!caster->IsControlledByPlayer() && @@ -501,7 +501,7 @@ int32 SpellEffectInfo::CalcValue(Unit const* caster, int32 const* bp, Unit const break; } - if ((sSpellMgr->GetSpellInfo(_spellInfo->Effects[_effIndex].TriggerSpell) && sSpellMgr->GetSpellInfo(_spellInfo->Effects[_effIndex].TriggerSpell)->HasAttribute(SPELL_ATTR0_SCALES_WITH_CREATURE_LEVEL)) && _spellInfo->HasAttribute(SPELL_ATTR0_SCALES_WITH_CREATURE_LEVEL)) + if ((sSpellMgr->GetSpellInfo(_spellInfo->Effects[EffectIndex].TriggerSpell) && sSpellMgr->GetSpellInfo(_spellInfo->Effects[EffectIndex].TriggerSpell)->HasAttribute(SPELL_ATTR0_SCALES_WITH_CREATURE_LEVEL)) && _spellInfo->HasAttribute(SPELL_ATTR0_SCALES_WITH_CREATURE_LEVEL)) canEffectScale = false; if (canEffectScale) @@ -1579,122 +1579,6 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a return SPELL_CAST_OK; } - -bool SpellInfo::IsStrongerAuraActive(Unit const* caster, Unit const* target) const -{ - if (!target) - return false; - - // xinef: check spell group - uint32 groupId = sSpellMgr->GetSpellGroup(Id); - if (!groupId) - return false; - - SpellGroupSpecialFlags sFlag = sSpellMgr->GetSpellGroupSpecialFlags(Id); - if (sFlag & SPELL_GROUP_SPECIAL_FLAG_SKIP_STRONGER_CHECK) - return false; - - for (uint8 i = EFFECT_0; i < MAX_SPELL_EFFECTS; ++i) - { - // xinef: Skip Empty effects - if (!Effects[i].IsEffect()) - continue; - - // xinef: if non-aura effect is preset - return false - if (!Effects[i].IsAura()) - return false; - - // xinef: aura is periodic - return false - if (Effects[i].Amplitude) - return false; - - // xinef: exclude dummy auras - if (Effects[i].ApplyAuraName == SPELL_AURA_DUMMY) - return false; - } - - for (uint8 i = EFFECT_0; i < MAX_SPELL_EFFECTS; ++i) - { - // xinef: skip non-aura efects - if (!Effects[i].IsAura()) - return false; - - Unit::AuraEffectList const& auraList = target->GetAuraEffectsByType((AuraType)Effects[i].ApplyAuraName); - for (Unit::AuraEffectList::const_iterator iter = auraList.begin(); iter != auraList.end(); ++iter) - { - // xinef: aura is not groupped or in different group - uint32 auraGroup = (*iter)->GetAuraGroup(); - if (!auraGroup || auraGroup != groupId) - continue; - - if (IsRankOf((*iter)->GetSpellInfo()) && (sFlag & SPELL_GROUP_SPECIAL_FLAG_SKIP_STRONGER_SAME_SPELL)) - { - continue; - } - - // xinef: check priority before effect mask - if (sFlag >= SPELL_GROUP_SPECIAL_FLAG_PRIORITY1 && sFlag <= SPELL_GROUP_SPECIAL_FLAG_PRIORITY4) - { - SpellGroupSpecialFlags sFlagCurr = sSpellMgr->GetSpellGroupSpecialFlags((*iter)->GetId()); - if (sFlagCurr >= SPELL_GROUP_SPECIAL_FLAG_PRIORITY1 && sFlagCurr <= SPELL_GROUP_SPECIAL_FLAG_PRIORITY4 && sFlagCurr < sFlag) - { - return true; - } - } - - // xinef: check aura effect equal auras only, some auras have different effects on different ranks - check rank also - if (!IsAuraEffectEqual((*iter)->GetSpellInfo()) && !IsRankOf((*iter)->GetSpellInfo())) - continue; - - // xinef: misc value mismatches - // xinef: commented, checked above - //if (Effects[i].MiscValue != (*iter)->GetMiscValue()) - // continue; - - // xinef: should not happen, or effect is not active - stronger one is present - AuraApplication* aurApp = (*iter)->GetBase()->GetApplicationOfTarget(target->GetGUID()); - if (!aurApp || !aurApp->IsActive((*iter)->GetEffIndex())) - continue; - - // xinef: assume that all spells are either positive or negative, otherwise they should not be in one group - // xinef: take custom values into account - - int32 basePoints = Effects[i].BasePoints; - int32 duration = GetMaxDuration(); - - // xinef: should have the same id, can be different if spell is triggered - // xinef: have to fix spell mods for triggered spell, turn off current spellmodtakingspell for preparing and restore after - if (Player const* player = caster->GetSpellModOwner()) - if (player->m_spellModTakingSpell && player->m_spellModTakingSpell->m_spellInfo->Id == Id) - basePoints = player->m_spellModTakingSpell->GetSpellValue()->EffectBasePoints[i]; - - int32 curValue = std::abs(Effects[i].CalcValue(caster, &basePoints)); - int32 auraValue = (sFlag & SPELL_GROUP_SPECIAL_FLAG_BASE_AMOUNT_CHECK) ? - std::abs((*iter)->GetSpellInfo()->Effects[(*iter)->GetEffIndex()].CalcValue((*iter)->GetCaster())) : - std::abs((*iter)->GetAmount()); - - // xinef: for same spells, divide amount by stack amount - if (Id == (*iter)->GetId()) - auraValue /= (*iter)->GetBase()->GetStackAmount(); - - if (curValue < auraValue) - return true; - - // xinef: little hack, if current spell is the same as aura spell, asume it is not stronger - // xinef: if values are the same, duration mods should be taken into account but they are almost always passive - if (curValue == auraValue) - { - if (Id == (*iter)->GetId()) - continue; - if (!(*iter)->GetBase()->IsPassive() && duration < (*iter)->GetBase()->GetDuration()) - return true; - } - } - } - - return false; -} - bool SpellInfo::IsAuraEffectEqual(SpellInfo const* otherSpellInfo) const { uint8 matchCount = 0; @@ -1938,10 +1822,6 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta if (HasEffect(SPELL_EFFECT_SELF_RESURRECT) || HasEffect(SPELL_EFFECT_RESURRECT) || HasEffect(SPELL_EFFECT_RESURRECT_NEW)) return SPELL_FAILED_TARGET_CANNOT_BE_RESURRECTED; - // xinef: check if stronger aura is active - if (IsStrongerAuraActive(caster, unitTarget)) - return SPELL_FAILED_AURA_BOUNCED; - return SPELL_CAST_OK; } @@ -2316,6 +2196,8 @@ SpellSpecificType SpellInfo::LoadSpellSpecific() const case SPELL_AURA_TRACK_RESOURCES: case SPELL_AURA_TRACK_STEALTHED: return SPELL_SPECIFIC_TRACKER; + default: + break; } } } @@ -2399,6 +2281,8 @@ uint32 SpellInfo::GetMaxTicks() const if (Effects[x].Amplitude != 0) return DotDuration / Effects[x].Amplitude; break; + default: + break; } } @@ -2889,50 +2773,3 @@ void SpellInfo::_UnloadImplicitTargetConditionLists() delete cur; } } - -bool SpellInfo::CheckElixirStacking(Unit const* caster) const -{ - if (!caster) - { - return true; - } - - // xinef: check spell group - uint32 groupId = sSpellMgr->GetSpellGroup(Id); - if (groupId != SPELL_GROUP_GUARDIAN_AND_BATTLE_ELIXIRS) - { - return true; - } - - SpellGroupSpecialFlags sFlag = sSpellMgr->GetSpellGroupSpecialFlags(Id); - for (uint8 i = EFFECT_0; i < MAX_SPELL_EFFECTS; ++i) - { - if (!Effects[i].IsAura()) - { - continue; - } - - Unit::AuraApplicationMap const& Auras = caster->GetAppliedAuras(); - for (Unit::AuraApplicationMap::const_iterator itr = Auras.begin(); itr != Auras.end(); ++itr) - { - // xinef: aura is not groupped or in different group - uint32 auraGroup = sSpellMgr->GetSpellGroup(itr->first); - if (auraGroup != groupId) - { - continue; - } - - // Cannot apply guardian/battle elixir if flask is present - if (sFlag == SPELL_GROUP_SPECIAL_FLAG_ELIXIR_BATTLE || sFlag == SPELL_GROUP_SPECIAL_FLAG_ELIXIR_GUARDIAN) - { - SpellGroupSpecialFlags sAuraFlag = sSpellMgr->GetSpellGroupSpecialFlags(itr->first); - if ((sAuraFlag & SPELL_GROUP_SPECIAL_FLAG_FLASK) == SPELL_GROUP_SPECIAL_FLAG_FLASK) - { - return false; - } - } - } - } - - return true; -} diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index eba1ad9013..50b6d88d40 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -248,10 +248,10 @@ private: class SpellEffectInfo { SpellInfo const* _spellInfo; - uint8 _effIndex; public: + uint8 EffectIndex; uint32 Effect; - uint32 ApplyAuraName; + AuraType ApplyAuraName; uint32 Amplitude; int32 DieSides; float RealPointsPerLevel; @@ -272,7 +272,7 @@ public: flag96 SpellClassMask; std::list<Condition*>* ImplicitTargetConditions; - SpellEffectInfo() : _spellInfo(nullptr), _effIndex(0), Effect(0), ApplyAuraName(0), Amplitude(0), DieSides(0), + SpellEffectInfo() : _spellInfo(nullptr), EffectIndex(0), Effect(0), ApplyAuraName(SPELL_AURA_NONE), Amplitude(0), DieSides(0), RealPointsPerLevel(0), BasePoints(0), PointsPerComboPoint(0), ValueMultiplier(0), DamageMultiplier(0), BonusMultiplier(0), MiscValue(0), MiscValueB(0), Mechanic(MECHANIC_NONE), RadiusEntry(nullptr), ChainTarget(0), ItemType(0), TriggerSpell(0), ImplicitTargetConditions(nullptr) {} @@ -482,8 +482,6 @@ public: SpellCastResult CheckExplicitTarget(Unit const* caster, WorldObject const* target, Item const* itemTarget = nullptr) const; bool CheckTargetCreatureType(Unit const* target) const; - // xinef: aura stacking - bool IsStrongerAuraActive(Unit const* caster, Unit const* target) const; bool IsAuraEffectEqual(SpellInfo const* otherSpellInfo) const; bool ValidateAttribute6SpellDamageMods(Unit const* caster, const AuraEffect* auraEffect, bool isDot) const; @@ -539,8 +537,6 @@ public: // unloading helpers void _UnloadImplicitTargetConditionLists(); - bool CheckElixirStacking(Unit const* caster) const; - private: std::array<SpellEffectInfo, MAX_SPELL_EFFECTS>& _GetEffects() { return Effects; } SpellEffectInfo& _GetEffect(SpellEffIndex index) { ASSERT(index < Effects.size()); return Effects[index]; } diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index d3322349b4..0615f39c00 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -570,14 +570,6 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->AttributesEx3 |= SPELL_ATTR3_SUPPRESS_CASTER_PROCS; }); - // Blessing of sanctuary stats - ApplySpellFix({ 67480 }, [](SpellInfo* spellInfo) - { - spellInfo->Effects[EFFECT_0].MiscValue = -1; - spellInfo->SpellFamilyName = SPELLFAMILY_UNK1; // allows stacking - spellInfo->Effects[EFFECT_1].ApplyAuraName = SPELL_AURA_DUMMY; // just a marker - }); - ApplySpellFix({ 6940, // Hand of Sacrifice 64205 // Divine Sacrifice @@ -5170,6 +5162,14 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->ProcCharges = 1; }); + ApplySpellFix({ + 56917, // To Icecrown Airship - Teleport to Airship (A) + 57417, // To Icecrown Airship - Teleport to Airship (H) + }, [](SpellInfo* spellInfo) + { + spellInfo->RangeEntry = sSpellRangeStore.LookupEntry(6); // 100 yards + }); + for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i) { SpellInfo* spellInfo = mSpellInfoMap[i]; @@ -5291,14 +5291,6 @@ void SpellMgr::LoadSpellInfoCorrections() factionTemplateEntry = const_cast<FactionTemplateEntry*>(sFactionTemplateStore.LookupEntry(1921)); // The Taunka factionTemplateEntry->hostileMask |= 8; - // Remove 1 from guards friendly mask, making able to attack players - factionTemplateEntry = const_cast<FactionTemplateEntry*>(sFactionTemplateStore.LookupEntry(1857)); // Area 52 Bruiser - factionTemplateEntry->friendlyMask &= ~1; - factionTemplateEntry = const_cast<FactionTemplateEntry*>(sFactionTemplateStore.LookupEntry(1806)); // Netherstorm Agent - factionTemplateEntry->friendlyMask &= ~1; - factionTemplateEntry = const_cast<FactionTemplateEntry*>(sFactionTemplateStore.LookupEntry(1812)); // K3 Bruiser - factionTemplateEntry->friendlyMask &= ~1; - // Remove vehicles attr, making accessories selectable VehicleSeatEntry* vse = const_cast<VehicleSeatEntry*>(sVehicleSeatStore.LookupEntry(4689)); // Siege Engine, Accessory vse->m_flags &= ~VEHICLE_SEAT_FLAG_PASSENGER_NOT_SELECTABLE; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index f424be399a..ba297d4588 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -18,12 +18,9 @@ #include "SpellMgr.h" #include "BattlefieldMgr.h" #include "BattlegroundIC.h" -#include "BattlegroundMgr.h" #include "Chat.h" #include "DBCStores.h" -#include "GameGraveyard.h" #include "InstanceScript.h" -#include "MapMgr.h" #include "ObjectMgr.h" #include "Player.h" #include "ScriptMgr.h" @@ -648,82 +645,143 @@ SpellTargetPosition const* SpellMgr::GetSpellTargetPosition(uint32 spell_id, Spe return nullptr; } -SpellGroupStackFlags SpellMgr::GetGroupStackFlags(uint32 groupid) const +SpellSpellGroupMapBounds SpellMgr::GetSpellSpellGroupMapBounds(uint32 spell_id) const { - SpellGroupStackMap::const_iterator itr = mSpellGroupStackMap.find(groupid); - if (itr != mSpellGroupStackMap.end()) - return itr->second; - - return SPELL_GROUP_STACK_FLAG_NONE; + spell_id = GetFirstSpellInChain(spell_id); + return mSpellSpellGroup.equal_range(spell_id); } -uint32 SpellMgr::GetSpellGroup(uint32 spell_id) const +bool SpellMgr::IsSpellMemberOfSpellGroup(uint32 spell_id, SpellGroup group_id) const { - uint32 first_rank = GetFirstSpellInChain(spell_id); - SpellGroupMap::const_iterator itr = mSpellGroupMap.find(first_rank); - if (itr != mSpellGroupMap.end()) - return itr->second.groupId; - - return 0; + SpellSpellGroupMapBounds spellGroup = GetSpellSpellGroupMapBounds(spell_id); + for (SpellSpellGroupMap::const_iterator itr = spellGroup.first; itr != spellGroup.second; ++itr) + { + if (itr->second == group_id) + return true; + } + return false; } -SpellGroupSpecialFlags SpellMgr::GetSpellGroupSpecialFlags(uint32 spell_id) const +SpellGroupSpellMapBounds SpellMgr::GetSpellGroupSpellMapBounds(SpellGroup group_id) const { - uint32 first_rank = GetFirstSpellInChain(spell_id); - SpellGroupMap::const_iterator itr = mSpellGroupMap.find(first_rank); - if (itr != mSpellGroupMap.end()) - return itr->second.specialFlags; - - return SPELL_GROUP_SPECIAL_FLAG_NONE; + return mSpellGroupSpell.equal_range(group_id); } -SpellGroupStackFlags SpellMgr::CheckSpellGroupStackRules(SpellInfo const* spellInfo1, SpellInfo const* spellInfo2, bool remove, bool areaAura) const +void SpellMgr::GetSetOfSpellsInSpellGroup(SpellGroup group_id, std::set<uint32>& foundSpells) const { - uint32 spellid_1 = spellInfo1->GetFirstRankSpell()->Id; - uint32 spellid_2 = spellInfo2->GetFirstRankSpell()->Id; - - uint32 groupId = GetSpellGroup(spellid_1); + std::set<SpellGroup> usedGroups; + GetSetOfSpellsInSpellGroup(group_id, foundSpells, usedGroups); +} - SpellGroupSpecialFlags flag1 = GetSpellGroupSpecialFlags(spellid_1); +void SpellMgr::GetSetOfSpellsInSpellGroup(SpellGroup group_id, std::set<uint32>& foundSpells, std::set<SpellGroup>& usedGroups) const +{ + if (usedGroups.find(group_id) != usedGroups.end()) + return; + usedGroups.insert(group_id); - // xinef: dunno why i added this - if (spellid_1 == spellid_2 && remove && !areaAura) + SpellGroupSpellMapBounds groupSpell = GetSpellGroupSpellMapBounds(group_id); + for (SpellGroupSpellMap::const_iterator itr = groupSpell.first; itr != groupSpell.second; ++itr) { - if (flag1 & SPELL_GROUP_SPECIAL_FLAG_SAME_SPELL_CHECK) + if (itr->second < 0) { - return SPELL_GROUP_STACK_FLAG_EXCLUSIVE; + SpellGroup currGroup = (SpellGroup)abs(itr->second); + GetSetOfSpellsInSpellGroup(currGroup, foundSpells, usedGroups); + } + else + { + foundSpells.insert(itr->second); } - - return SPELL_GROUP_STACK_FLAG_NONE; } +} - if (groupId > 0 && groupId == GetSpellGroup(spellid_2)) +bool SpellMgr::AddSameEffectStackRuleSpellGroups(SpellInfo const* spellInfo, uint32 auraType, int32 amount, std::map<SpellGroup, int32>& groups) const +{ + uint32 spellId = spellInfo->GetFirstRankSpell()->Id; + auto spellGroupBounds = GetSpellSpellGroupMapBounds(spellId); + // Find group with SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT if it belongs to one + for (auto itr = spellGroupBounds.first; itr != spellGroupBounds.second; ++itr) { - SpellGroupSpecialFlags flag2 = GetSpellGroupSpecialFlags(spellid_2); - SpellGroupStackFlags additionFlag = SPELL_GROUP_STACK_FLAG_NONE; - // xinef: first flags are used for elixir stacking rules - if (flag1 & SPELL_GROUP_SPECIAL_FLAG_STACK_EXCLUSIVE_MAX && flag2 & SPELL_GROUP_SPECIAL_FLAG_STACK_EXCLUSIVE_MAX) + SpellGroup group = itr->second; + auto found = mSpellSameEffectStack.find(group); + if (found != mSpellSameEffectStack.end()) { - if (flag1 & flag2) - return SPELL_GROUP_STACK_FLAG_NEVER_STACK; + // check auraTypes + if (!found->second.count(auraType)) + continue; + + // Put the highest amount in the map + auto groupItr = groups.find(group); + if (groupItr == groups.end()) + groups.emplace(group, amount); + else + { + int32 curr_amount = groups[group]; + // Take absolute value because this also counts for the highest negative aura + if (std::abs(curr_amount) < std::abs(amount)) + groupItr->second = amount; + } + // return because a spell should be in only one SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT group per auraType + return true; } - // xinef: check only flag1 (new spell) - else if (flag1 & SPELL_GROUP_SPECIAL_FLAG_FORCED_STRONGEST) - additionFlag = SPELL_GROUP_STACK_FLAG_FORCED_STRONGEST; - else if (flag2 & SPELL_GROUP_SPECIAL_FLAG_FORCED_STRONGEST) - additionFlag = SPELL_GROUP_STACK_FLAG_FORCED_WEAKEST; + } + // Not in a SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT group, so return false + return false; +} + +SpellGroupStackRule SpellMgr::CheckSpellGroupStackRules(SpellInfo const* spellInfo1, SpellInfo const* spellInfo2) const +{ + ASSERT(spellInfo1); + ASSERT(spellInfo2); - return SpellGroupStackFlags(GetGroupStackFlags(groupId) | additionFlag); + uint32 spell_id1 = spellInfo1->GetFirstRankSpell()->Id; + uint32 spell_id2 = spellInfo2->GetFirstRankSpell()->Id; + + // find SpellGroups which are common for both spells + SpellSpellGroupMapBounds spellGroup1 = GetSpellSpellGroupMapBounds(spell_id1); + std::set<SpellGroup> groups; + for (SpellSpellGroupMap::const_iterator itr = spellGroup1.first; itr != spellGroup1.second; ++itr) + { + if (IsSpellMemberOfSpellGroup(spell_id2, itr->second)) + { + bool add = true; + SpellGroupSpellMapBounds groupSpell = GetSpellGroupSpellMapBounds(itr->second); + for (SpellGroupSpellMap::const_iterator itr2 = groupSpell.first; itr2 != groupSpell.second; ++itr2) + { + if (itr2->second < 0) + { + SpellGroup currGroup = (SpellGroup)abs(itr2->second); + if (IsSpellMemberOfSpellGroup(spell_id1, currGroup) && IsSpellMemberOfSpellGroup(spell_id2, currGroup)) + { + add = false; + break; + } + } + } + if (add) + groups.insert(itr->second); + } } - return SPELL_GROUP_STACK_FLAG_NONE; + SpellGroupStackRule rule = SPELL_GROUP_STACK_RULE_DEFAULT; + + for (std::set<SpellGroup>::iterator itr = groups.begin(); itr!= groups.end(); ++itr) + { + SpellGroupStackMap::const_iterator found = mSpellGroupStack.find(*itr); + if (found != mSpellGroupStack.end()) + rule = found->second; + if (rule) + break; + } + return rule; } -void SpellMgr::GetSetOfSpellsInSpellGroupWithFlag(uint32 group_id, SpellGroupSpecialFlags flag, std::set<uint32>& availableElixirs) const +SpellGroupStackRule SpellMgr::GetSpellGroupStackRule(SpellGroup group) const { - for (SpellGroupMap::const_iterator itr = mSpellGroupMap.begin(); itr != mSpellGroupMap.end(); ++itr) - if (itr->second.groupId == group_id && itr->second.specialFlags == flag) - availableElixirs.insert(itr->first); // insert spell id + SpellGroupStackMap::const_iterator itr = mSpellGroupStack.find(group); + if (itr != mSpellGroupStack.end()) + return itr->second; + + return SPELL_GROUP_STACK_RULE_DEFAULT; } SpellProcEventEntry const* SpellMgr::GetSpellProcEvent(uint32 spellId) const @@ -1627,10 +1685,11 @@ void SpellMgr::LoadSpellGroups() { uint32 oldMSTime = getMSTime(); - mSpellGroupMap.clear(); // need for reload case + mSpellSpellGroup.clear(); // need for reload case + mSpellGroupSpell.clear(); - // 0 1 2 - QueryResult result = WorldDatabase.Query("SELECT id, spell_id, special_flag FROM spell_group"); + // 0 1 + QueryResult result = WorldDatabase.Query("SELECT id, spell_id FROM spell_group"); if (!result) { LOG_WARN("server.loading", ">> Loaded 0 spell group definitions. DB table `spell_group` is empty."); @@ -1638,48 +1697,68 @@ void SpellMgr::LoadSpellGroups() return; } + std::set<uint32> groups; uint32 count = 0; do { Field* fields = result->Fetch(); uint32 group_id = fields[0].Get<uint32>(); - int32 spell_id = fields[1].Get<uint32>(); - SpellGroupSpecialFlags specialFlag = (SpellGroupSpecialFlags)fields[2].Get<uint32>(); - SpellInfo const* spellInfo = GetSpellInfo(spell_id); - - if (!spellInfo) + if (group_id <= SPELL_GROUP_DB_RANGE_MIN && group_id >= SPELL_GROUP_CORE_RANGE_MAX) { - LOG_ERROR("sql.sql", "Spell {} listed in `spell_group` does not exist", spell_id); - continue; - } - else if (spellInfo->GetRank() > 1) - { - LOG_ERROR("sql.sql", "Spell {} listed in `spell_group` is not first rank of spell", spell_id); + LOG_ERROR("sql.sql", "SpellGroup id {} listed in `spell_group` is in core range, but is not defined in core!", group_id); continue; } + int32 spell_id = fields[1].Get<int32>(); + + groups.insert(group_id); + mSpellGroupSpell.emplace(SpellGroup(group_id), spell_id); + + } while (result->NextRow()); - if (mSpellGroupMap.find(spell_id) != mSpellGroupMap.end()) + for (auto itr = mSpellGroupSpell.begin(); itr!= mSpellGroupSpell.end();) + { + if (itr->second < 0) { - LOG_ERROR("sql.sql", "Spell {} listed in `spell_group` has more than one group", spell_id); - continue; + if (groups.find(abs(itr->second)) == groups.end()) + { + LOG_ERROR("sql.sql", "SpellGroup id {} listed in `spell_group` does not exist", abs(itr->second)); + itr = mSpellGroupSpell.erase(itr); + } + else + ++itr; } - - if (specialFlag >= SPELL_GROUP_SPECIAL_FLAG_MAX) + else { - LOG_ERROR("sql.sql", "Spell {} listed in `spell_group` has invalid special flag!", spell_id); - continue; + SpellInfo const* spellInfo = GetSpellInfo(itr->second); + if (!spellInfo) + { + LOG_ERROR("sql.sql", "Spell {} listed in `spell_group` does not exist", itr->second); + itr = mSpellGroupSpell.erase(itr); + } + else if (spellInfo->GetRank() > 1) + { + LOG_ERROR("sql.sql", "Spell {} listed in `spell_group` is not first rank of spell.", itr->second); + itr = mSpellGroupSpell.erase(itr); + } + else + ++itr; } + } - SpellStackInfo ssi; - ssi.groupId = group_id; - ssi.specialFlags = specialFlag; - mSpellGroupMap[spell_id] = ssi; + for (auto groupItr = groups.begin(); groupItr != groups.end(); ++groupItr) + { + std::set<uint32> spells; + GetSetOfSpellsInSpellGroup(SpellGroup(*groupItr), spells); - ++count; - } while (result->NextRow()); + for (auto spellItr = spells.begin(); spellItr != spells.end(); ++spellItr) + { + ++count; + mSpellSpellGroup.emplace(*spellItr, SpellGroup(*groupItr)); + } + } - LOG_INFO("server.loading", ">> Loaded {} Spell Group Definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); + LOG_INFO("server.loading", ">> Loaded {} spell group Definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", " "); } @@ -1687,7 +1766,10 @@ void SpellMgr::LoadSpellGroupStackRules() { uint32 oldMSTime = getMSTime(); - mSpellGroupStackMap.clear(); // need for reload case + mSpellGroupStack.clear(); // need for reload case + mSpellSameEffectStack.clear(); + + std::vector<uint32> sameEffectGroups; // 0 1 QueryResult result = WorldDatabase.Query("SELECT group_id, stack_rule FROM spell_group_stack_rules"); @@ -1705,32 +1787,132 @@ void SpellMgr::LoadSpellGroupStackRules() uint32 group_id = fields[0].Get<uint32>(); uint8 stack_rule = fields[1].Get<int8>(); - if (stack_rule >= SPELL_GROUP_STACK_FLAG_MAX) + if (stack_rule >= SPELL_GROUP_STACK_RULE_MAX) { - LOG_ERROR("sql.sql", "SpellGroupStackRule {} listed in `spell_group_stack_rules` does not exist", stack_rule); + LOG_ERROR("sql.sql", "SpellGroupStackRule {} listed in `spell_group_stack_rules` does not exist.", stack_rule); continue; } - bool present = false; - for (SpellGroupMap::const_iterator itr = mSpellGroupMap.begin(); itr != mSpellGroupMap.end(); ++itr) - if (itr->second.groupId == group_id) - { - present = true; - break; - } - - if (!present) + auto bounds = GetSpellGroupSpellMapBounds((SpellGroup)group_id); + if (bounds.first == bounds.second) { - LOG_ERROR("sql.sql", "SpellGroup id {} listed in `spell_group_stack_rules` does not exist", group_id); + LOG_ERROR("sql.sql", "SpellGroup id {} listed in `spell_group_stack_rules` does not exist.", group_id); continue; } - mSpellGroupStackMap[group_id] = (SpellGroupStackFlags)stack_rule; + mSpellGroupStack.emplace(SpellGroup(group_id), SpellGroupStackRule(stack_rule)); + + // different container for same effect stack rules, need to check effect types + if (stack_rule == SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT) + sameEffectGroups.push_back(group_id); ++count; } while (result->NextRow()); - LOG_INFO("server.loading", ">> Loaded {} Spell Group Stack Rules in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); + LOG_INFO("server.loading", ">> Loaded {} spell group stack rules in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); + LOG_INFO("server.loading", " "); + + count = 0; + oldMSTime = getMSTime(); + + for (uint32 group_id : sameEffectGroups) + { + std::set<uint32> spellIds; + GetSetOfSpellsInSpellGroup(SpellGroup(group_id), spellIds); + + std::unordered_set<uint32> auraTypes; + + // we have to 'guess' what effect this group corresponds to + { + std::unordered_multiset<uint32 /*auraName*/> frequencyContainer; + + // only waylay for the moment (shared group) + std::vector<std::vector<uint32 /*auraName*/>> const SubGroups = + { + { SPELL_AURA_MOD_MELEE_HASTE, SPELL_AURA_MOD_MELEE_RANGED_HASTE, SPELL_AURA_MOD_RANGED_HASTE } + }; + + for (uint32 spellId : spellIds) + { + SpellInfo const* spellInfo = AssertSpellInfo(spellId); + for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects()) + { + if (!spellEffectInfo.IsAura()) + continue; + + uint32 auraName = spellEffectInfo.ApplyAuraName; + for (std::vector<uint32> const& subGroup : SubGroups) + { + if (std::find(subGroup.begin(), subGroup.end(), auraName) != subGroup.end()) + { + // count as first aura + auraName = subGroup.front(); + break; + } + } + + frequencyContainer.insert(auraName); + } + } + + uint32 auraType = 0; + size_t auraTypeCount = 0; + for (uint32 auraName : frequencyContainer) + { + size_t currentCount = frequencyContainer.count(auraName); + if (currentCount > auraTypeCount) + { + auraType = auraName; + auraTypeCount = currentCount; + } + } + + for (std::vector<uint32> const& subGroup : SubGroups) + { + if (auraType == subGroup.front()) + { + auraTypes.insert(subGroup.begin(), subGroup.end()); + break; + } + } + + if (auraTypes.empty()) + auraTypes.insert(auraType); + } + + // re-check spells against guessed group + for (uint32 spellId : spellIds) + { + SpellInfo const* spellInfo = AssertSpellInfo(spellId); + + bool found = false; + while (spellInfo) + { + for (uint32 auraType : auraTypes) + { + if (spellInfo->HasAura(AuraType(auraType))) + { + found = true; + break; + } + } + + if (found) + break; + + spellInfo = spellInfo->GetNextRankSpell(); + } + + // not found either, log error + if (!found) + LOG_ERROR("sql.sql", "SpellId {} listed in `spell_group` with stack rule 3 does not share aura assigned for group {}", spellId, group_id); + } + + mSpellSameEffectStack[SpellGroup(group_id)] = auraTypes; + ++count; + } + + LOG_INFO("server.loading", ">> Loaded {} SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT stack rules in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", " "); } @@ -2920,6 +3102,8 @@ void SpellMgr::LoadSpellInfoCustomAttributes() case SPELL_AURA_WATER_BREATHING: spellInfo->AttributesCu |= SPELL_ATTR0_CU_NO_INITIAL_THREAT; break; + default: + break; } switch (spellInfo->Effects[j].ApplyAuraName) @@ -3494,6 +3678,9 @@ void SpellMgr::LoadSpellInfoCustomAttributes() if (triggerSpell->AttributesCu & SPELL_ATTR0_CU_BINARY_SPELL) allNonBinary = false; } + break; + default: + break; } } } diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index feb1e4f7ec..30220e1516 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -20,7 +20,6 @@ // For static or at-server-startup loaded spell data -#include "Common.h" #include "Log.h" #include "SharedDefines.h" #include "Unit.h" @@ -330,56 +329,49 @@ struct SpellBonusEntry typedef std::unordered_map<uint32, SpellBonusEntry> SpellBonusMap; -enum SpellGroupSpecialFlags +enum SpellGroup { - SPELL_GROUP_SPECIAL_FLAG_NONE = 0x000, - SPELL_GROUP_SPECIAL_FLAG_ELIXIR_BATTLE = 0x001, - SPELL_GROUP_SPECIAL_FLAG_ELIXIR_GUARDIAN = 0x002, - SPELL_GROUP_SPECIAL_FLAG_ELIXIR_UNSTABLE = 0x004, - SPELL_GROUP_SPECIAL_FLAG_ELIXIR_SHATTRATH = 0x008, - SPELL_GROUP_SPECIAL_FLAG_STACK_EXCLUSIVE_MAX = 0x00F, - SPELL_GROUP_SPECIAL_FLAG_FORCED_STRONGEST = 0x010, // xinef: specially helpful flag if some spells have different auras, but only one should be present - SPELL_GROUP_SPECIAL_FLAG_SKIP_STRONGER_CHECK = 0x020, - SPELL_GROUP_SPECIAL_FLAG_BASE_AMOUNT_CHECK = 0x040, - SPELL_GROUP_SPECIAL_FLAG_PRIORITY1 = 0x100, - SPELL_GROUP_SPECIAL_FLAG_PRIORITY2 = 0x200, - SPELL_GROUP_SPECIAL_FLAG_PRIORITY3 = 0x400, - SPELL_GROUP_SPECIAL_FLAG_PRIORITY4 = 0x800, - SPELL_GROUP_SPECIAL_FLAG_SAME_SPELL_CHECK = 0x1000, - SPELL_GROUP_SPECIAL_FLAG_SKIP_STRONGER_SAME_SPELL = 0x2000, - SPELL_GROUP_SPECIAL_FLAG_MAX = 0x4000, - - SPELL_GROUP_SPECIAL_FLAG_FLASK = SPELL_GROUP_SPECIAL_FLAG_ELIXIR_BATTLE | SPELL_GROUP_SPECIAL_FLAG_ELIXIR_GUARDIAN + SPELL_GROUP_NONE = 0, + SPELL_GROUP_ELIXIR_BATTLE = 1, + SPELL_GROUP_ELIXIR_GUARDIAN = 2, + SPELL_GROUP_CORE_RANGE_MAX = 3 }; -enum SpellGroupStackFlags +namespace std { - SPELL_GROUP_STACK_FLAG_NONE = 0x00, - SPELL_GROUP_STACK_FLAG_EXCLUSIVE = 0x01, - SPELL_GROUP_STACK_FLAG_NOT_SAME_CASTER = 0x02, - SPELL_GROUP_STACK_FLAG_FLAGGED = 0x04, // xinef: just a marker - SPELL_GROUP_STACK_FLAG_NEVER_STACK = 0x08, - SPELL_GROUP_STACK_FLAG_EFFECT_EXCLUSIVE = 0x10, - SPELL_GROUP_STACK_FLAG_MAX = 0x20, - - // Internal use - SPELL_GROUP_STACK_FLAG_FORCED_STRONGEST = 0x100, - SPELL_GROUP_STACK_FLAG_FORCED_WEAKEST = 0x200, -}; + template<> + struct hash<SpellGroup> + { + size_t operator()(SpellGroup const& group) const + { + return hash<uint32>()(uint32(group)); + } + }; +} -enum SpellGroupIDs -{ - SPELL_GROUP_GUARDIAN_AND_BATTLE_ELIXIRS = 1 -}; +#define SPELL_GROUP_DB_RANGE_MIN 1000 -struct SpellStackInfo +// spell_id, group_id +typedef std::unordered_multimap<uint32, SpellGroup> SpellSpellGroupMap; +typedef std::pair<SpellSpellGroupMap::const_iterator, SpellSpellGroupMap::const_iterator> SpellSpellGroupMapBounds; + +// group_id, spell_id +typedef std::unordered_multimap<SpellGroup, int32> SpellGroupSpellMap; +typedef std::pair<SpellGroupSpellMap::const_iterator, SpellGroupSpellMap::const_iterator> SpellGroupSpellMapBounds; + +enum SpellGroupStackRule { - uint32 groupId; - SpellGroupSpecialFlags specialFlags; + SPELL_GROUP_STACK_RULE_DEFAULT, + SPELL_GROUP_STACK_RULE_EXCLUSIVE, + SPELL_GROUP_STACK_RULE_EXCLUSIVE_FROM_SAME_CASTER, + SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT, + SPELL_GROUP_STACK_RULE_EXCLUSIVE_HIGHEST, + SPELL_GROUP_STACK_RULE_MAX }; -// spell_id, group_id -typedef std::map<uint32, SpellStackInfo> SpellGroupMap; -typedef std::map<uint32, SpellGroupStackFlags> SpellGroupStackMap; + +typedef std::unordered_map<SpellGroup, SpellGroupStackRule> SpellGroupStackMap; + +typedef std::unordered_map<SpellGroup, std::unordered_set<uint32 /*auraName*/>> SameEffectStackMap; struct SpellThreatEntry { @@ -679,12 +671,18 @@ public: // Spell target coordinates [[nodiscard]] SpellTargetPosition const* GetSpellTargetPosition(uint32 spell_id, SpellEffIndex effIndex) const; - // Spell Groups - [[nodiscard]] uint32 GetSpellGroup(uint32 spellid) const; - [[nodiscard]] SpellGroupSpecialFlags GetSpellGroupSpecialFlags(uint32 spell_id) const; - [[nodiscard]] SpellGroupStackFlags GetGroupStackFlags(uint32 groupid) const; - SpellGroupStackFlags CheckSpellGroupStackRules(SpellInfo const* spellInfo1, SpellInfo const* spellInfo2, bool remove, bool areaAura) const; - void GetSetOfSpellsInSpellGroupWithFlag(uint32 group_id, SpellGroupSpecialFlags flag, std::set<uint32>& availableElixirs) const; + // Spell Groups table + SpellSpellGroupMapBounds GetSpellSpellGroupMapBounds(uint32 spell_id) const; + bool IsSpellMemberOfSpellGroup(uint32 spell_id, SpellGroup group_id) const; + + SpellGroupSpellMapBounds GetSpellGroupSpellMapBounds(SpellGroup group_id) const; + void GetSetOfSpellsInSpellGroup(SpellGroup group_id, std::set<uint32>& foundSpells) const; + void GetSetOfSpellsInSpellGroup(SpellGroup group_id, std::set<uint32>& foundSpells, std::set<SpellGroup>& usedGroups) const; + + // Spell Group Stack Rules table + bool AddSameEffectStackRuleSpellGroups(SpellInfo const* spellInfo, uint32 auraType, int32 amount, std::map<SpellGroup, int32>& groups) const; + SpellGroupStackRule CheckSpellGroupStackRules(SpellInfo const* spellInfo1, SpellInfo const* spellInfo2) const; + SpellGroupStackRule GetSpellGroupStackRule(SpellGroup group_id) const; // Spell proc event table [[nodiscard]] SpellProcEventEntry const* GetSpellProcEvent(uint32 spellId) const; @@ -798,8 +796,10 @@ private: SpellRequiredMap mSpellReq; SpellLearnSkillMap mSpellLearnSkills; SpellTargetPositionMap mSpellTargetPositions; - SpellGroupMap mSpellGroupMap; - SpellGroupStackMap mSpellGroupStackMap; + SpellSpellGroupMap mSpellSpellGroup; + SpellGroupSpellMap mSpellGroupSpell; + SpellGroupStackMap mSpellGroupStack; + SameEffectStackMap mSpellSameEffectStack; SpellProcEventMap mSpellProcEventMap; SpellProcMap mSpellProcMap; SpellBonusMap mSpellBonusMap; |
