aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Entities/Object/Object.cpp13
-rw-r--r--src/server/game/Entities/Player/Player.cpp218
-rw-r--r--src/server/game/Entities/Player/Player.h53
-rw-r--r--src/server/game/Entities/Unit/StatSystem.cpp45
-rw-r--r--src/server/game/Spells/Spell.cpp119
5 files changed, 290 insertions, 158 deletions
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 24f690989d9..5b923fc5a3f 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -758,7 +758,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Playe
Player const* player = ToPlayer();
bool HasSceneInstanceIDs = !player->GetSceneMgr().GetSceneTemplateByInstanceMap().empty();
- bool HasRuneState = ToUnit()->GetPowerIndex(POWER_RUNES) != MAX_POWERS;
+ bool HasRuneState = ToUnit()->GetPowerIndex(POWER_RUNE_BLOOD) != MAX_POWERS;
bool HasActionButtons = true;
data->WriteBit(HasSceneInstanceIDs);
@@ -773,14 +773,11 @@ void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Playe
}
if (HasRuneState)
{
- float baseCd = float(player->GetRuneBaseCooldown());
- uint32 maxRunes = uint32(player->GetMaxPower(POWER_RUNES));
-
- *data << uint8((1 << maxRunes) - 1);
+ *data << uint8((1 << MAX_RUNES) - 1);
*data << uint8(player->GetRunesState());
- *data << uint32(maxRunes);
- for (uint32 i = 0; i < maxRunes; ++i)
- *data << uint8((baseCd - float(player->GetRuneCooldown(i))) / baseCd * 255);
+ *data << uint32(MAX_RUNES);
+ for (uint32 i = 0; i < MAX_RUNES; ++i)
+ *data << uint8(player->GetRuneCooldown(i) * uint32(255) / uint32(RUNE_BASE_COOLDOWN));
}
if (HasActionButtons)
{
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index dcd0e6afcbc..3f74950042a 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -1622,27 +1622,30 @@ void Player::RegenerateAll()
m_regenTimerCount += m_regenTimer;
for (Powers power = POWER_MANA; power < MAX_POWERS; power = Powers(power + 1))
- if (power != POWER_RUNES)
+ if (power != POWER_RUNE_BLOOD && power != POWER_RUNE_FROST && power != POWER_RUNE_UNHOLY)
Regenerate(power);
// Runes act as cooldowns, and they don't need to send any data
if (GetClass() == CLASS_DEATH_KNIGHT)
{
- uint32 regeneratedRunes = 0;
- uint32 regenIndex = 0;
- while (regeneratedRunes < MAX_RECHARGING_RUNES && m_runes->CooldownOrder.size() > regenIndex)
+ for (uint8 i = 0; i < MAX_RUNES; i += 2)
{
- uint8 runeToRegen = m_runes->CooldownOrder[regenIndex];
- uint32 runeCooldown = GetRuneCooldown(runeToRegen);
- if (runeCooldown > m_regenTimer)
+ uint8 runeToRegen = i;
+ uint32 runeCooldown = GetRuneCooldown(i);
+ uint32 secondRuneCd = GetRuneCooldown(i + 1);
+
+ //float cdmod = m_unitData->PowerRegenFlatModifier[GetPowerIndex(GetPowerTypeForBaseRune(runeToRegen))] * 10.0f;
+ // Regenerate second rune of the same type only after first rune is off the cooldown
+ if (secondRuneCd && (runeCooldown > secondRuneCd || !runeCooldown))
{
- SetRuneCooldown(runeToRegen, runeCooldown - m_regenTimer);
- ++regenIndex;
+ runeToRegen = i + 1;
+ runeCooldown = secondRuneCd;
}
+
+ if (runeCooldown > m_regenTimer)
+ SetRuneCooldown(runeToRegen, runeCooldown - m_regenTimer);
else
SetRuneCooldown(runeToRegen, 0);
-
- ++regeneratedRunes;
}
}
@@ -5332,8 +5335,6 @@ void Player::UpdateRating(CombatRating cr)
ApplyAttackTimePercentMod(OFF_ATTACK, oldVal, false);
ApplyAttackTimePercentMod(BASE_ATTACK, newVal, true);
ApplyAttackTimePercentMod(OFF_ATTACK, newVal, true);
- if (GetClass() == CLASS_DEATH_KNIGHT)
- UpdateAllRunesRegen();
break;
case CR_HASTE_RANGED:
ApplyAttackTimePercentMod(RANGED_ATTACK, oldVal, false);
@@ -17734,18 +17735,6 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::Power, loadedPowers), 0);
SetPower(POWER_LUNAR_POWER, 0);
- // Init rune recharge
- if (GetPowerIndex(POWER_RUNES) != MAX_POWERS)
- {
- int32 runes = GetPower(POWER_RUNES);
- int32 maxRunes = GetMaxPower(POWER_RUNES);
- uint32 runeCooldown = GetRuneBaseCooldown();
- while (runes < maxRunes)
- {
- SetRuneCooldown(runes, runeCooldown);
- ++runes;
- }
- }
SetPersonalTabard(fields.personalTabardEmblemStyle, fields.personalTabardEmblemColor, fields.personalTabardBorderStyle,
fields.personalTabardBorderColor, fields.personalTabardBackgroundColor);
@@ -25647,91 +25636,162 @@ void Player::SetTitle(CharTitlesEntry const* title, bool lost)
SendDirectMessage(packet.Write());
}
-uint8 Player::GetRunesState() const
+void Runes::SetRuneState(uint8 index, bool set /*= true*/)
{
- return uint8(m_runes->RuneState & ((1 << GetMaxPower(POWER_RUNES)) - 1));
+ if (set)
+ RuneState |= (1 << index); // usable
+ else
+ RuneState &= ~(1 << index); // on cooldown
}
-uint32 Player::GetRuneBaseCooldown() const
+void Player::ResyncRunes() const
{
- float cooldown = RUNE_BASE_COOLDOWN;
-
- AuraEffectList const& regenAura = GetAuraEffectsByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
- for (AuraEffectList::const_iterator i = regenAura.begin();i != regenAura.end(); ++i)
- if ((*i)->GetMiscValue() == POWER_RUNES)
- cooldown *= 1.0f - (*i)->GetAmount() / 100.0f;
+ WorldPackets::Spells::ResyncRunes data(MAX_RUNES);
+ data.Runes.Start = uint8((1 << MAX_RUNES) - 1);
+ data.Runes.Count = GetRunesState();
- // Runes cooldown are now affected by player's haste from equipment ...
- float hastePct = GetRatingBonusValue(CR_HASTE_MELEE);
+ data.Runes.Cooldowns.reserve(MAX_RUNES);
+ for (uint8 i = 0; i < MAX_RUNES; ++i)
+ data.Runes.Cooldowns.push_back(uint8(GetRuneCooldown(i) * uint32(255) / uint32(RUNE_BASE_COOLDOWN)));
- // ... and some auras.
- hastePct += GetTotalAuraModifier(SPELL_AURA_MOD_MELEE_HASTE);
- hastePct += GetTotalAuraModifier(SPELL_AURA_MOD_MELEE_HASTE_2);
- hastePct += GetTotalAuraModifier(SPELL_AURA_MOD_MELEE_HASTE_3);
+ SendDirectMessage(data.Write());
+}
- cooldown *= 1.0f - (hastePct / 100.0f);
+void Player::SendConvertedRunes() const
+{
- return cooldown;
}
-void Player::SetRuneCooldown(uint8 index, uint32 cooldown)
+void Player::AddRunePower(uint8 mask) const
{
- m_runes->Cooldown[index] = cooldown;
- m_runes->SetRuneState(index, (cooldown == 0) ? true : false);
- int32 activeRunes = std::count(std::begin(m_runes->Cooldown), &m_runes->Cooldown[std::min(GetMaxPower(POWER_RUNES), MAX_RUNES)], 0u);
- if (activeRunes != GetPower(POWER_RUNES))
- SetPower(POWER_RUNES, activeRunes);
+ WorldPackets::Spells::AddRunePower packet;
+ packet.AddedRunesMask = mask; // mask (0x00-0x3F probably)
+ SendDirectMessage(packet.Write());
}
-void Runes::SetRuneState(uint8 index, bool set /*= true*/)
+static RuneType runeSlotTypes[MAX_RUNES] =
{
- auto itr = std::find(CooldownOrder.begin(), CooldownOrder.end(), index);
- if (set)
+ /*0*/ RuneType::Blood,
+ /*1*/ RuneType::Blood,
+ /*2*/ RuneType::Unholy,
+ /*3*/ RuneType::Unholy,
+ /*4*/ RuneType::Frost,
+ /*5*/ RuneType::Frost
+};
+
+void Player::InitRunes()
+{
+ if (GetClass() != CLASS_DEATH_KNIGHT)
+ return;
+
+ m_runes = std::make_unique<Runes>();
+ m_runes->RuneState = 0;
+ m_runes->LastUsedRune = RuneType::Blood;
+ m_runes->LastUsedRuneMask = 0;
+
+ for (uint8 i = 0; i < MAX_RUNES; ++i)
{
- RuneState |= (1 << index); // usable
- if (itr != CooldownOrder.end())
- CooldownOrder.erase(itr);
+ SetBaseRune(i, runeSlotTypes[i]); // init base types
+ SetCurrentRune(i, runeSlotTypes[i]); // init current types
+ SetRuneCooldown(i, 0); // reset cooldowns
+ SetRuneConvertAura(i, nullptr, SPELL_AURA_NONE, nullptr);
+ m_runes->SetRuneState(i);
}
- else
+
+ for (uint8 i = 0; i < MAX_RUNES; ++i)
+ SetRuneCooldown(i, 0); // reset cooldowns
+
+ for (Powers rune : { POWER_RUNE_BLOOD, POWER_RUNE_FROST, POWER_RUNE_UNHOLY })
{
- RuneState &= ~(1 << index); // on cooldown
- if (itr == CooldownOrder.end())
- CooldownOrder.push_back(index);
+ int32 powerIndex = GetPowerIndex(rune);
+ if (powerIndex == MAX_POWERS)
+ continue;
+
+ SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenFlatModifier, powerIndex), 0.0f);
+ SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenInterruptedFlatModifier, powerIndex), 0.0f);
}
}
-void Player::ResyncRunes() const
+Powers Player::GetPowerTypeForBaseRune(uint8 index) const
{
- uint32 maxRunes = uint32(GetMaxPower(POWER_RUNES));
-
- WorldPackets::Spells::ResyncRunes data(maxRunes);
- data.Runes.Start = uint8((1 << maxRunes) - 1);
- data.Runes.Count = GetRunesState();
+ switch (GetBaseRune(index))
+ {
+ case RuneType::Blood: return POWER_RUNE_BLOOD;
+ case RuneType::Frost: return POWER_RUNE_FROST;
+ case RuneType::Unholy: return POWER_RUNE_UNHOLY;
+ default:
+ return MAX_POWERS;
+ }
+}
- float baseCd = float(GetRuneBaseCooldown());
- for (uint32 i = 0; i < maxRunes; ++i)
- data.Runes.Cooldowns.push_back(uint8((baseCd - float(GetRuneCooldown(i))) / baseCd * 255));
+bool Player::IsBaseRuneSlotsOnCooldown(RuneType runeType) const
+{
+ for (uint8 i = 0; i < MAX_RUNES; ++i)
+ if (GetBaseRune(i) == runeType && GetRuneCooldown(i) == 0)
+ return false;
- SendDirectMessage(data.Write());
+ return true;
}
-void Player::InitRunes()
+void Player::SetRuneConvertAura(uint8 index, AuraEffect const* aura, AuraType auraType, SpellInfo const* spellInfo)
{
- if (GetClass() != CLASS_DEATH_KNIGHT)
- return;
+ m_runes->_Runes[index].ConvertAura = aura;
+ m_runes->_Runes[index].ConvertAuraType = auraType;
+ m_runes->_Runes[index].ConvertAuraInfo = spellInfo;
+}
- uint32 runeIndex = GetPowerIndex(POWER_RUNES);
- if (runeIndex == MAX_POWERS)
- return;
+void Player::AddRuneByAuraEffect(uint8 index, RuneType newType, AuraEffect const* aura, AuraType auraType, SpellInfo const* spellInfo)
+{
+ SetRuneConvertAura(index, aura, auraType, spellInfo);
+ ConvertRune(index, newType);
+}
- m_runes = std::make_unique<Runes>();
- m_runes->RuneState = 0;
+void Player::SetRuneCooldown(uint8 index, uint32 cooldown)
+{
+ m_runes->_Runes[index].Cooldown = cooldown;
+ m_runes->SetRuneState(index, (cooldown == 0) ? true : false);
+}
+void Player::RemoveRunesByAuraEffect(AuraEffect const* aura)
+{
for (uint8 i = 0; i < MAX_RUNES; ++i)
- SetRuneCooldown(i, 0); // reset cooldowns
+ {
+ if (m_runes->_Runes[i].ConvertAura == aura)
+ {
+ ConvertRune(i, GetBaseRune(i));
+ SetRuneConvertAura(i, nullptr, SPELL_AURA_NONE, nullptr);
+ }
+ }
+}
- SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenFlatModifier, runeIndex), 0.0f);
- SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenInterruptedFlatModifier, runeIndex), 0.0f);
+void Player::RestoreBaseRune(uint8 index)
+{
+ SpellInfo const* spellInfo = m_runes->_Runes[index].ConvertAuraInfo;
+ AuraType type = m_runes->_Runes[index].ConvertAuraType;
+ // If rune was converted by a non-pasive aura that still active we should keep it converted
+ if (spellInfo)
+ {
+ if (!(spellInfo->Attributes & SPELL_ATTR0_PASSIVE))
+ return;
+
+ // Don't even convert aura for passive convertion
+ if (spellInfo->IsPassive() && type == SPELL_AURA_CONVERT_RUNE)
+ return;
+ }
+
+ ConvertRune(index, GetBaseRune(index));
+ SetRuneConvertAura(index, nullptr, SPELL_AURA_NONE, nullptr);
+}
+
+void Player::ConvertRune(uint8 index, RuneType newType)
+{
+ SetCurrentRune(index, newType);
+
+ WorldPackets::Spells::ConvertRune packet(MAX_RUNES);
+ packet.Index = index;
+ packet.Rune = AsUnderlyingType(newType);
+
+ SendDirectMessage(packet.Write());
}
void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore const& store, ItemContext context, bool broadcast, bool createdByPlayer)
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index e7b7cd939d6..e0239cc08d2 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -396,8 +396,7 @@ struct Areas
float y2;
};
-#define MAX_RUNES 7
-#define MAX_RECHARGING_RUNES 3
+constexpr uint8 MAX_RUNES = 6;
enum RuneCooldowns
{
@@ -405,11 +404,31 @@ enum RuneCooldowns
RUNE_MISS_COOLDOWN = 1500 // cooldown applied on runes when the spell misses
};
+enum class RuneType : uint8
+{
+ Blood = 0,
+ Unholy = 1,
+ Frost = 2,
+ Death = 3,
+ Max = 4
+};
+
+struct RuneInfo
+{
+ RuneType BaseRune;
+ RuneType CurrentRune;
+ uint32 Cooldown;
+ AuraEffect const* ConvertAura;
+ AuraType ConvertAuraType;
+ SpellInfo const* ConvertAuraInfo;
+};
+
struct Runes
{
- std::deque<uint8> CooldownOrder;
- uint32 Cooldown[MAX_RUNES];
+ RuneInfo _Runes[MAX_RUNES];
uint8 RuneState; // mask of available runes
+ RuneType LastUsedRune;
+ uint8 LastUsedRuneMask;
void SetRuneState(uint8 index, bool set = true);
};
@@ -2086,7 +2105,6 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
void ApplyManaRegenBonus(int32 amount, bool apply);
void ApplyHealthRegenBonus(int32 amount, bool apply);
void UpdatePowerRegen(Powers powerType);
- void UpdateAllRunesRegen();
void SetPetSpellPower(uint32 spellPower) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::PetSpellPower), spellPower); }
@@ -2633,11 +2651,30 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
bool isAllowedToLoot(Creature const* creature) const;
UF::DeclinedNames const* GetDeclinedNames() const { return m_playerData->DeclinedNames.has_value() ? &*m_playerData->DeclinedNames : nullptr; }
- uint8 GetRunesState() const;
- uint32 GetRuneCooldown(uint8 index) const { return m_runes->Cooldown[index]; }
- uint32 GetRuneBaseCooldown() const;
+
+ // Runes
+ uint8 GetRunesState() const { return m_runes->RuneState; }
+ RuneType GetBaseRune(uint8 index) const { return RuneType(m_runes->_Runes[index].BaseRune); }
+ RuneType GetCurrentRune(uint8 index) const { return RuneType(m_runes->_Runes[index].CurrentRune); }
+ Powers GetPowerTypeForBaseRune(uint8 index) const;
+ uint32 GetRuneCooldown(uint8 index) const { return m_runes->_Runes[index].Cooldown; }
+ bool IsBaseRuneSlotsOnCooldown(RuneType runeType) const;
+ RuneType GetLastUsedRune() { return m_runes->LastUsedRune; }
+ uint8 GetLastUsedRuneMask() { return m_runes->LastUsedRuneMask; }
+ void ClearLastUsedRuneMask() { m_runes->LastUsedRuneMask = 0; }
+ void SetLastUsedRune(RuneType type) { m_runes->LastUsedRune = type; }
+ void SetLastUsedRuneIndex(uint8 index) { m_runes->LastUsedRuneMask |= (1 << index); }
+ void SetBaseRune(uint8 index, RuneType baseRune) { m_runes->_Runes[index].BaseRune = baseRune; }
+ void SetCurrentRune(uint8 index, RuneType currentRune) { m_runes->_Runes[index].CurrentRune = currentRune; }
+ void SetRuneConvertAura(uint8 index, AuraEffect const* aura, AuraType auraType, SpellInfo const* spellInfo);
+ void AddRuneByAuraEffect(uint8 index, RuneType newType, AuraEffect const* aura, AuraType auraType, SpellInfo const* spellInfo);
void SetRuneCooldown(uint8 index, uint32 cooldown);
+ void RemoveRunesByAuraEffect(AuraEffect const* aura);
+ void RestoreBaseRune(uint8 index);
+ void ConvertRune(uint8 index, RuneType newType);
void ResyncRunes() const;
+ void SendConvertedRunes() const;
+ void AddRunePower(uint8 mask) const;
void InitRunes();
void SendRespondInspectAchievements(Player* player) const;
diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp
index 43bb0689481..aab089781e6 100644
--- a/src/server/game/Entities/Unit/StatSystem.cpp
+++ b/src/server/game/Entities/Unit/StatSystem.cpp
@@ -798,7 +798,7 @@ void Player::ApplyHealthRegenBonus(int32 amount, bool apply)
void Player::UpdatePowerRegen(Powers powerType)
{
uint32 powerIndex = GetPowerIndex(powerType);
- if (powerIndex == MAX_POWERS && powerType != POWER_RUNES)
+ if (powerIndex == MAX_POWERS)
return;
float powerRegenMod = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, powerType) / 5.f;
@@ -852,51 +852,20 @@ void Player::UpdatePowerRegen(Powers powerType)
// Butchery and Anger Management
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenInterruptedFlatModifier, powerIndex), powerRegenMod);
break;
- case POWER_RUNES:
+ case POWER_RUNE_BLOOD:
+ case POWER_RUNE_FROST:
+ case POWER_RUNE_UNHOLY:
{
- UpdateAllRunesRegen(); // @todo: replace this with the code below once runes have been downgraded for Cataclysm Classic
- break;
-
- /*
- // Formular: base cooldown / (1 - haste)
- float regeneration = 0.1f;
- float haste = m_unitData->ModHasteRegen;
- if (haste != 0.f)
- regeneration /= haste;
-
- for (int8 i = 0; i < NUM_RUNE_TYPES; i++)
- {
- float mod = 0.f;
- for (AuraEffect const* effect : GetAuraEffectsByType(SPELL_AURA_MOD_POWER_REGEN))
- if (effect->GetMiscValue() == int32(powerType) && effect->GetMiscValueB() == i)
- mod += effect->GetAmount();
-
- SetFloatValue(PLAYER_RUNE_REGEN_1 + i, regeneration + mod);
- }
+ PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(powerType);
+ SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenFlatModifier, powerIndex), float(1 * IN_MILLISECONDS) / float(RUNE_BASE_COOLDOWN) - powerTypeEntry->RegenPeace);
+ SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenInterruptedFlatModifier, powerIndex), float(1 * IN_MILLISECONDS) / float(RUNE_BASE_COOLDOWN) - powerTypeEntry->RegenCombat);
break;
- */
}
default:
break;
}
}
-void Player::UpdateAllRunesRegen()
-{
- if (GetClass() != CLASS_DEATH_KNIGHT)
- return;
-
- uint32 runeIndex = GetPowerIndex(POWER_RUNES);
- if (runeIndex == MAX_POWERS)
- return;
-
- PowerTypeEntry const* runeEntry = sDB2Manager.GetPowerTypeEntry(POWER_RUNES);
-
- uint32 cooldown = GetRuneBaseCooldown();
- SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenFlatModifier, runeIndex), float(1 * IN_MILLISECONDS) / float(cooldown) - runeEntry->RegenPeace);
- SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenInterruptedFlatModifier, runeIndex), float(1 * IN_MILLISECONDS) / float(cooldown) - runeEntry->RegenCombat);
-}
-
void Player::_ApplyAllStatBonuses()
{
SetCanModifyStats(false);
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 29a652721f3..370acf14178 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -4729,7 +4729,7 @@ void Spell::SendSpellStart()
&& std::find_if(m_powerCost.begin(), m_powerCost.end(), [](SpellPowerCost const& cost) { return cost.Power != POWER_HEALTH; }) != m_powerCost.end())
castFlags |= CAST_FLAG_POWER_LEFT_SELF;
- if (HasPowerTypeCost(POWER_RUNES))
+ if (HasPowerTypeCost(POWER_RUNE_BLOOD) || HasPowerTypeCost(POWER_RUNE_FROST) || HasPowerTypeCost(POWER_RUNE_UNHOLY))
castFlags |= CAST_FLAG_NO_GCD; // not needed, but Blizzard sends it
if (m_spellInfo->HasAttribute(SPELL_ATTR8_HEAL_PREDICTION) && m_casttime && m_caster->IsUnit())
@@ -4775,10 +4775,10 @@ void Spell::SendSpellStart()
{
castData.RemainingRunes->Start = m_runesState; // runes state before
castData.RemainingRunes->Count = player->GetRunesState(); // runes state after
- for (uint8 i = 0; i < player->GetMaxPower(POWER_RUNES); ++i)
+ for (uint8 i = 0; i < MAX_RUNES; ++i)
{
// float casts ensure the division is performed on floats as we need float result
- float baseCd = float(player->GetRuneBaseCooldown());
+ float baseCd = float(RUNE_BASE_COOLDOWN);
castData.RemainingRunes->Cooldowns.push_back((baseCd - float(player->GetRuneCooldown(i))) / baseCd * 255); // rune cooldown passed
}
}
@@ -4786,7 +4786,7 @@ void Spell::SendSpellStart()
{
castData.RemainingRunes->Start = 0;
castData.RemainingRunes->Count = 0;
- for (uint8 i = 0; i < player->GetMaxPower(POWER_RUNES); ++i)
+ for (uint8 i = 0; i < MAX_RUNES; ++i)
castData.RemainingRunes->Cooldowns.push_back(0);
}
}
@@ -4830,7 +4830,7 @@ void Spell::SendSpellGo()
if ((m_caster->GetTypeId() == TYPEID_PLAYER)
&& (m_caster->ToPlayer()->GetClass() == CLASS_DEATH_KNIGHT)
- && HasPowerTypeCost(POWER_RUNES)
+ && (HasPowerTypeCost(POWER_RUNE_BLOOD) || HasPowerTypeCost(POWER_RUNE_FROST) || HasPowerTypeCost(POWER_RUNE_UNHOLY))
&& !(_triggeredCastFlags & TRIGGERED_IGNORE_POWER_AND_REAGENT_COST))
{
castFlags |= CAST_FLAG_NO_GCD; // not needed, but Blizzard sends it
@@ -4881,10 +4881,10 @@ void Spell::SendSpellGo()
Player* player = ASSERT_NOTNULL(m_caster->ToPlayer());
castData.RemainingRunes->Start = m_runesState; // runes state before
castData.RemainingRunes->Count = player->GetRunesState(); // runes state after
- for (uint8 i = 0; i < player->GetMaxPower(POWER_RUNES); ++i)
+ for (uint8 i = 0; i < MAX_RUNES; ++i)
{
// float casts ensure the division is performed on floats as we need float result
- float baseCd = float(player->GetRuneBaseCooldown());
+ float baseCd = float(RUNE_BASE_COOLDOWN);
castData.RemainingRunes->Cooldowns.push_back((baseCd - float(player->GetRuneCooldown(i))) / baseCd * 255); // rune cooldown passed
}
}
@@ -5455,7 +5455,7 @@ void Spell::TakePower()
}
}
- if (powerType == POWER_RUNES)
+ if (powerType == POWER_RUNE_BLOOD || powerType == POWER_RUNE_FROST || powerType == POWER_RUNE_UNHOLY)
{
TakeRunePower(hit);
continue;
@@ -5483,12 +5483,12 @@ void Spell::TakePower()
SpellCastResult Spell::CheckRuneCost() const
{
- int32 runeCost = std::accumulate(m_powerCost.begin(), m_powerCost.end(), 0, [](int32 totalCost, SpellPowerCost const& cost)
+ int32 totalRuneCost = std::accumulate(m_powerCost.begin(), m_powerCost.end(), 0, [](int32 totalCost, SpellPowerCost const& cost)
{
- return totalCost + (cost.Power == POWER_RUNES ? cost.Amount : 0);
+ return totalCost + ((cost.Power == POWER_RUNE_BLOOD || cost.Power == POWER_RUNE_FROST || cost.Power == POWER_RUNE_UNHOLY) ? cost.Amount : 0);
});
- if (!runeCost)
+ if (!totalRuneCost)
return SPELL_CAST_OK;
Player* player = m_caster->ToPlayer();
@@ -5498,12 +5498,31 @@ SpellCastResult Spell::CheckRuneCost() const
if (player->GetClass() != CLASS_DEATH_KNIGHT)
return SPELL_CAST_OK;
- int32 readyRunes = 0;
- for (int32 i = 0; i < player->GetMaxPower(POWER_RUNES); ++i)
- if (player->GetRuneCooldown(i) == 0)
- ++readyRunes;
+ std::array<int32, AsUnderlyingType(RuneType::Max)> runeCost = { }; // blood, frost, unholy, death
+ for (SpellPowerCost const& cost : m_powerCost)
+ {
+ switch (cost.Power)
+ {
+ case POWER_RUNE_BLOOD: runeCost[AsUnderlyingType(RuneType::Blood)] = cost.Amount; break;
+ case POWER_RUNE_FROST: runeCost[AsUnderlyingType(RuneType::Frost)] = cost.Amount; break;
+ case POWER_RUNE_UNHOLY: runeCost[AsUnderlyingType(RuneType::Unholy)] = cost.Amount; break;
+ default:
+ break;
+ }
+ }
- if (readyRunes < runeCost)
+ for (uint32 i = 0; i < MAX_RUNES; ++i)
+ {
+ RuneType rune = player->GetCurrentRune(i);
+ if ((player->GetRuneCooldown(i) == 0) && (runeCost[AsUnderlyingType(rune)] > 0))
+ runeCost[AsUnderlyingType(rune)]--;
+ }
+
+ for (uint8 i = 0; i < AsUnderlyingType(RuneType::Death); ++i)
+ if (runeCost[i] > 0)
+ runeCost[AsUnderlyingType(RuneType::Death)] += runeCost[i];
+
+ if (runeCost[AsUnderlyingType(RuneType::Death)] > MAX_RUNES)
return SPELL_FAILED_NO_POWER; // not sure if result code is correct
return SPELL_CAST_OK;
@@ -5514,20 +5533,70 @@ void Spell::TakeRunePower(bool didHit)
if (m_caster->GetTypeId() != TYPEID_PLAYER || m_caster->ToPlayer()->GetClass() != CLASS_DEATH_KNIGHT)
return;
+ int32 totalRuneCost = std::accumulate(m_powerCost.begin(), m_powerCost.end(), 0, [](int32 totalCost, SpellPowerCost const& cost)
+ {
+ return totalCost + ((cost.Power == POWER_RUNE_BLOOD || cost.Power == POWER_RUNE_FROST || cost.Power == POWER_RUNE_UNHOLY) ? cost.Amount : 0);
+ });
+
+ // Spells that do not consume any runes, do not grant any Runic Power
+ if (!totalRuneCost)
+ return;
+
Player* player = m_caster->ToPlayer();
m_runesState = player->GetRunesState(); // store previous state
+ player->ClearLastUsedRuneMask();
+
+ std::array<int32, AsUnderlyingType(RuneType::Max)> runeCost = { }; // blood, frost, unholy, death
+ for (SpellPowerCost const& cost : m_powerCost)
+ {
+ switch (cost.Power)
+ {
+ case POWER_RUNE_BLOOD: runeCost[AsUnderlyingType(RuneType::Blood)] = cost.Amount; break;
+ case POWER_RUNE_FROST: runeCost[AsUnderlyingType(RuneType::Frost)] = cost.Amount; break;
+ case POWER_RUNE_UNHOLY: runeCost[AsUnderlyingType(RuneType::Unholy)] = cost.Amount; break;
+ default:
+ break;
+ }
+ }
- int32 runeCost = std::accumulate(m_powerCost.begin(), m_powerCost.end(), 0, [](int32 totalCost, SpellPowerCost const& cost)
+ // Let's say we use a skill that requires a Frost rune. This is the order:
+ // - Frost rune
+ // - Death rune, originally a Frost rune
+ // - Death rune, any kind
+ for (uint32 i = 0; i < MAX_RUNES; ++i)
{
- return totalCost + (cost.Power == POWER_RUNES ? cost.Amount : 0);
- });
+ RuneType rune = player->GetCurrentRune(i);
+ if (!player->GetRuneCooldown(i) && runeCost[AsUnderlyingType(rune)] > 0)
+ {
+ player->SetRuneCooldown(i, didHit ? uint32(RUNE_BASE_COOLDOWN) : uint32(RUNE_MISS_COOLDOWN));
+ player->SetLastUsedRune(rune);
+ player->SetLastUsedRuneIndex(i);
+ --runeCost[AsUnderlyingType(rune)];
+ }
+ }
- for (int32 i = 0; i < player->GetMaxPower(POWER_RUNES); ++i)
+ // Find a Death rune where the base rune matches the one we need
+ runeCost[AsUnderlyingType(RuneType::Death)] = runeCost[AsUnderlyingType(RuneType::Blood)] + runeCost[AsUnderlyingType(RuneType::Unholy)] + runeCost[AsUnderlyingType(RuneType::Frost)];
+
+ if (runeCost[AsUnderlyingType(RuneType::Death)] > 0)
{
- if (!player->GetRuneCooldown(i) && runeCost > 0)
+ for (uint8 i = 0; i < MAX_RUNES; ++i)
{
- player->SetRuneCooldown(i, didHit ? player->GetRuneBaseCooldown() : uint32(RUNE_MISS_COOLDOWN));
- --runeCost;
+ RuneType rune = player->GetCurrentRune(i);
+ if (!player->GetRuneCooldown(i) && rune == RuneType::Death)
+ {
+ player->SetRuneCooldown(i, didHit ? uint32(RUNE_BASE_COOLDOWN) : uint32(RUNE_MISS_COOLDOWN));
+ player->SetLastUsedRune(rune);
+ player->SetLastUsedRuneIndex(i);
+ runeCost[AsUnderlyingType(rune)]--;
+
+ // keep Death Rune type if missed
+ if (didHit)
+ player->RestoreBaseRune(i);
+
+ if (runeCost[AsUnderlyingType(RuneType::Death)] == 0)
+ break;
+ }
}
}
}
@@ -7260,8 +7329,8 @@ SpellCastResult Spell::CheckPower() const
return SPELL_FAILED_UNKNOWN;
}
- //check rune cost only if a spell has PowerType == POWER_RUNES
- if (cost.Power == POWER_RUNES)
+ // check rune cost only if a spell has PowerType == POWER_RUNE_BLOOD or POWER_RUNE_FROST or POWER_RUNE_UNHOLY
+ if (cost.Power == POWER_RUNE_BLOOD || cost.Power == POWER_RUNE_FROST || cost.Power == POWER_RUNE_UNHOLY)
{
SpellCastResult failReason = CheckRuneCost();
if (failReason != SPELL_CAST_OK)