Adding base increases to normalize damange similar to heroic dungeons from normal

This commit is contained in:
2024-09-30 21:03:28 -04:00
parent b6e21b5cf7
commit d7e1f7a026
11 changed files with 203 additions and 100 deletions

View File

@@ -1,23 +1,21 @@
#include "MapMgr.h"
#include "MpDataStore.h"
#include "ObjectMgr.h"
#include "MpLogger.h"
#include "MythicPlus.h"
#include "ScriptMgr.h"
#include "Log.h"
class MythicPlus_AllCreatureScript : public AllCreatureScript
{
public:
MythicPlus_AllCreatureScript() : AllCreatureScript("MythicPlus_AllCreatureScript")
{
}
MythicPlus_AllCreatureScript() : AllCreatureScript("MythicPlus_AllCreatureScript") {}
// void OnBeforeCreatureSelectLevel(const CreatureTemplate* /*creatureTemplate*/, Creature* creature, uint8& level) override
// {
// }
// void OnAllCreatureUpdate(Creature* creature, uint32 diff) override
// {
// }
// When a new creature is added into a mythic+ map add it to the list of creatures to scale later.
void OnCreatureAddWorld(Creature* creature) override
{
@@ -30,7 +28,6 @@ public:
return;
}
// if we have instance data about zone then just scale the creature otherwise add to be scaled once we do.
MpInstanceData* instanceData = sMpDataStore->GetInstanceData(map->GetId(), map->GetInstanceId());
if(instanceData) {
@@ -40,10 +37,6 @@ public:
}
}
// void OnAllCreatureUpdate(Creature* creature, uint32 diff) override
// {
// }
// Cleanup the creature from custom data used for mythic+ mod
void OnCreatureRemoveWorld(Creature* creature) override
{

View File

@@ -1,12 +1,12 @@
#include "Chat.h"
#include "Log.h"
#include "MapMgr.h"
#include "MpLogger.h"
#include "Map.h"
#include "MpDataStore.h"
#include "MythicPlus.h"
#include "Player.h"
#include "ScriptMgr.h"
class MythicPlus_AllMapScript : public AllMapScript
{
public:
@@ -55,7 +55,6 @@ public:
return;
}
uint8 avgLevel = 0;
MpInstanceData instanceData;
switch(groupData->difficulty) {
case MP_DIFFICULTY_MYTHIC:

View File

@@ -87,22 +87,13 @@ public:
}
if (difficulty == "mythic") {
sMpDataStore->AddGroupData(group, {
.group = group,
.difficulty = MP_DIFFICULTY_MYTHIC
});
sMpDataStore->AddGroupData(group, MpGroupData(group, MP_DIFFICULTY_MYTHIC, 0));
}
else if (difficulty == "legendary") {
sMpDataStore->AddGroupData(group,{
.group = group,
.difficulty = MP_DIFFICULTY_LEGENDARY
});
sMpDataStore->AddGroupData(group,MpGroupData(group, MP_DIFFICULTY_LEGENDARY, 0));
}
else if (difficulty == "ascendant") {
sMpDataStore->AddGroupData(group, {
.group = group,
.difficulty = MP_DIFFICULTY_ASCENDANT
});
sMpDataStore->AddGroupData(group, MpGroupData(group, MP_DIFFICULTY_ASCENDANT, 0));
}
else {
handler->PSendSysMessage("|cFFFF0000 Invalid difficulty level. Expected values are 'mythic', 'legendary', or 'ascendant'.");

View File

@@ -3,7 +3,6 @@
#include "ScriptMgr.h"
#include "Player.h"
#include "Map.h"
#include "ObjectMgr.h"
class MythicPlus_GlobalScript : public GlobalScript
{

View File

@@ -2,6 +2,7 @@
#include "MpDataStore.h"
#include "MpLogger.h"
#include "ScriptMgr.h"
#include "Group.h"
// this handles updating custom group difficulties used in auto balancing mobs and
// scripts that enable buffs on mobs randomly

View File

@@ -1,7 +1,6 @@
#include "CharacterDatabase.h"
#include "MpDataStore.h"
#include "MpLogger.h"
#include "Player.h"
// Adds an entry for the group difficult to memory and updats database
void MpDataStore::AddGroupData(Group *group, MpGroupData groupData) {
@@ -20,7 +19,7 @@ void MpDataStore::AddGroupData(Group *group, MpGroupData groupData) {
if (_groupData->contains(guid)) {
_groupData->at(guid) = groupData;
} else {
_groupData->insert({guid, groupData});
_groupData->emplace(guid, groupData);
}
MpLogger::debug("Add Group difficulty for group {} to {}", guid.GetCounter());
@@ -75,7 +74,7 @@ void MpDataStore::RemoveGroupData(Group *group) {
void MpDataStore::AddPlayerData(ObjectGuid guid, MpPlayerData pd) {
MpLogger::debug("AddPlayerData for player {}", guid.GetCounter());
_playerData->insert({guid, pd});
_playerData->emplace(guid, pd);
}
void MpDataStore::RemovePlayerData(ObjectGuid guid) {
@@ -84,7 +83,7 @@ void MpDataStore::RemovePlayerData(ObjectGuid guid) {
}
void MpDataStore::AddInstanceData(uint32 mapId, uint32 instanceId, MpInstanceData instanceSettings) {
_instanceData->insert({GetInstanceDataKey(mapId, instanceId), instanceSettings});
_instanceData->emplace(GetInstanceDataKey(mapId, instanceId), instanceSettings);
}
MpInstanceData* MpDataStore::GetInstanceData(uint32 mapId, uint32 instanceId) {
@@ -102,7 +101,7 @@ void MpDataStore::RemoveInstanceData(uint32 mapId, uint32 instanceId) {
void MpDataStore::AddCreatureData(ObjectGuid guid, MpCreatureData creatureData) {
MpLogger::debug("AddInstanceCreatureData for creature {}", guid.GetCounter());
_instanceCreatureData->insert({guid, creatureData});
_instanceCreatureData->emplace(guid, creatureData);
}
MpCreatureData* MpDataStore::GetCreatureData(ObjectGuid guid) {
@@ -129,3 +128,61 @@ void MpDataStore::RemoveCreatureData(ObjectGuid guid) {
MpLogger::debug("RemoveInstanceCreatureData data for creature {}", guid.GetCounter());
_instanceCreatureData->erase(guid);
}
MpScaleFactor MpDataStore::GetScaleFactor(int32 mapId, int32 difficulty) const {
auto key = GetScaleFactorKey(mapId, difficulty);
if (_scaleFactors->contains(key)) {
return _scaleFactors->at(key);
}
return MpScaleFactor{
.dmgBonus = 3,
.healthBonus = 2,
.maxDamageBonus = 30
};
}
int32 MpDataStore::GetHealthScaleFactor(int32 mapId, int32 difficulty) const {
return GetScaleFactor(mapId, difficulty).healthBonus;
}
int32 MpDataStore::GetDamageScaleFactor(int32 mapId, int32 difficulty) const {
return GetScaleFactor(mapId, difficulty).dmgBonus;
}
int32 MpDataStore::GetMaxDamageScaleFactor(int32 mapId, int32 difficulty) const {
return GetScaleFactor(mapId, difficulty).maxDamageBonus;
}
int32 MpDataStore::LoadScaleFactors() {
// 0 1 2 3 4
QueryResult result = WorldDatabase.Query("SELECT mapId, dmg_bonus, hp_bonus, difficulty, max FROM mythic_plus_scale_factors");
if (!result) {
MpLogger::error("Failed to load mythic scale factors from database");
return 0;
}
do {
Field* fields = result->Fetch();
uint32 mapId = fields[0].Get<uint32>();
int32 damageBonus = fields[1].Get<int32>();
int32 healthBonus = fields[2].Get<int32>();
int32 difficulty = fields[3].Get<int32>();
int32 maxDamageBonus = fields[4].Get<int32>();
MpScaleFactor scaleFactor = {
.dmgBonus = damageBonus,
.healthBonus = healthBonus,
.maxDamageBonus = maxDamageBonus
};
_mutableScaleFactors->emplace(GetScaleFactorKey(mapId, difficulty), scaleFactor);
} while (result->NextRow());
// move to const map one loaded so can not be changed after
_scaleFactors = std::move(_mutableScaleFactors);
return int32(_scaleFactors->size());
}

View File

@@ -1,9 +1,17 @@
#ifndef MYTHICPLUS_DATASTORE_H
#define MYTHICPLUS_DATASTORE_H
#include "Creature.h"
#include "Group.h"
#include "MapMgr.h"
#include "Player.h"
#include "ObjectGuid.h"
#include <unordered_map>
#include <map>
#include <string>
#include <vector>
#include <memory>
enum MpDifficulty {
MP_DIFFICULTY_NORMAL = 0,
@@ -20,7 +28,11 @@ struct MpGroupData
uint32 deaths;
std::vector<std::pair<uint32,uint32>> instanceDataKeys;
MpGroupData() : group(nullptr), deaths(0) {
MpGroupData() : group(nullptr), difficulty(MpDifficulty{}), deaths(0) {
instanceDataKeys.reserve(32);
}
MpGroupData(Group* group, MpDifficulty difficulty, uint32 deaths)
: group(group), difficulty(difficulty), deaths(deaths) {
instanceDataKeys.reserve(32);
}
@@ -41,8 +53,16 @@ struct MpPlayerData
struct MpScaleFactor
{
int32 mapId;
int8 difficulty;
int32 dmgBonus;
int32 healthBonus;
int32 maxDamageBonus;
std::string ToString() const {
return "MpScaleFactor: { dmgBonus: " + std::to_string(dmgBonus) +
", healthBonus: " + std::to_string(healthBonus) +
", maxDamageBonus: " + std::to_string(maxDamageBonus) + " }";
}
};
struct MpMultipliers
@@ -119,6 +139,9 @@ struct MpCreatureData
creature->GetCreatureTemplate()->unit_class
);
}
auras.reserve(3);
affixes.reserve(3);
}
void SetScaled(bool scaled) {
@@ -138,27 +161,32 @@ private:
: _playerData(std::make_unique<std::unordered_map<ObjectGuid, MpPlayerData>>()),
_instanceData(std::make_unique<std::map<std::pair<uint32, uint32>, MpInstanceData>>()),
_groupData(std::make_unique<std::unordered_map<ObjectGuid, MpGroupData>>()),
_instanceCreatureData(std::make_unique<std::unordered_map<ObjectGuid, MpCreatureData>>()) {
_instanceCreatureData(std::make_unique<std::unordered_map<ObjectGuid, MpCreatureData>>()),
_mutableScaleFactors(std::make_unique<std::map<std::pair<int32, int32>,MpScaleFactor>>())
{
_playerData->reserve(32);
_groupData->reserve(32);
_instanceCreatureData->reserve(500);
};
inline ~MpDataStore() {
}
inline ~MpDataStore() {}
std::unique_ptr<std::unordered_map<ObjectGuid, MpPlayerData>> _playerData;
// Instance Data map key is unique instance pair and values are modifiers of instance
std::unique_ptr<std::map<std::pair<uint32,uint32>,MpInstanceData>> _instanceData;
// Instance data containes information about how to scale creatures
std::unique_ptr<std::map<std::pair<uint32,uint32>,MpInstanceData>> _instanceData; // {mapId,instanceId}
// Group Data map key is group guid and values are mythic settings set by group leader
// Group data stored current group difficulty setting, and stats of group
std::unique_ptr<std::unordered_map<ObjectGuid, MpGroupData>> _groupData;
// Instance Creature Data map key is creature guid and values are creature itself from a mythic instance
// This is all creatures that have been scaled used for determining what has been scaled
std::unique_ptr<std::unordered_map<ObjectGuid, MpCreatureData>> _instanceCreatureData;
// use to mimic pattern normals scale to heroic (loaded at server start)
std::unique_ptr<std::map<std::pair<int32,int32>,MpScaleFactor>> _mutableScaleFactors; // {mapId,difficulty}
std::unique_ptr<const std::map<std::pair<int32,int32>,MpScaleFactor>> _scaleFactors; // {mapId,difficulty}
public:
// ensure we only ever have one instance of this class
@@ -186,7 +214,7 @@ public:
return GetGroupData(player->GetGroup()->GetGUID());
};
// Set and remove difficluty settig for a group
// Set and remove settigs for a group options (difficulty, deaths, stats, etc)
void AddGroupData(Group *group, MpGroupData groupData);
void RemoveGroupData(Group *group);
MpGroupData* GetGroupData(Group *group);
@@ -206,15 +234,22 @@ public:
void RemoveCreatureData(ObjectGuid guid);
std::vector<MpCreatureData*> GetUnscaledCreatures(uint32 mapId, uint32 instanceId);
// creates a unique instance key into the instance data store
auto GetInstanceDataKey(uint32 mapId, uint32 instanceId) {
// Scale factors are used to determine a base bonus for enemies base on the instance difficulty
int32 GetHealthScaleFactor(int32 mapId, int32 difficulty) const;
int32 GetDamageScaleFactor(int32 mapId, int32 difficulty) const;
int32 GetMaxDamageScaleFactor(int32 mapId, int32 difficulty) const;
MpScaleFactor GetScaleFactor(int32 mapId, int32 difficulty) const;
auto GetInstanceDataKey(uint32 mapId, uint32 instanceId) const {
return std::make_pair(mapId, instanceId);
}
auto GetScaleFactorKey(int32 mapId, int32 difficulty) const {
return std::make_pair(mapId, difficulty);
}
// loads scaling factors from the database
void LoadScaleFactors();
// Used at initial server load
int32 LoadScaleFactors();
// accessor for this singleton
static MpDataStore* instance() {
static MpDataStore instance;
return &instance;

View File

@@ -1,15 +1,12 @@
#include "MythicPlus.h"
#include "MpDataStore.h"
#include "MpLogger.h"
#include "ObjectMgr.h"
#include "MapMgr.h"
#include "ScriptMgr.h"
#include "Player.h"
#include "Group.h"
#include "Map.h"
#include "Unit.h"
#include "Creature.h"
#include "SpellInfo.h"
#include <algorithm>
#include <cmath>
bool MythicPlus::IsMapEligible(Map* map)
{
@@ -166,9 +163,9 @@ void MythicPlus::AddScaledCreature(Creature* creature, MpInstanceData* instanceD
// allow small variance in level for non-boss creatures
uint8 level = uint8(urand(instanceData->creature.avgLevel - 1, instanceData->creature.avgLevel + 1));
if(creature->IsDungeonBoss()) {
ScaleCreature(instanceData->boss.avgLevel, creature, &instanceData->boss);
ScaleCreature(instanceData->boss.avgLevel, creature, &instanceData->boss, instanceData->difficulty);
} else {
ScaleCreature(level, creature, &instanceData->creature);
ScaleCreature(level, creature, &instanceData->creature, instanceData->difficulty);
}
MpCreatureData creatureData = MpCreatureData(creature);
@@ -203,55 +200,39 @@ void MythicPlus::RemoveCreature(Creature* creature)
sMpDataStore->RemoveCreatureData(creature->GetGUID());
}
void MythicPlus::ScaleCreature(uint8 level, Creature* creature, MpMultipliers* multipliers)
void MythicPlus::ScaleCreature(uint8 level, Creature* creature, MpMultipliers* multipliers, MpDifficulty difficulty)
{
uint32 origLevel = creature->GetLevel();
CreatureTemplate const* cInfo = creature->GetCreatureTemplate();
uint32 mapId = creature->GetMapId();
creature->SetLevel(level);
CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(
level,
creature->GetCreatureTemplate()->unit_class
cInfo->unit_class
);
int32 rank = 0;
CreatureTemplate const* cInfo = creature->GetCreatureTemplate();
if(cInfo && cInfo->rank > 0) {
rank = cInfo->rank;
}
// Scales the creatures hitpoints
float healthmod = GetTypeHealthModifier(rank);
// Add some variance to the healthpool so enemies are not all the same
float healthVariation = frand(0.85f, 1.15f);
uint32 basehp = uint32(std::ceil(stats->BaseHealth[EXPANSION_WRATH_OF_THE_LICH_KING] * cInfo->ModHealth));
uint32 health = uint32(basehp * healthmod * multipliers->health * healthVariation);
uint32 basehp = stats->BaseHealth[EXPANSION_WRATH_OF_THE_LICH_KING];
uint32 health = CalculateNewHealth(cInfo, mapId, difficulty, basehp, multipliers->health);
creature->SetCreateHealth(health);
creature->SetMaxHealth(health);
creature->SetHealth(health);
creature->ResetPlayerDamageReq();
// Scales the creatures mana
uint32 mana = uint32(std::ceil(stats->BaseMana * cInfo->ModMana * multipliers->health));
/**
* @TODO: Figure out mana later for unit_types 2 and 8
*/
uint32 mana = uint32(std::ceil(stats->BaseMana * cInfo->ModMana));
creature->SetCreateMana(mana);
creature->SetMaxPower(POWER_MANA, mana);
creature->SetPower(POWER_MANA, mana);
creature->SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, (float)health);
creature->SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, (float)mana);
// Normalize the damage from earlier expansions
float cTemplateDmgMult = cInfo->DamageModifier;
if (origLevel <= 60 && cTemplateDmgMult < 3.0f) {
cTemplateDmgMult = 4.0f;
}
if (origLevel <= 70 && origLevel > 60 && cTemplateDmgMult < 4.0f) {
cTemplateDmgMult = 4.0f;
}
float basedamage = uint32(std::ceil(stats->BaseDamage[EXPANSION_WRATH_OF_THE_LICH_KING]));
float creatureTypeMult = GetTypeDamageModifier(rank);
float weaponBaseMinDamage = basedamage * cTemplateDmgMult * creatureTypeMult * multipliers->baseDamage;
float basedamage = stats->BaseDamage[EXPANSION_WRATH_OF_THE_LICH_KING];
float weaponBaseMinDamage = CalculateNewBaseDamage(cInfo, mapId, difficulty, basedamage);
float weaponBaseMaxDamage = weaponBaseMinDamage * 1.5f;
creature->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, weaponBaseMinDamage);
@@ -261,9 +242,6 @@ void MythicPlus::ScaleCreature(uint8 level, Creature* creature, MpMultipliers* m
creature->SetBaseWeaponDamage(RANGED_ATTACK, MINDAMAGE, weaponBaseMinDamage);
creature->SetBaseWeaponDamage(RANGED_ATTACK, MAXDAMAGE, weaponBaseMaxDamage);
creature->ApplyAttackTimePercentMod(BASE_ATTACK, 0.75, true);
creature->ApplyAttackTimePercentMod(OFF_ATTACK, 0.95, true);
creature->ApplyCastTimePercentMod(0.80, true);
creature->UpdateAllStats();
// Scale up the armor with some variance also to make some tougher enemies in the mix
@@ -271,7 +249,7 @@ void MythicPlus::ScaleCreature(uint8 level, Creature* creature, MpMultipliers* m
creature->SetArmor(armor);
/**
* @TODO Explore scaling other variable stats based on the creature type at a later date.
* @TODO: Explore scaling other variable stats and resistances on the creature type at a later date.
*/
// creature->SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, stats->AttackPower * multipliers->melee);
// creature->SetModifierValue(UNIT_MOD_ATTACK_POWER_RANGED, BASE_VALUE, stats->RangedAttackPower * multipliers->melee);
@@ -295,7 +273,6 @@ int32 MythicPlus::ScaleDamageSpell(SpellInfo const * spellInfo, MpCreatureData*
}
int32 originalLevel = creatureData->originalLevel;
int32 currentLevel = creature->GetLevel();
int32 totalDamage = 0;
// Calculate the scaling factor using the 1.8 exponent
@@ -377,6 +354,46 @@ float GetTypeHealthModifier(int32 Rank)
}
}
// This takes the orignal health and scales flat based on the factor then applies the configuration modifier from the conf file
uint32 CalculateNewHealth(CreatureTemplate const* cInfo, uint32 mapId, MpDifficulty difficulty, uint32 origHealth, float confHPMod)
{
int32 rank = 0;
if(cInfo && cInfo->rank > 0) {
rank = cInfo->rank;
}
// Add some variance to the healthpool so enemies are not all the same
float healthVariation = frand(0.85f, 1.15f);
float unitTypeMod = GetTypeHealthModifier(rank);
uint32 basehp = uint32(std::ceil(origHealth * unitTypeMod * healthVariation));
int32 hpScaleFactor = sMpDataStore->GetHealthScaleFactor(mapId, difficulty);
if(cInfo->ModHealth > 0.0f) {
return uint32(basehp * (cInfo->ModHealth + hpScaleFactor) * confHPMod);
} else {
return uint32(basehp * (hpScaleFactor) * confHPMod);
}
}
float CalculateNewBaseDamage(CreatureTemplate const* cInfo, uint32 mapId, MpDifficulty difficulty, float origDamage)
{
int32 rank = 0;
if(cInfo && cInfo->rank > 0) {
rank = cInfo->rank;
}
float unitTypeMod = GetTypeDamageModifier(rank);
float baseDamage = origDamage * unitTypeMod;
int32 dmgScaleFactor = sMpDataStore->GetDamageScaleFactor(mapId, difficulty);
if(cInfo->DamageModifier > 0.0f) {
return baseDamage * (cInfo->DamageModifier + dmgScaleFactor);
} else {
return baseDamage * (dmgScaleFactor);
}
}
float GetTypeDamageModifier(int32 Rank)
{
switch (Rank)
@@ -395,5 +412,3 @@ float GetTypeDamageModifier(int32 Rank)
return sWorld->getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE);
}
}
float NormalizeDamageMap

View File

@@ -1,11 +1,18 @@
#ifndef MYTHICPLUS_H
#define MYTHICPLUS_H
#include "Creature.h"
#include "Define.h"
#include "Map.h"
#include "MpDataStore.h"
#include "Player.h"
#include "SpellInfo.h"
#include "Unit.h"
#include <map>
#include <string>
#include <vector>
#include <unordered_map>
class MythicPlus
{
@@ -97,7 +104,7 @@ public:
void AddScaledCreature(Creature* creature, MpInstanceData* instanceData);
// Scales the creature based on the level and the creature base stats
void ScaleCreature(uint8 level, Creature* creature, MpMultipliers* multipliers);
void ScaleCreature(uint8 level, Creature* creature, MpMultipliers* multipliers, MpDifficulty difficulty);
// Scales a damage spell up based on the level increase
int32 ScaleDamageSpell(SpellInfo const * spellInfo, MpCreatureData* creatureData, Creature* creature, float damageMultiplier);
@@ -110,8 +117,9 @@ public:
~MythicPlus() { }
};
float GetHealthModifier(int32 rank);
float GetDamageModifier(int32 rank);
float GetTypeHealthModifier(int32 rank);
float GetTypeDamageModifier(int32 rank);
#define sMythicPlus MythicPlus::instance()

View File

@@ -1,6 +1,4 @@
#include "Log.h"
void Addmod_mythic_plusScripts();
void Add_MP_AllCreatureScripts();
void Add_MP_AllMapScripts();

View File

@@ -87,6 +87,13 @@ public:
sMythicPlus->legendaryItemOffset = sConfigMgr->GetOption<uint32>("MythicPlus.Legendary.ItemOffset", 21000000);
sMythicPlus->ascendantItemOffset = sConfigMgr->GetOption<uint32>("MythicPlus.Ascendant.ItemOffset", 22000000);
}
void OnStartup() override
{
int32 size = sMpDataStore->LoadScaleFactors();
MpLogger::info("Loaded {} Mythic+ Scaling Factors from database...", size);
}
};
void Add_MP_WorldScripts()