Scaling updates for non creatures and fixes for elemental damage melee

This commit is contained in:
2025-08-01 18:50:14 -04:00
parent 3f3f63f90e
commit 1b1c54782b
6 changed files with 141 additions and 137 deletions

View File

@@ -121,7 +121,7 @@ MythicPlus.Ascendant.ItemOffset = 22000000
# - The diminishing returns are based on the difficulty of the dungeon and the amount of damage that has been done to the target.
##########################################################
MythicPlus.DiminishingExponent = 0.98
MythicPlus.DiminishingExponent = 0.96
MythicPlus.DiminishingThreshold.Mythic = 10000
MythicPlus.DiminishingThreshold.Legendary = 20000
MythicPlus.DiminishingThreshold.Ascendant = 40000
@@ -129,5 +129,6 @@ MythicPlus.DiminishingThreshold.Ascendant = 40000
##############
# Scaling Adjusters
#############
MythicPlus.MeleeAttackPowerDampener = 800
MythicPlus.MeleeAttackPowerStart = 3000
MythicPlus.ElementalMeleeReducer = 0.50
MythicPlus.NormalEnemyReducer = 0.50
MythicPlus.NonCreatureSpellReducer = 0.50

View File

@@ -243,8 +243,11 @@ void MythicPlus::ScaleAll(Player* player, MpInstanceData* instanceData)
{
std::vector<MpCreatureData*> creatures = sMpDataStore->GetInstanceCreatures(player->GetMapId(), player->GetInstanceId());
for (MpCreatureData* creatureData : creatures) {
// Only scale living creatures
if (creatureData->creature && creatureData->creature->IsAlive()) {
ScaleCreature(creatureData->creature->GetLevel(), creatureData->creature, &instanceData->creature, instanceData->difficulty);
}
}
}
// Perform any memory cleanup when the creature is removed from the world and no longer needed.
@@ -321,8 +324,8 @@ void MythicPlus::ScaleCreature(uint8 level, Creature* creature, MpMultipliers* m
// Additionally need to add in a decrease in attack power for normal non elite enemies
if (creature->GetCreatureTemplate()->rank == CREATURE_ELITE_NORMAL) {
// Reduced scaling for elite/boss spells to prevent them from hitting too hard
ap *= 0.5f;
rangeAp *= 0.5f;
ap *= normalEnemyReducer;
rangeAp *= normalEnemyReducer;
}
MpCreatureData* creatureData = sMpDataStore->GetCreatureData(creature->GetGUID());
@@ -454,13 +457,13 @@ int32 MythicPlus::ScaleDamageSpell(SpellInfo const * spellInfo, uint32 damage, M
int32 ownerOriginalLevel = ownerCreatureData->originalLevel;
if (ownerCreature->GetCreatureTemplate()->rank == CREATURE_ELITE_NORMAL) {
totalModifier = totalModifier * 0.75f;
totalModifier = totalModifier * normalEnemyReducer;
}
newDamage = CalculateSpellDamage(damage, ownerOriginalLevel, ownerCreature->GetLevel());
} else {
// Fallback if no creature data found - use current level
if(ownerCreature->GetCreatureTemplate()->rank == CREATURE_ELITE_NORMAL) {
totalModifier = totalModifier * 0.75f;
totalModifier = totalModifier * normalEnemyReducer;
}
newDamage = CalculateSpellDamage(damage, ownerCreature->GetLevel(), ownerCreature->GetLevel());
MpLogger::debug("No creature data found for owner {}, using current level for scaling", ownerCreature->GetGUID().ToString());
@@ -564,7 +567,7 @@ int32 MythicPlus::ScaleHealSpell(SpellInfo const * spellInfo, uint32 heal, MpCre
MpCreatureData* ownerCreatureData = sMpDataStore->GetCreatureData(ownerCreature->GetGUID());
if (ownerCreatureData) {
if (ownerCreature->GetCreatureTemplate()->rank == CREATURE_ELITE_NORMAL) {
totalModifier = totalModifier * 0.7f; // Less reduction for heals than damage
totalModifier = totalModifier * normalEnemyReducer; // Less reduction for heals than damage
}
// Scale heal based on target's health, not caster's health
if (target) {
@@ -579,7 +582,7 @@ int32 MythicPlus::ScaleHealSpell(SpellInfo const * spellInfo, uint32 heal, MpCre
} else {
// Fallback if no creature data found - use current level
if(ownerCreature->GetCreatureTemplate()->rank == CREATURE_ELITE_NORMAL) {
totalModifier = totalModifier * 0.7f; // Less reduction for heals than damage
totalModifier = totalModifier * normalEnemyReducer; // Less reduction for heals than damage
}
// Scale heal based on target's health, not caster's health
if (target) {
@@ -854,7 +857,7 @@ uint32 CalculateNewHealth(Creature* creature, CreatureTemplate const* cInfo, uin
// if it is a heroic instance give the enemy an additional 20% boost
InstanceMap* instanceMap = creature->GetMap()->ToInstanceMap();
if (instanceMap && (instanceMap->IsHeroic() || instanceMap->Is25ManRaid())) {
if (instanceMap && instanceMap->IsRaidOrHeroicDungeon()) {
basehp *= 1.25f;
}

View File

@@ -77,6 +77,12 @@ public:
float diminishingExponent;
std::unordered_map<MpDifficulty, uint32> diminishingThresholds;
// Specialized variables used in calculations
float elementalMeleeReducer;
float normalEnemyReducer;
float nonCreatureSpellReducer;
enum MP_UNIT_EVENT_TYPE
{
UNIT_EVENT_MELEE,

View File

@@ -14,6 +14,25 @@ public:
// {
// }
void OnCreatureRespawn(Creature* creature) override
{
Map* map = creature->GetMap();
if (!sMythicPlus->IsMapEligible(map)) {
return;
}
if (!sMythicPlus->IsCreatureEligible(creature)) {
return;
}
// If we have instance data, scale the creature, otherwise add it to be scaled later
if (MpInstanceData* instanceData = sMpDataStore->GetInstanceData(map->GetId(), map->GetInstanceId())) {
sMythicPlus->AddScaledCreature(creature, instanceData);
} else {
sMythicPlus->AddCreatureForScaling(creature);
}
}
// void OnAllCreatureUpdate(Creature* creature, uint32 diff) override
// {
// }

View File

@@ -10,6 +10,83 @@ public:
MythicPlus_UnitScript() : UnitScript("MythicPlus_UnitScript", true) { }
private:
/**
* Handles damage from non-creature sources (GameObjects, Players, etc.)
* @tparam DamageType Type of damage (int32/float)
* @param target Target of the damage
* @param attacker The non-creature attacker (passed by reference and may be modified)
* @param damage Reference to damage value (will be modified)
* @param spellInfo The spell being cast
* @param eventType Type of event (spell/melee/etc)
*/
template<typename DamageType>
void HandleNonCreatureAttacker(Unit* target, Unit*& attacker, DamageType& damage,
SpellInfo const* spellInfo, MythicPlus::MP_UNIT_EVENT_TYPE eventType)
{
Map* map = target ? target->GetMap() : nullptr;
std::string attackerType = "nullptr";
std::string attackerName = "unknown";
uint32 entry = 0;
if (attacker) {
if (attacker->GetTypeId() == TYPEID_GAMEOBJECT) {
attackerType = "GameObject";
if (GameObject* go = attacker->ToGameObject()) {
entry = go->GetEntry();
if (GameObjectTemplate const* goInfo = go->GetGOInfo()) {
attackerName = goInfo->name;
}
}
} else if (attacker->GetTypeId() == TYPEID_PLAYER) {
attackerType = "Player";
attackerName = attacker->GetName();
} else if (attacker->GetTypeId() == TYPEID_UNIT) {
attackerType = "Unit (non-creature)";
attackerName = attacker->GetName();
} else {
attackerType = "Unknown Type";
}
}
// Try to find a creature attacker from target's attackers list if we have one use it for scaling
Unit::AttackerSet const& attackers = target ? target->getAttackers() : Unit::AttackerSet();
if (!attackers.empty()) {
attacker = *attackers.begin();
if (Creature* creatureAttacker = attacker->ToCreature()) {
if (MpCreatureData* creatureData = sMpDataStore->GetCreatureData(creatureAttacker->GetGUID())) {
damage = static_cast<DamageType>(modifyIncomingDmgHeal(eventType, target, creatureAttacker,
static_cast<uint32>(damage), spellInfo)) * sMythicPlus->nonCreatureSpellReducer;
return;
}
} else {
MpLogger::debug("====== SPELL SCALING: Non-Creature attacker - Name: {}, Spell: {}({}), Damage: {}",
attackerName,
spellInfo ? spellInfo->SpellName[0] : "No Spell",
spellInfo ? spellInfo->Id : 0,
damage);
if (map) {
if (MpInstanceData* instanceData = sMpDataStore->GetInstanceData(map->GetId(), map->GetInstanceId())) {
damage = static_cast<DamageType>(damage * instanceData->creature.spell * sMythicPlus->nonCreatureSpellReducer);
return;
}
}
}
}
// Fallback to instance-based scaling if we can't find a nearest creature
else if (map) {
if (MpInstanceData* instanceData = sMpDataStore->GetInstanceData(map->GetId(), map->GetInstanceId())) {
damage = static_cast<DamageType>(damage * instanceData->creature.spell * sMythicPlus->nonCreatureSpellReducer);
return;
}
}
// Default scaling if no specific handler applied
return;
}
// Helper function to determine if a spell scales with Attack Power
bool IsAttackPowerScalingSpell(SpellInfo const* spellInfo) {
if (!spellInfo || spellInfo->Effects.empty()) {
@@ -63,37 +140,11 @@ private:
return;
}
// Check if attacker is a GameObject (traps, totems, environmental hazards)
// If this is a special case where the attacker is not a creature
if (!attacker || !attacker->ToCreature()) {
MpLogger::debug("SPELL SCALING: Attacker is not a creature (likely GameObject or null), skipping CalcValue reversal");
// Apply Mythic+ scaling to original damage without CalcValue reversal
damage = static_cast<DamageType>(modifyIncomingDmgHeal(eventType, target, attacker, static_cast<uint32>(damage), spellInfo));
return;
return HandleNonCreatureAttacker(target, attacker, damage, spellInfo, eventType);
}
// Debug: Log spell effects to understand what we're dealing with
// if (spellInfo->Effects.size() > 0) {
// auto mainEffect = spellInfo->Effects[0];
// MpLogger::debug("{}: Incoming Damage: {} Spell {} (ID: {}) Family: {} has RealPointsPerLevel: {} and BasePoints: {} and DieSides: {} and School: {}", logPrefix,
// damage,
// spellInfo->SpellName[0],
// spellInfo->Id,
// mainEffect.RealPointsPerLevel,
// mainEffect.BasePoints,
// mainEffect.DieSides,
// spellInfo->SchoolMask);
// } else {
// MpLogger::debug("{}: Incoming Damage: {} Spell {} (ID: {}) has no effects and School: {}", logPrefix,
// damage,
// spellInfo->SpellName[0],
// spellInfo->Id,
// spellInfo->SchoolMask);
// return;
// }
Creature* creatureCaster = attacker->ToCreature();
MpCreatureData* creatureData = sMpDataStore->GetCreatureData(creatureCaster->GetGUID());
@@ -115,14 +166,14 @@ private:
if (spellInfo && !spellInfo->Effects.empty()) {
int32 baseEffect = spellInfo->Effects[0].CalcValue(attacker, nullptr, nullptr);
if (damage <= (baseEffect * 1.15f)) {
MpLogger::debug(">>>> MELEE SPELL SCALING: Spell {} (ID: {}) is not scaled by AP damage: {} vs originalEffect: {}",
spellInfo->SpellName[0], spellInfo->Id, damage, baseEffect);
// MpLogger::debug(">>>> MELEE SPELL SCALING: Spell {} (ID: {}) is not scaled by AP damage: {} vs originalEffect: {}",
// spellInfo->SpellName[0], spellInfo->Id, damage, baseEffect);
notScaledByAP = true;
}
} else {
// If we can't determine the base effect, default to treating it as not AP-scaled
notScaledByAP = true;
MpLogger::debug(">>>> MELEE SPELL SCALING: Could not determine base effect for spell, defaulting to spell scaling");
// MpLogger::debug(">>>> MELEE SPELL SCALING: Could not determine base effect for spell, defaulting to spell scaling");
}
// if the effect type of the spell is not physical (aka not mitigated by armor/defense) then it needs to instead have the typical
@@ -132,7 +183,7 @@ private:
damage = modifyIncomingDmgHeal(MythicPlus::UNIT_EVENT_MELEE, target, attacker, meleeDamage);
MpLogger::debug(">>MELEE SPELL SCALING: {} hits with spell: {} ID: {} meleeDamage: {} damage: {}", attacker->GetName(), spellInfo->SpellName[0], spellInfo->Id, meleeDamage, damage);
// MpLogger::debug(">>MELEE SPELL SCALING: {} hits with spell: {} ID: {} meleeDamage: {} damage: {}", attacker->GetName(), spellInfo->SpellName[0], spellInfo->Id, meleeDamage, damage);
} else {
// get the creatures original attack power
@@ -143,7 +194,7 @@ private:
uint32 apDmg = static_cast<uint32>(creatureData->originalStats->AttackPower * 0.10f);
uint32 finalDmg = spellDmg + apDmg;
MpLogger::debug(">> AP BASED DAMAGE Scaledown: origDamage: {} | spellDmg: {} | apDmg: {} | finalDmg: {}", static_cast<int32>(damage), spellDmg, apDmg, finalDmg);
// MpLogger::debug(">> AP BASED DAMAGE Scaledown: origDamage: {} | spellDmg: {} | apDmg: {} | finalDmg: {}", static_cast<int32>(damage), spellDmg, apDmg, finalDmg);
damage = modifyIncomingDmgHeal(MythicPlus::UNIT_EVENT_SPELL, target, attacker, finalDmg, spellInfo);
@@ -231,7 +282,7 @@ public:
if (!target && !attacker) {
if(spellInfo) {
MpLogger::info("ModifySpellDamageTaken: Target and attacker are null for spell: {} ID: {}", spellInfo->SpellName[0], spellInfo->Id);
// MpLogger::info("ModifySpellDamageTaken: Target and attacker are null for spell: {} ID: {}", spellInfo->SpellName[0], spellInfo->Id);
}
return;
@@ -244,12 +295,12 @@ public:
if(!sMythicPlus->EligibleDamageTarget(target)) {
if(spellInfo) {
MpLogger::info("ModifySpellDamageTaken: Target is not eligible for spell: {} ID: {}", spellInfo->SpellName[0], spellInfo->Id);
// MpLogger::info("ModifySpellDamageTaken: Target is not eligible for spell: {} ID: {}", spellInfo->SpellName[0], spellInfo->Id);
}
return;
}
MpLogger::debug("ModifySpellDamageTaken: {} hits {} with spell: {} ID: {}", attacker ? attacker->GetName() : "[null]", target ? target->GetName() : "[null]", spellInfo ? spellInfo->SpellName[0] : "[no spell]", spellInfo ? spellInfo->Id : 0);
// MpLogger::debug("ModifySpellDamageTaken: {} hits {} with spell: {} ID: {}", attacker ? attacker->GetName() : "[null]", target ? target->GetName() : "[null]", spellInfo ? spellInfo->SpellName[0] : "[no spell]", spellInfo ? spellInfo->Id : 0);
// Use the generic ProcessSpellDamage function
ProcessSpellDamage(target, attacker, damage, spellInfo, MythicPlus::UNIT_EVENT_SPELL, "SPELL DAMAGE");
@@ -286,93 +337,9 @@ public:
healing = modifyIncomingDmgHeal(MythicPlus::UNIT_EVENT_HEAL, target, healer, healing, spellInfo);
}
void OnAuraApply(Unit* unit, Aura* aura) override {
if (!unit || !aura) {
return;
}
// Only scale auras for players
if (!unit->IsPlayer()) {
return;
}
#if defined(MOD_PRESENT_NPCBOTS)
if (unit->IsNPCBotOrPet()) {
return;
}
#endif
Map* map = unit->GetMap();
if (!sMythicPlus->IsMapEligible(map)) {
return;
}
// Get instance data for scaling factors
MpInstanceData* instanceData = sMpDataStore->GetInstanceData(map->GetId(), map->GetInstanceId());
if (!instanceData) {
return;
}
SpellInfo const* spellInfo = aura->GetSpellInfo();
if (!spellInfo) {
return;
}
Creature* creatureCaster = aura->GetCaster()->ToCreature();
if (!creatureCaster) {
return;
}
// MpLogger::debug("Aura Apply: {} applied to {} by {} Id: {}", spellInfo->SpellName[0], unit->GetName(), aura->GetCaster()->GetName(), aura->GetId());
// Scale aura effects based on creature type and instance difficulty
float scaleFactor = 1.0f;
if (creatureCaster->IsDungeonBoss() || creatureCaster->isWorldBoss()) {
scaleFactor = instanceData->boss.spell * 0.8f; // Reduce boss aura scaling slightly
} else {
scaleFactor = instanceData->creature.spell * 0.7f; // Reduce normal creature aura scaling
}
// Apply scaling to stat modifier effects only (damage auras handled elsewhere)
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) {
AuraEffect* effect = aura->GetEffect(i);
if (!effect) {
continue;
}
auto amount = effect->GetAmount();
// MpLogger::debug("Aura Effect type: {} amount: {}", effect->GetAuraType(), amount);
// // Only scale stat modifiers and resistances (not damage/healing effects)
// if (effect->GetAuraType() == SPELL_AURA_MOD_STAT ||
// effect->GetAuraType() == SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE ||
// effect->GetAuraType() == SPELL_AURA_MOD_RESISTANCE ||
// effect->GetAuraType() == SPELL_AURA_MOD_RESISTANCE_PCT ||
// effect->GetAuraType() == SPELL_AURA_MOD_BASE_RESISTANCE_PCT ||
// effect->GetAuraType() == SPELL_AURA_MOD_DAMAGE_DONE ||
// effect->GetAuraType() == SPELL_AURA_MOD_DAMAGE_TAKEN ||
// effect->GetAuraType() == SPELL_AURA_MOD_HEALING_DONE ||
// effect->GetAuraType() == SPELL_AURA_MOD_HEALING_PCT ||
// effect->GetAuraType() == SPELL_AURA_MOD_HEALING_TAKEN_PCT ||
// effect->GetAuraType() == SPELL_AURA_MOD_ARMOR_PENETRATION_PCT ||
// effect->GetAuraType() == SPELL_AURA_MOD_ATTACK_POWER ||
// effect->GetAuraType() == SPELL_AURA_MOD_ATTACK_POWER_PCT ||
// effect->GetAuraType() == SPELL_AURA_MOD_SPELL_POWER_PCT) {
// int32 originalAmount = effect->GetAmount();
// int32 newAmount = int32(originalAmount * scaleFactor);
// effect->ChangeAmount(newAmount);
// MpLogger::debug("AURA SCALING: Scaled {} stat modifier effect {} from {} to {} (factor: {:.2f})",
// spellInfo->SpellName[0], i, originalAmount, newAmount, scaleFactor);
// }
}
}
uint32 modifyIncomingDmgHeal(MythicPlus::MP_UNIT_EVENT_TYPE eventType,Unit* target, Unit* attacker, uint32 damageOrHeal, SpellInfo const* spellInfo = nullptr) {
if (!target && !attacker) {
MpLogger::info("modifyIncomingDmgHeal: Target and attacker are null for event {}", eventType);
// MpLogger::info("modifyIncomingDmgHeal: Target and attacker are null for event {}", eventType);
return damageOrHeal;
}
@@ -431,12 +398,20 @@ public:
*/
switch (eventType) {
case MythicPlus::UNIT_EVENT_MELEE:
// Damage that is not mitigated by armor needs to be debuffed as it hits too hard and without resists
// it hits too hard give everyone a benefit of 30% armor reduction
MpLogger::debug(">>> Modify Melee Damage: Creature Name: {} alteredDmgHeal: {} School Mask: {}", creature->GetName(), alteredDmgHeal, creature->GetMeleeDamageSchoolMask());
if(creature->GetMeleeDamageSchoolMask() != SPELL_SCHOOL_MASK_NORMAL && creature->GetMeleeDamageSchoolMask() != SPELL_SCHOOL_MASK_NONE) {
damageOrHeal = damageOrHeal * 0.50f;
}
if(creature->IsDungeonBoss() || creature->isWorldBoss() || creature->GetEntry() == 23682) {
alteredDmgHeal = damageOrHeal * instanceData->boss.melee;
} else {
alteredDmgHeal = damageOrHeal * instanceData->creature.melee;
}
// MpLogger::debug("Incoming Melee New Damage: {}({}) {} hits {}", alteredDmgHeal, damageOrHeal, attacker->GetName(), target->GetName());
// MpLogger::debug(">>>>>>>>>>>> Incoming Melee New Damage: {}({}) {} hits {}", alteredDmgHeal, damageOrHeal, attacker->GetName(), target->GetName());
break;
case MythicPlus::UNIT_EVENT_DOT:

View File

@@ -89,10 +89,6 @@ public:
sMythicPlus->legendaryItemOffset = sConfigMgr->GetOption<uint32>("MythicPlus.Legendary.ItemOffset", 21000000);
sMythicPlus->ascendantItemOffset = sConfigMgr->GetOption<uint32>("MythicPlus.Ascendant.ItemOffset", 22000000);
// Deprecated part of old attack power scaling
sMythicPlus->meleeAttackPowerDampener = sConfigMgr->GetOption<uint32>("MythicPlus.MeleeAttackPowerDampener", 2000);
sMythicPlus->meleeAttackPowerStart = sConfigMgr->GetOption<uint32>("MythicPlus.MeleeAttackPowerStart", 10000);
// Get diminishing returns from configuration
sMythicPlus->diminishingExponent = sConfigMgr->GetOption<float>("MythicPlus.DiminishingExponent", 0.975f);
sMythicPlus->diminishingThresholds = {
@@ -100,6 +96,10 @@ public:
{MpDifficulty::MP_DIFFICULTY_LEGENDARY, sConfigMgr->GetOption<uint32>("MythicPlus.DiminishingThreshold.Legendary", 20000)},
{MpDifficulty::MP_DIFFICULTY_ASCENDANT, sConfigMgr->GetOption<uint32>("MythicPlus.DiminishingThreshold.Ascendant", 40000)}
};
sMythicPlus->elementalMeleeReducer = sConfigMgr->GetOption<float>("MythicPlus.ElementalMeleeReducer", 0.50f);
sMythicPlus->normalEnemyReducer = sConfigMgr->GetOption<float>("MythicPlus.NormalEnemyReducer", 0.50f);
sMythicPlus->nonCreatureSpellReducer = sConfigMgr->GetOption<float>("MythicPlus.NonCreatureSpellReducer", 0.50f);
}
void OnStartup() override