diff --git a/sql/updates/world/4.3.4/2022_01_29_03_world_2017_02_21_02_world.sql b/sql/updates/world/4.3.4/2022_01_29_03_world_2017_02_21_02_world.sql new file mode 100644 index 0000000000..fa0f11d7f8 --- /dev/null +++ b/sql/updates/world/4.3.4/2022_01_29_03_world_2017_02_21_02_world.sql @@ -0,0 +1,6 @@ +ALTER TABLE `spell_enchant_proc_data` + CHANGE `entry` `EnchantID` int(10) UNSIGNED NOT NULL, + CHANGE `customChance` `Chance` float DEFAULT '0' NOT NULL, + CHANGE `PPMChance` `ProcsPerMinute` float DEFAULT '0' NOT NULL, + CHANGE `procEx` `HitMask` int(10) UNSIGNED DEFAULT '0' NOT NULL, + ADD COLUMN `AttributesMask` int(10) UNSIGNED DEFAULT '0' NOT NULL AFTER `HitMask`; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 5bb926d81f..f1fe404f80 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -8005,10 +8005,10 @@ void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemT continue; SpellEnchantProcEntry const* entry = sSpellMgr->GetSpellEnchantProcEvent(enchant_id); - if (entry && entry->procEx) + if (entry && entry->HitMask) { // Check hit/crit/dodge/parry requirement - if ((entry->procEx & damageInfo.GetHitMask()) == 0) + if ((entry->HitMask & damageInfo.GetHitMask()) == 0) continue; } else @@ -8019,6 +8019,10 @@ void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemT continue; } + // check if enchant procs only on white hits + if (entry && (entry->AttributesMask & ENCHANT_PROC_ATTR_WHITE_HIT) && damageInfo.GetSpellInfo()) + continue; + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(pEnchant->EffectArg[s]); if (!spellInfo) { @@ -8030,10 +8034,10 @@ void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemT float chance = pEnchant->EffectPointsMin[s] != 0 ? float(pEnchant->EffectPointsMin[s]) : GetWeaponProcChance(); if (entry) { - if (entry->PPMChance) - chance = GetPPMProcChance(proto->GetDelay(), entry->PPMChance, spellInfo); - else if (entry->customChance) - chance = (float)entry->customChance; + if (entry->ProcsPerMinute) + chance = GetPPMProcChance(proto->GetDelay(), entry->ProcsPerMinute, spellInfo); + else if (entry->Chance) + chance = (float)entry->Chance; } // Apply spell mods @@ -8045,10 +8049,25 @@ void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemT if (roll_chance_f(chance)) { - if (spellInfo->IsPositive()) - CastSpell(this, spellInfo->Id, item); - else - CastSpell(damageInfo.GetVictim(), spellInfo->Id, item); + Unit* target = spellInfo->IsPositive() ? this : damageInfo.GetVictim(); + + // reduce effect values if enchant is limited + CastSpellExtraArgs args(item); + if ((entry->AttributesMask & ENCHANT_PROC_ATTR_LIMIT_60) && target->getLevel() > 60) + { + int32 const lvlDifference = target->getLevel() - 60; + int32 const lvlPenaltyFactor = 4; // 4% lost effectiveness per level + + int32 const effectPct = std::max(0, 100 - (lvlDifference * lvlPenaltyFactor)); + + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (spellInfo->Effects[i].IsEffect()) + args.AddSpellMod(static_cast(SPELLVALUE_BASE_POINT0 + i), CalculatePct(spellInfo->Effects[i].CalcValue(this), effectPct)); + } + } + + CastSpell(target, spellInfo->Id, args); } } } diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 6936d5c06d..dd28c65535 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -2091,8 +2091,8 @@ void SpellMgr::LoadSpellEnchantProcData() mSpellEnchantProcEventMap.clear(); // need for reload case - // 0 1 2 3 - QueryResult result = WorldDatabase.Query("SELECT entry, customChance, PPMChance, procEx FROM spell_enchant_proc_data"); + // 0 1 2 3 4 + QueryResult result = WorldDatabase.Query("SELECT EnchantID, Chance, ProcsPerMinute, HitMask, AttributesMask FROM spell_enchant_proc_data"); if (!result) { TC_LOG_INFO("server.loading", ">> Loaded 0 spell enchant proc event conditions. DB table `spell_enchant_proc_data` is empty."); @@ -2115,9 +2115,10 @@ void SpellMgr::LoadSpellEnchantProcData() SpellEnchantProcEntry spe; - spe.customChance = fields[1].GetUInt32(); - spe.PPMChance = fields[2].GetFloat(); - spe.procEx = fields[3].GetUInt32(); + spe.Chance = fields[1].GetFloat(); + spe.ProcsPerMinute = fields[2].GetFloat(); + spe.HitMask = fields[3].GetUInt32(); + spe.AttributesMask = fields[4].GetUInt32(); mSpellEnchantProcEventMap[enchantId] = spe; diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index d5104d23db..ee2473507a 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -269,11 +269,18 @@ struct SpellProcEntry typedef std::unordered_map SpellProcMap; +enum EnchantProcAttributes +{ + ENCHANT_PROC_ATTR_WHITE_HIT = 0x0000001, // enchant shall only proc off white hits (not abilities) + ENCHANT_PROC_ATTR_LIMIT_60 = 0x0000002 // enchant effects shall be reduced past lvl 60 +}; + struct SpellEnchantProcEntry { - uint32 customChance; - float PPMChance; - uint32 procEx; + float Chance; // if nonzero - overwrite SpellItemEnchantment value + float ProcsPerMinute; // if nonzero - chance to proc is equal to value * aura caster's weapon speed / 60 + uint32 HitMask; // if nonzero - bitmask for matching proc condition based on hit result, see enum ProcFlagsHit + uint32 AttributesMask; // bitmask, see EnchantProcAttributes }; typedef std::unordered_map SpellEnchantProcEventMap;