fix(Core/XP): Reduce XP when ungrouped high-level player helps kill#25161
fix(Core/XP): Reduce XP when ungrouped high-level player helps kill#25161Nyeriah wants to merge 1 commit intoazerothcore:masterfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adjusts solo XP rewards to mitigate a power-leveling exploit where an ungrouped high-level helper (for whom the mob is gray) enables a low-level tagger to receive full XP.
Changes:
- Track the highest player attacker level per
Creatureduring combat. - Apply a solo XP penalty on kill reward when the mob is gray for the highest-level attacker (halving XP via
xp / 2 + 1). - Reset the tracked highest attacker level on
Creaturedamage requirement reset.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/server/game/Entities/Unit/Unit.cpp | Passes attacker player level into Creature::LowerPlayerDamageReq while dealing damage. |
| src/server/game/Entities/Player/KillRewarder.cpp | Applies solo XP reduction when the mob is gray for the highest-level attacker. |
| src/server/game/Entities/Creature/Creature.h | Extends damage tracking API and exposes highest attacker level getter. |
| src/server/game/Entities/Creature/Creature.cpp | Stores/initializes/resets _highestPlayerAttackerLevel and updates it during damage tracking. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (highestLevel > player->GetLevel()) | ||
| { | ||
| uint8 grayLevel = Acore::XP::GetGrayLevel(highestLevel); | ||
| if (creature->GetLevel() <= grayLevel) |
There was a problem hiding this comment.
xp = xp / 2 + 1 can incorrectly turn an already-zero XP reward into 1 (e.g., if earlier logic set xp = 0 due to no-XP conditions). Add a guard so the reduction only applies when xp > 0 (or restructure so the “+1” is only used when the base XP was positive), ensuring no-XP kills remain no-XP.
| if (creature->GetLevel() <= grayLevel) | |
| if (creature->GetLevel() <= grayLevel && xp > 0) |
There was a problem hiding this comment.
This can't happen in practice. The solo path (else if) is mutually exclusive with the if (_group) branch, which is the only place xp gets zeroed. In the solo path, xp starts as _xp and nothing sets it to 0 before reaching this code.
Additionally, the caller already guards against this — _RewardXP is only invoked when _xp > 0 (line 214: if (_xp)), and _xp is a member that doesn't change during the reward flow.
When an ungrouped player helps kill a creature tagged by a lower-level player, apply group-like XP scaling: level ratio (playerLevel / sumOfLevels) plus gray penalty (half XP if mob is gray for the helper). This prevents the power-leveling exploit where a high-level player leaves the group to grant full XP to a lower-level character. Tracks the highest player attacker level on each creature during combat and uses it in KillRewarder to scale solo XP consistently with the group formula. Closes azerothcore#24191 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Tested but could use more testing |
The testing steps could even applied in world of having a level 60 helping farming mobs south of elywirn forest for full XP capped to your level ranged (prior to this, have not tested this and wont be able most likely during the weekend) |
|
Its enough to pull a mob and hit it with a higher level player, I think |
Changes Proposed:
This PR proposes changes to:
AI-assisted Pull Requests
Important
While the use of AI tools when preparing pull requests is not prohibited, contributors must clearly disclose when such tools have been used and specify the model involved.
Contributors are also expected to fully understand the changes they are submitting and must be able to explain and justify those changes when requested by maintainers.
Description
When an ungrouped player whose level makes the mob gray assists in killing a creature tagged by a lower-level player, the tagger now receives halved XP instead of the full amount. Currently the tagger receives full XP, which enables a power-leveling exploit where a high-level player leaves the group to grant full XP to a lower-level character.
Implementation:
Creatureduring combat via_highestPlayerAttackerLevel, updated inLowerPlayerDamageReq()alongside existing damage trackingKillRewarder::_RewardXP()checks whether the mob is gray for the highest attacker (using existingGetGrayLevel()) and if so, halves the XP using the same formula as the group gray-member penalty (xp / 2 + 1)ResetPlayerDamageReq()Issues Addressed:
SOURCE:
The changes have been validated through:
Source: https://wowpedia.fandom.com/wiki/Mob_experience#Solo_Experience_Modifiers
Tests Performed:
This PR has been:
How to Test the Changes:
.level 7and.level 79).cheat godfor both,.tele rfcfor bothKnown Issues and TODO List:
How to Test AzerothCore PRs
When a PR is ready to be tested, it will be marked as [WAITING TO BE TESTED].
You can help by testing PRs and writing your feedback here on the PR's page on GitHub. Follow the instructions here:
http://www.azerothcore.org/wiki/How-to-test-a-PR
REMEMBER: when testing a PR that changes something generic (i.e. a part of code that handles more than one specific thing), the tester should not only check that the PR does its job (e.g. fixing spell XXX) but especially check that the PR does not cause any regression (i.e. introducing new bugs).
For example: if a PR fixes spell X by changing a part of code that handles spells X, Y, and Z, we should not only test X, but we should test Y and Z as well.