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:
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.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
|
||||
# Description: Start rating for new arena teams.
|
||||
|
||||
224
src/server/game/Battlegrounds/ArenaSeason/ArenaSeasonMgr.cpp
Normal file
224
src/server/game/Battlegrounds/ArenaSeason/ArenaSeasonMgr.cpp
Normal file
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
126
src/server/game/Battlegrounds/ArenaSeason/ArenaSeasonMgr.h
Normal file
126
src/server/game/Battlegrounds/ArenaSeason/ArenaSeasonMgr.h
Normal file
@ -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
|
||||
113
src/server/game/Battlegrounds/ArenaSeason/ArenaTeamFilter.h
Normal file
113
src/server/game/Battlegrounds/ArenaSeason/ArenaTeamFilter.h
Normal file
@ -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 "ArenaTeamMgr.h"
|
||||
#include "ArenaSeasonMgr.h"
|
||||
#include "BattlegroundMgr.h"
|
||||
#include "CharacterCache.h"
|
||||
#include "Group.h"
|
||||
@ -658,7 +659,7 @@ uint32 ArenaTeam::GetPoints(uint32 memberRating)
|
||||
|
||||
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;
|
||||
else
|
||||
points = 344;
|
||||
|
||||
@ -125,6 +125,27 @@ void ArenaTeamMgr::RemoveArenaTeam(uint32 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()
|
||||
{
|
||||
if (NextArenaTeamId >= MAX_ARENA_TEAM_ID)
|
||||
|
||||
@ -43,6 +43,8 @@ public:
|
||||
void AddArenaTeam(ArenaTeam* arenaTeam);
|
||||
void RemoveArenaTeam(uint32 Id);
|
||||
|
||||
void DeleteAllArenaTeams();
|
||||
|
||||
ArenaTeamContainer::iterator GetArenaTeamMapBegin() { return ArenaTeamStore.begin(); }
|
||||
ArenaTeamContainer::iterator GetArenaTeamMapEnd() { return ArenaTeamStore.end(); }
|
||||
ArenaTeamContainer& GetArenaTeams() { return ArenaTeamStore; }
|
||||
|
||||
@ -334,6 +334,18 @@ Battleground* BattlegroundMgr::GetBattlegroundTemplate(BattlegroundTypeId bgType
|
||||
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)
|
||||
{
|
||||
if (IsArenaType(bgTypeId))
|
||||
|
||||
@ -82,6 +82,7 @@ public:
|
||||
Battleground* GetBattleground(uint32 instanceID, BattlegroundTypeId bgTypeId);
|
||||
Battleground* GetBattlegroundTemplate(BattlegroundTypeId bgTypeId);
|
||||
Battleground* CreateNewBattleground(BattlegroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated);
|
||||
std::vector<Battleground const*> GetActiveBattlegrounds();
|
||||
|
||||
void AddBattleground(Battleground* bg);
|
||||
void RemoveBattleground(BattlegroundTypeId bgTypeId, uint32 instanceId);
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
#include "ArenaSpectator.h"
|
||||
#include "ArenaTeam.h"
|
||||
#include "ArenaTeamMgr.h"
|
||||
#include "ArenaSeasonMgr.h"
|
||||
#include "Battlefield.h"
|
||||
#include "BattlefieldMgr.h"
|
||||
#include "BattlefieldWG.h"
|
||||
@ -8243,9 +8244,9 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
|
||||
data << uint32(0x8d4) << uint32(0x0); // 5
|
||||
data << uint32(0x8d3) << uint32(0x0); // 6
|
||||
// 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
|
||||
data << uint32(0xF3D) << uint32(sWorld->getIntConfig(CONFIG_ARENA_SEASON_ID));
|
||||
data << uint32(0xF3D) << uint32(sArenaSeasonMgr->GetCurrentSeason());
|
||||
|
||||
if (mapid == 530) // Outland
|
||||
{
|
||||
|
||||
@ -1163,34 +1163,6 @@ uint32 GameEventMgr::StartSystem() // return the next
|
||||
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
|
||||
{
|
||||
time_t currenttime = GameTime::GetGameTime().count();
|
||||
|
||||
@ -115,11 +115,10 @@ public:
|
||||
bool IsActiveEvent(uint16 eventId) { return (_activeEvents.find(eventId) != _activeEvents.end()); }
|
||||
uint32 StartSystem();
|
||||
void Initialize();
|
||||
void StartArenaSeason();
|
||||
void StartInternalEvent(uint16 eventId);
|
||||
bool StartEvent(uint16 eventId, bool overwrite = false);
|
||||
void StopEvent(uint16 eventId, bool overwrite = false);
|
||||
void HandleQuestComplete(uint32 questId); // called on world event type quest completions
|
||||
void StartInternalEvent(uint16 event_id);
|
||||
bool StartEvent(uint16 event_id, bool overwrite = false);
|
||||
void StopEvent(uint16 event_id, bool overwrite = false);
|
||||
void HandleQuestComplete(uint32 quest_id); // called on world event type quest completions
|
||||
uint32 GetNPCFlag(Creature* cr);
|
||||
// Load the game event npc vendor table from the DB
|
||||
void LoadEventVendors();
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
#include "ArenaTeam.h"
|
||||
#include "ArenaTeamMgr.h"
|
||||
#include "ArenaSeasonMgr.h"
|
||||
#include "Battleground.h"
|
||||
#include "BattlegroundMgr.h"
|
||||
#include "Chat.h"
|
||||
@ -812,7 +813,7 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData)
|
||||
if (isRated)
|
||||
{
|
||||
// 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;
|
||||
|
||||
ateamId = _player->GetArenaTeamId(arenaslot);
|
||||
|
||||
@ -116,7 +116,6 @@ enum WorldBoolConfigs
|
||||
CONFIG_BATTLEGROUND_TRACK_DESERTERS,
|
||||
CONFIG_BG_XP_FOR_KILL,
|
||||
CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS,
|
||||
CONFIG_ARENA_SEASON_IN_PROGRESS,
|
||||
CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE,
|
||||
CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY,
|
||||
CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN,
|
||||
@ -325,7 +324,6 @@ enum WorldIntConfigs
|
||||
CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER,
|
||||
CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS,
|
||||
CONFIG_ARENA_GAMES_REQUIRED,
|
||||
CONFIG_ARENA_SEASON_ID,
|
||||
CONFIG_ARENA_START_RATING,
|
||||
CONFIG_LEGACY_ARENA_POINTS_CALC,
|
||||
CONFIG_ARENA_START_PERSONAL_RATING,
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
#include "AchievementMgr.h"
|
||||
#include "AddonMgr.h"
|
||||
#include "ArenaTeamMgr.h"
|
||||
#include "ArenaSeasonMgr.h"
|
||||
#include "AuctionHouseMgr.h"
|
||||
#include "AutobroadcastMgr.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);
|
||||
_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_SEASON_ID] = sConfigMgr->GetOption<uint32>("Arena.ArenaSeason.ID", 8);
|
||||
_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_ARENA_START_PERSONAL_RATING] = sConfigMgr->GetOption<uint32>("Arena.ArenaStartPersonalRating", 0);
|
||||
_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_2] = sConfigMgr->GetOption<float>("Arena.ArenaWinRatingModifier2", 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...");
|
||||
opcodeTable.Initialize();
|
||||
|
||||
LOG_INFO("server.loading", "Starting Arena Season...");
|
||||
LOG_INFO("server.loading", " ");
|
||||
sGameEventMgr->StartArenaSeason();
|
||||
LOG_INFO("server.loading", "Loading Arena Season Rewards...");
|
||||
sArenaSeasonMgr->LoadRewards();
|
||||
LOG_INFO("server.loading", "Loading Active Arena Season...");
|
||||
sArenaSeasonMgr->LoadActiveSeason();
|
||||
|
||||
LOG_INFO("server.loading", "Loading WorldState...");
|
||||
sWorldState->Load();
|
||||
|
||||
@ -23,6 +23,8 @@ Category: commandscripts
|
||||
EndScriptData */
|
||||
|
||||
#include "ArenaTeamMgr.h"
|
||||
#include "ArenaSeasonMgr.h"
|
||||
#include "ArenaTeamFilter.h"
|
||||
#include "Chat.h"
|
||||
#include "CommandScript.h"
|
||||
#include "Player.h"
|
||||
@ -36,6 +38,19 @@ public:
|
||||
|
||||
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 =
|
||||
{
|
||||
{ "create", HandleArenaCreateCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
@ -44,6 +59,7 @@ public:
|
||||
{ "captain", HandleArenaCaptainCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "info", HandleArenaInfoCommand, SEC_GAMEMASTER, Console::Yes },
|
||||
{ "lookup", HandleArenaLookupCommand, SEC_GAMEMASTER, Console::No },
|
||||
{ "season", arenaSeasonCommandTable }
|
||||
};
|
||||
|
||||
static ChatCommandTable commandTable =
|
||||
@ -229,6 +245,80 @@ public:
|
||||
|
||||
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()
|
||||
|
||||
@ -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
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user