From 8f6de287c844045a05c38b5214d8643248f74e91 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Wed, 24 Jun 2026 04:19:30 -0300 Subject: [PATCH] fix(Core/XP): Reduce XP when ungrouped high-level player helps kill (#25161) Co-authored-by: Claude Opus 4.6 Co-authored-by: Ludwig --- src/server/game/Entities/Creature/Creature.cpp | 8 ++++++-- src/server/game/Entities/Creature/Creature.h | 4 +++- src/server/game/Entities/Player/KillRewarder.cpp | 14 ++++++++++++++ src/server/game/Entities/Unit/Unit.cpp | 13 ++++++++++++- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 028c3cb2a..f725f0c76 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -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 diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index c0eb2a753..b492bd6c8 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -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; }; diff --git a/src/server/game/Entities/Player/KillRewarder.cpp b/src/server/game/Entities/Player/KillRewarder.cpp index 0bbb4c541..5a32ec4b0 100644 --- a/src/server/game/Entities/Player/KillRewarder.cpp +++ b/src/server/game/Entities/Player/KillRewarder.cpp @@ -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). diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 0a66e4afd..d8fe891f0 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -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); } }