mirror of
https://github.com/araxiaonline/mod-autobalance.git
synced 2026-06-13 03:02:26 -04:00
3369 lines
179 KiB
C++
Executable File
3369 lines
179 KiB
C++
Executable File
/*
|
|
* Copyright (C) 2018 AzerothCore <http://www.azerothcore.org>
|
|
* Copyright (C) 2012 CVMagic <http://www.trinitycore.org/f/topic/6551-vas-autobalance/>
|
|
* Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/>
|
|
* Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
|
|
* Copyright (C) 1985-2010 KalCorp <http://vasserver.dyndns.org/>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* Script Name: AutoBalance
|
|
* Original Authors: KalCorp and Vaughner
|
|
* Maintainer(s): AzerothCore
|
|
* Original Script Name: AutoBalance
|
|
* Description: This script is intended to scale based on number of players,
|
|
* instance mobs & world bosses' level, health, mana, and damage.
|
|
*/
|
|
|
|
#include "Configuration/Config.h"
|
|
#include "Unit.h"
|
|
#include "Chat.h"
|
|
#include "Creature.h"
|
|
#include "Player.h"
|
|
#include "ObjectMgr.h"
|
|
#include "MapMgr.h"
|
|
#include "World.h"
|
|
#include "Map.h"
|
|
#include "ScriptMgr.h"
|
|
#include "Language.h"
|
|
#include <vector>
|
|
#include "AutoBalance.h"
|
|
#include "ScriptMgrMacros.h"
|
|
#include "Group.h"
|
|
#include "Log.h"
|
|
#include <chrono>
|
|
|
|
#if AC_COMPILER == AC_COMPILER_GNU
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
#endif
|
|
|
|
using namespace Acore::ChatCommands;
|
|
|
|
ABScriptMgr* ABScriptMgr::instance()
|
|
{
|
|
static ABScriptMgr instance;
|
|
return &instance;
|
|
}
|
|
|
|
bool ABScriptMgr::OnBeforeModifyAttributes(Creature *creature, uint32 & instancePlayerCount)
|
|
{
|
|
auto ret = IsValidBoolScript<ABModuleScript>([&](ABModuleScript* script)
|
|
{
|
|
return !script->OnBeforeModifyAttributes(creature, instancePlayerCount);
|
|
});
|
|
|
|
if (ret && *ret)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ABScriptMgr::OnAfterDefaultMultiplier(Creature *creature, float& defaultMultiplier)
|
|
{
|
|
auto ret = IsValidBoolScript<ABModuleScript>([&](ABModuleScript* script)
|
|
{
|
|
return !script->OnAfterDefaultMultiplier(creature, defaultMultiplier);
|
|
});
|
|
|
|
if (ret && *ret)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ABScriptMgr::OnBeforeUpdateStats(Creature* creature, uint32& scaledHealth, uint32& scaledMana, float& damageMultiplier, uint32& newBaseArmor)
|
|
{
|
|
auto ret = IsValidBoolScript<ABModuleScript>([&](ABModuleScript* script)
|
|
{
|
|
return !script->OnBeforeUpdateStats(creature, scaledHealth, scaledMana, damageMultiplier, newBaseArmor);
|
|
});
|
|
|
|
if (ret && *ret)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
ABModuleScript::ABModuleScript(const char* name)
|
|
: ModuleScript(name)
|
|
{
|
|
ScriptRegistry<ABModuleScript>::AddScript(this);
|
|
}
|
|
|
|
|
|
class AutoBalanceCreatureInfo : public DataMap::Base
|
|
{
|
|
public:
|
|
AutoBalanceCreatureInfo() {}
|
|
|
|
uint64_t configTime;
|
|
|
|
uint32 instancePlayerCount = 0;
|
|
uint8 selectedLevel = 0;
|
|
// this is used to detect creatures that update their entry
|
|
uint32 entry = 0;
|
|
float DamageMultiplier = 1.0f;
|
|
float HealthMultiplier = 1.0f;
|
|
float ManaMultiplier = 1.0f;
|
|
float ArmorMultiplier = 1.0f;
|
|
float CCDurationMultiplier = 1.0f;
|
|
|
|
float XPModifier = 1.0f;
|
|
float MoneyModifier = 1.0f;
|
|
|
|
uint8 UnmodifiedLevel = 0;
|
|
|
|
bool isActive = false;
|
|
bool wasAliveNowDead = false;
|
|
bool isInCreatureList = false;
|
|
};
|
|
|
|
class AutoBalanceMapInfo : public DataMap::Base
|
|
{
|
|
public:
|
|
AutoBalanceMapInfo() {}
|
|
|
|
uint64_t configTime;
|
|
|
|
uint32 playerCount = 0;
|
|
|
|
uint8 mapLevel = 0;
|
|
uint8 lowestPlayerLevel = 0;
|
|
uint8 highestPlayerLevel = 0;
|
|
|
|
uint8 lfgMinLevel = 0;
|
|
uint8 lfgTargetLevel = 80;
|
|
uint8 lfgMaxLevel = 80;
|
|
|
|
bool enabled = false;
|
|
|
|
std::vector<Creature*> allMapCreatures;
|
|
uint8 highestCreatureLevel = 0;
|
|
uint8 lowestCreatureLevel = 0;
|
|
float avgCreatureLevel;
|
|
uint32 activeCreatureCount = 0;
|
|
|
|
bool isLevelScalingEnabled;
|
|
int levelScalingSkipHigherLevels, levelScalingSkipLowerLevels;
|
|
int levelScalingDynamicCeiling, levelScalingDynamicFloor;
|
|
};
|
|
|
|
class AutoBalanceStatModifiers : public DataMap::Base
|
|
{
|
|
public:
|
|
AutoBalanceStatModifiers() {}
|
|
AutoBalanceStatModifiers(float global, float health, float mana, float armor, float damage, float ccduration) :
|
|
global(global), health(health), mana(mana), armor(armor), damage(damage), ccduration(ccduration) {}
|
|
float global;
|
|
float health;
|
|
float mana;
|
|
float armor;
|
|
float damage;
|
|
float ccduration;
|
|
|
|
std::time_t configTime;
|
|
};
|
|
|
|
class AutoBalanceInflectionPointSettings : public DataMap::Base
|
|
{
|
|
public:
|
|
AutoBalanceInflectionPointSettings() {}
|
|
AutoBalanceInflectionPointSettings(float value, float curveFloor, float curveCeiling) :
|
|
value(value), curveFloor(curveFloor), curveCeiling(curveCeiling) {}
|
|
float value;
|
|
float curveFloor;
|
|
float curveCeiling;
|
|
};
|
|
|
|
class AutoBalanceLevelScalingDynamicLevelSettings: public DataMap::Base
|
|
{
|
|
public:
|
|
AutoBalanceLevelScalingDynamicLevelSettings() {}
|
|
AutoBalanceLevelScalingDynamicLevelSettings(int skipHigher, int skipLower, int ceiling, int floor) :
|
|
skipHigher(skipHigher), skipLower(skipLower), ceiling(ceiling), floor(floor) {}
|
|
int skipHigher;
|
|
int skipLower;
|
|
int ceiling;
|
|
int floor;
|
|
};
|
|
|
|
enum ScalingMethod {
|
|
AUTOBALANCE_SCALING_FIXED,
|
|
AUTOBALANCE_SCALING_DYNAMIC
|
|
};
|
|
|
|
// The map values correspond with the .AutoBalance.XX.Name entries in the configuration file.
|
|
static std::map<int, int> forcedCreatureIds;
|
|
static std::map<uint32, uint8> enabledDungeonIds;
|
|
static std::map<uint32, AutoBalanceInflectionPointSettings> dungeonOverrides;
|
|
static std::map<uint32, AutoBalanceInflectionPointSettings> bossOverrides;
|
|
static std::map<uint32, AutoBalanceStatModifiers> statModifierOverrides;
|
|
static std::map<uint32, AutoBalanceStatModifiers> statModifierBossOverrides;
|
|
static std::map<uint32, AutoBalanceStatModifiers> statModifierCreatureOverrides;
|
|
static std::map<uint8, AutoBalanceLevelScalingDynamicLevelSettings> levelScalingDynamicLevelOverrides;
|
|
static std::map<uint32, uint32> levelScalingDistanceCheckOverrides;
|
|
// cheaphack for difficulty server-wide.
|
|
// Another value TODO in player class for the party leader's value to determine dungeon difficulty.
|
|
static int8 PlayerCountDifficultyOffset;
|
|
static bool LevelScaling;
|
|
static int8 LevelScalingSkipHigherLevels, LevelScalingSkipLowerLevels;
|
|
static int8 LevelScalingDynamicLevelCeilingDungeons, LevelScalingDynamicLevelFloorDungeons, LevelScalingDynamicLevelCeilingRaids, LevelScalingDynamicLevelFloorRaids;
|
|
static int8 LevelScalingDynamicLevelCeilingHeroicDungeons, LevelScalingDynamicLevelFloorHeroicDungeons, LevelScalingDynamicLevelCeilingHeroicRaids, LevelScalingDynamicLevelFloorHeroicRaids;
|
|
static ScalingMethod LevelScalingMethod;
|
|
static uint32 rewardRaid, rewardDungeon, MinPlayerReward;
|
|
static bool Announcement;
|
|
static bool LevelScalingEndGameBoost, PlayerChangeNotify, rewardEnabled;
|
|
static float MinHPModifier, MinManaModifier, MinDamageModifier, MinCCDurationModifier, MaxCCDurationModifier;
|
|
|
|
// RewardScaling.*
|
|
static ScalingMethod RewardScalingMethod;
|
|
static bool RewardScalingXP, RewardScalingMoney;
|
|
static float RewardScalingXPModifier, RewardScalingMoneyModifier;
|
|
|
|
// Track the last time the config was reloaded
|
|
static uint64_t lastConfigTime = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
|
|
// Enable.*
|
|
static bool EnableGlobal;
|
|
static bool Enable5M, Enable10M, Enable15M, Enable20M, Enable25M, Enable40M;
|
|
static bool Enable5MHeroic, Enable10MHeroic, Enable25MHeroic;
|
|
static bool EnableOtherNormal, EnableOtherHeroic;
|
|
|
|
// InflectionPoint*
|
|
static float InflectionPoint, InflectionPointCurveFloor, InflectionPointCurveCeiling, InflectionPointBoss;
|
|
static float InflectionPointHeroic, InflectionPointHeroicCurveFloor, InflectionPointHeroicCurveCeiling, InflectionPointHeroicBoss;
|
|
static float InflectionPointRaid, InflectionPointRaidCurveFloor, InflectionPointRaidCurveCeiling, InflectionPointRaidBoss;
|
|
static float InflectionPointRaidHeroic, InflectionPointRaidHeroicCurveFloor, InflectionPointRaidHeroicCurveCeiling, InflectionPointRaidHeroicBoss;
|
|
|
|
static float InflectionPointRaid10M, InflectionPointRaid10MCurveFloor, InflectionPointRaid10MCurveCeiling, InflectionPointRaid10MBoss;
|
|
static float InflectionPointRaid10MHeroic, InflectionPointRaid10MHeroicCurveFloor, InflectionPointRaid10MHeroicCurveCeiling, InflectionPointRaid10MHeroicBoss;
|
|
static float InflectionPointRaid15M, InflectionPointRaid15MCurveFloor, InflectionPointRaid15MCurveCeiling, InflectionPointRaid15MBoss;
|
|
static float InflectionPointRaid20M, InflectionPointRaid20MCurveFloor, InflectionPointRaid20MCurveCeiling, InflectionPointRaid20MBoss;
|
|
static float InflectionPointRaid25M, InflectionPointRaid25MCurveFloor, InflectionPointRaid25MCurveCeiling, InflectionPointRaid25MBoss;
|
|
static float InflectionPointRaid25MHeroic, InflectionPointRaid25MHeroicCurveFloor, InflectionPointRaid25MHeroicCurveCeiling, InflectionPointRaid25MHeroicBoss;
|
|
static float InflectionPointRaid40M, InflectionPointRaid40MCurveFloor, InflectionPointRaid40MCurveCeiling, InflectionPointRaid40MBoss;
|
|
|
|
// StatModifier*
|
|
static float StatModifier_Global, StatModifier_Health, StatModifier_Mana, StatModifier_Armor, StatModifier_Damage, StatModifier_CCDuration;
|
|
static float StatModifierHeroic_Global, StatModifierHeroic_Health, StatModifierHeroic_Mana, StatModifierHeroic_Armor, StatModifierHeroic_Damage, StatModifierHeroic_CCDuration;
|
|
static float StatModifierRaid_Global, StatModifierRaid_Health, StatModifierRaid_Mana, StatModifierRaid_Armor, StatModifierRaid_Damage, StatModifierRaid_CCDuration;
|
|
static float StatModifierRaidHeroic_Global, StatModifierRaidHeroic_Health, StatModifierRaidHeroic_Mana, StatModifierRaidHeroic_Armor, StatModifierRaidHeroic_Damage, StatModifierRaidHeroic_CCDuration;
|
|
|
|
static float StatModifierRaid10M_Global, StatModifierRaid10M_Health, StatModifierRaid10M_Mana, StatModifierRaid10M_Armor, StatModifierRaid10M_Damage, StatModifierRaid10M_CCDuration;
|
|
static float StatModifierRaid10MHeroic_Global, StatModifierRaid10MHeroic_Health, StatModifierRaid10MHeroic_Mana, StatModifierRaid10MHeroic_Armor, StatModifierRaid10MHeroic_Damage, StatModifierRaid10MHeroic_CCDuration;
|
|
static float StatModifierRaid15M_Global, StatModifierRaid15M_Health, StatModifierRaid15M_Mana, StatModifierRaid15M_Armor, StatModifierRaid15M_Damage, StatModifierRaid15M_CCDuration;
|
|
static float StatModifierRaid20M_Global, StatModifierRaid20M_Health, StatModifierRaid20M_Mana, StatModifierRaid20M_Armor, StatModifierRaid20M_Damage, StatModifierRaid20M_CCDuration;
|
|
static float StatModifierRaid25M_Global, StatModifierRaid25M_Health, StatModifierRaid25M_Mana, StatModifierRaid25M_Armor, StatModifierRaid25M_Damage, StatModifierRaid25M_CCDuration;
|
|
static float StatModifierRaid25MHeroic_Global, StatModifierRaid25MHeroic_Health, StatModifierRaid25MHeroic_Mana, StatModifierRaid25MHeroic_Armor, StatModifierRaid25MHeroic_Damage, StatModifierRaid25MHeroic_CCDuration;
|
|
static float StatModifierRaid40M_Global, StatModifierRaid40M_Health, StatModifierRaid40M_Mana, StatModifierRaid40M_Armor, StatModifierRaid40M_Damage, StatModifierRaid40M_CCDuration;
|
|
|
|
// StatModifier* (Boss)
|
|
static float StatModifier_Boss_Global, StatModifier_Boss_Health, StatModifier_Boss_Mana, StatModifier_Boss_Armor, StatModifier_Boss_Damage, StatModifier_Boss_CCDuration;
|
|
static float StatModifierHeroic_Boss_Global, StatModifierHeroic_Boss_Health, StatModifierHeroic_Boss_Mana, StatModifierHeroic_Boss_Armor, StatModifierHeroic_Boss_Damage, StatModifierHeroic_Boss_CCDuration;
|
|
static float StatModifierRaid_Boss_Global, StatModifierRaid_Boss_Health, StatModifierRaid_Boss_Mana, StatModifierRaid_Boss_Armor, StatModifierRaid_Boss_Damage, StatModifierRaid_Boss_CCDuration;
|
|
static float StatModifierRaidHeroic_Boss_Global, StatModifierRaidHeroic_Boss_Health, StatModifierRaidHeroic_Boss_Mana, StatModifierRaidHeroic_Boss_Armor, StatModifierRaidHeroic_Boss_Damage, StatModifierRaidHeroic_Boss_CCDuration;
|
|
|
|
static float StatModifierRaid10M_Boss_Global, StatModifierRaid10M_Boss_Health, StatModifierRaid10M_Boss_Mana, StatModifierRaid10M_Boss_Armor, StatModifierRaid10M_Boss_Damage, StatModifierRaid10M_Boss_CCDuration;
|
|
static float StatModifierRaid10MHeroic_Boss_Global, StatModifierRaid10MHeroic_Boss_Health, StatModifierRaid10MHeroic_Boss_Mana, StatModifierRaid10MHeroic_Boss_Armor, StatModifierRaid10MHeroic_Boss_Damage, StatModifierRaid10MHeroic_Boss_CCDuration;
|
|
static float StatModifierRaid15M_Boss_Global, StatModifierRaid15M_Boss_Health, StatModifierRaid15M_Boss_Mana, StatModifierRaid15M_Boss_Armor, StatModifierRaid15M_Boss_Damage, StatModifierRaid15M_Boss_CCDuration;
|
|
static float StatModifierRaid20M_Boss_Global, StatModifierRaid20M_Boss_Health, StatModifierRaid20M_Boss_Mana, StatModifierRaid20M_Boss_Armor, StatModifierRaid20M_Boss_Damage, StatModifierRaid20M_Boss_CCDuration;
|
|
static float StatModifierRaid25M_Boss_Global, StatModifierRaid25M_Boss_Health, StatModifierRaid25M_Boss_Mana, StatModifierRaid25M_Boss_Armor, StatModifierRaid25M_Boss_Damage, StatModifierRaid25M_Boss_CCDuration;
|
|
static float StatModifierRaid25MHeroic_Boss_Global, StatModifierRaid25MHeroic_Boss_Health, StatModifierRaid25MHeroic_Boss_Mana, StatModifierRaid25MHeroic_Boss_Armor, StatModifierRaid25MHeroic_Boss_Damage, StatModifierRaid25MHeroic_Boss_CCDuration;
|
|
static float StatModifierRaid40M_Boss_Global, StatModifierRaid40M_Boss_Health, StatModifierRaid40M_Boss_Mana, StatModifierRaid40M_Boss_Armor, StatModifierRaid40M_Boss_Damage, StatModifierRaid40M_Boss_CCDuration;
|
|
|
|
void LoadEnabledDungeons(std::string dungeonIdString) // Used for reading the string from the configuration file for selecting dungeons to scale
|
|
{
|
|
std::string delimitedValue;
|
|
std::stringstream dungeonIdStream;
|
|
|
|
dungeonIdStream.str(dungeonIdString);
|
|
while (std::getline(dungeonIdStream, delimitedValue, ',')) // Process each dungeon ID in the string, delimited by the comma - "," and then space " "
|
|
{
|
|
std::string pairOne, pairTwo;
|
|
std::stringstream dungeonPairStream(delimitedValue);
|
|
dungeonPairStream>>pairOne>>pairTwo;
|
|
auto dungeonMapId = atoi(pairOne.c_str());
|
|
auto minPlayers = atoi(pairTwo.c_str());
|
|
enabledDungeonIds[dungeonMapId] = minPlayers;
|
|
}
|
|
}
|
|
|
|
std::map<uint32, AutoBalanceInflectionPointSettings> LoadInflectionPointOverrides(std::string dungeonIdString) // Used for reading the string from the configuration file for selecting dungeons to override
|
|
{
|
|
std::string delimitedValue;
|
|
std::stringstream dungeonIdStream;
|
|
std::map<uint32, AutoBalanceInflectionPointSettings> overrideMap;
|
|
|
|
dungeonIdStream.str(dungeonIdString);
|
|
while (std::getline(dungeonIdStream, delimitedValue, ',')) // Process each dungeon ID in the string, delimited by the comma - "," and then space " "
|
|
{
|
|
std::string val1, val2, val3, val4;
|
|
std::stringstream dungeonPairStream(delimitedValue);
|
|
dungeonPairStream >> val1 >> val2 >> val3 >> val4;
|
|
|
|
auto dungeonMapId = atoi(val1.c_str());
|
|
|
|
// Replace any missing values with -1
|
|
if (val2.empty()) { val2 = "-1"; }
|
|
if (val3.empty()) { val3 = "-1"; }
|
|
if (val4.empty()) { val4 = "-1"; }
|
|
|
|
AutoBalanceInflectionPointSettings ipSettings = AutoBalanceInflectionPointSettings(
|
|
atof(val2.c_str()),
|
|
atof(val3.c_str()),
|
|
atof(val4.c_str())
|
|
);
|
|
|
|
overrideMap[dungeonMapId] = ipSettings;
|
|
}
|
|
|
|
return overrideMap;
|
|
}
|
|
|
|
std::map<uint32, AutoBalanceStatModifiers> LoadStatModifierOverrides(std::string dungeonIdString) // Used for reading the string from the configuration file for per-dungeon stat modifiers
|
|
{
|
|
std::string delimitedValue;
|
|
std::stringstream dungeonIdStream;
|
|
std::map<uint32, AutoBalanceStatModifiers> overrideMap;
|
|
|
|
dungeonIdStream.str(dungeonIdString);
|
|
while (std::getline(dungeonIdStream, delimitedValue, ',')) // Process each dungeon ID in the string, delimited by the comma - "," and then space " "
|
|
{
|
|
std::string val1, val2, val3, val4, val5, val6, val7;
|
|
std::stringstream dungeonStream(delimitedValue);
|
|
dungeonStream >> val1 >> val2 >> val3 >> val4 >> val5 >> val6 >> val7;
|
|
|
|
auto dungeonMapId = atoi(val1.c_str());
|
|
|
|
// Replace any missing values with -1
|
|
if (val2.empty()) { val2 = "-1"; }
|
|
if (val3.empty()) { val3 = "-1"; }
|
|
if (val4.empty()) { val4 = "-1"; }
|
|
if (val5.empty()) { val5 = "-1"; }
|
|
if (val6.empty()) { val6 = "-1"; }
|
|
if (val7.empty()) { val7 = "-1"; }
|
|
|
|
AutoBalanceStatModifiers statSettings = AutoBalanceStatModifiers(
|
|
atof(val2.c_str()),
|
|
atof(val3.c_str()),
|
|
atof(val4.c_str()),
|
|
atof(val5.c_str()),
|
|
atof(val6.c_str()),
|
|
atof(val7.c_str())
|
|
);
|
|
|
|
overrideMap[dungeonMapId] = statSettings;
|
|
}
|
|
|
|
return overrideMap;
|
|
}
|
|
|
|
std::map<uint8, AutoBalanceLevelScalingDynamicLevelSettings> LoadDynamicLevelOverrides(std::string dungeonIdString) // Used for reading the string from the configuration file for per-dungeon dynamic level overrides
|
|
{
|
|
std::string delimitedValue;
|
|
std::stringstream dungeonIdStream;
|
|
std::map<uint8, AutoBalanceLevelScalingDynamicLevelSettings> overrideMap;
|
|
|
|
dungeonIdStream.str(dungeonIdString);
|
|
while (std::getline(dungeonIdStream, delimitedValue, ',')) // Process each dungeon ID in the string, delimited by the comma - "," and then space " "
|
|
{
|
|
std::string val1, val2, val3, val4, val5;
|
|
std::stringstream dungeonStream(delimitedValue);
|
|
dungeonStream >> val1 >> val2 >> val3 >> val4 >> val5;
|
|
|
|
auto dungeonMapId = atoi(val1.c_str());
|
|
|
|
// Replace any missing values with -1
|
|
if (val2.empty()) { val2 = "-1"; }
|
|
if (val3.empty()) { val3 = "-1"; }
|
|
if (val4.empty()) { val3 = "-1"; }
|
|
if (val5.empty()) { val3 = "-1"; }
|
|
|
|
AutoBalanceLevelScalingDynamicLevelSettings dynamicLevelSettings = AutoBalanceLevelScalingDynamicLevelSettings(
|
|
atoi(val2.c_str()),
|
|
atoi(val3.c_str()),
|
|
atoi(val4.c_str()),
|
|
atoi(val5.c_str())
|
|
);
|
|
|
|
overrideMap[dungeonMapId] = dynamicLevelSettings;
|
|
}
|
|
|
|
return overrideMap;
|
|
}
|
|
|
|
std::map<uint32, uint32> LoadDistanceCheckOverrides(std::string dungeonIdString)
|
|
{
|
|
std::string delimitedValue;
|
|
std::stringstream dungeonIdStream;
|
|
std::map<uint32, uint32> overrideMap;
|
|
|
|
dungeonIdStream.str(dungeonIdString);
|
|
while (std::getline(dungeonIdStream, delimitedValue, ',')) // Process each dungeon ID in the string, delimited by the comma - "," and then space " "
|
|
{
|
|
std::string val1, val2;
|
|
std::stringstream dungeonStream(delimitedValue);
|
|
dungeonStream >> val1 >> val2;
|
|
|
|
auto dungeonMapId = atoi(val1.c_str());
|
|
overrideMap[dungeonMapId] = atoi(val2.c_str());
|
|
}
|
|
|
|
return overrideMap;
|
|
}
|
|
|
|
|
|
bool isEnabledDungeon(uint32 dungeonId)
|
|
{
|
|
return (enabledDungeonIds.find(dungeonId) != enabledDungeonIds.end());
|
|
}
|
|
|
|
bool perDungeonScalingEnabled()
|
|
{
|
|
return (!enabledDungeonIds.empty());
|
|
}
|
|
|
|
bool hasDungeonOverride(uint32 dungeonId)
|
|
{
|
|
return (dungeonOverrides.find(dungeonId) != dungeonOverrides.end());
|
|
}
|
|
|
|
bool hasBossOverride(uint32 dungeonId)
|
|
{
|
|
return (bossOverrides.find(dungeonId) != bossOverrides.end());
|
|
}
|
|
|
|
bool hasStatModifierOverride(uint32 dungeonId)
|
|
{
|
|
return (statModifierOverrides.find(dungeonId) != statModifierOverrides.end());
|
|
}
|
|
|
|
bool hasStatModifierBossOverride(uint32 dungeonId)
|
|
{
|
|
return (statModifierBossOverrides.find(dungeonId) != statModifierBossOverrides.end());
|
|
}
|
|
|
|
bool hasStatModifierCreatureOverride(uint32 creatureId)
|
|
{
|
|
return (statModifierCreatureOverrides.find(creatureId) != statModifierCreatureOverrides.end());
|
|
}
|
|
|
|
bool hasDynamicLevelOverride(uint32 dungeonId)
|
|
{
|
|
return (levelScalingDynamicLevelOverrides.find(dungeonId) != levelScalingDynamicLevelOverrides.end());
|
|
}
|
|
|
|
bool hasLevelScalingDistanceCheckOverride(uint32 dungeonId)
|
|
{
|
|
return (levelScalingDistanceCheckOverrides.find(dungeonId) != levelScalingDistanceCheckOverrides.end());
|
|
}
|
|
|
|
bool ShouldMapBeEnabled(Map* map)
|
|
{
|
|
if (map->IsDungeon() || map->IsRaid())
|
|
{
|
|
// get the current instance map
|
|
auto instanceMap = ((InstanceMap*)sMapMgr->FindMap(map->GetId(), map->GetInstanceId()));
|
|
|
|
// if there wasn't one, then we're not in an instance
|
|
if (!instanceMap)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// get the max player count for the instance
|
|
auto maxPlayerCount = instanceMap->GetMaxPlayers();
|
|
|
|
// if the player count is less than 1, then we're not in an instance
|
|
if (maxPlayerCount < 1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// use the configuration variables to determine if this instance type/size should have scaling enabled
|
|
if (instanceMap->IsHeroic())
|
|
{
|
|
switch (maxPlayerCount)
|
|
{
|
|
case 5:
|
|
return Enable5MHeroic;
|
|
case 10:
|
|
return Enable10MHeroic;
|
|
case 25:
|
|
return Enable25MHeroic;
|
|
default:
|
|
return EnableOtherHeroic;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (maxPlayerCount)
|
|
{
|
|
case 5:
|
|
return Enable5M;
|
|
case 10:
|
|
return Enable10M;
|
|
case 15:
|
|
return Enable15M;
|
|
case 20:
|
|
return Enable20M;
|
|
case 25:
|
|
return Enable25M;
|
|
case 40:
|
|
return Enable40M;
|
|
default:
|
|
return EnableOtherNormal;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// we're not in a dungeon or a raid, we never scale
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void LoadMapSettings(Map* map)
|
|
{
|
|
// Load (or create) the map's info
|
|
AutoBalanceMapInfo *mapABInfo=map->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
|
|
// create an InstanceMap object
|
|
InstanceMap* instanceMap = map->ToInstanceMap();
|
|
|
|
//check for null pointer
|
|
if (!map)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!map->IsDungeon() && !map->IsRaid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// should the map be enabled at all?
|
|
mapABInfo->enabled = ShouldMapBeEnabled(map);
|
|
|
|
//
|
|
// Dynamic Level Scaling Floor and Ceiling
|
|
//
|
|
|
|
// 5-player normal dungeons
|
|
if (instanceMap->GetMaxPlayers() <= 5 && !instanceMap->IsHeroic())
|
|
{
|
|
mapABInfo->levelScalingDynamicCeiling = LevelScalingDynamicLevelCeilingDungeons;
|
|
mapABInfo->levelScalingDynamicFloor = LevelScalingDynamicLevelFloorDungeons;
|
|
|
|
}
|
|
// 5-player heroic dungeons
|
|
else if (instanceMap->GetMaxPlayers() <= 5 && instanceMap->IsHeroic())
|
|
{
|
|
mapABInfo->levelScalingDynamicCeiling = LevelScalingDynamicLevelCeilingHeroicDungeons;
|
|
mapABInfo->levelScalingDynamicFloor = LevelScalingDynamicLevelFloorHeroicDungeons;
|
|
}
|
|
// Normal raids
|
|
else if (instanceMap->GetMaxPlayers() > 5 && !instanceMap->IsHeroic())
|
|
{
|
|
mapABInfo->levelScalingDynamicCeiling = LevelScalingDynamicLevelCeilingRaids;
|
|
mapABInfo->levelScalingDynamicFloor = LevelScalingDynamicLevelFloorRaids;
|
|
}
|
|
// Heroic raids
|
|
else if (instanceMap->GetMaxPlayers() > 5 && instanceMap->IsHeroic())
|
|
{
|
|
mapABInfo->levelScalingDynamicCeiling = LevelScalingDynamicLevelCeilingHeroicRaids;
|
|
mapABInfo->levelScalingDynamicFloor = LevelScalingDynamicLevelFloorHeroicRaids;
|
|
}
|
|
// something went wrong
|
|
else
|
|
{
|
|
LOG_ERROR("module.AutoBalance", "AutoBalance_AllCreatureScript::ModifyCreatureAttributes: Unable to determine dynamic scaling floor and ceiling for instance {}.", instanceMap->GetMapName());
|
|
mapABInfo->levelScalingDynamicCeiling = 3;
|
|
mapABInfo->levelScalingDynamicFloor = 5;
|
|
}
|
|
|
|
//
|
|
// Level Scaling Skip Levels
|
|
//
|
|
|
|
// Load the global settings into the map
|
|
mapABInfo->levelScalingSkipHigherLevels = LevelScalingSkipHigherLevels;
|
|
mapABInfo->levelScalingSkipLowerLevels = LevelScalingSkipLowerLevels;
|
|
|
|
//
|
|
// Per-instance overrides, if applicable
|
|
//
|
|
if (hasDynamicLevelOverride(map->GetId()))
|
|
{
|
|
AutoBalanceLevelScalingDynamicLevelSettings* myDynamicLevelSettings = &levelScalingDynamicLevelOverrides[map->GetId()];
|
|
|
|
// LevelScaling.SkipHigherLevels
|
|
if (myDynamicLevelSettings->skipHigher != -1)
|
|
mapABInfo->levelScalingSkipHigherLevels = myDynamicLevelSettings->skipHigher;
|
|
|
|
// LevelScaling.SkipLowerLevels
|
|
if (myDynamicLevelSettings->skipLower != -1)
|
|
mapABInfo->levelScalingSkipLowerLevels = myDynamicLevelSettings->skipLower;
|
|
|
|
// LevelScaling.DynamicLevelCeiling
|
|
if (myDynamicLevelSettings->ceiling != -1)
|
|
mapABInfo->levelScalingDynamicCeiling = myDynamicLevelSettings->ceiling;
|
|
|
|
// LevelScaling.DynamicLevelFloor
|
|
if (myDynamicLevelSettings->floor != -1)
|
|
mapABInfo->levelScalingDynamicFloor = myDynamicLevelSettings->floor;
|
|
}
|
|
}
|
|
|
|
void AddCreatureToMapData(Creature* creature, bool addToCreatureList = true, Player* playerToExcludeFromChecks = nullptr, bool forceRecalculation = false)
|
|
{
|
|
// make sure we have a creature and that it's assigned to a map
|
|
if (!creature || !creature->GetMap())
|
|
return;
|
|
|
|
// if this isn't a dungeon or a battleground, skip
|
|
if (!(creature->GetMap()->IsDungeon() || creature->GetMap()->IsRaid()))
|
|
return;
|
|
|
|
// get AutoBalance data
|
|
InstanceMap* instanceMap = ((InstanceMap*)sMapMgr->FindMap(creature->GetMapId(), creature->GetInstanceId()));
|
|
AutoBalanceMapInfo *mapABInfo=instanceMap->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
AutoBalanceCreatureInfo *creatureABInfo=creature->CustomData.GetDefault<AutoBalanceCreatureInfo>("AutoBalanceCreatureInfo");
|
|
|
|
// store the creature's original level if this is the first time seeing it
|
|
if (creatureABInfo->UnmodifiedLevel == 0)
|
|
{
|
|
// handle summoned creatures
|
|
if (creature->IsSummon())
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): Creature {} ({}->\?\?) is a summon.", creature->GetName(), creature->GetLevel());
|
|
if (creature->ToTempSummon() &&
|
|
creature->ToTempSummon()->GetSummoner() &&
|
|
creature->ToTempSummon()->GetSummoner()->ToCreature())
|
|
{
|
|
Creature* summoner = creature->ToTempSummon()->GetSummoner()->ToCreature();
|
|
if (!summoner)
|
|
{
|
|
creatureABInfo->UnmodifiedLevel = mapABInfo->avgCreatureLevel;
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): Summoned creature {} ({}) is not owned by a summoner.", creature->GetName(), creatureABInfo->UnmodifiedLevel);
|
|
}
|
|
else
|
|
{
|
|
Creature* summonerCreature = summoner->ToCreature();
|
|
AutoBalanceCreatureInfo *summonerABInfo=summonerCreature->CustomData.GetDefault<AutoBalanceCreatureInfo>("AutoBalanceCreatureInfo");
|
|
|
|
if (summonerABInfo->UnmodifiedLevel > 0)
|
|
{
|
|
creatureABInfo->UnmodifiedLevel = summonerABInfo->UnmodifiedLevel;
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): Summoned creature {} ({}) owned by {} ({}->{})", creature->GetName(), creatureABInfo->UnmodifiedLevel, summonerCreature->GetName(), summonerABInfo->UnmodifiedLevel, summonerCreature->GetLevel());
|
|
}
|
|
else
|
|
{
|
|
creatureABInfo->UnmodifiedLevel = summonerCreature->GetLevel();
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): Summoned creature {} ({}) owned by {} ({})", creature->GetName(), creatureABInfo->UnmodifiedLevel, summonerCreature->GetName(), summonerCreature->GetLevel());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
creatureABInfo->UnmodifiedLevel = mapABInfo->avgCreatureLevel;
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): Summoned creature {} ({}) does not have a summoner.", creature->GetName(), creatureABInfo->UnmodifiedLevel);
|
|
}
|
|
|
|
// if this is a summon, we shouldn't track it in any list and it does not contribute to the average level
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): Summoned creature {} ({}) will not affect the map's stats.", creature->GetName(), creatureABInfo->UnmodifiedLevel);
|
|
return;
|
|
|
|
}
|
|
// creature isn't a summon, just store their unmodified level
|
|
else
|
|
{
|
|
creatureABInfo->UnmodifiedLevel = creature->GetLevel();
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): {} ({})", creature->GetName(), creatureABInfo->UnmodifiedLevel);
|
|
}
|
|
}
|
|
|
|
// if this is a creature controlled by the player, skip
|
|
if (((creature->IsHunterPet() || creature->IsPet() || creature->IsSummon()) && creature->IsControlledByPlayer()))
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): {} ({}) is controlled by the player - skip.", creature->GetName(), creatureABInfo->UnmodifiedLevel);
|
|
return;
|
|
}
|
|
|
|
// if this is a non-relevant creature, skip
|
|
if (creature->IsCritter() || creature->IsTotem() || creature->IsTrigger())
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): {} ({}) is a critter, totem, or trigger - skip.", creature->GetName(), creatureABInfo->UnmodifiedLevel);
|
|
return;
|
|
}
|
|
|
|
// if the creature level is below 85% of the minimum LFG level, assume it's a flavor creature and shouldn't be tracked or modified
|
|
if (creatureABInfo->UnmodifiedLevel < ((float)mapABInfo->lfgMinLevel * .85f))
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): {} ({}) is below 85% of the LFG min level of {} and is NOT tracked.", creature->GetName(), creatureABInfo->UnmodifiedLevel, mapABInfo->lfgMinLevel);
|
|
return;
|
|
}
|
|
|
|
// if the creature level is above 125% of the maximum LFG level, assume it's a flavor creature or holiday boss and shouldn't be tracked or modified
|
|
if (creatureABInfo->UnmodifiedLevel > ((float)mapABInfo->lfgMaxLevel * 1.15f))
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): {} ({}) is above 115% of the LFG max level of {} and is NOT tracked.", creature->GetName(), creatureABInfo->UnmodifiedLevel, mapABInfo->lfgMaxLevel);
|
|
return;
|
|
}
|
|
|
|
// is this creature already in the map's creature list?
|
|
bool isCreatureAlreadyInCreatureList = creatureABInfo->isInCreatureList;
|
|
|
|
// add the creature to the map's creature list if configured to do so
|
|
if (addToCreatureList && !isCreatureAlreadyInCreatureList)
|
|
{
|
|
mapABInfo->allMapCreatures.push_back(creature);
|
|
creatureABInfo->isInCreatureList = true;
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): {} ({}) is creature #{} in the creature list.", creature->GetName(), creatureABInfo->UnmodifiedLevel, mapABInfo->allMapCreatures.size());
|
|
}
|
|
|
|
// alter stats for the map if needed
|
|
bool isIncludedInMapStats = true;
|
|
|
|
// if this creature was already in the creature list, don't consider it for map stats (again)
|
|
// exception for if forceRecalculation is true (used on player enter/exit to recalculate map stats)
|
|
if (isCreatureAlreadyInCreatureList && !forceRecalculation)
|
|
{
|
|
isIncludedInMapStats = false;
|
|
}
|
|
|
|
Map::PlayerList const &playerList = creature->GetMap()->GetPlayers();
|
|
if (!playerList.IsEmpty())
|
|
{
|
|
// only do these additional checks if we still think they need to be applied to the map stats
|
|
if (isIncludedInMapStats)
|
|
{
|
|
// if the creature is vendor, trainer, or has gossip, don't use it to update map stats
|
|
if ((creature->IsVendor() ||
|
|
creature->HasNpcFlag(UNIT_NPC_FLAG_GOSSIP) ||
|
|
creature->HasNpcFlag(UNIT_NPC_FLAG_QUESTGIVER) ||
|
|
creature->HasNpcFlag(UNIT_NPC_FLAG_TRAINER) ||
|
|
creature->HasNpcFlag(UNIT_NPC_FLAG_TRAINER_PROFESSION) ||
|
|
creature->HasNpcFlag(UNIT_NPC_FLAG_REPAIR) ||
|
|
creature->HasUnitFlag(UNIT_FLAG_IMMUNE_TO_PC) ||
|
|
creature->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) &&
|
|
(!creature->IsDungeonBoss())
|
|
)
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): {} ({}) is a a vendor, trainer, or is otherwise not attackable - do not include in map stats.", creature->GetName(), creatureABInfo->UnmodifiedLevel);
|
|
isIncludedInMapStats = false;
|
|
}
|
|
else
|
|
{
|
|
// if the creature is friendly to a player, don't use it to update map stats
|
|
for (Map::PlayerList::const_iterator playerIteration = playerList.begin(); playerIteration != playerList.end(); ++playerIteration)
|
|
{
|
|
Player* playerHandle = playerIteration->GetSource();
|
|
|
|
// if this player matches the player we're supposed to skip, skip
|
|
if (playerHandle == playerToExcludeFromChecks)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// if the creature is friendly and not a boss
|
|
if (creature->IsFriendlyTo(playerHandle) && !creature->IsDungeonBoss())
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): {} ({}) is friendly to {} - do not include in map stats.", creature->GetName(), creatureABInfo->UnmodifiedLevel, playerHandle->GetName());
|
|
isIncludedInMapStats = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// perform the distance check if an override is configured for this map
|
|
if (hasLevelScalingDistanceCheckOverride(instanceMap->GetId()))
|
|
{
|
|
uint32 distance = levelScalingDistanceCheckOverrides[instanceMap->GetId()];
|
|
bool isPlayerWithinDistance = false;
|
|
|
|
for (Map::PlayerList::const_iterator playerIteration = playerList.begin(); playerIteration != playerList.end(); ++playerIteration)
|
|
{
|
|
Player* playerHandle = playerIteration->GetSource();
|
|
|
|
// if this player matches the player we're supposed to skip, skip
|
|
if (playerHandle == playerToExcludeFromChecks)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (playerHandle->IsWithinDist(creature, 500))
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): {} ({}) is in range ({} world units) of player {} and is considered active.", creature->GetName(), creatureABInfo->UnmodifiedLevel, distance, playerHandle->GetName());
|
|
isPlayerWithinDistance = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): {} ({}) is NOT in range ({} world units) of any player and is NOT considered active.", creature->GetName(), creature->GetLevel(), distance);
|
|
}
|
|
}
|
|
|
|
// if no players were within the distance, don't include this creature in the map stats
|
|
if (!isPlayerWithinDistance)
|
|
isIncludedInMapStats = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isIncludedInMapStats)
|
|
{
|
|
// mark this creature as being considered in the map stats
|
|
creatureABInfo->isActive = true;
|
|
|
|
// update the highest and lowest creature levels
|
|
if (creatureABInfo->UnmodifiedLevel > mapABInfo->highestCreatureLevel || mapABInfo->highestCreatureLevel == 0)
|
|
mapABInfo->highestCreatureLevel = creatureABInfo->UnmodifiedLevel;
|
|
if (creatureABInfo->UnmodifiedLevel < mapABInfo->lowestCreatureLevel || mapABInfo->lowestCreatureLevel == 0)
|
|
mapABInfo->lowestCreatureLevel = creatureABInfo->UnmodifiedLevel;
|
|
|
|
// calculate the new average creature level
|
|
float creatureCount = mapABInfo->activeCreatureCount;
|
|
float newAvgCreatureLevel = (((float)mapABInfo->avgCreatureLevel * creatureCount) + (float)creatureABInfo->UnmodifiedLevel) / (creatureCount + 1.0f);
|
|
mapABInfo->avgCreatureLevel = newAvgCreatureLevel;
|
|
|
|
// increment the active creature counter
|
|
mapABInfo->activeCreatureCount++;
|
|
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): {} ({}) is included in map stats, adjusting avgCreatureLevel to {}", creature->GetName(), creatureABInfo->UnmodifiedLevel, newAvgCreatureLevel);
|
|
|
|
// reset the last config time so that the map data will get updated
|
|
lastConfigTime = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): lastConfigTime reset to {}", lastConfigTime);
|
|
}
|
|
else if (isCreatureAlreadyInCreatureList)
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): {} ({}) is already included in map stats.", creature->GetName(), creatureABInfo->UnmodifiedLevel);
|
|
}
|
|
else
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): {} ({}) is NOT included in map stats.", creature->GetName(), creatureABInfo->UnmodifiedLevel);
|
|
}
|
|
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::AddCreatureToMapData(): There are {} active creatures.", mapABInfo->activeCreatureCount);
|
|
}
|
|
}
|
|
|
|
void RemoveCreatureFromMapData(Creature* creature)
|
|
{
|
|
// get map data
|
|
AutoBalanceMapInfo *mapABInfo=creature->GetMap()->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
|
|
// if the creature is in the all creature list, remove it
|
|
if (mapABInfo->allMapCreatures.size() > 0)
|
|
{
|
|
for (std::vector<Creature*>::iterator creatureIteration = mapABInfo->allMapCreatures.begin(); creatureIteration != mapABInfo->allMapCreatures.end(); ++creatureIteration)
|
|
{
|
|
if (*creatureIteration == creature)
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreature::RemoveCreatureFromMapData(): {} ({}) is in the creature list and will be removed. There are {} creatures left.", creature->GetName(), creature->GetLevel(), mapABInfo->allMapCreatures.size() - 1);
|
|
mapABInfo->allMapCreatures.erase(creatureIteration);
|
|
|
|
// mark this creature as removed
|
|
AutoBalanceCreatureInfo *creatureABInfo=creature->CustomData.GetDefault<AutoBalanceCreatureInfo>("AutoBalanceCreatureInfo");
|
|
creatureABInfo->isInCreatureList = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdateMapLevelIfNeeded(Map* map)
|
|
{
|
|
// get map data
|
|
AutoBalanceMapInfo *mapABInfo=map->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
|
|
// if map needs update
|
|
if (mapABInfo->configTime != lastConfigTime)
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "UpdateMapLevelIfNeeded(): Map {} config is out of date ({} != {}) and will be updated.",
|
|
map->GetMapName(),
|
|
mapABInfo->configTime,
|
|
lastConfigTime);
|
|
|
|
// load the map's settings
|
|
LoadMapSettings(map);
|
|
|
|
// if LevelScaling is disabled OR if the average creature level is inside the skip range,
|
|
// set the map level to the average creature level, rounded to the nearest integer
|
|
if (!LevelScaling ||
|
|
((mapABInfo->avgCreatureLevel <= mapABInfo->highestPlayerLevel + mapABInfo->levelScalingSkipHigherLevels && mapABInfo->levelScalingSkipHigherLevels != 0) &&
|
|
(mapABInfo->avgCreatureLevel >= mapABInfo->highestPlayerLevel - mapABInfo->levelScalingSkipLowerLevels && mapABInfo->levelScalingSkipLowerLevels != 0))
|
|
)
|
|
{
|
|
mapABInfo->mapLevel = (uint8)(mapABInfo->avgCreatureLevel + 0.5f);
|
|
mapABInfo->isLevelScalingEnabled = false;
|
|
}
|
|
// If the average creature level is lower than the highest player level,
|
|
// set the map level to the average creature level, rounded to the nearest integer
|
|
else if (mapABInfo->avgCreatureLevel <= mapABInfo->highestPlayerLevel)
|
|
{
|
|
mapABInfo->mapLevel = (uint8)(mapABInfo->avgCreatureLevel + 0.5f);
|
|
mapABInfo->isLevelScalingEnabled = true;
|
|
}
|
|
// caps at the highest player level
|
|
else
|
|
{
|
|
mapABInfo->mapLevel = mapABInfo->highestPlayerLevel;
|
|
mapABInfo->isLevelScalingEnabled = true;
|
|
}
|
|
|
|
LOG_DEBUG("module.AutoBalance", "UpdateMapLevelIfNeeded(): Map {} level is now {}.", map->GetMapName(), mapABInfo->mapLevel);
|
|
|
|
// mark the config updated
|
|
mapABInfo->configTime = lastConfigTime;
|
|
}
|
|
}
|
|
|
|
void UpdateMapPlayerStats(Map* map)
|
|
{
|
|
// get the map's info
|
|
AutoBalanceMapInfo *mapABInfo=map->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
|
|
// get the map's player list
|
|
Map::PlayerList const &playerList = map->GetPlayers();
|
|
|
|
// if there are players on the map
|
|
if (!playerList.IsEmpty())
|
|
{
|
|
uint8 highestPlayerLevel = 0;
|
|
uint8 lowestPlayerLevel = 0;
|
|
|
|
// iterate through the players and update the highest and lowest player levels
|
|
for (Map::PlayerList::const_iterator playerIteration = playerList.begin(); playerIteration != playerList.end(); ++playerIteration)
|
|
{
|
|
Player* playerHandle = playerIteration->GetSource();
|
|
if (playerHandle && !playerHandle->IsGameMaster())
|
|
{
|
|
if (playerHandle->getLevel() > highestPlayerLevel || highestPlayerLevel == 0)
|
|
highestPlayerLevel = playerHandle->getLevel();
|
|
|
|
if (playerHandle->getLevel() < lowestPlayerLevel || lowestPlayerLevel == 0)
|
|
lowestPlayerLevel = playerHandle->getLevel();
|
|
}
|
|
mapABInfo->highestPlayerLevel = highestPlayerLevel;
|
|
mapABInfo->lowestPlayerLevel = lowestPlayerLevel;
|
|
}
|
|
|
|
LOG_DEBUG("module.AutoBalance", "UpdateMapPlayerStats(): Map {} player level range: {} - {}.", map->GetMapName(), mapABInfo->lowestPlayerLevel, mapABInfo->highestPlayerLevel);
|
|
}
|
|
|
|
// update the player count
|
|
mapABInfo->playerCount = map->GetPlayersCountExceptGMs();
|
|
}
|
|
|
|
void LoadForcedCreatureIdsFromString(std::string creatureIds, int forcedPlayerCount) // Used for reading the string from the configuration file to for those creatures who need to be scaled for XX number of players.
|
|
{
|
|
std::string delimitedValue;
|
|
std::stringstream creatureIdsStream;
|
|
|
|
creatureIdsStream.str(creatureIds);
|
|
while (std::getline(creatureIdsStream, delimitedValue, ',')) // Process each Creature ID in the string, delimited by the comma - ","
|
|
{
|
|
int creatureId = atoi(delimitedValue.c_str());
|
|
if (creatureId >= 0)
|
|
{
|
|
forcedCreatureIds[creatureId] = forcedPlayerCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
int GetForcedNumPlayers(int creatureId)
|
|
{
|
|
if (forcedCreatureIds.find(creatureId) == forcedCreatureIds.end()) // Don't want the forcedCreatureIds map to blowup to a massive empty array
|
|
{
|
|
return -1;
|
|
}
|
|
return forcedCreatureIds[creatureId];
|
|
}
|
|
|
|
class AutoBalance_WorldScript : public WorldScript
|
|
{
|
|
public:
|
|
AutoBalance_WorldScript()
|
|
: WorldScript("AutoBalance_WorldScript")
|
|
{
|
|
}
|
|
|
|
void OnBeforeConfigLoad(bool /*reload*/) override
|
|
{
|
|
SetInitialWorldSettings();
|
|
lastConfigTime = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
}
|
|
void OnStartup() override
|
|
{
|
|
}
|
|
|
|
void SetInitialWorldSettings()
|
|
{
|
|
forcedCreatureIds.clear();
|
|
enabledDungeonIds.clear();
|
|
dungeonOverrides.clear();
|
|
bossOverrides.clear();
|
|
statModifierOverrides.clear();
|
|
statModifierBossOverrides.clear();
|
|
statModifierCreatureOverrides.clear();
|
|
levelScalingDynamicLevelOverrides.clear();
|
|
levelScalingDistanceCheckOverrides.clear();
|
|
LoadForcedCreatureIdsFromString(sConfigMgr->GetOption<std::string>("AutoBalance.ForcedID40", ""), 40);
|
|
LoadForcedCreatureIdsFromString(sConfigMgr->GetOption<std::string>("AutoBalance.ForcedID25", ""), 25);
|
|
LoadForcedCreatureIdsFromString(sConfigMgr->GetOption<std::string>("AutoBalance.ForcedID10", ""), 10);
|
|
LoadForcedCreatureIdsFromString(sConfigMgr->GetOption<std::string>("AutoBalance.ForcedID5", ""), 5);
|
|
LoadForcedCreatureIdsFromString(sConfigMgr->GetOption<std::string>("AutoBalance.ForcedID2", ""), 2);
|
|
LoadForcedCreatureIdsFromString(sConfigMgr->GetOption<std::string>("AutoBalance.DisabledID", ""), 0);
|
|
LoadEnabledDungeons(sConfigMgr->GetOption<std::string>("AutoBalance.PerDungeonPlayerCounts", ""));
|
|
|
|
// Overrides
|
|
if (sConfigMgr->GetOption<float>("AutoBalance.PerDungeonScaling", false, false))
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.PerDungeonScaling` defined in `AutoBalance.conf`. This variable will be removed in a future release. Please see `AutoBalance.conf.dist` for more details.");
|
|
dungeonOverrides = LoadInflectionPointOverrides(
|
|
sConfigMgr->GetOption<std::string>("AutoBalance.InflectionPoint.PerInstance",sConfigMgr->GetOption<std::string>("AutoBalance.PerDungeonScaling", "", false), false)
|
|
); // `AutoBalance.PerDungeonScaling` for backwards compatibility
|
|
|
|
if (sConfigMgr->GetOption<float>("AutoBalance.PerDungeonBossScaling", false, false))
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.PerDungeonBossScaling` defined in `AutoBalance.conf`. This variable will be removed in a future release. Please see `AutoBalance.conf.dist` for more details.");
|
|
bossOverrides = LoadInflectionPointOverrides(
|
|
sConfigMgr->GetOption<std::string>("AutoBalance.InflectionPoint.Boss.PerInstance", sConfigMgr->GetOption<std::string>("AutoBalance.PerDungeonBossScaling", "", false), false)
|
|
); // `AutoBalance.PerDungeonBossScaling` for backwards compatibility
|
|
|
|
statModifierOverrides = LoadStatModifierOverrides(
|
|
sConfigMgr->GetOption<std::string>("AutoBalance.StatModifier.PerInstance", "", false)
|
|
);
|
|
|
|
statModifierBossOverrides = LoadStatModifierOverrides(
|
|
sConfigMgr->GetOption<std::string>("AutoBalance.StatModifier.Boss.PerInstance", "", false)
|
|
);
|
|
|
|
statModifierCreatureOverrides = LoadStatModifierOverrides(
|
|
sConfigMgr->GetOption<std::string>("AutoBalance.StatModifier.PerCreature", "", false)
|
|
);
|
|
|
|
levelScalingDynamicLevelOverrides = LoadDynamicLevelOverrides(
|
|
sConfigMgr->GetOption<std::string>("AutoBalance.LevelScaling.DynamicLevel.PerInstance", "", false)
|
|
);
|
|
|
|
levelScalingDistanceCheckOverrides = LoadDistanceCheckOverrides(
|
|
sConfigMgr->GetOption<std::string>("AutoBalance.LevelScaling.DynamicLevel.DistanceCheck.PerInstance", "", false)
|
|
);
|
|
|
|
// AutoBalance.Enable.*
|
|
// Deprecated setting warning
|
|
if (sConfigMgr->GetOption<int>("AutoBalance.enable", -1, false) != -1)
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.enable` defined in `AutoBalance.conf`. This variable will be removed in a future release. Please see `AutoBalance.conf.dist` for more details.");
|
|
|
|
EnableGlobal = sConfigMgr->GetOption<bool>("AutoBalance.Enable.Global", sConfigMgr->GetOption<bool>("AutoBalance.enable", 1, false)); // `AutoBalance.enable` for backwards compatibility
|
|
|
|
Enable5M = sConfigMgr->GetOption<bool>("AutoBalance.Enable.5M", sConfigMgr->GetOption<bool>("AutoBalance.enable", 1, false));
|
|
Enable10M = sConfigMgr->GetOption<bool>("AutoBalance.Enable.10M", sConfigMgr->GetOption<bool>("AutoBalance.enable", 1, false));
|
|
Enable15M = sConfigMgr->GetOption<bool>("AutoBalance.Enable.15M", sConfigMgr->GetOption<bool>("AutoBalance.enable", 1, false));
|
|
Enable20M = sConfigMgr->GetOption<bool>("AutoBalance.Enable.20M", sConfigMgr->GetOption<bool>("AutoBalance.enable", 1, false));
|
|
Enable25M = sConfigMgr->GetOption<bool>("AutoBalance.Enable.25M", sConfigMgr->GetOption<bool>("AutoBalance.enable", 1, false));
|
|
Enable40M = sConfigMgr->GetOption<bool>("AutoBalance.Enable.40M", sConfigMgr->GetOption<bool>("AutoBalance.enable", 1, false));
|
|
EnableOtherNormal = sConfigMgr->GetOption<bool>("AutoBalance.Enable.OtherNormal", sConfigMgr->GetOption<bool>("AutoBalance.enable", 1, false));
|
|
|
|
Enable5MHeroic = sConfigMgr->GetOption<bool>("AutoBalance.Enable.5MHeroic", sConfigMgr->GetOption<bool>("AutoBalance.enable", 1, false));
|
|
Enable10MHeroic = sConfigMgr->GetOption<bool>("AutoBalance.Enable.5MHeroic", sConfigMgr->GetOption<bool>("AutoBalance.enable", 1, false));
|
|
Enable25MHeroic = sConfigMgr->GetOption<bool>("AutoBalance.Enable.5MHeroic", sConfigMgr->GetOption<bool>("AutoBalance.enable", 1, false));
|
|
EnableOtherHeroic = sConfigMgr->GetOption<bool>("AutoBalance.Enable.5MHeroic", sConfigMgr->GetOption<bool>("AutoBalance.enable", 1, false));
|
|
|
|
// Deprecated setting warning
|
|
if (sConfigMgr->GetOption<int>("AutoBalance.DungeonsOnly", -1, false) != -1)
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.DungeonsOnly` defined in `AutoBalance.conf`. This variable has been removed and has no effect. Please see `AutoBalance.conf.dist` for more details.");
|
|
|
|
if (sConfigMgr->GetOption<int>("AutoBalance.levelUseDbValuesWhenExists", -1, false) != -1)
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.levelUseDbValuesWhenExists` defined in `AutoBalance.conf`. This variable has been removed and has no effect. Please see `AutoBalance.conf.dist` for more details.");
|
|
|
|
// Misc Settings
|
|
// TODO: Organize and standardize variable names
|
|
|
|
PlayerChangeNotify = sConfigMgr->GetOption<bool>("AutoBalance.PlayerChangeNotify", 1);
|
|
|
|
rewardEnabled = sConfigMgr->GetOption<bool>("AutoBalance.reward.enable", 1);
|
|
PlayerCountDifficultyOffset = sConfigMgr->GetOption<uint32>("AutoBalance.playerCountDifficultyOffset", 0);
|
|
rewardRaid = sConfigMgr->GetOption<uint32>("AutoBalance.reward.raidToken", 49426);
|
|
rewardDungeon = sConfigMgr->GetOption<uint32>("AutoBalance.reward.dungeonToken", 47241);
|
|
MinPlayerReward = sConfigMgr->GetOption<float>("AutoBalance.reward.MinPlayerReward", 1);
|
|
|
|
// InflectionPoint*
|
|
// warn the console if deprecated values are detected
|
|
if (sConfigMgr->GetOption<float>("AutoBalance.BossInflectionMult", false, false))
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.BossInflectionMult` defined in `AutoBalance.conf`. This variable will be removed in a future release. Please see `AutoBalance.conf.dist` for more details.");
|
|
|
|
InflectionPoint = sConfigMgr->GetOption<float>("AutoBalance.InflectionPoint", 0.5f, false);
|
|
InflectionPointCurveFloor = sConfigMgr->GetOption<float>("AutoBalance.InflectionPoint.CurveFloor", 0.0f, false);
|
|
InflectionPointCurveCeiling = sConfigMgr->GetOption<float>("AutoBalance.InflectionPoint.CurveCeiling", 1.0f, false);
|
|
InflectionPointBoss = sConfigMgr->GetOption<float>("AutoBalance.InflectionPoint.BossModifier", sConfigMgr->GetOption<float>("AutoBalance.BossInflectionMult", 1.0f, false), false); // `AutoBalance.BossInflectionMult` for backwards compatibility
|
|
|
|
InflectionPointHeroic = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointHeroic", 0.5f, false);
|
|
InflectionPointHeroicCurveFloor = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointHeroic.CurveFloor", 0.0f, false);
|
|
InflectionPointHeroicCurveCeiling = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointHeroic.CurveCeiling", 1.0f, false);
|
|
InflectionPointHeroicBoss = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointHeroic.BossModifier", sConfigMgr->GetOption<float>("AutoBalance.BossInflectionMult", 1.0f, false), false); // `AutoBalance.BossInflectionMult` for backwards compatibility
|
|
|
|
InflectionPointRaid = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid", 0.5f, false);
|
|
InflectionPointRaidCurveFloor = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid.CurveFloor", 0.0f, false);
|
|
InflectionPointRaidCurveCeiling = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid.CurveCeiling", 1.0f, false);
|
|
InflectionPointRaidBoss = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid.BossModifier", sConfigMgr->GetOption<float>("AutoBalance.BossInflectionMult", 1.0f, false), false); // `AutoBalance.BossInflectionMult` for backwards compatibility
|
|
|
|
InflectionPointRaidHeroic = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaidHeroic", 0.5f, false);
|
|
InflectionPointRaidHeroicCurveFloor = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaidHeroic.CurveFloor", 0.0f, false);
|
|
InflectionPointRaidHeroicCurveCeiling = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaidHeroic.CurveCeiling", 1.0f, false);
|
|
InflectionPointRaidHeroicBoss = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaidHeroic.BossModifier", sConfigMgr->GetOption<float>("AutoBalance.BossInflectionMult", 1.0f, false), false); // `AutoBalance.BossInflectionMult` for backwards compatibility
|
|
|
|
InflectionPointRaid10M = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid10M", InflectionPointRaid, false);
|
|
InflectionPointRaid10MCurveFloor = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid10M.CurveFloor", InflectionPointRaidCurveFloor, false);
|
|
InflectionPointRaid10MCurveCeiling = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid10M.CurveCeiling", InflectionPointRaidCurveCeiling, false);
|
|
InflectionPointRaid10MBoss = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid10M.BossModifier", InflectionPointRaidBoss, false);
|
|
|
|
InflectionPointRaid10MHeroic = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid10MHeroic", InflectionPointRaidHeroic, false);
|
|
InflectionPointRaid10MHeroicCurveFloor = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid10MHeroic.CurveFloor", InflectionPointRaidHeroicCurveFloor, false);
|
|
InflectionPointRaid10MHeroicCurveCeiling = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid10MHeroic.CurveCeiling", InflectionPointRaidHeroicCurveCeiling, false);
|
|
InflectionPointRaid10MHeroicBoss = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid10MHeroic.BossModifier", InflectionPointRaidHeroicBoss, false);
|
|
|
|
InflectionPointRaid15M = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid15M", InflectionPointRaid, false);
|
|
InflectionPointRaid15MCurveFloor = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid15M.CurveFloor", InflectionPointRaidCurveFloor, false);
|
|
InflectionPointRaid15MCurveCeiling = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid15M.CurveCeiling", InflectionPointRaidCurveCeiling, false);
|
|
InflectionPointRaid15MBoss = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid15M.BossModifier", InflectionPointRaidBoss, false);
|
|
|
|
InflectionPointRaid20M = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid20M", InflectionPointRaid, false);
|
|
InflectionPointRaid20MCurveFloor = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid20M.CurveFloor", InflectionPointRaidCurveFloor, false);
|
|
InflectionPointRaid20MCurveCeiling = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid20M.CurveCeiling", InflectionPointRaidCurveCeiling, false);
|
|
InflectionPointRaid20MBoss = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid20M.BossModifier", InflectionPointRaidBoss, false);
|
|
|
|
InflectionPointRaid25M = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid25M", InflectionPointRaid, false);
|
|
InflectionPointRaid25MCurveFloor = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid25M.CurveFloor", InflectionPointRaidCurveFloor, false);
|
|
InflectionPointRaid25MCurveCeiling = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid25M.CurveCeiling", InflectionPointRaidCurveCeiling, false);
|
|
InflectionPointRaid25MBoss = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid25M.BossModifier", InflectionPointRaidBoss, false);
|
|
|
|
InflectionPointRaid25MHeroic = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid25MHeroic", InflectionPointRaidHeroic, false);
|
|
InflectionPointRaid25MHeroicCurveFloor = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid25MHeroic.CurveFloor", InflectionPointRaidHeroicCurveFloor, false);
|
|
InflectionPointRaid25MHeroicCurveCeiling = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid25MHeroic.CurveCeiling", InflectionPointRaidHeroicCurveCeiling, false);
|
|
InflectionPointRaid25MHeroicBoss = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid25MHeroic.BossModifier", InflectionPointRaidHeroicBoss, false);
|
|
|
|
InflectionPointRaid40M = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid40M", InflectionPointRaid, false);
|
|
InflectionPointRaid40MCurveFloor = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid40M.CurveFloor", InflectionPointRaidCurveFloor, false);
|
|
InflectionPointRaid40MCurveCeiling = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid40M.CurveCeiling", InflectionPointRaidCurveCeiling, false);
|
|
InflectionPointRaid40MBoss = sConfigMgr->GetOption<float>("AutoBalance.InflectionPointRaid40M.BossModifier", InflectionPointRaidBoss, false);
|
|
|
|
// StatModifier*
|
|
// warn the console if deprecated values are detected
|
|
if (sConfigMgr->GetOption<float>("AutoBalance.rate.global", false, false))
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.rate.global` defined in `AutoBalance.conf`. This variable will be removed in a future release. Please see `AutoBalance.conf.dist` for more details.");
|
|
if (sConfigMgr->GetOption<float>("AutoBalance.rate.health", false, false))
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.rate.health` defined in `AutoBalance.conf`. This variable will be removed in a future release. Please see `AutoBalance.conf.dist` for more details.");
|
|
if (sConfigMgr->GetOption<float>("AutoBalance.rate.mana", false, false))
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.rate.mana` defined in `AutoBalance.conf`. This variable will be removed in a future release. Please see `AutoBalance.conf.dist` for more details.");
|
|
if (sConfigMgr->GetOption<float>("AutoBalance.rate.armor", false, false))
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.rate.armor` defined in `AutoBalance.conf`. This variable will be removed in a future release. Please see `AutoBalance.conf.dist` for more details.");
|
|
if (sConfigMgr->GetOption<float>("AutoBalance.rate.damage", false, false))
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.rate.damage` defined in `AutoBalance.conf`. This variable will be removed in a future release. Please see `AutoBalance.conf.dist` for more details.");
|
|
|
|
// 5-player dungeons
|
|
StatModifier_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifier.Global", sConfigMgr->GetOption<float>("AutoBalance.rate.global", 1.0f, false), false); // `AutoBalance.rate.global` for backwards compatibility
|
|
StatModifier_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifier.Health", sConfigMgr->GetOption<float>("AutoBalance.rate.health", 1.0f, false), false); // `AutoBalance.rate.health` for backwards compatibility
|
|
StatModifier_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifier.Mana", sConfigMgr->GetOption<float>("AutoBalance.rate.mana", 1.0f, false), false); // `AutoBalance.rate.mana` for backwards compatibility
|
|
StatModifier_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifier.Armor", sConfigMgr->GetOption<float>("AutoBalance.rate.armor", 1.0f, false), false); // `AutoBalance.rate.armor` for backwards compatibility
|
|
StatModifier_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifier.Damage", sConfigMgr->GetOption<float>("AutoBalance.rate.damage", 1.0f, false), false); // `AutoBalance.rate.damage` for backwards compatibility
|
|
StatModifier_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifier.CCDuration", -1.0f, false);
|
|
|
|
StatModifier_Boss_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifier.Boss.Global", sConfigMgr->GetOption<float>("AutoBalance.rate.global", 1.0f, false), false); // `AutoBalance.rate.global` for backwards compatibility
|
|
StatModifier_Boss_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifier.Boss.Health", sConfigMgr->GetOption<float>("AutoBalance.rate.health", 1.0f, false), false); // `AutoBalance.rate.health` for backwards compatibility
|
|
StatModifier_Boss_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifier.Boss.Mana", sConfigMgr->GetOption<float>("AutoBalance.rate.mana", 1.0f, false), false); // `AutoBalance.rate.mana` for backwards compatibility
|
|
StatModifier_Boss_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifier.Boss.Armor", sConfigMgr->GetOption<float>("AutoBalance.rate.armor", 1.0f, false), false); // `AutoBalance.rate.armor` for backwards compatibility
|
|
StatModifier_Boss_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifier.Boss.Damage", sConfigMgr->GetOption<float>("AutoBalance.rate.damage", 1.0f, false), false); // `AutoBalance.rate.damage` for backwards compatibility
|
|
StatModifier_Boss_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifier.Boss.CCDuration", -1.0f, false);
|
|
|
|
// 5-player heroic dungeons
|
|
StatModifierHeroic_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierHeroic.Global", sConfigMgr->GetOption<float>("AutoBalance.rate.global", 1.0f, false), false); // `AutoBalance.rate.global` for backwards compatibility
|
|
StatModifierHeroic_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierHeroic.Health", sConfigMgr->GetOption<float>("AutoBalance.rate.health", 1.0f, false), false); // `AutoBalance.rate.health` for backwards compatibility
|
|
StatModifierHeroic_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierHeroic.Mana", sConfigMgr->GetOption<float>("AutoBalance.rate.mana", 1.0f, false), false); // `AutoBalance.rate.mana` for backwards compatibility
|
|
StatModifierHeroic_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierHeroic.Armor", sConfigMgr->GetOption<float>("AutoBalance.rate.armor", 1.0f, false), false); // `AutoBalance.rate.armor` for backwards compatibility
|
|
StatModifierHeroic_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierHeroic.Damage", sConfigMgr->GetOption<float>("AutoBalance.rate.damage", 1.0f, false), false); // `AutoBalance.rate.damage` for backwards compatibility
|
|
StatModifierHeroic_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierHeroic.CCDuration", -1.0f, false);
|
|
|
|
StatModifierHeroic_Boss_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierHeroic.Boss.Global", sConfigMgr->GetOption<float>("AutoBalance.rate.global", 1.0f, false), false); // `AutoBalance.rate.global` for backwards compatibility
|
|
StatModifierHeroic_Boss_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierHeroic.Boss.Health", sConfigMgr->GetOption<float>("AutoBalance.rate.health", 1.0f, false), false); // `AutoBalance.rate.health` for backwards compatibility
|
|
StatModifierHeroic_Boss_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierHeroic.Boss.Mana", sConfigMgr->GetOption<float>("AutoBalance.rate.mana", 1.0f, false), false); // `AutoBalance.rate.mana` for backwards compatibility
|
|
StatModifierHeroic_Boss_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierHeroic.Boss.Armor", sConfigMgr->GetOption<float>("AutoBalance.rate.armor", 1.0f, false), false); // `AutoBalance.rate.armor` for backwards compatibility
|
|
StatModifierHeroic_Boss_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierHeroic.Boss.Damage", sConfigMgr->GetOption<float>("AutoBalance.rate.damage", 1.0f, false), false); // `AutoBalance.rate.damage` for backwards compatibility
|
|
StatModifierHeroic_Boss_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierHeroic.Boss.CCDuration", -1.0f, false);
|
|
|
|
// Default for all raids
|
|
StatModifierRaid_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid.Global", sConfigMgr->GetOption<float>("AutoBalance.rate.global", 1.0f, false), false); // `AutoBalance.rate.global` for backwards compatibility
|
|
StatModifierRaid_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid.Health", sConfigMgr->GetOption<float>("AutoBalance.rate.health", 1.0f, false), false); // `AutoBalance.rate.health` for backwards compatibility
|
|
StatModifierRaid_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid.Mana", sConfigMgr->GetOption<float>("AutoBalance.rate.mana", 1.0f, false), false); // `AutoBalance.rate.mana` for backwards compatibility
|
|
StatModifierRaid_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid.Armor", sConfigMgr->GetOption<float>("AutoBalance.rate.armor", 1.0f, false), false); // `AutoBalance.rate.armor` for backwards compatibility
|
|
StatModifierRaid_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid.Damage", sConfigMgr->GetOption<float>("AutoBalance.rate.damage", 1.0f, false), false); // `AutoBalance.rate.damage` for backwards compatibility
|
|
StatModifierRaid_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid.CCDuration", -1.0f, false);
|
|
|
|
StatModifierRaid_Boss_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid.Boss.Global", sConfigMgr->GetOption<float>("AutoBalance.rate.global", 1.0f, false), false); // `AutoBalance.rate.global` for backwards compatibility
|
|
StatModifierRaid_Boss_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid.Boss.Health", sConfigMgr->GetOption<float>("AutoBalance.rate.health", 1.0f, false), false); // `AutoBalance.rate.health` for backwards compatibility
|
|
StatModifierRaid_Boss_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid.Boss.Mana", sConfigMgr->GetOption<float>("AutoBalance.rate.mana", 1.0f, false), false); // `AutoBalance.rate.mana` for backwards compatibility
|
|
StatModifierRaid_Boss_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid.Boss.Armor", sConfigMgr->GetOption<float>("AutoBalance.rate.armor", 1.0f, false), false); // `AutoBalance.rate.armor` for backwards compatibility
|
|
StatModifierRaid_Boss_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid.Boss.Damage", sConfigMgr->GetOption<float>("AutoBalance.rate.damage", 1.0f, false), false); // `AutoBalance.rate.damage` for backwards compatibility
|
|
StatModifierRaid_Boss_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid.Boss.CCDuration", -1.0f, false);
|
|
|
|
// Default for all heroic raids
|
|
StatModifierRaidHeroic_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaidHeroic.Global", sConfigMgr->GetOption<float>("AutoBalance.rate.global", 1.0f, false), false); // `AutoBalance.rate.global` for backwards compatibility
|
|
StatModifierRaidHeroic_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaidHeroic.Health", sConfigMgr->GetOption<float>("AutoBalance.rate.health", 1.0f, false), false); // `AutoBalance.rate.health` for backwards compatibility
|
|
StatModifierRaidHeroic_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaidHeroic.Mana", sConfigMgr->GetOption<float>("AutoBalance.rate.mana", 1.0f, false), false); // `AutoBalance.rate.mana` for backwards compatibility
|
|
StatModifierRaidHeroic_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaidHeroic.Armor", sConfigMgr->GetOption<float>("AutoBalance.rate.armor", 1.0f, false), false); // `AutoBalance.rate.armor` for backwards compatibility
|
|
StatModifierRaidHeroic_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaidHeroic.Damage", sConfigMgr->GetOption<float>("AutoBalance.rate.damage", 1.0f, false), false); // `AutoBalance.rate.damage` for backwards compatibility
|
|
StatModifierRaidHeroic_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaidHeroic.CCDuration", -1.0f, false);
|
|
|
|
StatModifierRaidHeroic_Boss_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaidHeroic.Boss.Global", sConfigMgr->GetOption<float>("AutoBalance.rate.global", 1.0f, false), false); // `AutoBalance.rate.global` for backwards compatibility
|
|
StatModifierRaidHeroic_Boss_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaidHeroic.Boss.Health", sConfigMgr->GetOption<float>("AutoBalance.rate.health", 1.0f, false), false); // `AutoBalance.rate.health` for backwards compatibility
|
|
StatModifierRaidHeroic_Boss_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaidHeroic.Boss.Mana", sConfigMgr->GetOption<float>("AutoBalance.rate.mana", 1.0f, false), false); // `AutoBalance.rate.mana` for backwards compatibility
|
|
StatModifierRaidHeroic_Boss_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaidHeroic.Boss.Armor", sConfigMgr->GetOption<float>("AutoBalance.rate.armor", 1.0f, false), false); // `AutoBalance.rate.armor` for backwards compatibility
|
|
StatModifierRaidHeroic_Boss_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaidHeroic.Boss.Damage", sConfigMgr->GetOption<float>("AutoBalance.rate.damage", 1.0f, false), false); // `AutoBalance.rate.damage` for backwards compatibility
|
|
StatModifierRaidHeroic_Boss_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaidHeroic.Boss.CCDuration", -1.0f, false);
|
|
|
|
// 10-player raids
|
|
StatModifierRaid10M_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10M.Global", StatModifierRaid_Global, false);
|
|
StatModifierRaid10M_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10M.Health", StatModifierRaid_Health, false);
|
|
StatModifierRaid10M_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10M.Mana", StatModifierRaid_Mana, false);
|
|
StatModifierRaid10M_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10M.Armor", StatModifierRaid_Armor, false);
|
|
StatModifierRaid10M_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10M.Damage", StatModifierRaid_Damage, false);
|
|
StatModifierRaid10M_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10M.CCDuration", StatModifierRaid_CCDuration, false);
|
|
|
|
StatModifierRaid10M_Boss_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10M.Boss.Global", StatModifierRaid_Boss_Global, false);
|
|
StatModifierRaid10M_Boss_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10M.Boss.Health", StatModifierRaid_Boss_Health, false);
|
|
StatModifierRaid10M_Boss_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10M.Boss.Mana", StatModifierRaid_Boss_Mana, false);
|
|
StatModifierRaid10M_Boss_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10M.Boss.Armor", StatModifierRaid_Boss_Armor, false);
|
|
StatModifierRaid10M_Boss_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10M.Boss.Damage", StatModifierRaid_Boss_Damage, false);
|
|
StatModifierRaid10M_Boss_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10M.Boss.CCDuration", StatModifierRaid_Boss_CCDuration, false);
|
|
|
|
// 10-player heroic raids
|
|
StatModifierRaid10MHeroic_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10MHeroic.Global", StatModifierRaidHeroic_Global, false);
|
|
StatModifierRaid10MHeroic_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10MHeroic.Health", StatModifierRaidHeroic_Health, false);
|
|
StatModifierRaid10MHeroic_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10MHeroic.Mana", StatModifierRaidHeroic_Mana, false);
|
|
StatModifierRaid10MHeroic_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10MHeroic.Armor", StatModifierRaidHeroic_Armor, false);
|
|
StatModifierRaid10MHeroic_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10MHeroic.Damage", StatModifierRaidHeroic_Damage, false);
|
|
StatModifierRaid10MHeroic_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10MHeroic.CCDuration", StatModifierRaidHeroic_CCDuration, false);
|
|
|
|
StatModifierRaid10MHeroic_Boss_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10MHeroic.Boss.Global", StatModifierRaidHeroic_Boss_Global, false);
|
|
StatModifierRaid10MHeroic_Boss_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10MHeroic.Boss.Health", StatModifierRaidHeroic_Boss_Health, false);
|
|
StatModifierRaid10MHeroic_Boss_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10MHeroic.Boss.Mana", StatModifierRaidHeroic_Boss_Mana, false);
|
|
StatModifierRaid10MHeroic_Boss_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10MHeroic.Boss.Armor", StatModifierRaidHeroic_Boss_Armor, false);
|
|
StatModifierRaid10MHeroic_Boss_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10MHeroic.Boss.Damage", StatModifierRaidHeroic_Boss_Damage, false);
|
|
StatModifierRaid10MHeroic_Boss_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid10MHeroic.Boss.CCDuration", StatModifierRaidHeroic_Boss_CCDuration, false);
|
|
|
|
// 15-player raids
|
|
StatModifierRaid15M_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid15M.Global", StatModifierRaid_Global, false);
|
|
StatModifierRaid15M_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid15M.Health", StatModifierRaid_Health, false);
|
|
StatModifierRaid15M_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid15M.Mana", StatModifierRaid_Mana, false);
|
|
StatModifierRaid15M_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid15M.Armor", StatModifierRaid_Armor, false);
|
|
StatModifierRaid15M_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid15M.Damage", StatModifierRaid_Damage, false);
|
|
StatModifierRaid15M_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid15M.CCDuration", StatModifierRaid_CCDuration, false);
|
|
|
|
StatModifierRaid15M_Boss_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid15M.Boss.Global", StatModifierRaid_Boss_Global, false);
|
|
StatModifierRaid15M_Boss_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid15M.Boss.Health", StatModifierRaid_Boss_Health, false);
|
|
StatModifierRaid15M_Boss_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid15M.Boss.Mana", StatModifierRaid_Boss_Mana, false);
|
|
StatModifierRaid15M_Boss_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid15M.Boss.Armor", StatModifierRaid_Boss_Armor, false);
|
|
StatModifierRaid15M_Boss_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid15M.Boss.Damage", StatModifierRaid_Boss_Damage, false);
|
|
StatModifierRaid15M_Boss_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid15M.Boss.CCDuration", StatModifierRaid_Boss_CCDuration, false);
|
|
|
|
// 20-player raids
|
|
StatModifierRaid20M_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid20M.Global", StatModifierRaid_Global, false);
|
|
StatModifierRaid20M_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid20M.Health", StatModifierRaid_Health, false);
|
|
StatModifierRaid20M_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid20M.Mana", StatModifierRaid_Mana, false);
|
|
StatModifierRaid20M_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid20M.Armor", StatModifierRaid_Armor, false);
|
|
StatModifierRaid20M_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid20M.Damage", StatModifierRaid_Damage, false);
|
|
StatModifierRaid20M_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid20M.CCDuration", StatModifierRaid_CCDuration, false);
|
|
|
|
StatModifierRaid20M_Boss_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid20M.Boss.Global", StatModifierRaid_Boss_Global, false);
|
|
StatModifierRaid20M_Boss_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid20M.Boss.Health", StatModifierRaid_Boss_Health, false);
|
|
StatModifierRaid20M_Boss_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid20M.Boss.Mana", StatModifierRaid_Boss_Mana, false);
|
|
StatModifierRaid20M_Boss_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid20M.Boss.Armor", StatModifierRaid_Boss_Armor, false);
|
|
StatModifierRaid20M_Boss_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid20M.Boss.Damage", StatModifierRaid_Boss_Damage, false);
|
|
StatModifierRaid20M_Boss_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid20M.Boss.CCDuration", StatModifierRaid_Boss_CCDuration, false);
|
|
|
|
// 25-player raids
|
|
StatModifierRaid25M_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25M.Global", StatModifierRaid_Global, false);
|
|
StatModifierRaid25M_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25M.Health", StatModifierRaid_Health, false);
|
|
StatModifierRaid25M_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25M.Mana", StatModifierRaid_Mana, false);
|
|
StatModifierRaid25M_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25M.Armor", StatModifierRaid_Armor, false);
|
|
StatModifierRaid25M_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25M.Damage", StatModifierRaid_Damage, false);
|
|
StatModifierRaid25M_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25M.CCDuration", StatModifierRaid_CCDuration, false);
|
|
|
|
StatModifierRaid25M_Boss_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25M.Boss.Global", StatModifierRaid_Boss_Global, false);
|
|
StatModifierRaid25M_Boss_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25M.Boss.Health", StatModifierRaid_Boss_Health, false);
|
|
StatModifierRaid25M_Boss_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25M.Boss.Mana", StatModifierRaid_Boss_Mana, false);
|
|
StatModifierRaid25M_Boss_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25M.Boss.Armor", StatModifierRaid_Boss_Armor, false);
|
|
StatModifierRaid25M_Boss_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25M.Boss.Damage", StatModifierRaid_Boss_Damage, false);
|
|
StatModifierRaid25M_Boss_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25M.Boss.CCDuration", StatModifierRaid_Boss_CCDuration, false);
|
|
|
|
// 25-player heroic raids
|
|
StatModifierRaid25MHeroic_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25MHeroic.Global", StatModifierRaidHeroic_Global, false);
|
|
StatModifierRaid25MHeroic_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25MHeroic.Health", StatModifierRaidHeroic_Health, false);
|
|
StatModifierRaid25MHeroic_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25MHeroic.Mana", StatModifierRaidHeroic_Mana, false);
|
|
StatModifierRaid25MHeroic_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25MHeroic.Armor", StatModifierRaidHeroic_Armor, false);
|
|
StatModifierRaid25MHeroic_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25MHeroic.Damage", StatModifierRaidHeroic_Damage, false);
|
|
StatModifierRaid25MHeroic_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25MHeroic.CCDuration", StatModifierRaidHeroic_CCDuration, false);
|
|
|
|
StatModifierRaid25MHeroic_Boss_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25MHeroic.Boss.Global", StatModifierRaidHeroic_Boss_Global, false);
|
|
StatModifierRaid25MHeroic_Boss_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25MHeroic.Boss.Health", StatModifierRaidHeroic_Boss_Health, false);
|
|
StatModifierRaid25MHeroic_Boss_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25MHeroic.Boss.Mana", StatModifierRaidHeroic_Boss_Mana, false);
|
|
StatModifierRaid25MHeroic_Boss_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25MHeroic.Boss.Armor", StatModifierRaidHeroic_Boss_Armor, false);
|
|
StatModifierRaid25MHeroic_Boss_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25MHeroic.Boss.Damage", StatModifierRaidHeroic_Boss_Damage, false);
|
|
StatModifierRaid25MHeroic_Boss_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid25MHeroic.Boss.CCDuration", StatModifierRaidHeroic_Boss_CCDuration, false);
|
|
|
|
// 40-player raids
|
|
StatModifierRaid40M_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid40M.Global", StatModifierRaid_Global, false);
|
|
StatModifierRaid40M_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid40M.Health", StatModifierRaid_Health, false);
|
|
StatModifierRaid40M_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid40M.Mana", StatModifierRaid_Mana, false);
|
|
StatModifierRaid40M_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid40M.Armor", StatModifierRaid_Armor, false);
|
|
StatModifierRaid40M_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid40M.Damage", StatModifierRaid_Damage, false);
|
|
StatModifierRaid40M_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid40M.CCDuration", StatModifierRaid_CCDuration, false);
|
|
|
|
StatModifierRaid40M_Boss_Global = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid40M.Boss.Global", StatModifierRaid_Boss_Global, false);
|
|
StatModifierRaid40M_Boss_Health = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid40M.Boss.Health", StatModifierRaid_Boss_Health, false);
|
|
StatModifierRaid40M_Boss_Mana = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid40M.Boss.Mana", StatModifierRaid_Boss_Mana, false);
|
|
StatModifierRaid40M_Boss_Armor = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid40M.Boss.Armor", StatModifierRaid_Boss_Armor, false);
|
|
StatModifierRaid40M_Boss_Damage = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid40M.Boss.Damage", StatModifierRaid_Boss_Damage, false);
|
|
StatModifierRaid40M_Boss_CCDuration = sConfigMgr->GetOption<float>("AutoBalance.StatModifierRaid40M.Boss.CCDuration", StatModifierRaid_Boss_CCDuration, false);
|
|
|
|
// Modifier Min/Max
|
|
MinHPModifier = sConfigMgr->GetOption<float>("AutoBalance.MinHPModifier", 0.1f);
|
|
MinManaModifier = sConfigMgr->GetOption<float>("AutoBalance.MinManaModifier", 0.01f);
|
|
MinDamageModifier = sConfigMgr->GetOption<float>("AutoBalance.MinDamageModifier", 0.01f);
|
|
MinCCDurationModifier = sConfigMgr->GetOption<float>("AutoBalance.MinCCDurationModifier", 0.25f);
|
|
MaxCCDurationModifier = sConfigMgr->GetOption<float>("AutoBalance.MaxCCDurationModifier", 1.0f);
|
|
|
|
// LevelScaling.*
|
|
LevelScaling = sConfigMgr->GetOption<bool>("AutoBalance.LevelScaling", true);
|
|
|
|
std::string LevelScalingMethodString = sConfigMgr->GetOption<std::string>("AutoBalance.LevelScaling.Method", "dynamic", false);
|
|
if (LevelScalingMethodString == "fixed")
|
|
{
|
|
LevelScalingMethod = AUTOBALANCE_SCALING_FIXED;
|
|
}
|
|
else if (LevelScalingMethodString == "dynamic")
|
|
{
|
|
LevelScalingMethod = AUTOBALANCE_SCALING_DYNAMIC;
|
|
}
|
|
else
|
|
{
|
|
LOG_ERROR("server.loading", "mod-autobalance: invalid value `{}` for `AutoBalance.LevelScaling.Method` defined in `AutoBalance.conf`. Defaulting to a value of `dynamic`.", LevelScalingMethodString);
|
|
LevelScalingMethod = AUTOBALANCE_SCALING_DYNAMIC;
|
|
}
|
|
|
|
if (sConfigMgr->GetOption<float>("AutoBalance.LevelHigherOffset", false, false))
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.LevelHigherOffset` defined in `AutoBalance.conf`. This variable will be removed in a future release. Please see `AutoBalance.conf.dist` for more details.");
|
|
LevelScalingSkipHigherLevels = sConfigMgr->GetOption<uint8>("AutoBalance.LevelScaling.SkipHigherLevels", sConfigMgr->GetOption<uint32>("AutoBalance.LevelHigherOffset", 3, false), true);
|
|
if (sConfigMgr->GetOption<float>("AutoBalance.LevelLowerOffset", false, false))
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.LevelLowerOffset` defined in `AutoBalance.conf`. This variable will be removed in a future release. Please see `AutoBalance.conf.dist` for more details.");
|
|
LevelScalingSkipLowerLevels = sConfigMgr->GetOption<uint8>("AutoBalance.LevelScaling.SkipLowerLevels", sConfigMgr->GetOption<uint32>("AutoBalance.LevelLowerOffset", 5, false), true);
|
|
|
|
LevelScalingDynamicLevelCeilingDungeons = sConfigMgr->GetOption<uint8>("AutoBalance.LevelScaling.DynamicLevel.Ceiling.Dungeons", 1);
|
|
LevelScalingDynamicLevelFloorDungeons = sConfigMgr->GetOption<uint8>("AutoBalance.LevelScaling.DynamicLevel.Floor.Dungeons", 5);
|
|
LevelScalingDynamicLevelCeilingHeroicDungeons = sConfigMgr->GetOption<uint8>("AutoBalance.LevelScaling.DynamicLevel.Ceiling.HeroicDungeons", 2);
|
|
LevelScalingDynamicLevelFloorHeroicDungeons = sConfigMgr->GetOption<uint8>("AutoBalance.LevelScaling.DynamicLevel.Floor.HeroicDungeons", 5);
|
|
LevelScalingDynamicLevelCeilingRaids = sConfigMgr->GetOption<uint8>("AutoBalance.LevelScaling.DynamicLevel.Ceiling.Raids", 3);
|
|
LevelScalingDynamicLevelFloorRaids = sConfigMgr->GetOption<uint8>("AutoBalance.LevelScaling.DynamicLevel.Floor.Raids", 5);
|
|
LevelScalingDynamicLevelCeilingHeroicRaids = sConfigMgr->GetOption<uint8>("AutoBalance.LevelScaling.DynamicLevel.Ceiling.HeroicRaids", 3);
|
|
LevelScalingDynamicLevelFloorHeroicRaids = sConfigMgr->GetOption<uint8>("AutoBalance.LevelScaling.DynamicLevel.Floor.HeroicRaids", 5);
|
|
|
|
if (sConfigMgr->GetOption<float>("AutoBalance.LevelEndGameBoost", false, false))
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.LevelEndGameBoost` defined in `AutoBalance.conf`. This variable will be removed in a future release. Please see `AutoBalance.conf.dist` for more details.");
|
|
LevelScalingEndGameBoost = sConfigMgr->GetOption<bool>("AutoBalance.LevelScaling.EndGameBoost", sConfigMgr->GetOption<bool>("AutoBalance.LevelEndGameBoost", 1, false), true);
|
|
|
|
// RewardScaling.*
|
|
// warn the console if deprecated values are detected
|
|
if (sConfigMgr->GetOption<float>("AutoBalance.DungeonScaleDownXP", false, false))
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.DungeonScaleDownXP` defined in `AutoBalance.conf`. This variable will be removed in a future release. Please see `AutoBalance.conf.dist` for more details.");
|
|
if (sConfigMgr->GetOption<float>("AutoBalance.DungeonScaleDownMoney", false, false))
|
|
LOG_WARN("server.loading", "mod-autobalance: deprecated value `AutoBalance.DungeonScaleDownMoney` defined in `AutoBalance.conf`. This variable will be removed in a future release. Please see `AutoBalance.conf.dist` for more details.");
|
|
|
|
std::string RewardScalingMethodString = sConfigMgr->GetOption<std::string>("AutoBalance.RewardScaling.Method", "dynamic", false);
|
|
if (RewardScalingMethodString == "fixed")
|
|
{
|
|
RewardScalingMethod = AUTOBALANCE_SCALING_FIXED;
|
|
}
|
|
else if (RewardScalingMethodString == "dynamic")
|
|
{
|
|
RewardScalingMethod = AUTOBALANCE_SCALING_DYNAMIC;
|
|
}
|
|
else
|
|
{
|
|
LOG_ERROR("server.loading", "mod-autobalance: invalid value `{}` for `AutoBalance.RewardScaling.Method` defined in `AutoBalance.conf`. Defaulting to a value of `dynamic`.", RewardScalingMethodString);
|
|
RewardScalingMethod = AUTOBALANCE_SCALING_DYNAMIC;
|
|
}
|
|
|
|
RewardScalingXP = sConfigMgr->GetOption<bool>("AutoBalance.RewardScaling.XP", sConfigMgr->GetOption<bool>("AutoBalance.DungeonScaleDownXP", true, false));
|
|
RewardScalingXPModifier = sConfigMgr->GetOption<float>("AutoBalance.RewardScaling.XP.Modifier", 1.0f, false);
|
|
|
|
RewardScalingMoney = sConfigMgr->GetOption<bool>("AutoBalance.RewardScaling.Money", sConfigMgr->GetOption<bool>("AutoBalance.DungeonScaleDownMoney", true, false));
|
|
RewardScalingMoneyModifier = sConfigMgr->GetOption<float>("AutoBalance.RewardScaling.Money.Modifier", 1.0f, false);
|
|
|
|
// Announcement
|
|
Announcement = sConfigMgr->GetOption<bool>("AutoBalanceAnnounce.enable", true);
|
|
|
|
}
|
|
};
|
|
|
|
class AutoBalance_PlayerScript : public PlayerScript
|
|
{
|
|
public:
|
|
AutoBalance_PlayerScript()
|
|
: PlayerScript("AutoBalance_PlayerScript")
|
|
{
|
|
}
|
|
|
|
void OnLogin(Player *Player) override
|
|
{
|
|
if (EnableGlobal && Announcement) {
|
|
ChatHandler(Player->GetSession()).SendSysMessage("This server is running the |cff4CFF00AutoBalance |rmodule.");
|
|
}
|
|
}
|
|
|
|
virtual void OnLevelChanged(Player* player, uint8 oldlevel) override
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_PlayerScript::OnLevelChanged(): {} has leveled from {} to {}", player->GetName(), oldlevel, player->getLevel());
|
|
if (!player || player->IsGameMaster())
|
|
return;
|
|
|
|
Map* map = player->GetMap();
|
|
|
|
if (!map || !map->IsDungeon())
|
|
return;
|
|
|
|
// first update the map's player stats
|
|
UpdateMapPlayerStats(map);
|
|
|
|
// schedule all creatures for an update
|
|
lastConfigTime = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
}
|
|
|
|
void OnGiveXP(Player* player, uint32& amount, Unit* victim, uint8 /*xpSource*/) override
|
|
{
|
|
Map* map = player->GetMap();
|
|
|
|
// If this isn't a dungeon, make no changes
|
|
if (!map->IsDungeon() || !victim)
|
|
return;
|
|
|
|
AutoBalanceMapInfo *mapABInfo=map->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
|
|
if (victim && RewardScalingXP && mapABInfo->enabled)
|
|
{
|
|
Map* map = player->GetMap();
|
|
|
|
AutoBalanceCreatureInfo *creatureABInfo=victim->CustomData.GetDefault<AutoBalanceCreatureInfo>("AutoBalanceCreatureInfo");
|
|
|
|
if (map->IsDungeon())
|
|
{
|
|
if (RewardScalingMethod == AUTOBALANCE_SCALING_DYNAMIC)
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_PlayerScript::OnGiveXP(): Distributing XP from '{}' to '{}' in dynamic mode - {}->{}",
|
|
victim->GetName(), player->GetName(), amount, uint32(amount * creatureABInfo->XPModifier));
|
|
amount = uint32(amount * creatureABInfo->XPModifier);
|
|
}
|
|
else if (RewardScalingMethod == AUTOBALANCE_SCALING_FIXED)
|
|
{
|
|
// Ensure that the players always get the same XP, even when entering the dungeon alone
|
|
auto maxPlayerCount = ((InstanceMap*)sMapMgr->FindMap(map->GetId(), map->GetInstanceId()))->GetMaxPlayers();
|
|
auto currentPlayerCount = map->GetPlayersCountExceptGMs();
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_PlayerScript::OnGiveXP(): Distributing XP from '{}' to '{}' in fixed mode - {}->{}",
|
|
victim->GetName(), player->GetName(), amount, uint32(amount * creatureABInfo->XPModifier * ((float)currentPlayerCount / maxPlayerCount)));
|
|
amount = uint32(amount * creatureABInfo->XPModifier * ((float)currentPlayerCount / maxPlayerCount));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// void OnBeforeDropAddItem
|
|
void OnBeforeLootMoney(Player* player, Loot* loot) override
|
|
{
|
|
Map* map = player->GetMap();
|
|
|
|
// If this isn't a dungeon, make no changes
|
|
if (!map->IsDungeon())
|
|
return;
|
|
|
|
AutoBalanceMapInfo *mapABInfo=map->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
ObjectGuid sourceGuid = loot->sourceWorldObjectGUID;
|
|
|
|
if (mapABInfo->enabled && RewardScalingMoney)
|
|
{
|
|
// if the loot source is a creature, honor the modifiers for that creature
|
|
if (sourceGuid.IsCreature())
|
|
{
|
|
Creature* sourceCreature = ObjectAccessor::GetCreature(*player, sourceGuid);
|
|
AutoBalanceCreatureInfo *creatureABInfo=sourceCreature->CustomData.GetDefault<AutoBalanceCreatureInfo>("AutoBalanceCreatureInfo");
|
|
|
|
// Dynamic Mode
|
|
if (RewardScalingMethod == AUTOBALANCE_SCALING_DYNAMIC)
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_PlayerScript::OnBeforeLootMoney(): Distributing money from '{}' in dynamic mode - {}->{}",
|
|
sourceCreature->GetName(), loot->gold, uint32(loot->gold * creatureABInfo->MoneyModifier));
|
|
loot->gold = uint32(loot->gold * creatureABInfo->MoneyModifier);
|
|
}
|
|
// Fixed Mode
|
|
else if (RewardScalingMethod == AUTOBALANCE_SCALING_FIXED)
|
|
{
|
|
// Ensure that the players always get the same money, even when entering the dungeon alone
|
|
auto maxPlayerCount = ((InstanceMap*)sMapMgr->FindMap(map->GetId(), map->GetInstanceId()))->GetMaxPlayers();
|
|
auto currentPlayerCount = map->GetPlayersCountExceptGMs();
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_PlayerScript::OnBeforeLootMoney(): Distributing money from '{}' in fixed mode - {}->{}",
|
|
sourceCreature->GetName(), loot->gold, uint32(loot->gold * creatureABInfo->MoneyModifier * ((float)currentPlayerCount / maxPlayerCount)));
|
|
loot->gold = uint32(loot->gold * creatureABInfo->MoneyModifier * ((float)currentPlayerCount / maxPlayerCount));
|
|
}
|
|
}
|
|
// for all other loot sources, just distribute in Fixed mode as though the instance was full
|
|
else
|
|
{
|
|
auto maxPlayerCount = ((InstanceMap*)sMapMgr->FindMap(map->GetId(), map->GetInstanceId()))->GetMaxPlayers();
|
|
auto currentPlayerCount = map->GetPlayersCountExceptGMs();
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_PlayerScript::OnBeforeLootMoney(): Distributing money from a non-creature in fixed mode - {}->{}",
|
|
loot->gold, uint32(loot->gold * ((float)currentPlayerCount / maxPlayerCount)));
|
|
loot->gold = uint32(loot->gold * ((float)currentPlayerCount / maxPlayerCount));
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class AutoBalance_UnitScript : public UnitScript
|
|
{
|
|
public:
|
|
AutoBalance_UnitScript()
|
|
: UnitScript("AutoBalance_UnitScript", true)
|
|
{
|
|
}
|
|
|
|
uint32 DealDamage(Unit* AttackerUnit, Unit *playerVictim, uint32 damage, DamageEffectType /*damagetype*/) override
|
|
{
|
|
return _Modifer_DealDamage(playerVictim, AttackerUnit, damage);
|
|
}
|
|
|
|
void ModifyPeriodicDamageAurasTick(Unit* target, Unit* attacker, uint32& damage, SpellInfo const* /*spellInfo*/) override
|
|
{
|
|
damage = _Modifer_DealDamage(target, attacker, damage);
|
|
}
|
|
|
|
void ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage, SpellInfo const* /*spellInfo*/) override
|
|
{
|
|
damage = _Modifer_DealDamage(target, attacker, damage);
|
|
}
|
|
|
|
void ModifyMeleeDamage(Unit* target, Unit* attacker, uint32& damage) override
|
|
{
|
|
damage = _Modifer_DealDamage(target, attacker, damage);
|
|
}
|
|
|
|
void ModifyHealReceived(Unit* target, Unit* attacker, uint32& damage, SpellInfo const* /*spellInfo*/) override
|
|
{
|
|
damage = _Modifer_DealDamage(target, attacker, damage);
|
|
}
|
|
|
|
void OnAuraApply(Unit* unit, Aura* aura) override {
|
|
// Only if this aura has a duration
|
|
if (aura->GetDuration() > 0 || aura->GetMaxDuration() > 0)
|
|
{
|
|
uint32 auraDuration = _Modifier_CCDuration(unit, aura->GetCaster(), aura);
|
|
|
|
// only update if we decided to change it
|
|
if (auraDuration != (float)aura->GetDuration())
|
|
{
|
|
aura->SetMaxDuration(auraDuration);
|
|
aura->SetDuration(auraDuration);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32 _Modifer_DealDamage(Unit* target, Unit* attacker, uint32 damage)
|
|
{
|
|
// check that we're enabled globally, else return the original damage
|
|
if (!EnableGlobal)
|
|
return damage;
|
|
|
|
// make sure we have an attacker, that its not a player, and that the attacker is in the world, else return the original damage
|
|
if (!attacker || attacker->GetTypeId() == TYPEID_PLAYER || !attacker->IsInWorld())
|
|
return damage;
|
|
|
|
// make sure we're in an instance, else return the original damage
|
|
if (
|
|
!(
|
|
(target->GetMap()->IsDungeon() && attacker->GetMap()->IsDungeon()) ||
|
|
(target->GetMap()->IsRaid() && attacker->GetMap()->IsRaid())
|
|
)
|
|
)
|
|
return damage;
|
|
|
|
// get the map's info to see if we're enabled
|
|
AutoBalanceMapInfo *targetMapInfo = target->GetMap()->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
AutoBalanceMapInfo *attackerMapInfo = attacker->GetMap()->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
|
|
// if either the target or the attacker's maps are not enabled, return the original damage
|
|
if (!targetMapInfo->enabled || !attackerMapInfo->enabled)
|
|
return damage;
|
|
|
|
// get the current creature's damage multiplier
|
|
float damageMultiplier = attacker->CustomData.GetDefault<AutoBalanceCreatureInfo>("AutoBalanceCreatureInfo")->DamageMultiplier;
|
|
|
|
// if it's the default of 1.0, return the original damage
|
|
if (damageMultiplier == 1)
|
|
return damage;
|
|
|
|
// if the attacker is under the control of the player, return the original damage
|
|
if ((attacker->IsHunterPet() || attacker->IsPet() || attacker->IsSummon()) && attacker->IsControlledByPlayer())
|
|
return damage;
|
|
|
|
// we are good to go, return the original damage times the multiplier
|
|
return damage * damageMultiplier;
|
|
}
|
|
|
|
uint32 _Modifier_CCDuration(Unit* target, Unit* caster, Aura* aura)
|
|
{
|
|
// store the original duration of the aura
|
|
float originalDuration = (float)aura->GetDuration();
|
|
|
|
// check that we're enabled globally, else return the original duration
|
|
if (!EnableGlobal)
|
|
return originalDuration;
|
|
|
|
// ensure that both the target and the caster are defined
|
|
if (!target || !caster)
|
|
return originalDuration;
|
|
|
|
// if the aura wasn't cast just now, don't change it
|
|
if (aura->GetDuration() != aura->GetMaxDuration())
|
|
return originalDuration;
|
|
|
|
// if the target isn't a player or the caster is a player, return the original duration
|
|
if (!target->IsPlayer() || caster->IsPlayer())
|
|
return originalDuration;
|
|
|
|
// make sure we're in an instance, else return the original duration
|
|
if (
|
|
!(
|
|
(target->GetMap()->IsDungeon() && caster->GetMap()->IsDungeon()) ||
|
|
(target->GetMap()->IsRaid() && caster->GetMap()->IsRaid())
|
|
)
|
|
)
|
|
return originalDuration;
|
|
|
|
// get the current creature's CC duration multiplier
|
|
float ccDurationMultiplier = caster->CustomData.GetDefault<AutoBalanceCreatureInfo>("AutoBalanceCreatureInfo")->CCDurationMultiplier;
|
|
|
|
// if it's the default of 1.0, return the original damage
|
|
if (ccDurationMultiplier == 1)
|
|
return originalDuration;
|
|
|
|
// if the aura was cast by a pet or summon, return the original duration
|
|
if ((caster->IsHunterPet() || caster->IsPet() || caster->IsSummon()) && caster->IsControlledByPlayer())
|
|
return originalDuration;
|
|
|
|
// only if this aura is a CC
|
|
if (
|
|
aura->HasEffectType(SPELL_AURA_MOD_CHARM) ||
|
|
aura->HasEffectType(SPELL_AURA_MOD_CONFUSE) ||
|
|
aura->HasEffectType(SPELL_AURA_MOD_DISARM) ||
|
|
aura->HasEffectType(SPELL_AURA_MOD_FEAR) ||
|
|
aura->HasEffectType(SPELL_AURA_MOD_PACIFY) ||
|
|
aura->HasEffectType(SPELL_AURA_MOD_POSSESS) ||
|
|
aura->HasEffectType(SPELL_AURA_MOD_SILENCE) ||
|
|
aura->HasEffectType(SPELL_AURA_MOD_STUN) ||
|
|
aura->HasEffectType(SPELL_AURA_MOD_SPEED_SLOW_ALL)
|
|
)
|
|
{
|
|
return originalDuration * ccDurationMultiplier;
|
|
}
|
|
else
|
|
{
|
|
return originalDuration;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
class AutoBalance_AllMapScript : public AllMapScript
|
|
{
|
|
public:
|
|
AutoBalance_AllMapScript()
|
|
: AllMapScript("AutoBalance_AllMapScript")
|
|
{
|
|
}
|
|
|
|
void OnCreateMap(Map* map)
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllMapScript::OnCreateMap(): {}", map->GetMapName());
|
|
|
|
if (!map->IsDungeon() && !map->IsRaid())
|
|
return;
|
|
|
|
// get the map's info
|
|
AutoBalanceMapInfo *mapABInfo=map->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
|
|
// get the map's LFG stats
|
|
LFGDungeonEntry const* dungeon = GetLFGDungeon(map->GetId(), map->GetDifficulty());
|
|
if (dungeon) {
|
|
mapABInfo->lfgMinLevel = dungeon->MinLevel;
|
|
mapABInfo->lfgMaxLevel = dungeon->MaxLevel;
|
|
mapABInfo->lfgTargetLevel = dungeon->TargetLevel;
|
|
}
|
|
|
|
// load the map's settings
|
|
LoadMapSettings(map);
|
|
}
|
|
|
|
void OnPlayerEnterAll(Map* map, Player* player)
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllMapScript::OnPlayerEnterAll(): {}", map->GetMapName());
|
|
if (!map->IsDungeon() && !map->IsRaid())
|
|
return;
|
|
|
|
if (player->IsGameMaster())
|
|
return;
|
|
|
|
// get the map's info
|
|
AutoBalanceMapInfo *mapABInfo=map->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
|
|
// recalculate the zone's level stats
|
|
mapABInfo->highestCreatureLevel = 0;
|
|
mapABInfo->lowestCreatureLevel = 0;
|
|
mapABInfo->avgCreatureLevel = 0;
|
|
mapABInfo->activeCreatureCount = 0;
|
|
|
|
// see which existing creatures are active
|
|
for (std::vector<Creature*>::iterator creatureIterator = mapABInfo->allMapCreatures.begin(); creatureIterator != mapABInfo->allMapCreatures.end(); ++creatureIterator)
|
|
{
|
|
AddCreatureToMapData(*creatureIterator, false, nullptr, true);
|
|
}
|
|
|
|
// determine if the map should be enabled for scaling based on the current settings
|
|
mapABInfo->enabled = ShouldMapBeEnabled(map);
|
|
|
|
// updates the player count, player levels for the map
|
|
UpdateMapPlayerStats(map);
|
|
|
|
if (PlayerChangeNotify && EnableGlobal && mapABInfo->enabled)
|
|
{
|
|
if (map->GetEntry()->IsDungeon() && player)
|
|
{
|
|
Map::PlayerList const &playerList = map->GetPlayers();
|
|
if (!playerList.IsEmpty())
|
|
{
|
|
for (Map::PlayerList::const_iterator playerIteration = playerList.begin(); playerIteration != playerList.end(); ++playerIteration)
|
|
{
|
|
if (Player* playerHandle = playerIteration->GetSource())
|
|
{
|
|
ChatHandler chatHandle = ChatHandler(playerHandle->GetSession());
|
|
auto instanceMap = ((InstanceMap*)sMapMgr->FindMap(map->GetId(), map->GetInstanceId()));
|
|
|
|
std::string instanceDifficulty; if (instanceMap->IsHeroic()) instanceDifficulty = "Heroic"; else instanceDifficulty = "Normal";
|
|
|
|
chatHandle.PSendSysMessage("|cffFF0000 [AutoBalance]|r|cffFF8000 %s enters %s (%u-player %s). Player count set to %u (Player Difficulty Offset = %u) |r",
|
|
player->GetName().c_str(),
|
|
map->GetMapName(),
|
|
instanceMap->GetMaxPlayers(),
|
|
instanceDifficulty,
|
|
mapABInfo->playerCount + PlayerCountDifficultyOffset,
|
|
PlayerCountDifficultyOffset
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnPlayerLeaveAll(Map* map, Player* player)
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllMapScript::OnPlayerLeaveAll(): {}", map->GetMapName());
|
|
if (!EnableGlobal)
|
|
return;
|
|
|
|
// get the map's info
|
|
AutoBalanceMapInfo *mapABInfo=map->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
|
|
// recalculate the zone's level stats
|
|
mapABInfo->highestCreatureLevel = 0;
|
|
mapABInfo->lowestCreatureLevel = 0;
|
|
mapABInfo->avgCreatureLevel = 0;
|
|
mapABInfo->activeCreatureCount = 0;
|
|
|
|
// see which existing creatures are active
|
|
for (std::vector<Creature*>::iterator creatureIterator = mapABInfo->allMapCreatures.begin(); creatureIterator != mapABInfo->allMapCreatures.end(); ++creatureIterator)
|
|
{
|
|
AddCreatureToMapData(*creatureIterator, false, player, true);
|
|
}
|
|
|
|
// determine if the map should be enabled for scaling based on the current settings
|
|
mapABInfo->enabled = ShouldMapBeEnabled(map);
|
|
|
|
bool areAnyPlayersInCombat = false;
|
|
|
|
// updates the player count and levels for the map
|
|
if (map->GetEntry() && map->GetEntry()->IsDungeon())
|
|
{
|
|
// determine if any players in the map are in combat
|
|
// if so, do not adjust the player count
|
|
Map::PlayerList const& mapPlayerList = map->GetPlayers();
|
|
for (Map::PlayerList::const_iterator itr = mapPlayerList.begin(); itr != mapPlayerList.end(); ++itr)
|
|
{
|
|
if (Player* mapPlayer = itr->GetSource())
|
|
{
|
|
if (mapPlayer->IsInCombat() && mapPlayer->GetMap() == map)
|
|
{
|
|
areAnyPlayersInCombat = true;
|
|
|
|
// notify the player that they left the instance while combat was in progress
|
|
ChatHandler chatHandle = ChatHandler(player->GetSession());
|
|
chatHandle.PSendSysMessage("|cffFF0000 [AutoBalance]|r|cffFF8000 You left the instance while combat was in progress. The instance player count is still %u.", mapABInfo->playerCount);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (areAnyPlayersInCombat)
|
|
{
|
|
for (Map::PlayerList::const_iterator itr = mapPlayerList.begin(); itr != mapPlayerList.end(); ++itr)
|
|
{
|
|
if (Player* mapPlayer = itr->GetSource())
|
|
{
|
|
// only for the players who are in the instance and did not leave
|
|
if (mapPlayer != player)
|
|
{
|
|
ChatHandler chatHandle = ChatHandler(mapPlayer->GetSession());
|
|
chatHandle.PSendSysMessage("|cffFF0000 [AutoBalance]|r|cffFF8000 %s left the instance while combat was in progress. The instance player count is still %u.", player->GetName().c_str(), mapABInfo->playerCount);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mapABInfo->playerCount = map->GetPlayersCountExceptGMs() - 1;
|
|
}
|
|
}
|
|
|
|
if (PlayerChangeNotify && !player->IsGameMaster() && !areAnyPlayersInCombat && EnableGlobal && mapABInfo->enabled)
|
|
{
|
|
if (map->GetEntry()->IsDungeon() && player)
|
|
{
|
|
Map::PlayerList const &playerList = map->GetPlayers();
|
|
if (!playerList.IsEmpty())
|
|
{
|
|
for (Map::PlayerList::const_iterator playerIteration = playerList.begin(); playerIteration != playerList.end(); ++playerIteration)
|
|
{
|
|
Player* mapPlayer = playerIteration->GetSource();
|
|
if (mapPlayer && mapPlayer != player)
|
|
{
|
|
ChatHandler chatHandle = ChatHandler(mapPlayer->GetSession());
|
|
chatHandle.PSendSysMessage("|cffFF0000 [AutoBalance]|r|cffFF8000 %s left the instance. Player count set to %u (Player Difficulty Offset = %u) |r", player->GetName().c_str(), mapABInfo->playerCount, PlayerCountDifficultyOffset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class AutoBalance_AllCreatureScript : public AllCreatureScript
|
|
{
|
|
public:
|
|
AutoBalance_AllCreatureScript()
|
|
: AllCreatureScript("AutoBalance_AllCreatureScript")
|
|
{
|
|
}
|
|
|
|
void Creature_SelectLevel(const CreatureTemplate* /*creatureTemplate*/, Creature* creature) override
|
|
{
|
|
if (creature->GetMap()->IsDungeon())
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreatureScript::Creature_SelectLevel(): {} ({})", creature->GetName(), creature->GetLevel());
|
|
|
|
// add the creature to the map's tracking list
|
|
AddCreatureToMapData(creature);
|
|
|
|
// do an initial modification of the creature
|
|
ModifyCreatureAttributes(creature);
|
|
|
|
}
|
|
|
|
void OnCreatureAddWorld(Creature* creature) override
|
|
{
|
|
if (creature->GetMap()->IsDungeon())
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreatureScript::OnCreatureAddWorld(): {} ({})", creature->GetName(), creature->GetLevel());
|
|
}
|
|
|
|
void OnCreatureRemoveWorld(Creature* creature) override
|
|
{
|
|
if (creature->GetMap()->IsDungeon())
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreatureScript::OnCreatureRemoveWorld(): {} ({})", creature->GetName(), creature->GetLevel());
|
|
|
|
// remove the creature from the map's tracking list, if present
|
|
RemoveCreatureFromMapData(creature);
|
|
}
|
|
|
|
void OnAllCreatureUpdate(Creature* creature, uint32 /*diff*/) override
|
|
{
|
|
// If the config is out of date and the creature was reset, run modify against it
|
|
if (ResetCreatureIfNeeded(creature))
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreatureScript::OnAllCreatureUpdate(): Creature {} ({}) is reset to its original stats.", creature->GetName(), creature->GetLevel());
|
|
|
|
// Update the map's level if it is out of date
|
|
UpdateMapLevelIfNeeded(creature->GetMap());
|
|
|
|
ModifyCreatureAttributes(creature);
|
|
}
|
|
}
|
|
|
|
// Reset the passed creature to stock if the config has changed
|
|
bool ResetCreatureIfNeeded(Creature* creature)
|
|
{
|
|
// make sure we have a creature and that it's assigned to a map
|
|
if (!creature || !creature->GetMap())
|
|
return false;
|
|
|
|
|
|
// if this isn't a dungeon or a battleground, make no changes
|
|
if (!(creature->GetMap()->IsDungeon() || creature->GetMap()->IsRaid()))
|
|
return false;
|
|
|
|
// if this is a pet or summon controlled by the player, make no changes
|
|
if ((creature->IsHunterPet() || creature->IsPet() || creature->IsSummon()) && creature->IsControlledByPlayer())
|
|
return false;
|
|
|
|
// if this is a non-relevant creature, skip
|
|
if (creature->IsCritter() || creature->IsTotem() || creature->IsTrigger())
|
|
return false;
|
|
|
|
// get (or create) the creature and map's info
|
|
AutoBalanceCreatureInfo *creatureABInfo=creature->CustomData.GetDefault<AutoBalanceCreatureInfo>("AutoBalanceCreatureInfo");
|
|
AutoBalanceMapInfo *mapABInfo=creature->GetMap()->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
|
|
// if this creature is below 85% of the minimum level for the map, make no changes
|
|
if (creatureABInfo->UnmodifiedLevel < (float)mapABInfo->lfgMinLevel * .85f)
|
|
{
|
|
if (creatureABInfo->configTime == 0)
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreatureScript::ResetCreatureIfNeeded(): {} ({}) is below 85% of the LFG min level for the map, do not reset or modify.", creature->GetName(), creatureABInfo->UnmodifiedLevel);
|
|
|
|
creatureABInfo->configTime = lastConfigTime;
|
|
return false;
|
|
}
|
|
|
|
// if this creature is above 115% of the maximum level for the map, make no changes
|
|
if (creatureABInfo->UnmodifiedLevel > (float)mapABInfo->lfgMaxLevel * 1.15f)
|
|
{
|
|
if (creatureABInfo->configTime == 0)
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreatureScript::ResetCreatureIfNeeded(): {} ({}) is above 115% of the LFG max level for the map, do not reset or modify.", creature->GetName(), creatureABInfo->UnmodifiedLevel);
|
|
|
|
creatureABInfo->configTime = lastConfigTime;
|
|
return false;
|
|
}
|
|
|
|
// if creature is dead and configTime is 0, skip
|
|
if (creature->isDead() && creatureABInfo->configTime == 0)
|
|
{
|
|
return false;
|
|
}
|
|
// if the creature is dead but configTime is NOT 0, we set it to 0 so that it will be recalculated if revived
|
|
// also remember that this creature was once alive but is now dead
|
|
else if (creature->isDead())
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreatureScript::ResetCreatureIfNeeded(): {} ({}) is dead and configTime is not 0 - prime for reset if revived.", creature->GetName(), creature->GetLevel());
|
|
creatureABInfo->configTime = 0;
|
|
creatureABInfo->wasAliveNowDead = true;
|
|
return false;
|
|
}
|
|
|
|
// if the config is outdated, reset the creature
|
|
if (creatureABInfo->configTime != lastConfigTime)
|
|
{
|
|
// before updating the creature, we should update the map level if needed
|
|
UpdateMapLevelIfNeeded(creature->GetMap());
|
|
|
|
// retain some values
|
|
uint8 unmodifiedLevel = creatureABInfo->UnmodifiedLevel;
|
|
bool isActive = creatureABInfo->isActive;
|
|
bool wasAliveNowDead = creatureABInfo->wasAliveNowDead;
|
|
bool isInCreatureList = creatureABInfo->isInCreatureList;
|
|
|
|
// reset AutoBalance modifiers
|
|
creature->CustomData.Erase("AutoBalanceCreatureInfo");
|
|
AutoBalanceCreatureInfo *creatureABInfo=creature->CustomData.GetDefault<AutoBalanceCreatureInfo>("AutoBalanceCreatureInfo");
|
|
|
|
// grab the creature's template and the original creature's stats
|
|
CreatureTemplate const* creatureTemplate = creature->GetCreatureTemplate();
|
|
|
|
// set the creature's level
|
|
creature->SetLevel(unmodifiedLevel);
|
|
creatureABInfo->UnmodifiedLevel = unmodifiedLevel;
|
|
|
|
// get the creature's base stats
|
|
CreatureBaseStats const* origCreatureStats = sObjectMgr->GetCreatureBaseStats(unmodifiedLevel, creatureTemplate->unit_class);
|
|
|
|
// health
|
|
float currentHealthPercent = (float)creature->GetHealth() / (float)creature->GetMaxHealth();
|
|
creature->SetMaxHealth(origCreatureStats->GenerateHealth(creatureTemplate));
|
|
creature->SetHealth((float)origCreatureStats->GenerateHealth(creatureTemplate) * currentHealthPercent);
|
|
|
|
// mana
|
|
if (creature->getPowerType() == POWER_MANA && creature->GetPower(POWER_MANA) >= 0 && creature->GetMaxPower(POWER_MANA) > 0)
|
|
{
|
|
float currentManaPercent = creature->GetPower(POWER_MANA) / creature->GetMaxPower(POWER_MANA);
|
|
creature->SetMaxPower(POWER_MANA, origCreatureStats->GenerateMana(creatureTemplate));
|
|
creature->SetPower(POWER_MANA, creature->GetMaxPower(POWER_MANA) * currentManaPercent);
|
|
}
|
|
|
|
// armor
|
|
creature->SetArmor(origCreatureStats->GenerateArmor(creatureTemplate));
|
|
|
|
// restore the saved data
|
|
creatureABInfo->isActive = isActive;
|
|
creatureABInfo->wasAliveNowDead = wasAliveNowDead;
|
|
creatureABInfo->isInCreatureList = isInCreatureList;
|
|
|
|
// damage and ccduration are handled using AutoBalanceCreatureInfo data only
|
|
|
|
// return true to indicate that the creature was reset
|
|
return true;
|
|
}
|
|
|
|
// creature was not reset, return false
|
|
return false;
|
|
|
|
}
|
|
|
|
void ModifyCreatureAttributes(Creature* creature)
|
|
{
|
|
// make sure we have a creature and that it's assigned to a map
|
|
if (!creature || !creature->GetMap())
|
|
return;
|
|
|
|
// if this isn't a dungeon or a battleground, make no changes
|
|
if (!(creature->GetMap()->IsDungeon() || creature->GetMap()->IsRaid()))
|
|
return;
|
|
|
|
// if this is a pet or summon controlled by the player, make no changes
|
|
if (((creature->IsHunterPet() || creature->IsPet() || creature->IsSummon()) && creature->IsControlledByPlayer()))
|
|
return;
|
|
|
|
// if this is a non-relevant creature, make no changes
|
|
if (creature->IsCritter() || creature->IsTotem() || creature->IsTrigger())
|
|
return;
|
|
|
|
// grab creature and map data
|
|
AutoBalanceCreatureInfo *creatureABInfo=creature->CustomData.GetDefault<AutoBalanceCreatureInfo>("AutoBalanceCreatureInfo");
|
|
AutoBalanceMapInfo *mapABInfo=creature->GetMap()->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
|
|
// mark the creature as updated using the current settings if needed
|
|
if (creatureABInfo->configTime != lastConfigTime)
|
|
creatureABInfo->configTime = lastConfigTime;
|
|
|
|
// check to make sure that the creature's map is enabled for scaling
|
|
if (!mapABInfo->enabled || !EnableGlobal)
|
|
return;
|
|
|
|
// if this creature is below 85% of the minimum LFG level for the map, make no changes
|
|
if (creatureABInfo->UnmodifiedLevel < (float)mapABInfo->lfgMinLevel * .85f)
|
|
return;
|
|
|
|
// if this creature is above 115% of the maximum LFG level for the map, make no changes
|
|
if (creatureABInfo->UnmodifiedLevel > (float)mapABInfo->lfgMaxLevel * 1.15f)
|
|
return;
|
|
|
|
// if the creature was dead (but this function is being called because they are being revived), reset it and allow modifications
|
|
if (creatureABInfo->wasAliveNowDead)
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreatureScript::ModifyCreatureAttributes(): {} ({}) was dead but appears to be alive now, reset wasAliveNowDead flag.", creature->GetName(), creatureABInfo->UnmodifiedLevel);
|
|
// if the creature was dead, reset it
|
|
creatureABInfo->wasAliveNowDead = false;
|
|
}
|
|
// if the creature is dead and wasn't marked as dead by this script, simply skip
|
|
else if (creature->isDead())
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreatureScript::ModifyCreatureAttributes(): {} ({}) is dead, do not modify.", creature->GetName(), creatureABInfo->UnmodifiedLevel);
|
|
return;
|
|
}
|
|
|
|
CreatureTemplate const *creatureTemplate = creature->GetCreatureTemplate();
|
|
|
|
InstanceMap* instanceMap = ((InstanceMap*)sMapMgr->FindMap(creature->GetMapId(), creature->GetInstanceId()));
|
|
uint32 mapId = instanceMap->GetEntry()->MapID;
|
|
if (perDungeonScalingEnabled() && !isEnabledDungeon(mapId))
|
|
{
|
|
return;
|
|
}
|
|
uint32 maxNumberOfPlayers = instanceMap->GetMaxPlayers();
|
|
int forcedNumPlayers = GetForcedNumPlayers(creatureTemplate->Entry);
|
|
|
|
if (forcedNumPlayers > 0)
|
|
maxNumberOfPlayers = forcedNumPlayers; // Force maxNumberOfPlayers to be changed to match the Configuration entries ForcedID2, ForcedID5, ForcedID10, ForcedID20, ForcedID25, ForcedID40
|
|
else if (forcedNumPlayers == 0)
|
|
return; // forcedNumPlayers 0 means that the creature is contained in DisabledID -> no scaling
|
|
|
|
uint32 curCount=mapABInfo->playerCount + PlayerCountDifficultyOffset;
|
|
if (perDungeonScalingEnabled())
|
|
{
|
|
curCount = adjustCurCount(curCount, mapId);
|
|
}
|
|
creatureABInfo->instancePlayerCount = curCount;
|
|
|
|
if (!creatureABInfo->instancePlayerCount) // no players in map, do not modify attributes
|
|
return;
|
|
|
|
if (!sABScriptMgr->OnBeforeModifyAttributes(creature, creatureABInfo->instancePlayerCount))
|
|
return;
|
|
|
|
// only scale levels if level scaling is enabled and the instance's average creature level is not within the skip range
|
|
if (LevelScaling &&
|
|
((mapABInfo->avgCreatureLevel > mapABInfo->highestPlayerLevel + mapABInfo->levelScalingSkipHigherLevels || mapABInfo->levelScalingSkipHigherLevels == 0) ||
|
|
(mapABInfo->avgCreatureLevel < mapABInfo->highestPlayerLevel - mapABInfo->levelScalingSkipLowerLevels || mapABInfo->levelScalingSkipLowerLevels == 0))
|
|
)
|
|
{
|
|
uint8 selectedLevel;
|
|
|
|
// if we're using dynamic scaling, calculate the creature's level based relative to the highest player level in the map
|
|
if (LevelScalingMethod == AUTOBALANCE_SCALING_DYNAMIC)
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreatureScript::ModifyCreatureAttributes: Creature {} ({}) dynamic scaling floor: {}, ceiling: {}.", creature->GetName(), creatureABInfo->UnmodifiedLevel, mapABInfo->levelScalingDynamicFloor, mapABInfo->levelScalingDynamicCeiling);
|
|
|
|
// calculate the creature's new level
|
|
selectedLevel = (mapABInfo->highestPlayerLevel + mapABInfo->levelScalingDynamicCeiling) - (mapABInfo->highestCreatureLevel - creatureABInfo->UnmodifiedLevel);
|
|
|
|
// check to be sure that the creature's new level is at least the dynamic scaling floor
|
|
if (selectedLevel < (mapABInfo->highestPlayerLevel - mapABInfo->levelScalingDynamicFloor))
|
|
{
|
|
selectedLevel = mapABInfo->highestPlayerLevel - mapABInfo->levelScalingDynamicFloor;
|
|
}
|
|
|
|
// check to be sure that the creature's new level is no higher than the dynamic scaling ceiling
|
|
if (selectedLevel > (mapABInfo->highestPlayerLevel + mapABInfo->levelScalingDynamicCeiling))
|
|
{
|
|
selectedLevel = mapABInfo->highestPlayerLevel + mapABInfo->levelScalingDynamicCeiling;
|
|
}
|
|
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreatureScript::ModifyCreatureAttributes: Creature {} ({}) scaled to {} via dynamic scaling.", creature->GetName(), creatureABInfo->UnmodifiedLevel, selectedLevel);
|
|
}
|
|
// otherwise we're using "fixed" scaling and should use the highest player level in the map
|
|
else
|
|
{
|
|
selectedLevel = mapABInfo->highestPlayerLevel;
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreatureScript::ModifyCreatureAttributes: Creature {} ({}) scaled to {} via fixed scaling.", creature->GetName(), creatureABInfo->UnmodifiedLevel, selectedLevel);
|
|
}
|
|
|
|
creatureABInfo->selectedLevel = selectedLevel;
|
|
creature->SetLevel(creatureABInfo->selectedLevel);
|
|
}
|
|
else
|
|
{
|
|
LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreatureScript::ModifyCreatureAttributes: Creature {} ({}) not level scaled due to level scaling being disabled or the instance's average creature level being outside the skip range.", creature->GetName(), creatureABInfo->UnmodifiedLevel);
|
|
creatureABInfo->selectedLevel = creatureABInfo->UnmodifiedLevel;
|
|
}
|
|
|
|
creatureABInfo->entry = creature->GetEntry();
|
|
|
|
CreatureBaseStats const* origCreatureStats = sObjectMgr->GetCreatureBaseStats(creatureABInfo->UnmodifiedLevel, creatureTemplate->unit_class);
|
|
CreatureBaseStats const* creatureStats = sObjectMgr->GetCreatureBaseStats(creatureABInfo->selectedLevel, creatureTemplate->unit_class);
|
|
|
|
uint32 baseMana = origCreatureStats->GenerateMana(creatureTemplate);
|
|
uint32 scaledHealth = 0;
|
|
uint32 scaledMana = 0;
|
|
|
|
// Note: InflectionPoint handle the number of players required to get 50% health.
|
|
// you'd adjust this to raise or lower the hp modifier for per additional player in a non-whole group.
|
|
//
|
|
// diff modify the rate of percentage increase between
|
|
// number of players. Generally the closer to the value of 1 you have this
|
|
// the less gradual the rate will be. For example in a 5 man it would take 3
|
|
// total players to face a mob at full health.
|
|
//
|
|
// The +1 and /2 values raise the TanH function to a positive range and make
|
|
// sure the modifier never goes above the value or 1.0 or below 0.
|
|
//
|
|
// curveFloor and curveCeiling squishes the curve by adjusting the curve start and end points.
|
|
// This allows for better control over high and low player count scaling.
|
|
|
|
float defaultMultiplier;
|
|
float curveFloor;
|
|
float curveCeiling;
|
|
|
|
//
|
|
// Inflection Point
|
|
//
|
|
float inflectionValue = (float)maxNumberOfPlayers;
|
|
|
|
if (instanceMap->IsHeroic())
|
|
{
|
|
switch (maxNumberOfPlayers)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
inflectionValue *= InflectionPointHeroic;
|
|
curveFloor = InflectionPointHeroicCurveFloor;
|
|
curveCeiling = InflectionPointHeroicCurveCeiling;
|
|
break;
|
|
case 10:
|
|
inflectionValue *= InflectionPointRaid10MHeroic;
|
|
curveFloor = InflectionPointRaid10MHeroicCurveFloor;
|
|
curveCeiling = InflectionPointRaid10MHeroicCurveCeiling;
|
|
break;
|
|
case 25:
|
|
inflectionValue *= InflectionPointRaid25MHeroic;
|
|
curveFloor = InflectionPointRaid25MHeroicCurveFloor;
|
|
curveCeiling = InflectionPointRaid25MHeroicCurveCeiling;
|
|
break;
|
|
default:
|
|
inflectionValue *= InflectionPointRaidHeroic;
|
|
curveFloor = InflectionPointRaidHeroicCurveFloor;
|
|
curveCeiling = InflectionPointRaidHeroicCurveCeiling;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (maxNumberOfPlayers)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
inflectionValue *= InflectionPoint;
|
|
curveFloor = InflectionPointCurveFloor;
|
|
curveCeiling = InflectionPointCurveCeiling;
|
|
break;
|
|
case 10:
|
|
inflectionValue *= InflectionPointRaid10M;
|
|
curveFloor = InflectionPointRaid10MCurveFloor;
|
|
curveCeiling = InflectionPointRaid10MCurveCeiling;
|
|
break;
|
|
case 15:
|
|
inflectionValue *= InflectionPointRaid15M;
|
|
curveFloor = InflectionPointRaid15MCurveFloor;
|
|
curveCeiling = InflectionPointRaid15MCurveCeiling;
|
|
break;
|
|
case 20:
|
|
inflectionValue *= InflectionPointRaid20M;
|
|
curveFloor = InflectionPointRaid20MCurveFloor;
|
|
curveCeiling = InflectionPointRaid20MCurveCeiling;
|
|
break;
|
|
case 25:
|
|
inflectionValue *= InflectionPointRaid25M;
|
|
curveFloor = InflectionPointRaid25MCurveFloor;
|
|
curveCeiling = InflectionPointRaid25MCurveCeiling;
|
|
break;
|
|
case 40:
|
|
inflectionValue *= InflectionPointRaid40M;
|
|
curveFloor = InflectionPointRaid40MCurveFloor;
|
|
curveCeiling = InflectionPointRaid40MCurveCeiling;
|
|
break;
|
|
default:
|
|
inflectionValue *= InflectionPointRaid;
|
|
curveFloor = InflectionPointRaidCurveFloor;
|
|
curveCeiling = InflectionPointRaidCurveCeiling;
|
|
}
|
|
}
|
|
|
|
// Per map ID overrides alter the above settings, if set
|
|
if (hasDungeonOverride(mapId))
|
|
{
|
|
AutoBalanceInflectionPointSettings* myInflectionPointOverrides = &dungeonOverrides[mapId];
|
|
|
|
// Alter the inflectionValue according to the override, if set
|
|
if (myInflectionPointOverrides->value != -1)
|
|
{
|
|
inflectionValue = (float)maxNumberOfPlayers; // Starting over
|
|
inflectionValue *= myInflectionPointOverrides->value;
|
|
}
|
|
|
|
if (myInflectionPointOverrides->curveFloor != -1) { curveFloor = myInflectionPointOverrides->curveFloor; }
|
|
if (myInflectionPointOverrides->curveCeiling != -1) { curveCeiling = myInflectionPointOverrides->curveCeiling; }
|
|
}
|
|
|
|
//
|
|
// Boss Inflection Point
|
|
//
|
|
if (creature->IsDungeonBoss()) {
|
|
|
|
float bossInflectionPointMultiplier;
|
|
|
|
// Determine the correct boss inflection multiplier
|
|
if (instanceMap->IsHeroic())
|
|
{
|
|
switch (maxNumberOfPlayers)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
bossInflectionPointMultiplier = InflectionPointHeroicBoss;
|
|
break;
|
|
case 10:
|
|
bossInflectionPointMultiplier = InflectionPointRaid10MHeroicBoss;
|
|
break;
|
|
case 25:
|
|
bossInflectionPointMultiplier = InflectionPointRaid25MHeroicBoss;
|
|
break;
|
|
default:
|
|
bossInflectionPointMultiplier = InflectionPointRaidHeroicBoss;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (maxNumberOfPlayers)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
bossInflectionPointMultiplier = InflectionPointBoss;
|
|
break;
|
|
case 10:
|
|
bossInflectionPointMultiplier = InflectionPointRaid10MBoss;
|
|
break;
|
|
case 15:
|
|
bossInflectionPointMultiplier = InflectionPointRaid15MBoss;
|
|
break;
|
|
case 20:
|
|
bossInflectionPointMultiplier = InflectionPointRaid20MBoss;
|
|
break;
|
|
case 25:
|
|
bossInflectionPointMultiplier = InflectionPointRaid25MBoss;
|
|
break;
|
|
case 40:
|
|
bossInflectionPointMultiplier = InflectionPointRaid40MBoss;
|
|
break;
|
|
default:
|
|
bossInflectionPointMultiplier = InflectionPointRaidBoss;
|
|
}
|
|
}
|
|
|
|
// Per map ID overrides alter the above settings, if set
|
|
if (hasBossOverride(mapId))
|
|
{
|
|
AutoBalanceInflectionPointSettings* myBossOverrides = &bossOverrides[mapId];
|
|
|
|
// If set, alter the inflectionValue according to the override
|
|
if (myBossOverrides->value != -1)
|
|
{
|
|
inflectionValue *= myBossOverrides->value;
|
|
}
|
|
// Otherwise, calculate using the value determined by instance type
|
|
else
|
|
{
|
|
inflectionValue *= bossInflectionPointMultiplier;
|
|
}
|
|
}
|
|
// No override, use the value determined by the instance type
|
|
else
|
|
{
|
|
inflectionValue *= bossInflectionPointMultiplier;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Stat Modifiers
|
|
//
|
|
|
|
// Calculate stat modifiers
|
|
float statMod_global, statMod_health, statMod_mana, statMod_armor, statMod_damage, statMod_ccDuration;
|
|
float statMod_boss_global, statMod_boss_health, statMod_boss_mana, statMod_boss_armor, statMod_boss_damage, statMod_boss_ccDuration;
|
|
|
|
// Apply the per-instance-type modifiers first
|
|
if (instanceMap->IsHeroic())
|
|
{
|
|
switch (maxNumberOfPlayers)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
statMod_global = StatModifierHeroic_Global;
|
|
statMod_health = StatModifierHeroic_Health;
|
|
statMod_mana = StatModifierHeroic_Mana;
|
|
statMod_armor = StatModifierHeroic_Armor;
|
|
statMod_damage = StatModifierHeroic_Damage;
|
|
statMod_ccDuration = StatModifierHeroic_CCDuration;
|
|
|
|
statMod_boss_global = StatModifierHeroic_Boss_Global;
|
|
statMod_boss_health = StatModifierHeroic_Boss_Health;
|
|
statMod_boss_mana = StatModifierHeroic_Boss_Mana;
|
|
statMod_boss_armor = StatModifierHeroic_Boss_Armor;
|
|
statMod_boss_damage = StatModifierHeroic_Boss_Damage;
|
|
statMod_boss_ccDuration = StatModifierHeroic_Boss_CCDuration;
|
|
break;
|
|
case 10:
|
|
statMod_global = StatModifierRaid10MHeroic_Global;
|
|
statMod_health = StatModifierRaid10MHeroic_Health;
|
|
statMod_mana = StatModifierRaid10MHeroic_Mana;
|
|
statMod_armor = StatModifierRaid10MHeroic_Armor;
|
|
statMod_damage = StatModifierRaid10MHeroic_Damage;
|
|
statMod_ccDuration = StatModifierRaid10MHeroic_CCDuration;
|
|
|
|
statMod_boss_global = StatModifierRaid10MHeroic_Boss_Global;
|
|
statMod_boss_health = StatModifierRaid10MHeroic_Boss_Health;
|
|
statMod_boss_mana = StatModifierRaid10MHeroic_Boss_Mana;
|
|
statMod_boss_armor = StatModifierRaid10MHeroic_Boss_Armor;
|
|
statMod_boss_damage = StatModifierRaid10MHeroic_Boss_Damage;
|
|
statMod_boss_ccDuration = StatModifierRaid10MHeroic_Boss_CCDuration;
|
|
break;
|
|
case 25:
|
|
statMod_global = StatModifierRaid25MHeroic_Global;
|
|
statMod_health = StatModifierRaid25MHeroic_Health;
|
|
statMod_mana = StatModifierRaid25MHeroic_Mana;
|
|
statMod_armor = StatModifierRaid25MHeroic_Armor;
|
|
statMod_damage = StatModifierRaid25MHeroic_Damage;
|
|
statMod_ccDuration = StatModifierRaid25MHeroic_CCDuration;
|
|
|
|
statMod_boss_global = StatModifierRaid25MHeroic_Boss_Global;
|
|
statMod_boss_health = StatModifierRaid25MHeroic_Boss_Health;
|
|
statMod_boss_mana = StatModifierRaid25MHeroic_Boss_Mana;
|
|
statMod_boss_armor = StatModifierRaid25MHeroic_Boss_Armor;
|
|
statMod_boss_damage = StatModifierRaid25MHeroic_Boss_Damage;
|
|
statMod_boss_ccDuration = StatModifierRaid25MHeroic_Boss_CCDuration;
|
|
break;
|
|
default:
|
|
statMod_global = StatModifierRaidHeroic_Global;
|
|
statMod_health = StatModifierRaidHeroic_Health;
|
|
statMod_mana = StatModifierRaidHeroic_Mana;
|
|
statMod_armor = StatModifierRaidHeroic_Armor;
|
|
statMod_damage = StatModifierRaidHeroic_Damage;
|
|
statMod_ccDuration = StatModifierRaidHeroic_CCDuration;
|
|
|
|
statMod_boss_global = StatModifierRaidHeroic_Global;
|
|
statMod_boss_health = StatModifierRaidHeroic_Health;
|
|
statMod_boss_mana = StatModifierRaidHeroic_Mana;
|
|
statMod_boss_armor = StatModifierRaidHeroic_Armor;
|
|
statMod_boss_damage = StatModifierRaidHeroic_Damage;
|
|
statMod_boss_ccDuration = StatModifierRaidHeroic_Boss_CCDuration;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (maxNumberOfPlayers)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
statMod_global = StatModifier_Global;
|
|
statMod_health = StatModifier_Health;
|
|
statMod_mana = StatModifier_Mana;
|
|
statMod_armor = StatModifier_Armor;
|
|
statMod_damage = StatModifier_Damage;
|
|
statMod_ccDuration = StatModifier_CCDuration;
|
|
|
|
statMod_boss_global = StatModifier_Boss_Global;
|
|
statMod_boss_health = StatModifier_Boss_Health;
|
|
statMod_boss_mana = StatModifier_Boss_Mana;
|
|
statMod_boss_armor = StatModifier_Boss_Armor;
|
|
statMod_boss_damage = StatModifier_Boss_Damage;
|
|
statMod_boss_ccDuration = StatModifier_Boss_CCDuration;
|
|
break;
|
|
case 10:
|
|
statMod_global = StatModifierRaid10M_Global;
|
|
statMod_health = StatModifierRaid10M_Health;
|
|
statMod_mana = StatModifierRaid10M_Mana;
|
|
statMod_armor = StatModifierRaid10M_Armor;
|
|
statMod_damage = StatModifierRaid10M_Damage;
|
|
statMod_ccDuration = StatModifierRaid10M_CCDuration;
|
|
|
|
statMod_boss_global = StatModifierRaid10M_Boss_Global;
|
|
statMod_boss_health = StatModifierRaid10M_Boss_Health;
|
|
statMod_boss_mana = StatModifierRaid10M_Boss_Mana;
|
|
statMod_boss_armor = StatModifierRaid10M_Boss_Armor;
|
|
statMod_boss_damage = StatModifierRaid10M_Boss_Damage;
|
|
statMod_boss_ccDuration = StatModifierRaid10M_Boss_CCDuration;
|
|
break;
|
|
case 15:
|
|
statMod_global = StatModifierRaid15M_Global;
|
|
statMod_health = StatModifierRaid15M_Health;
|
|
statMod_mana = StatModifierRaid15M_Mana;
|
|
statMod_armor = StatModifierRaid15M_Armor;
|
|
statMod_damage = StatModifierRaid15M_Damage;
|
|
statMod_ccDuration = StatModifierRaid15M_CCDuration;
|
|
|
|
statMod_boss_global = StatModifierRaid15M_Boss_Global;
|
|
statMod_boss_health = StatModifierRaid15M_Boss_Health;
|
|
statMod_boss_mana = StatModifierRaid15M_Boss_Mana;
|
|
statMod_boss_armor = StatModifierRaid15M_Boss_Armor;
|
|
statMod_boss_damage = StatModifierRaid15M_Boss_Damage;
|
|
statMod_boss_ccDuration = StatModifierRaid15M_Boss_CCDuration;
|
|
break;
|
|
case 20:
|
|
statMod_global = StatModifierRaid20M_Global;
|
|
statMod_health = StatModifierRaid20M_Health;
|
|
statMod_mana = StatModifierRaid20M_Mana;
|
|
statMod_armor = StatModifierRaid20M_Armor;
|
|
statMod_damage = StatModifierRaid20M_Damage;
|
|
statMod_ccDuration = StatModifierRaid20M_CCDuration;
|
|
|
|
statMod_boss_global = StatModifierRaid20M_Boss_Global;
|
|
statMod_boss_health = StatModifierRaid20M_Boss_Health;
|
|
statMod_boss_mana = StatModifierRaid20M_Boss_Mana;
|
|
statMod_boss_armor = StatModifierRaid20M_Boss_Armor;
|
|
statMod_boss_damage = StatModifierRaid20M_Boss_Damage;
|
|
statMod_boss_ccDuration = StatModifierRaid20M_Boss_CCDuration;
|
|
break;
|
|
case 25:
|
|
statMod_global = StatModifierRaid25M_Global;
|
|
statMod_health = StatModifierRaid25M_Health;
|
|
statMod_mana = StatModifierRaid25M_Mana;
|
|
statMod_armor = StatModifierRaid25M_Armor;
|
|
statMod_damage = StatModifierRaid25M_Damage;
|
|
statMod_ccDuration = StatModifierRaid25M_CCDuration;
|
|
|
|
statMod_boss_global = StatModifierRaid25M_Boss_Global;
|
|
statMod_boss_health = StatModifierRaid25M_Boss_Health;
|
|
statMod_boss_mana = StatModifierRaid25M_Boss_Mana;
|
|
statMod_boss_armor = StatModifierRaid25M_Boss_Armor;
|
|
statMod_boss_damage = StatModifierRaid25M_Boss_Damage;
|
|
statMod_boss_ccDuration = StatModifierRaid25M_Boss_CCDuration;
|
|
break;
|
|
case 40:
|
|
statMod_global = StatModifierRaid40M_Global;
|
|
statMod_health = StatModifierRaid40M_Health;
|
|
statMod_mana = StatModifierRaid40M_Mana;
|
|
statMod_armor = StatModifierRaid40M_Armor;
|
|
statMod_damage = StatModifierRaid40M_Damage;
|
|
statMod_ccDuration = StatModifierRaid40M_CCDuration;
|
|
|
|
statMod_boss_global = StatModifierRaid40M_Boss_Global;
|
|
statMod_boss_health = StatModifierRaid40M_Boss_Health;
|
|
statMod_boss_mana = StatModifierRaid40M_Boss_Mana;
|
|
statMod_boss_armor = StatModifierRaid40M_Boss_Armor;
|
|
statMod_boss_damage = StatModifierRaid40M_Boss_Damage;
|
|
statMod_boss_ccDuration = StatModifierRaid40M_Boss_CCDuration;
|
|
break;
|
|
default:
|
|
statMod_global = StatModifierRaid_Global;
|
|
statMod_health = StatModifierRaid_Health;
|
|
statMod_mana = StatModifierRaid_Mana;
|
|
statMod_armor = StatModifierRaid_Armor;
|
|
statMod_damage = StatModifierRaid_Damage;
|
|
statMod_ccDuration = StatModifierRaid_CCDuration;
|
|
|
|
statMod_boss_global = StatModifierRaid_Boss_Global;
|
|
statMod_boss_health = StatModifierRaid_Boss_Health;
|
|
statMod_boss_mana = StatModifierRaid_Boss_Mana;
|
|
statMod_boss_armor = StatModifierRaid_Boss_Armor;
|
|
statMod_boss_damage = StatModifierRaid_Boss_Damage;
|
|
statMod_boss_ccDuration = StatModifierRaid_Boss_CCDuration;
|
|
}
|
|
}
|
|
|
|
// Boss modifiers
|
|
if (creature->IsDungeonBoss())
|
|
{
|
|
// Start with the settings determined above
|
|
// AutoBalance.StatModifier*.Boss.<stat>
|
|
if (creature->IsDungeonBoss())
|
|
{
|
|
statMod_global = statMod_boss_global;
|
|
statMod_health = statMod_boss_health;
|
|
statMod_mana = statMod_boss_mana;
|
|
statMod_armor = statMod_boss_armor;
|
|
statMod_damage = statMod_boss_damage;
|
|
statMod_ccDuration = statMod_boss_ccDuration;
|
|
}
|
|
|
|
// Per-instance boss overrides
|
|
// AutoBalance.StatModifier.Boss.PerInstance
|
|
if (creature->IsDungeonBoss() && hasStatModifierBossOverride(mapId))
|
|
{
|
|
AutoBalanceStatModifiers* myStatModifierBossOverrides = &statModifierBossOverrides[mapId];
|
|
|
|
if (myStatModifierBossOverrides->global != -1) { statMod_global = myStatModifierBossOverrides->global; }
|
|
if (myStatModifierBossOverrides->health != -1) { statMod_health = myStatModifierBossOverrides->health; }
|
|
if (myStatModifierBossOverrides->mana != -1) { statMod_mana = myStatModifierBossOverrides->mana; }
|
|
if (myStatModifierBossOverrides->armor != -1) { statMod_armor = myStatModifierBossOverrides->armor; }
|
|
if (myStatModifierBossOverrides->damage != -1) { statMod_damage = myStatModifierBossOverrides->damage; }
|
|
if (myStatModifierBossOverrides->ccduration != -1) { statMod_ccDuration = myStatModifierBossOverrides->ccduration; }
|
|
}
|
|
}
|
|
// Non-boss modifiers
|
|
else
|
|
{
|
|
// Per-instance non-boss overrides
|
|
// AutoBalance.StatModifier.PerInstance
|
|
if (hasStatModifierOverride(mapId))
|
|
{
|
|
AutoBalanceStatModifiers* myStatModifierOverrides = &statModifierOverrides[mapId];
|
|
|
|
if (myStatModifierOverrides->global != -1) { statMod_global = myStatModifierOverrides->global; }
|
|
if (myStatModifierOverrides->health != -1) { statMod_health = myStatModifierOverrides->health; }
|
|
if (myStatModifierOverrides->mana != -1) { statMod_mana = myStatModifierOverrides->mana; }
|
|
if (myStatModifierOverrides->armor != -1) { statMod_armor = myStatModifierOverrides->armor; }
|
|
if (myStatModifierOverrides->damage != -1) { statMod_damage = myStatModifierOverrides->damage; }
|
|
if (myStatModifierOverrides->ccduration != -1) { statMod_ccDuration = myStatModifierOverrides->ccduration; }
|
|
}
|
|
}
|
|
|
|
// Per-creature modifiers applied last
|
|
// AutoBalance.StatModifier.PerCreature
|
|
if (hasStatModifierCreatureOverride(creatureTemplate->Entry))
|
|
{
|
|
AutoBalanceStatModifiers* myCreatureOverrides = &statModifierCreatureOverrides[creatureTemplate->Entry];
|
|
|
|
if (myCreatureOverrides->global != -1) { statMod_global = myCreatureOverrides->global; }
|
|
if (myCreatureOverrides->health != -1) { statMod_health = myCreatureOverrides->health; }
|
|
if (myCreatureOverrides->mana != -1) { statMod_mana = myCreatureOverrides->mana; }
|
|
if (myCreatureOverrides->armor != -1) { statMod_armor = myCreatureOverrides->armor; }
|
|
if (myCreatureOverrides->damage != -1) { statMod_damage = myCreatureOverrides->damage; }
|
|
if (myCreatureOverrides->ccduration != -1) { statMod_ccDuration = myCreatureOverrides->ccduration; }
|
|
}
|
|
|
|
// #maththings
|
|
float diff = ((float)maxNumberOfPlayers/5)*1.5f;
|
|
|
|
// For math reasons that I do not understand, curveCeiling needs to be adjusted to bring the actual multiplier
|
|
// closer to the curveCeiling setting. Create an adjustment based on how much the ceiling should be changed at
|
|
// the max players multiplier.
|
|
float curveCeilingAdjustment = curveCeiling / (((tanh(((float)maxNumberOfPlayers - inflectionValue) / diff) + 1.0f) / 2.0f) * (curveCeiling - curveFloor) + curveFloor);
|
|
|
|
// Adjust the multiplier based on the configured floor and ceiling values, plus the ceiling adjustment we just calculated
|
|
defaultMultiplier = ((tanh(((float)creatureABInfo->instancePlayerCount - inflectionValue) / diff) + 1.0f) / 2.0f) * (curveCeiling * curveCeilingAdjustment - curveFloor) + curveFloor;
|
|
|
|
if (!sABScriptMgr->OnAfterDefaultMultiplier(creature, defaultMultiplier))
|
|
return;
|
|
|
|
//
|
|
// Health Scaling
|
|
//
|
|
|
|
float healthMultiplier = defaultMultiplier * statMod_global * statMod_health;
|
|
|
|
if (healthMultiplier <= MinHPModifier)
|
|
healthMultiplier = MinHPModifier;
|
|
|
|
float hpStatsRate = 1.0f;
|
|
float originalHealth = origCreatureStats->GenerateHealth(creatureTemplate);
|
|
|
|
float newBaseHealth;
|
|
|
|
// The database holds multiple values for base health, one for each expansion
|
|
// This code will smooth transition between the different expansions based on the highest player level in the instance
|
|
// Only do this if level scaling is enabled
|
|
|
|
if (LevelScaling)
|
|
{
|
|
float vanillaHealth = creatureStats->BaseHealth[0];
|
|
float bcHealth = creatureStats->BaseHealth[1];
|
|
float wotlkHealth = creatureStats->BaseHealth[2];
|
|
|
|
// vanilla health
|
|
if (mapABInfo->highestPlayerLevel <= 60)
|
|
{
|
|
newBaseHealth = vanillaHealth;
|
|
}
|
|
// transition from vanilla to BC health
|
|
else if (mapABInfo->highestPlayerLevel < 63)
|
|
{
|
|
float vanillaMultiplier = (63 - mapABInfo->highestPlayerLevel) / 3.0f;
|
|
float bcMultiplier = 1.0f - vanillaMultiplier;
|
|
|
|
newBaseHealth = (vanillaHealth * vanillaMultiplier) + (bcHealth * bcMultiplier);
|
|
}
|
|
// BC health
|
|
else if (mapABInfo->highestPlayerLevel <= 70)
|
|
{
|
|
newBaseHealth = bcHealth;
|
|
}
|
|
// transition from BC to WotLK health
|
|
else if (mapABInfo->highestPlayerLevel < 73)
|
|
{
|
|
float bcMultiplier = (73 - mapABInfo->highestPlayerLevel) / 3.0f;
|
|
float wotlkMultiplier = 1.0f - bcMultiplier;
|
|
|
|
newBaseHealth = (bcHealth * bcMultiplier) + (wotlkHealth * wotlkMultiplier);
|
|
}
|
|
// WotLK health
|
|
else
|
|
{
|
|
newBaseHealth = wotlkHealth;
|
|
|
|
// special increase for end-game content
|
|
if (LevelScalingEndGameBoost)
|
|
if (mapABInfo->highestPlayerLevel >= 75 && creatureABInfo->UnmodifiedLevel < 75)
|
|
{
|
|
newBaseHealth *= (float)(mapABInfo->highestPlayerLevel-70) * 0.3f;
|
|
}
|
|
}
|
|
|
|
float newHealth = newBaseHealth * creatureTemplate->ModHealth;
|
|
hpStatsRate = newHealth / originalHealth;
|
|
|
|
healthMultiplier *= hpStatsRate;
|
|
}
|
|
|
|
creatureABInfo->HealthMultiplier = healthMultiplier;
|
|
scaledHealth = round(originalHealth * creatureABInfo->HealthMultiplier);
|
|
|
|
//
|
|
// Mana Scaling
|
|
//
|
|
float manaStatsRate = 1.0f;
|
|
float newMana = creatureStats->GenerateMana(creatureTemplate);
|
|
manaStatsRate = newMana/float(baseMana);
|
|
|
|
// check to be sure that manaStatsRate is not nan
|
|
if (manaStatsRate != manaStatsRate)
|
|
{
|
|
creatureABInfo->ManaMultiplier = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
creatureABInfo->ManaMultiplier = defaultMultiplier * manaStatsRate * statMod_global * statMod_mana;
|
|
|
|
if (creatureABInfo->ManaMultiplier <= MinManaModifier)
|
|
{
|
|
creatureABInfo->ManaMultiplier = MinManaModifier;
|
|
}
|
|
}
|
|
|
|
scaledMana = round(baseMana * creatureABInfo->ManaMultiplier);
|
|
|
|
//
|
|
// Armor Scaling
|
|
//
|
|
creatureABInfo->ArmorMultiplier = defaultMultiplier * statMod_global * statMod_armor;
|
|
uint32 newBaseArmor = round(creatureABInfo->ArmorMultiplier * (LevelScaling ? creatureStats->GenerateArmor(creatureTemplate) : origCreatureStats->GenerateArmor(creatureTemplate)));
|
|
|
|
//
|
|
// Damage Scaling
|
|
//
|
|
float damageMul = defaultMultiplier * statMod_global * statMod_damage;
|
|
|
|
// Can not be less than MinDamageModifier
|
|
if (damageMul <= MinDamageModifier)
|
|
{
|
|
damageMul = MinDamageModifier;
|
|
}
|
|
|
|
// Calculate the new base damage
|
|
float origDmgBase = origCreatureStats->GenerateBaseDamage(creatureTemplate);
|
|
float newDmgBase = 0;
|
|
|
|
float vanillaDamage = creatureStats->BaseDamage[0];
|
|
float bcDamage = creatureStats->BaseDamage[1];
|
|
float wotlkDamage = creatureStats->BaseDamage[2];
|
|
|
|
// The database holds multiple values for base damage, one for each expansion
|
|
// This code will smooth transition between the different expansions based on the highest player level in the instance
|
|
// Only do this if level scaling is enabled
|
|
|
|
if (LevelScaling)
|
|
{
|
|
// vanilla damage
|
|
if (mapABInfo->highestPlayerLevel <= 60)
|
|
{
|
|
newDmgBase=vanillaDamage;
|
|
}
|
|
// transition from vanilla to BC damage
|
|
else if (mapABInfo->highestPlayerLevel < 63)
|
|
{
|
|
float vanillaMultiplier = (63 - mapABInfo->highestPlayerLevel) / 3.0;
|
|
float bcMultiplier = 1.0f - vanillaMultiplier;
|
|
|
|
newDmgBase=(vanillaDamage * vanillaMultiplier) + (bcDamage * bcMultiplier);
|
|
}
|
|
// BC damage
|
|
else if (mapABInfo->highestPlayerLevel <= 70)
|
|
{
|
|
newDmgBase=bcDamage;
|
|
}
|
|
// transition from BC to WotLK damage
|
|
else if (mapABInfo->highestPlayerLevel < 73)
|
|
{
|
|
float bcMultiplier = (73 - mapABInfo->highestPlayerLevel) / 3.0;
|
|
float wotlkMultiplier = 1.0f - bcMultiplier;
|
|
|
|
newDmgBase=(bcDamage * bcMultiplier) + (wotlkDamage * wotlkMultiplier);
|
|
}
|
|
// WotLK damage
|
|
else
|
|
{
|
|
newDmgBase=wotlkDamage;
|
|
|
|
// special increase for end-game content
|
|
if (LevelScalingEndGameBoost && maxNumberOfPlayers <= 5) {
|
|
if (mapABInfo->highestPlayerLevel >= 75 && creatureABInfo->UnmodifiedLevel < 75)
|
|
newDmgBase *= float(mapABInfo->highestPlayerLevel-70) * 0.3f;
|
|
}
|
|
}
|
|
|
|
damageMul *= newDmgBase/origDmgBase;
|
|
}
|
|
|
|
//
|
|
// Crowd Control Debuff Duration Scaling
|
|
//
|
|
float ccDurationMul;
|
|
if (statMod_ccDuration != -1.0f)
|
|
{
|
|
ccDurationMul = defaultMultiplier * statMod_ccDuration;
|
|
|
|
// Min/Max checking
|
|
if (ccDurationMul < MinCCDurationModifier)
|
|
{
|
|
ccDurationMul = MinCCDurationModifier;
|
|
}
|
|
else if (ccDurationMul > MaxCCDurationModifier)
|
|
{
|
|
ccDurationMul = MaxCCDurationModifier;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ccDurationMul = 1.0f;
|
|
}
|
|
|
|
//
|
|
// Apply New Values
|
|
//
|
|
if (!sABScriptMgr->OnBeforeUpdateStats(creature, scaledHealth, scaledMana, damageMul, newBaseArmor))
|
|
return;
|
|
|
|
uint32 prevMaxHealth = creature->GetMaxHealth();
|
|
uint32 prevMaxPower = creature->GetMaxPower(POWER_MANA);
|
|
uint32 prevHealth = creature->GetHealth();
|
|
uint32 prevPower = creature->GetPower(POWER_MANA);
|
|
|
|
Powers pType= creature->getPowerType();
|
|
|
|
creature->SetArmor(newBaseArmor);
|
|
creature->SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, (float)newBaseArmor);
|
|
creature->SetCreateHealth(scaledHealth);
|
|
creature->SetMaxHealth(scaledHealth);
|
|
creature->ResetPlayerDamageReq();
|
|
creature->SetCreateMana(scaledMana);
|
|
creature->SetMaxPower(POWER_MANA, scaledMana);
|
|
creature->SetModifierValue(UNIT_MOD_ENERGY, BASE_VALUE, (float)100.0f);
|
|
creature->SetModifierValue(UNIT_MOD_RAGE, BASE_VALUE, (float)100.0f);
|
|
creature->SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, (float)scaledHealth);
|
|
creature->SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, (float)scaledMana);
|
|
creatureABInfo->DamageMultiplier = damageMul;
|
|
creatureABInfo->CCDurationMultiplier = ccDurationMul;
|
|
|
|
uint32 scaledCurHealth=prevHealth && prevMaxHealth ? float(scaledHealth)/float(prevMaxHealth)*float(prevHealth) : 0;
|
|
uint32 scaledCurPower=prevPower && prevMaxPower ? float(scaledMana)/float(prevMaxPower)*float(prevPower) : 0;
|
|
|
|
creature->SetHealth(scaledCurHealth);
|
|
if (pType == POWER_MANA)
|
|
creature->SetPower(POWER_MANA, scaledCurPower);
|
|
else
|
|
creature->setPowerType(pType); // fix creatures with different power types
|
|
|
|
//
|
|
// Reward Scaling
|
|
//
|
|
|
|
// calculate the average multiplier after level scaling is applied
|
|
float averageMultiplierAfterLevelScaling;
|
|
// use health and damage to calculate the average multiplier
|
|
averageMultiplierAfterLevelScaling = (creatureABInfo->HealthMultiplier + creatureABInfo->DamageMultiplier) / 2.0f;
|
|
|
|
// XP Scaling
|
|
if (RewardScalingXP)
|
|
{
|
|
if (RewardScalingMethod == AUTOBALANCE_SCALING_FIXED)
|
|
{
|
|
creatureABInfo->XPModifier = RewardScalingXPModifier;
|
|
}
|
|
else if (RewardScalingMethod == AUTOBALANCE_SCALING_DYNAMIC)
|
|
{
|
|
creatureABInfo->XPModifier = averageMultiplierAfterLevelScaling * RewardScalingXPModifier;
|
|
}
|
|
}
|
|
|
|
// Money Scaling
|
|
if (RewardScalingMoney)
|
|
{
|
|
//LOG_DEBUG("module.AutoBalance", "AutoBalance_AllCreatureScript::ModifyCreatureAttributes: Creature {} ({}) has an average post-level-scaling modifier of {}.", creature->GetName(), creature->GetLevel(), averageMultiplierAfterLevelScaling);
|
|
|
|
if (RewardScalingMethod == AUTOBALANCE_SCALING_FIXED)
|
|
{
|
|
creatureABInfo->MoneyModifier = RewardScalingMoneyModifier;
|
|
}
|
|
else if (RewardScalingMethod == AUTOBALANCE_SCALING_DYNAMIC)
|
|
{
|
|
creatureABInfo->MoneyModifier = averageMultiplierAfterLevelScaling * RewardScalingMoneyModifier;
|
|
}
|
|
}
|
|
|
|
creature->UpdateAllStats();
|
|
}
|
|
|
|
private:
|
|
uint32 adjustCurCount(uint32 inputCount, uint32 dungeonId)
|
|
{
|
|
uint8 minPlayers = enabledDungeonIds[dungeonId];
|
|
return inputCount < minPlayers ? minPlayers : inputCount;
|
|
}
|
|
};
|
|
class AutoBalance_CommandScript : public CommandScript
|
|
{
|
|
public:
|
|
AutoBalance_CommandScript() : CommandScript("AutoBalance_CommandScript") { }
|
|
|
|
std::vector<ChatCommand> GetCommands() const
|
|
{
|
|
static std::vector<ChatCommand> ABCommandTable =
|
|
{
|
|
{ "setoffset", SEC_GAMEMASTER, true, &HandleABSetOffsetCommand, "Sets the global Player Difficulty Offset for instances. Example: (You + offset(1) = 2 player difficulty)." },
|
|
{ "getoffset", SEC_PLAYER, true, &HandleABGetOffsetCommand, "Shows current global player offset value." },
|
|
{ "checkmap", SEC_GAMEMASTER, true, &HandleABCheckMapCommand, "Run a check for current map/instance, it can help in case you're testing autobalance with GM." },
|
|
{ "mapstat", SEC_PLAYER, true, &HandleABMapStatsCommand, "Shows current autobalance information for this map" },
|
|
{ "creaturestat", SEC_PLAYER, true, &HandleABCreatureStatsCommand, "Shows current autobalance information for selected creature." },
|
|
{ "mythic", SEC_PLAYER, true, &HandleABMythicCommand, "Sets the group difficulty to Mythic" },
|
|
{ "legendary", SEC_PLAYER, true, &HandleABLegendaryCommand, "Sets the group difficulty to Legendary" },
|
|
{ "ascendant", SEC_PLAYER, true, &HandleABAscendantCommand, "Sets the group difficulty to Ascendant" },
|
|
{ "getdifficulty", SEC_PLAYER, true, &HandleABGetDifficultyCommand, "Shows the current group difficulty" },
|
|
|
|
};
|
|
|
|
static std::vector<ChatCommand> commandTable =
|
|
{
|
|
{ "autobalance", SEC_PLAYER, false, NULL, "", ABCommandTable },
|
|
{ "ab", SEC_PLAYER, false, NULL, "", ABCommandTable },
|
|
};
|
|
return commandTable;
|
|
}
|
|
|
|
static bool HandleABSetOffsetCommand(ChatHandler* handler, const char* args)
|
|
{
|
|
if (!*args)
|
|
{
|
|
handler->PSendSysMessage(".autobalance setoffset #");
|
|
handler->PSendSysMessage("Sets the Player Difficulty Offset for instances. Example: (You + offset(1) = 2 player difficulty).");
|
|
return false;
|
|
}
|
|
char* offset = strtok((char*)args, " ");
|
|
int32 offseti = -1;
|
|
|
|
if (offset)
|
|
{
|
|
offseti = (uint32)atoi(offset);
|
|
handler->PSendSysMessage("Changing Player Difficulty Offset to %i.", offseti);
|
|
PlayerCountDifficultyOffset = offseti;
|
|
lastConfigTime = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
return true;
|
|
}
|
|
else
|
|
handler->PSendSysMessage("Error changing Player Difficulty Offset! Please try again.");
|
|
return false;
|
|
}
|
|
|
|
static bool HandleABGetOffsetCommand(ChatHandler* handler, const char* /*args*/)
|
|
{
|
|
handler->PSendSysMessage("Current Player Difficulty Offset = %i", PlayerCountDifficultyOffset);
|
|
return true;
|
|
}
|
|
|
|
static bool HandleABCheckMapCommand(ChatHandler* handler, const char* args)
|
|
{
|
|
Player *pl = handler->getSelectedPlayer();
|
|
|
|
if (!pl)
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_PLAYER_OR_PET);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
AutoBalanceMapInfo *mapABInfo=pl->GetMap()->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
|
|
mapABInfo->playerCount = pl->GetMap()->GetPlayersCountExceptGMs();
|
|
|
|
Map::PlayerList const &playerList = pl->GetMap()->GetPlayers();
|
|
uint8 level = 0;
|
|
if (!playerList.IsEmpty())
|
|
{
|
|
for (Map::PlayerList::const_iterator playerIteration = playerList.begin(); playerIteration != playerList.end(); ++playerIteration)
|
|
{
|
|
if (Player* playerHandle = playerIteration->GetSource())
|
|
{
|
|
if (playerHandle->getLevel() > level)
|
|
mapABInfo->mapLevel = level = playerHandle->getLevel();
|
|
}
|
|
}
|
|
}
|
|
|
|
HandleABMapStatsCommand(handler, args);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool HandleABMapStatsCommand(ChatHandler* handler, const char* /*args*/)
|
|
{
|
|
Player *player;
|
|
player = handler->getSelectedPlayer() ? handler->getSelectedPlayer() : handler->GetPlayer();
|
|
|
|
AutoBalanceMapInfo *mapABInfo=player->GetMap()->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
|
|
if (player->GetMap()->IsDungeon() || player->GetMap()->IsRaid())
|
|
{
|
|
handler->PSendSysMessage("---");
|
|
handler->PSendSysMessage("Map: %s (ID: %u)", player->GetMap()->GetMapName(), player->GetMapId());
|
|
handler->PSendSysMessage("Players on map: %u (Lvl %u - %u)",
|
|
mapABInfo->playerCount,
|
|
mapABInfo->lowestPlayerLevel,
|
|
mapABInfo->highestPlayerLevel
|
|
);
|
|
handler->PSendSysMessage("Map Level: %u%s", (uint8)(mapABInfo->avgCreatureLevel+0.5f),
|
|
mapABInfo->isLevelScalingEnabled ? std::string("->") + std::to_string(mapABInfo->highestPlayerLevel) + std::string(" (Level Scaling Enabled)") : std::string(" (Level Scaling Disabled)")
|
|
);
|
|
handler->PSendSysMessage("LFG Range: Lvl %u - %u (Target: Lvl %u)", mapABInfo->lfgMinLevel, mapABInfo->lfgMaxLevel, mapABInfo->lfgTargetLevel);
|
|
handler->PSendSysMessage("Active Creatures in map: %u (Lvl %u - %u | Avg Lvl %.2f)",
|
|
mapABInfo->activeCreatureCount,
|
|
mapABInfo->lowestCreatureLevel,
|
|
mapABInfo->highestCreatureLevel,
|
|
mapABInfo->avgCreatureLevel
|
|
);
|
|
handler->PSendSysMessage("Total Creatures in map: %u",
|
|
mapABInfo->allMapCreatures.size()
|
|
);
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
handler->PSendSysMessage("The target is not in a dungeon or battleground.");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static bool HandleABCreatureStatsCommand(ChatHandler* handler, const char* /*args*/)
|
|
{
|
|
Creature* target = handler->getSelectedCreature();
|
|
|
|
if (!target)
|
|
{
|
|
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
|
handler->SetSentErrorMessage(true);
|
|
return false;
|
|
}
|
|
|
|
AutoBalanceCreatureInfo *creatureABInfo=target->CustomData.GetDefault<AutoBalanceCreatureInfo>("AutoBalanceCreatureInfo");
|
|
AutoBalanceMapInfo *mapABInfo=target->GetMap()->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
|
|
handler->PSendSysMessage("---");
|
|
handler->PSendSysMessage("%s (%u%s%s), %s",
|
|
target->GetName(),
|
|
creatureABInfo->UnmodifiedLevel,
|
|
mapABInfo->isLevelScalingEnabled ? std::string("->") + std::to_string(creatureABInfo->selectedLevel) : "",
|
|
target->IsDungeonBoss() ? " | Boss" : "",
|
|
creatureABInfo->isActive ? "Active for Map Stats" : "Ignored for Map Stats");
|
|
handler->PSendSysMessage("Health multiplier: %.3f", creatureABInfo->HealthMultiplier);
|
|
handler->PSendSysMessage("Mana multiplier: %.3f", creatureABInfo->ManaMultiplier);
|
|
handler->PSendSysMessage("Armor multiplier: %.3f", creatureABInfo->ArmorMultiplier);
|
|
handler->PSendSysMessage("Damage multiplier: %.3f", creatureABInfo->DamageMultiplier);
|
|
handler->PSendSysMessage("CC Duration multiplier: %.3f", creatureABInfo->CCDurationMultiplier);
|
|
handler->PSendSysMessage("XP multiplier: %.3f Money multiplier: %.3f", creatureABInfo->XPModifier, creatureABInfo->MoneyModifier);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
static bool HandleABMythicCommand(ChatHandler* handler, const char* /*args*/)
|
|
{
|
|
Player* player = handler->GetPlayer();
|
|
if(!player) {
|
|
return false;
|
|
}
|
|
|
|
Group* group = player->GetGroup();
|
|
if (!group)
|
|
{
|
|
handler->PSendSysMessage("autobalance: You are not in a group.");
|
|
return false;
|
|
}
|
|
|
|
if (group->GetLeader() != handler->GetSession()->GetPlayer())
|
|
{
|
|
handler->PSendSysMessage("autobalance: You are not the group leader.");
|
|
return false;
|
|
}
|
|
|
|
CharacterDatabase.DirectExecute("UPDATE groups SET difficulty = 2 WHERE guid = {}", group->GetGUID().GetEntry());
|
|
handler->PSendSysMessage("autobalance: group difficulty set to Mythic. Rewards improvements x1. level(81-83) recommended.");
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool HandleABLegendaryCommand(ChatHandler* handler, const char* /*args*/)
|
|
{
|
|
Player* player = handler->GetPlayer();
|
|
if(!player) {
|
|
return false;
|
|
}
|
|
|
|
Group* group = player->GetGroup();
|
|
if (!group)
|
|
{
|
|
handler->PSendSysMessage("autobalance: You are not in a group.");
|
|
return false;
|
|
}
|
|
|
|
if (group->GetLeader() != handler->GetSession()->GetPlayer())
|
|
{
|
|
handler->PSendSysMessage("autobalance: You are not the group leader.");
|
|
return false;
|
|
}
|
|
|
|
CharacterDatabase.DirectExecute("UPDATE groups SET difficulty = 3 WHERE guid = {}", group->GetGUID().GetEntry());
|
|
handler->PSendSysMessage("autobalance: group difficulty set to Mythic. Reward improvements x2 level(85) recommended.");
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool HandleABAscendantCommand(ChatHandler* handler, const char* /*args*/)
|
|
{
|
|
Player* player = handler->GetPlayer();
|
|
if(!player) {
|
|
return false;
|
|
}
|
|
|
|
Group* group = player->GetGroup();
|
|
if (!group)
|
|
{
|
|
handler->PSendSysMessage("autobalance: You are not in a group.");
|
|
return false;
|
|
}
|
|
|
|
if (group->GetLeader() != handler->GetSession()->GetPlayer())
|
|
{
|
|
handler->PSendSysMessage("autobalance: You are not the group leader.");
|
|
return false;
|
|
}
|
|
|
|
CharacterDatabase.DirectExecute("UPDATE groups SET difficulty = 2 WHERE guid = {}", group->GetGUID().GetEntry());
|
|
handler->PSendSysMessage("autobalance: group difficulty set to Ascendant. Reward improvements x3 leve(85) required.");
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool HandleABGetDifficultyCommand(ChatHandler* handler, const char*) {
|
|
Player* player = handler->GetPlayer();
|
|
if(!player) {
|
|
return false;
|
|
}
|
|
|
|
Group* group = player->GetGroup();
|
|
if (!group)
|
|
{
|
|
handler->PSendSysMessage("autobalance: You are not in a group.");
|
|
return false;
|
|
}
|
|
|
|
if (group->GetLeader() != handler->GetSession()->GetPlayer())
|
|
{
|
|
handler->PSendSysMessage("autobalance: You are not the group leader.");
|
|
return false;
|
|
}
|
|
|
|
QueryResult result = CharacterDatabase.Query("SELECT difficulty FROM groups WHERE guid = {}", group->GetGUID().GetEntry());
|
|
if (!result)
|
|
{
|
|
handler->PSendSysMessage("autobalance: group difficulty not found.");
|
|
return false;
|
|
}
|
|
|
|
Field* fields = result->Fetch();
|
|
handler->PSendSysMessage("autobalance: group difficulty is set to %u.", fields[0].Get<uint8>());
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class AutoBalance_GlobalScript : public GlobalScript {
|
|
public:
|
|
AutoBalance_GlobalScript() : GlobalScript("AutoBalance_GlobalScript") { }
|
|
|
|
void OnAfterUpdateEncounterState(Map* map, EncounterCreditType type, uint32 /*creditEntry*/, Unit* /*source*/, Difficulty /*difficulty_fixed*/, DungeonEncounterList const* /*encounters*/, uint32 /*dungeonCompleted*/, bool updated) override {
|
|
//if (!dungeonCompleted)
|
|
// return;
|
|
|
|
if (!rewardEnabled || !updated)
|
|
return;
|
|
|
|
if (map->GetPlayersCountExceptGMs() < MinPlayerReward)
|
|
return;
|
|
|
|
AutoBalanceMapInfo *mapABInfo=map->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
|
|
// skip if it's not a pre-wotlk dungeon/raid and if it's not scaled
|
|
if (!LevelScaling || mapABInfo->mapLevel <= 70 || mapABInfo->lfgMinLevel <= 70
|
|
// skip when not in dungeon or not kill credit
|
|
|| type != ENCOUNTER_CREDIT_KILL_CREATURE || !map->IsDungeon())
|
|
return;
|
|
|
|
Map::PlayerList const &playerList = map->GetPlayers();
|
|
|
|
if (playerList.IsEmpty())
|
|
return;
|
|
|
|
uint32 reward = map->ToInstanceMap()->GetMaxPlayers() > 5 ? rewardRaid : rewardDungeon;
|
|
if (!reward)
|
|
return;
|
|
|
|
//instanceStart=0, endTime;
|
|
uint8 difficulty = map->GetDifficulty();
|
|
|
|
for (Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr)
|
|
{
|
|
if (!itr->GetSource() || itr->GetSource()->IsGameMaster() || itr->GetSource()->getLevel() < DEFAULT_MAX_LEVEL)
|
|
continue;
|
|
|
|
itr->GetSource()->AddItem(reward, 1 + difficulty); // difficulty boost
|
|
}
|
|
}
|
|
|
|
void OnBeforeDropAddItem(Player const* player, Loot& loot, bool canRate, uint16 lootMode, LootStoreItem* LootStoreItem, LootStore const& store) override {
|
|
// just log out the item drops for now
|
|
|
|
if(LootStoreItem->itemid == 0) {
|
|
return;
|
|
}
|
|
|
|
ItemTemplate const* newItem = sObjectMgr->GetItemTemplate(LootStoreItem->itemid);
|
|
Map* map = player->GetMap();
|
|
|
|
LOG_INFO("server", "> OnBeforeDropAddItem: Current Loot Drop Item {}", newItem->Name1);
|
|
|
|
// 3 things things need to happen
|
|
// 1. Is the instance scaled up to max level or beyond?
|
|
// 2. Is the loot quality rare or higher?
|
|
// 3. What is the difficulty of the instances? 2 - Mythic 3 - Legendary 4 - Ascendant
|
|
|
|
// 1. Is the instance scaled up to max level or beyond?
|
|
AutoBalanceMapInfo *mapABInfo = map->CustomData.GetDefault<AutoBalanceMapInfo>("AutoBalanceMapInfo");
|
|
uint8 creatureLevel = mapABInfo->highestCreatureLevel;
|
|
|
|
// The items are deterministic based ont the id just need to add the correct id starting point
|
|
uint32 idStart = 0;
|
|
|
|
// 1. Is the instance scaled up to max level or beyond?
|
|
if(creatureLevel <= 80) {
|
|
return;
|
|
}
|
|
|
|
// 2. Is the loot quality rare or higher?
|
|
if (newItem->Quality < 3) {
|
|
return;
|
|
}
|
|
|
|
// 3. What is the difficulty of the instances? 2 - Mythic 3 - Legendary 4 - Ascendant
|
|
switch(map->GetDifficulty()) {
|
|
case 2:
|
|
idStart = 20000000;
|
|
break;
|
|
case 3:
|
|
idStart = 21000000;
|
|
break;
|
|
case 4:
|
|
idStart = 22000000;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
const Group* group = player->GetGroup();
|
|
if (!group)
|
|
{
|
|
ChatHandler(player->GetSession()).PSendSysMessage("autobalance: You are not in a group.");
|
|
return;
|
|
}
|
|
|
|
ChatHandler chandler = ChatHandler(player->GetSession());
|
|
|
|
QueryResult result = CharacterDatabase.Query("SELECT difficulty FROM groups WHERE guid = {}", group->GetGUID().GetEntry());
|
|
if (!result)
|
|
{
|
|
chandler.PSendSysMessage("autobalance: group difficulty not found.");
|
|
return;
|
|
}
|
|
|
|
Field* fields = result->Fetch();
|
|
chandler.PSendSysMessage("autobalance: group difficulty is set to %u.", fields[0].Get<uint8>());
|
|
|
|
|
|
// ItemTemplate const* newItem = sObjectMgr->GetItemTemplate(200000000);
|
|
// if(!newItem) {
|
|
// LOG_INFO("server", "> OnBeforeDropAddItem: New Loot Item not found");
|
|
// } else {
|
|
// LOG_INFO("server", "> OnBeforeDropAddItem: New ITEM ItemName {} Quality {} ItemLevel {}", newItem->Name1, newItem->Quality, newItem->ItemLevel);
|
|
// }
|
|
|
|
// for (LootItem& item : loot.items) {
|
|
// LOG_INFO("server", "> OnBeforeDropAddItem: Items {} {}", player->GetName(), item.itemid);
|
|
// ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(item.itemid);
|
|
// if (itemProto) {
|
|
// LOG_INFO("server", "> OnBeforeDropAddItem: OLD ITEM ItemName {} Quality {} ItemLevel {}", itemProto->Name1, itemProto->Quality, itemProto->ItemLevel);
|
|
// }
|
|
// }
|
|
}
|
|
};
|
|
|
|
// this handles updating custom group difficulties used in auto balancing mobs and
|
|
// scripts that enable buffs on mobs randomly
|
|
class AutoBalance_GroupScript : public GroupScript {
|
|
public:
|
|
AutoBalance_GroupScript() : GroupScript("AutoBalance_GroupScript") { }
|
|
|
|
void OnCreate(Group* group, Player* leader) override {
|
|
if (!group)
|
|
return;
|
|
|
|
Player* leader = group->GetLeader();
|
|
if (!leader)
|
|
return;
|
|
|
|
// default difficulty is whatever the player currently has it set as
|
|
uint8 difficulty = leader->GetDifficulty();
|
|
|
|
CharacterDatabase.DirectExecute("INSERT INTO group_difficulty (group_id, difficulty) VALUES ({}, {}) ON DUPLICATE KEY UPDATE difficulty = {}",
|
|
group->GetGUID().GetEntry(), difficulty, difficulty);
|
|
}
|
|
|
|
void OnDisband(Group* group) override {
|
|
if (!group)
|
|
return;
|
|
|
|
CharacterDatabase.DirectExecute("DELETE FROM group_difficulty WHERE group_id = {}", group->GetGUID().GetEntry());
|
|
}
|
|
}
|
|
|
|
void AddAutoBalanceScripts()
|
|
{
|
|
new AutoBalance_WorldScript();
|
|
new AutoBalance_PlayerScript();
|
|
new AutoBalance_UnitScript();
|
|
new AutoBalance_AllCreatureScript();
|
|
new AutoBalance_AllMapScript();
|
|
new AutoBalance_CommandScript();
|
|
new AutoBalance_GlobalScript();
|
|
new AutoBalance_GroupScript();
|
|
}
|