diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 70e11e0f5f..c895d8136d 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -929,7 +929,7 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons // Rage from damage received if (attacker != victim && victim->GetPowerType() == POWER_RAGE) { - uint32 rageBaseReward = std::max(damage, unmitigatedDamage); + uint32 rageBaseReward = CalcDamageTakenRageGain(victim, std::max(damage, unmitigatedDamage)); victim->RewardRage(rageBaseReward, false); } @@ -1150,6 +1150,37 @@ void Unit::DealSpellDamage(SpellNonMeleeDamage* damageInfo, bool durabilityLoss) Unit::DealDamage(this, victim, damageInfo->damage, damageInfo->unmitigatedDamage, SPELL_DIRECT_DAMAGE, SpellSchoolMask(damageInfo->schoolMask), spellProto, durabilityLoss); } +/*static*/ uint32 Unit::CalcMeleeAttackRageGain(Unit const* attacker, Unit const* victim, WeaponAttackType attType) +{ + if (!attacker || !victim || (attType != BASE_ATTACK && attType != OFF_ATTACK)) + return 0; + + uint32 rage = uint32(attacker->GetBaseAttackTime(attType) / 1000.f * 6.5f); + if (attType == OFF_ATTACK) + rage /= 2; + + // Players below level 10 start with a 50% rage gain reduction that slowly diminishes + if (attacker->IsPlayer() && attacker->getLevel() < 10) + rage -= (rage / 2.f) * (1.0f - (attacker->getLevel() / 10.f)); + + return uint32(rage * 10); +} + +// The minimum amount a damaging hit should award +constexpr uint32 MIN_RAGE_AMOUNT = 10; + +/*static*/ uint32 Unit::CalcDamageTakenRageGain(Unit const* victim, uint32 damage) +{ + if (!victim || damage == 0) + return 0; + + double multiplier = static_cast(damage) / victim->GetMaxHealth(); + + // This formula is based on sampling multiple sniff data at multiple level ranges + // There has never been any leak and the only info we have is that the amount is based on max HP and damage taken and that there is a hidden constant + return std::max(MIN_RAGE_AMOUNT, std::ceil(400 * multiplier)); +} + /// @todo for melee need create structure as in void Unit::CalculateMeleeDamage(Unit* victim, CalcDamageInfo* damageInfo, WeaponAttackType attackType /*= BASE_ATTACK*/) { @@ -1922,42 +1953,6 @@ static float GetArmorReduction(float armor, uint8 attackerLevel) healInfo.AbsorbHeal(absorbAmount); } -// Calculates the normalized rage amount per weapon swing -inline static uint32 CalcMeleeAttackRageGain(Unit const* attacker, Unit const* victim, WeaponAttackType attType) -{ - float attackDelay = [&]() - { - if (Player const* playerAttacker = attacker->ToPlayer()) - { - if (!playerAttacker->IsInFeralForm()) - { - Item const* weapon = playerAttacker->GetWeaponForAttack(attType); - if (weapon) - return static_cast(weapon->GetTemplate()->GetDelay()); - } - else if (SpellShapeshiftFormEntry const* shapeShiftFormEntry = sSpellShapeshiftFormStore.LookupEntry(playerAttacker->GetShapeshiftForm())) - return static_cast(shapeShiftFormEntry->CombatRoundTime); - } - else if (Creature const* creature = attacker->ToCreature()) - return static_cast(creature->GetCreatureTemplate()->BaseAttackTime); - - return 0.f; - - }(); - - uint32 rageAmount = attackDelay / 1000 * 6.5f; - - // Sentinel - if (victim->GetVictim() && victim->GetVictim() != attacker) - if (AuraEffect* aurEff = attacker->GetAuraEffect(SPELL_AURA_DUMMY, SPELLFAMILY_GENERIC, 1916, EFFECT_1)) - rageAmount += CalculatePct(rageAmount, aurEff->GetAmount()); - - if (attType == OFF_ATTACK) - rageAmount *= 0.5f; - - return rageAmount; -} - void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extra) { if (HasUnitState(UNIT_STATE_CANNOT_AUTOATTACK) || HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED)) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index d5a66cdf49..f90ece4173 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -888,6 +888,8 @@ class TC_GAME_API Unit : public WorldObject void HandleEmoteCommand(uint32 emoteId); void AttackerStateUpdate(Unit* victim, WeaponAttackType attType = BASE_ATTACK, bool extra = false); + static uint32 CalcMeleeAttackRageGain(Unit const* attacker, Unit const* victim, WeaponAttackType attType); + static uint32 CalcDamageTakenRageGain(Unit const* victim, uint32 damage); void CalculateMeleeDamage(Unit* victim, CalcDamageInfo* damageInfo, WeaponAttackType attackType = BASE_ATTACK); void DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss); void HandleProcExtraAttackFor(Unit* victim);