Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/server/game/Entities/Creature/Creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -3713,7 +3713,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;
Expand All @@ -3722,12 +3722,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
Expand Down
4 changes: 3 additions & 1 deletion src/server/game/Entities/Creature/Creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -369,9 +369,10 @@ class Creature : public Unit, public GridObject<Creature>, public MovableMapObje
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; }
Expand Down Expand Up @@ -539,6 +540,7 @@ class Creature : public Unit, public GridObject<Creature>, public MovableMapObje

uint32 _playerDamageReq;
bool _damagedByPlayer;
uint8 _highestPlayerAttackerLevel;
bool _isCombatMovementAllowed;
};

Expand Down
22 changes: 22 additions & 0 deletions src/server/game/Entities/Player/KillRewarder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

#include "KillRewarder.h"
#include "Creature.h"
#include "Formulas.h"
#include "Group.h"
#include "Pet.h"
Expand Down Expand Up @@ -160,6 +161,27 @@ void KillRewarder::_RewardXP(Player* player, float rate)
else
xp = 0;
}
else if (Creature* creature = _victim->ToCreature())
{
// If an ungrouped helper attacked this creature and the mob
// is gray for that helper, reduce XP — consistent with the
// group formula (half XP when a gray-level member is present).
// If an ungrouped higher-level player helped kill this creature,
// apply group-like XP scaling: level ratio + gray penalty.
uint8 highestLevel = creature->GetHighestPlayerAttackerLevel();
if (highestLevel > player->GetLevel())
{
// Level ratio: same as group formula (playerLevel / sumOfLevels)
uint32 sumLevel = uint32(player->GetLevel()) + uint32(highestLevel);
float levelRate = float(player->GetLevel()) / float(sumLevel);

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.

xp = uint32(xp * levelRate / 2) + 1;
else
xp = uint32(xp * levelRate);
}
}
if (xp)
{
// 4.2.2. Apply auras modifying rewarded XP (SPELL_AURA_MOD_XP_PCT).
Expand Down
5 changes: 4 additions & 1 deletion src/server/game/Entities/Unit/Unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1149,7 +1149,10 @@ 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);
victim->ToCreature()->LowerPlayerDamageReq(unDamage, damagedByPlayer);
uint8 attackerLevel = 0;
if (Player* attackerPlayer = attacker ? attacker->GetCharmerOrOwnerPlayerOrPlayerItself() : nullptr)
attackerLevel = attackerPlayer->GetLevel();
victim->ToCreature()->LowerPlayerDamageReq(unDamage, damagedByPlayer, attackerLevel);
}
}

Expand Down
Loading