diff options
Diffstat (limited to 'src/server/game')
45 files changed, 1848 insertions, 1586 deletions
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/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index f888403491..3c952a42d9 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -858,7 +858,7 @@ void SmartAI::AttackStart(Unit* who) return; } - if (who && me->Attack(who, me->IsWithinMeleeRange(who))) + if (who && me->Attack(who, me->IsWithinMeleeRange(who) || _currentRangeMode)) { if (!me->HasUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT)) { @@ -870,7 +870,7 @@ void SmartAI::AttackStart(Unit* who) me->GetMotionMaster()->Clear(false); } - me->GetMotionMaster()->MoveChase(who); + me->GetMotionMaster()->MoveChase(who, _attackDistance); } } } @@ -941,6 +941,35 @@ void SmartAI::PassengerBoarded(Unit* who, int8 seatId, bool apply) void SmartAI::InitializeAI() { GetScript()->OnInitialize(me); + + for (SmartScriptHolder const& event : GetScript()->GetEvents()) + { + if (event.GetActionType() != SMART_ACTION_CAST) + continue; + + if (!(event.action.cast.castFlags & SMARTCAST_MAIN_SPELL)) + continue; + + SetMainSpell(event.action.cast.spell); + break; + } + + // Fallback: use first SMARTCAST_COMBAT_MOVE if no MAIN_SPELL found + if (!_currentRangeMode) + { + for (SmartScriptHolder const& event : GetScript()->GetEvents()) + { + if (event.GetActionType() != SMART_ACTION_CAST) + continue; + + if (!(event.action.cast.castFlags & SMARTCAST_COMBAT_MOVE)) + continue; + + SetMainSpell(event.action.cast.spell); + break; + } + } + if (!me->isDead()) { mJustReset = true; @@ -1083,6 +1112,20 @@ void SmartAI::SetCurrentRangeMode(bool on, float range) me->GetMotionMaster()->MoveChase(victim, _attackDistance); } +void SmartAI::SetMainSpell(uint32 spellId) +{ + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + if (!spellInfo) + return; + + float maxRange = spellInfo->GetMaxRange(false); + if (maxRange <= NOMINAL_MELEE_RANGE) + return; + + _attackDistance = std::max(maxRange - NOMINAL_MELEE_RANGE, 0.0f); + _currentRangeMode = true; +} + void SmartAI::DistanceYourself(float range) { Unit* victim = me->GetVictim(); diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h index d6b957cc00..0876336458 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -67,6 +67,7 @@ public: void SetAutoAttack(bool on) { mCanAutoAttack = on; } void SetCombatMovement(bool on, bool stopOrStartMovement); void SetCurrentRangeMode(bool on, float range = 0.f); + void SetMainSpell(uint32 spellId); void DistanceYourself(float range); void SetFollow(Unit* target, float dist = 0.0f, float angle = 0.0f, uint32 credit = 0, uint32 end = 0, uint32 creditType = 0, bool aliveState = true); void StopFollow(bool complete); diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index a9b42edeb4..2f66b4ac11 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -707,7 +707,6 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u continue; } - // Let us not try to cast spell if we know it is going to fail anyway. Stick to chasing and continue. if (distanceToTarget > spellMaxRange && isWithinLOSInMap) { failedSpellCast = true; @@ -745,12 +744,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE) { - // If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed unless target is outside spell range, out of mana, or LOS. - if (result == SPELL_FAILED_OUT_OF_RANGE || result == SPELL_CAST_OK) - // if we are just out of range, we only chase until we are back in spell range. + if (result == SPELL_FAILED_OUT_OF_RANGE) CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f)); - else // move into melee on any other fail - // if spell fail for any other reason, we chase to melee range, or stay where we are if spellcast was successful. + else if (result != SPELL_CAST_OK) CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(false, 0.f); } @@ -2696,6 +2692,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u { if (me && me->FindMap()) me->FindMap()->LoadGrid(e.target.x, e.target.y); + else if (go && go->FindMap()) + go->FindMap()->LoadGrid(e.target.x, e.target.y); break; } case SMART_ACTION_PLAYER_TALK: diff --git a/src/server/game/AI/SmartScripts/SmartScript.h b/src/server/game/AI/SmartScripts/SmartScript.h index e0f32438c8..250fdd775a 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.h +++ b/src/server/game/AI/SmartScripts/SmartScript.h @@ -207,6 +207,8 @@ public: void AddCreatureSummon(ObjectGuid const& guid); void RemoveCreatureSummon(ObjectGuid const& guid); + SmartAIEventList const& GetEvents() const { return mEvents; } + private: void IncPhase(uint32 p); void DecPhase(uint32 p); diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 5e8e5e9e0c..8dd177dfa6 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -1932,16 +1932,17 @@ enum SmartEventFlags enum SmartCastFlags { - SMARTCAST_INTERRUPT_PREVIOUS = 0x001, // Interrupt any spell casting - SMARTCAST_TRIGGERED = 0x002, // Triggered (this makes spell cost zero mana and have no cast time) - //CAST_FORCE_CAST = 0x004, // Forces cast even if creature is out of mana or out of range - //CAST_NO_MELEE_IF_OOM = 0x008, // Prevents creature from entering melee if out of mana or out of range - //CAST_FORCE_TARGET_SELF = 0x010, // Forces the target to cast this spell on itself - SMARTCAST_AURA_NOT_PRESENT = 0x020, // Only casts the spell if the target does not have an aura from the spell - SMARTCAST_COMBAT_MOVE = 0x040, // Prevents combat movement if cast successful. Allows movement on range, OOM, LOS - SMARTCAST_THREATLIST_NOT_SINGLE = 0x080, // Only cast if the source's threatlist is higher than one. This includes pets (see Skeram's True Fulfillment) - SMARTCAST_TARGET_POWER_MANA = 0x100, // Only cast if the target has power type mana (e.g. Mana Drain) - SMARTCAST_ENABLE_COMBAT_MOVE_ON_LOS = 0x200, + SMARTCAST_INTERRUPT_PREVIOUS = 0x001, // Interrupt any spell casting + SMARTCAST_TRIGGERED = 0x002, // Triggered (this makes spell cost zero mana and have no cast time) + //CAST_FORCE_CAST = 0x004, // Forces cast even if creature is out of mana or out of range + //CAST_NO_MELEE_IF_OOM = 0x008, // Prevents creature from entering melee if out of mana or out of range + //CAST_FORCE_TARGET_SELF = 0x010, // Forces the target to cast this spell on itself + SMARTCAST_AURA_NOT_PRESENT = 0x020, // Only casts the spell if the target does not have an aura from the spell + SMARTCAST_COMBAT_MOVE = 0x040, // Prevents combat movement if cast successful. Allows movement on range, OOM, LOS + SMARTCAST_THREATLIST_NOT_SINGLE = 0x080, // Only cast if the source's threatlist is higher than one. This includes pets (see Skeram's True Fulfillment) + SMARTCAST_TARGET_POWER_MANA = 0x100, // Only cast if the target has power type mana (e.g. Mana Drain) + SMARTCAST_ENABLE_COMBAT_MOVE_ON_LOS = 0x200, // Allows combat movement when not in line of sight + SMARTCAST_MAIN_SPELL = 0x400, // Sets this spell's max range as the creature's chase distance on spawn }; enum SmartFollowType diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index b88d04c65e..4b19fac35c 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -688,7 +688,7 @@ namespace lfg // xinef: dont check compatibile dungeons for already running group (bind problems) if (!isContinue) { - GetCompatibleDungeons(dungeons, players, joinData.lockmap); + GetCompatibleDungeons(dungeons, players, joinData.lockmap, rDungeonId); if (dungeons.empty()) joinData.result = grp ? LFG_JOIN_PARTY_NOT_MEET_REQS : LFG_JOIN_NOT_MEET_REQS; } @@ -1485,7 +1485,7 @@ namespace lfg @param[in] players Set of players to check their dungeon restrictions @param[out] lockMap Map of players Lock status info of given dungeons (Empty if dungeons is not empty) */ - void LFGMgr::GetCompatibleDungeons(LfgDungeonSet& dungeons, LfgGuidSet const& players, LfgLockPartyMap& lockMap) + void LFGMgr::GetCompatibleDungeons(LfgDungeonSet& dungeons, LfgGuidSet const& players, LfgLockPartyMap& lockMap, bool isRDF) { lockMap.clear(); for (LfgGuidSet::const_iterator it = players.begin(); it != players.end() && !dungeons.empty(); ++it) @@ -1496,6 +1496,9 @@ namespace lfg { uint32 dungeonId = (it2->first & 0x00FFFFFF); // Compare dungeon ids + if (it2->second == LFG_LOCKSTATUS_RAID_LOCKED && isRDF && sWorld->getBoolConfig(CONFIG_LFG_ALLOW_COMPLETED)) + continue; + LfgDungeonSet::iterator itDungeon = dungeons.find(dungeonId); if (itDungeon != dungeons.end()) { @@ -1762,10 +1765,8 @@ namespace lfg else { // RDF removes all binds to that map - if (randomDungeon && !sInstanceSaveMgr->PlayerIsPermBoundToInstance(player->GetGUID(), dungeon->map, player->GetDungeonDifficulty())) - { + if (randomDungeon) sInstanceSaveMgr->PlayerUnbindInstance(player->GetGUID(), dungeon->map, player->GetDungeonDifficulty(), true); - } } playersTeleported.push_back(player); diff --git a/src/server/game/DungeonFinding/LFGMgr.h b/src/server/game/DungeonFinding/LFGMgr.h index 426f0c679a..e52bbda7dd 100644 --- a/src/server/game/DungeonFinding/LFGMgr.h +++ b/src/server/game/DungeonFinding/LFGMgr.h @@ -589,7 +589,7 @@ namespace lfg void DecreaseKicksLeft(ObjectGuid guid); void SetState(ObjectGuid guid, LfgState state); void SetCanOverrideRBState(ObjectGuid guid, bool val); - void GetCompatibleDungeons(LfgDungeonSet& dungeons, LfgGuidSet const& players, LfgLockPartyMap& lockMap); + void GetCompatibleDungeons(LfgDungeonSet& dungeons, LfgGuidSet const& players, LfgLockPartyMap& lockMap, bool isRDF = false); void _SaveToDB(ObjectGuid guid); LFGDungeonData const* GetLFGDungeon(uint32 id); 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..70e03af3c6 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 PCT_MOD: - ApplyPercentModFloatVar(m_auraBaseMod[modGroup][modType], amount, apply); + case OFF_ATTACK: + unitMod = UNIT_MOD_DAMAGE_OFFHAND; break; + 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,108 @@ void Player::UpdateItemObtainSpells(Item* item, uint8 bag, uint8 slot) ApplyItemObtainSpells(item, true); } -SpellSchoolMask Player::GetMeleeDamageSchoolMask(WeaponAttackType attackType /*= BASE_ATTACK*/, uint8 damageIndex /*= 0*/) const +// 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) { - if (Item const* weapon = GetWeaponForAttack(attackType, true)) + BaseModGroup modGroup; + switch (attackType) { - return SpellSchoolMask(1 << weapon->GetTemplate()->Damage[damageIndex].DamageType); + case BASE_ATTACK: + modGroup = CRIT_PERCENTAGE; + break; + case OFF_ATTACK: + modGroup = OFFHAND_CRIT_PERCENTAGE; + break; + case RANGED_ATTACK: + modGroup = RANGED_CRIT_PERCENTAGE; + break; + default: + ABORT(); + break; } - return SPELL_SCHOOL_MASK_NORMAL; + 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::_ApplyWeaponDependentAuraMods(Item* item, WeaponAttackType attackType, bool apply) +void Player::UpdateAllWeaponDependentCritAuras() { - 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); + for (uint8 i = BASE_ATTACK; i < MAX_ATTACK; ++i) + UpdateWeaponDependentCritAuras(WeaponAttackType(i)); +} - 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::UpdateWeaponDependentAuras(WeaponAttackType attackType) +{ + UpdateWeaponDependentCritAuras(attackType); + UpdateDamageDoneMods(attackType); + UpdateDamagePctDoneMods(attackType); } -void Player::_ApplyWeaponDependentAuraCritMod(Item* item, WeaponAttackType attackType, AuraEffect const* aura, bool apply) +void Player::ApplyItemDependentAuras(Item* item, bool apply) { - // don't apply mod if item is broken or cannot be used - if (item->IsBroken() || !CanUseAttackType(attackType)) - return; + if (apply) + { + for (auto [spellId, playerSpell]: GetSpellMap()) + { + if (playerSpell->State == PLAYERSPELL_REMOVED) + continue; - // generic not weapon specific case processes in aura code - if (aura->GetSpellInfo()->EquippedItemClass == -1) - return; + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + if (!spellInfo || !spellInfo->IsPassive() || spellInfo->EquippedItemClass < 0) + continue; - if (!sScriptMgr->OnPlayerCanApplyWeaponDependentAuraDamageMod(this, item, attackType, aura, apply)) - return; + if (!HasAura(spellId) && HasItemFitToSpellRequirements(spellInfo)) + AddAura(spellId, this); // no SMSG_SPELL_GO in sniff found + } - BaseModGroup mod = BASEMOD_END; - switch (attackType) - { - case BASE_ATTACK: - mod = CRIT_PERCENTAGE; - break; - case OFF_ATTACK: - mod = OFFHAND_CRIT_PERCENTAGE; - break; - case RANGED_ATTACK: - mod = RANGED_CRIT_PERCENTAGE; - break; - default: - return; - } + // Check talents (they are stored separately from regular spells) + for (auto [spellId, playerTalent] : GetTalentMap()) + { + if (playerTalent->State == PLAYERSPELL_REMOVED) + continue; + + if (!(playerTalent->IsInSpec(GetActiveSpec()))) + continue; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + if (!spellInfo || !spellInfo->IsPassive() || spellInfo->EquippedItemClass < 0) + continue; - if (item->IsFitToSpellRequirements(aura->GetSpellInfo())) - HandleBaseModValue(mod, FLAT_MOD, float (aura->GetAmount()), apply); + if (!HasAura(spellId) && HasItemFitToSpellRequirements(spellInfo)) + AddAura(spellId, this); + } + } + else + RemoveItemDependentAurasAndCasts(item); } -void Player::_ApplyWeaponDependentAuraDamageMod(Item* item, WeaponAttackType attackType, AuraEffect const* aura, bool apply) +bool Player::CheckAttackFitToAuraRequirement(WeaponAttackType attackType, AuraEffect const* aurEff) const { - // 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; + SpellInfo const* spellInfo = aurEff->GetSpellInfo(); + if (spellInfo->EquippedItemClass == -1) + return true; - // generic not weapon specific case processes in aura code - if (aura->GetSpellInfo()->EquippedItemClass == -1) - return; + Item* item = GetWeaponForAttack(attackType, true); + if (!item || !item->IsFitToSpellRequirements(spellInfo)) + return false; - UnitMods unitMod = UNIT_MOD_END; - 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: - return; - } + return true; +} - UnitModifierType unitModType = TOTAL_VALUE; - switch (aura->GetAuraType()) +SpellSchoolMask Player::GetMeleeDamageSchoolMask(WeaponAttackType attackType /*= BASE_ATTACK*/, uint8 damageIndex /*= 0*/) const +{ + if (Item const* weapon = GetWeaponForAttack(attackType, true)) { - case SPELL_AURA_MOD_DAMAGE_DONE: - unitModType = TOTAL_VALUE; - break; - case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: - unitModType = TOTAL_PCT; - break; - default: - return; + return SpellSchoolMask(1 << weapon->GetTemplate()->Damage[damageIndex].DamageType); } - if (item->IsFitToSpellRequirements(aura->GetSpellInfo())) - { - 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 SPELL_SCHOOL_MASK_NORMAL; } void Player::ApplyItemEquipSpell(Item* item, bool apply, bool form_change) @@ -7464,12 +7529,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 +7637,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 +7663,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(); } @@ -7837,7 +7894,9 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) // And permit out of range GO with no owner in case fishing hole if (!go || (loot_type != LOOT_FISHINGHOLE && ((loot_type != LOOT_FISHING && loot_type != LOOT_FISHING_JUNK) || go->GetOwnerGUID() != GetGUID()) && !go->IsWithinDistInMap(this)) || (loot_type == LOOT_CORPSE && go->GetRespawnTime() && go->isSpawnedByDefault())) { - go->ForceValuesUpdateAtIndex(GAMEOBJECT_BYTES_1); + if (go) + go->ForceValuesUpdateAtIndex(GAMEOBJECT_BYTES_1); + SendLootRelease(guid); return; } @@ -12583,9 +12642,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/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index fd5167cbe1..c6485d813c 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -656,7 +656,8 @@ void WorldSession::HandleReclaimCorpseOpcode(WorldPacket& recv_data) if (time_t(corpse->GetGhostTime() + _player->GetCorpseReclaimDelay(corpse->GetType() == CORPSE_RESURRECTABLE_PVP)) > time_t(GameTime::GetGameTime().count())) return; - if (!corpse->IsWithinDistInMap(_player, CORPSE_RECLAIM_RADIUS, true)) + // skip phase check + if (!corpse->IsInMap(_player) || !corpse->IsWithinDist(_player, CORPSE_RECLAIM_RADIUS, true)) return; // resurrect diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 8e7e6c1f35..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()) { @@ -415,33 +417,31 @@ void WorldSession::HandleMoverRelocation(MovementInfo& movementInfo, Unit* mover if (mover->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT)) { - // if we boarded a transport, add us to it - if (Player* plrMover = mover->ToPlayer()) + // if we boarded a transport, add us to it (generalized for both players and creatures) + if (!mover->GetTransport()) { - if (!plrMover->GetTransport()) + if (Transport* transport = mover->GetMap()->GetTransport(movementInfo.transport.guid)) { - if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) - { - plrMover->m_transport = transport; - transport->AddPassenger(plrMover); - } + mover->SetTransport(transport); + transport->AddPassenger(mover); } - else if (plrMover->GetTransport()->GetGUID() != movementInfo.transport.guid) + } + else if (mover->GetTransport()->GetGUID() != movementInfo.transport.guid) + { + // Switching transports + bool foundNewTransport = false; + mover->GetTransport()->RemovePassenger(mover); + if (Transport* transport = mover->GetMap()->GetTransport(movementInfo.transport.guid)) { - bool foundNewTransport = false; - plrMover->m_transport->RemovePassenger(plrMover); - if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) - { - foundNewTransport = true; - plrMover->m_transport = transport; - transport->AddPassenger(plrMover); - } + foundNewTransport = true; + mover->SetTransport(transport); + transport->AddPassenger(mover); + } - if (!foundNewTransport) - { - plrMover->m_transport = nullptr; - movementInfo.transport.Reset(); - } + if (!foundNewTransport) + { + mover->SetTransport(nullptr); + movementInfo.transport.Reset(); } } @@ -449,23 +449,20 @@ void WorldSession::HandleMoverRelocation(MovementInfo& movementInfo, Unit* mover { GameObject* go = mover->GetMap()->GetGameObject(movementInfo.transport.guid); if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) - { movementInfo.RemoveMovementFlag(MOVEMENTFLAG_ONTRANSPORT); - } } } - else if (mover->IsPlayer()) + else { - if (Player* plrMover = mover->ToPlayer()) + // if we were on a transport, leave (handles both players and creatures) + if (Transport* transport = mover->GetTransport()) { - if (plrMover->GetTransport()) // if we were on a transport, leave - { - sScriptMgr->AnticheatSetUnderACKmount(plrMover); // just for safe + if (mover->IsPlayer()) + sScriptMgr->AnticheatSetUnderACKmount(mover->ToPlayer()); // just for safe - plrMover->m_transport->RemovePassenger(plrMover); - plrMover->m_transport = nullptr; - movementInfo.transport.Reset(); - } + transport->RemovePassenger(mover); + mover->SetTransport(nullptr); + movementInfo.transport.Reset(); } } 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/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 04ff5ba9b1..254328b809 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -125,7 +125,7 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 accountFlags, s m_playerLogout(false), m_playerRecentlyLogout(false), m_playerSave(false), - m_sessionDbcLocale(sWorld->GetDefaultDbcLocale()), + m_sessionDbcLocale(sWorld->GetAvailableDbcLocale(locale)), m_sessionDbLocaleIndex(locale), m_latency(0), m_TutorialsChanged(false), 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 aad6a4f2d6..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" @@ -643,6 +642,8 @@ Spell::Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, gameObjTarget = nullptr; destTarget = nullptr; damage = 0; + m_reflectionTarget = nullptr; + m_reflectionTargetGuid.Clear(); effectHandleMode = SPELL_EFFECT_HANDLE_LAUNCH; m_diminishLevel = DIMINISHING_LEVEL_1; m_diminishGroup = DIMINISHING_NONE; @@ -1238,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); } @@ -1325,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); } @@ -2591,6 +2584,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) //Spells with this flag cannot trigger if effect is casted on self bool canEffectTrigger = !m_spellInfo->HasAttribute(SPELL_ATTR3_SUPPRESS_CASTER_PROCS) && unitTarget->CanProc() && (CanExecuteTriggersOnHit(mask) || missInfo == SPELL_MISS_IMMUNE2); bool reflectedSpell = missInfo == SPELL_MISS_REFLECT; + Unit* reflectionSource = nullptr; Unit* spellHitTarget = nullptr; if (missInfo == SPELL_MISS_NONE) // In case spell hit target, do all effect on that target @@ -2602,6 +2596,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) { spellHitTarget = m_caster; unitTarget = m_caster; + reflectionSource = effectUnit; if (m_caster->IsCreature()) m_caster->ToCreature()->LowerPlayerDamageReq(target->damage); } @@ -2609,7 +2604,24 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) if (spellHitTarget) { + if (reflectionSource) + { + m_reflectionTarget = reflectionSource; + m_reflectionTargetGuid = reflectionSource->GetGUID(); + m_reflectionTargetPosition.Relocate(reflectionSource); + } + else + { + m_reflectionTarget = nullptr; + m_reflectionTargetGuid.Clear(); + m_reflectionTargetPosition = Position(); + } + SpellMissInfo missInfo2 = DoSpellHitOnUnit(spellHitTarget, mask, target->scaleAura); + + m_reflectionTarget = nullptr; + m_reflectionTargetGuid.Clear(); + m_reflectionTargetPosition = Position(); if (missInfo2 != SPELL_MISS_NONE) { if (missInfo2 != SPELL_MISS_MISS) @@ -6055,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 @@ -6540,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) @@ -6588,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; @@ -6697,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) @@ -6947,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); @@ -7884,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/Spell.h b/src/server/game/Spells/Spell.h index 331d48997a..0f8cde5d37 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -666,6 +666,9 @@ public: WorldLocation* destTarget; int32 damage; SpellEffectHandleMode effectHandleMode; + Unit* m_reflectionTarget; + ObjectGuid m_reflectionTargetGuid; + Position m_reflectionTargetPosition; // used in effects handlers Aura* m_spellAura; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 8bb4c793ef..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); } @@ -5013,7 +5010,24 @@ void Spell::EffectKnockBack(SpellEffIndex effIndex) return; float x, y; - if (m_spellInfo->Effects[effIndex].Effect == SPELL_EFFECT_KNOCK_BACK_DEST) + Unit* reflectionSource = m_reflectionTarget; + + if (!reflectionSource && !m_reflectionTargetGuid.IsEmpty()) + { + if (Unit* resolvedSource = ObjectAccessor::GetUnit(*m_caster, m_reflectionTargetGuid)) + reflectionSource = resolvedSource; + } + + if (reflectionSource) + { + reflectionSource->GetPosition(x, y); + } + else if (!m_reflectionTargetGuid.IsEmpty()) + { + x = m_reflectionTargetPosition.GetPositionX(); + y = m_reflectionTargetPosition.GetPositionY(); + } + else if (m_spellInfo->Effects[effIndex].Effect == SPELL_EFFECT_KNOCK_BACK_DEST) { if (m_targets.HasDst()) destTarget->GetPosition(x, y); 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/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index 761b8dd7be..6ae1b16cec 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -555,8 +555,8 @@ void WorldConfig::BuildConfigCache() // Dungeon finder SetConfigValue<uint32>(CONFIG_LFG_OPTIONSMASK, "DungeonFinder.OptionsMask", 5); - SetConfigValue<bool>(CONFIG_LFG_CAST_DESERTER, "DungeonFinder.CastDeserter", true); + SetConfigValue<bool>(CONFIG_LFG_ALLOW_COMPLETED, "DungeonFinder.AllowCompleted", true); // DBC_ItemAttributes SetConfigValue<bool>(CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES, "DBC.EnforceItemAttributes", true); diff --git a/src/server/game/World/WorldConfig.h b/src/server/game/World/WorldConfig.h index 540fd8324c..7a34aad36e 100644 --- a/src/server/game/World/WorldConfig.h +++ b/src/server/game/World/WorldConfig.h @@ -94,6 +94,7 @@ enum ServerConfigs CONFIG_ALLOW_TICKETS, CONFIG_DELETE_CHARACTER_TICKET_TRACE, CONFIG_LFG_CAST_DESERTER, + CONFIG_LFG_ALLOW_COMPLETED, CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES, CONFIG_PRESERVE_CUSTOM_CHANNELS, CONFIG_PDUMP_NO_PATHS, |
