diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 31d309381..ca79c6b08 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -3675,6 +3675,13 @@ Battleground.BerserkingBuffRespawn = 120 Battleground.SpeedBuffRespawn = 150 +# +# Battleground.Override.LowLevels.MinPlayers +# Description: Overrides the minimum number of required players per team for all levels < MaxPlayerLevel +# Default: 0 (Disabled) + +Battleground.Override.LowLevels.MinPlayers = 0 + # ################################################################################################### diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 1e951e4b0..792e54342 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -146,6 +146,7 @@ Battleground::Battleground() m_LastResurrectTime = 0; m_ArenaType = 0; m_IsArena = false; + m_IsTemplate = true; m_WinnerId = PVP_TEAM_NEUTRAL; m_StartTime = 0; m_ResetStatTimer = 0; @@ -1822,6 +1823,7 @@ GraveyardStruct const* Battleground::GetClosestGraveyard(Player* player) void Battleground::SetBracket(PvPDifficultyEntry const* bracketEntry) { + m_IsTemplate = false; m_BracketId = bracketEntry->GetBracketId(); SetLevelRange(bracketEntry->minLevel, bracketEntry->maxLevel); } diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h index 5d7fc3c87..4ab13e9d8 100644 --- a/src/server/game/Battlegrounds/Battleground.h +++ b/src/server/game/Battlegrounds/Battleground.h @@ -23,6 +23,7 @@ #include "DBCEnums.h" #include "GameObject.h" #include "SharedDefines.h" +#include "World.h" class Creature; class GameObject; @@ -337,8 +338,20 @@ public: [[nodiscard]] uint32 GetMinLevel() const { return m_LevelMin; } [[nodiscard]] uint32 GetMaxLevel() const { return m_LevelMax; } + [[nodiscard]] bool isTemplate() const { return m_IsTemplate; } + [[nodiscard]] bool isMaxLevel() const + { + // NOTE: this only works when the BG is not a template but the real BG + auto maxPlayerLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); + return GetMinLevel() <= maxPlayerLevel && maxPlayerLevel <= GetMaxLevel(); + } + [[nodiscard]] uint32 GetMaxPlayersPerTeam() const { return m_MaxPlayersPerTeam; } - [[nodiscard]] uint32 GetMinPlayersPerTeam() const { return m_MinPlayersPerTeam; } + [[nodiscard]] uint32 GetMinPlayersPerTeam() const + { + auto lowLevelsOverride = sWorld->getIntConfig(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS); + return (lowLevelsOverride && !isTemplate() && !isMaxLevel() && !isArena()) ? lowLevelsOverride : m_MinPlayersPerTeam; + } [[nodiscard]] int32 GetStartDelayTime() const { return m_StartDelayTime; } [[nodiscard]] uint8 GetArenaType() const { return m_ArenaType; } @@ -658,6 +671,7 @@ private: bool _InBGFreeSlotQueue{ false }; // used to make sure that BG is only once inserted into the BattlegroundMgr.BGFreeSlotQueue[bgTypeId] deque bool m_SetDeleteThis; // used for safe deletion of the bg after end / all players leave bool m_IsArena; + bool m_IsTemplate; PvPTeamId m_WinnerId; int32 m_StartDelayTime; bool m_IsRated; // is this battle rated? diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.cpp b/src/server/game/Battlegrounds/BattlegroundQueue.cpp index 44673533e..9429afd9e 100644 --- a/src/server/game/Battlegrounds/BattlegroundQueue.cpp +++ b/src/server/game/Battlegrounds/BattlegroundQueue.cpp @@ -31,6 +31,8 @@ #include "ScriptMgr.h" #include +#include "BattlegroundUtils.h" + /*********************************************************/ /*** BATTLEGROUND QUEUE SYSTEM ***/ /*********************************************************/ @@ -760,7 +762,7 @@ void BattlegroundQueue::BattlegroundQueueUpdate(uint32 diff, BattlegroundTypeId } // get min and max players per team - uint32 MinPlayersPerTeam = bg_template->GetMinPlayersPerTeam(); + uint32 MinPlayersPerTeam = GetMinPlayersPerTeam(bg_template, bracketEntry); uint32 MaxPlayersPerTeam = bg_template->GetMaxPlayersPerTeam(); if (bg_template->isArena()) @@ -996,7 +998,7 @@ void BattlegroundQueue::BattlegroundQueueAnnouncerUpdate(uint32 diff, Battlegrou _queueAnnouncementTimer[bracket_id] = -1; auto bgName = bg_template->GetName(); - uint32 MaxPlayers = bg_template->GetMinPlayersPerTeam() * 2; + uint32 MaxPlayers = GetMinPlayersPerTeam(bg_template, bracketEntry) * 2; uint32 q_min_level = std::min(bracketEntry->minLevel, (uint32) 80); uint32 q_max_level = std::min(bracketEntry->maxLevel, (uint32) 80); @@ -1047,7 +1049,7 @@ void BattlegroundQueue::SendMessageBGQueue(Player* leader, Battleground* bg, PvP BattlegroundBracketId bracketId = bracketEntry->GetBracketId(); auto bgName = bg->GetName(); - uint32 MinPlayers = bg->GetMinPlayersPerTeam(); + uint32 MinPlayers = GetMinPlayersPerTeam(bg, bracketEntry); uint32 MaxPlayers = MinPlayers * 2; uint32 q_min_level = std::min(bracketEntry->minLevel, (uint32)80); uint32 q_max_level = std::min(bracketEntry->maxLevel, (uint32)80); diff --git a/src/server/game/Battlegrounds/BattlegroundUtils.cpp b/src/server/game/Battlegrounds/BattlegroundUtils.cpp new file mode 100644 index 000000000..54703472b --- /dev/null +++ b/src/server/game/Battlegrounds/BattlegroundUtils.cpp @@ -0,0 +1,20 @@ +#include "BattlegroundUtils.h" +#include "World.h" + +uint32 GetMinPlayersPerTeam(Battleground* bg, PvPDifficultyEntry const* bracketEntry) +{ + // The problem addressed here is that methods such as bg->GetMinLevel() and bg->GetMaxLevel() have a different meaning + // according to whether the BG is a template (then it's the value from the `battleground_template` table) + // or if it's the real BG (then it's the specific bracket minimum level, e.g. "60" for "60-69"). + + if (!bg->isTemplate() || bg->isArena()) + { + return bg->GetMinPlayersPerTeam(); + } + + auto maxPlayerLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); + auto isMaxLevel = bracketEntry->minLevel <= maxPlayerLevel && maxPlayerLevel <= bracketEntry->maxLevel; + auto lowLevelsOverride = sWorld->getIntConfig(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS); + + return (lowLevelsOverride && !isMaxLevel) ? lowLevelsOverride : bg->GetMinPlayersPerTeam(); +} diff --git a/src/server/game/Battlegrounds/BattlegroundUtils.h b/src/server/game/Battlegrounds/BattlegroundUtils.h new file mode 100644 index 000000000..9d18cbb82 --- /dev/null +++ b/src/server/game/Battlegrounds/BattlegroundUtils.h @@ -0,0 +1,8 @@ +#ifndef BATTLEGROUNDUTILS_H +#define BATTLEGROUNDUTILS_H + +#include "Battleground.h" + +uint32 GetMinPlayersPerTeam(Battleground* bg, PvPDifficultyEntry const* bracketEntry); + +#endif // BATTLEGROUNDUTILS_H diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index 7b3551a32..af08900c3 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -309,6 +309,7 @@ enum WorldIntConfigs CONFIG_DEATH_SICKNESS_LEVEL, CONFIG_INSTANT_LOGOUT, CONFIG_DISABLE_BREATHING, + CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS, CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_SPAM_DELAY, CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMER, CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER, diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 1500f3ec6..0cd40959e 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1112,6 +1112,7 @@ void World::LoadConfigSettings(bool reload) _float_configs[CONFIG_LISTEN_RANGE_TEXTEMOTE] = sConfigMgr->GetOption("ListenRange.TextEmote", 25.0f); _float_configs[CONFIG_LISTEN_RANGE_YELL] = sConfigMgr->GetOption("ListenRange.Yell", 300.0f); + _int_configs[CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS] = sConfigMgr->GetOption("Battleground.Override.LowLevels.MinPlayers", 0); _bool_configs[CONFIG_BATTLEGROUND_DISABLE_QUEST_SHARE_IN_BG] = sConfigMgr->GetOption("Battleground.DisableQuestShareInBG", false); _bool_configs[CONFIG_BATTLEGROUND_DISABLE_READY_CHECK_IN_BG] = sConfigMgr->GetOption("Battleground.DisableReadyCheckInBG", false); _bool_configs[CONFIG_BATTLEGROUND_CAST_DESERTER] = sConfigMgr->GetOption("Battleground.CastDeserter", true);