diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 815644e689a4d3..6842bc4a41187b 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; @@ -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; @@ -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 diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 0669d6e4573e5a..de50e452a5b13d 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -369,9 +369,10 @@ class Creature : public Unit, public GridObject, 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; } @@ -539,6 +540,7 @@ class Creature : public Unit, public GridObject, public MovableMapObje 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 78d24b4dbc5ede..b52d5c708ed722 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" @@ -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) + 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). diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 391bb12e3097eb..b872101a952fc7 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -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); } }