diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 6542d5dc9c..4b3f988f69 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -1332,6 +1332,59 @@ EmotesTextSoundEntry const* DBCManager::FindTextSoundEmoteFor(uint32 emote, uint return itr != sEmotesTextSoundMap.end() ? itr->second : nullptr; } +/*static*/ float DBCManager::GetBasePowerRegen(Powers powerType, bool isInCombat, uint32 powerBarId) +{ + 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; + case POWER_RUNE: return 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; +} + +/*static*/ bool DBCManager::IsPowerTypeAffectedByHaste(Powers powerType) +{ + switch (powerType) + { + case POWER_ENERGY: + case POWER_FOCUS: + case POWER_RUNE: + return true; + default: + return false; + } + + return false; +} + +/*static*/ float DBCManager::GetGtOCTRegenMPPerSpirit(uint8 classId, uint8 level) +{ + level = std::min(level, GT_MAX_LEVEL); + GtRegenMPPerSptEntry const* gt = sGtRegenMPPerSptStore.LookupEntry((classId - 1) * GT_MAX_LEVEL + level - 1); + if (gt == nullptr) + return 0.0f; + + return gt->ratio; +} + bool DBCManager::IsInArea(uint32 objectAreaId, uint32 areaId) { do diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index a992243e74..b70b56bd90 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -238,7 +238,7 @@ public: static uint32 GetLiquidFlags(uint32 liquidType); static uint32 GetDefaultMapLight(uint32 mapId); PvPDifficultyEntry const* GetBattlegroundBracketByLevel(uint32 mapid, uint32 level); - PvPDifficultyEntry const* GetBattlegroundBracketById(uint32 mapid, BattlegroundBracketId id); + PvPDifficultyEntry const* GetBattlegroundBracketById(uint32 mapid, BattlegroundBracketId id); CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, uint8 gender); CharSectionsEntry const* GetCharSectionEntry(uint8 race, CharSectionType genType, uint8 gender, uint8 type, uint8 color); uint32 GetPowerIndexByClass(Powers power, uint32 classId); @@ -248,7 +248,10 @@ public: std::vector const* GetSkillLineAbilitiesBySkill(uint32 skillId) const; SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_); ResponseCodes ValidateName(std::wstring const& name, LocaleConstant locale); - EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender); + EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender); + static float GetBasePowerRegen(Powers powerType, bool isInCombat, uint32 powerBarId = 0); + static bool IsPowerTypeAffectedByHaste(Powers powerType); + static float GetGtOCTRegenMPPerSpirit(uint8 classId, uint8 level); }; #define sDBCManager DBCManager::Instance() diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 80e38ccf4d..543678f816 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1428,6 +1428,7 @@ void Creature::UpdateLevelDependantStats() Powers powerType = CalculateDisplayPowerType(); SetCreateMana(stats->BaseMana); SetStatPctModifier(UnitMods(UNIT_MOD_POWER_START + AsUnderlyingType(powerType)), BASE_PCT, cInfo->ModMana * cInfo->ModManaExtra); + RegisterPowerTypes(); SetPowerType(powerType); switch (getClass()) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index f473b9c9e2..663c85253d 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -178,7 +178,6 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma uint32 GetPowerIndex(Powers power) const override; void UpdateAttackPowerAndDamage(bool ranged = false) override; void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage) const override; - void UpdatePowerRegeneration(Powers powerType) override; void SetCanDualWield(bool value) override; int8 GetOriginalEquipmentId() const { return m_originalEquipmentId; } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index c218a335fd..1053c01d4b 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -451,6 +451,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, createInfo->Class); SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, createInfo->Gender); SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, powertype); + RegisterPowerTypes(); InitDisplayIds(); if (sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP) { @@ -4929,25 +4930,6 @@ float Player::GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const return 0.0f; } -float Player::OCTRegenMPPerSpirit() const -{ - uint8 level = getLevel(); - uint32 pclass = getClass(); - - if (level > GT_MAX_LEVEL) - level = GT_MAX_LEVEL; - -// GtOCTRegenMPEntry const* baseRatio = sGtOCTRegenMPStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1); - GtRegenMPPerSptEntry const* moreRatio = sGtRegenMPPerSptStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1); - if (moreRatio == nullptr) - return 0.0f; - - // Formula get from PaperDollFrame script - float spirit = GetStat(STAT_SPIRIT); - float regen = spirit * moreRatio->ratio; - return regen; -} - void Player::ApplyRatingMod(CombatRating combatRating, int32 value, bool apply) { // explicit affected values @@ -4959,16 +4941,16 @@ void Player::ApplyRatingMod(CombatRating combatRating, int32 value, bool apply) ApplyAttackTimePercentMod(BASE_ATTACK, (m_baseRatingValue[combatRating] + (apply ? value : -value)) * GetRatingMultiplier(combatRating), true); ApplyAttackTimePercentMod(OFF_ATTACK, m_baseRatingValue[combatRating] * GetRatingMultiplier(combatRating), false); ApplyAttackTimePercentMod(OFF_ATTACK, (m_baseRatingValue[combatRating] + (apply ? value : -value)) * GetRatingMultiplier(combatRating), true); - ApplyHasteRegenMod(BASE_ATTACK, m_baseRatingValue[combatRating] * GetRatingMultiplier(combatRating), false); - ApplyHasteRegenMod(BASE_ATTACK, (m_baseRatingValue[combatRating] + (apply ? value : -value)) * GetRatingMultiplier(combatRating), true); + + // Haste Rating affects all three haste types so we only have to apply it once here + ApplyHasteRegenMod(m_baseRatingValue[combatRating] * GetRatingMultiplier(combatRating), false); + ApplyHasteRegenMod((m_baseRatingValue[combatRating] + (apply ? value : -value)) * GetRatingMultiplier(combatRating), true); break; } case CR_HASTE_RANGED: { ApplyAttackTimePercentMod(RANGED_ATTACK, m_baseRatingValue[combatRating] * GetRatingMultiplier(combatRating), false); ApplyAttackTimePercentMod(RANGED_ATTACK, (m_baseRatingValue[combatRating] + (apply ? value : -value)) * GetRatingMultiplier(combatRating), true); - ApplyHasteRegenMod(RANGED_ATTACK, m_baseRatingValue[combatRating] * GetRatingMultiplier(combatRating), false); - ApplyHasteRegenMod(RANGED_ATTACK, (m_baseRatingValue[combatRating] + (apply ? value : -value)) * GetRatingMultiplier(combatRating), true); break; } case CR_HASTE_SPELL: @@ -16973,6 +16955,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol //mails are loaded only when needed ;-) - when player in game click on mailbox. //_LoadMail(); + RegisterPowerTypes(); UpdateDisplayPower(); _LoadTalents(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS)); _LoadSpells(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS)); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 01e338377e..311078d8d7 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1793,7 +1793,6 @@ class TC_GAME_API Player : public Unit, public GridObject float GetMeleeCritFromAgility() const; void GetDodgeFromAgility(float &diminishing, float &nondiminishing) const; float GetSpellCritFromIntellect() const; - float OCTRegenMPPerSpirit() const; float GetRatingMultiplier(CombatRating cr) const; float GetRatingBonusValue(CombatRating cr) const; @@ -1819,7 +1818,6 @@ 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 UpdatePowerRegeneration(Powers powerType) override; 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]; } diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 2983967bb5..76277e2d84 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -119,6 +119,140 @@ int32 Unit::GetCreatePowerValue(Powers power) const return 0; } +void Unit::UpdatePowerRegeneration(Powers powerType) +{ + uint32 powerIndex = GetPowerIndex(powerType); + if ((powerIndex == MAX_POWERS || powerIndex >= MAX_POWERS_PER_CLASS) && powerType != POWER_RUNE) + return; + + // Runes are not officially considered a power type for the class so we gotta bypass the rules this way + if (powerType == POWER_RUNE && (!IsPlayer() || getClass() != CLASS_DEATH_KNIGHT)) + return; + + float powerRegenMod = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, powerType) / 5.f; + float powerRegenModPct = GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, powerType); + + switch (powerType) + { + case POWER_MANA: + { + // Get base of Mana Pool in sBaseMPGameTable + uint32 basemana = 0, basehp = 0; + if (IsPlayer()) + sObjectMgr->GetPlayerClassLevelInfo(getClass(), getLevel(), basemana, basehp); + else + basemana = GetCreateMana(); // this should also get replaced by the base mana game table in the future. + + // BaseRegen = 5% of Base Mana per five seconds + float baseRegen = basemana / 100.f; + // SPELL_AURA_MOD_POWER_REGEN flat bonus + baseRegen += powerRegenMod; + + // SpiritRegen = Spirit * GTRegenMpPerSpt * Sqrt(INT) * 5 + float spiritRegen = GetStat(STAT_SPIRIT) * DBCManager::GetGtOCTRegenMPPerSpirit(getClass(), getLevel()); + if (GetStat(STAT_INTELLECT) > 0.0f) + spiritRegen *= std::sqrt(GetStat(STAT_INTELLECT)); + + // SPELL_AURA_MOD_POWER_REGEN_PERCENT pct bonus + baseRegen *= powerRegenModPct; + spiritRegen *= powerRegenModPct; + + // SPELL_AURA_MOD_MANA_REGEN_INTERRUPT allow some of the spirit regeneration to bypass the combat restriction + int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT); + + SetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER + powerIndex, baseRegen + CalculatePct(spiritRegen, modManaRegenInterrupt)); + SetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER + powerIndex, baseRegen + spiritRegen); + break; + } + case POWER_RUNE: + { + float baseRegen = DBCManager::GetBasePowerRegen(powerType, false, 0); + + // Hase Regen + if (DBCManager::IsPowerTypeAffectedByHaste(powerType) && IsPlayer()) + { + float hasteRegen = GetFloatValue(PLAYER_FIELD_MOD_HASTE_REGEN); + if (G3D::fuzzyNe(hasteRegen, 0.0f)) + baseRegen /= hasteRegen; + } + + baseRegen += powerRegenMod; + + if (IsPlayer()) + for (uint8 i = 0; i < NUM_RUNE_TYPES; ++i) + SetFloatValue(PLAYER_RUNE_REGEN_1 + i, baseRegen); + break; + } + default: + { + // Base Regen + float peaceRegen = DBCManager::GetBasePowerRegen(powerType, false, 0); + float combatRegen = DBCManager::GetBasePowerRegen(powerType, true, 0); + + // Hase Regen + if (DBCManager::IsPowerTypeAffectedByHaste(powerType) && IsPlayer()) + { + float hasteRegen = GetFloatValue(PLAYER_FIELD_MOD_HASTE_REGEN); + if (G3D::fuzzyNe(hasteRegen, 0.0f)) + { + peaceRegen /= hasteRegen; + combatRegen /= hasteRegen; + } + } + + peaceRegen *= powerRegenModPct; + combatRegen *= powerRegenModPct; + + // Subtract the base value to get the proper offset + peaceRegen -= DBCManager::GetBasePowerRegen(powerType, false, 0); + combatRegen -= DBCManager::GetBasePowerRegen(powerType, true, 0); + + peaceRegen += powerRegenMod; + combatRegen += powerRegenMod; + + SetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER + powerIndex, combatRegen); + SetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER + powerIndex, peaceRegen); + } + + /* + case POWER_FOCUS: + case POWER_ENERGY: + { + // Energy and Focus regeneration is always consistent in and out of combat + float baseRegen = DBCManager::GetBasePowerRegen(powerType, false, 0); + SetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER + powerIndex, baseRegen * powerRegenModPct - baseRegen + powerRegenMod); + SetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER + powerIndex, baseRegen * powerRegenModPct - baseRegen + powerRegenMod); + break; + } + case POWER_RUNIC_POWER: + case POWER_RAGE: + // Butchery and Anger Management + SetStatFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER + powerIndex, powerRegenMod); + 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; + } + */ + } +} + + /*####################################### ######## ######## ######## PLAYERS STAT SYSTEM ######## @@ -817,76 +951,6 @@ void Player::ApplyHealthRegenBonus(int32 amount, bool apply) _ModifyUInt32(apply, m_baseHealthRegen, amount); } -void Player::UpdatePowerRegeneration(Powers powerType) -{ - uint32 powerIndex = GetPowerIndex(powerType); - if (powerIndex == MAX_POWERS && powerType != POWER_RUNE) - return; - - float powerRegenMod = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, powerType) / 5.f; - float powerRegenModPct = GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, powerType); - - switch (powerType) - { - 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)); - - // CombatRegen = 5% of Base Mana per five seconds - float base_regen = CalculatePct(GetCreateMana(), 1) + powerRegenMod; - - // Set regen rate in cast state apply only on spirit based regen - int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT); - - SetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER + powerIndex, base_regen + CalculatePct(spirit_regen, modManaRegenInterrupt)); - SetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER + powerIndex, spirit_regen + base_regen); - break; - } - case POWER_FOCUS: - case POWER_ENERGY: - { - // Energy and Focus regeneration is always consistent in and out of combat - float baseRegen = Unit::GetBasePowerRegen(0, powerType, false); - SetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER + powerIndex, baseRegen * powerRegenModPct - baseRegen + powerRegenMod); - SetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER + powerIndex, baseRegen * powerRegenModPct - baseRegen + powerRegenMod); - break; - } - case POWER_RUNIC_POWER: - case POWER_RAGE: - // Butchery and Anger Management - SetStatFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER + powerIndex, powerRegenMod); - 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; - } -} - void Player::_ApplyAllStatBonuses() { SetCanModifyStats(false); @@ -983,37 +1047,6 @@ void Creature::UpdateMaxPower(Powers power) SetMaxPower(power, uint32(std::lroundf(value))); } -void Creature::UpdatePowerRegeneration(Powers powerType) -{ - float powerRegenMod = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, powerType) / 5.f; - float powerRegenModPct = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, powerType); - - switch (powerType) - { - case POWER_MANA: - // Regeneration for controlled units - if (!GetCharmerGUID().IsEmpty() || !GetOwnerGUID().IsEmpty()) - { - float spiritRegen = (GetStat(STAT_SPIRIT) / 5.f + 17.f); - SetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER, spiritRegen + powerRegenMod); - SetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER, powerRegenMod); - } - else // Default creature regeneration value. - SetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER, 8.714059f + powerRegenMod); // Most common value in sniffs. Todo: research - break; - case POWER_ENERGY: - case POWER_FOCUS: - { - float regenerationRate = CalculatePct(10, powerRegenModPct) + powerRegenMod; - SetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER, regenerationRate); - SetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER, regenerationRate); - break; - } - default: - break; - } -} - void Creature::UpdateAttackPowerAndDamage(bool ranged) { UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 7391438545..cbdcacb03e 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -368,11 +368,12 @@ Unit::Unit(bool isWorldObject) : _powerBarId = 0; - _powerFraction.fill(0.0f); - _powerUpdateTimer = GetPowerUpdateInterval(); _healthRegenerationTimer = UNIT_HEALTH_REGENERATION_INTERVAL; + _powerFraction.fill(0.0f); + _usedPowerTypes.fill(MAX_POWERS); + _oldFactionId = 0; _isWalkingBeforeCharm = false; } @@ -5205,12 +5206,19 @@ void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit* target, uint8 /*SwingType SendAttackStateUpdate(&dmgInfo); } -void Unit::SetPowerType(Powers new_powertype) +void Unit::SetPowerType(Powers power) { - if (GetPowerType() == new_powertype) + if (GetPowerType() == power) return; - SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, new_powertype); + SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, power); + + // Creatures can swap out their power type at index 0 so we do keep track of the new power type here + if (IsCreature()) + { + _usedPowerTypes[GetPowerIndex(power)] = power; + UpdatePowerRegeneration(power); + } if (GetTypeId() == TYPEID_PLAYER) { @@ -5228,10 +5236,10 @@ void Unit::SetPowerType(Powers new_powertype) } // Update max power - UpdateMaxPower(new_powertype); + UpdateMaxPower(power); // Update current power - switch (new_powertype) + switch (power) { case POWER_MANA: // Keep the same (druid form switching...) case POWER_ENERGY: @@ -5240,7 +5248,7 @@ void Unit::SetPowerType(Powers new_powertype) SetPower(POWER_RAGE, 0); break; case POWER_FOCUS: // Make it full - SetFullPower(new_powertype); + SetFullPower(power); break; default: break; @@ -8218,39 +8226,12 @@ int32 Unit::ModifyPower(Powers power, int32 dVal, bool withPowerUpdate /*= true* return gain; } -/*static */ float Unit::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 + double result = DBCManager::GetBasePowerRegen(powerType, isInCombat, _powerBarId); float combatRegeneration = result; powerSlot = GetPowerIndex(powerType); @@ -8263,10 +8244,10 @@ float Unit::GetPowerRegen(Powers powerType, bool isInCombat) const else totalRegeneration = result; - if (IsPlayer() && (powerType == POWER_ENERGY || powerType == POWER_FOCUS)) + if (IsPlayer() && DBCManager::IsPowerTypeAffectedByHaste(powerType)) { float hasteRegen = GetFloatValue(PLAYER_FIELD_MOD_HASTE_REGEN); - if (hasteRegen != 0.f) + if (G3D::fuzzyNe(hasteRegen, 0.f)) result = totalRegeneration / hasteRegen; } @@ -9480,6 +9461,18 @@ void Unit::Regenerate(Powers powerType, uint32 diff) UpdateUInt32Value(UNIT_FIELD_POWER1 + powerIndex, curValue); } +void Unit::RegisterPowerTypes() +{ + for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i) + { + uint32 powerIndex = GetPowerIndex(Powers(i)); + if (powerIndex == MAX_POWERS || powerIndex == MAX_POWERS_PER_CLASS) + continue; + + _usedPowerTypes[powerIndex] = static_cast(i); + } +} + void Unit::AIUpdateTick(uint32 diff) { if (UnitAI* ai = GetAI()) @@ -10652,32 +10645,31 @@ void Unit::ApplyAttackTimePercentMod(WeaponAttackType att, float val, bool apply m_attackTimer[att] = uint32(GetAttackTime(att) * m_modAttackSpeedPct[att] * remainingTimePct); } -void Unit::ApplyHasteRegenMod(WeaponAttackType att, float val, bool apply) +void Unit::ApplyHasteRegenMod(float val, bool apply) { - if (GetTypeId() != TYPEID_PLAYER) + if (!IsPlayer()) return; float amount = GetFloatValue(PLAYER_FIELD_MOD_HASTE_REGEN); if (val > 0.f) - { - if (att == BASE_ATTACK && getClass() != CLASS_HUNTER) - ApplyPercentModFloatVar(amount, val, !apply); - else if (att == RANGED_ATTACK && getClass() == CLASS_HUNTER) - ApplyPercentModFloatVar(amount, val, !apply); - } + ApplyPercentModFloatVar(amount, val, !apply); else - { - if (att == BASE_ATTACK && getClass() != CLASS_HUNTER) - ApplyPercentModFloatVar(amount, -val, apply); - else if (att == RANGED_ATTACK && getClass() == CLASS_HUNTER) - ApplyPercentModFloatVar(amount, -val, apply); - } + ApplyPercentModFloatVar(amount, -val, apply); SetFloatValue(PLAYER_FIELD_MOD_HASTE_REGEN, amount); - if (getClass() == CLASS_DEATH_KNIGHT) - ToPlayer()->UpdatePowerRegeneration(POWER_RUNE); + for (Powers powerType : GetUsedPowerTypes()) + { + if (powerType == MAX_POWERS || GetPowerIndex(powerType) == MAX_POWERS) + continue; + + if (DBCManager::IsPowerTypeAffectedByHaste(powerType)) + UpdatePowerRegeneration(powerType); + } + + if (IsPlayer() && getClass() == CLASS_DEATH_KNIGHT) + UpdatePowerRegeneration(POWER_RUNE); } void Unit::ApplyCastTimePercentMod(float val, bool apply, bool withCastHaste /*= true*/, bool withCastSpeed /*= true*/) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 29b39528b0..331689d1c5 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -876,8 +876,6 @@ class TC_GAME_API Unit : public WorldObject void ClearUnitState(uint32 f) { m_state &= ~f; } bool CanFreeMove() const; - virtual void UpdatePowerRegeneration(Powers /*powerType*/) { } - uint32 HasUnitTypeMask(uint32 mask) const { return mask & m_unitTypeMask; } void AddUnitTypeMask(uint32 mask) { m_unitTypeMask |= mask; } bool IsSummon() const { return (m_unitTypeMask & UNIT_MASK_SUMMON) != 0; } @@ -938,12 +936,13 @@ class TC_GAME_API Unit : public WorldObject void SetMaxPower(Powers power, int32 val); void SetPowerBarID(uint32 id) { _powerBarId = id; } void Regenerate(Powers powerType, uint32 diff); + void UpdatePowerRegeneration(Powers powerType); + void RegisterPowerTypes(); inline void SetFullPower(Powers power) { SetPower(power, GetMaxPower(power)); } // returns the change in power int32 ModifyPower(Powers power, int32 val, bool withPowerUpdate = true); - static float GetBasePowerRegen(uint32 powerBarId, Powers powerType, bool isInCombat); float GetPowerRegen(Powers powerType, bool isInCombat) const; virtual void RegenerateHealth() { } @@ -951,7 +950,7 @@ class TC_GAME_API Unit : public WorldObject uint32 GetAttackTime(WeaponAttackType att) const; void SetAttackTime(WeaponAttackType att, uint32 val) { SetFloatValue(UNIT_FIELD_BASEATTACKTIME + AsUnderlyingType(att), val * m_modAttackSpeedPct[att]); } void ApplyAttackTimePercentMod(WeaponAttackType att, float val, bool apply); - void ApplyHasteRegenMod(WeaponAttackType att, float val, bool apply); + void ApplyHasteRegenMod(float val, bool apply); void ApplyCastTimePercentMod(float val, bool apply, bool withCastHaste = true, bool withCastSpeed = true); SheathState GetSheath() const { return SheathState(GetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_SHEATH_STATE)); } @@ -1846,10 +1845,17 @@ class TC_GAME_API Unit : public WorldObject uint32 m_unitTypeMask; LiquidTypeEntry const* _lastLiquid; + uint32 _powerBarId; - std::array _powerFraction; int32 _powerUpdateTimer; int32 _healthRegenerationTimer; + std::array _powerFraction; + std::array _usedPowerTypes; + public: + // Returns an array that contains information about which power type is used at which power index. MAX_POWERS implies that a power at given index is not used. + std::array const& GetUsedPowerTypes() const { return _usedPowerTypes; } + + protected: bool IsAlwaysVisibleFor(WorldObject const* seer) const override; bool IsAlwaysDetectableFor(WorldObject const* seer) const override; diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index b5f2dd5438..64b74bfde3 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -200,7 +200,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleAuraModIncreaseHealthPercent, //133 SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT &AuraEffect::HandleAuraModRegenInterrupt, //134 SPELL_AURA_MOD_MANA_REGEN_INTERRUPT &AuraEffect::HandleModHealingDone, //135 SPELL_AURA_MOD_HEALING_DONE - &AuraEffect::HandleModHealingDonePercent, //136 SPELL_AURA_MOD_HEALING_DONE_PERCENT + &AuraEffect::HandleModHealingDonePercent, //136 SPELL_AURA_MOD_HEALING_DONE_PERCENT &AuraEffect::HandleModTotalPercentStat, //137 SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE &AuraEffect::HandleModMeleeSpeedPct, //138 SPELL_AURA_MOD_MELEE_HASTE &AuraEffect::HandleForceReaction, //139 SPELL_AURA_FORCE_REACTION @@ -2197,7 +2197,7 @@ void AuraEffect::HandleAuraTransform(AuraApplication const* aurApp, uint8 mode, { if (caster->HasAura(52648)) // Glyph of the Penguin model_id = 26452; - else if (caster->HasAura(57927)) // Glyph of the Monkey + else if (caster->HasAura(57927)) // Glyph of the Monkey model_id = 21362; } } @@ -2316,7 +2316,7 @@ void AuraEffect::HandleFeignDeath(AuraApplication const* aurApp, uint8 mode, boo } } } - + for (auto& pair : target->GetThreatManager().GetThreatenedByMeList()) pair.second->ScaleThreat(0.0f); @@ -3344,7 +3344,7 @@ void AuraEffect::HandleAuraModSchoolImmunity(AuraApplication const* aurApp, uint target->GetThreatManager().EvaluateSuppressed(); } else - { + { // do not remove unit flag if there are more than this auraEffect of that kind on unit if (target->HasAuraType(GetAuraType())) return; @@ -4108,12 +4108,6 @@ void AuraEffect::HandleModMeleeRangedSpeedPct(AuraApplication const* aurApp, uin target->ApplyAttackTimePercentMod(BASE_ATTACK, (float)GetAmount(), apply); target->ApplyAttackTimePercentMod(OFF_ATTACK, (float)GetAmount(), apply); target->ApplyAttackTimePercentMod(RANGED_ATTACK, (float)GetAmount(), apply); - - if (GetAuraType() != SPELL_AURA_MOD_MELEE_RANGED_HASTE_2 && GetAuraType() != SPELL_AURA_MOD_RANGED_HASTE_2) - { - target->ApplyHasteRegenMod(BASE_ATTACK, (float)GetAmount(), apply); - target->ApplyHasteRegenMod(RANGED_ATTACK, (float)GetAmount(), apply); - } } void AuraEffect::HandleModCombatSpeedPct(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -4122,7 +4116,7 @@ void AuraEffect::HandleModCombatSpeedPct(AuraApplication const* aurApp, uint8 mo return; Unit* target = aurApp->GetTarget(); - int32 spellGroupVal = target->GetHighestExclusiveSameEffectSpellGroupValue(this, SPELL_AURA_MELEE_SLOW); + int32 spellGroupVal = target->GetHighestExclusiveSameEffectSpellGroupValue(this, GetAuraType()); if (abs(spellGroupVal) >= abs(GetAmount())) return; @@ -4132,16 +4126,16 @@ void AuraEffect::HandleModCombatSpeedPct(AuraApplication const* aurApp, uint8 mo target->ApplyAttackTimePercentMod(BASE_ATTACK, float(spellGroupVal), !apply); target->ApplyAttackTimePercentMod(OFF_ATTACK, float(spellGroupVal), !apply); target->ApplyAttackTimePercentMod(RANGED_ATTACK, float(spellGroupVal), !apply); + + target->ApplyHasteRegenMod(float(spellGroupVal), !apply); } + target->ApplyCastTimePercentMod(float(GetAmount()), apply); target->ApplyAttackTimePercentMod(BASE_ATTACK, float(GetAmount()), apply); target->ApplyAttackTimePercentMod(OFF_ATTACK, float(GetAmount()), apply); target->ApplyAttackTimePercentMod(RANGED_ATTACK, float(GetAmount()), apply); - if (GetAuraType() != SPELL_AURA_MOD_MELEE_RANGED_HASTE_2 && GetAuraType() != SPELL_AURA_MOD_RANGED_HASTE_2) - { - target->ApplyHasteRegenMod(BASE_ATTACK, (float)GetAmount(), apply); - target->ApplyHasteRegenMod(RANGED_ATTACK, (float)GetAmount(), apply); - } + + target->ApplyHasteRegenMod((float)GetAmount(), apply); } void AuraEffect::HandleModAttackSpeed(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -4152,8 +4146,6 @@ void AuraEffect::HandleModAttackSpeed(AuraApplication const* aurApp, uint8 mode, Unit* target = aurApp->GetTarget(); target->ApplyAttackTimePercentMod(BASE_ATTACK, float(GetAmount()), apply); - if (GetAuraType() != SPELL_AURA_MOD_MELEE_RANGED_HASTE_2 && GetAuraType() != SPELL_AURA_MOD_RANGED_HASTE_2) - target->ApplyHasteRegenMod(BASE_ATTACK, (float)GetAmount(), apply); target->UpdateDamagePhysical(BASE_ATTACK); } @@ -4163,35 +4155,26 @@ void AuraEffect::HandleModMeleeSpeedPct(AuraApplication const* aurApp, uint8 mod return; Unit* target = aurApp->GetTarget(); - bool applyRegenPct = GetAuraType() != SPELL_AURA_MOD_MELEE_HASTE_3 && GetAuraType() != SPELL_AURA_MOD_RANGED_HASTE_2; - int32 spellGroupVal = target->GetHighestExclusiveSameEffectSpellGroupValue(this, GetAuraType()); - if (abs(spellGroupVal) >= abs(GetAmount())) + bool applyHasteRegenMod = GetAuraType() == SPELL_AURA_MOD_MELEE_HASTE; + + if (std::abs(spellGroupVal) >= std::abs(GetAmount())) return; if (spellGroupVal) { - if (target->GetTypeId() == TYPEID_PLAYER && applyRegenPct && GetBase()->HasEffectType(SPELL_AURA_MOD_MELEE_HASTE_3)) - target->ApplyHasteRegenMod(BASE_ATTACK, (float)GetAmount(), !apply); - else - { - target->ApplyAttackTimePercentMod(BASE_ATTACK, (float)GetAmount(), !apply); - target->ApplyAttackTimePercentMod(OFF_ATTACK, (float)GetAmount(), !apply); - if (applyRegenPct) - target->ApplyHasteRegenMod(BASE_ATTACK, (float)GetAmount(), !apply); - } + target->ApplyAttackTimePercentMod(BASE_ATTACK, float(spellGroupVal), !apply); + target->ApplyAttackTimePercentMod(OFF_ATTACK, float(spellGroupVal), !apply); + + if (applyHasteRegenMod) + target->ApplyHasteRegenMod(float(spellGroupVal), !apply); } - // Auras that only increase regeneration - if (target->GetTypeId() == TYPEID_PLAYER && applyRegenPct && GetBase()->HasEffectType(SPELL_AURA_MOD_MELEE_HASTE_3)) - target->ApplyHasteRegenMod(BASE_ATTACK, (float)GetAmount(), apply); - else - { - target->ApplyAttackTimePercentMod(BASE_ATTACK, (float)GetAmount(), apply); - target->ApplyAttackTimePercentMod(OFF_ATTACK, (float)GetAmount(), apply); - if (applyRegenPct) - target->ApplyHasteRegenMod(BASE_ATTACK, (float)GetAmount(), apply); - } + target->ApplyAttackTimePercentMod(BASE_ATTACK, float(GetAmount()), apply); + target->ApplyAttackTimePercentMod(OFF_ATTACK, float(GetAmount()), apply); + + if (applyHasteRegenMod) + target->ApplyHasteRegenMod(float(GetAmount()), apply); } void AuraEffect::HandleAuraModRangedHaste(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -4201,19 +4184,22 @@ void AuraEffect::HandleAuraModRangedHaste(AuraApplication const* aurApp, uint8 m Unit* target = aurApp->GetTarget(); int32 spellGroupVal = target->GetHighestExclusiveSameEffectSpellGroupValue(this, GetAuraType()); - if (abs(spellGroupVal) >= abs(GetAmount())) + bool applyHasteRegenMod = GetAuraType() == SPELL_AURA_MOD_RANGED_HASTE; + + if (std::abs(spellGroupVal) >= std::abs(GetAmount())) return; if (spellGroupVal) { - target->ApplyAttackTimePercentMod(RANGED_ATTACK, (float)GetAmount(), !apply); - if (GetAuraType() != SPELL_AURA_MOD_MELEE_RANGED_HASTE_2 && GetAuraType() != SPELL_AURA_MOD_RANGED_HASTE_2) - target->ApplyHasteRegenMod(RANGED_ATTACK, (float)GetAmount(), !apply); - } + target->ApplyAttackTimePercentMod(RANGED_ATTACK, float(spellGroupVal), !apply); - target->ApplyAttackTimePercentMod(RANGED_ATTACK, (float)GetAmount(), apply); - if (GetAuraType() != SPELL_AURA_MOD_MELEE_RANGED_HASTE_2 && GetAuraType() != SPELL_AURA_MOD_RANGED_HASTE_2) - target->ApplyHasteRegenMod(RANGED_ATTACK, (float)GetAmount(), apply); + if (applyHasteRegenMod) + target->ApplyHasteRegenMod(float(spellGroupVal), !apply); + } + target->ApplyAttackTimePercentMod(RANGED_ATTACK, float(GetAmount()), apply); + + if (applyHasteRegenMod) + target->ApplyHasteRegenMod(float(GetAmount()), apply); } /********************************/