feat(Core/Arena): Add support for arena seasons completion with progression in runtime. (#19858)
Co-authored-by: Winfidonarleyan <dowlandtop@yandex.com>
This commit is contained in:
committed by
GitHub
parent
24dd7dfc21
commit
f6a0433297
@@ -0,0 +1,10 @@
|
|||||||
|
DROP TABLE IF EXISTS `active_arena_season`;
|
||||||
|
CREATE TABLE `active_arena_season` (
|
||||||
|
`season_id` TINYINT UNSIGNED NOT NULL,
|
||||||
|
`season_state` TINYINT UNSIGNED NOT NULL COMMENT 'Supported 2 states: 0 - disabled; 1 - in progress.'
|
||||||
|
)
|
||||||
|
CHARSET = utf8mb4
|
||||||
|
COLLATE = utf8mb4_unicode_ci
|
||||||
|
ENGINE = InnoDB;
|
||||||
|
|
||||||
|
INSERT INTO `active_arena_season` (`season_id`, `season_state`) VALUES (8, 1);
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
DROP TABLE IF EXISTS `arena_season_reward_group`;
|
||||||
|
CREATE TABLE `arena_season_reward_group` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`arena_season` TINYINT UNSIGNED NOT NULL,
|
||||||
|
`criteria_type` ENUM('pct', 'abs') NOT NULL DEFAULT 'pct' COMMENT 'Determines how rankings are evaluated: "pct" - percentage-based (e.g., top 20% of the ladder), "abs" - absolute position-based (e.g., top 10 players).',
|
||||||
|
`min_criteria` FLOAT NOT NULL,
|
||||||
|
`max_criteria` FLOAT NOT NULL,
|
||||||
|
`reward_mail_template_id` INT UNSIGNED,
|
||||||
|
`reward_mail_subject` VARCHAR(255),
|
||||||
|
`reward_mail_body` TEXT,
|
||||||
|
`gold_reward` INT UNSIGNED
|
||||||
|
)
|
||||||
|
CHARSET = utf8mb4
|
||||||
|
COLLATE = utf8mb4_unicode_ci
|
||||||
|
ENGINE = InnoDB;
|
||||||
|
|
||||||
|
-- Season 8
|
||||||
|
INSERT INTO `arena_season_reward_group` (`id`, `arena_season`, `criteria_type`, `min_criteria`, `max_criteria`, `reward_mail_template_id`, `reward_mail_subject`, `reward_mail_body`, `gold_reward`) VALUES
|
||||||
|
(1, 8, 'abs', 1, 1, 0, '', '', 0),
|
||||||
|
(2, 8, 'pct', 0, 0.5, 287, '', '', 0),
|
||||||
|
(3, 8, 'pct', 0.5, 3, 0, '', '', 0),
|
||||||
|
(4, 8, 'pct', 3, 10, 0, '', '', 0),
|
||||||
|
(5, 8, 'pct', 10, 35, 0, '', '', 0),
|
||||||
|
-- Season 7
|
||||||
|
(6, 7, 'abs', 1, 1, 0, '', '', 0),
|
||||||
|
(7, 7, 'pct', 0, 0.5, 286, '', '', 0),
|
||||||
|
(8, 7, 'pct', 0.5, 3, 0, '', '', 0),
|
||||||
|
(9, 7, 'pct', 3, 10, 0, '', '', 0),
|
||||||
|
(10, 7, 'pct', 10, 35, 0, '', '', 0),
|
||||||
|
-- Season 6
|
||||||
|
(11, 6, 'abs', 1, 1, 0, '', '', 0),
|
||||||
|
(12, 6, 'pct', 0, 0.5, 267, '', '', 0),
|
||||||
|
(13, 6, 'pct', 0.5, 3, 0, '', '', 0),
|
||||||
|
(14, 6, 'pct', 3, 10, 0, '', '', 0),
|
||||||
|
(15, 6, 'pct', 10, 35, 0, '', '', 0),
|
||||||
|
-- Season 5
|
||||||
|
(16, 5, 'abs', 1, 1, 0, '', '', 0),
|
||||||
|
(17, 5, 'pct', 0, 0.5, 266, '', '', 0),
|
||||||
|
(18, 5, 'pct', 0.5, 3, 0, '', '', 0),
|
||||||
|
(19, 5, 'pct', 3, 10, 0, '', '', 0),
|
||||||
|
(20, 5, 'pct', 10, 35, 0, '', '', 0);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `arena_season_reward`;
|
||||||
|
CREATE TABLE `arena_season_reward` (
|
||||||
|
`group_id` INT NOT NULL COMMENT 'id from arena_season_reward_group table',
|
||||||
|
`type` ENUM('achievement', 'item') NOT NULL DEFAULT 'achievement',
|
||||||
|
`entry` INT UNSIGNED NOT NULL COMMENT 'For item type - item entry, for achievement - achevement id.',
|
||||||
|
PRIMARY KEY (`group_id`, `type`, `entry`)
|
||||||
|
)
|
||||||
|
CHARSET = utf8mb4
|
||||||
|
COLLATE = utf8mb4_unicode_ci
|
||||||
|
ENGINE = InnoDB;
|
||||||
|
|
||||||
|
-- Season 8
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (1, 'achievement', 3336);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (2, 'item', 50435);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (2, 'achievement', 2091);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (3, 'achievement', 2092);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (4, 'achievement', 2093);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (5, 'achievement', 2090);
|
||||||
|
-- Season 7
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (6, 'achievement', 3336);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (7, 'item', 47840);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (7, 'achievement', 2091);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (8, 'achievement', 2092);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (9, 'achievement', 2093);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (10, 'achievement', 2090);
|
||||||
|
-- Season 6
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (11, 'achievement', 3336);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (12, 'item', 46171);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (12, 'achievement', 2091);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (13, 'achievement', 2092);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (14, 'achievement', 2093);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (15, 'achievement', 2090);
|
||||||
|
-- Season 5
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (16, 'achievement', 3336);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (17, 'item', 46708);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (17, 'achievement', 2091);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (18, 'achievement', 2092);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (19, 'achievement', 2093);
|
||||||
|
INSERT INTO `arena_season_reward` (`group_id`, `type`, `entry`) VALUES (20, 'achievement', 2090);
|
||||||
|
|
||||||
|
DELETE FROM `command` WHERE `name` IN ('arena season start', 'arena season reward', 'arena season set state', 'arena season deleteteams');
|
||||||
|
INSERT INTO `command` (`name`, `security`, `help`) VALUES ('arena season start', 3, 'Syntax: .arena season start $season_id\nStarts a new arena season, places the correct vendors, and sets the new season state to IN PROGRESS.');
|
||||||
|
INSERT INTO `command` (`name`, `security`, `help`) VALUES ('arena season reward', 3, 'Syntax: .arena season reward $brackets\nBuilds a ladder by combining team brackets and provides rewards from the arena_season_reward table.\nExample usage:\n \n# Combine all brackets, build a ladder, and distribute rewards among them\n.arena season reward all\n \n# Build ladders separately for 2v2, 3v3, and 5v5 brackets so each bracket receives its own rewards\n.arena season reward 2\n.arena season reward 3\n.arena season reward 5\n \n# Combine 2v2 and 3v3 brackets and distribute rewards\n.arena season reward 2,3');
|
||||||
|
INSERT INTO `command` (`name`, `security`, `help`) VALUES ('arena season deleteteams', 3, 'Syntax: .arena season deleteteams\nDeletes ALL arena teams.');
|
||||||
|
INSERT INTO `command` (`name`, `security`, `help`) VALUES ('arena season set state', 3, 'Syntax: .arena season set state $state\nChanges the state for the current season.\nAvailable states:\n 0 - disabled. Players can\'t queue for the arena.\n 1 - in progress. Players can use arena-related functionality.');
|
||||||
|
|
||||||
|
DELETE FROM `achievement_reward` WHERE `ID` IN (3336, 2091, 2092, 2093, 2090);
|
||||||
|
INSERT INTO `achievement_reward` (`ID`, `TitleA`, `TitleH`, `ItemID`, `Sender`, `Subject`, `Body`, `MailTemplateID`) VALUES
|
||||||
|
(3336, 157, 157, 0, 0, '', '', 0),
|
||||||
|
(2091, 42, 42, 0, 0, '', '', 0),
|
||||||
|
(2092, 43, 43, 0, 0, '', '', 0),
|
||||||
|
(2093, 44, 44, 0, 0, '', '', 0),
|
||||||
|
(2090, 45, 45, 0, 0, '', '', 0);
|
||||||
@@ -3789,21 +3789,6 @@ Arena.QueueAnnouncer.PlayerOnly = 0
|
|||||||
|
|
||||||
Arena.QueueAnnouncer.Detail = 3
|
Arena.QueueAnnouncer.Detail = 3
|
||||||
|
|
||||||
#
|
|
||||||
# Arena.ArenaSeason.ID
|
|
||||||
# Description: Current arena season id shown in clients.
|
|
||||||
# Default: 8
|
|
||||||
|
|
||||||
Arena.ArenaSeason.ID = 8
|
|
||||||
|
|
||||||
#
|
|
||||||
# Arena.ArenaSeason.InProgress
|
|
||||||
# Description: State of current arena season.
|
|
||||||
# Default: 1 - (Active)
|
|
||||||
# 0 - (Finished)
|
|
||||||
|
|
||||||
Arena.ArenaSeason.InProgress = 1
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Arena.ArenaStartRating
|
# Arena.ArenaStartRating
|
||||||
# Description: Start rating for new arena teams.
|
# Description: Start rating for new arena teams.
|
||||||
|
|||||||
@@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 3 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 Affero 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ArenaSeasonMgr.h"
|
||||||
|
#include "ArenaTeamMgr.h"
|
||||||
|
#include "ArenaSeasonRewardsDistributor.h"
|
||||||
|
#include "BattlegroundMgr.h"
|
||||||
|
#include "GameEventMgr.h"
|
||||||
|
#include "MapMgr.h"
|
||||||
|
#include "Player.h"
|
||||||
|
|
||||||
|
ArenaSeasonMgr* ArenaSeasonMgr::instance()
|
||||||
|
{
|
||||||
|
static ArenaSeasonMgr instance;
|
||||||
|
return &instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArenaSeasonMgr::LoadRewards()
|
||||||
|
{
|
||||||
|
uint32 oldMSTime = getMSTime();
|
||||||
|
|
||||||
|
std::unordered_map<std::string, ArenaSeasonRewardGroupCriteriaType> stringToArenaSeasonRewardGroupCriteriaType = {
|
||||||
|
{"pct", ArenaSeasonRewardGroupCriteriaType::ARENA_SEASON_REWARD_CRITERIA_TYPE_PERCENT_VALUE},
|
||||||
|
{"abs", ArenaSeasonRewardGroupCriteriaType::ARENA_SEASON_REWARD_CRITERIA_TYPE_ABSOLUTE_VALUE}
|
||||||
|
};
|
||||||
|
|
||||||
|
QueryResult result = WorldDatabase.Query("SELECT id, arena_season, criteria_type, min_criteria, max_criteria, reward_mail_template_id, reward_mail_subject, reward_mail_body, gold_reward FROM arena_season_reward_group");
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
LOG_WARN("server.loading", ">> Loaded 0 arena season rewards. DB table `arena_season_reward_group` is empty.");
|
||||||
|
LOG_INFO("server.loading", " ");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<uint32, ArenaSeasonRewardGroup> groupsMap;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Field* fields = result->Fetch();
|
||||||
|
uint32 id = fields[0].Get<uint32>();
|
||||||
|
|
||||||
|
ArenaSeasonRewardGroup group;
|
||||||
|
group.season = fields[1].Get<uint8>();
|
||||||
|
group.criteriaType = stringToArenaSeasonRewardGroupCriteriaType[fields[2].Get<std::string>()];
|
||||||
|
group.minCriteria = fields[3].Get<float>();
|
||||||
|
group.maxCriteria = fields[4].Get<float>();
|
||||||
|
group.rewardMailTemplateID = fields[5].Get<uint32>();
|
||||||
|
group.rewardMailSubject = fields[6].Get<std::string>();
|
||||||
|
group.rewardMailBody = fields[7].Get<std::string>();
|
||||||
|
group.goldReward = fields[8].Get<uint32>();
|
||||||
|
|
||||||
|
groupsMap[id] = group;
|
||||||
|
} while (result->NextRow());
|
||||||
|
|
||||||
|
std::unordered_map<std::string, ArenaSeasonRewardType> stringToArenaSeasonRewardType = {
|
||||||
|
{"achievement", ArenaSeasonRewardType::ARENA_SEASON_REWARD_TYPE_ACHIEVEMENT},
|
||||||
|
{"item", ArenaSeasonRewardType::ARENA_SEASON_REWARD_TYPE_ITEM}
|
||||||
|
};
|
||||||
|
|
||||||
|
result = WorldDatabase.Query("SELECT group_id, type, entry FROM arena_season_reward");
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
LOG_WARN("server.loading", ">> Loaded 0 arena season rewards. DB table `arena_season_reward` is empty.");
|
||||||
|
LOG_INFO("server.loading", " ");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Field* fields = result->Fetch();
|
||||||
|
uint32 groupId = fields[0].Get<uint32>();
|
||||||
|
|
||||||
|
ArenaSeasonReward reward;
|
||||||
|
reward.type = stringToArenaSeasonRewardType[fields[1].Get<std::string>()];
|
||||||
|
reward.entry = fields[2].Get<uint32>();
|
||||||
|
|
||||||
|
auto itr = groupsMap.find(groupId);
|
||||||
|
ASSERT(itr != groupsMap.end(), "Unknown arena_season_reward_group ({}) in arena_season_reward", groupId);
|
||||||
|
|
||||||
|
(reward.type == ARENA_SEASON_REWARD_TYPE_ITEM) ?
|
||||||
|
groupsMap[groupId].itemRewards.push_back(reward) :
|
||||||
|
groupsMap[groupId].achievementRewards.push_back(reward);
|
||||||
|
|
||||||
|
} while (result->NextRow());
|
||||||
|
|
||||||
|
for (auto const& itr : groupsMap)
|
||||||
|
_arenaSeasonRewardGroupsStore[itr.second.season].push_back(itr.second);
|
||||||
|
|
||||||
|
LOG_INFO("server.loading", ">> Loaded {} arena season rewards in {} ms", (uint32)groupsMap.size(), GetMSTimeDiffToNow(oldMSTime));
|
||||||
|
LOG_INFO("server.loading", " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArenaSeasonMgr::LoadActiveSeason()
|
||||||
|
{
|
||||||
|
QueryResult result = CharacterDatabase.Query("SELECT season_id, season_state FROM active_arena_season");
|
||||||
|
ASSERT(result, "active_arena_season can't be empty");
|
||||||
|
|
||||||
|
Field* fields = result->Fetch();
|
||||||
|
_currentSeason = fields[0].Get<uint8>();
|
||||||
|
_currentSeasonState = static_cast<ArenaSeasonState>(fields[1].Get<uint8>());
|
||||||
|
|
||||||
|
uint16 eventID = GameEventForArenaSeason(_currentSeason);
|
||||||
|
sGameEventMgr->StartEvent(eventID, true);
|
||||||
|
|
||||||
|
LOG_INFO("server.loading", "Arena Season {} loaded...", _currentSeason);
|
||||||
|
LOG_INFO("server.loading", " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArenaSeasonMgr::RewardTeamsForTheSeason(std::shared_ptr<ArenaTeamFilter> teamsFilter)
|
||||||
|
{
|
||||||
|
ArenaSeasonTeamRewarderImpl rewarder = ArenaSeasonTeamRewarderImpl();
|
||||||
|
ArenaSeasonRewardDistributor distributor = ArenaSeasonRewardDistributor(&rewarder);
|
||||||
|
std::vector<ArenaSeasonRewardGroup> rewards = _arenaSeasonRewardGroupsStore[GetCurrentSeason()];
|
||||||
|
ArenaTeamMgr::ArenaTeamContainer filteredTeams = teamsFilter->Filter(sArenaTeamMgr->GetArenaTeams());
|
||||||
|
distributor.DistributeRewards(filteredTeams, rewards);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ArenaSeasonMgr::CanDeleteArenaTeams()
|
||||||
|
{
|
||||||
|
std::vector<ArenaSeasonRewardGroup> rewards = _arenaSeasonRewardGroupsStore[GetCurrentSeason()];
|
||||||
|
if (rewards.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (auto const& bg : sBattlegroundMgr->GetActiveBattlegrounds())
|
||||||
|
if (bg->isRated())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArenaSeasonMgr::DeleteArenaTeams()
|
||||||
|
{
|
||||||
|
if (!CanDeleteArenaTeams())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Cleanup queue first.
|
||||||
|
std::vector<BattlegroundQueueTypeId> arenasQueueTypes = {BATTLEGROUND_QUEUE_2v2, BATTLEGROUND_QUEUE_3v3, BATTLEGROUND_QUEUE_5v5};
|
||||||
|
for (BattlegroundQueueTypeId queueType : arenasQueueTypes)
|
||||||
|
{
|
||||||
|
auto queue = sBattlegroundMgr->GetBattlegroundQueue(queueType);
|
||||||
|
for (auto const& [playerGUID, other] : queue.m_QueuedPlayers)
|
||||||
|
queue.RemovePlayer(playerGUID, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
sArenaTeamMgr->DeleteAllArenaTeams();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArenaSeasonMgr::ChangeCurrentSeason(uint8 season)
|
||||||
|
{
|
||||||
|
if (_currentSeason == season)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint16 currentEventID = GameEventForArenaSeason(_currentSeason);
|
||||||
|
sGameEventMgr->StopEvent(currentEventID, true);
|
||||||
|
|
||||||
|
uint16 newEventID = GameEventForArenaSeason(season);
|
||||||
|
sGameEventMgr->StartEvent(newEventID, true);
|
||||||
|
|
||||||
|
_currentSeason = season;
|
||||||
|
_currentSeasonState = ARENA_SEASON_STATE_IN_PROGRESS;
|
||||||
|
|
||||||
|
CharacterDatabase.Execute("UPDATE active_arena_season SET season_id = {}, season_state = {}", _currentSeason, _currentSeasonState);
|
||||||
|
|
||||||
|
BroadcastUpdatedWorldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArenaSeasonMgr::SetSeasonState(ArenaSeasonState state)
|
||||||
|
{
|
||||||
|
if (_currentSeasonState == state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_currentSeasonState = state;
|
||||||
|
|
||||||
|
CharacterDatabase.Execute("UPDATE active_arena_season SET season_state = {}", _currentSeasonState);
|
||||||
|
|
||||||
|
BroadcastUpdatedWorldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16 ArenaSeasonMgr::GameEventForArenaSeason(uint8 season)
|
||||||
|
{
|
||||||
|
QueryResult result = WorldDatabase.Query("SELECT eventEntry FROM game_event_arena_seasons WHERE season = '{}'", season);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
LOG_ERROR("arenaseasonmgr", "ArenaSeason ({}) must be an existant Arena Season", season);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Field* fields = result->Fetch();
|
||||||
|
return fields[0].Get<uint16>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArenaSeasonMgr::BroadcastUpdatedWorldState()
|
||||||
|
{
|
||||||
|
sMapMgr->DoForAllMaps([](Map* map)
|
||||||
|
{
|
||||||
|
// Ignore instanceable maps, players will get a fresh state once they change the map.
|
||||||
|
if (map->Instanceable())
|
||||||
|
return;
|
||||||
|
|
||||||
|
map->DoForAllPlayers([&](Player* player)
|
||||||
|
{
|
||||||
|
uint32 currZone, currArea;
|
||||||
|
player->GetZoneAndAreaId(currZone, currArea);
|
||||||
|
player->SendInitWorldStates(currZone, currArea);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 3 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 Affero 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ARENASEASONMGR_H
|
||||||
|
#define _ARENASEASONMGR_H
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "ArenaTeamFilter.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
enum ArenaSeasonState
|
||||||
|
{
|
||||||
|
ARENA_SEASON_STATE_DISABLED = 0,
|
||||||
|
ARENA_SEASON_STATE_IN_PROGRESS = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ArenaSeasonRewardType
|
||||||
|
{
|
||||||
|
ARENA_SEASON_REWARD_TYPE_ITEM,
|
||||||
|
ARENA_SEASON_REWARD_TYPE_ACHIEVEMENT
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ArenaSeasonRewardGroupCriteriaType
|
||||||
|
{
|
||||||
|
ARENA_SEASON_REWARD_CRITERIA_TYPE_PERCENT_VALUE,
|
||||||
|
ARENA_SEASON_REWARD_CRITERIA_TYPE_ABSOLUTE_VALUE
|
||||||
|
};
|
||||||
|
|
||||||
|
// ArenaSeasonReward represents one reward, it can be an item or achievement.
|
||||||
|
struct ArenaSeasonReward
|
||||||
|
{
|
||||||
|
ArenaSeasonReward() = default;
|
||||||
|
|
||||||
|
// Item or acheivement entry.
|
||||||
|
uint32 entry{};
|
||||||
|
|
||||||
|
ArenaSeasonRewardType type{ARENA_SEASON_REWARD_TYPE_ITEM};
|
||||||
|
|
||||||
|
// Used in unit tests.
|
||||||
|
bool operator==(const ArenaSeasonReward& other) const
|
||||||
|
{
|
||||||
|
return entry == other.entry && type == other.type;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ArenaSeasonRewardGroup
|
||||||
|
{
|
||||||
|
ArenaSeasonRewardGroup() = default;
|
||||||
|
|
||||||
|
uint8 season{};
|
||||||
|
|
||||||
|
ArenaSeasonRewardGroupCriteriaType criteriaType;
|
||||||
|
|
||||||
|
float minCriteria{};
|
||||||
|
float maxCriteria{};
|
||||||
|
|
||||||
|
uint32 rewardMailTemplateID{};
|
||||||
|
std::string rewardMailSubject{};
|
||||||
|
std::string rewardMailBody{};
|
||||||
|
uint32 goldReward{};
|
||||||
|
|
||||||
|
std::vector<ArenaSeasonReward> itemRewards;
|
||||||
|
std::vector<ArenaSeasonReward> achievementRewards;
|
||||||
|
|
||||||
|
// Used in unit tests.
|
||||||
|
bool operator==(const ArenaSeasonRewardGroup& other) const
|
||||||
|
{
|
||||||
|
return minCriteria == other.minCriteria &&
|
||||||
|
maxCriteria == other.maxCriteria &&
|
||||||
|
criteriaType == other.criteriaType &&
|
||||||
|
itemRewards == other.itemRewards &&
|
||||||
|
achievementRewards == other.achievementRewards;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArenaSeasonMgr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static ArenaSeasonMgr* instance();
|
||||||
|
|
||||||
|
using ArenaSeasonRewardGroupsBySeasonContainer = std::unordered_map<uint8, std::vector<ArenaSeasonRewardGroup>>;
|
||||||
|
|
||||||
|
// Loading functions
|
||||||
|
void LoadRewards();
|
||||||
|
void LoadActiveSeason();
|
||||||
|
|
||||||
|
// Season management functions
|
||||||
|
void ChangeCurrentSeason(uint8 season);
|
||||||
|
uint8 GetCurrentSeason() { return _currentSeason; }
|
||||||
|
|
||||||
|
void SetSeasonState(ArenaSeasonState state);
|
||||||
|
ArenaSeasonState GetSeasonState() { return _currentSeasonState; }
|
||||||
|
|
||||||
|
// Season completion functions
|
||||||
|
void RewardTeamsForTheSeason(std::shared_ptr<ArenaTeamFilter> teamsFilter);
|
||||||
|
bool CanDeleteArenaTeams();
|
||||||
|
void DeleteArenaTeams();
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16 GameEventForArenaSeason(uint8 season);
|
||||||
|
void BroadcastUpdatedWorldState();
|
||||||
|
|
||||||
|
ArenaSeasonRewardGroupsBySeasonContainer _arenaSeasonRewardGroupsStore;
|
||||||
|
|
||||||
|
uint8 _currentSeason{};
|
||||||
|
ArenaSeasonState _currentSeasonState{};
|
||||||
|
};
|
||||||
|
|
||||||
|
#define sArenaSeasonMgr ArenaSeasonMgr::instance()
|
||||||
|
|
||||||
|
#endif // _ARENASEASONMGR_H
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 3 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 Affero 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ArenaSeasonRewardsDistributor.h"
|
||||||
|
#include "AchievementMgr.h"
|
||||||
|
#include "CharacterDatabase.h"
|
||||||
|
#include "Mail.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
constexpr float minPctTeamGamesForMemberToGetReward = 30;
|
||||||
|
|
||||||
|
void ArenaSeasonTeamRewarderImpl::RewardTeamWithRewardGroup(ArenaTeam *arenaTeam, const ArenaSeasonRewardGroup &rewardGroup)
|
||||||
|
{
|
||||||
|
RewardWithMail(arenaTeam, rewardGroup);
|
||||||
|
RewardWithAchievements(arenaTeam, rewardGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArenaSeasonTeamRewarderImpl::RewardWithMail(ArenaTeam* arenaTeam, ArenaSeasonRewardGroup const & rewardGroup)
|
||||||
|
{
|
||||||
|
if (rewardGroup.itemRewards.empty() && rewardGroup.goldReward == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const uint32 npcKingDondSender = 18897;
|
||||||
|
|
||||||
|
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
||||||
|
for (auto const& member : arenaTeam->GetMembers())
|
||||||
|
{
|
||||||
|
uint32 teamSeasonGames = arenaTeam->GetStats().SeasonGames;
|
||||||
|
// Avoid division by zero.
|
||||||
|
if (teamSeasonGames == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float memberParticipationPercentage = (static_cast<float>(member.SeasonGames) / teamSeasonGames) * 100;
|
||||||
|
if (memberParticipationPercentage < minPctTeamGamesForMemberToGetReward)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Player* player = ObjectAccessor::FindPlayer(member.Guid);
|
||||||
|
|
||||||
|
auto draft = rewardGroup.rewardMailTemplateID > 0 ?
|
||||||
|
MailDraft(rewardGroup.rewardMailTemplateID, false) :
|
||||||
|
MailDraft(rewardGroup.rewardMailSubject, rewardGroup.rewardMailBody);
|
||||||
|
|
||||||
|
if (rewardGroup.goldReward > 0)
|
||||||
|
draft.AddMoney(rewardGroup.goldReward);
|
||||||
|
|
||||||
|
for (auto const& reward : rewardGroup.itemRewards)
|
||||||
|
if (Item* item = Item::CreateItem(reward.entry, 1))
|
||||||
|
{
|
||||||
|
item->SaveToDB(trans);
|
||||||
|
draft.AddItem(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
draft.SendMailTo(trans, MailReceiver(player, member.Guid.GetCounter()), MailSender(npcKingDondSender));
|
||||||
|
}
|
||||||
|
CharacterDatabase.CommitTransaction(trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArenaSeasonTeamRewarderImpl::RewardWithAchievements(ArenaTeam* arenaTeam, ArenaSeasonRewardGroup const & rewardGroup)
|
||||||
|
{
|
||||||
|
if (rewardGroup.achievementRewards.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (auto const& member : arenaTeam->GetMembers())
|
||||||
|
{
|
||||||
|
uint32 teamSeasonGames = arenaTeam->GetStats().SeasonGames;
|
||||||
|
// Avoid division by zero.
|
||||||
|
if (teamSeasonGames == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float memberParticipationPercentage = (static_cast<float>(member.SeasonGames) / teamSeasonGames) * 100;
|
||||||
|
if (memberParticipationPercentage < minPctTeamGamesForMemberToGetReward)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Player* player = ObjectAccessor::FindPlayer(member.Guid);
|
||||||
|
for (auto const& reward : rewardGroup.achievementRewards)
|
||||||
|
{
|
||||||
|
AchievementEntry const* achievement = sAchievementStore.LookupEntry(reward.entry);
|
||||||
|
if (!achievement)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (player)
|
||||||
|
player->CompletedAchievement(achievement);
|
||||||
|
else
|
||||||
|
sAchievementMgr->CompletedAchievementForOfflinePlayer(member.Guid.GetCounter(), achievement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArenaSeasonRewardDistributor::ArenaSeasonRewardDistributor(ArenaSeasonTeamRewarder* rewarder)
|
||||||
|
: _rewarder(rewarder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArenaSeasonRewardDistributor::DistributeRewards(ArenaTeamMgr::ArenaTeamContainer &arenaTeams, std::vector<ArenaSeasonRewardGroup> &rewardGroups)
|
||||||
|
{
|
||||||
|
std::vector<ArenaTeam*> sortedTeams;
|
||||||
|
sortedTeams.reserve(arenaTeams.size());
|
||||||
|
|
||||||
|
static constexpr uint16 minRequiredGames = 30;
|
||||||
|
|
||||||
|
for (auto const& [id, team] : arenaTeams)
|
||||||
|
if (team->GetStats().SeasonGames >= minRequiredGames)
|
||||||
|
sortedTeams.push_back(team);
|
||||||
|
|
||||||
|
std::sort(sortedTeams.begin(), sortedTeams.end(), [](ArenaTeam* a, ArenaTeam* b) {
|
||||||
|
return a->GetRating() > b->GetRating();
|
||||||
|
});
|
||||||
|
|
||||||
|
std::vector<ArenaSeasonRewardGroup> pctRewardGroup;
|
||||||
|
std::vector<ArenaSeasonRewardGroup> absRewardGroup;
|
||||||
|
|
||||||
|
for (auto const& reward : rewardGroups)
|
||||||
|
{
|
||||||
|
if (reward.criteriaType == ARENA_SEASON_REWARD_CRITERIA_TYPE_PERCENT_VALUE)
|
||||||
|
pctRewardGroup.push_back(reward);
|
||||||
|
else
|
||||||
|
absRewardGroup.push_back(reward);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t totalTeams = sortedTeams.size();
|
||||||
|
for (auto const& rewardGroup : pctRewardGroup)
|
||||||
|
{
|
||||||
|
size_t minIndex = static_cast<size_t>(rewardGroup.minCriteria * totalTeams / 100);
|
||||||
|
size_t maxIndex = static_cast<size_t>(rewardGroup.maxCriteria * totalTeams / 100);
|
||||||
|
|
||||||
|
minIndex = std::min(minIndex, totalTeams);
|
||||||
|
maxIndex = std::min(maxIndex, totalTeams);
|
||||||
|
|
||||||
|
for (size_t i = minIndex; i < maxIndex; ++i)
|
||||||
|
{
|
||||||
|
ArenaTeam* team = sortedTeams[i];
|
||||||
|
_rewarder->RewardTeamWithRewardGroup(team, rewardGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& rewardGroup : absRewardGroup)
|
||||||
|
{
|
||||||
|
size_t minIndex = rewardGroup.minCriteria-1; // Top 1 team is the team with index 0, so we need make -1.
|
||||||
|
size_t maxIndex = rewardGroup.maxCriteria;
|
||||||
|
|
||||||
|
minIndex = std::max(minIndex, size_t(0));
|
||||||
|
minIndex = std::min(minIndex, totalTeams);
|
||||||
|
maxIndex = std::min(maxIndex, totalTeams);
|
||||||
|
|
||||||
|
for (size_t i = minIndex; i < maxIndex; ++i)
|
||||||
|
{
|
||||||
|
ArenaTeam* team = sortedTeams[i];
|
||||||
|
_rewarder->RewardTeamWithRewardGroup(team, rewardGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 3 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 Affero 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ARENASEASONREWARDDISTRIBUTOR_H
|
||||||
|
#define _ARENASEASONREWARDDISTRIBUTOR_H
|
||||||
|
|
||||||
|
#include "ArenaSeasonMgr.h"
|
||||||
|
#include "ArenaTeam.h"
|
||||||
|
#include "ArenaTeamMgr.h"
|
||||||
|
|
||||||
|
class ArenaSeasonTeamRewarder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~ArenaSeasonTeamRewarder() = default;
|
||||||
|
|
||||||
|
virtual void RewardTeamWithRewardGroup(ArenaTeam* arenaTeam, ArenaSeasonRewardGroup const & rewardGroup) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArenaSeasonTeamRewarderImpl: public ArenaSeasonTeamRewarder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void RewardTeamWithRewardGroup(ArenaTeam* arenaTeam, ArenaSeasonRewardGroup const & rewardGroup) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void RewardWithMail(ArenaTeam* arenaTeam, ArenaSeasonRewardGroup const & rewardGroup);
|
||||||
|
void RewardWithAchievements(ArenaTeam* arenaTeam, ArenaSeasonRewardGroup const & rewardGroup);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArenaSeasonRewardDistributor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ArenaSeasonRewardDistributor(ArenaSeasonTeamRewarder* rewarder);
|
||||||
|
|
||||||
|
void DistributeRewards(ArenaTeamMgr::ArenaTeamContainer& arenaTeams, std::vector<ArenaSeasonRewardGroup>& rewardGroups);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ArenaSeasonTeamRewarder* _rewarder;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _ARENASEASONREWARDDISTRIBUTOR_H
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 3 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 Affero 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ARENATEAMFILTER_H
|
||||||
|
#define _ARENATEAMFILTER_H
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "ArenaTeamMgr.h"
|
||||||
|
#include "ArenaTeam.h"
|
||||||
|
#include "Tokenize.h"
|
||||||
|
#include "StringConvert.h"
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
class ArenaTeamFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~ArenaTeamFilter() = default;
|
||||||
|
|
||||||
|
virtual ArenaTeamMgr::ArenaTeamContainer Filter(ArenaTeamMgr::ArenaTeamContainer teams) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArenaTeamFilterByTypes : public ArenaTeamFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ArenaTeamFilterByTypes(std::vector<uint8> validTypes) : _validTypes(validTypes) {}
|
||||||
|
|
||||||
|
ArenaTeamMgr::ArenaTeamContainer Filter(ArenaTeamMgr::ArenaTeamContainer teams) override
|
||||||
|
{
|
||||||
|
ArenaTeamMgr::ArenaTeamContainer result;
|
||||||
|
|
||||||
|
for (auto const& pair : teams)
|
||||||
|
{
|
||||||
|
ArenaTeam* team = pair.second;
|
||||||
|
for (uint8 arenaType : _validTypes)
|
||||||
|
{
|
||||||
|
if (team->GetType() == arenaType)
|
||||||
|
{
|
||||||
|
result[pair.first] = team;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<uint8> _validTypes;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArenaTeamFilterAllTeams : public ArenaTeamFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ArenaTeamMgr::ArenaTeamContainer Filter(ArenaTeamMgr::ArenaTeamContainer teams) override
|
||||||
|
{
|
||||||
|
return teams;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArenaTeamFilterFactoryByUserInput
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::unique_ptr<ArenaTeamFilter> CreateFilterByUserInput(std::string userInput)
|
||||||
|
{
|
||||||
|
std::transform(userInput.begin(), userInput.end(), userInput.begin(),
|
||||||
|
[](unsigned char c) { return std::tolower(c); });
|
||||||
|
|
||||||
|
if (userInput == "all")
|
||||||
|
return std::make_unique<ArenaTeamFilterAllTeams>();
|
||||||
|
|
||||||
|
// Parse the input string (e.g., "2,3") into valid types
|
||||||
|
std::vector<uint8> validTypes = ParseTypes(userInput);
|
||||||
|
|
||||||
|
if (!validTypes.empty())
|
||||||
|
return std::make_unique<ArenaTeamFilterByTypes>(validTypes);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<uint8> ParseTypes(std::string_view userInput)
|
||||||
|
{
|
||||||
|
std::vector<uint8> validTypes;
|
||||||
|
auto tokens = Acore::Tokenize(userInput, ',', false);
|
||||||
|
|
||||||
|
for (auto const& token : tokens)
|
||||||
|
if (auto typeOpt = Acore::StringTo<uint8>(token))
|
||||||
|
validTypes.push_back(*typeOpt);
|
||||||
|
|
||||||
|
return validTypes;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _ARENATEAMFILTER_H
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include "ArenaTeam.h"
|
#include "ArenaTeam.h"
|
||||||
#include "ArenaTeamMgr.h"
|
#include "ArenaTeamMgr.h"
|
||||||
|
#include "ArenaSeasonMgr.h"
|
||||||
#include "BattlegroundMgr.h"
|
#include "BattlegroundMgr.h"
|
||||||
#include "CharacterCache.h"
|
#include "CharacterCache.h"
|
||||||
#include "Group.h"
|
#include "Group.h"
|
||||||
@@ -658,7 +659,7 @@ uint32 ArenaTeam::GetPoints(uint32 memberRating)
|
|||||||
|
|
||||||
if (rating <= 1500)
|
if (rating <= 1500)
|
||||||
{
|
{
|
||||||
if (sWorld->getIntConfig(CONFIG_ARENA_SEASON_ID) < 6 && !sWorld->getIntConfig(CONFIG_LEGACY_ARENA_POINTS_CALC))
|
if (sArenaSeasonMgr->GetCurrentSeason() < 6 && !sWorld->getIntConfig(CONFIG_LEGACY_ARENA_POINTS_CALC))
|
||||||
points = (float)rating * 0.22f + 14.0f;
|
points = (float)rating * 0.22f + 14.0f;
|
||||||
else
|
else
|
||||||
points = 344;
|
points = 344;
|
||||||
|
|||||||
@@ -125,6 +125,27 @@ void ArenaTeamMgr::RemoveArenaTeam(uint32 arenaTeamId)
|
|||||||
ArenaTeamStore.erase(arenaTeamId);
|
ArenaTeamStore.erase(arenaTeamId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ArenaTeamMgr::DeleteAllArenaTeams()
|
||||||
|
{
|
||||||
|
for (auto const& [id, team] : ArenaTeamStore)
|
||||||
|
{
|
||||||
|
while (team->GetMembersSize() > 0)
|
||||||
|
team->DelMember(team->GetMembers().front().Guid, false);
|
||||||
|
|
||||||
|
delete team;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArenaTeamStore.clear();
|
||||||
|
|
||||||
|
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
||||||
|
trans->Append("DELETE FROM arena_team_member");
|
||||||
|
trans->Append("DELETE FROM arena_team");
|
||||||
|
trans->Append("DELETE FROM character_arena_stats");
|
||||||
|
CharacterDatabase.CommitTransaction(trans);
|
||||||
|
|
||||||
|
NextArenaTeamId = 1;
|
||||||
|
}
|
||||||
|
|
||||||
uint32 ArenaTeamMgr::GenerateArenaTeamId()
|
uint32 ArenaTeamMgr::GenerateArenaTeamId()
|
||||||
{
|
{
|
||||||
if (NextArenaTeamId >= MAX_ARENA_TEAM_ID)
|
if (NextArenaTeamId >= MAX_ARENA_TEAM_ID)
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ public:
|
|||||||
void AddArenaTeam(ArenaTeam* arenaTeam);
|
void AddArenaTeam(ArenaTeam* arenaTeam);
|
||||||
void RemoveArenaTeam(uint32 Id);
|
void RemoveArenaTeam(uint32 Id);
|
||||||
|
|
||||||
|
void DeleteAllArenaTeams();
|
||||||
|
|
||||||
ArenaTeamContainer::iterator GetArenaTeamMapBegin() { return ArenaTeamStore.begin(); }
|
ArenaTeamContainer::iterator GetArenaTeamMapBegin() { return ArenaTeamStore.begin(); }
|
||||||
ArenaTeamContainer::iterator GetArenaTeamMapEnd() { return ArenaTeamStore.end(); }
|
ArenaTeamContainer::iterator GetArenaTeamMapEnd() { return ArenaTeamStore.end(); }
|
||||||
ArenaTeamContainer& GetArenaTeams() { return ArenaTeamStore; }
|
ArenaTeamContainer& GetArenaTeams() { return ArenaTeamStore; }
|
||||||
|
|||||||
@@ -334,6 +334,18 @@ Battleground* BattlegroundMgr::GetBattlegroundTemplate(BattlegroundTypeId bgType
|
|||||||
return bgs.empty() ? nullptr : bgs.begin()->second;
|
return bgs.empty() ? nullptr : bgs.begin()->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Battleground const*> BattlegroundMgr::GetActiveBattlegrounds()
|
||||||
|
{
|
||||||
|
std::vector<Battleground const*> result;
|
||||||
|
|
||||||
|
for (auto const& [bgType, bgData] : bgDataStore)
|
||||||
|
for (auto const& [id, bg] : bgData._Battlegrounds)
|
||||||
|
if (bg->GetStatus() == STATUS_WAIT_JOIN || bg->GetStatus() == STATUS_IN_PROGRESS)
|
||||||
|
result.push_back(static_cast<const Battleground*>(bg));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
uint32 BattlegroundMgr::CreateClientVisibleInstanceId(BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id)
|
uint32 BattlegroundMgr::CreateClientVisibleInstanceId(BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id)
|
||||||
{
|
{
|
||||||
if (IsArenaType(bgTypeId))
|
if (IsArenaType(bgTypeId))
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ public:
|
|||||||
Battleground* GetBattleground(uint32 instanceID, BattlegroundTypeId bgTypeId);
|
Battleground* GetBattleground(uint32 instanceID, BattlegroundTypeId bgTypeId);
|
||||||
Battleground* GetBattlegroundTemplate(BattlegroundTypeId bgTypeId);
|
Battleground* GetBattlegroundTemplate(BattlegroundTypeId bgTypeId);
|
||||||
Battleground* CreateNewBattleground(BattlegroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated);
|
Battleground* CreateNewBattleground(BattlegroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated);
|
||||||
|
std::vector<Battleground const*> GetActiveBattlegrounds();
|
||||||
|
|
||||||
void AddBattleground(Battleground* bg);
|
void AddBattleground(Battleground* bg);
|
||||||
void RemoveBattleground(BattlegroundTypeId bgTypeId, uint32 instanceId);
|
void RemoveBattleground(BattlegroundTypeId bgTypeId, uint32 instanceId);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "ArenaSpectator.h"
|
#include "ArenaSpectator.h"
|
||||||
#include "ArenaTeam.h"
|
#include "ArenaTeam.h"
|
||||||
#include "ArenaTeamMgr.h"
|
#include "ArenaTeamMgr.h"
|
||||||
|
#include "ArenaSeasonMgr.h"
|
||||||
#include "Battlefield.h"
|
#include "Battlefield.h"
|
||||||
#include "BattlefieldMgr.h"
|
#include "BattlefieldMgr.h"
|
||||||
#include "BattlefieldWG.h"
|
#include "BattlefieldWG.h"
|
||||||
@@ -8243,9 +8244,9 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
|
|||||||
data << uint32(0x8d4) << uint32(0x0); // 5
|
data << uint32(0x8d4) << uint32(0x0); // 5
|
||||||
data << uint32(0x8d3) << uint32(0x0); // 6
|
data << uint32(0x8d3) << uint32(0x0); // 6
|
||||||
// 7 1 - Arena season in progress, 0 - end of season
|
// 7 1 - Arena season in progress, 0 - end of season
|
||||||
data << uint32(0xC77) << uint32(sWorld->getBoolConfig(CONFIG_ARENA_SEASON_IN_PROGRESS));
|
data << uint32(0xC77) << uint32(sArenaSeasonMgr->GetSeasonState() == ARENA_SEASON_STATE_IN_PROGRESS);
|
||||||
// 8 Arena season id
|
// 8 Arena season id
|
||||||
data << uint32(0xF3D) << uint32(sWorld->getIntConfig(CONFIG_ARENA_SEASON_ID));
|
data << uint32(0xF3D) << uint32(sArenaSeasonMgr->GetCurrentSeason());
|
||||||
|
|
||||||
if (mapid == 530) // Outland
|
if (mapid == 530) // Outland
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1163,34 +1163,6 @@ uint32 GameEventMgr::StartSystem() // return the next
|
|||||||
return delay;
|
return delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameEventMgr::StartArenaSeason()
|
|
||||||
{
|
|
||||||
uint8 season = sWorld->getIntConfig(CONFIG_ARENA_SEASON_ID);
|
|
||||||
|
|
||||||
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_GAME_EVENT_ARENA_SEASON);
|
|
||||||
stmt->SetData(0, season);
|
|
||||||
PreparedQueryResult result = WorldDatabase.Query(stmt);
|
|
||||||
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
LOG_ERROR("gameevent", "ArenaSeason ({}) must be an existant Arena Season", season);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Field* fields = result->Fetch();
|
|
||||||
uint16 eventId = fields[0].Get<uint8>();
|
|
||||||
|
|
||||||
if (eventId >= _gameEvent.size())
|
|
||||||
{
|
|
||||||
LOG_ERROR("gameevent", "EventEntry {} for ArenaSeason ({}) does not exists", eventId, season);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
StartEvent(eventId, true);
|
|
||||||
LOG_INFO("server.loading", "Arena Season {} started...", season);
|
|
||||||
LOG_INFO("server.loading", " ");
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 GameEventMgr::Update() // return the next event delay in ms
|
uint32 GameEventMgr::Update() // return the next event delay in ms
|
||||||
{
|
{
|
||||||
time_t currenttime = GameTime::GetGameTime().count();
|
time_t currenttime = GameTime::GetGameTime().count();
|
||||||
|
|||||||
@@ -115,11 +115,10 @@ public:
|
|||||||
bool IsActiveEvent(uint16 eventId) { return (_activeEvents.find(eventId) != _activeEvents.end()); }
|
bool IsActiveEvent(uint16 eventId) { return (_activeEvents.find(eventId) != _activeEvents.end()); }
|
||||||
uint32 StartSystem();
|
uint32 StartSystem();
|
||||||
void Initialize();
|
void Initialize();
|
||||||
void StartArenaSeason();
|
void StartInternalEvent(uint16 event_id);
|
||||||
void StartInternalEvent(uint16 eventId);
|
bool StartEvent(uint16 event_id, bool overwrite = false);
|
||||||
bool StartEvent(uint16 eventId, bool overwrite = false);
|
void StopEvent(uint16 event_id, bool overwrite = false);
|
||||||
void StopEvent(uint16 eventId, bool overwrite = false);
|
void HandleQuestComplete(uint32 quest_id); // called on world event type quest completions
|
||||||
void HandleQuestComplete(uint32 questId); // called on world event type quest completions
|
|
||||||
uint32 GetNPCFlag(Creature* cr);
|
uint32 GetNPCFlag(Creature* cr);
|
||||||
// Load the game event npc vendor table from the DB
|
// Load the game event npc vendor table from the DB
|
||||||
void LoadEventVendors();
|
void LoadEventVendors();
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include "ArenaTeam.h"
|
#include "ArenaTeam.h"
|
||||||
#include "ArenaTeamMgr.h"
|
#include "ArenaTeamMgr.h"
|
||||||
|
#include "ArenaSeasonMgr.h"
|
||||||
#include "Battleground.h"
|
#include "Battleground.h"
|
||||||
#include "BattlegroundMgr.h"
|
#include "BattlegroundMgr.h"
|
||||||
#include "Chat.h"
|
#include "Chat.h"
|
||||||
@@ -812,7 +813,7 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData)
|
|||||||
if (isRated)
|
if (isRated)
|
||||||
{
|
{
|
||||||
// pussywizard: for rated matches check if season is in progress!
|
// pussywizard: for rated matches check if season is in progress!
|
||||||
if (!sWorld->getBoolConfig(CONFIG_ARENA_SEASON_IN_PROGRESS))
|
if (sArenaSeasonMgr->GetSeasonState() == ARENA_SEASON_STATE_DISABLED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ateamId = _player->GetArenaTeamId(arenaslot);
|
ateamId = _player->GetArenaTeamId(arenaslot);
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ enum WorldBoolConfigs
|
|||||||
CONFIG_BATTLEGROUND_TRACK_DESERTERS,
|
CONFIG_BATTLEGROUND_TRACK_DESERTERS,
|
||||||
CONFIG_BG_XP_FOR_KILL,
|
CONFIG_BG_XP_FOR_KILL,
|
||||||
CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS,
|
CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS,
|
||||||
CONFIG_ARENA_SEASON_IN_PROGRESS,
|
|
||||||
CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE,
|
CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE,
|
||||||
CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY,
|
CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY,
|
||||||
CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN,
|
CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN,
|
||||||
@@ -325,7 +324,6 @@ enum WorldIntConfigs
|
|||||||
CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER,
|
CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER,
|
||||||
CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS,
|
CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS,
|
||||||
CONFIG_ARENA_GAMES_REQUIRED,
|
CONFIG_ARENA_GAMES_REQUIRED,
|
||||||
CONFIG_ARENA_SEASON_ID,
|
|
||||||
CONFIG_ARENA_START_RATING,
|
CONFIG_ARENA_START_RATING,
|
||||||
CONFIG_LEGACY_ARENA_POINTS_CALC,
|
CONFIG_LEGACY_ARENA_POINTS_CALC,
|
||||||
CONFIG_ARENA_START_PERSONAL_RATING,
|
CONFIG_ARENA_START_PERSONAL_RATING,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include "AchievementMgr.h"
|
#include "AchievementMgr.h"
|
||||||
#include "AddonMgr.h"
|
#include "AddonMgr.h"
|
||||||
#include "ArenaTeamMgr.h"
|
#include "ArenaTeamMgr.h"
|
||||||
|
#include "ArenaSeasonMgr.h"
|
||||||
#include "AuctionHouseMgr.h"
|
#include "AuctionHouseMgr.h"
|
||||||
#include "AutobroadcastMgr.h"
|
#include "AutobroadcastMgr.h"
|
||||||
#include "BattlefieldMgr.h"
|
#include "BattlefieldMgr.h"
|
||||||
@@ -1178,12 +1179,10 @@ void World::LoadConfigSettings(bool reload)
|
|||||||
_bool_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS] = sConfigMgr->GetOption<bool>("Arena.AutoDistributePoints", false);
|
_bool_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS] = sConfigMgr->GetOption<bool>("Arena.AutoDistributePoints", false);
|
||||||
_int_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS] = sConfigMgr->GetOption<uint32>("Arena.AutoDistributeInterval", 7); // pussywizard: spoiled by implementing constant day and hour, always 7 now
|
_int_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS] = sConfigMgr->GetOption<uint32>("Arena.AutoDistributeInterval", 7); // pussywizard: spoiled by implementing constant day and hour, always 7 now
|
||||||
_int_configs[CONFIG_ARENA_GAMES_REQUIRED] = sConfigMgr->GetOption<uint32>("Arena.GamesRequired", 10);
|
_int_configs[CONFIG_ARENA_GAMES_REQUIRED] = sConfigMgr->GetOption<uint32>("Arena.GamesRequired", 10);
|
||||||
_int_configs[CONFIG_ARENA_SEASON_ID] = sConfigMgr->GetOption<uint32>("Arena.ArenaSeason.ID", 8);
|
|
||||||
_int_configs[CONFIG_ARENA_START_RATING] = sConfigMgr->GetOption<uint32>("Arena.ArenaStartRating", 0);
|
_int_configs[CONFIG_ARENA_START_RATING] = sConfigMgr->GetOption<uint32>("Arena.ArenaStartRating", 0);
|
||||||
_int_configs[CONFIG_LEGACY_ARENA_POINTS_CALC] = sConfigMgr->GetOption<uint32>("Arena.LegacyArenaPoints", 0);
|
_int_configs[CONFIG_LEGACY_ARENA_POINTS_CALC] = sConfigMgr->GetOption<uint32>("Arena.LegacyArenaPoints", 0);
|
||||||
_int_configs[CONFIG_ARENA_START_PERSONAL_RATING] = sConfigMgr->GetOption<uint32>("Arena.ArenaStartPersonalRating", 0);
|
_int_configs[CONFIG_ARENA_START_PERSONAL_RATING] = sConfigMgr->GetOption<uint32>("Arena.ArenaStartPersonalRating", 0);
|
||||||
_int_configs[CONFIG_ARENA_START_MATCHMAKER_RATING] = sConfigMgr->GetOption<uint32>("Arena.ArenaStartMatchmakerRating", 1500);
|
_int_configs[CONFIG_ARENA_START_MATCHMAKER_RATING] = sConfigMgr->GetOption<uint32>("Arena.ArenaStartMatchmakerRating", 1500);
|
||||||
_bool_configs[CONFIG_ARENA_SEASON_IN_PROGRESS] = sConfigMgr->GetOption<bool>("Arena.ArenaSeason.InProgress", true);
|
|
||||||
_float_configs[CONFIG_ARENA_WIN_RATING_MODIFIER_1] = sConfigMgr->GetOption<float>("Arena.ArenaWinRatingModifier1", 48.0f);
|
_float_configs[CONFIG_ARENA_WIN_RATING_MODIFIER_1] = sConfigMgr->GetOption<float>("Arena.ArenaWinRatingModifier1", 48.0f);
|
||||||
_float_configs[CONFIG_ARENA_WIN_RATING_MODIFIER_2] = sConfigMgr->GetOption<float>("Arena.ArenaWinRatingModifier2", 24.0f);
|
_float_configs[CONFIG_ARENA_WIN_RATING_MODIFIER_2] = sConfigMgr->GetOption<float>("Arena.ArenaWinRatingModifier2", 24.0f);
|
||||||
_float_configs[CONFIG_ARENA_LOSE_RATING_MODIFIER] = sConfigMgr->GetOption<float>("Arena.ArenaLoseRatingModifier", 24.0f);
|
_float_configs[CONFIG_ARENA_LOSE_RATING_MODIFIER] = sConfigMgr->GetOption<float>("Arena.ArenaLoseRatingModifier", 24.0f);
|
||||||
@@ -2123,9 +2122,10 @@ void World::SetInitialWorldSettings()
|
|||||||
LOG_INFO("server.loading", "Initializing Opcodes...");
|
LOG_INFO("server.loading", "Initializing Opcodes...");
|
||||||
opcodeTable.Initialize();
|
opcodeTable.Initialize();
|
||||||
|
|
||||||
LOG_INFO("server.loading", "Starting Arena Season...");
|
LOG_INFO("server.loading", "Loading Arena Season Rewards...");
|
||||||
LOG_INFO("server.loading", " ");
|
sArenaSeasonMgr->LoadRewards();
|
||||||
sGameEventMgr->StartArenaSeason();
|
LOG_INFO("server.loading", "Loading Active Arena Season...");
|
||||||
|
sArenaSeasonMgr->LoadActiveSeason();
|
||||||
|
|
||||||
LOG_INFO("server.loading", "Loading WorldState...");
|
LOG_INFO("server.loading", "Loading WorldState...");
|
||||||
sWorldState->Load();
|
sWorldState->Load();
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ Category: commandscripts
|
|||||||
EndScriptData */
|
EndScriptData */
|
||||||
|
|
||||||
#include "ArenaTeamMgr.h"
|
#include "ArenaTeamMgr.h"
|
||||||
|
#include "ArenaSeasonMgr.h"
|
||||||
|
#include "ArenaTeamFilter.h"
|
||||||
#include "Chat.h"
|
#include "Chat.h"
|
||||||
#include "CommandScript.h"
|
#include "CommandScript.h"
|
||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
@@ -36,6 +38,19 @@ public:
|
|||||||
|
|
||||||
ChatCommandTable GetCommands() const override
|
ChatCommandTable GetCommands() const override
|
||||||
{
|
{
|
||||||
|
static ChatCommandTable arenaSeasonSetCommandTable =
|
||||||
|
{
|
||||||
|
{ "state", HandleArenaSeasonSetStateCommand, SEC_ADMINISTRATOR, Console::Yes }
|
||||||
|
};
|
||||||
|
|
||||||
|
static ChatCommandTable arenaSeasonCommandTable =
|
||||||
|
{
|
||||||
|
{ "reward", HandleArenaSeasonRewardCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||||
|
{ "deleteteams", HandleArenaSeasonDeleteTeamsCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||||
|
{ "start", HandleArenaSeasonStartCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||||
|
{ "set", arenaSeasonSetCommandTable }
|
||||||
|
};
|
||||||
|
|
||||||
static ChatCommandTable arenaCommandTable =
|
static ChatCommandTable arenaCommandTable =
|
||||||
{
|
{
|
||||||
{ "create", HandleArenaCreateCommand, SEC_ADMINISTRATOR, Console::Yes },
|
{ "create", HandleArenaCreateCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||||
@@ -44,6 +59,7 @@ public:
|
|||||||
{ "captain", HandleArenaCaptainCommand, SEC_ADMINISTRATOR, Console::No },
|
{ "captain", HandleArenaCaptainCommand, SEC_ADMINISTRATOR, Console::No },
|
||||||
{ "info", HandleArenaInfoCommand, SEC_GAMEMASTER, Console::Yes },
|
{ "info", HandleArenaInfoCommand, SEC_GAMEMASTER, Console::Yes },
|
||||||
{ "lookup", HandleArenaLookupCommand, SEC_GAMEMASTER, Console::No },
|
{ "lookup", HandleArenaLookupCommand, SEC_GAMEMASTER, Console::No },
|
||||||
|
{ "season", arenaSeasonCommandTable }
|
||||||
};
|
};
|
||||||
|
|
||||||
static ChatCommandTable commandTable =
|
static ChatCommandTable commandTable =
|
||||||
@@ -229,6 +245,80 @@ public:
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool HandleArenaSeasonRewardCommand(ChatHandler* handler, std::string teamsFilterStr)
|
||||||
|
{
|
||||||
|
std::unique_ptr<ArenaTeamFilter> uniqueFilter = ArenaTeamFilterFactoryByUserInput().CreateFilterByUserInput(teamsFilterStr);
|
||||||
|
if (!uniqueFilter)
|
||||||
|
{
|
||||||
|
handler->PSendSysMessage("Invalid filter. Please check your input.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<ArenaTeamFilter> sharedFilter = std::move(uniqueFilter);
|
||||||
|
|
||||||
|
if (!sArenaSeasonMgr->CanDeleteArenaTeams())
|
||||||
|
{
|
||||||
|
handler->PSendSysMessage("Cannot proceed. Make sure there are no active arenas and that rewards exist for the current season.");
|
||||||
|
handler->PSendSysMessage("Hint: You can disable the arena queue using the following command: .arena season set state 0");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
handler->PSendSysMessage("Distributing rewards for arena teams (types: "+teamsFilterStr+")...");
|
||||||
|
sArenaSeasonMgr->RewardTeamsForTheSeason(sharedFilter);
|
||||||
|
handler->PSendSysMessage("Rewards distributed.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleArenaSeasonDeleteTeamsCommand(ChatHandler* handler)
|
||||||
|
{
|
||||||
|
handler->PSendSysMessage("Deleting arena teams...");
|
||||||
|
sArenaSeasonMgr->DeleteArenaTeams();
|
||||||
|
handler->PSendSysMessage("Arena teams deleted.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleArenaSeasonStartCommand(ChatHandler* handler, uint8 seasonId)
|
||||||
|
{
|
||||||
|
if (seasonId == sArenaSeasonMgr->GetCurrentSeason())
|
||||||
|
{
|
||||||
|
sArenaSeasonMgr->SetSeasonState(ARENA_SEASON_STATE_IN_PROGRESS);
|
||||||
|
handler->PSendSysMessage("Arena season updated.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8 maxSeasonId = 8;
|
||||||
|
if (seasonId > maxSeasonId)
|
||||||
|
{
|
||||||
|
handler->PSendSysMessage("Invalid season id.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sArenaSeasonMgr->ChangeCurrentSeason(seasonId);
|
||||||
|
handler->PSendSysMessage("Arena season changed to season {}.", seasonId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HandleArenaSeasonSetStateCommand(ChatHandler* handler, uint8 state)
|
||||||
|
{
|
||||||
|
ArenaSeasonState seasonState;
|
||||||
|
switch (state) {
|
||||||
|
case ARENA_SEASON_STATE_DISABLED:
|
||||||
|
seasonState = ARENA_SEASON_STATE_DISABLED;
|
||||||
|
break;
|
||||||
|
case ARENA_SEASON_STATE_IN_PROGRESS:
|
||||||
|
seasonState = ARENA_SEASON_STATE_IN_PROGRESS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
handler->PSendSysMessage("Invalid state.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sArenaSeasonMgr->SetSeasonState(seasonState);
|
||||||
|
handler->PSendSysMessage("Arena season updated.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void AddSC_arena_commandscript()
|
void AddSC_arena_commandscript()
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 3 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 Affero 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Define.h"
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "ArenaSeasonRewardsDistributor.h"
|
||||||
|
|
||||||
|
class MockArenaSeasonTeamRewarder : public ArenaSeasonTeamRewarder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MOCK_METHOD(void, RewardTeamWithRewardGroup, (ArenaTeam* arenaTeam, ArenaSeasonRewardGroup const& rewardGroup), (override));
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArenaSeasonRewardDistributorTest : public ::testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
void SetUp() override
|
||||||
|
{
|
||||||
|
_mockRewarder = std::make_unique<MockArenaSeasonTeamRewarder>();
|
||||||
|
_distributor = std::make_unique<ArenaSeasonRewardDistributor>(_mockRewarder.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<MockArenaSeasonTeamRewarder> _mockRewarder;
|
||||||
|
std::unique_ptr<ArenaSeasonRewardDistributor> _distributor;
|
||||||
|
};
|
||||||
|
|
||||||
|
ArenaTeam ArenaTeamWithRating(int rating, int gamesPlayed)
|
||||||
|
{
|
||||||
|
ArenaTeamStats stats;
|
||||||
|
stats.Rating = rating;
|
||||||
|
stats.SeasonGames = gamesPlayed;
|
||||||
|
ArenaTeam team;
|
||||||
|
team.SetArenaTeamStats(stats);
|
||||||
|
return team;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test verifies that a single team receives the correct reward group when multiple percent reward groups are defined.
|
||||||
|
TEST_F(ArenaSeasonRewardDistributorTest, SingleTeamMultiplePctRewardDistribution)
|
||||||
|
{
|
||||||
|
ArenaTeamMgr::ArenaTeamContainer arenaTeams;
|
||||||
|
std::vector<ArenaSeasonRewardGroup> rewardGroups;
|
||||||
|
|
||||||
|
ArenaTeam team = ArenaTeamWithRating(1500, 50);
|
||||||
|
arenaTeams[1] = &team;
|
||||||
|
|
||||||
|
ArenaSeasonRewardGroup rewardGroup;
|
||||||
|
rewardGroup.criteriaType = ARENA_SEASON_REWARD_CRITERIA_TYPE_PERCENT_VALUE;
|
||||||
|
rewardGroup.minCriteria = 0;
|
||||||
|
rewardGroup.maxCriteria = 0.5;
|
||||||
|
rewardGroups.push_back(rewardGroup);
|
||||||
|
ArenaSeasonRewardGroup rewardGroup2;
|
||||||
|
rewardGroup2.criteriaType = ARENA_SEASON_REWARD_CRITERIA_TYPE_PERCENT_VALUE;
|
||||||
|
rewardGroup2.minCriteria = 0.5;
|
||||||
|
rewardGroup2.maxCriteria = 100;
|
||||||
|
rewardGroups.push_back(rewardGroup2);
|
||||||
|
|
||||||
|
EXPECT_CALL(*_mockRewarder, RewardTeamWithRewardGroup(&team, rewardGroup2)).Times(1);
|
||||||
|
|
||||||
|
_distributor->DistributeRewards(arenaTeams, rewardGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test verifies that a single team receives the correct reward group when multiple abs percent reward groups are defined.
|
||||||
|
TEST_F(ArenaSeasonRewardDistributorTest, SingleTeamMultipleAbsRewardDistribution)
|
||||||
|
{
|
||||||
|
ArenaTeamMgr::ArenaTeamContainer arenaTeams;
|
||||||
|
std::vector<ArenaSeasonRewardGroup> rewardGroups;
|
||||||
|
|
||||||
|
ArenaTeam team = ArenaTeamWithRating(1500, 50);
|
||||||
|
arenaTeams[1] = &team;
|
||||||
|
|
||||||
|
ArenaSeasonRewardGroup rewardGroup;
|
||||||
|
rewardGroup.criteriaType = ARENA_SEASON_REWARD_CRITERIA_TYPE_ABSOLUTE_VALUE;
|
||||||
|
rewardGroup.minCriteria = 1;
|
||||||
|
rewardGroup.maxCriteria = 1;
|
||||||
|
rewardGroups.push_back(rewardGroup);
|
||||||
|
ArenaSeasonRewardGroup rewardGroup2;
|
||||||
|
rewardGroup2.criteriaType = ARENA_SEASON_REWARD_CRITERIA_TYPE_ABSOLUTE_VALUE;
|
||||||
|
rewardGroup2.minCriteria = 2;
|
||||||
|
rewardGroup2.maxCriteria = 10;
|
||||||
|
rewardGroups.push_back(rewardGroup2);
|
||||||
|
|
||||||
|
EXPECT_CALL(*_mockRewarder, RewardTeamWithRewardGroup(&team, rewardGroup)).Times(1);
|
||||||
|
|
||||||
|
_distributor->DistributeRewards(arenaTeams, rewardGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input: 1000 teams with incremental ratings and two reward groups with 0% - 0.5% and 0.5% - 3% percentage criteria.
|
||||||
|
// Purpose: Ensures that the top 0.5% of teams receive the first reward and the next 3% receive the second reward.
|
||||||
|
// Each team should be rewarded only once.
|
||||||
|
TEST_F(ArenaSeasonRewardDistributorTest, ManyTeamsTwoRewardsDistribution)
|
||||||
|
{
|
||||||
|
ArenaTeamMgr::ArenaTeamContainer arenaTeams;
|
||||||
|
std::vector<ArenaSeasonRewardGroup> rewardGroups;
|
||||||
|
|
||||||
|
const int numTeams = 1000;
|
||||||
|
ArenaTeam teams[numTeams + 1]; // used just to prevent teams deletion
|
||||||
|
for (int i = 1; i <= numTeams; i++)
|
||||||
|
{
|
||||||
|
teams[i] = ArenaTeamWithRating(i, 50);
|
||||||
|
arenaTeams[i] = &teams[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
ArenaSeasonRewardGroup rewardGroup1;
|
||||||
|
rewardGroup1.criteriaType = ARENA_SEASON_REWARD_CRITERIA_TYPE_PERCENT_VALUE;
|
||||||
|
rewardGroup1.minCriteria = 0.0; // 0%
|
||||||
|
rewardGroup1.maxCriteria = 0.5; // 0.5% of total teams
|
||||||
|
rewardGroups.push_back(rewardGroup1);
|
||||||
|
|
||||||
|
ArenaSeasonRewardGroup rewardGroup2;
|
||||||
|
rewardGroup2.criteriaType = ARENA_SEASON_REWARD_CRITERIA_TYPE_PERCENT_VALUE;
|
||||||
|
rewardGroup2.minCriteria = 0.5; // 0.5% (the top 0.5% of the teams)
|
||||||
|
rewardGroup2.maxCriteria = 3.0; // 3% of total teams
|
||||||
|
rewardGroups.push_back(rewardGroup2);
|
||||||
|
|
||||||
|
ArenaSeasonRewardGroup rewardGroup3;
|
||||||
|
rewardGroup3.criteriaType = ARENA_SEASON_REWARD_CRITERIA_TYPE_PERCENT_VALUE;
|
||||||
|
rewardGroup3.minCriteria = 3;
|
||||||
|
rewardGroup3.maxCriteria = 10;
|
||||||
|
rewardGroups.push_back(rewardGroup3);
|
||||||
|
|
||||||
|
ArenaSeasonRewardGroup rewardGroup4;
|
||||||
|
rewardGroup4.criteriaType = ARENA_SEASON_REWARD_CRITERIA_TYPE_PERCENT_VALUE;
|
||||||
|
rewardGroup4.minCriteria = 10;
|
||||||
|
rewardGroup4.maxCriteria = 35;
|
||||||
|
rewardGroups.push_back(rewardGroup4);
|
||||||
|
|
||||||
|
// Top 1
|
||||||
|
ArenaSeasonRewardGroup rewardGroup5;
|
||||||
|
rewardGroup5.criteriaType = ARENA_SEASON_REWARD_CRITERIA_TYPE_ABSOLUTE_VALUE;
|
||||||
|
rewardGroup5.minCriteria = 1;
|
||||||
|
rewardGroup5.maxCriteria = 1;
|
||||||
|
rewardGroups.push_back(rewardGroup5);
|
||||||
|
|
||||||
|
// Calculate expected reward distributions
|
||||||
|
int expectedTeamsInGroup1 = static_cast<int>(0.005 * numTeams); // 0.5% of 1000 = 5
|
||||||
|
int expectedTeamsInGroup2 = static_cast<int>(0.03 * numTeams); // 3% of 1000 = 30
|
||||||
|
int expectedTeamsInGroup3 = static_cast<int>(0.10 * numTeams); // 10% of 1000 = 100
|
||||||
|
int expectedTeamsInGroup4 = static_cast<int>(0.35 * numTeams); // 35% of 1000 = 350
|
||||||
|
|
||||||
|
int teamsIndexCounter = numTeams;
|
||||||
|
|
||||||
|
// Expectation for rewardGroup1 (top 0.5% of teams)
|
||||||
|
for (; teamsIndexCounter > numTeams - expectedTeamsInGroup1; --teamsIndexCounter)
|
||||||
|
EXPECT_CALL(*_mockRewarder, RewardTeamWithRewardGroup(&teams[teamsIndexCounter], rewardGroup1)).Times(1);
|
||||||
|
|
||||||
|
// Expectation for rewardGroup2 (next 3% - 0.5% teams)
|
||||||
|
for (; teamsIndexCounter > numTeams - expectedTeamsInGroup2; --teamsIndexCounter)
|
||||||
|
EXPECT_CALL(*_mockRewarder, RewardTeamWithRewardGroup(&teams[teamsIndexCounter], rewardGroup2)).Times(1);
|
||||||
|
|
||||||
|
for (; teamsIndexCounter > numTeams - expectedTeamsInGroup3; --teamsIndexCounter)
|
||||||
|
EXPECT_CALL(*_mockRewarder, RewardTeamWithRewardGroup(&teams[teamsIndexCounter], rewardGroup3)).Times(1);
|
||||||
|
|
||||||
|
for (; teamsIndexCounter > numTeams - expectedTeamsInGroup4; --teamsIndexCounter)
|
||||||
|
EXPECT_CALL(*_mockRewarder, RewardTeamWithRewardGroup(&teams[teamsIndexCounter], rewardGroup4)).Times(1);
|
||||||
|
|
||||||
|
// Top 1
|
||||||
|
EXPECT_CALL(*_mockRewarder, RewardTeamWithRewardGroup(&teams[numTeams], rewardGroup5)).Times(1);
|
||||||
|
|
||||||
|
_distributor->DistributeRewards(arenaTeams, rewardGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input: Three teams where one has fewer than the minimum required games and two have enough games.
|
||||||
|
// Purpose: Ensures that only teams meeting the minimum required games threshold are eligible for rewards.
|
||||||
|
TEST_F(ArenaSeasonRewardDistributorTest, MinimumRequiredGamesFilter)
|
||||||
|
{
|
||||||
|
ArenaTeamMgr::ArenaTeamContainer arenaTeams;
|
||||||
|
std::vector<ArenaSeasonRewardGroup> rewardGroups;
|
||||||
|
|
||||||
|
// Creating three teams: one below and two above the minRequiredGames threshold (30 games)
|
||||||
|
ArenaTeam team1 = ArenaTeamWithRating(1500, 50); // Eligible, as it has 50 games
|
||||||
|
ArenaTeam team2 = ArenaTeamWithRating(1100, 20); // Not eligible, as it has only 20 games
|
||||||
|
ArenaTeam team3 = ArenaTeamWithRating(1300, 40); // Eligible, as it has 40 games
|
||||||
|
|
||||||
|
// Adding teams to the container
|
||||||
|
arenaTeams[1] = &team1;
|
||||||
|
arenaTeams[2] = &team2;
|
||||||
|
arenaTeams[3] = &team3;
|
||||||
|
|
||||||
|
// Creating a single reward group covering all teams
|
||||||
|
ArenaSeasonRewardGroup rewardGroup;
|
||||||
|
rewardGroup.criteriaType = ARENA_SEASON_REWARD_CRITERIA_TYPE_PERCENT_VALUE;
|
||||||
|
rewardGroup.minCriteria = 0.0;
|
||||||
|
rewardGroup.maxCriteria = 100;
|
||||||
|
rewardGroups.push_back(rewardGroup);
|
||||||
|
|
||||||
|
// We expect the rewarder to be called for team1 and team3, but not for team2.
|
||||||
|
EXPECT_CALL(*_mockRewarder, RewardTeamWithRewardGroup(&team1, rewardGroup)).Times(1);
|
||||||
|
EXPECT_CALL(*_mockRewarder, RewardTeamWithRewardGroup(&team3, rewardGroup)).Times(1);
|
||||||
|
EXPECT_CALL(*_mockRewarder, RewardTeamWithRewardGroup(&team2, rewardGroup)).Times(0);
|
||||||
|
|
||||||
|
_distributor->DistributeRewards(arenaTeams, rewardGroups);
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 3 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 Affero 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Define.h"
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "ArenaTeamFilter.h"
|
||||||
|
#include "ArenaTeamMgr.h"
|
||||||
|
#include "ArenaTeam.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// Used to expose Type property.
|
||||||
|
class ArenaTeamTest : public ArenaTeam
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void SetType(uint8 type)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ArenaTeam* ArenaTeamWithType(uint8 type)
|
||||||
|
{
|
||||||
|
ArenaTeamTest* team = new ArenaTeamTest();
|
||||||
|
team->SetType(type);
|
||||||
|
return team;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fixture for ArenaTeamFilter tests
|
||||||
|
class ArenaTeamFilterTest : public ::testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
void SetUp() override
|
||||||
|
{
|
||||||
|
team1 = ArenaTeamWithType(2); // 2v2
|
||||||
|
team2 = ArenaTeamWithType(3); // 3v3
|
||||||
|
team3 = ArenaTeamWithType(5); // 5v5
|
||||||
|
|
||||||
|
arenaTeams[1] = team1;
|
||||||
|
arenaTeams[2] = team2;
|
||||||
|
arenaTeams[3] = team3;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override
|
||||||
|
{
|
||||||
|
delete team1;
|
||||||
|
delete team2;
|
||||||
|
delete team3;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArenaTeamMgr::ArenaTeamContainer arenaTeams;
|
||||||
|
ArenaTeam* team1;
|
||||||
|
ArenaTeam* team2;
|
||||||
|
ArenaTeam* team3;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test for ArenaTeamFilterAllTeams: it should return all teams without filtering
|
||||||
|
TEST_F(ArenaTeamFilterTest, AllTeamsFilter)
|
||||||
|
{
|
||||||
|
ArenaTeamFilterAllTeams filter;
|
||||||
|
ArenaTeamMgr::ArenaTeamContainer result = filter.Filter(arenaTeams);
|
||||||
|
|
||||||
|
EXPECT_EQ(result.size(), arenaTeams.size());
|
||||||
|
EXPECT_EQ(result[1], team1);
|
||||||
|
EXPECT_EQ(result[2], team2);
|
||||||
|
EXPECT_EQ(result[3], team3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test for ArenaTeamFilterByTypes: should filter only teams matching the provided types
|
||||||
|
TEST_F(ArenaTeamFilterTest, FilterBySpecificTypes)
|
||||||
|
{
|
||||||
|
std::vector<uint8> validTypes = {2, 3}; // Filtering for 2v2 and 3v3
|
||||||
|
ArenaTeamFilterByTypes filter(validTypes);
|
||||||
|
|
||||||
|
ArenaTeamMgr::ArenaTeamContainer result = filter.Filter(arenaTeams);
|
||||||
|
|
||||||
|
EXPECT_EQ(result.size(), 2); // Only 2v2 and 3v3 should pass
|
||||||
|
EXPECT_EQ(result[1], team1); // team1 is 2v2
|
||||||
|
EXPECT_EQ(result[2], team2); // team2 is 3v3
|
||||||
|
EXPECT_EQ(result.find(3), result.end()); // team3 (5v5) should be filtered out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test for ArenaTeamFilterFactoryByUserInput: should create the correct filter based on input
|
||||||
|
TEST_F(ArenaTeamFilterTest, FabricCreatesFilterByInput)
|
||||||
|
{
|
||||||
|
ArenaTeamFilterFactoryByUserInput fabric;
|
||||||
|
|
||||||
|
// Test for "all" input
|
||||||
|
auto allTeamsFilter = fabric.CreateFilterByUserInput("all");
|
||||||
|
ArenaTeamMgr::ArenaTeamContainer allTeamsResult = allTeamsFilter->Filter(arenaTeams);
|
||||||
|
EXPECT_EQ(allTeamsResult.size(), arenaTeams.size()); // All teams should pass
|
||||||
|
|
||||||
|
// Test for "2,3" input
|
||||||
|
auto specificTypesFilter = fabric.CreateFilterByUserInput("2,3");
|
||||||
|
ArenaTeamMgr::ArenaTeamContainer filteredResult = specificTypesFilter->Filter(arenaTeams);
|
||||||
|
EXPECT_EQ(filteredResult.size(), 2); // Only 2v2 and 3v3 teams should pass
|
||||||
|
EXPECT_EQ(filteredResult[1], team1); // 2v2
|
||||||
|
EXPECT_EQ(filteredResult[2], team2); // 3v3
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user