TrinityCore
Loading...
Searching...
No Matches
ArenaTeam.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "ArenaTeam.h"
19#include "ArenaTeamMgr.h"
20#include "BattlegroundMgr.h"
21#include "BattlegroundPackets.h"
22#include "CalendarPackets.h"
23#include "CharacterCache.h"
24#include "DatabaseEnv.h"
25#include "Group.h"
26#include "Log.h"
27#include "Map.h"
28#include "ObjectAccessor.h"
29#include "ObjectMgr.h"
30#include "Player.h"
31#include "World.h"
32#include "WorldSession.h"
33
35 : TeamId(0), Type(0), TeamName(), CaptainGuid(), BackgroundColor(0), EmblemStyle(0), EmblemColor(0),
36 BorderStyle(0), BorderColor(0), PreviousOpponents(0)
37{
38 Stats.WeekGames = 0;
39 Stats.SeasonGames = 0;
40 Stats.Rank = 0;
41 Stats.Rating = sWorld->getIntConfig(CONFIG_ARENA_START_RATING);
42 Stats.WeekWins = 0;
43 Stats.SeasonWins = 0;
44}
45
48
49bool ArenaTeam::Create(ObjectGuid captainGuid, uint8 type, std::string const& teamName, uint32 backgroundColor, uint8 emblemStyle, uint32 emblemColor, uint8 borderStyle, uint32 borderColor)
50{
51 // Check if captain exists
52 if (!sCharacterCache->GetCharacterCacheByGuid(captainGuid))
53 return false;
54
55 // Check if arena team name is already taken
56 if (sArenaTeamMgr->GetArenaTeamByName(teamName))
57 return false;
58
59 // Generate new arena team id
60 TeamId = sArenaTeamMgr->GenerateArenaTeamId();
61
62 // Assign member variables
63 CaptainGuid = captainGuid;
64 Type = type;
65 TeamName = teamName;
66 BackgroundColor = backgroundColor;
67 EmblemStyle = emblemStyle;
68 EmblemColor = emblemColor;
69 BorderStyle = borderStyle;
70 BorderColor = borderColor;
71 ObjectGuid::LowType captainLowGuid = captainGuid.GetCounter();
72
73 // Save arena team to db
75 stmt->setUInt32(0, TeamId);
76 stmt->setString(1, TeamName);
77 stmt->setUInt32(2, captainLowGuid);
78 stmt->setUInt8(3, Type);
79 stmt->setUInt16(4, Stats.Rating);
80 stmt->setUInt32(5, BackgroundColor);
81 stmt->setUInt8(6, EmblemStyle);
82 stmt->setUInt32(7, EmblemColor);
83 stmt->setUInt8(8, BorderStyle);
84 stmt->setUInt32(9, BorderColor);
85 CharacterDatabase.Execute(stmt);
86
87 // Add captain as member
89
90 TC_LOG_DEBUG("bg.arena", "New ArenaTeam created [Id: {}, Name: {}] [Type: {}] [Captain low GUID: {}]", GetId(), GetName(), GetType(), captainLowGuid);
91 return true;
92}
93
95{
96 std::string playerName;
97 uint8 playerClass;
98
99 // Check if arena team is full (Can't have more than type * 2 players)
100 if (GetMembersSize() >= GetType() * 2)
101 return false;
102
103 // Get player name and class either from db or character cache
104 Player* player = ObjectAccessor::FindPlayer(playerGuid);
105 if (player)
106 {
107 playerClass = player->GetClass();
108 playerName = player->GetName();
109 }
110 else if (CharacterCacheEntry const* characterInfo = sCharacterCache->GetCharacterCacheByGuid(playerGuid))
111 {
112 playerName = characterInfo->Name;
113 playerClass = characterInfo->Class;
114 }
115 else
116 return false;
117
118 // Check if player is already in a similar arena team
119 if ((player && player->GetArenaTeamId(GetSlot())) || sCharacterCache->GetCharacterArenaTeamIdByGuid(playerGuid, GetType()) != 0)
120 {
121 TC_LOG_DEBUG("bg.arena", "Arena: {} {} already has an arena team of type {}", playerGuid.ToString(), playerName, GetType());
122 return false;
123 }
124
125 // Set player's personal rating
126 uint16 personalRating = 0;
127
128 if (sWorld->getIntConfig(CONFIG_ARENA_START_PERSONAL_RATING) > 0)
129 personalRating = uint16(sWorld->getIntConfig(CONFIG_ARENA_START_PERSONAL_RATING));
130 else if (GetRating() >= 1000)
131 personalRating = 1000;
132
133 // Try to get player's match maker rating from db and fall back to config setting if not found
135 stmt->setUInt32(0, playerGuid.GetCounter());
136 stmt->setUInt8(1, GetSlot());
137 PreparedQueryResult result = CharacterDatabase.Query(stmt);
138
139 uint32 matchMakerRating;
140 if (result)
141 matchMakerRating = (*result)[0].GetUInt16();
142 else
143 matchMakerRating = sWorld->getIntConfig(CONFIG_ARENA_START_MATCHMAKER_RATING);
144
145 // Remove all player signatures from other petitions
146 // This will prevent player from joining too many arena teams and corrupt arena team data integrity
147 Player::RemovePetitionsAndSigns(playerGuid, static_cast<CharterTypes>(GetType()));
148
149 // Feed data to the struct
150 ArenaTeamMember newMember;
151 newMember.Name = playerName;
152 newMember.Guid = playerGuid;
153 newMember.Class = playerClass;
154 newMember.SeasonGames = 0;
155 newMember.WeekGames = 0;
156 newMember.SeasonWins = 0;
157 newMember.WeekWins = 0;
158 newMember.PersonalRating = personalRating;
159 newMember.MatchMakerRating = matchMakerRating;
160
161 Members.push_back(newMember);
162 sCharacterCache->UpdateCharacterArenaTeamId(playerGuid, GetSlot(), GetId());
163
164 // Save player's arena team membership to db
165 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_TEAM_MEMBER);
166 stmt->setUInt32(0, TeamId);
167 stmt->setUInt32(1, playerGuid.GetCounter());
168 stmt->setUInt16(2, personalRating);
169 CharacterDatabase.Execute(stmt);
170
171 // Inform player if online
172 if (player)
173 {
174 player->SetInArenaTeam(TeamId, GetSlot(), GetType());
175 player->SetArenaTeamIdInvited(0);
176
177 // Hide promote/remove buttons
178 if (CaptainGuid != playerGuid)
180 }
181
182 TC_LOG_DEBUG("bg.arena", "Player: {} [{}] joined arena team type: {} [Id: {}, Name: {}].", playerName, playerGuid.ToString(), GetType(), GetId(), GetName());
183
184 return true;
185}
186
188{
189 if (!result)
190 return false;
191
192 Field* fields = result->Fetch();
193
194 TeamId = fields[0].GetUInt32();
195 TeamName = fields[1].GetString();
196 CaptainGuid = ObjectGuid::Create<HighGuid::Player>(fields[2].GetUInt32());
197 Type = fields[3].GetUInt8();
198 BackgroundColor = fields[4].GetUInt32();
199 EmblemStyle = fields[5].GetUInt8();
200 EmblemColor = fields[6].GetUInt32();
201 BorderStyle = fields[7].GetUInt8();
202 BorderColor = fields[8].GetUInt32();
203 Stats.Rating = fields[9].GetUInt16();
204 Stats.WeekGames = fields[10].GetUInt16();
205 Stats.WeekWins = fields[11].GetUInt16();
206 Stats.SeasonGames = fields[12].GetUInt16();
207 Stats.SeasonWins = fields[13].GetUInt16();
208 Stats.Rank = fields[14].GetUInt32();
209
210 return true;
211}
212
214{
215 if (!result)
216 return false;
217
218 bool captainPresentInTeam = false;
219
220 do
221 {
222 Field* fields = result->Fetch();
223
224 // Prevent crash if db records are broken when all members in result are already processed and current team doesn't have any members
225 if (!fields)
226 break;
227
228 uint32 arenaTeamId = fields[0].GetUInt32();
229
230 // We loaded all members for this arena_team already, break cycle
231 if (arenaTeamId > TeamId)
232 break;
233
234 ArenaTeamMember newMember;
235 newMember.Guid = ObjectGuid::Create<HighGuid::Player>(fields[1].GetUInt32());
236 newMember.WeekGames = fields[2].GetUInt16();
237 newMember.WeekWins = fields[3].GetUInt16();
238 newMember.SeasonGames = fields[4].GetUInt16();
239 newMember.SeasonWins = fields[5].GetUInt16();
240 newMember.Name = fields[6].GetString();
241 newMember.Class = fields[7].GetUInt8();
242 newMember.PersonalRating = fields[8].GetUInt16();
243 newMember.MatchMakerRating = fields[9].GetUInt16() > 0 ? fields[9].GetUInt16() : sWorld->getIntConfig(CONFIG_ARENA_START_MATCHMAKER_RATING);
244
245 // Delete member if character information is missing
246 if (newMember.Name.empty())
247 {
248 TC_LOG_ERROR("sql.sql", "ArenaTeam {} has member with empty name - probably {} doesn't exist, deleting him from memberlist!", arenaTeamId, newMember.Guid.ToString());
249 DelMember(newMember.Guid, true);
250 continue;
251 }
252
253 // Check if team team has a valid captain
254 if (newMember.Guid == GetCaptain())
255 captainPresentInTeam = true;
256
257 // Put the player in the team
258 Members.push_back(newMember);
259 sCharacterCache->UpdateCharacterArenaTeamId(newMember.Guid, GetSlot(), GetId());
260 }
261 while (result->NextRow());
262
263 if (Empty() || !captainPresentInTeam)
264 {
265 // Arena team is empty or captain is not in team, delete from db
266 TC_LOG_DEBUG("bg.arena", "ArenaTeam {} does not have any members or its captain is not in team, disbanding it...", TeamId);
267 return false;
268 }
269
270 return true;
271}
272
273bool ArenaTeam::SetName(std::string const& name)
274{
275 if (TeamName == name || name.empty() || name.length() > 24 || sObjectMgr->IsReservedName(name) || !ObjectMgr::IsValidCharterName(name))
276 return false;
277
278 TeamName = name;
280 stmt->setString(0, TeamName);
281 stmt->setUInt32(1, GetId());
282 CharacterDatabase.Execute(stmt);
283 return true;
284}
285
287{
288 // Disable remove/promote buttons
290 if (oldCaptain)
292
293 // Set new captain
294 CaptainGuid = guid;
295
296 // Update database
298 stmt->setUInt32(0, guid.GetCounter());
299 stmt->setUInt32(1, GetId());
300 CharacterDatabase.Execute(stmt);
301
302 // Enable remove/promote buttons
303 if (Player* newCaptain = ObjectAccessor::FindPlayer(guid))
304 {
305 newCaptain->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_MEMBER, 0);
306 if (oldCaptain)
307 {
308 TC_LOG_DEBUG("bg.arena", "Player: {} {} promoted player: {} {} to leader of arena team [Id: {}, Name: {}] [Type: {}].",
309 oldCaptain->GetName(), oldCaptain->GetGUID().ToString(), newCaptain->GetName(),
310 newCaptain->GetGUID().ToString(), GetId(), GetName(), GetType());
311 }
312 }
313}
314
315void ArenaTeam::DelMember(ObjectGuid guid, bool cleanDb)
316{
318 Group* group = (player && player->GetGroup()) ? player->GetGroup() : nullptr;
319
320 // Remove member from team
321 for (MemberList::iterator itr = Members.begin(); itr != Members.end(); ++itr)
322 {
323 // Remove queues of members
324 if (Player* playerMember = ObjectAccessor::FindConnectedPlayer(itr->Guid))
325 {
326 if (group && playerMember->GetGroup() && group->GetGUID() == playerMember->GetGroup()->GetGUID())
327 {
328 for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
329 {
330 BattlegroundQueueTypeId bgQueue = playerMember->GetBattlegroundQueueTypeId(i);
331 if (bgQueue.BattlemasterListId != BATTLEGROUND_AA || bgQueue.TeamSize != GetType())
332 continue;
333
334 GroupQueueInfo ginfo;
335 BattlegroundQueue& queue = sBattlegroundMgr->GetBattlegroundQueue(bgQueue);
336 if (queue.GetPlayerGroupInfoData(playerMember->GetGUID(), &ginfo))
337 if (!ginfo.IsInvitedToBGInstanceGUID)
338 {
339 playerMember->RemoveBattlegroundQueueId(bgQueue);
342 queue.RemovePlayer(playerMember->GetGUID(), true);
343 playerMember->SendDirectMessage(battlefieldStatus.Write());
344 }
345 }
346 }
347 }
348
349 if (itr->Guid == guid)
350 {
351 Members.erase(itr);
352 sCharacterCache->UpdateCharacterArenaTeamId(guid, GetSlot(), 0);
353 break;
354 }
355 }
356
357 // Inform player and remove arena team info from player data
358 if (player)
359 {
361 // delete all info regarding this team
362 for (uint32 i = 0; i < ARENA_TEAM_END; ++i)
364 TC_LOG_DEBUG("bg.arena", "Player: {} {} left arena team type: {} [Id: {}, Name: {}].", player->GetName(), player->GetGUID().ToString(), GetType(), GetId(), GetName());
365 }
366
367 // Only used for single member deletion, for arena team disband we use a single query for more efficiency
368 if (cleanDb)
369 {
371 stmt->setUInt32(0, GetId());
372 stmt->setUInt32(1, guid.GetCounter());
373 CharacterDatabase.Execute(stmt);
374 }
375}
376
378{
379 // Remove all members from arena team
380 while (!Members.empty())
381 DelMember(Members.front().Guid, false);
382
383 // Broadcast update
384 if (session)
385 {
387
388 if (Player* player = session->GetPlayer())
389 TC_LOG_DEBUG("bg.arena", "Player: {} {} disbanded arena team type: {} [Id: {}, Name: {}].", player->GetName(), player->GetGUID().ToString(), GetType(), GetId(), GetName());
390 }
391
392 // Update database
393 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
394
396 stmt->setUInt32(0, TeamId);
397 trans->Append(stmt);
398
399 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ARENA_TEAM_MEMBERS);
400 stmt->setUInt32(0, TeamId);
401 trans->Append(stmt);
402
403 CharacterDatabase.CommitTransaction(trans);
404
405 // Remove arena team from ArenaTeamMgr
406 sArenaTeamMgr->RemoveArenaTeam(TeamId);
407}
408
410{
411 // Remove all members from arena team
412 while (!Members.empty())
413 DelMember(Members.front().Guid, false);
414
415 // Update database
416 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
417
419 stmt->setUInt32(0, TeamId);
420 trans->Append(stmt);
421
422 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ARENA_TEAM_MEMBERS);
423 stmt->setUInt32(0, TeamId);
424 trans->Append(stmt);
425
426 CharacterDatabase.CommitTransaction(trans);
427
428 // Remove arena team from ArenaTeamMgr
429 sArenaTeamMgr->RemoveArenaTeam(TeamId);
430}
431
433{
434 Player* player = nullptr;
435
436 uint8 unk308 = 0;
437
439 data << uint32(GetId()); // team id
440 data << uint8(unk308); // 3.0.8 unknown value but affect packet structure
441 data << uint32(GetMembersSize()); // members count
442 data << uint32(GetType()); // arena team type?
443
444 for (MemberList::const_iterator itr = Members.begin(); itr != Members.end(); ++itr)
445 {
446 player = ObjectAccessor::FindConnectedPlayer(itr->Guid);
447
448 data << itr->Guid; // guid
449 data << uint8((player ? 1 : 0)); // online flag
450 data << itr->Name; // member name
451 data << uint32((itr->Guid == GetCaptain() ? 0 : 1)); // captain flag 0 captain 1 member
452 data << uint8((player ? player->GetLevel() : 0)); // unknown, level?
453 data << uint8(itr->Class); // class
454 data << uint32(itr->WeekGames); // played this week
455 data << uint32(itr->WeekWins); // wins this week
456 data << uint32(itr->SeasonGames); // played this season
457 data << uint32(itr->SeasonWins); // wins this season
458 data << uint32(itr->PersonalRating); // personal rating
459 //if (unk308)
460 //{
461 // data << float(0.0f); // 308 unk
462 // data << float(0.0f); // 308 unk
463 //}
464 }
465
466 session->SendPacket(&data);
467 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_ARENA_TEAM_ROSTER");
468}
469
471{
473 data << uint32(GetId()); // team id
474 data << GetName(); // team name
475 data << uint32(GetType()); // arena team type (2=2x2, 3=3x3 or 5=5x5)
476 data << uint32(BackgroundColor); // background color
477 data << uint32(EmblemStyle); // emblem style
478 data << uint32(EmblemColor); // emblem color
479 data << uint32(BorderStyle); // border style
480 data << uint32(BorderColor); // border color
481 session->SendPacket(&data);
482 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_ARENA_TEAM_QUERY_RESPONSE");
483}
484
486{
488 data << uint32(GetId()); // team id
489 data << uint32(Stats.Rating); // rating
490 data << uint32(Stats.WeekGames); // games this week
491 data << uint32(Stats.WeekWins); // wins this week
492 data << uint32(Stats.SeasonGames); // played this season
493 data << uint32(Stats.SeasonWins); // wins this season
494 data << uint32(Stats.Rank); // rank
495 session->SendPacket(&data);
496}
497
499{
500 // This is called after a rated match ended
501 // Updates arena team stats for every member of the team (not only the ones who participated!)
502 for (MemberList::const_iterator itr = Members.begin(); itr != Members.end(); ++itr)
503 if (Player* player = ObjectAccessor::FindConnectedPlayer(itr->Guid))
504 SendStats(player->GetSession());
505}
506
508{
509 ArenaTeamMember* member = GetMember(guid);
510 if (!member)
511 return;
512
514 data << guid; // player guid
515 data << uint8(GetSlot()); // slot (0...2)
516 data << uint32(GetId()); // arena team id
517 data << uint32(Stats.Rating); // rating
518 data << uint32(Stats.SeasonGames); // season played
519 data << uint32(Stats.SeasonWins); // season wins
520 data << uint32(member->SeasonGames); // played (count of all games, that the inspected member participated...)
521 data << uint32(member->PersonalRating); // personal rating
522 session->SendPacket(&data);
523}
524
538
540{
541 if (int32(MatchMakerRating) + mod < 0)
543 else
544 MatchMakerRating += mod;
545}
546
548{
549 for (MemberList::const_iterator itr = Members.begin(); itr != Members.end(); ++itr)
550 if (Player* player = ObjectAccessor::FindConnectedPlayer(itr->Guid))
551 player->SendDirectMessage(packet);
552}
553
554void ArenaTeam::BroadcastEvent(ArenaTeamEvents event, ObjectGuid guid, uint8 strCount, std::string const& str1, std::string const& str2, std::string const& str3)
555{
557 data << uint8(event);
558 data << uint8(strCount);
559 switch (strCount)
560 {
561 case 0:
562 break;
563 case 1:
564 data << str1;
565 break;
566 case 2:
567 data << str1 << str2;
568 break;
569 case 3:
570 data << str1 << str2 << str3;
571 break;
572 default:
573 TC_LOG_ERROR("bg.arena", "Unhandled strCount {} in ArenaTeam::BroadcastEvent", strCount);
574 return;
575 }
576
577 if (!guid.IsEmpty())
578 data << guid;
579
580 BroadcastPacket(&data);
581
582 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_ARENA_TEAM_EVENT");
583}
584
586{
588
589 for (ArenaTeamMember const& member : Members)
590 if (member.Guid != session->GetPlayer()->GetGUID())
591 packet.Invites.emplace_back(member.Guid, 0);
592
593 session->SendPacket(packet.Write());
594}
595
597{
598 switch (type)
599 {
600 case ARENA_TEAM_2v2: return 0;
601 case ARENA_TEAM_3v3: return 1;
602 case ARENA_TEAM_5v5: return 2;
603 default:
604 break;
605 }
606 TC_LOG_ERROR("bg.arena", "FATAL: Unknown arena team type {} for some arena team", type);
607 return 0xFF;
608}
609
611{
612 switch (slot)
613 {
614 case 0: return ARENA_TEAM_2v2;
615 case 1: return ARENA_TEAM_3v3;
616 case 2: return ARENA_TEAM_5v5;
617 default:
618 break;
619 }
620 TC_LOG_ERROR("bg.arena", "FATAL: Unknown arena team slot {} for some arena team", slot);
621 return 0xFF;
622}
623
625{
626 for (MemberList::const_iterator itr = Members.begin(); itr != Members.end(); ++itr)
627 if (itr->Guid == guid)
628 return true;
629
630 return false;
631}
632
634{
635 // Returns how many points would be awarded with this team type with this rating
636 float points;
637
638 uint32 rating = memberRating + 150 < Stats.Rating ? memberRating : Stats.Rating;
639
640 if (rating <= 1500)
641 {
642 if (sWorld->getIntConfig(CONFIG_ARENA_SEASON_ID) < 6)
643 points = (float)rating * 0.22f + 14.0f;
644 else
645 points = 344;
646 }
647 else
648 points = 1511.26f / (1.0f + 1639.28f * std::exp(-0.00412f * float(rating)));
649
650 // Type penalties for teams < 5v5
651 if (Type == ARENA_TEAM_2v2)
652 points *= 0.76f;
653 else if (Type == ARENA_TEAM_3v3)
654 points *= 0.88f;
655
656 points *= sWorld->getRate(RATE_ARENA_POINTS);
657
658 return (uint32) points;
659}
660
662{
663 if (!group)
664 return 0;
665
666 uint32 matchMakerRating = 0;
667 uint32 playerDivider = 0;
668 for (MemberList::const_iterator itr = Members.begin(); itr != Members.end(); ++itr)
669 {
670 // Skip if player is not online
672 continue;
673
674 // Skip if player is not member of group
675 if (!group->IsMember(itr->Guid))
676 continue;
677
678 matchMakerRating += itr->MatchMakerRating;
679 ++playerDivider;
680 }
681
682 // x/0 = crash
683 if (playerDivider == 0)
684 playerDivider = 1;
685
686 matchMakerRating /= playerDivider;
687
688 return matchMakerRating;
689}
690
691float ArenaTeam::GetChanceAgainst(uint32 ownRating, uint32 opponentRating)
692{
693 // Returns the chance to win against a team with the given rating, used in the rating adjustment calculation
694 // ELO system
695 return 1.0f / (1.0f + std::exp(std::log(10.0f) * (float(opponentRating) - float(ownRating)) / 650.0f));
696}
697
698int32 ArenaTeam::GetMatchmakerRatingMod(uint32 ownRating, uint32 opponentRating, bool won /*, float& confidence_factor*/)
699{
700 // 'Chance' calculation - to beat the opponent
701 // This is a simulation. Not much info on how it really works
702 float chance = GetChanceAgainst(ownRating, opponentRating);
703 float won_mod = (won) ? 1.0f : 0.0f;
704 float mod = won_mod - chance;
705
706 // Work in progress:
707 /*
708 // This is a simulation, as there is not much info on how it really works
709 float confidence_mod = min(1.0f - fabs(mod), 0.5f);
710
711 // Apply confidence factor to the mod:
712 mod *= confidence_factor
713
714 // And only after that update the new confidence factor
715 confidence_factor -= ((confidence_factor - 1.0f) * confidence_mod) / confidence_factor;
716 */
717
718 // Real rating modification
719 mod *= sWorld->getFloatConfig(CONFIG_ARENA_MATCHMAKER_RATING_MODIFIER);
720
721 return (int32)ceil(mod);
722}
723
724int32 ArenaTeam::GetRatingMod(uint32 ownRating, uint32 opponentRating, bool won /*, float confidence_factor*/)
725{
726 // 'Chance' calculation - to beat the opponent
727 // This is a simulation. Not much info on how it really works
728 float chance = GetChanceAgainst(ownRating, opponentRating);
729
730 // Calculate the rating modification
731 float mod;
732
734 if (won)
735 {
736 if (ownRating < 1300)
737 {
738 float win_rating_modifier1 = sWorld->getFloatConfig(CONFIG_ARENA_WIN_RATING_MODIFIER_1);
739
740 if (ownRating < 1000)
741 mod = win_rating_modifier1 * (1.0f - chance);
742 else
743 mod = ((win_rating_modifier1 / 2.0f) + ((win_rating_modifier1 / 2.0f) * (1300.0f - float(ownRating)) / 300.0f)) * (1.0f - chance);
744 }
745 else
746 mod = sWorld->getFloatConfig(CONFIG_ARENA_WIN_RATING_MODIFIER_2) * (1.0f - chance);
747 }
748 else
749 mod = sWorld->getFloatConfig(CONFIG_ARENA_LOSE_RATING_MODIFIER) * (-chance);
750
751 return (int32)ceil(mod);
752}
753
755{
756 // Rating can only drop to 0
757 if (int32(Stats.Rating) + mod < 0)
758 Stats.Rating = 0;
759 else
760 {
761 Stats.Rating += mod;
762
763 // Check if rating related achivements are met
764 for (MemberList::iterator itr = Members.begin(); itr != Members.end(); ++itr)
765 if (Player* member = ObjectAccessor::FindConnectedPlayer(itr->Guid))
766 member->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING, Stats.Rating, Type);
767 }
768
769 // Update number of games played per season or week
770 Stats.WeekGames += 1;
771 Stats.SeasonGames += 1;
772
773 // Update team's rank, start with rank 1 and increase until no team with more rating was found
774 Stats.Rank = 1;
775 for (auto [teamId, team] : sArenaTeamMgr->GetArenaTeams())
776 if (team->GetType() == Type && team->GetStats().Rating > Stats.Rating)
777 ++Stats.Rank;
778}
779
780int32 ArenaTeam::WonAgainst(uint32 Own_MMRating, uint32 Opponent_MMRating, int32& rating_change)
781{
782 // Called when the team has won
783 // Change in Matchmaker rating
784 int32 mod = GetMatchmakerRatingMod(Own_MMRating, Opponent_MMRating, true);
785
786 // Change in Team Rating
787 rating_change = GetRatingMod(Stats.Rating, Opponent_MMRating, true);
788
789 // Modify the team stats accordingly
790 FinishGame(rating_change);
791
792 // Update number of wins per season and week
793 Stats.WeekWins += 1;
794 Stats.SeasonWins += 1;
795
796 // Return the rating change, used to display it on the results screen
797 return mod;
798}
799
800int32 ArenaTeam::LostAgainst(uint32 Own_MMRating, uint32 Opponent_MMRating, int32& rating_change)
801{
802 // Called when the team has lost
803 // Change in Matchmaker Rating
804 int32 mod = GetMatchmakerRatingMod(Own_MMRating, Opponent_MMRating, false);
805
806 // Change in Team Rating
807 rating_change = GetRatingMod(Stats.Rating, Opponent_MMRating, false);
808
809 // Modify the team stats accordingly
810 FinishGame(rating_change);
811
812 // return the rating change, used to display it on the results screen
813 return mod;
814}
815
816void ArenaTeam::MemberLost(Player* player, uint32 againstMatchmakerRating, int32 MatchmakerRatingChange)
817{
818 // Called for each participant of a match after losing
819 for (MemberList::iterator itr = Members.begin(); itr != Members.end(); ++itr)
820 {
821 if (itr->Guid == player->GetGUID())
822 {
823 // Update personal rating
824 int32 mod = GetRatingMod(itr->PersonalRating, againstMatchmakerRating, false);
825 itr->ModifyPersonalRating(player, mod, GetType());
826
827 // Update matchmaker rating
828 itr->ModifyMatchmakerRating(MatchmakerRatingChange, GetSlot());
829
830 // Update personal played stats
831 itr->WeekGames +=1;
832 itr->SeasonGames +=1;
833
834 // update the unit fields
835 player->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_WEEK, itr->WeekGames);
836 player->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_SEASON, itr->SeasonGames);
837 return;
838 }
839 }
840}
841
842void ArenaTeam::OfflineMemberLost(ObjectGuid guid, uint32 againstMatchmakerRating, int32 MatchmakerRatingChange)
843{
844 // Called for offline player after ending rated arena match!
845 for (MemberList::iterator itr = Members.begin(); itr != Members.end(); ++itr)
846 {
847 if (itr->Guid == guid)
848 {
849 // update personal rating
850 int32 mod = GetRatingMod(itr->PersonalRating, againstMatchmakerRating, false);
851 itr->ModifyPersonalRating(nullptr, mod, GetType());
852
853 // update matchmaker rating
854 itr->ModifyMatchmakerRating(MatchmakerRatingChange, GetSlot());
855
856 // update personal played stats
857 itr->WeekGames += 1;
858 itr->SeasonGames += 1;
859 return;
860 }
861 }
862}
863
864void ArenaTeam::MemberWon(Player* player, uint32 againstMatchmakerRating, int32 MatchmakerRatingChange)
865{
866 // called for each participant after winning a match
867 for (MemberList::iterator itr = Members.begin(); itr != Members.end(); ++itr)
868 {
869 if (itr->Guid == player->GetGUID())
870 {
871 // update personal rating
872 int32 mod = GetRatingMod(itr->PersonalRating, againstMatchmakerRating, true);
873 itr->ModifyPersonalRating(player, mod, GetType());
874
875 // update matchmaker rating
876 itr->ModifyMatchmakerRating(MatchmakerRatingChange, GetSlot());
877
878 // update personal stats
879 itr->WeekGames +=1;
880 itr->SeasonGames +=1;
881 itr->SeasonWins += 1;
882 itr->WeekWins += 1;
883 // update unit fields
884 player->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_WEEK, itr->WeekGames);
885 player->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_SEASON, itr->SeasonGames);
886 return;
887 }
888 }
889}
890
891void ArenaTeam::UpdateArenaPointsHelper(std::map<ObjectGuid, uint32>& playerPoints)
892{
893 // Called after a match has ended and the stats are already modified
894 // Helper function for arena point distribution (this way, when distributing, no actual calculation is required, just a few comparisons)
895 // 10 played games per week is a minimum
896 if (Stats.WeekGames < 10)
897 return;
898
899 // To get points, a player has to participate in at least 30% of the matches
900 uint32 requiredGames = (uint32)ceil(Stats.WeekGames * 0.3f);
901
902 for (MemberList::const_iterator itr = Members.begin(); itr != Members.end(); ++itr)
903 {
904 // The player participated in enough games, update his points
905 uint32 pointsToAdd = 0;
906 if (itr->WeekGames >= requiredGames)
907 pointsToAdd = GetPoints(itr->PersonalRating);
908
909 std::map<ObjectGuid, uint32>::iterator plr_itr = playerPoints.find(itr->Guid);
910 if (plr_itr != playerPoints.end())
911 {
912 // Check if there is already more points
913 if (plr_itr->second < pointsToAdd)
914 playerPoints[itr->Guid] = pointsToAdd;
915 }
916 else
917 playerPoints[itr->Guid] = pointsToAdd;
918 }
919}
920
921void ArenaTeam::SaveToDB(bool forceMemberSave)
922{
923 // Save team and member stats to db
924 // Called after a match has ended or when calculating arena_points
925
926 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
927
929 stmt->setUInt16(0, Stats.Rating);
930 stmt->setUInt16(1, Stats.WeekGames);
931 stmt->setUInt16(2, Stats.WeekWins);
932 stmt->setUInt16(3, Stats.SeasonGames);
933 stmt->setUInt16(4, Stats.SeasonWins);
934 stmt->setUInt32(5, Stats.Rank);
935 stmt->setUInt32(6, GetId());
936 trans->Append(stmt);
937
938 for (MemberList::const_iterator itr = Members.begin(); itr != Members.end(); ++itr)
939 {
940 // Save the effort and go
941 if (itr->WeekGames == 0 && !forceMemberSave)
942 continue;
943
944 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ARENA_TEAM_MEMBER);
945 stmt->setUInt16(0, itr->PersonalRating);
946 stmt->setUInt16(1, itr->WeekGames);
947 stmt->setUInt16(2, itr->WeekWins);
948 stmt->setUInt16(3, itr->SeasonGames);
949 stmt->setUInt16(4, itr->SeasonWins);
950 stmt->setUInt32(5, GetId());
951 stmt->setUInt32(6, itr->Guid.GetCounter());
952 trans->Append(stmt);
953
954 stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHARACTER_ARENA_STATS);
955 stmt->setUInt32(0, itr->Guid.GetCounter());
956 stmt->setUInt8(1, GetSlot());
957 stmt->setUInt16(2, itr->MatchMakerRating);
958 trans->Append(stmt);
959 }
960
961 CharacterDatabase.CommitTransaction(trans);
962}
963
965{
966 // No need to go further than this
967 if (Stats.WeekGames == 0)
968 return false;
969
970 // Reset team stats
971 Stats.WeekGames = 0;
972 Stats.WeekWins = 0;
973
974 // Reset member stats
975 for (MemberList::iterator itr = Members.begin(); itr != Members.end(); ++itr)
976 {
977 itr->WeekGames = 0;
978 itr->WeekWins = 0;
979 }
980
981 return true;
982}
983
985{
986 for (MemberList::const_iterator itr = Members.begin(); itr != Members.end(); ++itr)
987 if (Player* player = ObjectAccessor::FindPlayer(itr->Guid))
988 if (player->GetMap()->IsBattleArena())
989 return true;
990
991 return false;
992}
993
994ArenaTeamMember* ArenaTeam::GetMember(const std::string& name)
995{
996 for (MemberList::iterator itr = Members.begin(); itr != Members.end(); ++itr)
997 if (itr->Name == name)
998 return &(*itr);
999
1000 return nullptr;
1001}
1002
1004{
1005 for (MemberList::iterator itr = Members.begin(); itr != Members.end(); ++itr)
1006 if (itr->Guid == guid)
1007 return &(*itr);
1008
1009 return nullptr;
1010}
#define sArenaTeamMgr
@ ERR_ARENA_TEAM_QUIT_S
Definition ArenaTeam.h:35
@ ARENA_TEAM_5v5
Definition ArenaTeam.h:85
@ ARENA_TEAM_2v2
Definition ArenaTeam.h:83
@ ARENA_TEAM_3v3
Definition ArenaTeam.h:84
ArenaTeamEvents
Definition ArenaTeam.h:63
@ ERR_ARENA_TEAM_DISBANDED_S
Definition ArenaTeam.h:69
#define sBattlegroundMgr
#define sCharacterCache
@ CHAR_DEL_ARENA_TEAM_MEMBERS
@ CHAR_DEL_ARENA_TEAM
@ CHAR_UPD_ARENA_TEAM_STATS
@ CHAR_INS_ARENA_TEAM_MEMBER
@ CHAR_REP_CHARACTER_ARENA_STATS
@ CHAR_UPD_ARENA_TEAM_MEMBER
@ CHAR_SEL_MATCH_MAKER_RATING
@ CHAR_UPD_ARENA_TEAM_NAME
@ CHAR_UPD_ARENA_TEAM_CAPTAIN
@ CHAR_INS_ARENA_TEAM
@ CHAR_DEL_ARENA_TEAM_MEMBER
@ ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING
Definition DBCEnums.h:164
@ ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_PERSONAL_RATING
Definition DBCEnums.h:165
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< ResultSet > QueryResult
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
uint8_t uint8
Definition Define.h:135
int32_t int32
Definition Define.h:129
uint16_t uint16
Definition Define.h:134
uint32_t uint32
Definition Define.h:133
#define TC_LOG_DEBUG(filterType__,...)
Definition Log.h:156
#define TC_LOG_ERROR(filterType__,...)
Definition Log.h:165
#define sObjectMgr
Definition ObjectMgr.h:1721
ArenaTeamInfoType
Definition Player.h:657
@ ARENA_TEAM_GAMES_WEEK
Definition Player.h:661
@ ARENA_TEAM_END
Definition Player.h:665
@ ARENA_TEAM_PERSONAL_RATING
Definition Player.h:664
@ ARENA_TEAM_MEMBER
Definition Player.h:660
@ ARENA_TEAM_GAMES_SEASON
Definition Player.h:662
TeamId
@ BATTLEGROUND_AA
Stats
#define PLAYER_MAX_BATTLEGROUND_QUEUES
CharterTypes
void SaveToDB(bool forceMemberSave=false)
uint32 EmblemColor
Definition ArenaTeam.h:199
ObjectGuid CaptainGuid
Definition ArenaTeam.h:195
void Inspect(WorldSession *session, ObjectGuid guid)
void Roster(WorldSession *session)
bool Empty() const
Definition ArenaTeam.h:146
ObjectGuid GetCaptain() const
Definition ArenaTeam.h:133
uint32 GetRating() const
Definition ArenaTeam.h:137
bool LoadArenaTeamFromDB(QueryResult arenaTeamDataResult)
void OfflineMemberLost(ObjectGuid guid, uint32 againstMatchmakerRating, int32 MatchmakerRatingChange=-12)
int32 LostAgainst(uint32 Own_MMRating, uint32 Opponent_MMRating, int32 &rating_change)
float GetChanceAgainst(uint32 ownRating, uint32 opponentRating)
uint32 BorderColor
Definition ArenaTeam.h:201
int32 GetRatingMod(uint32 ownRating, uint32 opponentRating, bool won)
size_t GetMembersSize() const
Definition ArenaTeam.h:145
void DelMember(ObjectGuid guid, bool cleanDb)
void MemberLost(Player *player, uint32 againstMatchmakerRating, int32 MatchmakerRatingChange=-12)
bool IsMember(ObjectGuid guid) const
uint8 Type
Definition ArenaTeam.h:193
bool SetName(std::string const &name)
uint8 EmblemStyle
Definition ArenaTeam.h:198
bool FinishWeek()
bool Create(ObjectGuid captainGuid, uint8 type, std::string const &teamName, uint32 backgroundColor, uint8 emblemStyle, uint32 emblemColor, uint8 borderStyle, uint32 borderColor)
Definition ArenaTeam.cpp:49
uint8 BorderStyle
Definition ArenaTeam.h:200
int32 GetMatchmakerRatingMod(uint32 ownRating, uint32 opponentRating, bool won)
void SendStats(WorldSession *session)
int32 WonAgainst(uint32 Own_MMRating, uint32 Opponent_MMRating, int32 &rating_change)
uint8 GetSlot() const
Definition ArenaTeam.h:130
void Query(WorldSession *session)
void SetCaptain(ObjectGuid guid)
bool AddMember(ObjectGuid PlayerGuid)
Definition ArenaTeam.cpp:94
ArenaTeamMember * GetMember(ObjectGuid guid)
void FinishGame(int32 mod)
void UpdateArenaPointsHelper(std::map< ObjectGuid, uint32 > &PlayerPoints)
uint32 GetType() const
Definition ArenaTeam.h:129
std::string TeamName
Definition ArenaTeam.h:194
void BroadcastEvent(ArenaTeamEvents event, ObjectGuid guid, uint8 strCount, std::string const &str1, std::string const &str2, std::string const &str3)
MemberList Members
Definition ArenaTeam.h:203
void NotifyStatsChanged()
void MassInviteToEvent(WorldSession *session)
uint32 BackgroundColor
Definition ArenaTeam.h:197
void BroadcastPacket(WorldPacket *packet)
void Disband()
uint32 GetAverageMMR(Group *group) const
bool IsFighting() const
std::string const & GetName() const
Definition ArenaTeam.h:134
uint32 GetPoints(uint32 MemberRating)
static uint8 GetTypeBySlot(uint8 slot)
bool LoadMembersFromDB(QueryResult arenaTeamMembersResult)
uint32 GetId() const
Definition ArenaTeam.h:128
void MemberWon(Player *player, uint32 againstMatchmakerRating, int32 MatchmakerRatingChange)
static uint8 GetSlotByType(uint32 type)
static void BuildBattlegroundStatusNone(WorldPackets::Battleground::BattlefieldStatusNone *battlefieldStatus, uint32 queueSlot)
bool GetPlayerGroupInfoData(ObjectGuid guid, GroupQueueInfo *ginfo)
void RemovePlayer(ObjectGuid guid, bool decreaseInvitedCount)
Class used to access individual fields of database query result.
Definition Field.h:92
uint8 GetUInt8() const
Definition Field.cpp:29
std::string GetString() const
Definition Field.cpp:125
uint16 GetUInt16() const
Definition Field.cpp:45
uint32 GetUInt32() const
Definition Field.cpp:61
Definition Group.h:165
ObjectGuid GetGUID() const
Definition Group.cpp:2473
bool IsMember(ObjectGuid guid) const
Definition Group.cpp:2505
LowType GetCounter() const
Definition ObjectGuid.h:156
static ObjectGuid const Empty
Definition ObjectGuid.h:140
bool IsEmpty() const
Definition ObjectGuid.h:172
std::string ToString() const
uint32 LowType
Definition ObjectGuid.h:142
static bool IsValidCharterName(std::string_view name)
static ObjectGuid GetGUID(Object const *o)
Definition Object.h:78
void SetArenaTeamInfoField(uint8 slot, ArenaTeamInfoType type, uint32 value)
Definition Player.cpp:6717
void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1=0, uint32 miscValue2=0, WorldObject *ref=nullptr)
Definition Player.cpp:24940
void SetInArenaTeam(uint32 ArenaTeamId, uint8 slot, uint8 type)
Definition Player.cpp:6711
WorldSession * GetSession() const
Definition Player.h:1719
uint32 GetArenaTeamId(uint8 slot) const
Definition Player.h:1629
Group * GetGroup()
Definition Player.h:2171
void SetArenaTeamIdInvited(uint32 ArenaTeamId)
Definition Player.h:1631
static void RemovePetitionsAndSigns(ObjectGuid guid, CharterTypes type)
Definition Player.cpp:20953
void setUInt16(uint8 index, uint16 value)
void setUInt32(uint8 index, uint32 value)
void setUInt8(uint8 index, uint8 value)
void setString(uint8 index, std::string const &value)
uint8 GetClass() const
Definition Unit.h:895
uint8 GetLevel() const
Definition Unit.h:889
std::string const & GetName() const
Definition Object.h:382
std::vector< CalendarEventInitialInviteInfo > Invites
Player session in the World.
void SendPacket(WorldPacket const *packet)
Send a packet to the client.
Player * GetPlayer() const
void SendArenaTeamCommandResult(uint32 team_action, std::string const &team, std::string const &player, uint32 error_id=0)
std::string const & GetPlayerName() const
@ SMSG_ARENA_TEAM_STATS
Definition Opcodes.h:888
@ SMSG_ARENA_TEAM_EVENT
Definition Opcodes.h:884
@ MSG_INSPECT_ARENA_TEAMS
Definition Opcodes.h:916
@ SMSG_ARENA_TEAM_ROSTER
Definition Opcodes.h:875
@ SMSG_ARENA_TEAM_QUERY_RESPONSE
Definition Opcodes.h:873
#define sWorld
Definition World.h:900
@ CONFIG_ARENA_START_MATCHMAKER_RATING
Definition World.h:325
@ CONFIG_ARENA_SEASON_ID
Definition World.h:322
@ CONFIG_ARENA_START_RATING
Definition World.h:323
@ CONFIG_ARENA_START_PERSONAL_RATING
Definition World.h:324
@ RATE_ARENA_POINTS
Definition World.h:462
@ CONFIG_ARENA_WIN_RATING_MODIFIER_1
Definition World.h:200
@ CONFIG_ARENA_WIN_RATING_MODIFIER_2
Definition World.h:201
@ CONFIG_ARENA_LOSE_RATING_MODIFIER
Definition World.h:202
@ CONFIG_ARENA_MATCHMAKER_RATING_MODIFIER
Definition World.h:203
TC_GAME_API Player * FindPlayer(ObjectGuid const &)
TC_GAME_API Player * FindConnectedPlayer(ObjectGuid const &)
uint16 PersonalRating
Definition ArenaTeam.h:97
void ModifyPersonalRating(Player *player, int32 mod, uint32 type)
uint16 WeekWins
Definition ArenaTeam.h:94
uint16 SeasonWins
Definition ArenaTeam.h:96
std::string Name
Definition ArenaTeam.h:91
uint16 WeekGames
Definition ArenaTeam.h:93
void ModifyMatchmakerRating(int32 mod, uint32 slot)
ObjectGuid Guid
Definition ArenaTeam.h:90
uint16 MatchMakerRating
Definition ArenaTeam.h:98
uint16 SeasonGames
Definition ArenaTeam.h:95
uint32 IsInvitedToBGInstanceGUID