fix(Core/XP): Reduce XP when ungrouped high-level player helps kill (#25161)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Ludwig <sudlud@users.noreply.github.com>
This commit is contained in:
Andrew
2026-06-24 04:19:30 -03:00
committed by GitHub
parent dd7af13fb3
commit 8f6de287c8
4 changed files with 35 additions and 4 deletions

View File

@@ -264,7 +264,7 @@ Creature::Creature(): Unit(), MovableMapObject(), m_groupLootTimer(0), lootingGr
m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_alreadyCallForHelp(false), m_AlreadyCallAssistance(false),
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_regenPower(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), _gossipMenuId(0), m_moveInLineOfSightDisabled(false), m_moveInLineOfSightStrictlyDisabled(false),
m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_detectionDistance(20.0f),_sparringPct(0.0f), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_lastLeashExtensionTime(nullptr),
_isMissingSwimmingFlagOutOfCombat(false), m_assistanceTimer(0), _playerDamageReq(0), _damagedByPlayer(false), _isCombatMovementAllowed(true)
_isMissingSwimmingFlagOutOfCombat(false), m_assistanceTimer(0), _playerDamageReq(0), _damagedByPlayer(false), _highestPlayerAttackerLevel(0), _isCombatMovementAllowed(true)
{
m_regenTimer = CREATURE_REGEN_INTERVAL;
m_valuesCount = UNIT_END;
@@ -3836,7 +3836,7 @@ bool Creature::IsDamageEnoughForLootingAndReward() const
return m_creatureInfo->HasFlagsExtra(CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ) || (_playerDamageReq == 0 && _damagedByPlayer);
}
void Creature::LowerPlayerDamageReq(uint32 unDamage, bool damagedByPlayer /*= true*/)
void Creature::LowerPlayerDamageReq(uint32 unDamage, bool damagedByPlayer /*= true*/, uint8 attackerLevel /*= 0*/)
{
if (_playerDamageReq)
_playerDamageReq > unDamage ? _playerDamageReq -= unDamage : _playerDamageReq = 0;
@@ -3845,12 +3845,16 @@ void Creature::LowerPlayerDamageReq(uint32 unDamage, bool damagedByPlayer /*= tr
{
_damagedByPlayer = damagedByPlayer;
}
if (attackerLevel > _highestPlayerAttackerLevel)
_highestPlayerAttackerLevel = attackerLevel;
}
void Creature::ResetPlayerDamageReq()
{
_playerDamageReq = GetHealth() / 2;
_damagedByPlayer = false;
_highestPlayerAttackerLevel = 0;
}
uint32 Creature::GetPlayerDamageReq() const

View File

@@ -380,9 +380,10 @@ public:
void SetLootRewardDisabled(bool disable) { DisableLootReward = disable; }
[[nodiscard]] bool IsLootRewardDisabled() const { return DisableLootReward; }
[[nodiscard]] bool IsDamageEnoughForLootingAndReward() const;
void LowerPlayerDamageReq(uint32 unDamage, bool damagedByPlayer = true);
void LowerPlayerDamageReq(uint32 unDamage, bool damagedByPlayer = true, uint8 attackerLevel = 0);
void ResetPlayerDamageReq();
[[nodiscard]] uint32 GetPlayerDamageReq() const;
[[nodiscard]] uint8 GetHighestPlayerAttackerLevel() const { return _highestPlayerAttackerLevel; }
[[nodiscard]] uint32 GetOriginalEntry() const { return m_originalEntry; }
void SetOriginalEntry(uint32 entry) { m_originalEntry = entry; }
@@ -554,6 +555,7 @@ private:
uint32 _playerDamageReq;
bool _damagedByPlayer;
uint8 _highestPlayerAttackerLevel;
bool _isCombatMovementAllowed;
};

View File

@@ -16,6 +16,7 @@
*/
#include "KillRewarder.h"
#include "Creature.h"
#include "Formulas.h"
#include "Group.h"
#include "Pet.h"
@@ -161,6 +162,19 @@ void KillRewarder::_RewardXP(Player* player, float rate)
else
xp = 0;
}
// An ungrouped helper who out-levels everyone eligible for the kill and for
// whom the victim is gray halves the tagger's XP, matching the group
// gray-member penalty. Closes a power-leveling exploit.
if (xp)
if (Creature* creature = _victim->ToCreature())
{
uint8 const referenceLevel = _group ? _maxLevel : player->GetLevel();
uint8 const highestLevel = creature->GetHighestPlayerAttackerLevel();
if (highestLevel > referenceLevel && creature->GetLevel() <= Acore::XP::GetGrayLevel(highestLevel))
xp = xp / 2 + 1;
}
if (xp)
{
// 4.2.2. Apply auras modifying rewarded XP (SPELL_AURA_MOD_XP_PCT).

View File

@@ -1233,7 +1233,18 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage
uint32 unDamage = health < damage ? health : damage;
bool damagedByPlayer = unDamage && attacker && (attacker->IsPlayer() || attacker->m_movedByPlayer != nullptr
|| attacker->GetCharmerGUID().IsPlayer());
victim->ToCreature()->LowerPlayerDamageReq(unDamage, damagedByPlayer);
uint8 attackerLevel = 0;
if (damagedByPlayer)
{
Player* attackerPlayer = attacker->GetCharmerOrOwnerPlayerOrPlayerItself();
if (!attackerPlayer && attacker->m_movedByPlayer)
attackerPlayer = attacker->m_movedByPlayer->ToPlayer();
if (attackerPlayer)
attackerLevel = attackerPlayer->GetLevel();
}
victim->ToCreature()->LowerPlayerDamageReq(unDamage, damagedByPlayer, attackerLevel);
}
}