Skip to content

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

Open
Nyeriah wants to merge 1 commit intoazerothcore:masterfrom
Nyeriah:groupxp
Open

fix(Core/XP): Reduce XP when ungrouped high-level player helps kill#25161
Nyeriah wants to merge 1 commit intoazerothcore:masterfrom
Nyeriah:groupxp

Conversation

@Nyeriah
Copy link
Copy Markdown
Member

@Nyeriah Nyeriah commented Mar 21, 2026

Changes Proposed:

This PR proposes changes to:

  • Core (units, players, creatures, game systems).
  • Scripts (bosses, spell scripts, creature scripts).
  • Database (SAI, creatures, etc).

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.

  • AI tools (e.g. ChatGPT, Claude, or similar) were used entirely or partially in preparing this pull request. Claude Opus 4.6 was used.

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:

  • Tracks the highest player attacker level on each Creature during combat via _highestPlayerAttackerLevel, updated in LowerPlayerDamageReq() alongside existing damage tracking
  • On creature death, KillRewarder::_RewardXP() checks whether the mob is gray for the highest attacker (using existing GetGrayLevel()) and if so, halves the XP using the same formula as the group gray-member penalty (xp / 2 + 1)
  • The field resets on evade/respawn in ResetPlayerDamageReq()
  • Only applies to the solo (non-group) reward path — group XP already handles level differences

Issues Addressed:

SOURCE:

The changes have been validated through:

  • Live research (checked on live servers, e.g Classic WotLK, Retail, etc.)
  • Sniffs (remember to share them with the open source community!)
  • Video evidence, knowledge databases or other public sources (e.g forums, Wowhead, etc.)
  • The changes promoted by this pull request come partially or entirely from another project (cherry-pick).

Source: https://wowpedia.fandom.com/wiki/Mob_experience#Solo_Experience_Modifiers

"The XP you receive depends on if that someone else will receive xp for killing that mob. If yes, you get full XP. If not, you get a tiny fraction."

Tests Performed:

This PR has been:

  • Tested in-game by the author.
  • Tested in-game by other community members/someone else other than the author/has been live on production servers.
  • This pull request requires further testing and may have edge cases to be tested.

How to Test the Changes:

  • This pull request can be tested by following the reproduction steps provided in the linked issue
  • This pull request requires further testing. Provide steps to test your changes. If it requires any specific setup e.g multiple players please specify it as well.
  1. Create two characters: one level 8, one level 80 (.level 7 and .level 79)
  2. .cheat god for both, .tele rfc for both
  3. Grouped test (baseline): Group both characters, kill a level 13 Earthborer with the level 80 → level 8 gets ~12 XP (existing behavior, unchanged)
  4. Ungrouped test (fix): Leave group. Tag a level 13 Earthborer with the level 8, kill it with the level 80 → level 8 should now get ~128 XP instead of 255
  5. Solo test (no regression): Kill a level 13 Earthborer solo with the level 8 → should still get 255 XP
  6. Same-level helper test: Use a level 10 ungrouped helper instead of level 80 (mob is NOT gray for level 10) → level 8 should get full 255 XP

Known Issues and TODO List:

  • The "tiny fraction" described by Wowpedia is implemented as the group gray-member formula (half XP). The exact Blizzlike fraction is not precisely documented — this may need tuning based on further research or sniff data.

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.

Copilot AI review requested due to automatic review settings March 21, 2026 14:39
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 Creature during 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 Creature damage 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)
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
if (creature->GetLevel() <= grayLevel)
if (creature->GetLevel() <= grayLevel && xp > 0)

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@Nyeriah
Copy link
Copy Markdown
Member Author

Nyeriah commented Mar 21, 2026

Tested but could use more testing

@TheSCREWEDSoftware
Copy link
Copy Markdown
Contributor

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)

@Nyeriah
Copy link
Copy Markdown
Member Author

Nyeriah commented Mar 21, 2026

Its enough to pull a mob and hit it with a higher level player, I think

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CORE Related to the core file-cpp Used to trigger the matrix build Ready to be Reviewed Waiting to be Tested

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Core] - Experience doesn't get properly reduced when a higher level player kills a lower level player's mob

3 participants