diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 0f5c736a6a..dc9eeace1b 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -2476,8 +2476,8 @@ struct UnitPowerBarEntry uint32 MaxPower; // 3 uint32 StartPower; // 4 //uint32 CenterPower; // 5 - //float RegenerationPeace; // 6 - //float RegenerationCombat; // 7 + float RegenerationPeace; // 6 + float RegenerationCombat; // 7 //uint32 BarType; // 8 //uint32 FileDataID[6]; // 9 - 14 //uint32 Color[6]; // 15 - 20 diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 20a635d797..031932e169 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -184,7 +184,7 @@ char const TaxiNodesEntryfmt[] = "nifffsiiiff"; char const TaxiPathEntryfmt[] = "niii"; char const TaxiPathNodeEntryfmt[] = "diiifffiiii"; char const TotemCategoryEntryfmt[] = "nxii"; -char const UnitPowerBarfmt[] = "niiixxxxxxxxxxxxxxxxxxxxxxx"; +char const UnitPowerBarfmt[] = "niiixffxxxxxxxxxxxxxxxxxxxx"; char const TransportAnimationfmt[] = "diifffx"; char const TransportRotationfmt[] = "diiffff"; char const VehicleEntryfmt[] = "niffffiiiiiiiifffffffffffffffssssfifiiii"; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 006bd12910..bae24fcf59 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -205,7 +205,7 @@ m_AlreadySearchedAssistance(false), m_regenHealth(true), m_cannotReachTarget(fal m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0), m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false) { - m_regenTimer = CREATURE_REGEN_INTERVAL; + m_regenTimer = REGENERATION_INTERVAL; m_valuesCount = UNIT_END; for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i) @@ -788,7 +788,7 @@ void Creature::Update(uint32 diff) else RegenerateMana(); } - m_regenTimer = CREATURE_REGEN_INTERVAL; + m_regenTimer = REGENERATION_INTERVAL; } // Update serverside orientation of channeled spells that are suposed to track the channel target @@ -839,7 +839,7 @@ void Creature::RegenerateMana() if ((*i)->GetMiscValue() == POWER_MANA) AddPct(addvalue, (*i)->GetAmount()); - addvalue += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA) * CREATURE_REGEN_INTERVAL / (5 * IN_MILLISECONDS); + addvalue += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA) * REGENERATION_INTERVAL / (5 * IN_MILLISECONDS); ModifyPower(POWER_MANA, addvalue); } @@ -876,7 +876,7 @@ void Creature::RegenerateHealth() for (AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) AddPct(addvalue, (*i)->GetAmount()); - addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_REGEN) * CREATURE_REGEN_INTERVAL / (5 * IN_MILLISECONDS); + addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_REGEN) * REGENERATION_INTERVAL / (5 * IN_MILLISECONDS); ModifyHealth(addvalue); } diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index f823de369d..cd3d9dd2a7 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -318,6 +318,7 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma static float _GetDamageMod(int32 Rank); + uint32 m_regenTimer; float m_SightDistance, m_CombatDistance; bool m_isTempWorldObject; //true when possessed diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index 17c38ab39c..04fcb1ccab 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -122,7 +122,6 @@ struct TC_GAME_API CreatureMovementData std::string ToString() const; }; -static uint32 const CREATURE_REGEN_INTERVAL = 2 * IN_MILLISECONDS; static uint32 const CREATURE_NOPATH_EVADE_TIME = 5 * IN_MILLISECONDS; static uint8 const MAX_KILL_CREDIT = 2; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 63342ad962..1a77bb1980 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -641,7 +641,7 @@ void Creature::Regenerate(Powers power) // Apply modifiers (if any). AddPct(addvalue, GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, power)); - addvalue += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, power) * (IsHunterPet() ? PetFocusRegenInterval : CREATURE_REGEN_INTERVAL) / (5 * IN_MILLISECONDS); + addvalue += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, power) * (IsHunterPet() ? PetFocusRegenInterval : REGENERATION_INTERVAL) / (5 * IN_MILLISECONDS); ModifyPower(power, int32(addvalue)); } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 08c297ed19..eecd344d54 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -159,10 +159,7 @@ Player::Player(WorldSession* session): Unit(true) m_comboPoints = 0; - m_regenTimer = 0; - m_regenTimerCount = 0; - m_holyPowerRegenTimerCount = 0; - m_focusRegenTimerCount = 0; + _regenerationTimer = 0; m_foodEmoteTimerCount = 0; m_weaponChangeTimer = 0; @@ -1223,10 +1220,7 @@ void Player::Update(uint32 p_time) } if (IsAlive()) - { - m_regenTimer += p_time; - RegenerateAll(); - } + RegenerateAll(p_time); if (m_deathState == JUST_DIED) KillPlayer(); @@ -1838,24 +1832,18 @@ bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Un return Unit::IsImmunedToSpellEffect(spellInfo, index, caster); } -void Player::RegenerateAll() +void Player::RegenerateAll(uint32 diff) { - //if (m_regenTimer <= 500) - // return; + _regenerationTimer += diff; + m_foodEmoteTimerCount += diff; - m_regenTimerCount += m_regenTimer; - - if (getClass() == CLASS_PALADIN) - m_holyPowerRegenTimerCount += m_regenTimer; - - if (getClass() == CLASS_HUNTER) - m_focusRegenTimerCount += m_regenTimer; - - m_foodEmoteTimerCount += m_regenTimer; - - Regenerate(POWER_ENERGY); - Regenerate(POWER_MANA); - Regenerate(POWER_FOCUS); + Regenerate(POWER_ENERGY, diff); + Regenerate(POWER_MANA, diff); + Regenerate(POWER_FOCUS, diff); + Regenerate(POWER_HOLY_POWER, diff); + Regenerate(POWER_RUNIC_POWER, diff); + Regenerate(POWER_RAGE, diff); + Regenerate(POWER_ALTERNATE_POWER, diff); // Runes act as cooldowns, and they don't need to send any data if (getClass() == CLASS_DEATH_KNIGHT) @@ -1874,11 +1862,11 @@ void Player::RegenerateAll() } if (cd) - SetRuneCooldown(runeToRegen, (cd > (m_regenTimer * cdmod)) ? cd - (m_regenTimer * cdmod) : 0); + SetRuneCooldown(runeToRegen, (cd > (diff * cdmod)) ? cd - (diff * cdmod) : 0); } } - if (m_regenTimerCount >= 2000) + if (_regenerationTimer >= REGENERATION_INTERVAL) { // Not in combat or they have regeneration if (!IsInCombat() || IsPolymorphed() || m_baseHealthRegen || @@ -1888,21 +1876,9 @@ void Player::RegenerateAll() RegenerateHealth(); } - Regenerate(POWER_RAGE); - if (getClass() == CLASS_DEATH_KNIGHT) - Regenerate(POWER_RUNIC_POWER); - - m_regenTimerCount -= 2000; + _regenerationTimer -= REGENERATION_INTERVAL; } - if (m_holyPowerRegenTimerCount >= 10000 && getClass() == CLASS_PALADIN) - { - Regenerate(POWER_HOLY_POWER); - m_holyPowerRegenTimerCount -= 10000; - } - - m_regenTimer = 0; - // Handles the emotes for drinking and eating. // According to sniffs there is a background timer going on that repeats independed from the time window where the aura applies. // That's why we dont need to reset the timer on apply. In sniffs I have seen that the first call for the spell visual is totally random, then after @@ -1935,7 +1911,7 @@ void Player::RegenerateAll() } } -void Player::Regenerate(Powers power) +void Player::Regenerate(Powers power, uint32 diff) { uint32 maxValue = GetMaxPower(power); if (!maxValue) @@ -1943,8 +1919,8 @@ void Player::Regenerate(Powers power) uint32 curValue = GetPower(power); - /// @todo possible use of miscvalueb instead of amount - if (HasAuraTypeWithValue(SPELL_AURA_PREVENT_REGENERATE_POWER, power)) + // Block power regeneration + if (HasAuraTypeWithValue(SPELL_AURA_PREVENT_REGENERATE_POWER, power) || HasAuraType(SPELL_AURA_INTERRUPT_REGEN)) return; // Skip regeneration for power type we cannot have @@ -1952,88 +1928,36 @@ void Player::Regenerate(Powers power) if (powerIndex == MAX_POWERS) return; - float addvalue = 0.0f; - - // Powers now benefit from haste. - float meleeHaste = GetFloatValue(PLAYER_FIELD_MOD_HASTE); - float spellHaste = GetFloatValue(UNIT_MOD_CAST_SPEED); + float addValue = GetPowerRegen(power, IsInCombat()) * (0.001f * diff); + // Apply config values switch (power) { case POWER_MANA: - { - float ManaIncreaseRate = sWorld->getRate(RATE_POWER_MANA); - - if (IsInCombat()) // Trinity Updates Mana in intervals of 2s, which is correct - addvalue += GetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER) * ManaIncreaseRate * ((0.001f * m_regenTimer) + CalculatePct(0.001f, spellHaste)); - else - addvalue += GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER) * ManaIncreaseRate * ((0.001f * m_regenTimer) + CalculatePct(0.001f, spellHaste)); + addValue *= sWorld->getRate(RATE_POWER_MANA); break; - } - case POWER_RAGE: // Regenerate rage - { - if (!IsInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN)) - { - float RageDecreaseRate = sWorld->getRate(RATE_POWER_RAGE_LOSS); - addvalue += -25 * RageDecreaseRate / meleeHaste; // 2.5 rage by tick (= 2 seconds => 1.25 rage/sec) - } + case POWER_RAGE: + addValue *= sWorld->getRate(RATE_POWER_RAGE_LOSS); break; - } - case POWER_FOCUS: - { - float haste = (1.0f / GetFloatValue(PLAYER_FIELD_MOD_HASTE_REGEN) - 1.0f) * 100.0f; - addvalue += (5.0f + CalculatePct(5.0f, haste)) * (m_regenTimer / 1000.0f); - addvalue *= GetTotalAuraMultiplier(SPELL_AURA_HASTE_SPELLS); - break; - } - case POWER_ENERGY: // Regenerate energy (rogue) - { - float haste = (1.0f / GetFloatValue(PLAYER_FIELD_MOD_HASTE_REGEN) - 1.0f) * 100.0f; - addvalue += (10.0f + CalculatePct(10.0f, haste)) * (m_regenTimer / 1000.0f); - break; - } case POWER_RUNIC_POWER: - { - if (!IsInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN)) - { - float RunicPowerDecreaseRate = sWorld->getRate(RATE_POWER_RUNICPOWER_LOSS); - addvalue += -30 * RunicPowerDecreaseRate; // 3 RunicPower by tick - } + addValue*= sWorld->getRate(RATE_POWER_RUNICPOWER_LOSS); break; - } - case POWER_HOLY_POWER: // Regenerate holy power - { - if (!IsInCombat()) - addvalue += -1.0f; // remove 1 each 10 sec + case POWER_FOCUS: + addValue *= sWorld->getRate(RATE_POWER_FOCUS); break; - } - case POWER_RUNE: + case POWER_ENERGY: + addValue *= sWorld->getRate(RATE_POWER_ENERGY); break; - case POWER_HEALTH: - return; default: break; } - // Mana regen calculated in Player::UpdateManaRegen() - if (power != POWER_MANA && power != POWER_FOCUS && 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()); - - // Butchery requires combat for this effect - if (power != POWER_RUNIC_POWER || IsInCombat()) - addvalue += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, power) * ((power != POWER_ENERGY) ? m_regenTimerCount : m_regenTimer) / (5 * IN_MILLISECONDS); - } - - if (addvalue < 0.0f) + if (addValue < 0.0f) { if (curValue == 0) return; } - else if (addvalue > 0.0f) + else if (addValue > 0.0f) { if (curValue == maxValue) return; @@ -2041,15 +1965,15 @@ void Player::Regenerate(Powers power) else return; - addvalue += m_powerFraction[powerIndex]; - uint32 integerValue = uint32(std::fabs(addvalue)); + addValue += m_powerFraction[powerIndex]; + uint32 integerValue = uint32(std::fabs(addValue)); - if (addvalue < 0.0f) + if (addValue < 0.0f) { if (curValue > integerValue) { curValue -= integerValue; - m_powerFraction[powerIndex] = addvalue + integerValue; + m_powerFraction[powerIndex] = addValue + integerValue; } else { @@ -2067,10 +1991,10 @@ void Player::Regenerate(Powers power) m_powerFraction[powerIndex] = 0; } else - m_powerFraction[powerIndex] = addvalue - integerValue; + m_powerFraction[powerIndex] = addValue - integerValue; } - if (m_regenTimerCount >= 2000) + if (_regenerationTimer >= REGENERATION_INTERVAL) SetPower(power, curValue); else UpdateUInt32Value(UNIT_FIELD_POWER1 + powerIndex, curValue); @@ -25682,18 +25606,6 @@ void Player::InitRunes() SetFloatValue(PLAYER_RUNE_REGEN_1 + i, 0.1f); // set a base regen timer equal to 10 sec } -void Player::UpdateRuneRegeneration() -{ - // Formular: base cooldown / (1 - haste) - float regeneration = 0.1f; - float haste = GetFloatValue(PLAYER_FIELD_MOD_HASTE_REGEN); - if (haste) - regeneration /= haste; - - for (uint8 i = 0; i < NUM_RUNE_TYPES; i++) - SetFloatValue(PLAYER_RUNE_REGEN_1 + i, regeneration); -} - bool Player::IsBaseRuneSlotsOnCooldown(RuneType runeType) const { for (uint8 i = 0; i < MAX_RUNES; ++i) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 0ac26fadbc..e24e1d61dc 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1515,11 +1515,11 @@ class TC_GAME_API Player : public Unit, public GridObject void SetBindPoint(ObjectGuid guid) const; void SendTalentWipeConfirm(ObjectGuid guid) const; void ResetPetTalents(); - void RegenerateAll(); - void Regenerate(Powers power); + void RegenerateAll(uint32 diff); + void Regenerate(Powers power, uint32 diff); void RegenerateHealth(); - void setRegenTimerCount(uint32 time) {m_regenTimerCount = time;} - void setWeaponChangeTimer(uint32 time) {m_weaponChangeTimer = time;} + void SetRegenerationTimer(uint32 time) { _regenerationTimer = time;} + void setWeaponChangeTimer(uint32 time) { m_weaponChangeTimer = time;} uint64 GetMoney() const { return GetUInt64Value(PLAYER_FIELD_COINAGE); } bool ModifyMoney(int64 amount, bool sendError = true); @@ -1837,7 +1837,7 @@ class TC_GAME_API Player : public Unit, public GridObject void UpdateExpertise(WeaponAttackType attType); void ApplyManaRegenBonus(int32 amount, bool apply); void ApplyHealthRegenBonus(int32 amount, bool apply); - void UpdateManaRegen(); + void UpdatePowerRegeneration(Powers powerType); uint32 GetRuneTimer(uint8 index) const { return m_runeGraceCooldown[index]; } void SetRuneTimer(uint8 index, uint32 timer) { m_runeGraceCooldown[index] = timer; } uint32 GetLastRuneGraceTimer(uint8 index) const { return m_lastRuneGraceTimers[index]; } @@ -2320,7 +2320,6 @@ class TC_GAME_API Player : public Unit, public GridObject void ResyncRunes(uint8 count); void AddRunePower(uint8 mask); void InitRunes(); - void UpdateRuneRegeneration(); void SendRespondInspectAchievements(Player* player) const; uint32 GetAchievementPoints() const; @@ -2395,13 +2394,10 @@ class TC_GAME_API Player : public Unit, public GridObject uint32 GetTransportSpawnID() const { return _transportSpawnID; } void SetTransportSpawnID(uint32 spawnId) { _transportSpawnID = spawnId; } - void ResetHolyPowerRegenerationTimer() { m_holyPowerRegenTimerCount = 0; } protected: // Gamemaster whisper whitelist GuidList WhisperList; - uint32 m_regenTimerCount; - uint32 m_holyPowerRegenTimerCount; - uint32 m_focusRegenTimerCount; + uint32 _regenerationTimer; uint32 m_foodEmoteTimerCount; float m_powerFraction[MAX_POWERS_PER_CLASS]; uint32 m_contestedPvPTimer; diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 27f3c3b84d..e371ea6c81 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -132,7 +132,7 @@ bool Player::UpdateStats(Stats stat) } UpdateSpellDamageAndHealingBonus(); - UpdateManaRegen(); + UpdatePowerRegeneration(POWER_MANA); // Update ratings in exist SPELL_AURA_MOD_RATING_FROM_STAT and only depends from stat uint32 mask = 0; @@ -222,7 +222,7 @@ bool Player::UpdateAllStats() UpdateParryPercentage(); UpdateDodgePercentage(); UpdateSpellDamageAndHealingBonus(); - UpdateManaRegen(); + UpdatePowerRegeneration(POWER_MANA); UpdateExpertise(BASE_ATTACK); UpdateExpertise(OFF_ATTACK); UpdateAllResistances(); @@ -739,7 +739,7 @@ void Player::UpdateExpertise(WeaponAttackType attack) void Player::ApplyManaRegenBonus(int32 amount, bool apply) { _ModifyUInt32(apply, m_baseManaRegen, amount); - UpdateManaRegen(); + UpdatePowerRegeneration(POWER_MANA); } void Player::ApplyHealthRegenBonus(int32 amount, bool apply) @@ -747,30 +747,65 @@ void Player::ApplyHealthRegenBonus(int32 amount, bool apply) _ModifyUInt32(apply, m_baseHealthRegen, amount); } -void Player::UpdateManaRegen() +void Player::UpdatePowerRegeneration(Powers powerType) { - if (getPowerType() == POWER_MANA || getClass() == CLASS_DRUID) + uint32 powerIndex = GetPowerIndex(powerType); + if (powerIndex == MAX_POWERS && powerType != POWER_RUNE) + return; + + float powerRegenMod = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, powerType); + float powerRegenModPct = GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, powerType); + + switch (powerType) { - // Mana regen from spirit - float spirit_regen = OCTRegenMPPerSpirit(); - // Apply PCT bonus from SPELL_AURA_MOD_POWER_REGEN_PERCENT aura on spirit base regen - spirit_regen *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, POWER_MANA); + case POWER_MANA: + { + // Mana regen from spirit + float spirit_regen = OCTRegenMPPerSpirit(); + // Apply PCT bonus from SPELL_AURA_MOD_POWER_REGEN_PERCENT aura on spirit base regen + spirit_regen *= powerRegenModPct; - // SpiritRegen(SPI, INT, LEVEL) = (0.001 + (SPI x sqrt(INT) x BASE_REGEN[LEVEL])) x 5 - if (GetStat(STAT_INTELLECT) > 0.0f) - spirit_regen *= std::sqrt(GetStat(STAT_INTELLECT)); + // SpiritRegen(SPI, INT, LEVEL) = (0.001 + (SPI x sqrt(INT) x BASE_REGEN[LEVEL])) x 5 + if (GetStat(STAT_INTELLECT) > 0.0f) + spirit_regen *= std::sqrt(GetStat(STAT_INTELLECT)); - // CombatRegen = 5% of Base Mana - float base_regen = GetCreateMana() * 0.01f + GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA) / 5.0f; + // CombatRegen = 5% of Base Mana + float base_regen = GetCreateMana() * 0.01f + powerRegenMod / 5.0f; - // Set regen rate in cast state apply only on spirit based regen - int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT); + // Set regen rate in cast state apply only on spirit based regen + int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT); - SetStatFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER, base_regen + CalculatePct(spirit_regen, modManaRegenInterrupt)); - SetStatFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER, 0.001f + spirit_regen + base_regen); + SetStatFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER + powerIndex, base_regen + CalculatePct(spirit_regen, modManaRegenInterrupt)); + SetStatFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER + powerIndex, 0.001f + spirit_regen + base_regen); + break; + } + case POWER_RUNIC_POWER: + case POWER_RAGE: + // Butchery and Anger Management + SetStatFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER + powerIndex, powerRegenMod / 5.0f); + break; + case POWER_RUNE: + { + // Formular: base cooldown / (1 - haste) + float regeneration = 0.1f; + float haste = GetFloatValue(PLAYER_FIELD_MOD_HASTE_REGEN); + 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); + } + break; + } + default: + break; } - else if (getClass() == CLASS_HUNTER) - SetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER, -0.2f); } void Player::_ApplyAllStatBonuses() diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 52a33e33bf..798946dddf 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -322,7 +322,7 @@ Unit::Unit(bool isWorldObject) : IsAIEnabled(false), NeedChangeAI(false), LastCharmerGUID(), m_ControlledByPlayer(false), movespline(new Movement::MoveSpline()), i_AI(nullptr), i_disabledAI(nullptr), m_AutoRepeatFirstCast(false), m_procDeep(0), - m_removedAurasCount(0), i_motionMaster(new MotionMaster(this)), m_regenTimer(0), m_ThreatManager(this), + m_removedAurasCount(0), i_motionMaster(new MotionMaster(this)), m_ThreatManager(this), m_vehicle(nullptr), m_vehicleKit(nullptr), m_unitTypeMask(UNIT_MASK_NONE), m_Diminishing(), m_HostileRefManager(this), _lastDamagedTime(0), m_spellHistory(new SpellHistory(this)) { @@ -409,6 +409,8 @@ Unit::Unit(bool isWorldObject) : _lastLiquid = nullptr; + _powerBarId = 0; + _oldFactionId = 0; _isWalkingBeforeCharm = false; } @@ -860,7 +862,7 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam // Rage from Damage made (only from direct weapon damage) if (cleanDamage && damagetype == DIRECT_DAMAGE && this != victim && getPowerType() == POWER_RAGE) { - uint32 rage = uint32(GetAttackTime(cleanDamage->attackType) / 1000 * 8.125f); + uint32 rage = uint32(GetAttackTime(cleanDamage->attackType) / 1000 * 6.5f); // Sentinel if (victim->GetVictim() && victim->GetVictim() != this) @@ -6651,9 +6653,6 @@ void Unit::EnergizeBySpell(Unit* victim, uint32 spellId, int32 damage, Powers po // needs to be called after sending spell log victim->ModifyPower(powerType, damage); - if (powerType == POWER_HOLY_POWER && GetTypeId() == TYPEID_PLAYER) - ToPlayer()->ResetHolyPowerRegenerationTimer(); - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); victim->getHostileRefManager().threatAssist(this, float(damage) * 0.5f, spellInfo); } @@ -8902,6 +8901,61 @@ int32 Unit::ModifyPowerPct(Powers power, float pct, bool apply) return ModifyPower(power, (int32)amount - GetMaxPower(power)); } +inline float GetBasePowerRegen(uint32 powerBarId, Powers powerType, bool isInCombat) +{ + if (powerType != POWER_ALTERNATE_POWER) + { + switch (powerType) + { + case POWER_RAGE: return isInCombat ? 0.f : -12.5f; + case POWER_FOCUS: return 5.f; + case POWER_RUNIC_POWER: return isInCombat ? 0.f : -12.5f; + case POWER_ENERGY: return 10.f; + case POWER_HOLY_POWER: return isInCombat ? 0.f : -0.1f; + default: + return 0.f; + } + } + else + { + UnitPowerBarEntry const* powerBar = sUnitPowerBarStore.LookupEntry(powerBarId); + if (!powerBar) + return 0.f; + + return isInCombat ? powerBar->RegenerationCombat : powerBar->RegenerationPeace; + } + + return 0.f; +} + +// Based on client function +float Unit::GetPowerRegen(Powers powerType, bool isInCombat) const +{ + uint32 powerSlot = MAX_POWERS; + float totalRegeneration = 0.f; + double result = GetBasePowerRegen(_powerBarId, powerType, isInCombat); // base power regen + float combatRegeneration = result; + + powerSlot = GetPowerIndex(powerType); + if (powerSlot != MAX_POWERS) + { + uint32 powerRegenIndex = isInCombat ? UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER : UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER; + totalRegeneration = GetFloatValue(powerRegenIndex + powerSlot) + combatRegeneration; + result = totalRegeneration; + } + else + totalRegeneration = result; + + if (IsPlayer() && (powerType == POWER_ENERGY || powerType == POWER_FOCUS)) + { + float hasteRegen = GetFloatValue(PLAYER_FIELD_MOD_HASTE_REGEN); + if (hasteRegen != 0.f) + result = totalRegeneration / hasteRegen; + } + + return result; +} + uint32 Unit::GetAttackTime(WeaponAttackType att) const { float f_BaseAttackTime = GetFloatValue(UNIT_FIELD_BASEATTACKTIME + att) / m_modAttackSpeedPct[att]; @@ -11303,7 +11357,7 @@ void Unit::ApplyHasteRegenMod(WeaponAttackType att, float val, bool apply) } if (getClass() == CLASS_DEATH_KNIGHT) - ToPlayer()->UpdateRuneRegeneration(); + ToPlayer()->UpdatePowerRegeneration(POWER_RUNE); } void Unit::ApplyCastTimePercentMod(float val, bool apply) @@ -13995,21 +14049,20 @@ void Unit::RewardRage(uint32 baseRage, bool attacker) if (attacker) { addRage = baseRage; - // talent who gave more rage on attack + // Additional rage from attacks (e.g. Anger Management) AddPct(addRage, GetTotalAuraModifier(SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT)); } else { - // Calculate rage from health and damage taken - //! ToDo: Check formula - addRage = floor(0.5f + (25.7f * baseRage / GetMaxHealth())); - // Berserker Rage effect + // Calculate rage from health and damage taken (formular taken from SimulationCraft) + addRage = baseRage * 18.92 / GetMaxHealth(); + + // Berserker Rage if (HasAura(18499)) addRage *= 2.0f; } addRage *= sWorld->getRate(RATE_POWER_RAGE_INCOME); - ModifyPower(POWER_RAGE, uint32(addRage * 10)); } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 276d2778b0..a3262474be 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1110,9 +1110,12 @@ class TC_GAME_API Unit : public WorldObject int32 CountPctFromMaxPower(Powers power, int32 pct) const { return CalculatePct(GetMaxPower(power), pct); } void SetPower(Powers power, int32 val); void SetMaxPower(Powers power, int32 val); + void SetPowerBarID(uint32 id) { _powerBarId = id; } + // returns the change in power int32 ModifyPower(Powers power, int32 val); int32 ModifyPowerPct(Powers power, float pct, bool apply = true); + float GetPowerRegen(Powers powerType, bool isInCombat) const; uint32 GetAttackTime(WeaponAttackType att) const; void SetAttackTime(WeaponAttackType att, uint32 val) { SetFloatValue(UNIT_FIELD_BASEATTACKTIME+att, val*m_modAttackSpeedPct[att]); } @@ -1983,7 +1986,6 @@ class TC_GAME_API Unit : public WorldObject MotionMaster* i_motionMaster; uint32 m_reactiveTimer[MAX_REACTIVE]; - uint32 m_regenTimer; ThreatManager m_ThreatManager; @@ -1992,6 +1994,7 @@ class TC_GAME_API Unit : public WorldObject uint32 m_unitTypeMask; LiquidTypeEntry const* _lastLiquid; + uint32 _powerBarId; bool IsAlwaysVisibleFor(WorldObject const* seer) const override; bool IsAlwaysDetectableFor(WorldObject const* seer) const override; diff --git a/src/server/game/Entities/Vehicle/VehicleDefines.h b/src/server/game/Entities/Vehicle/VehicleDefines.h index 2d00d5806b..2cbaf41b1c 100644 --- a/src/server/game/Entities/Vehicle/VehicleDefines.h +++ b/src/server/game/Entities/Vehicle/VehicleDefines.h @@ -24,7 +24,7 @@ struct VehicleSeatEntry; -enum PowerType +enum PowerType : uint32 { POWER_STEAM = 61, POWER_PYRITE = 41, diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 2dbb0356da..e28c26bf02 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -245,6 +245,7 @@ enum Powers }; #define MAX_POWERS_PER_CLASS 5 +#define REGENERATION_INTERVAL 2 * IN_MILLISECONDS enum SpellSchools { diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 4fa012b672..35aed5580d 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -2178,7 +2178,7 @@ void AuraEffect::HandleAuraTransform(AuraApplication const* aurApp, uint8 mode, // for players, start regeneration after 1s (in polymorph fast regeneration case) // only if caster is Player (after patch 2.4.2) if (GetCasterGUID().IsPlayer()) - target->ToPlayer()->setRegenTimerCount(1*IN_MILLISECONDS); + target->ToPlayer()->SetRegenerationTimer(1 * IN_MILLISECONDS); //dismount polymorphed target (after patch 2.4.2) if (target->IsMounted()) @@ -3638,22 +3638,17 @@ void AuraEffect::HandleAuraModExpertise(AuraApplication const* aurApp, uint8 mod /********************************/ /*** HEAL & ENERGIZE ***/ /********************************/ -void AuraEffect::HandleModPowerRegen(AuraApplication const* aurApp, uint8 mode, bool apply) const +void AuraEffect::HandleModPowerRegen(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->GetTypeId() != TYPEID_PLAYER) + Player* target = aurApp->GetTarget()->ToPlayer(); + if (!target) return; - // Update manaregen value - if (GetMiscValue() == POWER_MANA) - target->ToPlayer()->UpdateManaRegen(); - else if (GetMiscValue() == POWER_RUNE) - target->ApplyPercentModFloatValue(PLAYER_RUNE_REGEN_1 + GetMiscValueB(), float(GetAmount()), apply); - // other powers are not immediate effects - implemented in Player::Regenerate, Creature::Regenerate + // Update power regen values + target->UpdatePowerRegeneration(Powers(GetMiscValue())); } void AuraEffect::HandleModPowerRegenPCT(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3678,7 +3673,7 @@ void AuraEffect::HandleModManaRegen(AuraApplication const* aurApp, uint8 mode, b return; //Note: an increase in regen does NOT cause threat. - target->ToPlayer()->UpdateManaRegen(); + target->ToPlayer()->UpdatePowerRegeneration(POWER_MANA); } void AuraEffect::HandleAuraModIncreaseHealth(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -6338,13 +6333,18 @@ void AuraEffect::HandleEnableAltPower(AuraApplication const* aurApp, uint8 mode, if (!powerEntry) return; + Unit* target = aurApp->GetTarget(); if (apply) { - aurApp->GetTarget()->SetMaxPower(POWER_ALTERNATE_POWER, powerEntry->MaxPower); - aurApp->GetTarget()->SetPower(POWER_ALTERNATE_POWER, powerEntry->StartPower); + target->SetMaxPower(POWER_ALTERNATE_POWER, powerEntry->MaxPower); + target->SetPower(POWER_ALTERNATE_POWER, powerEntry->StartPower); + target->SetPowerBarID(altPowerId); } else - aurApp->GetTarget()->SetMaxPower(POWER_ALTERNATE_POWER, 0); + { + target->SetMaxPower(POWER_ALTERNATE_POWER, 0); + target->SetPowerBarID(0); + } } void AuraEffect::HandleModSpellCategoryCooldown(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const