aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2016-05-02 18:52:15 +0200
committerShauren <shauren.trinity@gmail.com>2016-05-02 18:52:15 +0200
commit110ae3e6261694cd5a9ad1687ee209ef42a55c3e (patch)
tree77310f66c1d1d703ae9b8723d8aa0b6e2ce3dfe0 /src
parente8730061530cba48703508297dedc8aeb49681b1 (diff)
Core/Spells: Implemented RPPM proc effects
Closes #17001
Diffstat (limited to 'src')
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.cpp10
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.h7
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp16
-rw-r--r--src/server/game/DataStores/DB2Stores.h4
-rw-r--r--src/server/game/DataStores/DB2Structure.h16
-rw-r--r--src/server/game/DataStores/DB2fmt.h2
-rw-r--r--src/server/game/DataStores/DBCEnums.h11
-rw-r--r--src/server/game/Entities/Player/Player.cpp1
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp28
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp20
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.h12
-rw-r--r--src/server/game/Spells/SpellInfo.cpp129
-rw-r--r--src/server/game/Spells/SpellInfo.h6
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;