diff --git a/conf/arena_3v3_solo_queue.conf.dist b/conf/arena_3v3_solo_queue.conf.dist index 2f2df0d..67c77cf 100644 --- a/conf/arena_3v3_solo_queue.conf.dist +++ b/conf/arena_3v3_solo_queue.conf.dist @@ -17,8 +17,12 @@ Solo.3v3.FilterTalents = 0 Arena.CheckEquipAndTalents = 0 Arena.3v3.BlockForbiddenTalents = 0 Solo.3v3.CastDeserterOnAfk = 1 -Solo.3v3.CastDeserterOnLeave = 0 -Solo.3v3.StopGameIncomplete = 0 +Solo.3v3.CastDeserterOnLeave = 1 +Solo.3v3.StopGameIncomplete = 1 +Solo.3v3.RatingPenalty.LeaveDuringMatch = 24 +Solo.3v3.RatingPenalty.LeaveBeforeMatchStart = 50 + + Solo.3v3.MinLevel = 80 Solo.3v3.Cost = 45 Solo.3v3.ArenaPointsMulti = 0.8 diff --git a/src/solo3v3.cpp b/src/solo3v3.cpp index 096969b..2221a3e 100644 --- a/src/solo3v3.cpp +++ b/src/solo3v3.cpp @@ -41,6 +41,64 @@ uint32 Solo3v3::GetAverageMMR(ArenaTeam* team) return matchMakerRating; } +void Solo3v3::CountAsLoss(Player* player, bool isInProgress) +{ + ArenaTeam* plrArenaTeam = sArenaTeamMgr->GetArenaTeamById(player->GetArenaTeamId(ARENA_SLOT_SOLO_3v3)); + + if (!plrArenaTeam) + return; + + int32 ratingLoss = 0; + + // leave while arena is in progress + if (isInProgress) + { + ratingLoss = sConfigMgr->GetOption("Solo.3v3.RatingPenalty.LeaveDuringMatch", 24); + } + // leave while arena is in preparation || don't accept queue || logout while invited + else + { + ratingLoss = sConfigMgr->GetOption("Solo.3v3.RatingPenalty.LeaveBeforeMatchStart", 50); + } + + ArenaTeamStats atStats = plrArenaTeam->GetStats(); + + if (int32(atStats.Rating) - ratingLoss < 0) + atStats.Rating = 0; + else + atStats.Rating -= ratingLoss; + + atStats.SeasonGames += 1; + atStats.WeekGames += 1; + atStats.Rank = 1; + + // Update team's rank, start with rank 1 and increase until no team with more rating was found + ArenaTeamMgr::ArenaTeamContainer::const_iterator i = sArenaTeamMgr->GetArenaTeamMapBegin(); + for (; i != sArenaTeamMgr->GetArenaTeamMapEnd(); ++i) { + if (i->second->GetType() == ARENA_TEAM_SOLO_3v3 && i->second->GetStats().Rating > atStats.Rating) + ++atStats.Rank; + } + + for (ArenaTeam::MemberList::iterator itr = plrArenaTeam->GetMembers().begin(); itr != plrArenaTeam->GetMembers().end(); ++itr) { + if (itr->Guid == player->GetGUID()) { + itr->WeekGames = atStats.WeekGames; + itr->SeasonGames = atStats.SeasonGames; + itr->PersonalRating = atStats.Rating; + + if (int32(itr->MatchMakerRating) - ratingLoss < 0) + itr->MatchMakerRating = 0; + else + itr->MatchMakerRating -= ratingLoss; + + break; + } + } + + plrArenaTeam->SetArenaTeamStats(atStats); + plrArenaTeam->NotifyStatsChanged(); + plrArenaTeam->SaveToDB(true); +} + void Solo3v3::CleanUp3v3SoloQ(Battleground* bg) { // Cleanup temp arena teams for solo 3v3 @@ -65,50 +123,31 @@ void Solo3v3::CleanUp3v3SoloQ(Battleground* bg) void Solo3v3::CheckStartSolo3v3Arena(Battleground* bg) { - // Fix crash with Arena Replay module + bool someoneNotInArena = false; + uint32 PlayersInArena = 0; + for (const auto& playerPair : bg->GetPlayers()) { Player* player = playerPair.second; - if (player->IsSpectator()) - return; - } - if (bg->GetArenaType() != ARENA_TYPE_3v3_SOLO) - return; - - if (bg->GetStatus() != STATUS_IN_PROGRESS) - return; // if CheckArenaWinConditions ends the game - - bool someoneNotInArena = false; + if (!player) + continue; - ArenaTeam* team[2]; - team[0] = sArenaTeamMgr->GetArenaTeamById(bg->GetArenaTeamIdForTeam(TEAM_ALLIANCE)); - team[1] = sArenaTeamMgr->GetArenaTeamById(bg->GetArenaTeamIdForTeam(TEAM_HORDE)); + // Fix crash with Arena Replay module + if (player->IsSpectator()) + return; - ASSERT(team[0] && team[1]); + PlayersInArena++; + } - for (int i = 0; i < 2; i++) + uint32 AmountPlayersSolo3v3 = 6; + if (PlayersInArena < AmountPlayersSolo3v3) { - for (auto const& itr : team[i]->GetMembers()) - { - Player* plr = ObjectAccessor::FindPlayer(itr.Guid); - if (!plr) - { - someoneNotInArena = true; - continue; - } - - if (plr->GetInstanceId() != bg->GetInstanceID()) - { - if (sConfigMgr->GetOption("Solo.3v3.CastDeserterOnAfk", true)) - plr->CastSpell(plr, 26013, true); // Deserter - - someoneNotInArena = true; - } - } + someoneNotInArena = true; } - if (someoneNotInArena && sConfigMgr->GetOption("Solo.3v3.StopGameIncomplete", false)) + // if one player didn't enter arena and StopGameIncomplete is true, then end arena + if (someoneNotInArena && sConfigMgr->GetOption("Solo.3v3.StopGameIncomplete", true)) { bg->SetRated(false); bg->EndBattleground(TEAM_NEUTRAL); diff --git a/src/solo3v3.h b/src/solo3v3.h index d58b605..2aaf9c1 100644 --- a/src/solo3v3.h +++ b/src/solo3v3.h @@ -127,6 +127,7 @@ class Solo3v3 void CleanUp3v3SoloQ(Battleground* bg); bool CheckSolo3v3Arena(BattlegroundQueue* queue, BattlegroundBracketId bracket_id, bool isRated); void CreateTempArenaTeamForQueue(BattlegroundQueue* queue, ArenaTeam* arenaTeams[]); + void CountAsLoss(Player* player, bool isInProgress); // Return false, if player have invested more than 35 talentpoints in a forbidden talenttree. bool Arena3v3CheckTalents(Player* player); diff --git a/src/solo3v3_sc.cpp b/src/solo3v3_sc.cpp index 0c50cc7..9526a46 100644 --- a/src/solo3v3_sc.cpp +++ b/src/solo3v3_sc.cpp @@ -106,7 +106,7 @@ bool NpcSolo3v3::OnGossipSelect(Player* player, Creature* creature, uint32 /*sen } else { - ChatHandler(player->GetSession()).PSendSysMessage("You need level %u+ to create an arena team.", sConfigMgr->GetOption("Solo.3v3.MinLevel", 80)); + ChatHandler(player->GetSession()).PSendSysMessage("You need level {}+ to create an arena team.", sConfigMgr->GetOption("Solo.3v3.MinLevel", 80)); } CloseGossipMenuFor(player); @@ -180,6 +180,19 @@ bool NpcSolo3v3::OnGossipSelect(Player* player, Creature* creature, uint32 /*sen ChatHandler(player->GetSession()).PSendSysMessage("{}", s.str().c_str()); CloseGossipMenuFor(player); + + ArenaTeam::MemberList::iterator itr; + for (itr = at->GetMembers().begin(); itr != at->GetMembers().end(); ++itr) + { + if (itr->Guid == player->GetGUID()) + { + std::stringstream s; + s << "\nSolo MMR: " << itr->MatchMakerRating; + + ChatHandler(player->GetSession()).PSendSysMessage("{}", s.str().c_str()); + break; + } + } } return true; @@ -514,14 +527,6 @@ bool Solo3v3BG::OnQueueUpdateValidity(BattlegroundQueue* /* queue */, uint32 /*d return true; } -void Solo3v3BG::OnBattlegroundUpdate(Battleground* bg, uint32 /*diff*/) -{ - if (bg->GetStatus() != STATUS_IN_PROGRESS || !bg->isArena()) - return; - - sSolo->CheckStartSolo3v3Arena(bg); -} - void Solo3v3BG::OnBattlegroundDestroy(Battleground* bg) { sSolo->CleanUp3v3SoloQ(bg); @@ -531,7 +536,8 @@ void Solo3v3BG::OnBattlegroundEndReward(Battleground* bg, Player* player, TeamId { if (bg->isRated() && bg->GetArenaType() == ARENA_TYPE_3v3_SOLO) { - ArenaTeam* plrArenaTeam = sArenaTeamMgr->GetArenaTeamByCaptain(player->GetGUID(), ARENA_TYPE_3v3_SOLO); + // this way we always get the correct solo team (sometimes when using GetArenaTeamByCaptain inside solo arena it can return a teamID >= 4293918720) + ArenaTeam* plrArenaTeam = sArenaTeamMgr->GetArenaTeamById(player->GetArenaTeamId(ARENA_SLOT_SOLO_3v3)); if (!plrArenaTeam) return; @@ -567,13 +573,10 @@ void Solo3v3BG::OnBattlegroundEndReward(Battleground* bg, Player* player, TeamId atStats.Rank = 1; ArenaTeamMgr::ArenaTeamContainer::const_iterator i = sArenaTeamMgr->GetArenaTeamMapBegin(); for (; i != sArenaTeamMgr->GetArenaTeamMapEnd(); ++i) { - if (i->second->GetType() == ARENA_TYPE_3v3_SOLO && i->second->GetStats().Rating > atStats.Rating) + if (i->second->GetType() == ARENA_TEAM_SOLO_3v3 && i->second->GetStats().Rating > atStats.Rating) ++atStats.Rank; } - plrArenaTeam->SetArenaTeamStats(atStats); - plrArenaTeam->NotifyStatsChanged(); - for (ArenaTeam::MemberList::iterator itr = plrArenaTeam->GetMembers().begin(); itr != plrArenaTeam->GetMembers().end(); ++itr) { if (itr->Guid == player->GetGUID()) @@ -602,6 +605,8 @@ void Solo3v3BG::OnBattlegroundEndReward(Battleground* bg, Player* player, TeamId } + plrArenaTeam->SetArenaTeamStats(atStats); + plrArenaTeam->NotifyStatsChanged(); plrArenaTeam->SaveToDB(true); } } @@ -648,9 +653,94 @@ void Team3v3arena::OnQueueIdToArenaType(const BattlegroundQueueTypeId _bgQueueTy } } +void Arena_SC::OnArenaStart(Battleground* bg) +{ + if (bg->GetArenaType() != ARENA_TYPE_3v3_SOLO) + return; + + sSolo->CheckStartSolo3v3Arena(bg); +} + +void PlayerScript3v3Arena::OnBattlegroundDesertion(Player* player, const BattlegroundDesertionType type) +{ + Battleground* bg = ((BattlegroundMap*)player->FindMap())->GetBG(); + + switch (type) + { + case ARENA_DESERTION_TYPE_LEAVE_BG: + + if (bg->GetArenaType() == ARENA_TYPE_3v3_SOLO) + { + if (bg->GetStatus() == STATUS_WAIT_JOIN) + { + if (sConfigMgr->GetOption("Solo.3v3.CastDeserterOnAfk", true) || sConfigMgr->GetOption("Solo.3v3.CastDeserterOnLeave", true)) + player->CastSpell(player, 26013, true); + + // end arena if a player leaves while in preparation + if (sConfigMgr->GetOption("Solo.3v3.StopGameIncomplete", true)) + { + bg->SetRated(false); + bg->EndBattleground(TEAM_NEUTRAL); + } + + sSolo->CountAsLoss(player, false); + } + + if (bg->GetStatus() == STATUS_IN_PROGRESS) + sSolo->CountAsLoss(player, true); + } + break; + + case ARENA_DESERTION_TYPE_NO_ENTER_BUTTON: // called if player doesn't click 'enter arena' for solo 3v3 + + if (player->IsInvitedForBattlegroundQueueType((BattlegroundQueueTypeId)BATTLEGROUND_QUEUE_3v3_SOLO)) + { + if (sConfigMgr->GetOption("Solo.3v3.CastDeserterOnAfk", true)) + player->CastSpell(player, 26013, true); + + sSolo->CountAsLoss(player, false); + } + break; + + case ARENA_DESERTION_TYPE_INVITE_LOGOUT: // called if player logout when solo 3v3 queue pops (it removes the queue) + + if (player->IsInvitedForBattlegroundQueueType((BattlegroundQueueTypeId)BATTLEGROUND_QUEUE_3v3_SOLO)) + { + if (sConfigMgr->GetOption("Solo.3v3.CastDeserterOnAfk", true) || sConfigMgr->GetOption("Solo.3v3.CastDeserterOnLeave", true)) + player->CastSpell(player, 26013, true); + + sSolo->CountAsLoss(player, false); + } + break; + + /* + case ARENA_DESERTION_TYPE_LEAVE_QUEUE: // called if player uses macro to leave queue when it pops. /run AcceptBattlefieldPort(1, 0); + + // I believe these are being called AFTER the player removes the queue, so we can't know his queue + if (player->IsInvitedForBattlegroundQueueType((BattlegroundQueueTypeId)BATTLEGROUND_QUEUE_3v3_SOLO)) + { + LOG_ERROR("solo3v3", "IsInvitedForBattlegroundQueueType BATTLEGROUND_QUEUE_3v3_SOLO"); + sSolo->CountAsLoss(player, false); + + } + else if (player->InBattlegroundQueueForBattlegroundQueueType((BattlegroundQueueTypeId)BATTLEGROUND_QUEUE_3v3_SOLO)) + { + LOG_ERROR("solo3v3", "InBattlegroundQueueForBattlegroundQueueType BATTLEGROUND_QUEUE_3v3_SOLO"); + } + else + { + LOG_ERROR("solo3v3", "ARENA_DESERTION_TYPE_LEAVE_QUEUE - else"); + } + */ + + default: + break; + } +} + void PlayerScript3v3Arena::OnLogin(Player* pPlayer) { - if (sConfigMgr->GetOption("Solo.3v3.Enable", false)) { + if (sConfigMgr->GetOption("Solo.3v3.ShowMessageOnLogin", false)) { ChatHandler(pPlayer->GetSession()).SendSysMessage("This server is running the |cff4CFF00Arena solo Q 3v3 |rmodule."); } } @@ -704,7 +794,6 @@ bool PlayerScript3v3Arena::NotSetArenaTeamInfoField(Player* player, uint8 slot, return true; } - bool PlayerScript3v3Arena::CanBattleFieldPort(Player* player, uint8 arenaType, BattlegroundTypeId BGTypeID, uint8 /*action*/) { if (!player) diff --git a/src/solo3v3_sc.h b/src/solo3v3_sc.h index 508d61c..31e59c3 100644 --- a/src/solo3v3_sc.h +++ b/src/solo3v3_sc.h @@ -67,7 +67,6 @@ class Solo3v3BG : public AllBattlegroundScript uint32 oldTeamRatingHorde; void OnQueueUpdate(BattlegroundQueue* queue, uint32 /*diff*/, BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id, uint8 arenaType, bool isRated, uint32 /*arenaRatedTeamId*/) override; - void OnBattlegroundUpdate(Battleground* bg, uint32 /*diff*/) override; bool OnQueueUpdateValidity(BattlegroundQueue* /* queue */, uint32 /*diff*/, BattlegroundTypeId /* bgTypeId */, BattlegroundBracketId /* bracket_id */, uint8 arenaType, bool /* isRated */, uint32 /*arenaRatedTeamId*/) override; void OnBattlegroundDestroy(Battleground* bg) override; void OnBattlegroundEndReward(Battleground* bg, Player* player, TeamId /* winnerTeamId */) override; @@ -103,6 +102,7 @@ class PlayerScript3v3Arena : public PlayerScript void OnGetArenaTeamId(Player* player, uint8 slot, uint32& result) override; bool NotSetArenaTeamInfoField(Player* player, uint8 slot, ArenaTeamInfoType type, uint32 value) override; bool CanBattleFieldPort(Player* player, uint8 arenaType, BattlegroundTypeId BGTypeID, uint8 action) override; + void OnBattlegroundDesertion(Player* player, const BattlegroundDesertionType type) override; }; class Arena_SC : public ArenaScript @@ -140,6 +140,8 @@ class Arena_SC : public ArenaScript return true; } + + void OnArenaStart(Battleground* bg) override; }; class Solo3v3Spell : public SpellSC