diff options
Diffstat (limited to 'src/server')
58 files changed, 2142 insertions, 1896 deletions
diff --git a/src/server/apps/worldserver/Main.cpp b/src/server/apps/worldserver/Main.cpp index d1c10131b1..511c17e367 100644 --- a/src/server/apps/worldserver/Main.cpp +++ b/src/server/apps/worldserver/Main.cpp @@ -48,6 +48,7 @@ #include "SecretMgr.h" #include "SharedDefines.h" #include "SteadyTimer.h" +#include "Systemd.h" #include "World.h" #include "WorldSessionMgr.h" #include "WorldSocket.h" @@ -406,7 +407,8 @@ int main(int argc, char** argv) sScriptMgr->OnShutdown(); // set server offline - LoginDatabase.DirectExecute("UPDATE realmlist SET flag = flag | {} WHERE id = '{}'", REALM_FLAG_OFFLINE, realm.Id.Realm); + if (!sConfigMgr->GetOption<bool>("Network.UseSocketActivation", false)) + LoginDatabase.DirectExecute("UPDATE realmlist SET flag = flag | {} WHERE id = '{}'", REALM_FLAG_OFFLINE, realm.Id.Realm); LOG_INFO("server.worldserver", "Halting process..."); diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 07945f07f3..089c1911a5 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -398,6 +398,20 @@ Network.TcpNodelay = 1 Network.EnableProxyProtocol = 0 # +# Network.UseSocketActivation +# Description: Enable systemd socket activation support for the worldserver. +# When enabled and the process is started by systemd socket activation, +# the server will use the socket passed by systemd instead of +# creating and binding its own listening socket. Disabled by default. +# +# When enabled the realm is not automatically set as offline on shutdown. +# +# Example: 1 - (Enabled) +# Default: 0 - (Disabled) + +Network.UseSocketActivation = 0 + +# ################################################################################################### ################################################################################################### diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index 762dc6e12e..522b17e41e 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -15,12 +15,13 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "ScriptedCreature.h" #include "Cell.h" #include "CellImpl.h" +#include "Containers.h" #include "GameTime.h" #include "GridNotifiers.h" #include "ObjectMgr.h" +#include "ScriptedCreature.h" #include "Spell.h" #include "TemporarySummon.h" @@ -141,6 +142,23 @@ Creature* SummonList::GetCreatureWithEntry(uint32 entry) const return nullptr; } +Creature* SummonList::GetRandomCreatureWithEntry(uint32 entry) const +{ + std::vector<ObjectGuid> candidates; + candidates.reserve(storage_.size()); + + for (auto const guid : storage_) + if (Creature* summon = ObjectAccessor::GetCreature(*me, guid)) + if (summon->GetEntry() == entry) + candidates.push_back(guid); + + if (candidates.empty()) + return nullptr; + + ObjectGuid randomGuid = Acore::Containers::SelectRandomContainerElement(candidates); + return ObjectAccessor::GetCreature(*me, randomGuid); +} + bool SummonList::IsAnyCreatureAlive() const { for (auto const& guid : storage_) diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h index 4aceb3fae5..47f8cf30c4 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -157,6 +157,7 @@ public: uint32 GetEntryCount(uint32 entry) const; void Respawn(); Creature* GetCreatureWithEntry(uint32 entry) const; + Creature* GetRandomCreatureWithEntry(uint32 entry) const; private: Creature* me; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 370e4c07df..c0b78bd2ae 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -38,6 +38,7 @@ #include "PoolMgr.h" #include "ScriptMgr.h" #include "ScriptedGossip.h" +#include "SpellAuraDefines.h" #include "SpellAuraEffects.h" #include "SpellMgr.h" #include "TemporarySummon.h" @@ -601,13 +602,13 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele SetMeleeDamageSchool(SpellSchools(cInfo->dmgschool)); CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(GetLevel(), cInfo->unit_class); float armor = stats->GenerateArmor(cInfo); - SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, armor); - SetModifierValue(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_HOLY])); - SetModifierValue(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_FIRE])); - SetModifierValue(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_NATURE])); - SetModifierValue(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_FROST])); - SetModifierValue(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_SHADOW])); - SetModifierValue(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_ARCANE])); + SetStatFlatModifier(UNIT_MOD_ARMOR, BASE_VALUE, armor); + SetStatFlatModifier(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_HOLY])); + SetStatFlatModifier(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_FIRE])); + SetStatFlatModifier(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_NATURE])); + SetStatFlatModifier(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_FROST])); + SetStatFlatModifier(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_SHADOW])); + SetStatFlatModifier(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_ARCANE])); SetCanModifyStats(true); UpdateAllStats(); @@ -1017,10 +1018,7 @@ void Creature::Regenerate(Powers power) } // Apply modifiers (if any). - AuraEffectList const& ModPowerRegenPCTAuras = GetAuraEffectsByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT); - for (AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) - if (Powers((*i)->GetMiscValue()) == power) - AddPct(addvalue, (*i)->GetAmount()); + addvalue *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, power); addvalue += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, power) * (power == POWER_FOCUS ? PET_FOCUS_REGEN_INTERVAL.count() : CREATURE_REGEN_INTERVAL) / (5 * IN_MILLISECONDS); @@ -1056,9 +1054,7 @@ void Creature::RegenerateHealth() } // Apply modifiers (if any). - AuraEffectList const& ModPowerRegenPCTAuras = GetAuraEffectsByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT); - for (AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) - AddPct(addvalue, (*i)->GetAmount()); + addvalue *= GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT); addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_REGEN) * CREATURE_REGEN_INTERVAL / (5 * IN_MILLISECONDS); @@ -1558,8 +1554,8 @@ void Creature::SelectLevel(bool changelevel) /// @todo: set UNIT_FIELD_POWER*, for some creature class case (energy, etc) - SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, (float)health); - SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, (float)mana); + SetStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, (float)health); + SetStatFlatModifier(UNIT_MOD_MANA, BASE_VALUE, (float)mana); // damage @@ -1577,8 +1573,8 @@ void Creature::SelectLevel(bool changelevel) SetBaseWeaponDamage(RANGED_ATTACK, MINDAMAGE, weaponBaseMinDamage); SetBaseWeaponDamage(RANGED_ATTACK, MAXDAMAGE, weaponBaseMaxDamage); - SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, stats->AttackPower); - SetModifierValue(UNIT_MOD_ATTACK_POWER_RANGED, BASE_VALUE, stats->RangedAttackPower); + SetStatFlatModifier(UNIT_MOD_ATTACK_POWER, BASE_VALUE, stats->AttackPower); + SetStatFlatModifier(UNIT_MOD_ATTACK_POWER_RANGED, BASE_VALUE, stats->RangedAttackPower); sScriptMgr->OnCreatureSelectLevel(cInfo, this); } diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 3d86fd0599..73ced58747 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -815,13 +815,6 @@ void Object::ApplyModSignedFloatValue(uint16 index, float val, bool apply) SetFloatValue(index, cur); } -void Object::ApplyPercentModFloatValue(uint16 index, float val, bool apply) -{ - float value = GetFloatValue(index); - ApplyPercentModFloatVar(value, val, apply); - SetFloatValue(index, value); -} - void Object::ApplyModPositiveFloatValue(uint16 index, float val, bool apply) { float cur = GetFloatValue(index); diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 9e84a3732a..02d38a2f5a 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -165,7 +165,6 @@ public: void ApplyModUInt64Value(uint16 index, int32 val, bool apply); void ApplyModPositiveFloatValue(uint16 index, float val, bool apply); void ApplyModSignedFloatValue(uint16 index, float val, bool apply); - void ApplyPercentModFloatValue(uint16 index, float val, bool apply); void SetFlag(uint16 index, uint32 newFlag); void RemoveFlag(uint16 index, uint32 oldFlag); diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 4cb7d3d1b1..55ebf72afd 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -1075,7 +1075,7 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool)); } - SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(petlevel * 50)); + SetStatFlatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(petlevel * 50)); uint32 attackTime = BASE_ATTACK_TIME; if (!owner->IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET) && cinfo->BaseAttackTime >= 1000) @@ -1094,7 +1094,7 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) // xinef: hunter pets should not inherit template resistances if (!IsHunterPet()) for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) - SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(cinfo->resistance[i])); + SetStatFlatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(cinfo->resistance[i])); //health, mana, armor and resistance PetLevelInfo const* pInfo = sObjectMgr->GetPetLevelInfo(creature_ID, petlevel); @@ -1111,15 +1111,15 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) } SetCreateHealth(pInfo->health*factorHealth); - SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, (float)pInfo->health); + SetStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, (float)pInfo->health); if (petType != HUNTER_PET) //hunter pet use focus { SetCreateMana(pInfo->mana); - SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, (float)pInfo->mana); + SetStatFlatModifier(UNIT_MOD_MANA, BASE_VALUE, (float)pInfo->mana); } if (pInfo->armor > 0) - SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor)); + SetStatFlatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor)); for (uint8 stat = 0; stat < MAX_STATS; ++stat) SetCreateStat(Stats(stat), float(pInfo->stats[stat])); @@ -1138,9 +1138,9 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) } SetCreateHealth(std::max<uint32>(1, stats->BaseHealth[cinfo->expansion]*factorHealth)); - SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, GetCreateHealth()); + SetStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, GetCreateHealth()); SetCreateMana(stats->BaseMana * factorMana); - SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, GetCreateMana()); + SetStatFlatModifier(UNIT_MOD_MANA, BASE_VALUE, GetCreateMana()); // xinef: added some multipliers so debuffs can affect pets in any way... SetCreateStat(STAT_STRENGTH, 22); @@ -1174,24 +1174,6 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) switch (GetEntry()) { - case NPC_FELGUARD: - { - // xinef: Glyph of Felguard, so ugly im crying... no appropriate spell - if (AuraEffect* aurEff = owner->GetAuraEffectDummy(SPELL_GLYPH_OF_FELGUARD)) - { - HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, aurEff->GetAmount(), true); - } - - break; - } - case NPC_VOIDWALKER: - { - if (AuraEffect* aurEff = owner->GetAuraEffectDummy(SPELL_GLYPH_OF_VOIDWALKER)) - { - HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_PCT, aurEff->GetAmount(), true); - } - break; - } case NPC_WATER_ELEMENTAL_PERM: { AddAura(SPELL_PET_AVOIDANCE, this); diff --git a/src/server/game/Entities/Player/KillRewarder.cpp b/src/server/game/Entities/Player/KillRewarder.cpp index dce44da0f5..78d24b4dbc 100644 --- a/src/server/game/Entities/Player/KillRewarder.cpp +++ b/src/server/game/Entities/Player/KillRewarder.cpp @@ -21,6 +21,7 @@ #include "Pet.h" #include "Player.h" #include "ScriptMgr.h" +#include "SpellAuraDefines.h" #include "SpellAuraEffects.h" // KillRewarder incapsulates logic of rewarding player upon kill with: @@ -162,9 +163,7 @@ void KillRewarder::_RewardXP(Player* player, float rate) if (xp) { // 4.2.2. Apply auras modifying rewarded XP (SPELL_AURA_MOD_XP_PCT). - Unit::AuraEffectList const& auras = player->GetAuraEffectsByType(SPELL_AURA_MOD_XP_PCT); - for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i) - AddPct(xp, (*i)->GetAmount()); + xp *= player->GetTotalAuraMultiplier(SPELL_AURA_MOD_XP_PCT); // 4.2.3. Give XP to player. sScriptMgr->OnPlayerGiveXP(player, xp, _victim, PlayerXPSource::XPSOURCE_KILL); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 33d73b6e26..5bcd49fcf3 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -69,8 +69,10 @@ #include "Realm.h" #include "ReputationMgr.h" #include "ScriptMgr.h" +#include "SharedDefines.h" #include "SocialMgr.h" #include "Spell.h" +#include "SpellAuraDefines.h" #include "SpellAuraEffects.h" #include "SpellAuras.h" #include "SpellMgr.h" @@ -312,8 +314,8 @@ Player::Player(WorldSession* session): Unit(), m_mover(this) for (uint8 i = 0; i < BASEMOD_END; ++i) { - m_auraBaseMod[i][FLAT_MOD] = 0.0f; - m_auraBaseMod[i][PCT_MOD] = 1.0f; + m_auraBaseFlatMod[i] = 0.0f; + m_auraBasePctMod[i] = 1.0f; } for (uint8 i = 0; i < MAX_COMBAT_RATING; i++) @@ -831,9 +833,7 @@ int32 Player::getMaxTimer(MirrorTimerType timer) if (!IsAlive() || HasWaterBreathingAura() || GetSession()->GetSecurity() >= AccountTypes(sWorld->getIntConfig(CONFIG_DISABLE_BREATHING))) return DISABLED_MIRROR_TIMER; int32 UnderWaterTime = sWorld->getIntConfig(CONFIG_WATER_BREATH_TIMER); - AuraEffectList const& mModWaterBreathing = GetAuraEffectsByType(SPELL_AURA_MOD_WATER_BREATHING); - for (AuraEffectList::const_iterator i = mModWaterBreathing.begin(); i != mModWaterBreathing.end(); ++i) - AddPct(UnderWaterTime, (*i)->GetAmount()); + UnderWaterTime *= GetTotalAuraMultiplier(SPELL_AURA_MOD_WATER_BREATHING); return UnderWaterTime; } case FIRE_TIMER: @@ -1922,10 +1922,7 @@ void Player::Regenerate(Powers power) // Mana regen calculated in Player::UpdateManaRegen(), energy regen calculated in Player::UpdateEnergyRegen() if (power != POWER_MANA && power != POWER_ENERGY) { - AuraEffectList const& ModPowerRegenPCTAuras = GetAuraEffectsByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT); - for (AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) - if (Powers((*i)->GetMiscValue()) == power) - AddPct(addvalue, (*i)->GetAmount()); + addvalue *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, power); // Butchery requires combat for this effect if (power != POWER_RUNIC_POWER || IsInCombat()) @@ -2008,11 +2005,7 @@ void Player::RegenerateHealth() addvalue *= 1.33f; } - AuraEffectList const& mModHealthRegenPct = GetAuraEffectsByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT); - for (AuraEffectList::const_iterator i = mModHealthRegenPct.begin(); i != mModHealthRegenPct.end(); ++i) - { - AddPct(addvalue, (*i)->GetAmount()); - } + addvalue *= GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT); if (!IsInCombat()) { @@ -2666,14 +2659,14 @@ void Player::InitStatsForLevel(bool reapplyMods) // set armor (resistance 0) to original value (create_agility*2) SetArmor(int32(m_createStats[STAT_AGILITY] * 2)); - SetResistanceBuffMods(SpellSchools(0), true, 0.0f); - SetResistanceBuffMods(SpellSchools(0), false, 0.0f); + SetFloatValue(UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE + AsUnderlyingType(SPELL_SCHOOL_NORMAL), 0.0f); + SetFloatValue(UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE + AsUnderlyingType(SPELL_SCHOOL_NORMAL), 0.0f); // set other resistance to original value (0) for (uint8 i = 1; i < MAX_SPELL_SCHOOL; ++i) { SetResistance(SpellSchools(i), 0); - SetResistanceBuffMods(SpellSchools(i), true, 0.0f); - SetResistanceBuffMods(SpellSchools(i), false, 0.0f); + SetFloatValue(UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE + i, 0.0f); + SetFloatValue(UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE + i, 0.0f); } SetUInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE, 0); @@ -5012,24 +5005,107 @@ void Player::ClearChannelWatch() (*itr)->RemoveWatching(this); } -void Player::HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply) +void Player::HandleBaseModFlatValue(BaseModGroup modGroup, float amount, bool apply) { if (modGroup >= BASEMOD_END) { - LOG_ERROR("entities.player", "ERROR in HandleBaseModValue(): non existed BaseModGroup!"); + LOG_ERROR("entities.player", "Player::HandleBaseModFlatValue: Invalid BaseModGroup/BaseModType ({}/{}) for player '{}' ({})", + modGroup, FLAT_MOD, GetName(), GetGUID().ToString()); return; } - switch (modType) + m_auraBaseFlatMod[modGroup] += apply ? amount : -amount; + UpdateBaseModGroup(modGroup); +} + +void Player::ApplyBaseModPctValue(BaseModGroup modGroup, float pct) +{ + if (modGroup >= BASEMOD_END) { - case FLAT_MOD: - m_auraBaseMod[modGroup][modType] += apply ? amount : -amount; + LOG_ERROR("entities.player", "Player::ApplyBaseModPctValue: Invalid BaseModGroup/BaseModType ({}/{}) for player '{}' ({})", + modGroup, PCT_MOD, GetName(), GetGUID().ToString()); + return; + } + + m_auraBasePctMod[modGroup] += CalculatePct(1.0f, pct); + UpdateBaseModGroup(modGroup); +} + +void Player::SetBaseModFlatValue(BaseModGroup modGroup, float val) +{ + if (m_auraBaseFlatMod[modGroup] == val) + return; + + m_auraBaseFlatMod[modGroup] = val; + UpdateBaseModGroup(modGroup); +} + +void Player::SetBaseModPctValue(BaseModGroup modGroup, float val) +{ + if (m_auraBasePctMod[modGroup] == val) + return; + + m_auraBasePctMod[modGroup] = val; + UpdateBaseModGroup(modGroup); +} + +void Player::UpdateDamageDoneMods(WeaponAttackType attackType, int32 skipEnchantSlot /*= -1*/) +{ + Unit::UpdateDamageDoneMods(attackType, skipEnchantSlot); + + UnitMods unitMod; + switch (attackType) + { + case BASE_ATTACK: + unitMod = UNIT_MOD_DAMAGE_MAINHAND; + break; + case OFF_ATTACK: + unitMod = UNIT_MOD_DAMAGE_OFFHAND; break; - case PCT_MOD: - ApplyPercentModFloatVar(m_auraBaseMod[modGroup][modType], amount, apply); + case RANGED_ATTACK: + unitMod = UNIT_MOD_DAMAGE_RANGED; break; + default: + ABORT(); + break; + } + + float amount = 0.0f; + Item* item = GetWeaponForAttack(attackType, true); + if (!item) + return; + + for (uint8 slot = 0; slot < MAX_ENCHANTMENT_SLOT; ++slot) + { + if (skipEnchantSlot == slot) + continue; + + SpellItemEnchantmentEntry const* enchantmentEntry = sSpellItemEnchantmentStore.LookupEntry(item->GetEnchantmentId(EnchantmentSlot(slot))); + if (!enchantmentEntry) + continue; + + for (uint8 i = 0; i < MAX_SPELL_ITEM_ENCHANTMENT_EFFECTS; ++i) + { + switch (enchantmentEntry->type[i]) + { + case ITEM_ENCHANTMENT_TYPE_DAMAGE: + amount += enchantmentEntry->amount[i]; + break; + case ITEM_ENCHANTMENT_TYPE_TOTEM: + if (IsClass(CLASS_SHAMAN, CLASS_CONTEXT_ABILITY)) + amount += enchantmentEntry->amount[i] * item->GetTemplate()->Delay / 1000.0f; + break; + default: + break; + } + } } + HandleStatFlatModifier(unitMod, TOTAL_VALUE, amount, true); +} + +void Player::UpdateBaseModGroup(BaseModGroup modGroup) +{ if (!CanModifyStats()) return; @@ -5060,10 +5136,7 @@ float Player::GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const return 0.0f; } - if (modType == PCT_MOD && m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f) - return 0.0f; - - return m_auraBaseMod[modGroup][modType]; + return (modType == FLAT_MOD ? m_auraBaseFlatMod[modGroup] : m_auraBasePctMod[modGroup]); } float Player::GetTotalBaseModValue(BaseModGroup modGroup) const @@ -5074,15 +5147,15 @@ float Player::GetTotalBaseModValue(BaseModGroup modGroup) const return 0.0f; } - if (m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f) + if (m_auraBasePctMod[modGroup] <= 0.0f) return 0.0f; - return m_auraBaseMod[modGroup][FLAT_MOD] * m_auraBaseMod[modGroup][PCT_MOD]; + return m_auraBaseFlatMod[modGroup] * m_auraBasePctMod[modGroup]; } uint32 Player::GetShieldBlockValue() const { - float value = (m_auraBaseMod[SHIELD_BLOCK_VALUE][FLAT_MOD] + GetStat(STAT_STRENGTH) * 0.5f - 10) * m_auraBaseMod[SHIELD_BLOCK_VALUE][PCT_MOD]; + float value = (m_auraBaseFlatMod[SHIELD_BLOCK_VALUE] + GetStat(STAT_STRENGTH) * 0.5f - 10) * m_auraBasePctMod[SHIELD_BLOCK_VALUE]; value = (value < 0) ? 0 : value; @@ -5151,7 +5224,7 @@ void Player::GetDodgeFromAgility(float& diminishing, float& nondiminishing) return; /// @todo: research if talents/effects that increase total agility by x% should increase non-diminishing part - float base_agility = GetCreateStat(STAT_AGILITY) * m_auraModifiersGroup[UNIT_MOD_STAT_START + static_cast<uint16>(STAT_AGILITY)][BASE_PCT]; + float base_agility = GetCreateStat(STAT_AGILITY) * GetPctModifierValue(UnitMods(UNIT_MOD_STAT_START + AsUnderlyingType(STAT_AGILITY)), BASE_PCT); float bonus_agility = GetStat(STAT_AGILITY) - base_agility; // calculate diminishing (green in char screen) and non-diminishing (white) contribution @@ -6574,20 +6647,22 @@ void Player::_ApplyItemMods(Item* item, uint8 slot, bool apply) LOG_DEBUG("entities.player", "applying mods for item {} ", item->GetGUID().ToString()); - WeaponAttackType attacktype = Player::GetAttackBySlot(slot); - if (item->HasSocket()) //only (un)equipping of items with sockets can influence metagems, so no need to waste time with normal items CorrectMetaGemEnchants(slot, apply); - if (attacktype < MAX_ATTACK) - _ApplyWeaponDependentAuraMods(item, attacktype, apply); - _ApplyItemBonuses(proto, slot, apply); if (slot == EQUIPMENT_SLOT_RANGED) _ApplyAmmoBonuses(); ApplyItemEquipSpell(item, apply); + + ApplyItemDependentAuras(item, apply); + + WeaponAttackType const attackType = Player::GetAttackBySlot(slot); + if (attackType != MAX_ATTACK) + UpdateWeaponDependentAuras(attackType); + ApplyEnchantment(item, apply); LOG_DEBUG("entities.player.items", "_ApplyItemMods complete."); @@ -6658,30 +6733,30 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply switch (statType) { case ITEM_MOD_MANA: - HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(val), apply); + HandleStatFlatModifier(UNIT_MOD_MANA, BASE_VALUE, float(val), apply); break; case ITEM_MOD_HEALTH: // modify HP - HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(val), apply); + HandleStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(val), apply); break; case ITEM_MOD_AGILITY: // modify agility - HandleStatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val), apply); - ApplyStatBuffMod(STAT_AGILITY, float(val), apply); + HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val), apply); + UpdateStatBuffMod(STAT_AGILITY); break; case ITEM_MOD_STRENGTH: //modify strength - HandleStatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val), apply); - ApplyStatBuffMod(STAT_STRENGTH, float(val), apply); + HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val), apply); + UpdateStatBuffMod(STAT_STRENGTH); break; case ITEM_MOD_INTELLECT: //modify intellect - HandleStatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val), apply); - ApplyStatBuffMod(STAT_INTELLECT, float(val), apply); + HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val), apply); + UpdateStatBuffMod(STAT_INTELLECT); break; case ITEM_MOD_SPIRIT: //modify spirit - HandleStatModifier(UNIT_MOD_STAT_SPIRIT, BASE_VALUE, float(val), apply); - ApplyStatBuffMod(STAT_SPIRIT, float(val), apply); + HandleStatFlatModifier(UNIT_MOD_STAT_SPIRIT, BASE_VALUE, float(val), apply); + UpdateStatBuffMod(STAT_SPIRIT); break; case ITEM_MOD_STAMINA: //modify stamina - HandleStatModifier(UNIT_MOD_STAT_STAMINA, BASE_VALUE, float(val), apply); - ApplyStatBuffMod(STAT_STAMINA, float(val), apply); + HandleStatFlatModifier(UNIT_MOD_STAT_STAMINA, BASE_VALUE, float(val), apply); + UpdateStatBuffMod(STAT_STAMINA); break; case ITEM_MOD_DEFENSE_SKILL_RATING: ApplyRatingMod(CR_DEFENSE_SKILL, int32(val), apply); @@ -6770,11 +6845,11 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply ApplyRatingMod(CR_EXPERTISE, int32(val), apply); break; case ITEM_MOD_ATTACK_POWER: - HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(val), apply); - HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(val), apply); + HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(val), apply); + HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(val), apply); break; case ITEM_MOD_RANGED_ATTACK_POWER: - HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(val), apply); + HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(val), apply); break; // case ITEM_MOD_FERAL_ATTACK_POWER: // ApplyFeralAPBonus(int32(val), apply); @@ -6795,7 +6870,7 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply ApplySpellPenetrationBonus(val, apply); break; case ITEM_MOD_BLOCK_VALUE: - HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, float(val), apply); + HandleBaseModFlatValue(SHIELD_BLOCK_VALUE, float(val), apply); break; /// @deprecated item mods case ITEM_MOD_SPELL_HEALING_DONE: @@ -6825,7 +6900,7 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply if (armor) { - UnitModifierType modType = TOTAL_VALUE; + UnitModifierFlatType modType = TOTAL_VALUE; if (proto->Class == ITEM_CLASS_ARMOR) { switch (proto->SubClass) @@ -6839,33 +6914,33 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply break; } } - HandleStatModifier(UNIT_MOD_ARMOR, modType, float(armor), apply); + HandleStatFlatModifier(UNIT_MOD_ARMOR, modType, float(armor), apply); } // Add armor bonus from ArmorDamageModifier if > 0 if (proto->ArmorDamageModifier > 0 && sScriptMgr->OnPlayerCanArmorDamageModifier(this)) - HandleStatModifier(UNIT_MOD_ARMOR, TOTAL_VALUE, float(proto->ArmorDamageModifier), apply); + HandleStatFlatModifier(UNIT_MOD_ARMOR, TOTAL_VALUE, float(proto->ArmorDamageModifier), apply); if (proto->Block) - HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, float(proto->Block), apply); + HandleBaseModFlatValue(SHIELD_BLOCK_VALUE, float(proto->Block), apply); if (proto->HolyRes) - HandleStatModifier(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(proto->HolyRes), apply); + HandleStatFlatModifier(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(proto->HolyRes), apply); if (proto->FireRes) - HandleStatModifier(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(proto->FireRes), apply); + HandleStatFlatModifier(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(proto->FireRes), apply); if (proto->NatureRes) - HandleStatModifier(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(proto->NatureRes), apply); + HandleStatFlatModifier(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(proto->NatureRes), apply); if (proto->FrostRes) - HandleStatModifier(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(proto->FrostRes), apply); + HandleStatFlatModifier(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(proto->FrostRes), apply); if (proto->ShadowRes) - HandleStatModifier(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(proto->ShadowRes), apply); + HandleStatFlatModifier(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(proto->ShadowRes), apply); if (proto->ArcaneRes) - HandleStatModifier(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(proto->ArcaneRes), apply); + HandleStatFlatModifier(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(proto->ArcaneRes), apply); WeaponAttackType attType = Player::GetAttackBySlot(slot); if (attType != MAX_ATTACK) @@ -7036,118 +7111,92 @@ void Player::UpdateItemObtainSpells(Item* item, uint8 bag, uint8 slot) ApplyItemObtainSpells(item, true); } -SpellSchoolMask Player::GetMeleeDamageSchoolMask(WeaponAttackType attackType /*= BASE_ATTACK*/, uint8 damageIndex /*= 0*/) const -{ - if (Item const* weapon = GetWeaponForAttack(attackType, true)) - { - return SpellSchoolMask(1 << weapon->GetTemplate()->Damage[damageIndex].DamageType); - } - - return SPELL_SCHOOL_MASK_NORMAL; -} - -void Player::_ApplyWeaponDependentAuraMods(Item* item, WeaponAttackType attackType, bool apply) -{ - AuraEffectList const& auraCritList = GetAuraEffectsByType(SPELL_AURA_MOD_WEAPON_CRIT_PERCENT); - for (AuraEffectList::const_iterator itr = auraCritList.begin(); itr != auraCritList.end(); ++itr) - _ApplyWeaponDependentAuraCritMod(item, attackType, *itr, apply); - - AuraEffectList const& auraDamageFlatList = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE); - for (AuraEffectList::const_iterator itr = auraDamageFlatList.begin(); itr != auraDamageFlatList.end(); ++itr) - _ApplyWeaponDependentAuraDamageMod(item, attackType, *itr, apply); - - AuraEffectList const& auraDamagePctList = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); - for (AuraEffectList::const_iterator itr = auraDamagePctList.begin(); itr != auraDamagePctList.end(); ++itr) - _ApplyWeaponDependentAuraDamageMod(item, attackType, *itr, apply); -} - -void Player::_ApplyWeaponDependentAuraCritMod(Item* item, WeaponAttackType attackType, AuraEffect const* aura, bool apply) +// this one rechecks weapon auras and stores them in BaseModGroup container +// needed for things like axe specialization applying only to axe weapons in case of dual-wield +void Player::UpdateWeaponDependentCritAuras(WeaponAttackType attackType) { - // don't apply mod if item is broken or cannot be used - if (item->IsBroken() || !CanUseAttackType(attackType)) - return; - - // generic not weapon specific case processes in aura code - if (aura->GetSpellInfo()->EquippedItemClass == -1) - return; - - if (!sScriptMgr->OnPlayerCanApplyWeaponDependentAuraDamageMod(this, item, attackType, aura, apply)) - return; - - BaseModGroup mod = BASEMOD_END; + BaseModGroup modGroup; switch (attackType) { case BASE_ATTACK: - mod = CRIT_PERCENTAGE; + modGroup = CRIT_PERCENTAGE; break; case OFF_ATTACK: - mod = OFFHAND_CRIT_PERCENTAGE; + modGroup = OFFHAND_CRIT_PERCENTAGE; break; case RANGED_ATTACK: - mod = RANGED_CRIT_PERCENTAGE; + modGroup = RANGED_CRIT_PERCENTAGE; break; default: - return; + ABORT(); + break; } - if (item->IsFitToSpellRequirements(aura->GetSpellInfo())) - HandleBaseModValue(mod, FLAT_MOD, float (aura->GetAmount()), apply); + float amount = 0.0f; + amount += GetTotalAuraModifier(SPELL_AURA_MOD_WEAPON_CRIT_PERCENT, std::bind(&Unit::CheckAttackFitToAuraRequirement, this, attackType, std::placeholders::_1)); + + // these auras don't have item requirement (only Combat Expertise in 3.3.5a) + amount += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_PCT); + + SetBaseModFlatValue(modGroup, amount); } -void Player::_ApplyWeaponDependentAuraDamageMod(Item* item, WeaponAttackType attackType, AuraEffect const* aura, bool apply) +void Player::UpdateAllWeaponDependentCritAuras() { - // don't apply mod if item is broken or cannot be used - if (item->IsBroken() || !CanUseAttackType(attackType)) - return; - - // ignore spell mods for not wands - if ((aura->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) == 0 && (getClassMask() & CLASSMASK_WAND_USERS) == 0) - return; + for (uint8 i = BASE_ATTACK; i < MAX_ATTACK; ++i) + UpdateWeaponDependentCritAuras(WeaponAttackType(i)); +} - // generic not weapon specific case processes in aura code - if (aura->GetSpellInfo()->EquippedItemClass == -1) - return; +void Player::UpdateWeaponDependentAuras(WeaponAttackType attackType) +{ + UpdateWeaponDependentCritAuras(attackType); + UpdateDamageDoneMods(attackType); + UpdateDamagePctDoneMods(attackType); +} - UnitMods unitMod = UNIT_MOD_END; - switch (attackType) +void Player::ApplyItemDependentAuras(Item* item, bool apply) +{ + if (apply) { - case BASE_ATTACK: - unitMod = UNIT_MOD_DAMAGE_MAINHAND; - break; - case OFF_ATTACK: - unitMod = UNIT_MOD_DAMAGE_OFFHAND; - break; - case RANGED_ATTACK: - unitMod = UNIT_MOD_DAMAGE_RANGED; - break; - default: - return; - } + PlayerSpellMap const& spells = GetSpellMap(); + for (auto itr = spells.begin(); itr != spells.end(); ++itr) + { + if (itr->second->State == PLAYERSPELL_REMOVED) + continue; - UnitModifierType unitModType = TOTAL_VALUE; - switch (aura->GetAuraType()) - { - case SPELL_AURA_MOD_DAMAGE_DONE: - unitModType = TOTAL_VALUE; - break; - case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: - unitModType = TOTAL_PCT; - break; - default: - return; + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); + if (!spellInfo || !spellInfo->IsPassive() || spellInfo->EquippedItemClass < 0) + continue; + + if (!HasAura(itr->first) && HasItemFitToSpellRequirements(spellInfo)) + AddAura(itr->first, this); // no SMSG_SPELL_GO in sniff found + } } + else + RemoveItemDependentAurasAndCasts(item); +} - if (item->IsFitToSpellRequirements(aura->GetSpellInfo())) +bool Player::CheckAttackFitToAuraRequirement(WeaponAttackType attackType, AuraEffect const* aurEff) const +{ + SpellInfo const* spellInfo = aurEff->GetSpellInfo(); + if (spellInfo->EquippedItemClass == -1) + return true; + + Item* item = GetWeaponForAttack(attackType, true); + if (!item || !item->IsFitToSpellRequirements(spellInfo)) + return false; + + return true; +} + +SpellSchoolMask Player::GetMeleeDamageSchoolMask(WeaponAttackType attackType /*= BASE_ATTACK*/, uint8 damageIndex /*= 0*/) const +{ + if (Item const* weapon = GetWeaponForAttack(attackType, true)) { - HandleStatModifier(unitMod, unitModType, float(aura->GetAmount()), apply); - if (unitModType == TOTAL_VALUE) - { - if (aura->GetAmount() > 0) - ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS, aura->GetAmount(), apply); - else - ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG, aura->GetAmount(), apply); - } + return SpellSchoolMask(1 << weapon->GetTemplate()->Damage[damageIndex].DamageType); } + + return SPELL_SCHOOL_MASK_NORMAL; } void Player::ApplyItemEquipSpell(Item* item, bool apply, bool form_change) @@ -7464,12 +7513,6 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8 continue; } - if (!spellInfo->CheckElixirStacking(this)) - { - Spell::SendCastResult(this, spellInfo, cast_count, SPELL_FAILED_AURA_BOUNCED); - continue; - } - Spell* spell = new Spell(this, spellInfo, (count > 0) ? TRIGGERED_FULL_MASK : TRIGGERED_NONE); spell->m_CastItem = item; spell->m_cast_count = cast_count; // set count of casts @@ -7578,10 +7621,7 @@ void Player::_RemoveAllItemMods() if (!proto) continue; - WeaponAttackType attacktype = Player::GetAttackBySlot(i); - if (attacktype < MAX_ATTACK) - _ApplyWeaponDependentAuraMods(m_items[i], attacktype, false); - + ApplyItemDependentAuras(m_items[i], false); _ApplyItemBonuses(proto, i, false); if (i == EQUIPMENT_SLOT_RANGED) @@ -7607,12 +7647,13 @@ void Player::_ApplyAllItemMods() if (!proto) continue; - WeaponAttackType attacktype = Player::GetAttackBySlot(i); - if (attacktype < MAX_ATTACK) - _ApplyWeaponDependentAuraMods(m_items[i], attacktype, true); - + ApplyItemDependentAuras(m_items[i], true); _ApplyItemBonuses(proto, i, true); + WeaponAttackType const attackType = Player::GetAttackBySlot(i); + if (attackType != MAX_ATTACK) + UpdateWeaponDependentAuras(attackType); + if (i == EQUIPMENT_SLOT_RANGED) _ApplyAmmoBonuses(); } @@ -12583,9 +12624,9 @@ void Player::RemoveItemDependentAurasAndCasts(Item* pItem) { Aura* aura = itr->second; - // skip passive (passive item dependent spells work in another way) and not self applied auras + // skip not self applied auras SpellInfo const* spellInfo = aura->GetSpellInfo(); - if (aura->IsPassive() || aura->GetCasterGUID() != GetGUID()) + if (aura->GetCasterGUID() != GetGUID()) { ++itr; continue; diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 1603a4f687..4e29cd5f5e 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1334,7 +1334,7 @@ public: { return StoreItem(dest, pItem, update); } - void RemoveItem(uint8 bag, uint8 slot, bool update, bool swap = false); + void RemoveItem(uint8 bag, uint8 slot, bool update); void MoveItemFromInventory(uint8 bag, uint8 slot, bool update); // in trade, auction, guild bank, mail.... void MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool update, bool in_characterInventoryDB = false); @@ -2187,11 +2187,19 @@ public: [[nodiscard]] bool CanTameExoticPets() const { return IsGameMaster() || HasAuraType(SPELL_AURA_ALLOW_TAME_PET_TYPE); } void SetRegularAttackTime(); - void SetBaseModValue(BaseModGroup modGroup, BaseModType modType, float value) { m_auraBaseMod[modGroup][modType] = value; } - void HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply); + + void HandleBaseModFlatValue(BaseModGroup modGroup, float amount, bool apply); + void ApplyBaseModPctValue(BaseModGroup modGroup, float pct); + + void SetBaseModFlatValue(BaseModGroup modGroup, float val); + void SetBaseModPctValue(BaseModGroup modGroup, float val); + + void UpdateDamageDoneMods(WeaponAttackType attackType, int32 skipEnchantSlot = -1) override; + void UpdateBaseModGroup(BaseModGroup modGroup); + [[nodiscard]] float GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const; [[nodiscard]] float GetTotalBaseModValue(BaseModGroup modGroup) const; - [[nodiscard]] float GetTotalPercentageModValue(BaseModGroup modGroup) const { return m_auraBaseMod[modGroup][FLAT_MOD] + m_auraBaseMod[modGroup][PCT_MOD]; } + void _ApplyAllStatBonuses(); void _RemoveAllStatBonuses(); @@ -2203,9 +2211,13 @@ public: SpellSchoolMask GetMeleeDamageSchoolMask(WeaponAttackType attackType = BASE_ATTACK, uint8 damageIndex = 0) const override; - void _ApplyWeaponDependentAuraMods(Item* item, WeaponAttackType attackType, bool apply); - void _ApplyWeaponDependentAuraCritMod(Item* item, WeaponAttackType attackType, AuraEffect const* aura, bool apply); - void _ApplyWeaponDependentAuraDamageMod(Item* item, WeaponAttackType attackType, AuraEffect const* aura, bool apply); + void UpdateWeaponDependentAuras(WeaponAttackType attackType); + void ApplyItemDependentAuras(Item* item, bool apply); + + bool CheckAttackFitToAuraRequirement(WeaponAttackType attackType, AuraEffect const* aurEff) const override; + + void UpdateWeaponDependentCritAuras(WeaponAttackType attackType); + void UpdateAllWeaponDependentCritAuras(); void _ApplyItemMods(Item* item, uint8 slot, bool apply); void _RemoveAllItemMods(); @@ -2829,7 +2841,8 @@ protected: ActionButtonList m_actionButtons; - float m_auraBaseMod[BASEMOD_END][MOD_END]; + float m_auraBaseFlatMod[BASEMOD_END]; + float m_auraBasePctMod[BASEMOD_END]; int32 m_baseRatingValue[MAX_COMBAT_RATING]; uint32 m_baseSpellPower; uint32 m_baseSpellDamage; diff --git a/src/server/game/Entities/Player/PlayerQuest.cpp b/src/server/game/Entities/Player/PlayerQuest.cpp index 554622bba6..a2a99a4bd9 100644 --- a/src/server/game/Entities/Player/PlayerQuest.cpp +++ b/src/server/game/Entities/Player/PlayerQuest.cpp @@ -1405,9 +1405,7 @@ uint32 Player::CalculateQuestRewardXP(Quest const* quest) uint32 xp = uint32(quest->XPValue(GetLevel()) * GetQuestRate(quest->IsDFQuest())); // handle SPELL_AURA_MOD_XP_QUEST_PCT auras - Unit::AuraEffectList const& ModXPPctAuras = GetAuraEffectsByType(SPELL_AURA_MOD_XP_QUEST_PCT); - for (Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin(); i != ModXPPctAuras.end(); ++i) - AddPct(xp, (*i)->GetAmount()); + xp *= GetTotalAuraMultiplier(SPELL_AURA_MOD_XP_QUEST_PCT); return xp; } diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp index bad5c36824..46d964e819 100644 --- a/src/server/game/Entities/Player/PlayerStorage.cpp +++ b/src/server/game/Entities/Player/PlayerStorage.cpp @@ -60,6 +60,7 @@ #include "StringConvert.h" #include "Tokenize.h" #include "Transport.h" +#include "Unit.h" #include "UpdateFieldFlags.h" #include "Util.h" #include "World.h" @@ -2896,7 +2897,7 @@ void Player::VisualizeItem(uint8 slot, Item* pItem) pItem->SetState(ITEM_CHANGED, this); } -void Player::RemoveItem(uint8 bag, uint8 slot, bool update, bool swap) +void Player::RemoveItem(uint8 bag, uint8 slot, bool update) { // note: removeitem does not actually change the item // it only takes the item out of storage temporarily @@ -2931,12 +2932,6 @@ void Player::RemoveItem(uint8 bag, uint8 slot, bool update, bool swap) // remove item dependent auras and casts (only weapon and armor slots) if (slot < INVENTORY_SLOT_BAG_END && slot < EQUIPMENT_SLOT_END) { - // Xinef: Ensure that this function is called for places with swap=true - if (!swap) - { - RemoveItemDependentAurasAndCasts(pItem); - } - // remove held enchantments, update expertise if (slot == EQUIPMENT_SLOT_MAINHAND) { @@ -3071,9 +3066,6 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update) if (slot < EQUIPMENT_SLOT_END) { - // remove item dependent auras and casts (only weapon and armor slots) - RemoveItemDependentAurasAndCasts(pItem); - // update expertise and armor penetration - passive auras may need it switch (slot) { @@ -3865,8 +3857,8 @@ void Player::SwapItem(uint16 src, uint16 dst) } // now do moves, remove... - RemoveItem(dstbag, dstslot, false, true); - RemoveItem(srcbag, srcslot, false, true); + RemoveItem(dstbag, dstslot, false); + RemoveItem(srcbag, srcslot, false); // add to dest if (IsInventoryPos(dst)) @@ -4355,13 +4347,12 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool // processed in Player::CastItemCombatSpell break; case ITEM_ENCHANTMENT_TYPE_DAMAGE: - if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND) - HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(enchant_amount), apply); - else if (item->GetSlot() == EQUIPMENT_SLOT_OFFHAND) - HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(enchant_amount), apply); - else if (item->GetSlot() == EQUIPMENT_SLOT_RANGED) - HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(enchant_amount), apply); + { + WeaponAttackType const attackType = Player::GetAttackBySlot(item->GetSlot()); + if (attackType != MAX_ATTACK) + UpdateDamageDoneMods(attackType, apply ? -1 : slot); break; + } case ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL: if (enchant_spell_id) { @@ -4412,7 +4403,7 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool } } - HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + enchant_spell_id), TOTAL_VALUE, float(enchant_amount), apply); + HandleStatFlatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + enchant_spell_id), TOTAL_VALUE, float(enchant_amount), apply); break; case ITEM_ENCHANTMENT_TYPE_STAT: { @@ -4439,36 +4430,36 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool { case ITEM_MOD_MANA: LOG_DEBUG("entities.player.items", "+ {} MANA", enchant_amount); - HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(enchant_amount), apply); + HandleStatFlatModifier(UNIT_MOD_MANA, BASE_VALUE, float(enchant_amount), apply); break; case ITEM_MOD_HEALTH: LOG_DEBUG("entities.player.items", "+ {} HEALTH", enchant_amount); - HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(enchant_amount), apply); + HandleStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(enchant_amount), apply); break; case ITEM_MOD_AGILITY: LOG_DEBUG("entities.player.items", "+ {} AGILITY", enchant_amount); - HandleStatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(enchant_amount), apply); - ApplyStatBuffMod(STAT_AGILITY, (float)enchant_amount, apply); + HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(enchant_amount), apply); + UpdateStatBuffMod(STAT_AGILITY); break; case ITEM_MOD_STRENGTH: LOG_DEBUG("entities.player.items", "+ {} STRENGTH", enchant_amount); - HandleStatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(enchant_amount), apply); - ApplyStatBuffMod(STAT_STRENGTH, (float)enchant_amount, apply); + HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(enchant_amount), apply); + UpdateStatBuffMod(STAT_STRENGTH); break; case ITEM_MOD_INTELLECT: LOG_DEBUG("entities.player.items", "+ {} INTELLECT", enchant_amount); - HandleStatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(enchant_amount), apply); - ApplyStatBuffMod(STAT_INTELLECT, (float)enchant_amount, apply); + HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(enchant_amount), apply); + UpdateStatBuffMod(STAT_INTELLECT); break; case ITEM_MOD_SPIRIT: LOG_DEBUG("entities.player.items", "+ {} SPIRIT", enchant_amount); - HandleStatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, float(enchant_amount), apply); - ApplyStatBuffMod(STAT_SPIRIT, (float)enchant_amount, apply); + HandleStatFlatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, float(enchant_amount), apply); + UpdateStatBuffMod(STAT_SPIRIT); break; case ITEM_MOD_STAMINA: LOG_DEBUG("entities.player.items", "+ {} STAMINA", enchant_amount); - HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, float(enchant_amount), apply); - ApplyStatBuffMod(STAT_STAMINA, (float)enchant_amount, apply); + HandleStatFlatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, float(enchant_amount), apply); + UpdateStatBuffMod(STAT_STAMINA); break; case ITEM_MOD_DEFENSE_SKILL_RATING: ApplyRatingMod(CR_DEFENSE_SKILL, enchant_amount, apply); @@ -4579,12 +4570,12 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool LOG_DEBUG("entities.player.items", "+ {} EXPERTISE", enchant_amount); break; case ITEM_MOD_ATTACK_POWER: - HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(enchant_amount), apply); - HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply); + HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(enchant_amount), apply); + HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply); LOG_DEBUG("entities.player.items", "+ {} ATTACK_POWER", enchant_amount); break; case ITEM_MOD_RANGED_ATTACK_POWER: - HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply); + HandleStatFlatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(enchant_amount), apply); LOG_DEBUG("entities.player.items", "+ {} RANGED_ATTACK_POWER", enchant_amount); break; // case ITEM_MOD_FERAL_ATTACK_POWER: @@ -4612,7 +4603,7 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool LOG_DEBUG("entities.player.items", "+ {} SPELL_PENETRATION", enchant_amount); break; case ITEM_MOD_BLOCK_VALUE: - HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, float(enchant_amount), apply); + HandleBaseModFlatValue(SHIELD_BLOCK_VALUE, float(enchant_amount), apply); LOG_DEBUG("entities.player.items", "+ {} BLOCK_VALUE", enchant_amount); break; /// @deprecated item mods @@ -4631,20 +4622,9 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool } case ITEM_ENCHANTMENT_TYPE_TOTEM: // Shaman Rockbiter Weapon { - if (IsClass(CLASS_SHAMAN, CLASS_CONTEXT_ABILITY)) - { - float addValue = 0.0f; - if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND) - { - addValue = float(enchant_amount * item->GetTemplate()->Delay / 1000.0f); - HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, addValue, apply); - } - else if (item->GetSlot() == EQUIPMENT_SLOT_OFFHAND) - { - addValue = float(enchant_amount * item->GetTemplate()->Delay / 1000.0f); - HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, addValue, apply); - } - } + WeaponAttackType const attackType = Player::GetAttackBySlot(item->GetSlot()); + if (attackType != MAX_ATTACK) + UpdateDamageDoneMods(attackType); break; } case ITEM_ENCHANTMENT_TYPE_USE_SPELL: @@ -6308,7 +6288,7 @@ void Player::_LoadQuestStatus(PreparedQueryResult result) ++slot; } - LOG_DEBUG("entities.player.loading", "Quest status is {{}} for quest {{}} for player ({})", questStatusData.Status, quest_id, GetGUID().ToString()); + LOG_DEBUG("entities.player.loading", "Quest status is ({}) for quest ({}) for player ({})", questStatusData.Status, quest_id, GetGUID().ToString()); } } while (result->NextRow()); } @@ -6419,7 +6399,7 @@ void Player::_LoadWeeklyQuestStatus(PreparedQueryResult result) continue; m_weeklyquests.insert(quest_id); - LOG_DEBUG("entities.player.loading", "Weekly quest {{}} cooldown for player ({})", quest_id, GetGUID().ToString()); + LOG_DEBUG("entities.player.loading", "Weekly quest ({}) cooldown for player ({})", quest_id, GetGUID().ToString()); } while (result->NextRow()); } @@ -6442,7 +6422,7 @@ void Player::_LoadSeasonalQuestStatus(PreparedQueryResult result) continue; m_seasonalquests[event_id].insert(quest_id); - LOG_DEBUG("entities.player.loading", "Seasonal quest {{}} cooldown for player ({})", quest_id, GetGUID().ToString()); + LOG_DEBUG("entities.player.loading", "Seasonal quest ({}) cooldown for player ({})", quest_id, GetGUID().ToString()); } while (result->NextRow()); } @@ -6464,7 +6444,7 @@ void Player::_LoadMonthlyQuestStatus(PreparedQueryResult result) continue; m_monthlyquests.insert(quest_id); - LOG_DEBUG("entities.player.loading", "Monthly quest {{}} cooldown for player ({})", quest_id, GetGUID().ToString()); + LOG_DEBUG("entities.player.loading", "Monthly quest ({}) cooldown for player ({})", quest_id, GetGUID().ToString()); } while (result->NextRow()); } diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index 2913f66238..9a4427ce49 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -701,7 +701,7 @@ void Player::UpdateRating(CombatRating cr) void Player::UpdateAllRatings() { - for (int cr = 0; cr < MAX_COMBAT_RATING; ++cr) + for (uint8 cr = 0; cr < MAX_COMBAT_RATING; ++cr) UpdateRating(CombatRating(cr)); } diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 9009bc7d73..24c025ac6d 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -157,7 +157,7 @@ bool Player::UpdateStats(Stats stat) mask |= (*i)->GetMiscValue(); if (mask) { - for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating) + for (uint8 rating = 0; rating < MAX_COMBAT_RATING; ++rating) if (mask & (1 << rating)) ApplyRatingMod(CombatRating(rating), 0, true); } @@ -204,7 +204,7 @@ void Player::UpdateSpellDamageAndHealingBonus() bool Player::UpdateAllStats() { - for (int8 i = STAT_STRENGTH; i < MAX_STATS; ++i) + for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i) { float value = GetTotalStatValue(Stats(i)); SetStat(Stats(i), int32(value)); @@ -247,9 +247,9 @@ void Player::UpdateResistances(uint32 school) float value = 0.0f; UnitMods unitMod = UnitMods(UNIT_MOD_RESISTANCE_START + school); - value = GetModifierValue(unitMod, BASE_VALUE); - value *= GetModifierValue(unitMod, BASE_PCT); - value += GetModifierValue(unitMod, TOTAL_VALUE); + value = GetFlatModifierValue(unitMod, BASE_VALUE); + value *= GetPctModifierValue(unitMod, BASE_PCT); + value += GetFlatModifierValue(unitMod, TOTAL_VALUE); AuraEffectList const& mResbyIntellect = GetAuraEffectsByType(SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT); for(AuraEffectList::const_iterator i = mResbyIntellect.begin(); i != mResbyIntellect.end(); ++i) @@ -258,7 +258,7 @@ void Player::UpdateResistances(uint32 school) value += int32(GetStat(Stats((*i)->GetMiscValueB())) * (*i)->GetAmount() / 100.0f); } - value *= GetModifierValue(unitMod, TOTAL_PCT); + value *= GetPctModifierValue(unitMod, TOTAL_PCT); SetResistance(SpellSchools(school), int32(value)); } @@ -270,10 +270,10 @@ void Player::UpdateArmor() { UnitMods unitMod = UNIT_MOD_ARMOR; - float value = GetModifierValue(unitMod, BASE_VALUE); // base armor (from items) - value *= GetModifierValue(unitMod, BASE_PCT); // armor percent from items - value += GetStat(STAT_AGILITY) * 2.0f; // armor bonus from stats - value += GetModifierValue(unitMod, TOTAL_VALUE); + float value = GetFlatModifierValue(unitMod, BASE_VALUE); // base armor (from items) + value *= GetPctModifierValue(unitMod, BASE_PCT); // armor percent from items + value += GetStat(STAT_AGILITY) * 2.0f; // armor bonus from stats + value += GetFlatModifierValue(unitMod, TOTAL_VALUE); //add dynamic flat mods AuraEffectList const& mResbyIntellect = GetAuraEffectsByType(SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT); @@ -283,7 +283,7 @@ void Player::UpdateArmor() value += CalculatePct(GetStat(Stats((*i)->GetMiscValueB())), (*i)->GetAmount()); } - value *= GetModifierValue(unitMod, TOTAL_PCT); + value *= GetPctModifierValue(unitMod, TOTAL_PCT); SetArmor(int32(value)); @@ -314,10 +314,10 @@ void Player::UpdateMaxHealth() { UnitMods unitMod = UNIT_MOD_HEALTH; - float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth(); - value *= GetModifierValue(unitMod, BASE_PCT); - value += GetModifierValue(unitMod, TOTAL_VALUE) + GetHealthBonusFromStamina(); - value *= GetModifierValue(unitMod, TOTAL_PCT); + float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreateHealth(); + value *= GetPctModifierValue(unitMod, BASE_PCT); + value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + GetHealthBonusFromStamina(); + value *= GetPctModifierValue(unitMod, TOTAL_PCT); sScriptMgr->OnPlayerAfterUpdateMaxHealth(this, value); SetMaxHealth((uint32)value); @@ -329,10 +329,10 @@ void Player::UpdateMaxPower(Powers power) float bonusPower = (power == POWER_MANA && GetCreatePowers(power) > 0) ? GetManaBonusFromIntellect() : 0; - float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power); - value *= GetModifierValue(unitMod, BASE_PCT); - value += GetModifierValue(unitMod, TOTAL_VALUE) + bonusPower; - value *= GetModifierValue(unitMod, TOTAL_PCT); + float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power); + value *= GetPctModifierValue(unitMod, BASE_PCT); + value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + bonusPower; + value *= GetPctModifierValue(unitMod, TOTAL_PCT); sScriptMgr->OnPlayerAfterUpdateMaxPower(this, power, value); SetMaxPower(power, uint32(value)); @@ -487,10 +487,10 @@ void Player::UpdateAttackPowerAndDamage(bool ranged) } } - SetModifierValue(unitMod, BASE_VALUE, val2); + SetStatFlatModifier(unitMod, BASE_VALUE, val2); - float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT); - float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE); + float base_attPower = GetFlatModifierValue(unitMod, BASE_VALUE) * GetPctModifierValue(unitMod, BASE_PCT); + float attPowerMod = GetFlatModifierValue(unitMod, TOTAL_VALUE); //add dynamic flat mods if (ranged) @@ -514,7 +514,7 @@ void Player::UpdateAttackPowerAndDamage(bool ranged) attPowerMod += int32(GetArmor() / (*iter)->GetAmount()); } - float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f; + float attPowerMultiplier = GetPctModifierValue(unitMod, TOTAL_PCT) - 1.0f; sScriptMgr->OnPlayerAfterUpdateAttackPowerAndDamage(this, level, base_attPower, attPowerMod, attPowerMultiplier, ranged); SetInt32Value(index, (uint32)base_attPower); //UNIT_FIELD_(RANGED)_ATTACK_POWER field @@ -576,10 +576,10 @@ void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bo float attackSpeedMod = GetAPMultiplier(attType, normalized); - float baseValue = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType) / 14.0f * attackSpeedMod; - float basePct = GetModifierValue(unitMod, BASE_PCT); - float totalValue = GetModifierValue(unitMod, TOTAL_VALUE); - float totalPct = addTotalPct ? GetModifierValue(unitMod, TOTAL_PCT) : 1.0f; + float baseValue = GetFlatModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType) / 14.0f * attackSpeedMod; + float basePct = GetPctModifierValue(unitMod, BASE_PCT); + float totalValue = GetFlatModifierValue(unitMod, TOTAL_VALUE); + float totalPct = addTotalPct ? GetPctModifierValue(unitMod, TOTAL_PCT) : 1.0f; float weaponMinDamage = GetWeaponDamageRange(attType, MINDAMAGE); float weaponMaxDamage = GetWeaponDamageRange(attType, MAXDAMAGE); @@ -681,7 +681,8 @@ void Player::UpdateCritPercentage(WeaponAttackType attType) break; } - float value = GetTotalPercentageModValue(modGroup) + GetRatingBonusValue(cr); + // flat = bonus from crit auras, pct = bonus from agility, combat rating = mods from items + float value = GetBaseModValue(modGroup, FLAT_MOD) + GetBaseModValue(modGroup, PCT_MOD) + GetRatingBonusValue(cr); // Modify crit from weapon skill and maximized defense skill of same level victim difference value += (int32(GetWeaponSkillValue(attType)) - int32(GetMaxSkillValueForLevel())) * 0.04f; @@ -698,9 +699,9 @@ void Player::UpdateAllCritPercentages() { float value = GetMeleeCritFromAgility(); - SetBaseModValue(CRIT_PERCENTAGE, PCT_MOD, value); - SetBaseModValue(OFFHAND_CRIT_PERCENTAGE, PCT_MOD, value); - SetBaseModValue(RANGED_CRIT_PERCENTAGE, PCT_MOD, value); + SetBaseModPctValue(CRIT_PERCENTAGE, value); + SetBaseModPctValue(OFFHAND_CRIT_PERCENTAGE, value); + SetBaseModPctValue(RANGED_CRIT_PERCENTAGE, value); UpdateCritPercentage(BASE_ATTACK); UpdateCritPercentage(OFF_ATTACK); @@ -849,7 +850,7 @@ void Player::UpdateSpellCritChance(uint32 school) // Crit from Intellect crit += GetSpellCritFromIntellect(); // Increase crit from SPELL_AURA_MOD_SPELL_CRIT_CHANCE - crit += GetTotalAuraModifierAreaExclusive(SPELL_AURA_MOD_SPELL_CRIT_CHANCE); + crit += GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_CRIT_CHANCE); // Increase crit from SPELL_AURA_MOD_CRIT_PCT crit += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_PCT); // Increase crit by school from SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL @@ -900,16 +901,11 @@ void Player::UpdateExpertise(WeaponAttackType attack) Item* weapon = GetWeaponForAttack(attack, true); - AuraEffectList const& expAuras = GetAuraEffectsByType(SPELL_AURA_MOD_EXPERTISE); - for (AuraEffectList::const_iterator itr = expAuras.begin(); itr != expAuras.end(); ++itr) + expertise += GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE, [weapon](AuraEffect const* aurEff) { - // item neutral spell - if ((*itr)->GetSpellInfo()->EquippedItemClass == -1) - expertise += (*itr)->GetAmount(); - // item dependent spell - else if (weapon && weapon->IsFitToSpellRequirements((*itr)->GetSpellInfo())) - expertise += (*itr)->GetAmount(); - } + return aurEff->GetSpellInfo()->EquippedItemClass == -1 || // item neutral spell + (weapon && weapon->IsFitToSpellRequirements(aurEff->GetSpellInfo())); // item dependent spell + }); if (expertise < 0) expertise = 0; @@ -1100,9 +1096,9 @@ void Creature::UpdateAttackPowerAndDamage(bool ranged) indexMulti = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER; } - float baseAttackPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT); - float attackPowerMod = GetModifierValue(unitMod, TOTAL_VALUE); - float attackPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f; + float baseAttackPower = GetFlatModifierValue(unitMod, BASE_VALUE) * GetPctModifierValue(unitMod, BASE_PCT); + float attackPowerMod = GetFlatModifierValue(unitMod, TOTAL_VALUE); + float attackPowerMultiplier = GetPctModifierValue(unitMod, TOTAL_PCT) - 1.0f; SetInt32Value(index, uint32(baseAttackPower)); // UNIT_FIELD_(RANGED)_ATTACK_POWER SetInt32Value(indexMod, uint32(attackPowerMod)); // UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS @@ -1166,10 +1162,10 @@ void Creature::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, float attackPower = GetTotalAttackPowerValue(attType); float attackSpeedMulti = GetAPMultiplier(attType, normalized); - float baseValue = GetModifierValue(unitMod, BASE_VALUE) + (attackPower / 14.0f) * variance; - float basePct = GetModifierValue(unitMod, BASE_PCT) * attackSpeedMulti; - float totalValue = GetModifierValue(unitMod, TOTAL_VALUE); - float totalPct = addTotalPct ? GetModifierValue(unitMod, TOTAL_PCT) : 1.0f; + float baseValue = GetFlatModifierValue(unitMod, BASE_VALUE) + (attackPower / 14.0f) * variance; + float basePct = GetPctModifierValue(unitMod, BASE_PCT) * attackSpeedMulti; + float totalValue = GetFlatModifierValue(unitMod, TOTAL_VALUE); + float totalPct = addTotalPct ? GetPctModifierValue(unitMod, TOTAL_PCT) : 1.0f; float dmgMultiplier = GetCreatureTemplate()->DamageModifier; // = DamageModifier * _GetDamageMod(rank); minDamage = ((weaponMinDamage + baseValue) * dmgMultiplier * basePct + totalValue) * totalPct; @@ -1233,11 +1229,11 @@ bool Guardian::UpdateAllStats() void Guardian::UpdateArmor() { - float value = GetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE); - value *= GetModifierValue(UNIT_MOD_ARMOR, BASE_PCT); + float value = GetFlatModifierValue(UNIT_MOD_ARMOR, BASE_VALUE); + value *= GetPctModifierValue(UNIT_MOD_ARMOR, BASE_PCT); value += std::max<float>(GetStat(STAT_AGILITY) - GetCreateStat(STAT_AGILITY), 0.0f) * 2.0f; - value += GetModifierValue(UNIT_MOD_ARMOR, TOTAL_VALUE); - value *= GetModifierValue(UNIT_MOD_ARMOR, TOTAL_PCT); + value += GetFlatModifierValue(UNIT_MOD_ARMOR, TOTAL_VALUE); + value *= GetPctModifierValue(UNIT_MOD_ARMOR, TOTAL_PCT); SetArmor(int32(value)); } @@ -1278,10 +1274,10 @@ void Guardian::UpdateMaxHealth() break; } - float value = GetModifierValue(unitMod, BASE_VALUE);// xinef: Do NOT add base health TWICE + GetCreateHealth(); - value *= GetModifierValue(unitMod, BASE_PCT); - value += GetModifierValue(unitMod, TOTAL_VALUE) + stamina * multiplicator; - value *= GetModifierValue(unitMod, TOTAL_PCT); + float value = GetFlatModifierValue(unitMod, BASE_VALUE);// xinef: Do NOT add base health TWICE + GetCreateHealth(); + value *= GetPctModifierValue(unitMod, BASE_PCT); + value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + stamina * multiplicator; + value *= GetPctModifierValue(unitMod, TOTAL_PCT); SetMaxHealth((uint32)value); } @@ -1311,11 +1307,11 @@ void Guardian::UpdateMaxPower(Powers power) break; } - // xinef: Do NOT add base mana TWICE - float value = GetModifierValue(unitMod, BASE_VALUE) + (power != POWER_MANA ? GetCreatePowers(power) : 0); - value *= GetModifierValue(unitMod, BASE_PCT); - value += GetModifierValue(unitMod, TOTAL_VALUE) + addValue * multiplicator; - value *= GetModifierValue(unitMod, TOTAL_PCT); + // Do NOT add base mana TWICE + float value = GetFlatModifierValue(unitMod, BASE_VALUE) + (power != POWER_MANA ? GetCreatePowers(power) : 0); + value *= GetPctModifierValue(unitMod, BASE_PCT); + value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + addValue * multiplicator; + value *= GetPctModifierValue(unitMod, TOTAL_PCT); SetMaxPower(power, uint32(value)); } @@ -1335,12 +1331,12 @@ void Guardian::UpdateAttackPowerAndDamage(bool ranged) else val = 2 * GetStat(STAT_STRENGTH) - 20.0f; - SetModifierValue(unitMod, BASE_VALUE, val); + SetStatFlatModifier(unitMod, BASE_VALUE, val); //in BASE_VALUE of UNIT_MOD_ATTACK_POWER for creatures we store data of meleeattackpower field in DB - float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT); - float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE); - float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f; + float base_attPower = GetFlatModifierValue(unitMod, BASE_VALUE) * GetPctModifierValue(unitMod, BASE_PCT); + float attPowerMod = GetFlatModifierValue(unitMod, TOTAL_VALUE); + float attPowerMultiplier = GetPctModifierValue(unitMod, TOTAL_PCT) - 1.0f; //UNIT_FIELD_(RANGED)_ATTACK_POWER field SetInt32Value(UNIT_FIELD_ATTACK_POWER, (int32)base_attPower); @@ -1362,10 +1358,10 @@ void Guardian::UpdateDamagePhysical(WeaponAttackType attType) float att_speed = float(GetAttackTime(BASE_ATTACK)) / 1000.0f; - float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType) / 14.0f * att_speed; - float base_pct = GetModifierValue(unitMod, BASE_PCT); - float total_value = GetModifierValue(unitMod, TOTAL_VALUE); - float total_pct = GetModifierValue(unitMod, TOTAL_PCT); + float base_value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType) / 14.0f * att_speed; + float base_pct = GetPctModifierValue(unitMod, BASE_PCT); + float total_value = GetFlatModifierValue(unitMod, TOTAL_VALUE); + float total_pct = GetPctModifierValue(unitMod, TOTAL_PCT); float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE); float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 31da399db5..623886eb78 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -16,7 +16,6 @@ */ #include "Unit.h" -#include "AccountMgr.h" #include "AreaDefines.h" #include "ArenaSpectator.h" #include "Battlefield.h" @@ -35,6 +34,7 @@ #include "CreatureGroups.h" #include "DisableMgr.h" #include "DynamicVisibility.h" +#include "Errors.h" #include "GameObjectAI.h" #include "GameTime.h" #include "GridNotifiersImpl.h" @@ -54,19 +54,19 @@ #include "Player.h" #include "ReputationMgr.h" #include "ScriptMgr.h" -#include "SmartAI.h" +#include "SharedDefines.h" #include "Spell.h" +#include "SpellAuraDefines.h" #include "SpellAuraEffects.h" #include "SpellAuras.h" #include "SpellInfo.h" #include "SpellMgr.h" -#include "TargetedMovementGenerator.h" #include "TemporarySummon.h" -#include "Tokenize.h" #include "Totem.h" #include "TotemAI.h" #include "Transport.h" #include "UpdateFieldFlags.h" +#include "UpdateFields.h" #include "Util.h" #include "Vehicle.h" #include "World.h" @@ -268,13 +268,13 @@ Unit::Unit() : WorldObject(), for (uint8 i = 0; i < UNIT_MOD_END; ++i) { - m_auraModifiersGroup[i][BASE_VALUE] = 0.0f; - m_auraModifiersGroup[i][BASE_PCT] = 1.0f; - m_auraModifiersGroup[i][TOTAL_VALUE] = 0.0f; - m_auraModifiersGroup[i][TOTAL_PCT] = 1.0f; + m_auraFlatModifiersGroup[i][BASE_VALUE] = 0.0f; + m_auraFlatModifiersGroup[i][TOTAL_VALUE] = 0.0f; + m_auraPctModifiersGroup[i][BASE_PCT] = 1.0f; + m_auraPctModifiersGroup[i][TOTAL_PCT] = 1.0f; } // implement 50% base damage from offhand - m_auraModifiersGroup[UNIT_MOD_DAMAGE_OFFHAND][TOTAL_PCT] = 0.5f; + m_auraPctModifiersGroup[UNIT_MOD_DAMAGE_OFFHAND][TOTAL_PCT] = 0.5f; for (uint8 i = 0; i < MAX_ATTACK; ++i) { @@ -628,7 +628,7 @@ void Unit::UpdateSplinePosition() //if (HasUnitState(UNIT_STATE_CANNOT_TURN)) // loc.orientation = GetOrientation(); - if (IsPlayer()) + if (IsPlayer() || IsPet()) UpdatePosition(loc.x, loc.y, loc.z, loc.orientation); else ToCreature()->SetPosition(loc.x, loc.y, loc.z, loc.orientation); @@ -2096,22 +2096,22 @@ uint32 Unit::CalcArmorReducedDamage(Unit const* attacker, Unit const* victim, co if (attacker->IsPlayer()) { float bonusPct = 0; - AuraEffectList const& armorPenAuras = attacker->GetAuraEffectsByType(SPELL_AURA_MOD_ARMOR_PENETRATION_PCT); - for (AuraEffectList::const_iterator itr = armorPenAuras.begin(); itr != armorPenAuras.end(); ++itr) + bonusPct += attacker->GetTotalAuraModifier(SPELL_AURA_MOD_ARMOR_PENETRATION_PCT, [spellInfo,attacker](AuraEffect const* aurEff) { - if ((*itr)->GetSpellInfo()->EquippedItemClass == -1) + if (aurEff->GetSpellInfo()->EquippedItemClass == -1) { - if (!spellInfo || (*itr)->IsAffectedOnSpell(spellInfo) || (*itr)->GetMiscValue() & spellInfo->GetSchoolMask()) - bonusPct += (*itr)->GetAmount(); - else if (!(*itr)->GetMiscValue() && !(*itr)->HasSpellClassMask()) - bonusPct += (*itr)->GetAmount(); + if (!spellInfo || aurEff->IsAffectedOnSpell(spellInfo) || aurEff->GetMiscValue() & spellInfo->GetSchoolMask()) + return true; + else if (!aurEff->GetMiscValue() && !aurEff->HasSpellClassMask()) + return true; } else { - if (attacker->ToPlayer()->HasItemFitToSpellRequirements((*itr)->GetSpellInfo())) - bonusPct += (*itr)->GetAmount(); + if (attacker->ToPlayer()->HasItemFitToSpellRequirements(aurEff->GetSpellInfo())) + return true; } - } + return false; + }); float maxArmorPen = 0; if (victim->GetLevel() < 60) @@ -3534,10 +3534,8 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spell, bool Ca if (CanReflect) { int32 reflectchance = victim->GetTotalAuraModifier(SPELL_AURA_REFLECT_SPELLS); - Unit::AuraEffectList const& mReflectSpellsSchool = victim->GetAuraEffectsByType(SPELL_AURA_REFLECT_SPELLS_SCHOOL); - for (Unit::AuraEffectList::const_iterator i = mReflectSpellsSchool.begin(); i != mReflectSpellsSchool.end(); ++i) - if ((*i)->GetMiscValue() & spell->GetSchoolMask()) - reflectchance += (*i)->GetAmount(); + reflectchance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_REFLECT_SPELLS_SCHOOL, spell->GetSchoolMask()); + if (reflectchance > 0 && roll_chance_i(reflectchance)) { // Start triggers for remove charges if need (trigger only for victim, and mark as active spell) @@ -3611,14 +3609,7 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, Spell const* spell, bool CanRef if (CanReflect) { int32 reflectchance = victim->GetTotalAuraModifier(SPELL_AURA_REFLECT_SPELLS); - Unit::AuraEffectList const& mReflectSpellsSchool = victim->GetAuraEffectsByType(SPELL_AURA_REFLECT_SPELLS_SCHOOL); - for (Unit::AuraEffectList::const_iterator i = mReflectSpellsSchool.begin(); i != mReflectSpellsSchool.end(); ++i) - { - if ((*i)->GetMiscValue() & spell->GetSpellSchoolMask()) - { - reflectchance += (*i)->GetAmount(); - } - } + reflectchance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_REFLECT_SPELLS_SCHOOL, spellInfo->GetSchoolMask()); if (reflectchance > 0 && roll_chance_i(reflectchance)) { @@ -3799,14 +3790,10 @@ float Unit::GetUnitCriticalChance(WeaponAttackType attackType, Unit const* victi else crit += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE); - AuraEffectList const& mTotalAuraList = victim->GetAuraEffectsByType(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) + crit += victim->GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER, [this](AuraEffect const* aurEff) { - if (GetGUID() != (*i)->GetCasterGUID()) - continue; - - crit += (*i)->GetAmount(); - } + return GetGUID() == aurEff->GetCasterGUID(); + }); // reduce crit chance from Rating for players if (attackType != RANGED_ATTACK) @@ -4420,16 +4407,85 @@ void Unit::DeMorph() SetDisplayId(GetNativeDisplayId()); } +int32 Unit::GetHighestExclusiveSameEffectSpellGroupValue(AuraEffect const* aurEff, AuraType auraType, bool checkMiscValue /*= false*/, int32 miscValue /*= 0*/) const +{ + int32 val = 0; + SpellSpellGroupMapBounds spellGroup = sSpellMgr->GetSpellSpellGroupMapBounds(aurEff->GetSpellInfo()->GetFirstRankSpell()->Id); + for (SpellSpellGroupMap::const_iterator itr = spellGroup.first; itr != spellGroup.second ; ++itr) + { + if (sSpellMgr->GetSpellGroupStackRule(itr->second) == SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT) + { + AuraEffectList const& auraEffList = GetAuraEffectsByType(auraType); + for (AuraEffectList::const_iterator auraItr = auraEffList.begin(); auraItr != auraEffList.end(); ++auraItr) + { + if (aurEff != (*auraItr) && (!checkMiscValue || (*auraItr)->GetMiscValue() == miscValue) && + sSpellMgr->IsSpellMemberOfSpellGroup((*auraItr)->GetSpellInfo()->Id, itr->second)) + { + // absolute value only + if (abs(val) < abs((*auraItr)->GetAmount())) + val = (*auraItr)->GetAmount(); + } + } + } + } + return val; +} + +bool Unit::IsHighestExclusiveAura(Aura const* aura, bool removeOtherAuraApplications /*= false*/) +{ + for (uint32 i = 0 ; i < MAX_SPELL_EFFECTS; ++i) + if (AuraEffect const* aurEff = aura->GetEffect(i)) + if (!IsHighestExclusiveAuraEffect(aura->GetSpellInfo(), aurEff->GetAuraType(), aurEff->GetAmount(), aura->GetEffectMask(), removeOtherAuraApplications)) + return false; + + return true; +} + +bool Unit::IsHighestExclusiveAuraEffect(SpellInfo const* spellInfo, AuraType auraType, int32 effectAmount, uint8 auraEffectMask, bool removeOtherAuraApplications /*= false*/) +{ + AuraEffectList const& auras = GetAuraEffectsByType(auraType); + for (Unit::AuraEffectList::const_iterator itr = auras.begin(); itr != auras.end();) + { + AuraEffect const* existingAurEff = (*itr); + ++itr; + + if (sSpellMgr->CheckSpellGroupStackRules(spellInfo, existingAurEff->GetSpellInfo()) == SPELL_GROUP_STACK_RULE_EXCLUSIVE_HIGHEST) + { + int32 diff = abs(effectAmount) - abs(existingAurEff->GetAmount()); + if (!diff) + for (int32 i = 0; i < MAX_SPELL_EFFECTS; ++i) + diff += int32((auraEffectMask & (1 << i)) >> i) - int32((existingAurEff->GetBase()->GetEffectMask() & (1 << i)) >> i); + + if (diff > 0) + { + Aura const* base = existingAurEff->GetBase(); + // no removing of area auras from the original owner, as that completely cancels them + if (removeOtherAuraApplications && (!base->IsArea() || base->GetOwner() != this)) + { + if (AuraApplication* aurApp = existingAurEff->GetBase()->GetApplicationOfTarget(GetGUID())) + { + bool hasMoreThanOneEffect = base->HasMoreThanOneEffectForType(auraType); + uint32 removedAuras = m_removedAurasCount; + RemoveAura(aurApp); + if (hasMoreThanOneEffect || m_removedAurasCount > removedAuras) + itr = auras.begin(); + } + } + } + else if (diff < 0) + return false; + } + } + + return true; +} + Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 effMask, Unit* caster, int32* baseAmount /*= nullptr*/, Item* castItem /*= nullptr*/, ObjectGuid casterGUID /*= ObjectGuid::Empty*/, bool periodicReset /*= false*/) { ASSERT(casterGUID || caster); if (!casterGUID) casterGUID = caster->GetGUID(); - // Xinef: Hax for mixology, best solution qq - if (sSpellMgr->GetSpellGroup(newAura->Id) == 1) - return nullptr; - // passive and Incanter's Absorption and auras with different type can stack with themselves any number of times if (!newAura->IsMultiSlotAura()) { @@ -4485,7 +4541,7 @@ void Unit::_AddAura(UnitAura* aura, Unit* caster) ASSERT(!m_cleanupDone); m_ownedAuras.insert(AuraMap::value_type(aura->GetId(), aura)); - _RemoveNoStackAurasDueToAura(aura); + _RemoveNoStackAurasDueToAura(aura, true); if (aura->IsRemoved()) return; @@ -4575,7 +4631,7 @@ void Unit::_ApplyAura(AuraApplication* aurApp, uint8 effMask) { Aura* aura = aurApp->GetBase(); - _RemoveNoStackAurasDueToAura(aura); + _RemoveNoStackAurasDueToAura(aura, false); if (aurApp->GetRemoveMode()) return; @@ -4733,7 +4789,7 @@ void Unit::_UnapplyAura(AuraApplication* aurApp, AuraRemoveMode removeMode) ABORT(); } -void Unit::_RemoveNoStackAurasDueToAura(Aura* aura) +void Unit::_RemoveNoStackAurasDueToAura(Aura* aura, bool owned) { //SpellInfo const* spellProto = aura->GetSpellInfo(); @@ -4743,23 +4799,17 @@ void Unit::_RemoveNoStackAurasDueToAura(Aura* aura) // if (spellProto->IsPassiveStackableWithRanks()) // return; - bool remove = false; - for (AuraApplicationMap::iterator i = m_appliedAuras.begin(); i != m_appliedAuras.end(); ++i) + ASSERT(aura); + if (!IsHighestExclusiveAura(aura)) { - if (remove) - { - remove = false; - i = m_appliedAuras.begin(); - } - - if (aura->CanStackWith(i->second->GetBase(), true)) - continue; - - RemoveAura(i, AURA_REMOVE_BY_DEFAULT); - if (i == m_appliedAuras.end()) - break; - remove = true; + aura->Remove(); + return; } + + if (owned) + RemoveOwnedAuras([aura](Aura const* ownedAura) { return !aura->CanStackWith(ownedAura); }); + else + RemoveAppliedAuras([aura](AuraApplication const* appliedAura) { return !aura->CanStackWith(appliedAura->GetBase()); }); } void Unit::_RegisterAuraEffect(AuraEffect* aurEff, bool apply) @@ -5987,232 +6037,309 @@ uint32 Unit::GetDoTsByCaster(ObjectGuid casterGUID) const return dots; } -int32 Unit::GetTotalAuraModifierAreaExclusive(AuraType auratype) const +int32 Unit::GetTotalAuraModifier(AuraType auraType, std::function<bool(AuraEffect const*)> const& predicate) const { + AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auraType); + if (mTotalAuraList.empty()) + return 0; + + std::map<SpellGroup, int32> sameEffectSpellGroup; int32 modifier = 0; - int32 areaModifier = 0; - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) + for (AuraEffect const* aurEff : mTotalAuraList) { - if ((*i)->GetSpellInfo()->HasAreaAuraEffect()) + if (predicate(aurEff)) { - if (areaModifier < (*i)->GetAmount()) - areaModifier = (*i)->GetAmount(); + // Check if the Aura Effect has a the Same Effect Stack Rule and if so, use the highest amount of that SpellGroup + // If the Aura Effect does not have this Stack Rule, it returns false so we can add to the multiplier as usual + if (!sSpellMgr->AddSameEffectStackRuleSpellGroups(aurEff->GetSpellInfo(), static_cast<uint32>(auraType), aurEff->GetAmount(), sameEffectSpellGroup)) + modifier += aurEff->GetAmount(); } - else - modifier += (*i)->GetAmount(); } - return modifier + areaModifier; -} - -int32 Unit::GetTotalAuraModifier(AuraType auratype) const -{ - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - if (mTotalAuraList.empty()) - return 0; - - int32 modifier = 0; - - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) - modifier += (*i)->GetAmount(); + // Add the highest of the Same Effect Stack Rule SpellGroups to the accumulator + for (auto const& [_, amount] : sameEffectSpellGroup) + modifier += amount; return modifier; } -float Unit::GetTotalAuraMultiplier(AuraType auratype) const +float Unit::GetTotalAuraMultiplier(AuraType auraType, std::function<bool(AuraEffect const*)> const& predicate) const { + AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auraType); + if (mTotalAuraList.empty()) + return 1.0f; + + std::map<SpellGroup, int32> sameEffectSpellGroup; float multiplier = 1.0f; - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) - AddPct(multiplier, (*i)->GetAmount()); + for (AuraEffect const* aurEff : mTotalAuraList) + { + if (predicate(aurEff)) + { + // Check if the Aura Effect has a the Same Effect Stack Rule and if so, use the highest amount of that SpellGroup + // If the Aura Effect does not have this Stack Rule, it returns false so we can add to the multiplier as usual + if (!sSpellMgr->AddSameEffectStackRuleSpellGroups(aurEff->GetSpellInfo(), static_cast<uint32>(auraType), aurEff->GetAmount(), sameEffectSpellGroup)) + AddPct(multiplier, aurEff->GetAmount()); + } + } + + // Add the highest of the Same Effect Stack Rule SpellGroups to the multiplier + for (auto itr = sameEffectSpellGroup.begin(); itr != sameEffectSpellGroup.end(); ++itr) + AddPct(multiplier, itr->second); return multiplier; } -int32 Unit::GetMaxPositiveAuraModifier(AuraType auratype) +int32 Unit::GetMaxPositiveAuraModifier(AuraType auraType, std::function<bool(AuraEffect const*)> const& predicate) const { - int32 modifier = 0; + AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auraType); + if (mTotalAuraList.empty()) + return 0; - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) + int32 modifier = 0; + for (AuraEffect const* aurEff : mTotalAuraList) { - if ((*i)->GetAmount() > modifier) - modifier = (*i)->GetAmount(); + if (predicate(aurEff)) + modifier = std::max(modifier, aurEff->GetAmount()); } return modifier; } -int32 Unit::GetMaxNegativeAuraModifier(AuraType auratype) const +int32 Unit::GetMaxNegativeAuraModifier(AuraType auraType, std::function<bool(AuraEffect const*)> const& predicate) const { - int32 modifier = 0; - - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) - if ((*i)->GetAmount() < modifier) - modifier = (*i)->GetAmount(); - - return modifier; -} + AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auraType); + if (mTotalAuraList.empty()) + return 0; -int32 Unit::GetTotalAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const -{ int32 modifier = 0; - - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) + for (AuraEffect const* aurEff : mTotalAuraList) { - if ((*i)->GetMiscValue()& misc_mask) - modifier += (*i)->GetAmount(); + if (predicate(aurEff)) + modifier = std::min(modifier, aurEff->GetAmount()); } + return modifier; } -float Unit::GetTotalAuraMultiplierByMiscMask(AuraType auratype, uint32 misc_mask) const +int32 Unit::GetTotalAuraModifier(AuraType auraType) const { - float multiplier = 1.0f; + return GetTotalAuraModifier(auraType, [](AuraEffect const* /*aurEff*/) { return true; }); +} - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) - if (((*i)->GetMiscValue() & misc_mask)) - AddPct(multiplier, (*i)->GetAmount()); +float Unit::GetTotalAuraMultiplier(AuraType auraType) const +{ + return GetTotalAuraMultiplier(auraType, [](AuraEffect const* /*aurEff*/) { return true; }); +} - return multiplier; +int32 Unit::GetMaxPositiveAuraModifier(AuraType auraType) const +{ + return GetMaxPositiveAuraModifier(auraType, [](AuraEffect const* /*aurEff*/) { return true; }); } -int32 Unit::GetMaxPositiveAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask, const AuraEffect* except) const +int32 Unit::GetMaxNegativeAuraModifier(AuraType auraType) const { - int32 modifier = 0; + return GetMaxNegativeAuraModifier(auraType, [](AuraEffect const* /*aurEff*/) { return true; }); +} - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) +int32 Unit::GetTotalAuraModifierByMiscMask(AuraType auraType, uint32 miscMask) const +{ + return GetTotalAuraModifier(auraType, [miscMask](AuraEffect const* aurEff) -> bool { - if (except != (*i) && (*i)->GetMiscValue()& misc_mask && (*i)->GetAmount() > modifier) - modifier = (*i)->GetAmount(); - } - - return modifier; + if ((aurEff->GetMiscValue() & miscMask) != 0) + return true; + return false; + }); } -int32 Unit::GetMaxNegativeAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const +float Unit::GetTotalAuraMultiplierByMiscMask(AuraType auraType, uint32 miscMask) const { - int32 modifier = 0; - - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) + return GetTotalAuraMultiplier(auraType, [miscMask](AuraEffect const* aurEff) -> bool { - if ((*i)->GetMiscValue()& misc_mask && (*i)->GetAmount() < modifier) - modifier = (*i)->GetAmount(); - } - - return modifier; + if ((aurEff->GetMiscValue() & miscMask) != 0) + return true; + return false; + }); } -int32 Unit::GetTotalAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const +int32 Unit::GetMaxPositiveAuraModifierByMiscMask(AuraType auraType, uint32 miscMask, AuraEffect const* except /*= nullptr*/) const { - int32 modifier = 0; - - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) - if ((*i)->GetMiscValue() == misc_value) - modifier += (*i)->GetAmount(); - - return modifier; + return GetMaxPositiveAuraModifier(auraType, [miscMask, except](AuraEffect const* aurEff) -> bool + { + if (except != aurEff && (aurEff->GetMiscValue() & miscMask) != 0) + return true; + return false; + }); } -float Unit::GetTotalAuraMultiplierByMiscValue(AuraType auratype, int32 misc_value) const +int32 Unit::GetMaxNegativeAuraModifierByMiscMask(AuraType auraType, uint32 miscMask) const { - float multiplier = 1.0f; - - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) - if ((*i)->GetMiscValue() == misc_value) - AddPct(multiplier, (*i)->GetAmount()); - - return multiplier; + return GetMaxNegativeAuraModifier(auraType, [miscMask](AuraEffect const* aurEff) -> bool + { + if ((aurEff->GetMiscValue() & miscMask) != 0) + return true; + return false; + }); } -int32 Unit::GetMaxPositiveAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const +int32 Unit::GetTotalAuraModifierByMiscValue(AuraType auraType, int32 miscValue) const { - int32 modifier = 0; - - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) + return GetTotalAuraModifier(auraType, [miscValue](AuraEffect const* aurEff) -> bool { - if ((*i)->GetMiscValue() == misc_value && (*i)->GetAmount() > modifier) - modifier = (*i)->GetAmount(); - } + if (aurEff->GetMiscValue() == miscValue) + return true; + return false; + }); +} - return modifier; +float Unit::GetTotalAuraMultiplierByMiscValue(AuraType auraType, int32 miscValue) const +{ + return GetTotalAuraMultiplier(auraType, [miscValue](AuraEffect const* aurEff) -> bool + { + if (aurEff->GetMiscValue() == miscValue) + return true; + return false; + }); } -int32 Unit::GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const +int32 Unit::GetMaxPositiveAuraModifierByMiscValue(AuraType auraType, int32 miscValue) const { - int32 modifier = 0; + return GetMaxPositiveAuraModifier(auraType, [miscValue](AuraEffect const* aurEff) -> bool + { + if (aurEff->GetMiscValue() == miscValue) + return true; + return false; + }); +} - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) +int32 Unit::GetMaxNegativeAuraModifierByMiscValue(AuraType auraType, int32 miscValue) const +{ + return GetMaxNegativeAuraModifier(auraType, [miscValue](AuraEffect const* aurEff) -> bool { - if ((*i)->GetMiscValue() == misc_value && (*i)->GetAmount() < modifier) - modifier = (*i)->GetAmount(); - } + if (aurEff->GetMiscValue() == miscValue) + return true; + return false; + }); +} - return modifier; +int32 Unit::GetTotalAuraModifierByAffectMask(AuraType auraType, SpellInfo const* affectedSpell) const +{ + return GetTotalAuraModifier(auraType, [affectedSpell](AuraEffect const* aurEff) -> bool + { + if (aurEff->IsAffectedOnSpell(affectedSpell)) + return true; + return false; + }); } -int32 Unit::GetTotalAuraModifierByAffectMask(AuraType auratype, SpellInfo const* affectedSpell) const +float Unit::GetTotalAuraMultiplierByAffectMask(AuraType auraType, SpellInfo const* affectedSpell) const { - int32 modifier = 0; + return GetTotalAuraMultiplier(auraType, [affectedSpell](AuraEffect const* aurEff) -> bool + { + if (aurEff->IsAffectedOnSpell(affectedSpell)) + return true; + return false; + }); +} - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) - if ((*i)->IsAffectedOnSpell(affectedSpell)) - modifier += (*i)->GetAmount(); +int32 Unit::GetMaxPositiveAuraModifierByAffectMask(AuraType auraType, SpellInfo const* affectedSpell) const +{ + return GetMaxPositiveAuraModifier(auraType, [affectedSpell](AuraEffect const* aurEff) -> bool + { + if (aurEff->IsAffectedOnSpell(affectedSpell)) + return true; + return false; + }); +} - return modifier; +int32 Unit::GetMaxNegativeAuraModifierByAffectMask(AuraType auraType, SpellInfo const* affectedSpell) const +{ + return GetMaxNegativeAuraModifier(auraType, [affectedSpell](AuraEffect const* aurEff) -> bool + { + if (aurEff->IsAffectedOnSpell(affectedSpell)) + return true; + return false; + }); } -float Unit::GetTotalAuraMultiplierByAffectMask(AuraType auratype, SpellInfo const* affectedSpell) const +void Unit::UpdateResistanceBuffModsMod(SpellSchools school) { - float multiplier = 1.0f; + float modPos = 0.0f; + float modNeg = 0.0f; - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) - if ((*i)->IsAffectedOnSpell(affectedSpell)) - AddPct(multiplier, (*i)->GetAmount()); + // these auras are always positive + modPos = GetMaxPositiveAuraModifierByMiscMask(SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE, 1 << school); + modPos += GetTotalAuraModifier(SPELL_AURA_MOD_RESISTANCE, [school](AuraEffect const* aurEff) -> bool + { + if ((aurEff->GetMiscValue() & (1 << school)) && aurEff->GetAmount() > 0) + return true; + return false; + }); - return multiplier; + modNeg = GetTotalAuraModifier(SPELL_AURA_MOD_RESISTANCE, [school](AuraEffect const* aurEff) -> bool + { + if ((aurEff->GetMiscValue() & (1 << school)) && aurEff->GetAmount() < 0) + return true; + return false; + }); + + float factor = GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_RESISTANCE_PCT, 1 << school); + modPos *= factor; + modNeg *= factor; + + SetFloatValue(UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE + AsUnderlyingType(school), modPos); + SetFloatValue(UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE + AsUnderlyingType(school), modNeg); } -int32 Unit::GetMaxPositiveAuraModifierByAffectMask(AuraType auratype, SpellInfo const* affectedSpell) const +void Unit::UpdateStatBuffMod(Stats stat) { - int32 modifier = 0; + float modPos = 0.0f; + float modNeg = 0.0f; + float factor = 0.0f; - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) + UnitMods const unitMod = static_cast<UnitMods>(UNIT_MOD_STAT_START + AsUnderlyingType(stat)); + + // includes value from items and enchantments + float modValue = GetFlatModifierValue(unitMod, BASE_VALUE); + if (modValue > 0.f) + modPos += modValue; + else + modNeg += modValue; + + modPos += GetTotalAuraModifier(SPELL_AURA_MOD_STAT, [stat](AuraEffect const* aurEff) -> bool { - if ((*i)->IsAffectedOnSpell(affectedSpell) && (*i)->GetAmount() > modifier) - modifier = (*i)->GetAmount(); - } + if ((aurEff->GetMiscValue() < 0 || aurEff->GetMiscValue() == stat) && aurEff->GetAmount() > 0) + return true; + return false; + }); - return modifier; -} + modNeg += GetTotalAuraModifier(SPELL_AURA_MOD_STAT, [stat](AuraEffect const* aurEff) -> bool + { + if ((aurEff->GetMiscValue() < 0 || aurEff->GetMiscValue() == stat) && aurEff->GetAmount() < 0) + return true; + return false; + }); -int32 Unit::GetMaxNegativeAuraModifierByAffectMask(AuraType auratype, SpellInfo const* affectedSpell) const -{ - int32 modifier = 0; + factor = GetTotalAuraMultiplier(SPELL_AURA_MOD_PERCENT_STAT, [stat](AuraEffect const* aurEff) -> bool + { + if (aurEff->GetMiscValue() == -1 || aurEff->GetMiscValue() == stat) + return true; + return false; + }); - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) + factor *= GetTotalAuraMultiplier(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE, [stat](AuraEffect const* aurEff) -> bool { - if ((*i)->IsAffectedOnSpell(affectedSpell) && (*i)->GetAmount() < modifier) - modifier = (*i)->GetAmount(); - } + if (aurEff->GetMiscValue() == -1 || aurEff->GetMiscValue() == stat) + return true; + return false; + }); - return modifier; + modPos *= factor; + modNeg *= factor; + + SetFloatValue(UNIT_FIELD_POSSTAT0 + AsUnderlyingType(stat), modPos); + SetFloatValue(UNIT_FIELD_NEGSTAT0 + AsUnderlyingType(stat), modNeg); } void Unit::_RegisterDynObject(DynamicObject* dynObj) @@ -11387,44 +11514,41 @@ float Unit::SpellPctDamageModsDone(Unit* victim, SpellInfo const* spellProto, Da // Done total percent damage auras float DoneTotalMod = 1.0f; - AuraEffectList const& mModDamagePercentDone = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); - for (AuraEffectList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i) + DoneTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, [spellProto, this, damagetype](AuraEffect const* aurEff) { // prevent apply mods from weapon specific case to non weapon specific spells (Example: thunder clap and two-handed weapon specialization) - if (spellProto->EquippedItemClass == -1 && (*i)->GetSpellInfo()->EquippedItemClass != -1 && - !(*i)->GetSpellInfo()->HasAttribute(SPELL_ATTR5_AURA_AFFECTS_NOT_JUST_REQ_EQUIPPED_ITEM) && (*i)->GetMiscValue() == SPELL_SCHOOL_MASK_NORMAL) + if (spellProto->EquippedItemClass == -1 && aurEff->GetSpellInfo()->EquippedItemClass != -1 && + !aurEff->GetSpellInfo()->HasAttribute(SPELL_ATTR5_AURA_AFFECTS_NOT_JUST_REQ_EQUIPPED_ITEM) && aurEff->GetMiscValue() == SPELL_SCHOOL_MASK_NORMAL) { - continue; - } - - if (!spellProto->ValidateAttribute6SpellDamageMods(this, *i, damagetype == DOT)) - continue; + return false; + } - if (!sScriptMgr->IsNeedModSpellDamagePercent(this, *i, DoneTotalMod, spellProto)) - continue; + if (!spellProto->ValidateAttribute6SpellDamageMods(this, aurEff, damagetype == DOT)) + return false; - if ((*i)->GetMiscValue() & spellProto->GetSchoolMask()) + if (aurEff->GetMiscValue() & spellProto->GetSchoolMask()) { - if ((*i)->GetSpellInfo()->EquippedItemClass == -1) - AddPct(DoneTotalMod, (*i)->GetAmount()); - else if (!(*i)->GetSpellInfo()->HasAttribute(SPELL_ATTR5_AURA_AFFECTS_NOT_JUST_REQ_EQUIPPED_ITEM) && ((*i)->GetSpellInfo()->EquippedItemSubClassMask == 0)) - AddPct(DoneTotalMod, (*i)->GetAmount()); - else if (ToPlayer() && ToPlayer()->HasItemFitToSpellRequirements((*i)->GetSpellInfo())) - AddPct(DoneTotalMod, (*i)->GetAmount()); + if (aurEff->GetSpellInfo()->EquippedItemClass == -1) + return true; + else if (!aurEff->GetSpellInfo()->HasAttribute(SPELL_ATTR5_AURA_AFFECTS_NOT_JUST_REQ_EQUIPPED_ITEM) && (aurEff->GetSpellInfo()->EquippedItemSubClassMask == 0)) + return true; + else if (ToPlayer() && ToPlayer()->HasItemFitToSpellRequirements(aurEff->GetSpellInfo())) + return true; } - } + return false; + }); uint32 creatureTypeMask = victim->GetCreatureTypeMask(); - AuraEffectList const& mDamageDoneVersus = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS); - for (AuraEffectList::const_iterator i = mDamageDoneVersus.begin(); i != mDamageDoneVersus.end(); ++i) - if ((creatureTypeMask & uint32((*i)->GetMiscValue())) && spellProto->ValidateAttribute6SpellDamageMods(this, *i, damagetype == DOT)) - AddPct(DoneTotalMod, (*i)->GetAmount()); + DoneTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS, [creatureTypeMask, spellProto, damagetype, this](AuraEffect const* aurEff) + { + return creatureTypeMask & aurEff->GetMiscValue() && spellProto->ValidateAttribute6SpellDamageMods(this, aurEff, damagetype == DOT); + }); // bonus against aurastate - AuraEffectList const& mDamageDoneVersusAurastate = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS_AURASTATE); - for (AuraEffectList::const_iterator i = mDamageDoneVersusAurastate.begin(); i != mDamageDoneVersusAurastate.end(); ++i) - if (victim->HasAuraState(AuraStateType((*i)->GetMiscValue())) && spellProto->ValidateAttribute6SpellDamageMods(this, *i, damagetype == DOT)) - AddPct(DoneTotalMod, (*i)->GetAmount()); + DoneTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS_AURASTATE, [victim, spellProto, damagetype, this](AuraEffect const* aurEff) + { + return victim->HasAuraState(AuraStateType(aurEff->GetMiscValue())) && spellProto->ValidateAttribute6SpellDamageMods(this, aurEff, damagetype == DOT); + }); // done scripted mod (take it from owner) Unit* owner = GetOwner() ? GetOwner() : this; @@ -11876,22 +12000,19 @@ uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, ui // from positive and negative SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN // multiplicative bonus, for example Dispersion + Shadowform (0.10*0.85=0.085) - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) - if (((*i)->GetMiscValue() & spellProto->GetSchoolMask())) - if (spellProto->ValidateAttribute6SpellDamageMods(caster, *i, damagetype == DOT)) - AddPct(TakenTotalMod, (*i)->GetAmount()); + TakenTotalMod *= GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, spellProto->GetSchoolMask()); TakenTotalMod = processDummyAuras(TakenTotalMod); // From caster spells if (caster) { - AuraEffectList const& mOwnerTaken = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_FROM_CASTER); - for (AuraEffectList::const_iterator i = mOwnerTaken.begin(); i != mOwnerTaken.end(); ++i) - if ((*i)->GetCasterGUID() == caster->GetGUID() && (*i)->IsAffectedOnSpell(spellProto)) - if (spellProto->ValidateAttribute6SpellDamageMods(caster, *i, damagetype == DOT)) - AddPct(TakenTotalMod, (*i)->GetAmount()); + TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_DAMAGE_FROM_CASTER, [caster, spellProto](AuraEffect const* aurEff) -> bool + { + if (aurEff->GetCasterGUID() == caster->GetGUID() && aurEff->IsAffectedOnSpell(spellProto)) + return true; + return false; + }); } if (uint32 mechanicMask = spellProto->GetAllEffectsMechanicMask()) @@ -12014,16 +12135,14 @@ float Unit::processDummyAuras(float TakenTotalMod) const int32 Unit::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask) { - int32 DoneAdvertisedBenefit = 0; - - AuraEffectList const& mDamageDone = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE); - for (AuraEffectList::const_iterator i = mDamageDone.begin(); i != mDamageDone.end(); ++i) - if (((*i)->GetMiscValue() & schoolMask) != 0 && - (*i)->GetSpellInfo()->EquippedItemClass == -1 && + int32 DoneAdvertisedBenefit = GetTotalAuraModifier(SPELL_AURA_MOD_DAMAGE_DONE, [schoolMask](AuraEffect const* aurEff) + { + return aurEff->GetMiscValue() & schoolMask && // -1 == any item class (not wand then) - (*i)->GetSpellInfo()->EquippedItemInventoryTypeMask == 0) - // 0 == any inventory type (not wand then) - DoneAdvertisedBenefit += (*i)->GetAmount(); + aurEff->GetSpellInfo()->EquippedItemClass == -1 && + // 0 == any inventory type (not wand then) + aurEff->GetSpellInfo()->EquippedItemInventoryTypeMask == 0; + }); if (IsPlayer()) { @@ -12043,31 +12162,20 @@ int32 Unit::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask) } } // ... and attack power - AuraEffectList const& mDamageDonebyAP = GetAuraEffectsByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER); - for (AuraEffectList::const_iterator i = mDamageDonebyAP.begin(); i != mDamageDonebyAP.end(); ++i) - if ((*i)->GetMiscValue() & schoolMask) - DoneAdvertisedBenefit += int32(CalculatePct(GetTotalAttackPowerValue(BASE_ATTACK), (*i)->GetAmount())); + DoneAdvertisedBenefit += int32(CalculatePct(GetTotalAttackPowerValue(BASE_ATTACK), GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER, schoolMask))); } return DoneAdvertisedBenefit; } int32 Unit::SpellBaseDamageBonusTaken(SpellSchoolMask schoolMask, bool isDoT) { - int32 TakenAdvertisedBenefit = 0; - - AuraEffectList const& mDamageTaken = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_TAKEN); - for (AuraEffectList::const_iterator i = mDamageTaken.begin(); i != mDamageTaken.end(); ++i) - if (((*i)->GetMiscValue() & schoolMask) != 0) - { + return GetTotalAuraModifier(SPELL_AURA_MOD_DAMAGE_TAKEN, [schoolMask, isDoT](AuraEffect const* aurEff) + { + return (aurEff->GetMiscValue() & schoolMask) != 0 // Xinef: if we have DoT damage type and aura has charges, check if it affects DoTs // Xinef: required for hemorrhage & rupture / garrote - if (isDoT && (*i)->GetBase()->IsUsingCharges() && !((*i)->GetSpellInfo()->ProcFlags & PROC_FLAG_TAKEN_PERIODIC)) - continue; - - TakenAdvertisedBenefit += (*i)->GetAmount(); - } - - return TakenAdvertisedBenefit; + && !(isDoT && aurEff->GetBase()->IsUsingCharges() && aurEff->GetSpellInfo()->ProcFlags & PROC_FLAG_TAKEN_PERIODIC); + }); } float Unit::SpellDoneCritChance(Unit const* /*victim*/, SpellInfo const* spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType, bool skipEffectCheck) const @@ -12338,14 +12446,10 @@ float Unit::SpellTakenCritChance(Unit const* caster, SpellInfo const* spellProto if (caster) { - AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER); - for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i) + crit_chance += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER, [caster](AuraEffect const* aurEff) { - if (caster->GetGUID() != (*i)->GetCasterGUID()) - continue; - - crit_chance += (*i)->GetAmount(); - } + return caster->GetGUID() == aurEff->GetCasterGUID(); + }); } // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE @@ -12458,14 +12562,7 @@ float Unit::SpellPctHealingModsDone(Unit* victim, SpellInfo const* spellProto, D float DoneTotalMod = 1.0f; // Healing done percent - AuraEffectList const& mHealingDonePct = GetAuraEffectsByType(SPELL_AURA_MOD_HEALING_DONE_PERCENT); - for (auto const& auraEff : mHealingDonePct) - { - if (!sScriptMgr->IsNeedModHealPercent(this, auraEff, DoneTotalMod, spellProto)) - continue; - - AddPct(DoneTotalMod, auraEff->GetAmount()); - } + DoneTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALING_DONE_PERCENT); // done scripted mod (take it from owner) Unit* owner = GetOwner() ? GetOwner() : this; @@ -12631,6 +12728,8 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui case SPELL_AURA_PERIODIC_HEALTH_FUNNEL: DoneTotal = 0; break; + default: + break; } if (spellProto->Effects[i].Effect == SPELL_EFFECT_HEALTH_LEECH) DoneTotal = 0; @@ -12672,7 +12771,7 @@ uint32 Unit::SpellHealingBonusTaken(Unit* caster, SpellInfo const* spellProto, u int32 TakenTotal = 0; // Taken fixed damage bonus auras - int32 TakenAdvertisedBenefit = SpellBaseHealingBonusTaken(spellProto->GetSchoolMask()); + int32 TakenAdvertisedBenefit = GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_HEALING, spellProto->GetSchoolMask()); // Nourish cast, glyph of nourish if (spellProto->SpellFamilyName == SPELLFAMILY_DRUID && spellProto->SpellFamilyFlags[1] & 0x2000000 && caster) @@ -12746,10 +12845,10 @@ uint32 Unit::SpellHealingBonusTaken(Unit* caster, SpellInfo const* spellProto, u if (caster) { - AuraEffectList const& mHealingGet = GetAuraEffectsByType(SPELL_AURA_MOD_HEALING_RECEIVED); - for (AuraEffectList::const_iterator i = mHealingGet.begin(); i != mHealingGet.end(); ++i) - if (caster->GetGUID() == (*i)->GetCasterGUID() && (*i)->IsAffectedOnSpell(spellProto)) - AddPct(TakenTotalMod, (*i)->GetAmount()); + TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALING_RECEIVED, [caster, spellProto](AuraEffect const* aurEff) + { + return caster->GetGUID() == aurEff->GetCasterGUID() && aurEff->IsAffectedOnSpell(spellProto); + }); } for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) @@ -12761,6 +12860,8 @@ uint32 Unit::SpellHealingBonusTaken(Unit* caster, SpellInfo const* spellProto, u case SPELL_AURA_PERIODIC_HEALTH_FUNNEL: TakenTotal = 0; break; + default: + break; } if (spellProto->Effects[i].Effect == SPELL_EFFECT_HEALTH_LEECH) TakenTotal = 0; @@ -12782,10 +12883,10 @@ int32 Unit::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask) { int32 AdvertisedBenefit = 0; - AuraEffectList const& mHealingDone = GetAuraEffectsByType(SPELL_AURA_MOD_HEALING_DONE); - for (AuraEffectList::const_iterator i = mHealingDone.begin(); i != mHealingDone.end(); ++i) - if (!(*i)->GetMiscValue() || ((*i)->GetMiscValue() & schoolMask) != 0) - AdvertisedBenefit += (*i)->GetAmount(); + AdvertisedBenefit += GetTotalAuraModifier(SPELL_AURA_MOD_HEALING_DONE, [schoolMask](AuraEffect const* aurEff) + { + return !aurEff->GetMiscValue() || (aurEff->GetMiscValue() & schoolMask) != 0; + }); // Healing bonus of spirit, intellect and strength if (IsPlayer()) @@ -12804,26 +12905,11 @@ int32 Unit::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask) } // ... and attack power - AuraEffectList const& mHealingDonebyAP = GetAuraEffectsByType(SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER); - for (AuraEffectList::const_iterator i = mHealingDonebyAP.begin(); i != mHealingDonebyAP.end(); ++i) - if ((*i)->GetMiscValue() & schoolMask) - AdvertisedBenefit += int32(CalculatePct(GetTotalAttackPowerValue(BASE_ATTACK), (*i)->GetAmount())); + AdvertisedBenefit += int32(CalculatePct(GetTotalAttackPowerValue(BASE_ATTACK), GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER, schoolMask))); } return AdvertisedBenefit; } -int32 Unit::SpellBaseHealingBonusTaken(SpellSchoolMask schoolMask) -{ - int32 AdvertisedBenefit = 0; - - AuraEffectList const& mDamageTaken = GetAuraEffectsByType(SPELL_AURA_MOD_HEALING); - for (AuraEffectList::const_iterator i = mDamageTaken.begin(); i != mDamageTaken.end(); ++i) - if (((*i)->GetMiscValue() & schoolMask) != 0) - AdvertisedBenefit += (*i)->GetAmount(); - - return AdvertisedBenefit; -} - bool Unit::IsImmunedToDamage(SpellSchoolMask meleeSchoolMask) const { if (meleeSchoolMask == SPELL_SCHOOL_MASK_NONE) @@ -13172,10 +13258,7 @@ uint32 Unit::MeleeDamageBonusDone(Unit* victim, uint32 pdamage, WeaponAttackType int32 DoneFlatBenefit = 0; // ..done - AuraEffectList const& mDamageDoneCreature = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE); - for (AuraEffectList::const_iterator i = mDamageDoneCreature.begin(); i != mDamageDoneCreature.end(); ++i) - if (creatureTypeMask & uint32((*i)->GetMiscValue())) - DoneFlatBenefit += (*i)->GetAmount(); + DoneFlatBenefit += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE, creatureTypeMask); // ..done // SPELL_AURA_MOD_DAMAGE_DONE included in weapon damage @@ -13188,20 +13271,14 @@ uint32 Unit::MeleeDamageBonusDone(Unit* victim, uint32 pdamage, WeaponAttackType APbonus += victim->GetTotalAuraModifier(SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS); // ..done (base at attack power and creature type) - AuraEffectList const& mCreatureAttackPower = GetAuraEffectsByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS); - for (AuraEffectList::const_iterator i = mCreatureAttackPower.begin(); i != mCreatureAttackPower.end(); ++i) - if (creatureTypeMask & uint32((*i)->GetMiscValue())) - APbonus += (*i)->GetAmount(); + APbonus += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS, creatureTypeMask); } else { APbonus += victim->GetTotalAuraModifier(SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS); // ..done (base at attack power and creature type) - AuraEffectList const& mCreatureAttackPower = GetAuraEffectsByType(SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS); - for (AuraEffectList::const_iterator i = mCreatureAttackPower.begin(); i != mCreatureAttackPower.end(); ++i) - if (creatureTypeMask & uint32((*i)->GetMiscValue())) - APbonus += (*i)->GetAmount(); + APbonus += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS, creatureTypeMask); } if (APbonus != 0) // Can be negative @@ -13224,37 +13301,34 @@ uint32 Unit::MeleeDamageBonusDone(Unit* victim, uint32 pdamage, WeaponAttackType if (!(damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL)) { // Some spells don't benefit from pct done mods - AuraEffectList const& mModDamagePercentDone = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); - for (AuraEffectList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i) - { - if (!spellProto || (spellProto->ValidateAttribute6SpellDamageMods(this, *i, false) && - sScriptMgr->IsNeedModMeleeDamagePercent(this, *i, DoneTotalMod, spellProto))) + DoneTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, [spellProto, this, damageSchoolMask](AuraEffect const* aurEff) + { + if (!spellProto || (spellProto->ValidateAttribute6SpellDamageMods(this, aurEff, false))) { - if (((*i)->GetMiscValue() & damageSchoolMask)) + if ((aurEff->GetMiscValue() & damageSchoolMask)) { - if ((*i)->GetSpellInfo()->EquippedItemClass == -1) - AddPct(DoneTotalMod, (*i)->GetAmount()); - else if (!(*i)->GetSpellInfo()->HasAttribute(SPELL_ATTR5_AURA_AFFECTS_NOT_JUST_REQ_EQUIPPED_ITEM) && ((*i)->GetSpellInfo()->EquippedItemSubClassMask == 0)) - AddPct(DoneTotalMod, (*i)->GetAmount()); - else if (ToPlayer() && ToPlayer()->HasItemFitToSpellRequirements((*i)->GetSpellInfo())) - AddPct(DoneTotalMod, (*i)->GetAmount()); + if (aurEff->GetSpellInfo()->EquippedItemClass == -1) + return true; + else if (!aurEff->GetSpellInfo()->HasAttribute(SPELL_ATTR5_AURA_AFFECTS_NOT_JUST_REQ_EQUIPPED_ITEM) && (aurEff->GetSpellInfo()->EquippedItemSubClassMask == 0)) + return true; + else if (ToPlayer() && ToPlayer()->HasItemFitToSpellRequirements(aurEff->GetSpellInfo())) + return true; } } - } + return false; + }); } - AuraEffectList const& mDamageDoneVersus = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS); - for (AuraEffectList::const_iterator i = mDamageDoneVersus.begin(); i != mDamageDoneVersus.end(); ++i) - if (creatureTypeMask & uint32((*i)->GetMiscValue())) - if (!spellProto || spellProto->ValidateAttribute6SpellDamageMods(this, *i, false)) - AddPct(DoneTotalMod, (*i)->GetAmount()); + DoneTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS, [creatureTypeMask, spellProto, this](AuraEffect const* aurEff) + { + return (creatureTypeMask & aurEff->GetMiscValue() && (!spellProto || spellProto->ValidateAttribute6SpellDamageMods(this, aurEff, false))); + }); // bonus against aurastate - AuraEffectList const& mDamageDoneVersusAurastate = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS_AURASTATE); - for (AuraEffectList::const_iterator i = mDamageDoneVersusAurastate.begin(); i != mDamageDoneVersusAurastate.end(); ++i) - if (victim->HasAuraState(AuraStateType((*i)->GetMiscValue()))) - if (!spellProto || spellProto->ValidateAttribute6SpellDamageMods(this, *i, false)) - AddPct(DoneTotalMod, (*i)->GetAmount()); + DoneTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS_AURASTATE, [victim, spellProto, this](AuraEffect const* aurEff) + { + return (victim->HasAuraState(AuraStateType(aurEff->GetMiscValue())) && (!spellProto || spellProto->ValidateAttribute6SpellDamageMods(this, aurEff, false))); + }); // done scripted mod (take it from owner) Unit* owner = GetOwner() ? GetOwner() : this; @@ -13361,10 +13435,7 @@ uint32 Unit::MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackT int32 TakenFlatBenefit = 0; // ..taken - AuraEffectList const& mDamageTaken = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_TAKEN); - for (AuraEffectList::const_iterator i = mDamageTaken.begin(); i != mDamageTaken.end(); ++i) - if ((*i)->GetMiscValue() & damageSchoolMask) - TakenFlatBenefit += (*i)->GetAmount(); + TakenFlatBenefit += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_DAMAGE_TAKEN, damageSchoolMask); if (attType != RANGED_ATTACK) TakenFlatBenefit += GetTotalAuraModifier(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN); @@ -13380,10 +13451,10 @@ uint32 Unit::MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackT if (spellProto) { // From caster spells - AuraEffectList const& mOwnerTaken = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_FROM_CASTER); - for (AuraEffectList::const_iterator i = mOwnerTaken.begin(); i != mOwnerTaken.end(); ++i) - if ((*i)->GetCasterGUID() == attacker->GetGUID() && (*i)->IsAffectedOnSpell(spellProto)) - AddPct(TakenTotalMod, (*i)->GetAmount()); + TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_DAMAGE_FROM_CASTER, [attacker, spellProto](AuraEffect const* aurEff) + { + return attacker->GetGUID() == aurEff->GetCasterGUID() && aurEff->IsAffectedOnSpell(spellProto); + }); // Mod damage from spell mechanic uint32 mechanicMask = spellProto->GetAllEffectsMechanicMask(); @@ -13394,10 +13465,12 @@ uint32 Unit::MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackT if (mechanicMask) { - AuraEffectList const& mDamageDoneMechanic = GetAuraEffectsByType(SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT); - for (AuraEffectList::const_iterator i = mDamageDoneMechanic.begin(); i != mDamageDoneMechanic.end(); ++i) - if (mechanicMask & uint32(1 << ((*i)->GetMiscValue()))) - AddPct(TakenTotalMod, (*i)->GetAmount()); + TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT, [mechanicMask](AuraEffect const* aurEff) -> bool + { + if (mechanicMask & uint32(1 << (aurEff->GetMiscValue()))) + return true; + return false; + }); } } @@ -13414,15 +13487,11 @@ uint32 Unit::MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackT if (attType != RANGED_ATTACK) { - AuraEffectList const& mModMeleeDamageTakenPercent = GetAuraEffectsByType(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT); - for (AuraEffectList::const_iterator i = mModMeleeDamageTakenPercent.begin(); i != mModMeleeDamageTakenPercent.end(); ++i) - AddPct(TakenTotalMod, (*i)->GetAmount()); + TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT); } else { - AuraEffectList const& mModRangedDamageTakenPercent = GetAuraEffectsByType(SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT); - for (AuraEffectList::const_iterator i = mModRangedDamageTakenPercent.begin(); i != mModRangedDamageTakenPercent.end(); ++i) - AddPct(TakenTotalMod, (*i)->GetAmount()); + TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT); } // No positive taken bonus, custom attr @@ -14021,7 +14090,22 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo if (repThisToTarget > REP_NEUTRAL || (repTargetToThis = target->GetReactionTo(this)) > REP_NEUTRAL) - return false; + { + // contested guards can attack contested PvP players even though players may be friendly + if (!target->IsControlledByPlayer()) + return false; + + bool isContestedGuard = false; + if (FactionTemplateEntry const* entry = GetFactionTemplateEntry()) + isContestedGuard = entry->factionFlags & FACTION_TEMPLATE_FLAG_ATTACK_PVP_ACTIVE_PLAYERS; + + bool isContestedPvp = false; + if (Player const* player = target->GetCharmerOrOwnerPlayerOrPlayerItself()) + isContestedPvp = player->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP); + + if (!isContestedGuard && !isContestedPvp) + return false; + } // Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar) if (repThisToTarget == REP_NEUTRAL && @@ -14272,15 +14356,6 @@ int32 Unit::ModifyPower(Powers power, int32 dVal, bool withPowerUpdate /*= true* return gain; } -// returns negative amount on power reduction -int32 Unit::ModifyPowerPct(Powers power, float pct, bool apply) -{ - float amount = (float)GetMaxPower(power); - ApplyPercentModFloatVar(amount, pct, apply); - - return ModifyPower(power, (int32)amount - (int32)GetMaxPower(power)); -} - bool Unit::IsAlwaysVisibleFor(WorldObject const* seer) const { if (WorldObject::IsAlwaysVisibleFor(seer)) @@ -15230,30 +15305,100 @@ uint32 Unit::GetCreatureType() const ######## ######## #######################################*/ -bool Unit::HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, float amount, bool apply) +bool Unit::HandleStatFlatModifier(UnitMods unitMod, UnitModifierFlatType modifierType, float amount, bool apply) { - if (unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_END) + if (unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_FLAT_END) { LOG_ERROR("entities.unit", "ERROR in HandleStatModifier(): non-existing UnitMods or wrong UnitModifierType!"); return false; } + if (!amount) + return false; + switch (modifierType) { case BASE_VALUE: case TOTAL_VALUE: - m_auraModifiersGroup[unitMod][modifierType] += apply ? amount : -amount; + m_auraFlatModifiersGroup[unitMod][modifierType] += apply ? amount : -amount; break; + default: + break; + } + + UpdateUnitMod(unitMod); + return true; +} + +// Usage outside of AuraEffect Handlers is discouraged as the value will be lost when auras change. Use an Aura instead. +void Unit::ApplyStatPctModifier(UnitMods unitMod, UnitModifierPctType modifierType, float pct) +{ + if (unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_PCT_END) + { + LOG_ERROR("entities.unit", "ERROR in ApplyStatPctModifier(): non-existing UnitMods or wrong UnitModifierType!"); + return; + } + + if (!pct) + return; + + switch (modifierType) + { case BASE_PCT: case TOTAL_PCT: - ApplyPercentModFloatVar(m_auraModifiersGroup[unitMod][modifierType], amount, apply); + AddPct(m_auraPctModifiersGroup[unitMod][modifierType], pct); break; default: break; } - if (!CanModifyStats()) - return false; + UpdateUnitMod(unitMod); +} + +void Unit::SetStatFlatModifier(UnitMods unitMod, UnitModifierFlatType modifierType, float val) +{ + if (m_auraFlatModifiersGroup[unitMod][modifierType] == val) + return; + + m_auraFlatModifiersGroup[unitMod][modifierType] = val; + UpdateUnitMod(unitMod); +} + +void Unit::SetStatPctModifier(UnitMods unitMod, UnitModifierPctType modifierType, float val) +{ + if (m_auraPctModifiersGroup[unitMod][modifierType] == val) + return; + + m_auraPctModifiersGroup[unitMod][modifierType] = val; + UpdateUnitMod(unitMod); +} + +float Unit::GetFlatModifierValue(UnitMods unitMod, UnitModifierFlatType modifierType) const +{ + if (unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_FLAT_END) + { + LOG_ERROR("entities.unit", "attempt to access non-existing modifier value from UnitMods!"); + return 0.0f; + } + + return m_auraFlatModifiersGroup[unitMod][modifierType]; +} + +float Unit::GetPctModifierValue(UnitMods unitMod, UnitModifierPctType modifierType) const +{ + if (unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_PCT_END) + { + LOG_ERROR("entities.unit", "attempt to access non-existing modifier value from UnitMods!"); + return 0.0f; + } + + return m_auraPctModifiersGroup[unitMod][modifierType]; +} + +void Unit::UpdateUnitMod(UnitMods unitMod) +{ + if (!CanModifyStats()) + return; switch (unitMod) { @@ -15311,36 +15456,100 @@ bool Unit::HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, f default: break; } +} - return true; +void Unit::UpdateDamageDoneMods(WeaponAttackType attackType, int32 /*skipEnchantSlot = -1*/) +{ + UnitMods unitMod; + switch (attackType) + { + case BASE_ATTACK: + unitMod = UNIT_MOD_DAMAGE_MAINHAND; + break; + case OFF_ATTACK: + unitMod = UNIT_MOD_DAMAGE_OFFHAND; + break; + case RANGED_ATTACK: + unitMod = UNIT_MOD_DAMAGE_RANGED; + break; + default: + ABORT(); + break; + } + + float amount = GetTotalAuraModifier(SPELL_AURA_MOD_DAMAGE_DONE, [&](AuraEffect const* aurEff) -> bool + { + if (!(aurEff->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL)) + return false; + + return CheckAttackFitToAuraRequirement(attackType, aurEff); + }); + + SetStatFlatModifier(unitMod, TOTAL_VALUE, amount); +} + +void Unit::UpdateAllDamageDoneMods() +{ + for (uint8 i = BASE_ATTACK; i < MAX_ATTACK; ++i) + UpdateDamageDoneMods(WeaponAttackType(i)); } -float Unit::GetModifierValue(UnitMods unitMod, UnitModifierType modifierType) const +void Unit::UpdateDamagePctDoneMods(WeaponAttackType attackType) { - if (unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_END) + float factor; + UnitMods unitMod; + switch (attackType) { - LOG_ERROR("entities.unit", "attempt to access non-existing modifier value from UnitMods!"); - return 0.0f; + case BASE_ATTACK: + factor = 1.0f; + unitMod = UNIT_MOD_DAMAGE_MAINHAND; + break; + case OFF_ATTACK: + // off hand has 50% penalty + factor = 0.5f; + unitMod = UNIT_MOD_DAMAGE_OFFHAND; + break; + case RANGED_ATTACK: + factor = 1.0f; + unitMod = UNIT_MOD_DAMAGE_RANGED; + break; + default: + ABORT(); + break; } - if (modifierType == TOTAL_PCT && m_auraModifiersGroup[unitMod][modifierType] <= 0.0f) - return 0.0f; + factor *= GetTotalAuraMultiplier(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, [attackType, this](AuraEffect const* aurEff) -> bool + { + if (!(aurEff->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL)) + return false; + + return CheckAttackFitToAuraRequirement(attackType, aurEff); + }); + + if (attackType == OFF_ATTACK) + factor *= GetTotalAuraMultiplier(SPELL_AURA_MOD_OFFHAND_DAMAGE_PCT, std::bind(&Unit::CheckAttackFitToAuraRequirement, this, attackType, std::placeholders::_1)); - return m_auraModifiersGroup[unitMod][modifierType]; + SetStatPctModifier(unitMod, TOTAL_PCT, factor); +} + +void Unit::UpdateAllDamagePctDoneMods() +{ + for (uint8 i = BASE_ATTACK; i < MAX_ATTACK; ++i) + UpdateDamagePctDoneMods(WeaponAttackType(i)); } float Unit::GetTotalStatValue(Stats stat, float additionalValue) const { UnitMods unitMod = UnitMods(static_cast<uint16>(UNIT_MOD_STAT_START) + stat); - if (m_auraModifiersGroup[unitMod][TOTAL_PCT] <= 0.0f) + if (GetPctModifierValue(unitMod, TOTAL_PCT) <= 0.0f) return 0.0f; // value = ((base_value * base_pct) + total_value) * total_pct - float value = m_auraModifiersGroup[unitMod][BASE_VALUE] + GetCreateStat(stat); - value *= m_auraModifiersGroup[unitMod][BASE_PCT]; - value += m_auraModifiersGroup[unitMod][TOTAL_VALUE] + additionalValue; - value *= m_auraModifiersGroup[unitMod][TOTAL_PCT]; + float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreateStat(stat); + value *= GetPctModifierValue(unitMod, BASE_PCT); + value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + additionalValue; + value *= GetPctModifierValue(unitMod, TOTAL_PCT); return value; } @@ -15353,25 +15562,16 @@ float Unit::GetTotalAuraModValue(UnitMods unitMod) const return 0.0f; } - if (m_auraModifiersGroup[unitMod][TOTAL_PCT] <= 0.0f) + if (GetPctModifierValue(unitMod, TOTAL_PCT) <= 0.0f) return 0.0f; - float value = m_auraModifiersGroup[unitMod][BASE_VALUE]; - value *= m_auraModifiersGroup[unitMod][BASE_PCT]; - value += m_auraModifiersGroup[unitMod][TOTAL_VALUE]; - value *= m_auraModifiersGroup[unitMod][TOTAL_PCT]; - + float value = GetFlatModifierValue(unitMod, BASE_VALUE); + value *= GetPctModifierValue(unitMod, BASE_PCT); + value += GetFlatModifierValue(unitMod, TOTAL_VALUE); + value *= GetPctModifierValue(unitMod, TOTAL_PCT); return value; } -void Unit::ApplyStatPercentBuffMod(Stats stat, float val, bool apply) -{ - if (val == -100.0f) // prevent set var to zero - val = -99.99f; - float var = GetStat(stat) * val / 100.0f; - ApplyModSignedFloatValue((var > 0 ? static_cast<uint16>(UNIT_FIELD_POSSTAT0) + stat : static_cast<uint16>(UNIT_FIELD_NEGSTAT0) + stat), var, apply); -} - SpellSchools Unit::GetSpellSchoolByAuraGroup(UnitMods unitMod) const { SpellSchools school = SPELL_SCHOOL_NORMAL; @@ -17078,28 +17278,40 @@ Unit* Unit::SelectNearbyNoTotemTarget(Unit* exclude, float dist) const return Acore::Containers::SelectRandomContainerElement(targets); } +void ApplyPercentModFloatVar(float& var, float val, bool apply) +{ + var *= (apply ? (100.0f + val) / 100.0f : 100.0f / (100.0f + val)); +} + void Unit::ApplyAttackTimePercentMod(WeaponAttackType att, float val, bool apply) { + float amount = GetFloatValue(UNIT_FIELD_BASEATTACKTIME + AsUnderlyingType(att)); + float remainingTimePct = std::max((float)m_attackTimer[att], 0.0f) / (GetAttackTime(att) * m_modAttackSpeedPct[att]); - if (val > 0) + if (val > 0.f) { ApplyPercentModFloatVar(m_modAttackSpeedPct[att], val, !apply); - ApplyPercentModFloatValue(static_cast<uint16>(UNIT_FIELD_BASEATTACKTIME) + att, val, !apply); + ApplyPercentModFloatVar(amount, val, !apply); } else { ApplyPercentModFloatVar(m_modAttackSpeedPct[att], -val, apply); - ApplyPercentModFloatValue(static_cast<uint16>(UNIT_FIELD_BASEATTACKTIME) + att, -val, apply); + ApplyPercentModFloatVar(amount, -val, apply); } + SetFloatValue(UNIT_FIELD_BASEATTACKTIME + AsUnderlyingType(att), amount); m_attackTimer[att] = uint32(GetAttackTime(att) * m_modAttackSpeedPct[att] * remainingTimePct); } void Unit::ApplyCastTimePercentMod(float val, bool apply) { - if (val > 0) - ApplyPercentModFloatValue(UNIT_MOD_CAST_SPEED, val, !apply); + float amount = GetFloatValue(UNIT_MOD_CAST_SPEED); + + if (val > 0.f) + ApplyPercentModFloatVar(amount, val, !apply); else - ApplyPercentModFloatValue(UNIT_MOD_CAST_SPEED, -val, apply); + ApplyPercentModFloatVar(amount, -val, apply); + + SetFloatValue(UNIT_MOD_CAST_SPEED, amount); } uint32 Unit::GetCastingTimeForBonus(SpellInfo const* spellProto, DamageEffectType damagetype, uint32 CastingTime) const @@ -18388,7 +18600,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au } // dismount players when charmed - if (IsPlayer()) + if (IsPlayer() && type != CHARM_TYPE_POSSESS) RemoveAurasByType(SPELL_AURA_MOUNTED); if (charmer->IsPlayer()) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index c335f7e0ed..934bb1c70c 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -123,13 +123,18 @@ enum HitInfo HITINFO_FAKE_DAMAGE = 0x01000000 // enables damage animation even if no damage done, set only if no damage }; -enum UnitModifierType +enum UnitModifierFlatType { BASE_VALUE = 0, - BASE_PCT = 1, - TOTAL_VALUE = 2, - TOTAL_PCT = 3, - MODIFIER_TYPE_END = 4 + TOTAL_VALUE = 1, + MODIFIER_TYPE_FLAT_END = 3 +}; + +enum UnitModifierPctType +{ + BASE_PCT = 0, + TOTAL_PCT = 1, + MODIFIER_TYPE_PCT_END = 2 }; enum WeaponDamageRange @@ -214,7 +219,7 @@ enum WeaponAttackType : uint8 MAX_ATTACK }; -enum CombatRating +enum CombatRating : uint8 { CR_WEAPON_SKILL = 0, CR_DEFENSE_SKILL = 1, @@ -1050,16 +1055,32 @@ public: for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i) SetFloatValue(static_cast<uint16>(UNIT_FIELD_NEGSTAT0) + i, 0); } - bool HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, float amount, bool apply); - void SetModifierValue(UnitMods unitMod, UnitModifierType modifierType, float value) { m_auraModifiersGroup[unitMod][modifierType] = value; } - [[nodiscard]] float GetModifierValue(UnitMods unitMod, UnitModifierType modifierType) const; + bool HandleStatFlatModifier(UnitMods unitMod, UnitModifierFlatType modifierType, float amount, bool apply); + void ApplyStatPctModifier(UnitMods unitMod, UnitModifierPctType modifierType, float amount); + + void SetStatFlatModifier(UnitMods unitMod, UnitModifierFlatType modifierType, float val); + void SetStatPctModifier(UnitMods unitMod, UnitModifierPctType modifierType, float val); + + [[nodiscard]] float GetFlatModifierValue(UnitMods unitMod, UnitModifierFlatType modifierType) const; + [[nodiscard]] float GetPctModifierValue(UnitMods unitMod, UnitModifierPctType modifierType) const; + + void UpdateUnitMod(UnitMods unitMod); + + // only players have item requirements + [[nodiscard]] virtual bool CheckAttackFitToAuraRequirement(WeaponAttackType /*attackType*/, AuraEffect const* /*aurEff*/) const { return true; } + + virtual void UpdateDamageDoneMods(WeaponAttackType attackType, int32 skipEnchantSlot = -1); + void UpdateAllDamageDoneMods(); + + void UpdateDamagePctDoneMods(WeaponAttackType attackType); + void UpdateAllDamagePctDoneMods(); + [[nodiscard]] float GetTotalStatValue(Stats stat, float additionalValue = 0.0f) const; void SetCanModifyStats(bool modifyStats) { m_canModifyStats = modifyStats; } [[nodiscard]] bool CanModifyStats() const { return m_canModifyStats; } - void ApplyStatBuffMod(Stats stat, float val, bool apply) { ApplyModSignedFloatValue((val > 0 ? static_cast<uint16>(UNIT_FIELD_POSSTAT0) + stat : static_cast<uint16>(UNIT_FIELD_NEGSTAT0) + stat), val, apply); } - void ApplyStatPercentBuffMod(Stats stat, float val, bool apply); + void UpdateStatBuffMod(Stats stat); // Unit level methods [[nodiscard]] uint8 GetLevel() const { return uint8(GetUInt32Value(UNIT_FIELD_LEVEL)); } @@ -1104,7 +1125,6 @@ public: void SetMaxPower(Powers power, uint32 val); int32 ModifyPower(Powers power, int32 val, bool withPowerUpdate = true); - int32 ModifyPowerPct(Powers power, float pct, bool apply = true); void RewardRage(uint32 damage, uint32 weaponSpeedHitFactor, bool attacker); @@ -1159,13 +1179,9 @@ public: [[nodiscard]] uint32 GetResistance(SpellSchoolMask mask) const; [[nodiscard]] uint32 GetResistance(SpellSchools school) const { return GetUInt32Value(static_cast<uint16>(UNIT_FIELD_RESISTANCES) + school); } static float GetEffectiveResistChance(Unit const* owner, SpellSchoolMask schoolMask, Unit const* victim); - [[nodiscard]] float GetResistanceBuffMods(SpellSchools school, bool positive) const { return GetFloatValue(positive ? static_cast<uint16>(UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE) + school : static_cast<uint16>(UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE) + + school); } void SetResistance(SpellSchools school, int32 val) { SetStatInt32Value(static_cast<uint16>(UNIT_FIELD_RESISTANCES) + school, val); } - void SetResistanceBuffMods(SpellSchools school, bool positive, float val) { SetFloatValue(positive ? static_cast<uint16>(UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE) + school : static_cast<uint16>(UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE) + + school, val); } - - void ApplyResistanceBuffModsMod(SpellSchools school, bool positive, float val, bool apply) { ApplyModSignedFloatValue(positive ? static_cast<uint16>(UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE) + school : static_cast<uint16>(UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE) + + school, val, apply); } - void ApplyResistanceBuffModsPercentMod(SpellSchools school, bool positive, float val, bool apply) { ApplyPercentModFloatValue(positive ? static_cast<uint16>(UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE) + school : static_cast<uint16>(UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE) + + school, val, apply); } + void UpdateResistanceBuffModsMod(SpellSchools school); //////////// Need triage //////////////// uint16 GetMaxSkillValueForLevel(Unit const* target = nullptr) const { return (target ? getLevelForTarget(target) : GetLevel()) * 5; } @@ -1319,6 +1335,10 @@ public: void SetAuraStack(uint32 spellId, Unit* target, uint32 stack); + int32 GetHighestExclusiveSameEffectSpellGroupValue(AuraEffect const* aurEff, AuraType auraType, bool checkMiscValue = false, int32 miscValue = 0) const; + bool IsHighestExclusiveAura(Aura const* aura, bool removeOtherAuraApplications = false); + bool IsHighestExclusiveAuraEffect(SpellInfo const* spellInfo, AuraType auraType, int32 effectAmount, uint8 auraEffectMask, bool removeOtherAuraApplications = false); + // aura apply/remove helpers - you should better not use these Aura* _TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 effMask, Unit* caster, int32* baseAmount = nullptr, Item* castItem = nullptr, ObjectGuid casterGUID = ObjectGuid::Empty, bool periodicReset = false); void _AddAura(UnitAura* aura, Unit* caster); @@ -1328,7 +1348,7 @@ public: void _UnapplyAura(AuraApplicationMap::iterator& i, AuraRemoveMode removeMode); void _UnapplyAura(AuraApplication* aurApp, AuraRemoveMode removeMode); void _RemoveNoStackAuraApplicationsDueToAura(Aura* aura); - void _RemoveNoStackAurasDueToAura(Aura* aura); + void _RemoveNoStackAurasDueToAura(Aura* aura, bool owned); bool _IsNoStackAuraDueToAura(Aura* appliedAura, Aura* existingAura) const; void _RegisterAuraEffect(AuraEffect* aurEff, bool apply); @@ -1474,15 +1494,19 @@ public: uint32 GetDiseasesByCaster(ObjectGuid casterGUID, uint8 mode = 0); [[nodiscard]] uint32 GetDoTsByCaster(ObjectGuid casterGUID) const; - [[nodiscard]] int32 GetTotalAuraModifierAreaExclusive(AuraType auratype) const; [[nodiscard]] int32 GetTotalAuraModifier(AuraType auratype) const; [[nodiscard]] float GetTotalAuraMultiplier(AuraType auratype) const; - int32 GetMaxPositiveAuraModifier(AuraType auratype); + [[nodiscard]] int32 GetMaxPositiveAuraModifier(AuraType auratype) const; [[nodiscard]] int32 GetMaxNegativeAuraModifier(AuraType auratype) const; + [[nodiscard]] int32 GetTotalAuraModifier(AuraType auratype, std::function<bool(AuraEffect const*)> const& predicate) const; + [[nodiscard]] float GetTotalAuraMultiplier(AuraType auraType, std::function<bool(AuraEffect const*)> const& predicate) const; + [[nodiscard]] int32 GetMaxPositiveAuraModifier(AuraType auraType, std::function<bool(AuraEffect const*)> const& predicate) const; + [[nodiscard]] int32 GetMaxNegativeAuraModifier(AuraType auraType, std::function<bool(AuraEffect const*)> const& predicate) const; + [[nodiscard]] int32 GetTotalAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const; [[nodiscard]] float GetTotalAuraMultiplierByMiscMask(AuraType auratype, uint32 misc_mask) const; - int32 GetMaxPositiveAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask, const AuraEffect* except = nullptr) const; + [[nodiscard]] int32 GetMaxPositiveAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask, const AuraEffect* except = nullptr) const; [[nodiscard]] int32 GetMaxNegativeAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const; [[nodiscard]] int32 GetTotalAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const; @@ -1581,7 +1605,6 @@ public: int32 HealBySpell(HealInfo& healInfo, bool critical = false); int32 SpellBaseHealingBonusDone(SpellSchoolMask schoolMask); - int32 SpellBaseHealingBonusTaken(SpellSchoolMask schoolMask); float SpellPctHealingModsDone(Unit* victim, SpellInfo const* spellProto, DamageEffectType damagetype); uint32 SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype, uint8 effIndex, float TotalMod = 0.0f, uint32 stack = 1); uint32 SpellHealingBonusTaken(Unit* caster, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype, uint32 stack = 1); @@ -2132,7 +2155,8 @@ protected: AuraStateAurasMap m_auraStateAuras; // Used for improve performance of aura state checks on aura apply/remove uint32 m_interruptMask; - float m_auraModifiersGroup[UNIT_MOD_END][MODIFIER_TYPE_END]; + float m_auraFlatModifiersGroup[UNIT_MOD_END][MODIFIER_TYPE_FLAT_END]; + float m_auraPctModifiersGroup[UNIT_MOD_END][MODIFIER_TYPE_PCT_END]; float m_weaponDamage[MAX_ATTACK][MAX_WEAPON_DAMAGE_RANGE][MAX_ITEM_PROTO_DAMAGES]; bool m_canModifyStats; VisibleAuraMap m_visibleAuras; diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index aef8767541..9fc5643d0a 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -256,8 +256,8 @@ void WorldSession::HandleAutoEquipItemOpcode(WorldPackets::Item::AutoEquipItem& } // now do moves, remove... - _player->RemoveItem(dstbag, dstslot, true, true); - _player->RemoveItem(packet.SourceBag, packet.SourceSlot, true, true); + _player->RemoveItem(dstbag, dstslot, true); + _player->RemoveItem(packet.SourceBag, packet.SourceSlot, true); // add to dest _player->EquipItem(dest, pSrcItem, true); diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index a878194589..fb40ce36ac 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -299,6 +299,8 @@ void WorldSession::HandleMoveTeleportAck(WorldPacket& recvData) plMover->UpdatePosition(dest, true); + plMover->SetFallInformation(GameTime::GetGameTime().count(), dest.GetPositionZ()); + // xinef: teleport pets if they are not unsummoned if (Pet* pet = plMover->GetPet()) { diff --git a/src/server/game/Scripting/ScriptDefines/AllSpellScript.cpp b/src/server/game/Scripting/ScriptDefines/AllSpellScript.cpp index 3f15648935..d47939fe0b 100644 --- a/src/server/game/Scripting/ScriptDefines/AllSpellScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/AllSpellScript.cpp @@ -24,16 +24,6 @@ void ScriptMgr::OnCalcMaxDuration(Aura const* aura, int32& maxDuration) CALL_ENABLED_HOOKS(AllSpellScript, ALLSPELLHOOK_ON_CALC_MAX_DURATION, script->OnCalcMaxDuration(aura, maxDuration)); } -bool ScriptMgr::CanModAuraEffectDamageDone(AuraEffect const* auraEff, Unit* target, AuraApplication const* aurApp, uint8 mode, bool apply) -{ - CALL_ENABLED_BOOLEAN_HOOKS(AllSpellScript, ALLSPELLHOOK_CAN_MOD_AURA_EFFECT_DAMAGE_DONE, !script->CanModAuraEffectDamageDone(auraEff, target, aurApp, mode, apply)); -} - -bool ScriptMgr::CanModAuraEffectModDamagePercentDone(AuraEffect const* auraEff, Unit* target, AuraApplication const* aurApp, uint8 mode, bool apply) -{ - CALL_ENABLED_BOOLEAN_HOOKS(AllSpellScript, ALLSPELLHOOK_CAN_MOD_AURA_EFFECT_MOD_DAMAGE_PERCENT_DONE, !script->CanModAuraEffectModDamagePercentDone(auraEff, target, aurApp, mode, apply)); -} - void ScriptMgr::OnSpellCheckCast(Spell* spell, bool strict, SpellCastResult& res) { CALL_ENABLED_HOOKS(AllSpellScript, ALLSPELLHOOK_ON_SPELL_CHECK_CAST, script->OnSpellCheckCast(spell, strict, res)); diff --git a/src/server/game/Scripting/ScriptDefines/AllSpellScript.h b/src/server/game/Scripting/ScriptDefines/AllSpellScript.h index 7536f8cf5b..df069bcb11 100644 --- a/src/server/game/Scripting/ScriptDefines/AllSpellScript.h +++ b/src/server/game/Scripting/ScriptDefines/AllSpellScript.h @@ -24,8 +24,6 @@ enum AllSpellHook { ALLSPELLHOOK_ON_CALC_MAX_DURATION, - ALLSPELLHOOK_CAN_MOD_AURA_EFFECT_DAMAGE_DONE, - ALLSPELLHOOK_CAN_MOD_AURA_EFFECT_MOD_DAMAGE_PERCENT_DONE, ALLSPELLHOOK_ON_SPELL_CHECK_CAST, ALLSPELLHOOK_CAN_PREPARE, ALLSPELLHOOK_CAN_SCALING_EVERYTHING, @@ -56,10 +54,6 @@ public: // Calculate max duration in applying aura virtual void OnCalcMaxDuration(Aura const* /*aura*/, int32& /*maxDuration*/) { } - [[nodiscard]] virtual bool CanModAuraEffectDamageDone(AuraEffect const* /*auraEff*/, Unit* /*target*/, AuraApplication const* /*aurApp*/, uint8 /*mode*/, bool /*apply*/) { return true; } - - [[nodiscard]] virtual bool CanModAuraEffectModDamagePercentDone(AuraEffect const* /*auraEff*/, Unit* /*target*/, AuraApplication const* /*aurApp*/, uint8 /*mode*/, bool /*apply*/) { return true; } - virtual void OnSpellCheckCast(Spell* /*spell*/, bool /*strict*/, SpellCastResult& /*res*/) { } [[nodiscard]] virtual bool CanPrepare(Spell* /*spell*/, SpellCastTargets const* /*targets*/, AuraEffect const* /*triggeredByAura*/) { return true; } diff --git a/src/server/game/Scripting/ScriptDefines/UnitScript.cpp b/src/server/game/Scripting/ScriptDefines/UnitScript.cpp index 2975ec4f1a..c36cfcd25a 100644 --- a/src/server/game/Scripting/ScriptDefines/UnitScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/UnitScript.cpp @@ -84,21 +84,6 @@ bool ScriptMgr::IfNormalReaction(Unit const* unit, Unit const* target, Reputatio CALL_ENABLED_BOOLEAN_HOOKS(UnitScript, UNITHOOK_IF_NORMAL_REACTION, !script->IfNormalReaction(unit, target, repRank)); } -bool ScriptMgr::IsNeedModSpellDamagePercent(Unit const* unit, AuraEffect* auraEff, float& doneTotalMod, SpellInfo const* spellProto) -{ - CALL_ENABLED_BOOLEAN_HOOKS(UnitScript, UNITHOOK_IS_NEEDMOD_SPELL_DAMAGE_PERCENT, !script->IsNeedModSpellDamagePercent(unit, auraEff, doneTotalMod, spellProto)); -} - -bool ScriptMgr::IsNeedModMeleeDamagePercent(Unit const* unit, AuraEffect* auraEff, float& doneTotalMod, SpellInfo const* spellProto) -{ - CALL_ENABLED_BOOLEAN_HOOKS(UnitScript, UNITHOOK_IS_NEEDMOD_MELEE_DAMAGE_PERCENT, !script->IsNeedModMeleeDamagePercent(unit, auraEff, doneTotalMod, spellProto)); -} - -bool ScriptMgr::IsNeedModHealPercent(Unit const* unit, AuraEffect* auraEff, float& doneTotalMod, SpellInfo const* spellProto) -{ - CALL_ENABLED_BOOLEAN_HOOKS(UnitScript, UNITHOOK_IS_NEEDMOD_HEAL_PERCENT, !script->IsNeedModHealPercent(unit, auraEff, doneTotalMod, spellProto)); -} - bool ScriptMgr::CanSetPhaseMask(Unit const* unit, uint32 newPhaseMask, bool update) { CALL_ENABLED_BOOLEAN_HOOKS(UnitScript, UNITHOOK_CAN_SET_PHASE_MASK, !script->CanSetPhaseMask(unit, newPhaseMask, update)); diff --git a/src/server/game/Scripting/ScriptDefines/UnitScript.h b/src/server/game/Scripting/ScriptDefines/UnitScript.h index 26c4e2b13d..046f9cc117 100644 --- a/src/server/game/Scripting/ScriptDefines/UnitScript.h +++ b/src/server/game/Scripting/ScriptDefines/UnitScript.h @@ -33,9 +33,6 @@ enum UnitHook UNITHOOK_ON_AURA_APPLY, UNITHOOK_ON_AURA_REMOVE, UNITHOOK_IF_NORMAL_REACTION, - UNITHOOK_IS_NEEDMOD_SPELL_DAMAGE_PERCENT, - UNITHOOK_IS_NEEDMOD_MELEE_DAMAGE_PERCENT, - UNITHOOK_IS_NEEDMOD_HEAL_PERCENT, UNITHOOK_CAN_SET_PHASE_MASK, UNITHOOK_IS_CUSTOM_BUILD_VALUES_UPDATE, UNITHOOK_SHOULD_TRACK_VALUES_UPDATE_POS_BY_INDEX, @@ -89,12 +86,6 @@ public: [[nodiscard]] virtual bool IfNormalReaction(Unit const* /*unit*/, Unit const* /*target*/, ReputationRank& /*repRank*/) { return true; } - [[nodiscard]] virtual bool IsNeedModSpellDamagePercent(Unit const* /*unit*/, AuraEffect* /*auraEff*/, float& /*doneTotalMod*/, SpellInfo const* /*spellProto*/) { return true; } - - [[nodiscard]] virtual bool IsNeedModMeleeDamagePercent(Unit const* /*unit*/, AuraEffect* /*auraEff*/, float& /*doneTotalMod*/, SpellInfo const* /*spellProto*/) { return true; } - - [[nodiscard]] virtual bool IsNeedModHealPercent(Unit const* /*unit*/, AuraEffect* /*auraEff*/, float& /*doneTotalMod*/, SpellInfo const* /*spellProto*/) { return true; } - [[nodiscard]] virtual bool CanSetPhaseMask(Unit const* /*unit*/, uint32 /*newPhaseMask*/, bool /*update*/) { return true; } [[nodiscard]] virtual bool IsCustomBuildValuesUpdate(Unit const* /*unit*/, uint8 /*updateType*/, ByteBuffer& /*fieldBuffer*/, Player const* /*target*/, uint16 /*index*/) { return false; } diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 6a9a0914d3..5aa131f042 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -546,9 +546,6 @@ public: /* UnitScript */ void OnAuraApply(Unit* /*unit*/, Aura* /*aura*/); void OnAuraRemove(Unit* unit, AuraApplication* aurApp, AuraRemoveMode mode); bool IfNormalReaction(Unit const* unit, Unit const* target, ReputationRank& repRank); - bool IsNeedModSpellDamagePercent(Unit const* unit, AuraEffect* auraEff, float& doneTotalMod, SpellInfo const* spellProto); - bool IsNeedModMeleeDamagePercent(Unit const* unit, AuraEffect* auraEff, float& doneTotalMod, SpellInfo const* spellProto); - bool IsNeedModHealPercent(Unit const* unit, AuraEffect* auraEff, float& doneTotalMod, SpellInfo const* spellProto); bool CanSetPhaseMask(Unit const* unit, uint32 newPhaseMask, bool update); bool IsCustomBuildValuesUpdate(Unit const* unit, uint8 updateType, ByteBuffer& fieldBuffer, Player const* target, uint16 index); bool ShouldTrackValuesUpdatePosByIndex(Unit const* unit, uint8 updateType, uint16 index); @@ -606,8 +603,6 @@ public: /* Arena Team Script */ public: /* SpellSC */ void OnCalcMaxDuration(Aura const* aura, int32& maxDuration); - bool CanModAuraEffectDamageDone(AuraEffect const* auraEff, Unit* target, AuraApplication const* aurApp, uint8 mode, bool apply); - bool CanModAuraEffectModDamagePercentDone(AuraEffect const* auraEff, Unit* target, AuraApplication const* aurApp, uint8 mode, bool apply); void OnSpellCheckCast(Spell* spell, bool strict, SpellCastResult& res); bool CanPrepare(Spell* spell, SpellCastTargets const* targets, AuraEffect const* triggeredByAura); bool CanScalingEverything(Spell* spell); 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; diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp index 584e05f6f2..83275a1793 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp @@ -78,6 +78,10 @@ Position const PosMoveOnSpawn[1] = { -11561.9f, -1627.868f, 41.29941f, 0.0f } }; +// hack +float const DamageIncrease = 35.0f; +float const DamageDecrease = 100.f / (1.f + DamageIncrease / 100.f) - 100.f; + class boss_arlokk : public CreatureScript { public: @@ -90,7 +94,7 @@ public: void Reset() override { if (events.IsInPhase(PHASE_TWO)) - me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); // hack + me->ApplyStatPctModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, DamageDecrease); // hack _Reset(); _summonCountA = 0; _summonCountB = 0; @@ -253,7 +257,7 @@ public: events.ScheduleEvent(EVENT_RAVAGE, 10s, 14s, 0, PHASE_TWO); events.ScheduleEvent(EVENT_TRANSFORM_BACK, 30s, 40s, 0, PHASE_TWO); events.SetPhase(PHASE_TWO); - me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, true); // hack + me->ApplyStatPctModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, DamageIncrease); // hack break; case EVENT_RAVAGE: DoCastVictim(SPELL_RAVAGE, true); @@ -265,7 +269,7 @@ public: DoCast(me, SPELL_VANISH_VISUAL); me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(WEAPON_DAGGER)); me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(WEAPON_DAGGER)); - me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); // hack + me->ApplyStatPctModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, DamageDecrease); // hack events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 4s, 7s, 0, PHASE_ONE); events.ScheduleEvent(EVENT_GOUGE, 12s, 15s, 0, PHASE_ONE); events.ScheduleEvent(EVENT_TRANSFORM, 30s, 0, PHASE_ONE); diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp index d453d5e2f7..b350b680e5 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp @@ -73,6 +73,10 @@ enum Misc GO_SPIDER_EGGS = 179985, }; +// hack +float const DamageIncrease = 35.0f; +float const DamageDecrease = 100.f / (1.f + DamageIncrease / 100.f) - 100.f; + // High Priestess Mar'li (14510) struct boss_marli : public BossAI { @@ -84,7 +88,7 @@ public: if (_phase == PHASE_SPIDER) { me->RemoveAura(SPELL_SPIDER_FORM); - me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); + me->ApplyStatPctModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, DamageDecrease); _phase = PHASE_TROLL; } @@ -143,7 +147,7 @@ private: me->RemoveAura(SPELL_SPIDER_FORM); DoCastSelf(SPELL_TRANSFORM_BACK, true); Talk(SAY_TRANSFORM_BACK); - me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); + me->ApplyStatPctModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, DamageDecrease); scheduler.CancelGroup(PHASE_SPIDER); } @@ -186,7 +190,7 @@ private: Talk(SAY_TRANSFORM); DoCastSelf(SPELL_SPIDER_FORM, true); - me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, true); + me->ApplyStatPctModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, DamageIncrease); scheduler.Schedule(5s, PHASE_SPIDER, [this](TaskContext context) { diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_jedoga_shadowseeker.cpp b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_jedoga_shadowseeker.cpp index 6392a738ed..d780776064 100644 --- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_jedoga_shadowseeker.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_jedoga_shadowseeker.cpp @@ -16,7 +16,6 @@ */ #include "AchievementCriteriaScript.h" -#include "Containers.h" #include "CreatureScript.h" #include "ObjectAccessor.h" #include "ScriptedCreature.h" @@ -209,7 +208,7 @@ struct boss_jedoga_shadowseeker : public BossAI } } - sacraficeTarget_GUID.Clear(); + sacrificeTargetGUID.Clear(); sayPreachTimer = 120000; ritualTriggered = false; volunteerWork = true; @@ -268,12 +267,12 @@ struct boss_jedoga_shadowseeker : public BossAI } case NPC_TWILIGHT_VOLUNTEER: { - if (sacraficeTarget_GUID && summon->GetGUID() != sacraficeTarget_GUID) + if (sacrificeTargetGUID && summon->GetGUID() != sacrificeTargetGUID) { break; } - if (killer != me && killer->GetGUID() != sacraficeTarget_GUID) + if (killer != me && killer->GetGUID() != sacrificeTargetGUID) { volunteerWork = false; } @@ -315,7 +314,7 @@ struct boss_jedoga_shadowseeker : public BossAI { if (action == ACTION_SACRAFICE) { - if (Creature* target = ObjectAccessor::GetCreature(*me, sacraficeTarget_GUID)) + if (Creature* target = ObjectAccessor::GetCreature(*me, sacrificeTargetGUID)) { Unit::Kill(me, target); } @@ -395,9 +394,9 @@ struct boss_jedoga_shadowseeker : public BossAI me->SetFacingTo(5.66f); if (!summons.empty()) { - sacraficeTarget_GUID = Acore::Containers::SelectRandomContainerElement(summons); - if (ObjectAccessor::GetCreature(*me, sacraficeTarget_GUID)) + if (Creature* creature = summons.GetRandomCreatureWithEntry(NPC_TWILIGHT_VOLUNTEER)) { + sacrificeTargetGUID = creature->GetGUID(); events.ScheduleEvent(EVENT_JEDGA_START_RITUAL, 3s, 0, PHASE_RITUAL); } // Something failed, let players continue but do not grant achievement @@ -518,15 +517,16 @@ struct boss_jedoga_shadowseeker : public BossAI } case EVENT_JEDGA_START_RITUAL: { - sacraficeTarget_GUID = Acore::Containers::SelectRandomContainerElement(summons); - if (Creature* volunteer = ObjectAccessor::GetCreature(*me, sacraficeTarget_GUID)) + if (Creature* creature = summons.GetRandomCreatureWithEntry(NPC_TWILIGHT_VOLUNTEER)) { + sacrificeTargetGUID = creature->GetGUID(); Talk(SAY_SACRIFICE_1); - sacraficeTarget_GUID = volunteer->GetGUID(); - volunteer->AI()->DoAction(ACTION_RITUAL_BEGIN); + creature->AI()->DoAction(ACTION_RITUAL_BEGIN); } break; } + default: + break; } } @@ -546,7 +546,7 @@ struct boss_jedoga_shadowseeker : public BossAI private: GuidList oocSummons; GuidList oocTriggers; - ObjectGuid sacraficeTarget_GUID; + ObjectGuid sacrificeTargetGUID; uint32 sayPreachTimer; bool combatSummonsSummoned; bool ritualTriggered; diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp index 319fc1f83e..d27d43b388 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp @@ -2037,7 +2037,7 @@ public: npc_toc_enh_shamanAI(Creature* pCreature) : boss_faction_championsAI(pCreature, AI_MELEE) { SetEquipmentSlots(false, 51803, 48013, EQUIP_NO_CHANGE); - me->SetModifierValue(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, 1.0f); + me->SetStatPctModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, 1.0f); me->UpdateDamagePhysical(OFF_ATTACK); events.Reset(); diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp index eb26cad02b..2ffe2f7dd5 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp @@ -109,7 +109,7 @@ struct boss_twin_valkyrAI : public ScriptedAI { pInstance = pCreature->GetInstanceScript(); me->SetReactState(REACT_PASSIVE); - me->SetModifierValue(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, 1.0f); + me->SetStatPctModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, 1.0f); me->UpdateDamagePhysical(OFF_ATTACK); LastSynchroHP = (int32)me->GetMaxHealth(); SpecialMask = 0; diff --git a/src/server/scripts/Northrend/Gundrak/boss_eck.cpp b/src/server/scripts/Northrend/Gundrak/boss_eck.cpp index 9e7a6bbc97..12f84b71f1 100644 --- a/src/server/scripts/Northrend/Gundrak/boss_eck.cpp +++ b/src/server/scripts/Northrend/Gundrak/boss_eck.cpp @@ -36,7 +36,8 @@ enum Misc EVENT_ECK_BITE = 2, EVENT_ECK_SPIT = 3, EVENT_ECK_SPRING = 4, - EVENT_ECK_HEALTH = 5 + EVENT_ECK_CRAZED_EMOTE = 5, + EMOTE_CRAZED = 1 }; class boss_eck : public CreatureScript @@ -89,7 +90,8 @@ public: void JustEngagedWith(Unit* who) override { BossAI::JustEngagedWith(who); - events.ScheduleEvent(EVENT_ECK_BERSERK, 60s, 90s); + events.ScheduleEvent(EVENT_ECK_CRAZED_EMOTE, 76s, 78s); + events.ScheduleEvent(EVENT_ECK_BERSERK, 90s); events.ScheduleEvent(EVENT_ECK_BITE, 5s); events.ScheduleEvent(EVENT_ECK_SPIT, 10s, 37s); events.ScheduleEvent(EVENT_ECK_SPRING, 10s, 24s); @@ -111,18 +113,11 @@ public: switch (events.ExecuteEvent()) { - case EVENT_ECK_HEALTH: - if (me->HealthBelowPct(21)) - { - events.CancelEvent(EVENT_ECK_BERSERK); - me->CastSpell(me, SPELL_ECK_BERSERK, false); - break; - } - events.ScheduleEvent(EVENT_ECK_HEALTH, 1s); + case EVENT_ECK_CRAZED_EMOTE: + Talk(EMOTE_CRAZED); break; case EVENT_ECK_BERSERK: me->CastSpell(me, SPELL_ECK_BERSERK, false); - events.CancelEvent(EVENT_ECK_HEALTH); break; case EVENT_ECK_BITE: me->CastSpell(me->GetVictim(), SPELL_ECK_BITE, false); diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp index 54406752f9..933c006ce9 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp @@ -664,7 +664,6 @@ public: me->CastSpell(me, 59046, true); // credit } - me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER); me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STAND); me->SendMovementFlagUpdate(); @@ -871,6 +870,7 @@ void brann_bronzebeard::brann_bronzebeardAI::WaypointReached(uint32 id) //Tribunal end, stand in the middle of the sky room case 17: SetEscortPaused(true); + me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER); me->SetOrientation(3.91672f); me->SendMovementFlagUpdate(); break; diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/utgarde_keep.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/utgarde_keep.cpp index 09ee369913..1a7cb30300 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/utgarde_keep.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/utgarde_keep.cpp @@ -82,30 +82,33 @@ struct npc_dragonflayer_forge_master : public ScriptedAI enum EnslavedProtoDrake { - TYPE_PROTODRAKE_AT = 28, - DATA_PROTODRAKE_MOVE = 6, - - PATH_PROTODRAKE = 125946, + SPELL_REND = 43931, + SPELL_FLAME_BREATH = 50653, + SPELL_KNOCK_AWAY = 49722, EVENT_REND = 1, EVENT_FLAME_BREATH = 2, EVENT_KNOCKAWAY = 3, + // Special + EVENT_PRE_LAND = 4, + EVENT_LAND = 5, - SPELL_REND = 43931, - SPELL_FLAME_BREATH = 50653, - SPELL_KNOCK_AWAY = 49722, - - POINT_LAST = 6, + // Special + TYPE_PROTODRAKE_AT = 28, + DATA_PROTODRAKE_MOVE = 6, + POINT_TAKE_OFF = 1, + POINT_PRE_LAND = 2, + POINT_LAND = 3, }; -const Position protodrakeCheckPos = {206.24f, -190.28f, 200.11f, 0.f}; +const Position protodrakeCheckPos{206.24f, -190.28f, 200.11f, 0.f}; +const Position protodrakeTakeOffPos{209.1206f, -187.86578f, 215.00346f}; +const Position protodrakePreLandPos{230.80234f, -164.99632f, 196.74878f}; +const Position protodrakeLandPos{241.2079f, -163.06265f, 193.47125f}; struct npc_enslaved_proto_drake : public ScriptedAI { - npc_enslaved_proto_drake(Creature* creature) : ScriptedAI(creature) - { - _setData = false; - } + explicit npc_enslaved_proto_drake(Creature* creature) : ScriptedAI(creature) { } void Reset() override { @@ -113,22 +116,35 @@ struct npc_enslaved_proto_drake : public ScriptedAI _events.ScheduleEvent(EVENT_REND, 2s, 3s); _events.ScheduleEvent(EVENT_FLAME_BREATH, 5500ms, 7s); _events.ScheduleEvent(EVENT_KNOCKAWAY, 3500ms, 6s); + scheduler.CancelAll(); } void MovementInform(uint32 type, uint32 id) override { - if (type == WAYPOINT_MOTION_TYPE && id == POINT_LAST) + if (type == EFFECT_MOTION_TYPE && id == POINT_TAKE_OFF) { + ScheduleUniqueTimedEvent(500ms, [&] + { + me->GetMotionMaster()->MovePoint(POINT_PRE_LAND, protodrakePreLandPos); + }, EVENT_PRE_LAND); + } + + if (type == POINT_MOTION_TYPE && id == POINT_PRE_LAND) + { + ScheduleUniqueTimedEvent(0s, [&] + { + me->GetMotionMaster()->MovePoint(POINT_LAND, protodrakeLandPos); + }, EVENT_LAND); + } + + if (type == POINT_MOTION_TYPE && id == POINT_LAND) + { + me->SetFacingTo(0.25f); me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0.25f); if (Vehicle* v = me->GetVehicleKit()) if (Unit* p = v->GetPassenger(0)) if (Creature* rider = p->ToCreature()) rider->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0.25f); - - me->SetCanFly(false); - me->SetDisableGravity(false); - me->SetFacingTo(0.25f); - me->SetImmuneToAll(false); } } @@ -137,14 +153,14 @@ struct npc_enslaved_proto_drake : public ScriptedAI if (type == TYPE_PROTODRAKE_AT && data == DATA_PROTODRAKE_MOVE && !_setData && me->IsAlive() && me->GetDistance(protodrakeCheckPos) < 10.0f) { _setData = true; - me->SetCanFly(true); - me->SetDisableGravity(true); - me->GetMotionMaster()->MoveWaypoint(PATH_PROTODRAKE, false); + me->GetMotionMaster()->MoveTakeoff(POINT_TAKE_OFF, protodrakeTakeOffPos, 8.0f); } } void UpdateAI(uint32 diff) override { + scheduler.Update(diff); + if (!UpdateVictim()) return; @@ -178,7 +194,7 @@ struct npc_enslaved_proto_drake : public ScriptedAI } private: - bool _setData; + bool _setData{false}; EventMap _events; }; diff --git a/src/server/scripts/Northrend/zone_crystalsong_forest.cpp b/src/server/scripts/Northrend/zone_crystalsong_forest.cpp index bdf394cad9..645d3c0224 100644 --- a/src/server/scripts/Northrend/zone_crystalsong_forest.cpp +++ b/src/server/scripts/Northrend/zone_crystalsong_forest.cpp @@ -15,52 +15,100 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "CombatAI.h" #include "CreatureScript.h" -#include "PassiveAI.h" #include "Player.h" #include "ScriptedCreature.h" -#include "SmartScriptMgr.h" +#include "SpellAuras.h" +#include "SpellScript.h" #include "Transport.h" #include "Vehicle.h" enum ePreparationsForWar { - NPC_HAMMERHEAD = 30585, - NPC_CLOUDBUSTER = 30470, - TRANSPORT_ORGRIMS_HAMMER = 192241, - TRANSPORT_THE_SKYBREAKER = 192242 + NPC_CLOUDBUSTER = 30470, + NPC_HAMMERHEAD = 30585, + TRANSPORT_ORGRIMS_HAMMER = 192241, + TRANSPORT_THE_SKYBREAKER = 192242, + SEAT_PLAYER = 0, + SPELL_FLIGHT = 48602, + SPELL_TO_ICECROWN_PLAYER_AURA_DISMOUNT_A = 56904, + SPELL_TO_ICECROWN_PLAYER_AURA_DISMOUNT_H = 57419, + SPELL_TO_ICECROWN_AIRSHIP_PLAYER_AURA_TELEPORT_TO_DALARAN = 57460, + SPELL_TO_ICECROWN_AIRSHIP_FROST_WYRM_WAITING_TO_SUMMON_AURA = 57498, + POINT_END = 16, + SPELL_TO_ICECROWN_AIRSHIP_AURA_DISMOUNT_RESPONSE = 56921, // unhandled - vehicle casts 50630 on self + SPELL_EJECT_ALL_PASSENGERS = 50630, + SPELL_TO_ICECROWN_AIRSHIP_TELEPORT_TO_AIRSHIP_A_FORCE_PLAYER_TO_CAST = 57554, + SPELL_TO_ICECROWN_AIRSHIP_TELEPORT_TO_AIRSHIP_H_FORCE_PLAYER_TO_CAST = 57556, + SPELL_TO_ICECROWN_AIRSHIP_TELEPORT_TO_AIRSHIP_A = 56917, + SPELL_TO_ICECROWN_AIRSHIP_TELEPORT_TO_AIRSHIP_H = 57417, }; -struct npc_preparations_for_war_vehicle : public NullCreatureAI +struct npc_preparations_for_war_vehicle : public VehicleAI { - npc_preparations_for_war_vehicle(Creature* creature) : NullCreatureAI(creature) { } - - uint8 pointId; - uint32 searchForShipTimer; - uint32 transportEntry; + explicit npc_preparations_for_war_vehicle(Creature* creature) : VehicleAI(creature), searchForShipTimer(0), transportEntry(me->GetEntry() == NPC_CLOUDBUSTER ? TRANSPORT_THE_SKYBREAKER : TRANSPORT_ORGRIMS_HAMMER) + { + if (transportEntry == TRANSPORT_THE_SKYBREAKER) + { + // 30476 - [DND] Icecrown Flight To Airship Bunny (A) + passenger_x = 31.41805; + passenger_y = 0.126893; + passenger_z = 41.69821; + } + else // TRANSPORT_ORGRIMS_HAMMER + { + // 30588 - [DND] Icecrown Flight To Airship Bunny (H) + passenger_x = -18.10283; + passenger_y = -0.042108; + passenger_z = 45.31725; + } + } - void InitializeAI() override + void PassengerBoarded(Unit* who, int8 /*seatId*/, bool apply) override { - me->GetMotionMaster()->MovePath(me->GetEntry(), FORCED_MOVEMENT_NONE, PathSource::SMART_WAYPOINT_MGR); + if (apply) + { + DoCastSelf(SPELL_TO_ICECROWN_AIRSHIP_PLAYER_AURA_TELEPORT_TO_DALARAN, true); + DoCastSelf(SPELL_FLIGHT, true); + DoCastSelf(me->GetEntry() == NPC_CLOUDBUSTER ? SPELL_TO_ICECROWN_PLAYER_AURA_DISMOUNT_A : SPELL_TO_ICECROWN_PLAYER_AURA_DISMOUNT_H , true); + DoCastSelf(SPELL_TO_ICECROWN_AIRSHIP_FROST_WYRM_WAITING_TO_SUMMON_AURA, true); + me->GetMotionMaster()->MovePath(me->GetEntry(), FORCED_MOVEMENT_NONE, PathSource::SMART_WAYPOINT_MGR); + } + else + who->RemoveAurasDueToSpell(VEHICLE_SPELL_PARACHUTE); // maybe vehicle / seat flag should be responsible for parachute gain? + } - NullCreatureAI::InitializeAI(); - pointId = 0; - searchForShipTimer = 0; - transportEntry = (me->GetEntry() == NPC_HAMMERHEAD ? TRANSPORT_ORGRIMS_HAMMER : TRANSPORT_THE_SKYBREAKER); + void MovementInform(uint32 type, uint32 id) override + { + if (type == ESCORT_MOTION_TYPE && id == POINT_END) + searchForShipTimer = 3000; } - void MovementInform(uint32 type, uint32 /*id*/) override + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override { - if (type == ESCORT_MOTION_TYPE) - if (++pointId == 17) // path size - searchForShipTimer = 3000; + switch (spell->Id) + { + case SPELL_TO_ICECROWN_AIRSHIP_AURA_DISMOUNT_RESPONSE: + break; + case SPELL_TO_ICECROWN_AIRSHIP_TELEPORT_TO_AIRSHIP_A_FORCE_PLAYER_TO_CAST: + case SPELL_TO_ICECROWN_AIRSHIP_TELEPORT_TO_AIRSHIP_H_FORCE_PLAYER_TO_CAST: + { + uint32 teleportSpell = (spell->Id == SPELL_TO_ICECROWN_AIRSHIP_TELEPORT_TO_AIRSHIP_A_FORCE_PLAYER_TO_CAST) + ? SPELL_TO_ICECROWN_AIRSHIP_TELEPORT_TO_AIRSHIP_A + : SPELL_TO_ICECROWN_AIRSHIP_TELEPORT_TO_AIRSHIP_H; + DoCastSelf(teleportSpell, true); // hack: cast on self to avoid visual glitch on player when ejecting and teleporting on transport + DoCastSelf(SPELL_EJECT_ALL_PASSENGERS, true); + me->DespawnOrUnsummon(0s); + break; + } + default: + break; + } } void UpdateAI(uint32 diff) override { - // horde 7.55f, -0.09, 34.44, 3.13, +20 - // ally 45.18f, 0.03, 40.09, 3.14 +5 - if (searchForShipTimer) { searchForShipTimer += diff; @@ -68,37 +116,14 @@ struct npc_preparations_for_war_vehicle : public NullCreatureAI { searchForShipTimer = 1; TransportsContainer const& transports = me->GetMap()->GetAllTransports(); - for (TransportsContainer::const_iterator itr = transports.begin(); itr != transports.end(); ++itr) + for (auto const transport : transports) { - if ((*itr)->GetEntry() == transportEntry) + if (transport->GetEntry() == transportEntry) { - float x, y, z; - if (transportEntry == TRANSPORT_ORGRIMS_HAMMER) - { - x = 7.55f; - y = -0.09f; - z = 54.44f; - } - else - { - x = 45.18f; - y = 0.03f; - z = 45.09f; - } + float x = passenger_x, y = passenger_y, z = passenger_z; + transport->CalculatePassengerPosition(x, y, z); - (*itr)->CalculatePassengerPosition(x, y, z); - - if (me->GetDistance2d(x, y) < 10.0f) - { - me->DespawnOrUnsummon(1s); - if (Vehicle* vehicle = me->GetVehicleKit()) - if (Unit* passenger = vehicle->GetPassenger(0)) - { - passenger->NearTeleportTo(x, y, z - (transportEntry == TRANSPORT_ORGRIMS_HAMMER ? 19.0f : 4.0f), M_PI); - passenger->RemoveAurasDueToSpell(VEHICLE_SPELL_PARACHUTE); // maybe vehicle / seat flag should be responsible for parachute gain? - } - } - else + if (me->GetDistance2d(x, y) > 20.0f) // dismount trigger (56905, 57420) range is 30 me->GetMotionMaster()->MovePoint(0, x, y, z, FORCED_MOVEMENT_NONE, 0.f, 0.f, false, false); break; } @@ -106,6 +131,10 @@ struct npc_preparations_for_war_vehicle : public NullCreatureAI } } } +private: + float passenger_x, passenger_y, passenger_z; + uint32 searchForShipTimer; + uint32 transportEntry; }; /******************************************************* diff --git a/src/server/scripts/Northrend/zone_dalaran.cpp b/src/server/scripts/Northrend/zone_dalaran.cpp index ca887acbe8..7c5b9fba41 100644 --- a/src/server/scripts/Northrend/zone_dalaran.cpp +++ b/src/server/scripts/Northrend/zone_dalaran.cpp @@ -428,7 +428,7 @@ public: struct npc_mageguard_dalaranAI : public ScriptedAI { - npc_mageguard_dalaranAI(Creature* creature) : ScriptedAI(creature) + explicit npc_mageguard_dalaranAI(Creature* creature) : ScriptedAI(creature) { creature->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_NORMAL, true); @@ -449,6 +449,9 @@ public: if (!me->IsWithinDist(who, 5.0f, false)) return; + if (who->IsCreature() && who->GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET) + return; + Player* player = who->GetCharmerOrOwnerPlayerOrPlayerItself(); if (!player || player->IsGameMaster() || player->IsBeingTeleported() || (player->GetPositionZ() > 670 && player->GetVehicle()) || @@ -484,7 +487,6 @@ public: break; } me->SetOrientation(me->GetHomePosition().GetOrientation()); - return; } void UpdateAI(uint32 /*diff*/) override {} diff --git a/src/server/scripts/Northrend/zone_howling_fjord.cpp b/src/server/scripts/Northrend/zone_howling_fjord.cpp index fc28598ec2..928a0a71e8 100644 --- a/src/server/scripts/Northrend/zone_howling_fjord.cpp +++ b/src/server/scripts/Northrend/zone_howling_fjord.cpp @@ -66,109 +66,6 @@ public: } }; -// The cleansing -enum TurmoilTexts -{ - SAY_TURMOIL_0 = 0, - SAY_TURMOIL_1 = 1, - SAY_TURMOIL_HALF_HP = 2, - SAY_TURMOIL_DEATH = 3, -}; - -class npc_your_inner_turmoil : public CreatureScript -{ -public: - npc_your_inner_turmoil() : CreatureScript("npc_your_inner_turmoil") { } - - struct npc_your_inner_turmoilAI : public ScriptedAI - { - npc_your_inner_turmoilAI(Creature* creature) : ScriptedAI(creature) {} - - uint32 timer; - short phase; - bool health50; - - void Reset() override - { - timer = 0; - phase = 0; - health50 = false; - } - - void UpdateAI(uint32 diff) override - { - if (timer >= 6000 && phase < 2) - { - phase++; - setphase(phase); - timer = 0; - } - - timer += diff; - - DoMeleeAttackIfReady(); - } - - void DamageTaken(Unit*, uint32& /*damage*/, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override - { - if (HealthBelowPct(50) && !health50) - { - if (TempSummon const* tempSummon = me->ToTempSummon()) - { - if (WorldObject* summoner = tempSummon->GetSummonerUnit()) - { - Talk(SAY_TURMOIL_HALF_HP, summoner); - } - } - - health50 = true; - } - } - - void JustDied(Unit* /*killer*/) override - { - if (TempSummon const* tempSummon = me->ToTempSummon()) - { - if (WorldObject* summoner = tempSummon->GetSummonerUnit()) - { - Talk(SAY_TURMOIL_DEATH, summoner); - } - } - } - - void setphase(short newPhase) - { - Unit* summoner = me->ToTempSummon() ? me->ToTempSummon()->GetSummonerUnit() : nullptr; - if (!summoner || !summoner->IsPlayer()) - return; - - switch (newPhase) - { - case 1: - Talk(SAY_TURMOIL_0, summoner->ToPlayer()); - return; - case 2: - { - Talk(SAY_TURMOIL_1, summoner->ToPlayer()); - me->SetLevel(summoner->GetLevel()); - me->SetFaction(FACTION_MONSTER); - if (me->GetExactDist(summoner) < 50.0f) - { - me->UpdatePosition(summoner->GetPositionX(), summoner->GetPositionY(), summoner->GetPositionZ(), 0.0f, true); - summoner->CastSpell(me, 50218, true); // clone caster - AttackStart(summoner); - } - } - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_your_inner_turmoilAI(creature); - } -}; - /*###### ## npc_apothecary_hanes ######*/ @@ -457,13 +354,145 @@ class spell_hawk_hunting : public SpellScript } }; +/*###### +## Quest 11317, 11322: The Cleansing +######*/ + +enum TheCleansing +{ + SPELL_CLEANSING_SOUL = 43351, + SPELL_SUMMON_INNER_TURMOIL = 50167, + SPELL_RECENT_MEDITATION = 61720, + SPELL_MIRROR_IMAGE_AURA = 50218, + + QUEST_THE_CLEANSING_H = 11317, + QUEST_THE_CLEANSING_A = 11322 +}; + +// 43365 - The Cleansing: Shrine Cast +class spell_the_cleansing_shrine_cast : public SpellScript +{ + PrepareSpellScript(spell_the_cleansing_shrine_cast); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_RECENT_MEDITATION, SPELL_CLEANSING_SOUL }) && + sObjectMgr->GetQuestTemplate(QUEST_THE_CLEANSING_H) && + sObjectMgr->GetQuestTemplate(QUEST_THE_CLEANSING_A); + } + + SpellCastResult CheckCast() + { + // Error is correct for quest check but may be not correct for aura and this may be a wrong place to send error + if (Player* target = GetExplTargetUnit()->ToPlayer()) + { + if (target->HasAura(SPELL_RECENT_MEDITATION) || (!(target->GetQuestStatus(QUEST_THE_CLEANSING_H) == QUEST_STATUS_INCOMPLETE || + target->GetQuestStatus(QUEST_THE_CLEANSING_A) == QUEST_STATUS_INCOMPLETE))) + { + Spell::SendCastResult(target, GetSpellInfo(), 0, SPELL_FAILED_FIZZLE); + return SPELL_FAILED_FIZZLE; + } + } + return SPELL_CAST_OK; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_CLEANSING_SOUL, true); + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_the_cleansing_shrine_cast::CheckCast); + OnEffectHitTarget += SpellEffectFn(spell_the_cleansing_shrine_cast::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +// 43351 - Cleansing Soul +class spell_the_cleansing_cleansing_soul : public AuraScript +{ + PrepareAuraScript(spell_the_cleansing_cleansing_soul); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SUMMON_INNER_TURMOIL, SPELL_RECENT_MEDITATION }); + } + + void AfterApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->SetStandState(UNIT_STAND_STATE_SIT); + } + + void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + target->SetStandState(UNIT_STAND_STATE_STAND); + target->CastSpell(target, SPELL_SUMMON_INNER_TURMOIL, true); + target->CastSpell(target, SPELL_RECENT_MEDITATION, true); + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_the_cleansing_cleansing_soul::AfterApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_the_cleansing_cleansing_soul::AfterRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } +}; + +// 50217 - The Cleansing: Script Effect Player Cast Mirror Image +class spell_the_cleansing_mirror_image_script_effect : public SpellScript +{ + PrepareSpellScript(spell_the_cleansing_mirror_image_script_effect); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_MIRROR_IMAGE_AURA }); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_MIRROR_IMAGE_AURA, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_the_cleansing_mirror_image_script_effect::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +// 50238 - The Cleansing: Your Inner Turmoil's On Death Cast on Master +class spell_the_cleansing_on_death_cast_on_master : public SpellScript +{ + PrepareSpellScript(spell_the_cleansing_on_death_cast_on_master); + + bool Validate(SpellInfo const* spellInfo) override + { + return ValidateSpellInfo({ uint32(spellInfo->GetEffect(EFFECT_0).CalcValue()) }); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + if (TempSummon* casterSummon = caster->ToTempSummon()) + if (Unit* summoner = casterSummon->GetSummonerUnit()) + summoner->CastSpell(summoner, GetSpellInfo()->Effects[EFFECT_0].CalcValue(), true); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_the_cleansing_on_death_cast_on_master::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + void AddSC_howling_fjord() { new npc_attracted_reef_bull(); - new npc_your_inner_turmoil(); new npc_apothecary_hanes(); new npc_plaguehound_tracker(); new npc_razael_and_lyana(); RegisterCreatureAI(npc_rodin_lightning_enabler); RegisterSpellScript(spell_hawk_hunting); + RegisterSpellScript(spell_the_cleansing_shrine_cast); + RegisterSpellScript(spell_the_cleansing_cleansing_soul); + RegisterSpellScript(spell_the_cleansing_mirror_image_script_effect); + RegisterSpellScript(spell_the_cleansing_on_death_cast_on_master); } diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp index 6cc9c8ae3a..94e300720c 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp @@ -271,12 +271,12 @@ class spell_astromancer_solarian_transform : public AuraScript void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - GetUnitOwner()->HandleStatModifier(UnitMods(UNIT_MOD_ARMOR), TOTAL_PCT, 400.0f, true); + GetUnitOwner()->ApplyStatPctModifier(UNIT_MOD_ARMOR, TOTAL_PCT, 400.0f); } void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - GetUnitOwner()->HandleStatModifier(UnitMods(UNIT_MOD_ARMOR), TOTAL_PCT, 400.0f, false); + GetUnitOwner()->ApplyStatPctModifier(UnitMods(UNIT_MOD_ARMOR), TOTAL_PCT, -80.0f); } void Register() override diff --git a/src/server/scripts/Pet/pet_hunter.cpp b/src/server/scripts/Pet/pet_hunter.cpp index 3a6d979506..b82180e666 100644 --- a/src/server/scripts/Pet/pet_hunter.cpp +++ b/src/server/scripts/Pet/pet_hunter.cpp @@ -99,7 +99,7 @@ struct npc_pet_hunter_snake_trap : public ScriptedAI uint32 health = uint32(107 * (me->GetLevel() - 40) * 0.025f); me->SetCreateHealth(health); - me->SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, (float)health); + me->SetStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, (float)health); me->SetMaxHealth(health); //Add delta to make them not all hit the same time diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 49687928fb..4cb86db3da 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -174,8 +174,8 @@ class spell_dk_raise_ally : public SpellScript if (pInfo) // exist in DB { ghoul->SetCreateHealth(pInfo->health); - ghoul->SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, pInfo->health); - ghoul->SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor)); + ghoul->SetStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, pInfo->health); + ghoul->SetStatFlatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor)); for (uint8 stat = 0; stat < MAX_STATS; ++stat) ghoul->SetCreateStat(Stats(stat), float(pInfo->stats[stat])); } @@ -194,9 +194,9 @@ class spell_dk_raise_ally : public SpellScript // DK Ghoul haste refresh float val = (GetCaster()->m_modAttackSpeedPct[BASE_ATTACK] - 1.0f) * 100.0f; + val *= 2000.0f + 2000.0f * ((100.0f + val) / 100.0f); ghoul->m_modAttackSpeedPct[BASE_ATTACK] = GetCaster()->m_modAttackSpeedPct[BASE_ATTACK]; - ghoul->SetFloatValue(UNIT_FIELD_BASEATTACKTIME, 2000.0f); - ghoul->ApplyPercentModFloatValue(UNIT_FIELD_BASEATTACKTIME, val, true); // we want to reduce attack time + ghoul->SetFloatValue(UNIT_FIELD_BASEATTACKTIME, val); // Strength + Stamina for (uint8 i = STAT_STRENGTH; i <= STAT_STAMINA; ++i) @@ -223,20 +223,20 @@ class spell_dk_raise_ally : public SpellScript value = float(GetCaster()->GetStat(stat)) * mod; value = ghoul->GetTotalStatValue(stat, value); ghoul->SetStat(stat, int32(value)); - ghoul->ApplyStatBuffMod(stat, value, true); + ghoul->UpdateStatBuffMod(stat); } // Attack Power - ghoul->SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, 589 + ghoul->GetStat(STAT_STRENGTH) + ghoul->GetStat(STAT_AGILITY)); - ghoul->SetInt32Value(UNIT_FIELD_ATTACK_POWER, (int32)ghoul->GetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE) * ghoul->GetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_PCT)); - ghoul->SetInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, (int32)ghoul->GetModifierValue(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE)); - ghoul->SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, ghoul->GetModifierValue(UNIT_MOD_ATTACK_POWER, TOTAL_PCT) - 1.0f); + ghoul->SetStatFlatModifier(UNIT_MOD_ATTACK_POWER, BASE_VALUE, 589 + ghoul->GetStat(STAT_STRENGTH) + ghoul->GetStat(STAT_AGILITY)); + ghoul->SetInt32Value(UNIT_FIELD_ATTACK_POWER, (int32)ghoul->GetFlatModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE) * ghoul->GetPctModifierValue(UNIT_MOD_ATTACK_POWER, BASE_PCT)); + ghoul->SetInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, (int32)ghoul->GetFlatModifierValue(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE)); + ghoul->SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, ghoul->GetPctModifierValue(UNIT_MOD_ATTACK_POWER, TOTAL_PCT) - 1.0f); // Health - ghoul->SetModifierValue(UNIT_MOD_HEALTH, TOTAL_VALUE, (ghoul->GetStat(STAT_STAMINA) - ghoul->GetCreateStat(STAT_STAMINA)) * 10.0f); + ghoul->SetStatFlatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, (ghoul->GetStat(STAT_STAMINA) - ghoul->GetCreateStat(STAT_STAMINA)) * 10.0f); // Power Energy - ghoul->SetModifierValue(UnitMods(UNIT_MOD_POWER_START + static_cast<uint8>(POWER_ENERGY)), BASE_VALUE, ghoul->GetCreatePowers(POWER_ENERGY)); + ghoul->SetStatFlatModifier(UnitMods(UNIT_MOD_POWER_START + static_cast<uint8>(POWER_ENERGY)), BASE_VALUE, ghoul->GetCreatePowers(POWER_ENERGY)); ghoul->UpdateAllStats(); ghoul->SetFullHealth(); diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index c6f90e153e..85a12610da 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -443,15 +443,16 @@ class spell_dru_enrage : public AuraScript void RecalculateBaseArmor() { + // Recalculate modifies the list while we're iterating through it, so let's copy it instead Unit::AuraEffectList const& auras = GetTarget()->GetAuraEffectsByType(SPELL_AURA_MOD_BASE_RESISTANCE_PCT); - for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i) + std::vector<AuraEffect*> aurEffs(auras.begin(), auras.end()); + + for (AuraEffect* aurEff : aurEffs) { - SpellInfo const* spellInfo = (*i)->GetSpellInfo(); + SpellInfo const* spellInfo = aurEff->GetSpellInfo(); // Dire- / Bear Form (Passive) if (spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && spellInfo->SpellFamilyFlags.HasFlag(0x0, 0x0, 0x2)) - { - (*i)->RecalculateAmount(); - } + aurEff->RecalculateAmount(); } } diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 68ef5e5540..38f669693f 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -4423,7 +4423,9 @@ class spell_gen_eject_all_passengers : public SpellScript } }; -/* 62539 - Eject Passenger 2 +/* 49259 - Despawn Driver + 49261 - Dismount Passenger + 62539 - Eject Passenger 2 64614 - Eject Passenger 4 64629 - Eject Passenger 1 64630 - Eject Passenger 2 @@ -5671,6 +5673,29 @@ class spell_gen_bm_on : public SpellScript } }; +class spell_gen_whisper_to_controller : public SpellScript +{ + PrepareSpellScript(spell_gen_whisper_to_controller); + + bool Validate(SpellInfo const* spellInfo) override + { + return sObjectMgr->GetBroadcastText(uint32(spellInfo->GetEffect(EFFECT_0).CalcValue())); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + if (TempSummon* casterSummon = caster->ToTempSummon()) + if (Player* target = casterSummon->GetSummonerUnit()->ToPlayer()) + casterSummon->Unit::Whisper(uint32(GetEffectValue()), target, false); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_gen_whisper_to_controller::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + void AddSC_generic_spell_scripts() { RegisterSpellScript(spell_silithyst); @@ -5844,4 +5869,5 @@ void AddSC_generic_spell_scripts() RegisterSpellScript(spell_gen_invis_on); RegisterSpellScript(spell_gen_bm_on); RegisterSpellScript(spell_gen_bm_off); + RegisterSpellScript(spell_gen_whisper_to_controller); } diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index f9cdd76231..0acf87cb21 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -473,11 +473,7 @@ class spell_pal_blessing_of_sanctuary : public AuraScript { Unit* target = GetTarget(); if (Unit* caster = GetCaster()) - { - // xinef: hack - int32 value = 9; - caster->CastCustomSpell(target, SPELL_PALADIN_BLESSING_OF_SANCTUARY_BUFF, &value, &value, 0, true); - } + caster->CastSpell(target, SPELL_PALADIN_BLESSING_OF_SANCTUARY_BUFF, true); } void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) @@ -965,6 +961,24 @@ class spell_pal_lay_on_hands : public SpellScript return true; } + void HandleMaxHealthHeal(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + Unit* target = GetExplTargetUnit(); + + if (!target || !caster) + return; + + uint32 baseHeal = caster->GetMaxHealth(); + uint32 modifiedHeal = target->SpellHealingBonusTaken(caster, GetSpellInfo(), baseHeal, HEAL); + + // EffectHealMaxHealth() ignores healing modifiers, so we pre-apply the + // difference here; this delta will be added on top of the raw heal. + int64 healAdjustment = int64(modifiedHeal) - int64(baseHeal); + + SetHitHeal(healAdjustment); + } + SpellCastResult CheckCast() { Unit* caster = GetCaster(); @@ -1004,6 +1018,7 @@ class spell_pal_lay_on_hands : public SpellScript { OnCheckCast += SpellCheckCastFn(spell_pal_lay_on_hands::CheckCast); AfterHit += SpellHitFn(spell_pal_lay_on_hands::HandleScript); + OnEffectHitTarget += SpellEffectFn(spell_pal_lay_on_hands::HandleMaxHealthHeal, EFFECT_0, SPELL_EFFECT_HEAL_MAX_HEALTH); } int32 _manaAmount; diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp index 870cc73850..4e1b563333 100644 --- a/src/server/scripts/Spells/spell_quest.cpp +++ b/src/server/scripts/Spells/spell_quest.cpp @@ -247,28 +247,6 @@ class spell_q10525_vision_guide : public AuraScript } }; -class spell_q11322_q11317_the_cleansing : public AuraScript -{ - PrepareAuraScript(spell_q11322_q11317_the_cleansing) - - void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* ar = GetCaster(); - if (ar && ar->ToPlayer()) - { - if (ar->ToPlayer()->GetQuestStatus(11317) == QUEST_STATUS_INCOMPLETE || ar->ToPlayer()->GetQuestStatus(11322) == QUEST_STATUS_INCOMPLETE) - ar->SummonCreature(27959, 3032.0f, -5095.0f, 723.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); - - ar->SetStandState(UNIT_STAND_STATE_SIT); - } - } - - void Register() override - { - OnEffectApply += AuraEffectApplyFn(spell_q11322_q11317_the_cleansing::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } -}; - class spell_q10714_on_spirits_wings : public SpellScript { PrepareSpellScript(spell_q10714_on_spirits_wings); @@ -2499,7 +2477,6 @@ void AddSC_quest_spell_scripts() RegisterSpellScript(spell_q12014_steady_as_a_rock); RegisterSpellAndAuraScriptPair(spell_q11026_a11051_banish_the_demons, spell_q11026_a11051_banish_the_demons_aura); RegisterSpellScript(spell_q10525_vision_guide); - RegisterSpellScript(spell_q11322_q11317_the_cleansing); RegisterSpellScript(spell_q10714_on_spirits_wings); RegisterSpellScript(spell_q10720_the_smallest_creature); RegisterSpellScript(spell_q13086_last_line_of_defence); diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index d6a6a3d8cf..e484b2b333 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -18,6 +18,7 @@ #include "AreaDefines.h" #include "CreatureScript.h" #include "Pet.h" +#include "PetDefines.h" #include "Player.h" #include "SpellAuraEffects.h" #include "SpellInfo.h" @@ -25,6 +26,8 @@ #include "SpellScript.h" #include "SpellScriptLoader.h" #include "TemporarySummon.h" +#include "Unit.h" +#include "Util.h" /* * Scripts for spells with SPELLFAMILY_WARLOCK and SPELLFAMILY_GENERIC spells used by warlock players. * Ordered alphabetically using scriptname. @@ -73,6 +76,7 @@ enum WarlockSpells SPELL_WARLOCK_EYE_OF_KILROGG_FLY = 58083, SPELL_WARLOCK_PET_VOID_STAR_TALISMAN = 37386, // Void Star Talisman SPELL_WARLOCK_DEMONIC_PACT_PROC = 48090, + SPELL_WARLOCK_GLYPH_OF_VOIDWALKER = 56247, }; enum WarlockSpellIcons @@ -292,7 +296,7 @@ class spell_warl_generic_scaling : public AuraScript void CalculateResistanceAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/) { - // xinef: pet inherits 40% of resistance from owner and 35% of armor + // pet inherits 40% of resistance from owner and 35% of armor if (Unit* owner = GetUnitOwner()->GetOwner()) { SpellSchoolMask schoolMask = SpellSchoolMask(aurEff->GetSpellInfo()->Effects[aurEff->GetEffIndex()].MiscValue); @@ -308,7 +312,7 @@ class spell_warl_generic_scaling : public AuraScript void CalculateStatAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/) { - // xinef: by default warlock pet inherits 75% of stamina and 30% of intellect + // by default warlock pet inherits 75% of stamina and 30% of intellect if (Unit* owner = GetUnitOwner()->GetOwner()) { Stats stat = Stats(aurEff->GetSpellInfo()->Effects[aurEff->GetEffIndex()].MiscValue); @@ -317,21 +321,33 @@ class spell_warl_generic_scaling : public AuraScript } } - void CalculateAPAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + void CalculateAPAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/) { - // xinef: by default warlock pet inherits 57% of max(SP FIRE, SP SHADOW) as AP - if (Unit* owner = GetUnitOwner()->GetOwner()) + if (Unit* pet = GetUnitOwner()) { - int32 fire = owner->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_FIRE); - int32 shadow = owner->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_SHADOW); - int32 maximum = (fire > shadow) ? fire : shadow; - amount = CalculatePct(std::max<int32>(0, maximum), 57); + // by default warlock pet inherits 57% of max(SP FIRE, SP SHADOW) as AP + if (Unit* owner = pet->GetOwner()) + { + int32 fire = owner->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_FIRE); + int32 shadow = owner->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_SHADOW); + int32 maximum = (fire > shadow) ? fire : shadow; + amount = CalculatePct(std::max<int32>(0, maximum), 57); + + // Glyph of felguard, 99% sure this is a HACK + if (pet->GetEntry() == NPC_FELGUARD) + { + if (AuraEffect* glyph = owner->GetAuraEffect(SPELL_GLYPH_OF_FELGUARD, EFFECT_0)) + { + amount += CalculatePct(pet->GetTotalAuraModValue(UNIT_MOD_ATTACK_POWER) - aurEff->GetAmount() + amount, glyph->GetAmount()); + } + } + } } } void CalculateSPAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) { - // xinef: by default warlock pet inherits 15% of max(SP FIRE, SP SHADOW) as SP + // by default warlock pet inherits 15% of max(SP FIRE, SP SHADOW) as SP if (Unit* owner = GetUnitOwner()->GetOwner()) { int32 fire = owner->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_FIRE); @@ -339,7 +355,7 @@ class spell_warl_generic_scaling : public AuraScript int32 maximum = (fire > shadow) ? fire : shadow; amount = CalculatePct(std::max<int32>(0, maximum), 15); - // xinef: Update appropriate player field + // Update appropriate player field if (owner->IsPlayer()) owner->SetUInt32Value(PLAYER_PET_SPELL_POWER, (uint32)amount); } @@ -1370,81 +1386,27 @@ class spell_warl_shadowburn : public AuraScript } }; -class spell_warl_glyph_of_felguard : public AuraScript +class spell_warl_voidwalker_pet_passive : public AuraScript { - PrepareAuraScript(spell_warl_glyph_of_felguard); + PrepareAuraScript(spell_warl_voidwalker_pet_passive); - void HandleApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) - { - if (Player* player = GetCaster()->ToPlayer()) - { - if (Pet* pet = player->GetPet()) - { - if (pet->GetEntry() == NPC_FELGUARD) - { - pet->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, aurEff->GetAmount(), true); - } - } - } - } - - void HandleRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) - { - if (Player* player = GetCaster()->ToPlayer()) - { - if (Pet* pet = player->GetPet()) - { - if (pet->GetEntry() == NPC_FELGUARD) - { - pet->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, aurEff->GetAmount(), false); - } - } - } - } - - void Register() override - { - OnEffectApply += AuraEffectApplyFn(spell_warl_glyph_of_felguard::HandleApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - OnEffectRemove += AuraEffectRemoveFn(spell_warl_glyph_of_felguard::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } -}; - -class spell_warl_glyph_of_voidwalker : public AuraScript -{ - PrepareAuraScript(spell_warl_glyph_of_voidwalker); - - void HandleApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + bool Validate(SpellInfo const* /*spellInfo*/) override { - if (Player* player = GetCaster()->ToPlayer()) - { - if (Pet* pet = player->GetPet()) - { - if (pet->GetEntry() == NPC_VOIDWALKER) - { - pet->HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_PCT, aurEff->GetAmount(), true); - } - } - } + return ValidateSpellInfo({ SPELL_WARLOCK_GLYPH_OF_VOIDWALKER }); } - void HandleRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + void CalculateAmount(AuraEffect const* /* aurEff */, int32& amount, bool& /*canBeRecalculated*/) { - if (Player* player = GetCaster()->ToPlayer()) - { - if (Pet* pet = player->GetPet()) - { - if (pet->GetEntry() == NPC_VOIDWALKER) - { - pet->HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_PCT, aurEff->GetAmount(), false); - } - } - } + if (Unit* pet = GetUnitOwner()) + if (pet->IsPet()) + if (Unit* owner = pet->ToPet()->GetOwner()) + if (AuraEffect* aurEff = owner->GetAuraEffect(SPELL_WARLOCK_GLYPH_OF_VOIDWALKER, EFFECT_0)) + amount += aurEff->GetAmount(); } void Register() override { - OnEffectApply += AuraEffectApplyFn(spell_warl_glyph_of_voidwalker::HandleApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - OnEffectRemove += AuraEffectRemoveFn(spell_warl_glyph_of_voidwalker::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_warl_voidwalker_pet_passive::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE); } }; @@ -1529,7 +1491,6 @@ void AddSC_warlock_spell_scripts() RegisterSpellScript(spell_warl_unstable_affliction); RegisterSpellScript(spell_warl_drain_soul); RegisterSpellScript(spell_warl_shadowburn); - RegisterSpellScript(spell_warl_glyph_of_felguard); - RegisterSpellScript(spell_warl_glyph_of_voidwalker); + RegisterSpellScript(spell_warl_voidwalker_pet_passive); RegisterSpellScript(spell_warl_demonic_pact_aura); } diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index 23b46b039d..cb669c3f0e 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -563,10 +563,6 @@ public: if (!SpawnAssoc) return; - // check if they're hostile - if (!(me->IsHostileTo(who) || who->IsHostileTo(me))) - return; - if (me->IsValidAttackTarget(who)) { Player* playerTarget = who->ToPlayer(); diff --git a/src/server/shared/DataStores/DBCStructure.h b/src/server/shared/DataStores/DBCStructure.h index 59ebe9b57f..4e7e8ca2cd 100644 --- a/src/server/shared/DataStores/DBCStructure.h +++ b/src/server/shared/DataStores/DBCStructure.h @@ -1208,8 +1208,7 @@ struct ItemRandomPropertiesEntry { uint32 ID; // 0 //char const* InternalName; // 1 - std::array<uint32, MAX_ITEM_ENCHANTMENT_EFFECTS> Enchantment; // 2-4 - //std::array<uint32, 2> UnusedEnchantment; // 5-6 + std::array<uint32, MAX_ITEM_ENCHANTMENT_EFFECTS> Enchantment; // 2-6 std::array<char const*, 16> Name; // 7-22 //uint32 Name_lang_mask; // 23 }; @@ -1220,10 +1219,8 @@ struct ItemRandomSuffixEntry std::array<char const*, 16> Name; // 1-16 //uint32 Name_lang_mask; // 17 //char const* InternalName; // 18 - std::array<uint32, MAX_ITEM_ENCHANTMENT_EFFECTS> Enchantment; // 19-21 - //std::array<uint32, 2> UnusedEnchantment; // 22-23 - std::array<uint32, MAX_ITEM_ENCHANTMENT_EFFECTS> AllocationPct; // 24-26 - //std::array<uint32, 2> UnusedAllocationPct; // 27-28 + std::array<uint32, MAX_ITEM_ENCHANTMENT_EFFECTS> Enchantment; // 19-23 + std::array<uint32, MAX_ITEM_ENCHANTMENT_EFFECTS> AllocationPct; // 24-28 }; #define MAX_ITEM_SET_ITEMS 10 diff --git a/src/server/shared/Network/AsyncAcceptor.h b/src/server/shared/Network/AsyncAcceptor.h index f91c2ca37e..71c58ed937 100644 --- a/src/server/shared/Network/AsyncAcceptor.h +++ b/src/server/shared/Network/AsyncAcceptor.h @@ -20,6 +20,7 @@ #include "IpAddress.h" #include "Log.h" +#include "Systemd.h" #include <atomic> #include <boost/asio/ip/tcp.hpp> #include <functional> @@ -33,10 +34,20 @@ class AsyncAcceptor public: typedef void(*AcceptCallback)(tcp::socket&& newSocket, uint32 threadIndex); - AsyncAcceptor(Acore::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port) : + AsyncAcceptor(Acore::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, bool supportSocketActivation = false) : _acceptor(ioContext), _endpoint(Acore::Net::make_address(bindIp), port), - _socket(ioContext), _closed(false), _socketFactory([this](){ return DefaultSocketFactory(); }) + _socket(ioContext), _closed(false), _socketFactory([this](){ return DefaultSocketFactory(); }), + _supportSocketActivation(supportSocketActivation) { + int const listen_fd = get_listen_fd(); + if (_supportSocketActivation && listen_fd > 0) + { + LOG_DEBUG("network", "Using socket from systemd socket activation"); + boost::system::error_code errorCode; + _acceptor.assign(boost::asio::ip::tcp::v4(), listen_fd, errorCode); + if (errorCode) + LOG_WARN("network", "Failed to assign socket {}", errorCode.message()); + } } template<class T> @@ -72,27 +83,31 @@ public: bool Bind() { boost::system::error_code errorCode; - _acceptor.open(_endpoint.protocol(), errorCode); - if (errorCode) + // with socket activation the acceptor is already open and bound + if (!_acceptor.is_open()) { - LOG_INFO("network", "Failed to open acceptor {}", errorCode.message()); - return false; - } + _acceptor.open(_endpoint.protocol(), errorCode); + if (errorCode) + { + LOG_INFO("network", "Failed to open acceptor {}", errorCode.message()); + return false; + } #if AC_PLATFORM != AC_PLATFORM_WINDOWS - _acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true), errorCode); - if (errorCode) - { - LOG_INFO("network", "Failed to set reuse_address option on acceptor {}", errorCode.message()); - return false; - } + _acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true), errorCode); + if (errorCode) + { + LOG_INFO("network", "Failed to set reuse_address option on acceptor {}", errorCode.message()); + return false; + } #endif - _acceptor.bind(_endpoint, errorCode); - if (errorCode) - { - LOG_INFO("network", "Could not bind to {}:{} {}", _endpoint.address().to_string(), _endpoint.port(), errorCode.message()); - return false; + _acceptor.bind(_endpoint, errorCode); + if (errorCode) + { + LOG_INFO("network", "Could not bind to {}:{} {}", _endpoint.address().to_string(), _endpoint.port(), errorCode.message()); + return false; + } } _acceptor.listen(ACORE_MAX_LISTEN_CONNECTIONS, errorCode); @@ -124,6 +139,7 @@ private: tcp::socket _socket; std::atomic<bool> _closed; std::function<std::pair<tcp::socket*, uint32>()> _socketFactory; + bool _supportSocketActivation; }; template<class T> diff --git a/src/server/shared/Network/SocketMgr.h b/src/server/shared/Network/SocketMgr.h index dc0c5e6f5f..085e4e2380 100644 --- a/src/server/shared/Network/SocketMgr.h +++ b/src/server/shared/Network/SocketMgr.h @@ -19,6 +19,7 @@ #define SocketMgr_h__ #include "AsyncAcceptor.h" +#include "Config.h" #include "Errors.h" #include "NetworkThread.h" #include <boost/asio/ip/tcp.hpp> @@ -42,7 +43,8 @@ public: std::unique_ptr<AsyncAcceptor> acceptor; try { - acceptor = std::make_unique<AsyncAcceptor>(ioContext, bindIp, port); + bool supportSocketActivation = sConfigMgr->GetOption<bool>("Network.UseSocketActivation", false); + acceptor = std::make_unique<AsyncAcceptor>(ioContext, bindIp, port, supportSocketActivation); } catch (boost::system::system_error const& err) { |
