diff options
author | Shauren <shauren.trinity@gmail.com> | 2016-05-02 18:52:15 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2016-05-02 18:52:15 +0200 |
commit | 110ae3e6261694cd5a9ad1687ee209ef42a55c3e (patch) | |
tree | 77310f66c1d1d703ae9b8723d8aa0b6e2ce3dfe0 /src | |
parent | e8730061530cba48703508297dedc8aeb49681b1 (diff) |
Core/Spells: Implemented RPPM proc effects
Closes #17001
Diffstat (limited to 'src')
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.cpp | 10 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.h | 7 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.cpp | 16 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.h | 4 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Structure.h | 16 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2fmt.h | 2 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCEnums.h | 11 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 1 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 28 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.cpp | 20 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.h | 12 | ||||
-rw-r--r-- | src/server/game/Spells/SpellInfo.cpp | 129 | ||||
-rw-r--r-- | src/server/game/Spells/SpellInfo.h | 6 |
13 files changed, 249 insertions, 13 deletions
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index 3bf770ba2db..6749300000b 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -15,6 +15,9 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ + // DO NOT EDIT! + // Autogenerated from DB2Structure.h + #include "HotfixDatabase.h" // Force locale statments to appear exactly in locale declaration order, right after normal data fetch statement @@ -440,6 +443,13 @@ void HotfixDatabaseConnection::DoPrepareStatements() PrepareStatement(HOTFIX_SEL_SPELL_POWER_DIFFICULTY, "SELECT SpellPowerID, DifficultyID, PowerIndex FROM spell_power_difficulty" " ORDER BY SpellPowerID DESC", CONNECTION_SYNCH); + // SpellProcsPerMinute.db2 + PrepareStatement(HOTFIX_SEL_SPELL_PROCS_PER_MINUTE, "SELECT ID, BaseProcRate, Flags FROM spell_procs_per_minute ORDER BY ID DESC", CONNECTION_SYNCH); + + // SpellProcsPerMinuteMod.db2 + PrepareStatement(HOTFIX_SEL_SPELL_PROCS_PER_MINUTE_MOD, "SELECT ID, Type, Param, Coeff, SpellProcsPerMinuteID FROM spell_procs_per_minute_mod" + " ORDER BY ID DESC", CONNECTION_SYNCH); + // SpellRadius.db2 PrepareStatement(HOTFIX_SEL_SPELL_RADIUS, "SELECT ID, Radius, RadiusPerLevel, RadiusMin, RadiusMax FROM spell_radius ORDER BY ID DESC", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index 438485156d5..999c9316dad 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -15,6 +15,9 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ + // DO NOT EDIT! + // Autogenerated from DB2Structure.h + #ifndef _HOTFIXDATABASE_H #define _HOTFIXDATABASE_H @@ -242,6 +245,10 @@ enum HotfixDatabaseStatements HOTFIX_SEL_SPELL_POWER_DIFFICULTY, + HOTFIX_SEL_SPELL_PROCS_PER_MINUTE, + + HOTFIX_SEL_SPELL_PROCS_PER_MINUTE_MOD, + HOTFIX_SEL_SPELL_RADIUS, HOTFIX_SEL_SPELL_RANGE, diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index a2dccee6f76..2fd9c22b080 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -115,6 +115,8 @@ DB2Storage<SpellLearnSpellEntry> sSpellLearnSpellStore("SpellLear DB2Storage<SpellMiscEntry> sSpellMiscStore("SpellMisc.db2", SpellMiscFormat, HOTFIX_SEL_SPELL_MISC); DB2Storage<SpellPowerEntry> sSpellPowerStore("SpellPower.db2", SpellPowerFormat, HOTFIX_SEL_SPELL_POWER); DB2Storage<SpellPowerDifficultyEntry> sSpellPowerDifficultyStore("SpellPowerDifficulty.db2", SpellPowerDifficultyFormat, HOTFIX_SEL_SPELL_POWER_DIFFICULTY); +DB2Storage<SpellProcsPerMinuteEntry> sSpellProcsPerMinuteStore("SpellProcsPerMinute.db2", SpellProcsPerMinuteFormat, HOTFIX_SEL_SPELL_PROCS_PER_MINUTE); +DB2Storage<SpellProcsPerMinuteModEntry> sSpellProcsPerMinuteModStore("SpellProcsPerMinuteMod.db2", SpellProcsPerMinuteModFormat, HOTFIX_SEL_SPELL_PROCS_PER_MINUTE_MOD); DB2Storage<SpellRadiusEntry> sSpellRadiusStore("SpellRadius.db2", SpellRadiusFormat, HOTFIX_SEL_SPELL_RADIUS); DB2Storage<SpellRangeEntry> sSpellRangeStore("SpellRange.db2", SpellRangeFormat, HOTFIX_SEL_SPELL_RANGE); DB2Storage<SpellReagentsEntry> sSpellReagentsStore("SpellReagents.db2", SpellReagentsFormat, HOTFIX_SEL_SPELL_REAGENTS); @@ -295,6 +297,8 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale) LOAD_DB2(sSpellMiscStore); LOAD_DB2(sSpellPowerStore); LOAD_DB2(sSpellPowerDifficultyStore); + LOAD_DB2(sSpellProcsPerMinuteStore); + LOAD_DB2(sSpellProcsPerMinuteModStore); LOAD_DB2(sSpellRadiusStore); LOAD_DB2(sSpellRangeStore); LOAD_DB2(sSpellReagentsStore); @@ -443,6 +447,9 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale) } } + for (SpellProcsPerMinuteModEntry const* ppmMod : sSpellProcsPerMinuteModStore) + _spellProcsPerMinuteMods[ppmMod->SpellProcsPerMinuteID].push_back(ppmMod); + for (TaxiPathEntry const* entry : sTaxiPathStore) sTaxiPathSetBySource[entry->From][entry->To] = TaxiPathBySourceAndDestination(entry->ID, entry->Cost); @@ -853,6 +860,15 @@ std::vector<SpellPowerEntry const*> DB2Manager::GetSpellPowers(uint32 spellId, D return powers; } +std::vector<SpellProcsPerMinuteModEntry const*> DB2Manager::GetSpellProcsPerMinuteMods(uint32 spellprocsPerMinuteId) const +{ + auto itr = _spellProcsPerMinuteMods.find(spellprocsPerMinuteId); + if (itr != _spellProcsPerMinuteMods.end()) + return itr->second; + + return std::vector<SpellProcsPerMinuteModEntry const*>(); +} + bool DB2Manager::IsToyItem(uint32 toy) const { return _toys.count(toy) > 0; diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 5088b750de4..b42d1dd8bab 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -95,6 +95,7 @@ TC_GAME_API extern DB2Storage<SpellItemEnchantmentConditionEntry> sSpellItemEn TC_GAME_API extern DB2Storage<SpellLearnSpellEntry> sSpellLearnSpellStore; TC_GAME_API extern DB2Storage<SpellMiscEntry> sSpellMiscStore; TC_GAME_API extern DB2Storage<SpellPowerEntry> sSpellPowerStore; +TC_GAME_API extern DB2Storage<SpellProcsPerMinuteEntry> sSpellProcsPerMinuteStore; TC_GAME_API extern DB2Storage<SpellRadiusEntry> sSpellRadiusStore; TC_GAME_API extern DB2Storage<SpellRangeEntry> sSpellRangeStore; TC_GAME_API extern DB2Storage<SpellReagentsEntry> sSpellReagentsStore; @@ -160,6 +161,7 @@ public: typedef std::unordered_map<uint32, std::vector<SpecializationSpellsEntry const*>> SpecializationSpellsContainer; typedef std::unordered_map<uint32, std::vector<SpellPowerEntry const*>> SpellPowerContainer; typedef std::unordered_map<uint32, std::unordered_map<uint32, std::vector<SpellPowerEntry const*>>> SpellPowerDifficultyContainer; + typedef std::unordered_map<uint32, std::vector<SpellProcsPerMinuteModEntry const*>> SpellProcsPerMinuteModContainer; typedef std::unordered_set<uint32> ToyItemIdsContainer; static DB2Manager& Instance(); @@ -192,6 +194,7 @@ public: std::set<uint32> GetPhasesForGroup(uint32 group) const; std::vector<SpecializationSpellsEntry const*> const* GetSpecializationSpells(uint32 specId) const; std::vector<SpellPowerEntry const*> GetSpellPowers(uint32 spellId, Difficulty difficulty = DIFFICULTY_NONE, bool* hasDifficultyPowers = nullptr) const; + std::vector<SpellProcsPerMinuteModEntry const*> GetSpellProcsPerMinuteMods(uint32 spellprocsPerMinuteId) const; bool IsToyItem(uint32 toy) const; private: @@ -218,6 +221,7 @@ private: SpecializationSpellsContainer _specializationSpellsBySpec; SpellPowerContainer _spellPowers; SpellPowerDifficultyContainer _spellPowerDifficulties; + SpellProcsPerMinuteModContainer _spellProcsPerMinuteMods; ToyItemIdsContainer _toys; }; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index 01b468dae9d..a6774b03684 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -1261,6 +1261,22 @@ struct SpellPowerDifficultyEntry uint32 PowerIndex; // 2 }; +struct SpellProcsPerMinuteEntry +{ + uint32 ID; + float BaseProcRate; + uint32 Flags; +}; + +struct SpellProcsPerMinuteModEntry +{ + uint32 ID; + uint32 Type; + uint32 Param; + float Coeff; + uint32 SpellProcsPerMinuteID; +}; + struct SpellRadiusEntry { uint32 ID; // 0 diff --git a/src/server/game/DataStores/DB2fmt.h b/src/server/game/DataStores/DB2fmt.h index def5759a450..2a64a2d4eb5 100644 --- a/src/server/game/DataStores/DB2fmt.h +++ b/src/server/game/DataStores/DB2fmt.h @@ -109,6 +109,8 @@ char const SpellLearnSpellFormat[] = "niii"; char const SpellMiscFormat[] = "niiiiiiiiiiiiiiiiifiiif"; char const SpellPowerDifficultyFormat[] = "nii"; char const SpellPowerFormat[] = "niiiiiiiiiffif"; +char const SpellProcsPerMinuteFormat[] = "nfi"; +char const SpellProcsPerMinuteModFormat[] = "niifi"; char const SpellRadiusFormat[] = "nffff"; char const SpellRangeFormat[] = "nffffiss"; char const SpellReagentsFormat[] = "niiiiiiiiiiiiiiii"; diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 8efa1ab11c0..822415d05ab 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -623,6 +623,17 @@ enum SpellCategoryFlags SPELL_CATEGORY_FLAG_COOLDOWN_EXPIRES_AT_DAILY_RESET = 0x08 }; +enum SpellProcsPerMinuteModType +{ + SPELL_PPM_MOD_HASTE = 1, + SPELL_PPM_MOD_CRIT = 2, + SPELL_PPM_MOD_CLASS = 3, + SPELL_PPM_MOD_SPEC = 4, + SPELL_PPM_MOD_RACE = 5, + SPELL_PPM_MOD_ITEM_LEVEL = 6, + SPELL_PPM_MOD_BATTLEGROUND = 7 +}; + enum TotemCategoryType { TOTEM_CATEGORY_TYPE_KNIFE = 1, diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index c2a3a48f203..6232ceaa7ea 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2553,6 +2553,7 @@ void Player::InitStatsForLevel(bool reapplyMods) SetFloatValue(UNIT_MOD_CAST_HASTE, 1.0f); SetFloatValue(UNIT_FIELD_MOD_HASTE, 1.0f); SetFloatValue(UNIT_FIELD_MOD_RANGED_HASTE, 1.0f); + SetFloatValue(UNIT_FIELD_MOD_HASTE_REGEN, 1.0f); // reset size before reapply auras SetObjectScale(1.0f); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 61d353b6825..ad7549b8327 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -12023,6 +12023,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u HealInfo healInfo = HealInfo(actor, actionTarget, damage, procSpell, procSpell ? SpellSchoolMask(procSpell->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL); ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, 0, procExtra, nullptr, &damageInfo, &healInfo); + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); ProcTriggeredList procTriggered; // Fill procTriggered list for (AuraApplicationMap::const_iterator itr = GetAppliedAuras().begin(); itr!= GetAppliedAuras().end(); ++itr) @@ -12050,9 +12051,6 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u } } } - if (!IsTriggeredAtSpellProcEvent(target, triggerData.aura, procSpell, procFlag, procExtra, attType, isVictim, active, triggerData.spellProcEvent)) - continue; - // do checks using conditions table if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_SPELL_PROC, spellProto->Id, eventInfo.GetActor(), eventInfo.GetActionTarget())) continue; @@ -12061,6 +12059,11 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u if (!triggerData.aura->CallScriptCheckProcHandlers(itr->second, eventInfo)) continue; + bool procSuccess = IsTriggeredAtSpellProcEvent(target, triggerData.aura, procSpell, procFlag, procExtra, attType, isVictim, active, triggerData.spellProcEvent); + triggerData.aura->SetLastProcAttemptTime(now); + if (!procSuccess) + continue; + // Triggered spells not triggering additional spells bool triggered = !spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED) ? (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION)) : false; @@ -12115,8 +12118,14 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u // For players set spell cooldown if need uint32 cooldown = 0; - if (prepare && GetTypeId() == TYPEID_PLAYER && i->spellProcEvent && i->spellProcEvent->cooldown) - cooldown = i->spellProcEvent->cooldown; + if (prepare && GetTypeId() == TYPEID_PLAYER) + { + cooldown = spellInfo->ProcCooldown; + if (i->spellProcEvent && i->spellProcEvent->cooldown) + cooldown = i->spellProcEvent->cooldown; + } + + i->aura->SetLastProcSuccessTime(now); // Note: must SetCantProc(false) before return if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) @@ -12685,11 +12694,13 @@ void Unit::ApplyCastTimePercentMod(float val, bool apply) { ApplyPercentModFloatValue(UNIT_MOD_CAST_SPEED, val, !apply); ApplyPercentModFloatValue(UNIT_MOD_CAST_HASTE, val, !apply); + ApplyPercentModFloatValue(UNIT_FIELD_MOD_HASTE_REGEN, val, !apply); } else { ApplyPercentModFloatValue(UNIT_MOD_CAST_SPEED, -val, apply); ApplyPercentModFloatValue(UNIT_MOD_CAST_HASTE, -val, apply); + ApplyPercentModFloatValue(UNIT_FIELD_MOD_HASTE_REGEN, -val, apply); } } @@ -13068,11 +13079,14 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const chance = victim->GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellProto); } } + + if (spellProto->ProcBasePPM > 0.0f) + chance = aura->CalcPPMProcChance(isVictim ? victim : this); + // Apply chance modifer aura if (Player* modOwner = GetSpellModOwner()) - { modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance); - } + return roll_chance_f(chance); } diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 5e5b17f4993..521abd26e7b 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -368,7 +368,8 @@ m_spellInfo(spellproto), m_casterGuid(!casterGUID.IsEmpty() ? casterGUID : caste m_castItemGuid(castItem ? castItem->GetGUID() : ObjectGuid::Empty), m_castItemLevel(castItemLevel), m_applyTime(time(NULL)), m_owner(owner), m_timeCla(0), m_updateTargetMapInterval(0), m_casterLevel(caster ? caster->getLevel() : m_spellInfo->SpellLevel), m_procCharges(0), m_stackAmount(1), -m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false), m_dropEvent(nullptr) +m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false), m_dropEvent(nullptr), +m_lastProcAttemptTime(std::chrono::steady_clock::now() - Seconds(10)), m_lastProcSuccessTime(std::chrono::steady_clock::now() - Seconds(120)) { std::vector<SpellPowerEntry const*> powers = sDB2Manager.GetSpellPowers(GetId(), caster ? caster->GetMap()->GetDifficultyID() : DIFFICULTY_NONE); for (SpellPowerEntry const* power : powers) @@ -1933,6 +1934,23 @@ void Aura::TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) Remove(); } +float Aura::CalcPPMProcChance(Unit* actor) const +{ + using FSeconds = std::chrono::duration<float, Seconds::period>; + + // Formula see http://us.battle.net/wow/en/forum/topic/8197741003#1 + float ppm = m_spellInfo->CalcProcPPM(actor, m_castItemLevel); + float averageProcInterval = 60.0f / ppm; + + std::chrono::steady_clock::time_point currentTime = std::chrono::steady_clock::now(); + float secondsSinceLastAttempt = std::min(std::chrono::duration_cast<FSeconds>(currentTime - m_lastProcAttemptTime).count(), 10.0f); + float secondsSinceLastProc = std::min(std::chrono::duration_cast<FSeconds>(currentTime - m_lastProcSuccessTime).count(), 1000.0f); + + float chance = std::max(1.0f, 1.0f + ((secondsSinceLastProc / averageProcInterval - 1.5f) * 3.0f)) * ppm * secondsSinceLastAttempt / 60.0f; + RoundToInterval(chance, 0.0f, 1.0f); + return chance * 100.0f; +} + void Aura::_DeleteRemovedApplications() { while (!m_removedApplications.empty()) diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 65cdbced339..27e2333bd97 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -253,6 +253,9 @@ class TC_GAME_API Aura bool IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) const; float CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const; void TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo); + float CalcPPMProcChance(Unit* actor) const; + void SetLastProcAttemptTime(std::chrono::steady_clock::time_point lastProcAttemptTime) { m_lastProcAttemptTime = lastProcAttemptTime; } + void SetLastProcSuccessTime(std::chrono::steady_clock::time_point lastProcSuccessTime) { m_lastProcSuccessTime = lastProcSuccessTime; } // AuraScript void LoadScripts(); @@ -313,12 +316,15 @@ class TC_GAME_API Aura //AuraEffect* m_effects[3]; ApplicationMap m_applications; - bool m_isRemoved:1; - bool m_isSingleTarget:1; // true if it's a single target spell and registered at caster - can change at spell steal for example - bool m_isUsingCharges:1; + bool m_isRemoved; + bool m_isSingleTarget; // true if it's a single target spell and registered at caster - can change at spell steal for example + bool m_isUsingCharges; ChargeDropEvent* m_dropEvent; + std::chrono::steady_clock::time_point m_lastProcAttemptTime; + std::chrono::steady_clock::time_point m_lastProcSuccessTime; + private: Unit::AuraApplicationList m_removedApplications; diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index bc16ad62075..a91c94b63c8 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -995,7 +995,6 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry, SpellEffectEntryMap const& ef } SpellName = spellEntry->Name_lang; - Rank = nullptr; RuneCostID = spellEntry->RuneCostID; SpellDifficultyId = 0; SpellScalingId = spellEntry->ScalingID; @@ -1054,9 +1053,14 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry, SpellEffectEntryMap const& ef // SpellAuraOptionsEntry SpellAuraOptionsEntry const* _options = GetSpellAuraOptions(); + SpellProcsPerMinuteEntry const* _ppm = _options ? sSpellProcsPerMinuteStore.LookupEntry(_options->SpellProcsPerMinuteID) : nullptr; ProcFlags = _options ? _options->ProcTypeMask : 0; ProcChance = _options ? _options->ProcChance : 0; ProcCharges = _options ? _options->ProcCharges : 0; + ProcCooldown = _options ? _options->ProcCategoryRecovery : 0; + ProcBasePPM = _ppm ? _ppm->BaseProcRate : 0.0f; + if (_options) + ProcPPMMods = sDB2Manager.GetSpellProcsPerMinuteMods(_options->SpellProcsPerMinuteID); StackAmount = _options ? _options->CumulativeAura : 0; // SpellAuraRestrictionsEntry @@ -2748,6 +2752,126 @@ std::vector<SpellInfo::CostData> SpellInfo::CalcPowerCost(Unit const* caster, Sp return costs; } +inline float CalcPPMHasteMod(SpellProcsPerMinuteModEntry const* mod, Unit* caster) +{ + float haste = caster->GetFloatValue(UNIT_FIELD_MOD_HASTE); + float rangedHaste = caster->GetFloatValue(UNIT_FIELD_MOD_RANGED_HASTE); + float spellHaste = caster->GetFloatValue(UNIT_MOD_CAST_HASTE); + float regenHaste = caster->GetFloatValue(UNIT_FIELD_MOD_HASTE_REGEN); + + switch (mod->Param) + { + case 1: + return (1.0f / haste - 1.0f) * mod->Coeff; + case 2: + return (1.0f / rangedHaste - 1.0f) * mod->Coeff; + case 3: + return (1.0f / spellHaste - 1.0f) * mod->Coeff; + case 4: + return (1.0f / regenHaste - 1.0f) * mod->Coeff; + case 5: + return (1.0f / std::min(std::min(std::min(haste, rangedHaste), spellHaste), regenHaste) - 1.0f) * mod->Coeff; + default: + break; + } + + return 0.0f; +} + +inline float CalcPPMCritMod(SpellProcsPerMinuteModEntry const* mod, Unit* caster) +{ + if (caster->GetTypeId() != TYPEID_PLAYER) + return 0.0f; + + float crit = caster->GetFloatValue(PLAYER_CRIT_PERCENTAGE); + float rangedCrit = caster->GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE); + float spellCrit = caster->GetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1); + + switch (mod->Param) + { + case 1: + return crit * mod->Coeff * 0.01f; + case 2: + return rangedCrit * mod->Coeff * 0.01f; + case 3: + return spellCrit * mod->Coeff * 0.01f; + case 4: + return std::min(std::min(crit, rangedCrit), spellCrit) * mod->Coeff * 0.01f; + default: + break; + } + + return 0.0f; +} + +inline float CalcPPMItemLevelMod(SpellProcsPerMinuteModEntry const* mod, int32 itemLevel) +{ + if (uint32(itemLevel) == mod->Param) + return 0.0f; + + float itemLevelPoints = GetRandomPropertyPoints(itemLevel, ITEM_QUALITY_RARE, INVTYPE_CHEST, 0); + float basePoints = GetRandomPropertyPoints(mod->Param, ITEM_QUALITY_RARE, INVTYPE_CHEST, 0); + if (itemLevelPoints != basePoints) + return 0.0f; + + return ((itemLevelPoints / basePoints) - 1.0f) * mod->Coeff; +} + +float SpellInfo::CalcProcPPM(Unit* caster, int32 itemLevel) const +{ + float ppm = ProcBasePPM; + for (SpellProcsPerMinuteModEntry const* mod : ProcPPMMods) + { + switch (mod->Type) + { + case SPELL_PPM_MOD_HASTE: + { + ppm *= 1.0f + CalcPPMHasteMod(mod, caster); + break; + } + case SPELL_PPM_MOD_CRIT: + { + ppm *= 1.0f + CalcPPMCritMod(mod, caster); + break; + } + case SPELL_PPM_MOD_CLASS: + { + if (caster->getClassMask() & mod->Param) + ppm *= 1.0f + mod->Coeff; + break; + } + case SPELL_PPM_MOD_SPEC: + { + if (Player* plrCaster = caster->ToPlayer()) + if (plrCaster->GetSpecId(plrCaster->GetActiveTalentGroup()) == mod->Param) + ppm *= 1.0f + mod->Coeff; + break; + } + case SPELL_PPM_MOD_RACE: + { + if (caster->getRaceMask() & mod->Param) + ppm *= 1.0f + mod->Coeff; + break; + } + case SPELL_PPM_MOD_ITEM_LEVEL: + { + ppm *= 1.0f + CalcPPMItemLevelMod(mod, itemLevel); + break; + } + case SPELL_PPM_MOD_BATTLEGROUND: + { + if (caster->GetMap()->IsBattlegroundOrArena()) + ppm *= 1.0f + mod->Coeff; + break; + } + default: + break; + } + } + + return ppm; +} + bool SpellInfo::IsRanked() const { return ChainEntry != NULL; @@ -2766,18 +2890,21 @@ SpellInfo const* SpellInfo::GetFirstRankSpell() const return this; return ChainEntry->first; } + SpellInfo const* SpellInfo::GetLastRankSpell() const { if (!ChainEntry) return NULL; return ChainEntry->last; } + SpellInfo const* SpellInfo::GetNextRankSpell() const { if (!ChainEntry) return NULL; return ChainEntry->next; } + SpellInfo const* SpellInfo::GetPrevRankSpell() const { if (!ChainEntry) diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 4217fb16916..4faf98a0791 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -371,6 +371,9 @@ public: uint32 ProcFlags; uint32 ProcChance; uint32 ProcCharges; + uint32 ProcCooldown; + float ProcBasePPM; + std::vector<SpellProcsPerMinuteModEntry const*> ProcPPMMods; uint32 MaxLevel; uint32 BaseLevel; uint32 SpellLevel; @@ -391,7 +394,6 @@ public: uint32 SpellIconID; uint32 ActiveIconID; char* SpellName; - char* Rank; uint32 MaxTargetLevel; uint32 MaxAffectedTargets; uint32 SpellFamilyName; @@ -560,6 +562,8 @@ public: std::vector<CostData> CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) const; + float CalcProcPPM(Unit* caster, int32 itemLevel) const; + bool IsRanked() const; uint8 GetRank() const; SpellInfo const* GetFirstRankSpell() const; |