fix(Core/Spells): Implement TrinityCore spell_group and spell_group_stack_rules (#23346)

Co-authored-by: treeston <treeston.mmoc@gmail.com>
Co-authored-by: Trisjdc <trisjdc@gmail.com>
Co-authored-by: QAston <none@none>
Co-authored-by: ariel- <ariel-@users.noreply.github.com>
Co-authored-by: Shauren <shauren.trinity@gmail.com>
Co-authored-by: Jelle Meeus <sogladev@gmail.com>
This commit is contained in:
Tereneckla
2025-11-28 19:01:25 +00:00
committed by GitHub
parent 55989205e7
commit 2f7f9bd72f
42 changed files with 1976 additions and 1705 deletions

View File

@@ -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();
@@ -3000,14 +2999,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 +3441,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 +4328,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 +4336,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 +4355,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 +4368,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 +4415,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 +4447,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 +4492,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 +4579,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 +4595,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 +4706,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 +4715,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 +4729,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 +4759,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 +4778,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 +4800,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 +4822,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 +4882,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 +4968,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 +4992,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 +5020,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 +5055,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);
}
@@ -5105,7 +5137,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 +5150,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 +5161,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 +5181,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,85 +5228,25 @@ void AuraEffect::HandleModDamageDone(AuraApplication const* aurApp, uint8 mode,
Unit* target = aurApp->GetTarget();
// apply item specific bonuses for already equipped weapon
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);
}
if ((GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) != 0)
target->UpdateAllDamageDoneMods();
// 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
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
}
}
// Skip non magic case for Speedup
if ((GetMiscValue() & SPELL_SCHOOL_MASK_MAGIC) == 0)
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
return;
}
// Magic damage modifiers implemented in Unit::SpellDamageBonus
// Magic damage modifiers implemented in Unit::SpellBaseDamageBonus
// This information for client side use only
if (target->IsPlayer())
{
if (GetAmount() > 0)
{
for (uint32 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++)
{
if ((GetMiscValue() & (1 << i)) != 0)
target->ApplyModInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + i, GetAmount(), apply);
}
}
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);
}
}
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 (Guardian* pet = target->ToPlayer()->GetGuardianPet())
pet->UpdateAttackPowerAndDamage();
}
}
void AuraEffect::HandleModDamagePercentDone(AuraApplication const* aurApp, uint8 mode, bool apply) const
void AuraEffect::HandleModDamagePercentDone(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
{
if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT)))
return;
@@ -5271,39 +5255,32 @@ void AuraEffect::HandleModDamagePercentDone(AuraApplication const* aurApp, uint8
if (!target)
return;
if (!sScriptMgr->CanModAuraEffectModDamagePercentDone(this, target, aurApp, mode, apply))
return;
if ((GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL))
target->UpdateAllDamagePctDoneMods();
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
for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i)
{
if (GetMiscValue() & (1 << i))
{
// 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);
}
}
}
}
void AuraEffect::HandleModOffhandDamagePercent(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();
target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(GetAmount()), apply);
// also handles spell group stacks
target->UpdateDamagePctDoneMods(OFF_ATTACK);
}
void AuraEffect::HandleShieldBlockValue(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -5311,14 +5288,29 @@ void AuraEffect::HandleShieldBlockValue(AuraApplication const* aurApp, uint8 mod
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;
BaseModType modType = FLAT_MOD;
if (GetAuraType() == SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT)
modType = PCT_MOD;
target->HandleBaseModFlatValue(SHIELD_BLOCK_VALUE, float(GetAmount()), apply);
}
if (target->IsPlayer())
target->ToPlayer()->HandleBaseModValue(SHIELD_BLOCK_VALUE, modType, float(GetAmount()), apply);
void AuraEffect::HandleShieldBlockValuePercent(AuraApplication const* aurApp, uint8 mode, bool apply) const
{
if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT)))
return;
Player* target = aurApp->GetTarget()->ToPlayer();
if (!target)
return;
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);
}
}
/********************************/

View File

@@ -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;

View File

@@ -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)

View File

@@ -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

View File

@@ -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)
@@ -6723,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)
@@ -6973,26 +6978,35 @@ 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 (target->HasAuraEffect(m_spellInfo->Id, j))
return false;
}
else
{
if (AuraEffect* aureff = target->GetAuraEffect(m_spellInfo->Id, j))
if (aureff->GetBase()->GetStackAmount() >= m_spellInfo->StackAmount)
return false;
}
}
else if (m_spellInfo->Effects[j].IsAreaAuraEffect())
{
if (target->HasAuraEffect(m_spellInfo->Id, j))
if (GetSpellInfo()->Id == (*auraIt)->GetSpellInfo()->Id)
return false;
switch (sSpellMgr->CheckSpellGroupStackRules(GetSpellInfo(), (*auraIt)->GetSpellInfo()))
{
case SPELL_GROUP_STACK_RULE_EXCLUSIVE:
return false;
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;
}
}
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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]; }

View File

@@ -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

View File

@@ -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())
spell_id = GetFirstSpellInChain(spell_id);
return mSpellSpellGroup.equal_range(spell_id);
}
bool SpellMgr::IsSpellMemberOfSpellGroup(uint32 spell_id, SpellGroup group_id) const
{
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;
}
SpellGroupSpellMapBounds SpellMgr::GetSpellGroupSpellMapBounds(SpellGroup group_id) const
{
return mSpellGroupSpell.equal_range(group_id);
}
void SpellMgr::GetSetOfSpellsInSpellGroup(SpellGroup group_id, std::set<uint32>& foundSpells) const
{
std::set<SpellGroup> usedGroups;
GetSetOfSpellsInSpellGroup(group_id, foundSpells, usedGroups);
}
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);
SpellGroupSpellMapBounds groupSpell = GetSpellGroupSpellMapBounds(group_id);
for (SpellGroupSpellMap::const_iterator itr = groupSpell.first; itr != groupSpell.second; ++itr)
{
if (itr->second < 0)
{
SpellGroup currGroup = (SpellGroup)abs(itr->second);
GetSetOfSpellsInSpellGroup(currGroup, foundSpells, usedGroups);
}
else
{
foundSpells.insert(itr->second);
}
}
}
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)
{
SpellGroup group = itr->second;
auto found = mSpellSameEffectStack.find(group);
if (found != mSpellSameEffectStack.end())
{
// 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;
}
}
// 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);
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);
}
}
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;
}
SpellGroupStackRule SpellMgr::GetSpellGroupStackRule(SpellGroup group) const
{
SpellGroupStackMap::const_iterator itr = mSpellGroupStack.find(group);
if (itr != mSpellGroupStack.end())
return itr->second;
return SPELL_GROUP_STACK_FLAG_NONE;
}
uint32 SpellMgr::GetSpellGroup(uint32 spell_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;
}
SpellGroupSpecialFlags SpellMgr::GetSpellGroupSpecialFlags(uint32 spell_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;
}
SpellGroupStackFlags SpellMgr::CheckSpellGroupStackRules(SpellInfo const* spellInfo1, SpellInfo const* spellInfo2, bool remove, bool areaAura) const
{
uint32 spellid_1 = spellInfo1->GetFirstRankSpell()->Id;
uint32 spellid_2 = spellInfo2->GetFirstRankSpell()->Id;
uint32 groupId = GetSpellGroup(spellid_1);
SpellGroupSpecialFlags flag1 = GetSpellGroupSpecialFlags(spellid_1);
// xinef: dunno why i added this
if (spellid_1 == spellid_2 && remove && !areaAura)
{
if (flag1 & SPELL_GROUP_SPECIAL_FLAG_SAME_SPELL_CHECK)
{
return SPELL_GROUP_STACK_FLAG_EXCLUSIVE;
}
return SPELL_GROUP_STACK_FLAG_NONE;
}
if (groupId > 0 && groupId == GetSpellGroup(spellid_2))
{
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)
{
if (flag1 & flag2)
return SPELL_GROUP_STACK_FLAG_NEVER_STACK;
}
// 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;
return SpellGroupStackFlags(GetGroupStackFlags(groupId) | additionFlag);
}
return SPELL_GROUP_STACK_FLAG_NONE;
}
void SpellMgr::GetSetOfSpellsInSpellGroupWithFlag(uint32 group_id, SpellGroupSpecialFlags flag, std::set<uint32>& availableElixirs) 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
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>();
if (mSpellGroupMap.find(spell_id) != mSpellGroupMap.end())
{
LOG_ERROR("sql.sql", "Spell {} listed in `spell_group` has more than one group", spell_id);
continue;
}
groups.insert(group_id);
mSpellGroupSpell.emplace(SpellGroup(group_id), spell_id);
if (specialFlag >= SPELL_GROUP_SPECIAL_FLAG_MAX)
{
LOG_ERROR("sql.sql", "Spell {} listed in `spell_group` has invalid special flag!", spell_id);
continue;
}
SpellStackInfo ssi;
ssi.groupId = group_id;
ssi.specialFlags = specialFlag;
mSpellGroupMap[spell_id] = ssi;
++count;
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded {} Spell Group Definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
for (auto itr = mSpellGroupSpell.begin(); itr!= mSpellGroupSpell.end();)
{
if (itr->second < 0)
{
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;
}
else
{
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;
}
}
for (auto groupItr = groups.begin(); groupItr != groups.end(); ++groupItr)
{
std::set<uint32> spells;
GetSetOfSpellsInSpellGroup(SpellGroup(*groupItr), spells);
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", " ");
}
@@ -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;
}
}
}

View File

@@ -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,
template<>
struct hash<SpellGroup>
{
size_t operator()(SpellGroup const& group) const
{
return hash<uint32>()(uint32(group));
}
};
}
// Internal use
SPELL_GROUP_STACK_FLAG_FORCED_STRONGEST = 0x100,
SPELL_GROUP_STACK_FLAG_FORCED_WEAKEST = 0x200,
#define SPELL_GROUP_DB_RANGE_MIN 1000
// 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
{
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
};
enum SpellGroupIDs
{
SPELL_GROUP_GUARDIAN_AND_BATTLE_ELIXIRS = 1
};
typedef std::unordered_map<SpellGroup, SpellGroupStackRule> SpellGroupStackMap;
struct SpellStackInfo
{
uint32 groupId;
SpellGroupSpecialFlags specialFlags;
};
// spell_id, group_id
typedef std::map<uint32, SpellStackInfo> SpellGroupMap;
typedef std::map<uint32, SpellGroupStackFlags> 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;