Index: src/game/ArenaTeam.cpp =================================================================== --- src/game/ArenaTeam.cpp (revision 5306) +++ src/game/ArenaTeam.cpp (working copy) @@ -51,7 +51,7 @@ if(objmgr.GetArenaTeamByName(ArenaTeamName)) // arena team with this name already exist return false; - sLog.outDebug("GUILD: creating arena team %s to leader: %u", ArenaTeamName.c_str(), GUID_LOPART(CaptainGuid)); + sLog.outDebug("GUILD: creating arena team %s to leader: %u", ArenaTeamName.c_str(), GUID_LOPART(captainGuid)); CaptainGuid = captainGuid; Name = ArenaTeamName; @@ -88,11 +88,21 @@ std::string plName; uint8 plClass; + if(GetMembersSize() >= GetType() * 2) + { + // arena team is full (can't have more than type * 2 players!) + // return false + return false; + } + if(!objmgr.GetPlayerNameByGUID(PlayerGuid, plName)) // player doesnt exist return false; // player already in arenateam of that size - if(Player::GetArenaTeamIdFromDB(PlayerGuid, GetSlot()) != 0) + if(Player::GetArenaTeamIdFromDB(PlayerGuid, GetType()) != 0) + { + sLog.outError("Arena::AddMember() : player already in this sized team"); return false; + } // remove all player signs from another petitions // this will be prevent attempt joining player to many arenateams and corrupt arena team data integrity @@ -120,6 +130,7 @@ newmember.played_week = 0; newmember.wons_season = 0; newmember.wons_week = 0; + newmember.points_to_add = 0; members.push_back(newmember); CharacterDatabase.PExecute("INSERT INTO arena_team_member (arenateamid,guid) VALUES ('%u', '%u')", Id, GUID_LOPART(newmember.guid)); @@ -128,10 +139,14 @@ { pl->SetInArenaTeam(Id, GetSlot()); pl->SetArenaTeamIdInvited(0); + // personal rating + pl->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6) + 5, 1500); } else { Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6), Id, PlayerGuid); + // personal rating + Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6) + 5, 1500, PlayerGuid); } // hide promote/remove buttons @@ -197,7 +212,7 @@ { Field *fields; - QueryResult *result = CharacterDatabase.PQuery("SELECT guid,played_week,wons_week,played_season,wons_season FROM arena_team_member WHERE arenateamid = '%u'", ArenaTeamId); + QueryResult *result = CharacterDatabase.PQuery("SELECT guid,played_week,wons_week,played_season,wons_season,points_to_add FROM arena_team_member WHERE arenateamid = '%u'", ArenaTeamId); if(!result) return; @@ -211,6 +226,7 @@ newmember.wons_week = fields[2].GetUInt32(); newmember.played_season = fields[3].GetUInt32(); newmember.wons_season = fields[4].GetUInt32(); + newmember.points_to_add = fields[5].GetUInt32(); members.push_back(newmember); }while( result->NextRow() ); delete result; @@ -269,13 +285,22 @@ { player->SetInArenaTeam(0, GetSlot()); player->GetSession()->SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, GetName(), "", 0); + // delete all info regarding this team + for(int i = 0; i < 6; ++i) + { + player->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6) + i, 0); + Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6) + i, 0, guid); + } } else { - Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6), 0, guid); + // delete all info regarding this team + for(int i = 0; i < 6; ++i) + Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6) + i, 0, guid); } - CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE guid = '%u'", GUID_LOPART(guid)); + // only delete from this arena team! + CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u' AND guid = '%u'", GetId(), GUID_LOPART(guid)); } void ArenaTeam::Disband(WorldSession *session) @@ -302,6 +327,7 @@ CharacterDatabase.BeginTransaction(); CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid = '%u'", Id); + CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u'", Id); CharacterDatabase.PExecute("DELETE FROM arena_team_stats WHERE arenateamid = '%u'", Id); CharacterDatabase.CommitTransaction(); objmgr.RemoveArenaTeam(this); @@ -380,6 +406,18 @@ session->SendPacket(&data); } +void ArenaTeam::NotifyStatsChanged() +{ + // this is called after a rated match ended + // updates arena team stats for every member of the team (not only the ones who participated!) + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + Player * plr=objmgr.GetPlayer(itr->guid); + if(plr) + Stats(plr->GetSession()); + } +} + void ArenaTeam::InspectStats(WorldSession *session, uint64 guid) { WorldPacket data(MSG_INSPECT_ARENA_STATS, 8+1+4*6); @@ -387,10 +425,20 @@ data << uint8(GetSlot()); // slot (0...2) data << uint32(GetId()); // arena team id data << uint32(stats.rating); // rating - data << uint32(stats.games); // games - data << uint32(stats.wins); // wins - data << uint32(stats.played); // played (count of all games, that played...) - data << uint32(0); // unk, 2.3.3 + data << uint32(stats.played); // season played + data << uint32(stats.wins2); // season wins + uint32 participated = 0; + for(MemberList::iterator itr = members.begin(); itr!= members.end(); ++itr) + { + if(itr->guid == guid) + { + participated = itr->played_season; + break; + } + } + data << uint32(participated); // played (count of all games, that the inspected member participated...) + data << uint32(0); // unk, 2.3.3 (personal rating?) + session->SendPacket(&data); } @@ -468,24 +516,204 @@ } } +uint32 ArenaTeam::GetPoints() +{ + // returns how many points would be awarded with this team type with this rating + float points; + + if(stats.rating<=1500) + { + points = (float)stats.rating * 0.22f + 14.0f; + } + else + { + points = 1511.26f / (1.0f + 1639.28f * exp(-0.00412f * (float)stats.rating)); + } + + // type penalties for <5v5 teams + if(Type == ARENA_TEAM_2v2) + points *= 0.76f; + else if(Type == ARENA_TEAM_3v3) + points *= 0.88f; + + return (uint32) points; +} + +float ArenaTeam::GetChanceAgainst(uint32 rating) +{ + // returns the chance to win against a team with the given rating, used in the rating adjustment calculation + // ELO system + return 1.0f/(1.0f+exp(log(10.0f)*(float)((float)rating - (float)stats.rating)/400.0f)); +} + +int32 ArenaTeam::WonAgainstChance(float chance) +{ + // called when the team has won, and had 'chance' calculated chance to beat the opponent + // calculate the rating modification (ELO system with k=32) + int32 mod = (int32)(32.0f * (1.0f - chance)); + // modify the team stats accordingly + stats.rating += mod; + stats.games += 1; + stats.wins += 1; + stats.played += 1; + stats.wins2 += 1; +/* this should be done in .flusharenapoints; not a breaker though. + uint32 higher_rank = 0; + QueryResult *result = CharacterDatabase.PQuery("SELECT DISTINCT COUNT(arenateamid) FROM arena_team_stats WHERE rating > '%u' AND arenateamid <> '%u'",stats.rating, Id); + if(result) + { + higher_rank = result->Fetch()->GetUInt32(); + delete result; + } + stats.rank = higher_rank + 1;*/ + // return the rating change, used to display it on the results screen + return mod; +} + +int32 ArenaTeam::LostAgainstChance(float chance) +{ + // called when the team has lost, and had 'chance' calculated chance to beat the opponent + // calculate the rating modification (ELO system with k=32) + int32 mod = (int32)(32.0f * (0.0f - chance)); + // modify the team stats accordingly + stats.rating += mod; + stats.games += 1; + stats.played += 1; +/* uint32 higher_rank = 0; + QueryResult *result = CharacterDatabase.PQuery("SELECT DISTINCT COUNT (arenateamid) FROM arena_team_stats WHERE rating > '%u' AND arenateamid <> '%u'",stats.rating, Id); + if(result) + { + higher_rank = result->Fetch()->GetUInt32(); + delete result; + } + stats.rank = higher_rank + 1;*/ + // return the rating adjustment for display + return mod; +} + +void ArenaTeam::MemberLost(Player * plr, uint32 againstrating) +{ + // called for each participant of a match after losing + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + if(itr->guid == plr->GetGUID()) + { + // update personal rating + int32 personalrating = plr->GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot()*6) + 5); + float chance = 1.0f/(1.0f+exp(log(10.0f)*(float)((float)againstrating - (float)personalrating)/400.0f)); + int32 mod = (int32)(32.0f * (0.0f - chance)); + personalrating += mod; + if(personalrating < 0) + personalrating = 0; + plr->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot()*6) + 5, personalrating); + // update personal played stats + itr->played_week +=1; + itr->played_season +=1; + // update the unit fields + plr->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 6 * GetSlot() + 2, itr->played_week); + plr->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 6 * GetSlot() + 3, itr->played_season); + return; + } + } +} + +void ArenaTeam::MemberWon(Player * plr, uint32 againstrating) +{ + // called for each participant after winning a match + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + if(itr->guid == plr->GetGUID()) + { + // update personal rating + int32 personalrating = plr->GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot()*6) + 5); + float chance = 1.0f/(1.0f+exp(log(10.0f)*(float)((float)againstrating - (float)personalrating)/400.0f)); + int32 mod = (int32)(32.0f * (1.0f - chance)); + personalrating += mod; + if(personalrating < 0) + personalrating = 0; + plr->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot()*6) + 5, personalrating); + // update personal stats + itr->played_week +=1; + itr->played_season +=1; + itr->wons_season += 1; + itr->wons_week += 1; + // update unit fields + plr->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 6 * GetSlot() + 2, itr->played_week); + plr->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 6 * GetSlot() + 3, itr->played_season); + return; + } + } +} + +uint32 ArenaTeam::GetMemberPoints(uint64 guid) +{ + // get the points a member should be awarded + // called from arena point distribution (.flusharenapoints command) + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + if(itr->guid == guid) + { + return itr->points_to_add; + } + } + return 0; +} + +void ArenaTeam::UpdateArenaPointsHelper() +{ + // called after a match has ended and the stats are already modified + // helper function for arena point distribution (this way, when distributing, no actual calculation is required, just a few comparisons) + // 10 played games per week is a minimum + if(stats.games < 10) + return; + // to get points, a player has to participate in at least 30% of the matches + uint32 min_plays = stats.games * 0.3; + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + // the player participated in enough games, update his points + if(itr->played_week >= min_plays) + { + // it's enough to set the points in memory, the saving is done in separate function + itr->points_to_add = GetPoints(); + // CharacterDatabase.PExecute("UPDATE arena_team_member SET points_to_add = '%u' WHERE arenateamid = '%u' AND guid = '%u'", itr->points_to_add,Id, itr->guid); + } + // the player failed to participate in enough games, so no points for him + else + { + itr->points_to_add = 0; + } + } +} + +void ArenaTeam::SaveToDB() +{ + // save team and member stats to db + // called after a match has ended + CharacterDatabase.PExecute("UPDATE arena_team_stats SET rating = '%u',games = '%u',played = '%u',rank = '%u',wins = '%u',wins2 = '%u' WHERE arenateamid = '%u'", stats.rating, stats.games, stats.played, stats.rank, stats.wins, stats.wins2, GetId()); + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + CharacterDatabase.PExecute("UPDATE arena_team_member SET played_week = '%u', wons_week = '%u', played_season = '%u', wons_season = '%u', points_to_add = '%u' WHERE arenateamid = '%u' AND guid = '%u'", itr->played_week, itr->wons_week, itr->played_season, itr->wons_season, itr->points_to_add, Id, itr->guid); + } +} + /* arenateam fields (id from 2.3.3 client): 1414 - arena team id 2v2 1415 - 0=captain, 1=member -1416 - played this season -1417 - played this week +1416 - played this week +1417 - played this season 1418 - unk -1419 - unk +1419 - personal arena rating 1420 - arena team id 3v3 1421 - 0=captain, 1=member -1422 - played this season -1423 - played this week +1422 - played this week +1423 - played this season 1424 - unk -1425 - unk +1425 - personal arena rating 1426 - arena team id 5v5 1427 - 0=captain, 1=member -1428 - played this season -1429 - played this week +1428 - played this week +1429 - played this season 1430 - unk -1431 - unk +1431 - personal arena rating */ Index: src/game/ArenaTeam.h =================================================================== --- src/game/ArenaTeam.h (revision 5306) +++ src/game/ArenaTeam.h (working copy) @@ -92,6 +92,7 @@ uint32 wons_week; uint32 played_season; uint32 wons_season; + uint32 points_to_add; }; struct ArenaTeamStats @@ -145,6 +146,8 @@ void LoadStatsFromDB(uint32 ArenaTeamId); void LoadPlayerStats(ArenaTeamMember* member); + void SaveToDB(); + void BroadcastPacket(WorldPacket *packet); void Roster(WorldSession *session); @@ -152,6 +155,18 @@ void Stats(WorldSession *session); void InspectStats(WorldSession *session, uint64 guid); + uint32 GetPoints(); + float GetChanceAgainst(uint32 rating); + int32 WonAgainstChance(float chance); + void MemberWon(Player * plr, uint32 againstrating); + int32 LostAgainstChance(float chance); + void MemberLost(Player * plr, uint32 againstrating); + + void UpdateArenaPointsHelper(); + uint32 GetMemberPoints(uint64 guid); + + void NotifyStatsChanged(); + protected: uint32 Id; Index: src/game/ArenaTeamHandler.cpp =================================================================== --- src/game/ArenaTeamHandler.cpp (revision 5306) +++ src/game/ArenaTeamHandler.cpp (working copy) @@ -111,7 +111,7 @@ return; } - if(player->getLevel() != 70) + if(player->getLevel() < 70) { //SendArenaTeamCommandResult(ARENA_TEAM_INVITE_SS,"",Invitedname,ARENA_TEAM_PLAYER_NOT_FOUND_S); // can't find related opcode @@ -148,6 +148,14 @@ return; } + if(arenateam->GetMembersSize() >= arenateam->GetType() * 2) + { + // should send an "arena team is full" or the likes message, I just don't know the proper values so... ERR_INTERNAL +// SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_INTERNAL); + SendNotification("Your arena team is full, %s cannot join it.", player->GetName()); + return; + } + sLog.outDebug("Player %s Invited %s to Join his ArenaTeam", GetPlayer()->GetName(), Invitedname.c_str()); player->SetArenaTeamIdInvited(GetPlayer()->GetArenaTeamId(team_slot)); Index: src/game/BattleGround.cpp =================================================================== --- src/game/BattleGround.cpp (revision 5306) +++ src/game/BattleGround.cpp (working copy) @@ -24,6 +24,7 @@ #include "Language.h" // for chat messages #include "Chat.h" #include "SpellAuras.h" +#include "ArenaTeam.h" #include "World.h" BattleGround::BattleGround() @@ -45,6 +46,8 @@ m_Name = ""; m_LevelMin = 0; m_LevelMax = 0; + m_InBGFreeSlotQueue = false; + m_SetDeleteThis = false; m_MaxPlayersPerTeam = 0; m_MaxPlayers = 0; @@ -65,6 +68,12 @@ m_TeamStartLocO[BG_TEAM_ALLIANCE] = 0; m_TeamStartLocO[BG_TEAM_HORDE] = 0; + m_ArenaTeamIds[BG_TEAM_ALLIANCE] = 0; + m_ArenaTeamIds[BG_TEAM_HORDE] = 0; + + m_ArenaTeamRatingChanges[BG_TEAM_ALLIANCE] = 0; + m_ArenaTeamRatingChanges[BG_TEAM_HORDE] = 0; + m_BgRaids[BG_TEAM_ALLIANCE] = NULL; m_BgRaids[BG_TEAM_HORDE] = NULL; @@ -74,12 +83,34 @@ BattleGround::~BattleGround() { + // remove objects and creatures + // (this is done automatically in mapmanager update, when the instance is reset after the reset time) + int size = m_BgCreatures.size(); + for(int i = 0; i < size; ++i) + { + DelCreature(i); + } + size = m_BgObjects.size(); + for(int i = 0; i < size; ++i) + { + DelObject(i); + } + // delete bound char instances (this should have been already deleted?) + CharacterDatabase.PExecute("DELETE FROM `character_instance` WHERE `instance` = '%u'", GetInstanceID()); + // delete creature and go respawn times + WorldDatabase.PExecute("DELETE FROM `creature_respawn` WHERE `instance` = '%u'",GetInstanceID()); + WorldDatabase.PExecute("DELETE FROM `gameobject_respawn` WHERE `instance` = '%u'",GetInstanceID()); + // delete instance from db + CharacterDatabase.PExecute("DELETE FROM `instance` WHERE id = '%u'",GetInstanceID()); + // remove from battlegrounds + sBattleGroundMgr.RemoveBattleGround(GetInstanceID()); + // remove from bg free slot queue + this->RemoveFromBGFreeSlotQueue(); } void BattleGround::Update(time_t diff) { - if(!GetPlayersSize() && !GetRemovedPlayersSize() && !GetReviveQueueSize()) //BG is empty return; @@ -236,7 +267,10 @@ if(!self && sender == plr) continue; - if(plr->GetTeam() == TeamID) + uint32 team = itr->second.Team;//GetPlayerTeam(plr->GetGUID()); + if(!team) team = plr->GetTeam(); + + if(team == TeamID) plr->GetSession()->SendPacket(packet); } } @@ -262,7 +296,10 @@ continue; } - if(plr->GetTeam() == TeamID) + uint32 team = itr->second.Team;//GetPlayerTeam(plr->GetGUID()); + if(!team) team = plr->GetTeam(); + + if(team == TeamID) { sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID); plr->GetSession()->SendPacket(&data); @@ -282,7 +319,10 @@ continue; } - if(plr->GetTeam() == TeamID) + uint32 team = itr->second.Team;//GetPlayerTeam(plr->GetGUID()); + if(!team) team = plr->GetTeam(); + + if(team == TeamID) plr->CastSpell(plr, SpellID, true); } } @@ -299,7 +339,10 @@ continue; } - if(plr->GetTeam() == TeamID) + uint32 team = itr->second.Team;//GetPlayerTeam(plr->GetGUID()); + if(!team) team = plr->GetTeam(); + + if(team == TeamID) UpdatePlayerScore(plr, SCORE_BONUS_HONOR, Honor); } } @@ -321,7 +364,10 @@ continue; } - if(plr->GetTeam() == TeamID) + uint32 team = itr->second.Team;//GetPlayerTeam(plr->GetGUID()); + if(!team) team = plr->GetTeam(); + + if(team == TeamID) plr->ModifyFactionReputation(factionEntry, Reputation); } } @@ -335,13 +381,22 @@ void BattleGround::EndBattleGround(uint32 winner) { + this->RemoveFromBGFreeSlotQueue(); + + ArenaTeam * winner_arena_team = NULL; + ArenaTeam * loser_arena_team = NULL; + uint32 loser_rating = 0; + uint32 winner_rating = 0; WorldPacket data; Player *Source = NULL; const char *winmsg = ""; if(winner == ALLIANCE) { - winmsg = GetMangosString(LANG_BG_A_WINS); + if(isBattleGround()) + winmsg = GetMangosString(LANG_BG_A_WINS); + else + winmsg = GetMangosString(LANG_ARENA_GOLD_WINS); PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound @@ -349,7 +404,10 @@ } else { - winmsg = GetMangosString(LANG_BG_H_WINS); + if(isBattleGround()) + winmsg = GetMangosString(LANG_BG_H_WINS); + else + winmsg = GetMangosString(LANG_ARENA_GREEN_WINS); PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound @@ -359,6 +417,47 @@ SetStatus(STATUS_WAIT_LEAVE); m_EndTime = 0; + // arena rating calculation + if(isArena() && isRated()) + { + if(winner == ALLIANCE) + { + winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(ALLIANCE)); + loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(HORDE)); + } + else + { + winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(HORDE)); + loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(ALLIANCE)); + } + if(winner_arena_team && loser_arena_team) + { + loser_rating = loser_arena_team->GetStats().rating; + winner_rating = winner_arena_team->GetStats().rating; + float winner_chance = winner_arena_team->GetChanceAgainst(loser_rating); + float loser_chance = loser_arena_team->GetChanceAgainst(winner_rating); + int32 winner_change = winner_arena_team->WonAgainstChance(winner_chance); + int32 loser_change = loser_arena_team->LostAgainstChance(loser_chance); + if(winner == ALLIANCE) + { + SetArenaTeamRatingChangeForTeam(ALLIANCE, winner_change); + SetArenaTeamRatingChangeForTeam(HORDE, loser_change); + } + else + { + SetArenaTeamRatingChangeForTeam(HORDE, winner_change); + SetArenaTeamRatingChangeForTeam(ALLIANCE, loser_change); + } + winner_arena_team->UpdateArenaPointsHelper(); + loser_arena_team->UpdateArenaPointsHelper(); + } + else + { + SetArenaTeamRatingChangeForTeam(ALLIANCE, 0); + SetArenaTeamRatingChangeForTeam(HORDE, 0); + } + } + uint32 mark = 0; for(std::map::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) @@ -370,14 +469,30 @@ continue; } + // should remove spirit of redemption + if(plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) + plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT); + if(!plr->isAlive()) { plr->ResurrectPlayer(1.0f); plr->SpawnCorpseBones(); } - if(plr->GetTeam() == winner) + uint32 team = itr->second.Team;//GetPlayerTeam(plr->GetGUID()); + if(!team) team = plr->GetTeam(); + + // per player calculation + if(isArena() && isRated() && winner_arena_team && loser_arena_team) { + if(team == winner) + winner_arena_team->MemberWon(plr,loser_rating); + else + loser_arena_team->MemberLost(plr,winner_rating); + } + + if(team == winner) + { if(!Source) Source = plr; switch(GetTypeID()) @@ -428,10 +543,25 @@ sBattleGroundMgr.BuildPvpLogDataPacket(&data, this); plr->GetSession()->SendPacket(&data); - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(m_TypeID), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime()); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(GetTypeID(), GetArenaType()); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime()); plr->GetSession()->SendPacket(&data); } + if(isArena() && isRated() && winner_arena_team && loser_arena_team) + { + // save the stat changes + winner_arena_team->SaveToDB(); + loser_arena_team->SaveToDB(); + // send updated arena team stats to players + // this way all arena team members will get notified, not only the ones who participated in this match + winner_arena_team->NotifyStatsChanged(); + loser_arena_team->NotifyStatsChanged(); + } + + // inform invited players about the removal + sBattleGroundMgr.m_BattleGroundQueues[sBattleGroundMgr.BGQueueTypeId(GetTypeID(), GetArenaType())].BGEndedRemoveInvites(this); + if(Source) { ChatHandler(Source).FillMessageData(&data, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_GLOBAL, Source->GetGUID(), winmsg); @@ -448,11 +578,12 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket) { + uint32 team = GetPlayerTeam(guid); // Remove from lists/maps std::map::iterator itr = m_Players.find(guid); if(itr != m_Players.end()) { - UpdatePlayersCountByTeam(itr->second.Team, true); // -1 player + UpdatePlayersCountByTeam(team, true); // -1 player m_Players.erase(itr); } @@ -467,6 +598,10 @@ Player *plr = objmgr.GetPlayer(guid); + // should remove spirit of redemption + if(plr && plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) + plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT); + if(plr && !plr->isAlive()) // resurrect on exit { plr->ResurrectPlayer(1.0f); @@ -477,41 +612,103 @@ if(plr) { + if(!team) team = plr->GetTeam(); + + uint32 bgTypeId = GetTypeID(); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(GetTypeID(), GetArenaType()); + // if arena, remove the specific arena auras if(isArena()) { - if(!sWorld.IsFFAPvPRealm()) - plr->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP); + plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out + plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); + plr->RemoveAurasDueToSpell(SPELL_ALLIANCE_GOLD_FLAG); + plr->RemoveAurasDueToSpell(SPELL_ALLIANCE_GREEN_FLAG); + plr->RemoveAurasDueToSpell(SPELL_HORDE_GOLD_FLAG); + plr->RemoveAurasDueToSpell(SPELL_HORDE_GREEN_FLAG); + bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing) - plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); + Pet* NewPet = new Pet(plr); + if(!NewPet->LoadPetFromDB(plr, 0, (plr)->GetOldPetNumber(), true)) + delete NewPet; + + (plr)->SetOldPetNumber(0); + if(isRated() && GetStatus() == STATUS_IN_PROGRESS) + { + //left a rated match while the encounter was in progress, consider as loser + ArenaTeam * winner_arena_team = 0; + ArenaTeam * loser_arena_team = 0; + if(team == HORDE) + { + winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(ALLIANCE)); + loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(HORDE)); + } + else + { + winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(HORDE)); + loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(ALLIANCE)); + } + if(winner_arena_team && loser_arena_team) + { + loser_arena_team->MemberLost(plr,winner_arena_team->GetRating()); + } + } } WorldPacket data; if(SendPacket) { - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(m_TypeID), STATUS_NONE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, team, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0); plr->GetSession()->SendPacket(&data); } // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg - plr->RemoveBattleGroundQueueId(m_TypeID); + plr->RemoveBattleGroundQueueId(bgQueueTypeId); - DecreaseInvitedCount(plr->GetTeam()); + DecreaseInvitedCount(team); //we should update battleground queue, but only if bg isn't ending if (GetQueueType() < MAX_BATTLEGROUND_QUEUES) - sBattleGroundMgr.m_BattleGroundQueues[GetTypeID()].Update(GetTypeID(), GetQueueType()); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, GetQueueType()); if(!plr->GetBattleGroundId()) return; Group * group = plr->GetGroup(); - // remove from raid group if exist - if(group && group == GetBgRaid(plr->GetTeam())) + if(group && group == GetBgRaid(team)) { + uint32 groupSize = group->GetMembersCount(); + GroupReference * grpRef = group->GetFirstMember(); + Player * grpGuy = grpRef->getSource(); + // get a member of the group, will be used if the group is disbanded + // if the one we got is the player, get another + if(grpGuy->GetGUID()==guid) + { + if(grpRef->hasNext()) + { + grpRef = grpRef->next(); + grpGuy = grpRef->getSource(); + } + else if(grpRef->hasPrev()) + { + grpRef = (GroupReference*)grpRef->prev(); + grpGuy = grpRef->getSource(); + } + else + grpGuy = NULL; + } if(!group->RemoveMember(guid, 0)) // group was disbanded { - SetBgRaid(plr->GetTeam(), NULL); + SetBgRaid(team, NULL); delete group; + if(groupSize >= 2 && grpGuy != 0) // someone's still left in the bg, recreate group for him + { + group = new Group; + SetBgRaid(team, group); + group->ConvertToRaid(); + group->AddMember(grpGuy->GetGUID(), grpGuy->GetName()); + group->ChangeLeader(grpGuy->GetGUID()); + // we don't have to add other players, since the group is only disbanded if the player count before the leave was exactly 2 + } } } @@ -520,11 +717,12 @@ // Let others know sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, plr); - SendPacketToTeam(plr->GetTeam(), &data, plr, false); + SendPacketToTeam(team, &data, plr, false); if(Transport) { plr->TeleportTo(plr->GetBattleGroundEntryPointMap(), plr->GetBattleGroundEntryPointX(), plr->GetBattleGroundEntryPointY(), plr->GetBattleGroundEntryPointZ(), plr->GetBattleGroundEntryPointO(), true, false); + //plr->SetInstanceId(plr->GetBattleGroundEntryPointInstanceId()); //sLog.outDetail("BATTLEGROUND: Sending %s to %f,%f,%f,%f", pl->GetName(), x,y,z,O); } @@ -532,13 +730,29 @@ sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName()); } + // remove player from bg instance! + CharacterDatabase.PExecute("DELETE FROM `character_instance` WHERE `guid` = '%u' AND `instance` = '%u'", guid, GetInstanceID()); + + if(plr) + { + // remove player from bg instance!!! + plr->m_BoundInstances.erase(GetMapId()); + } + /// there will be code which will add battleground to BGFreeSlotQueue , when battleground instance will exist // we always should check if BG is in that queue before adding.. - if(!GetPlayersSize()) + if(!GetPlayersSize() && !GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE)) { - Reset(); + // if no players left AND no invitees left, set this bg to delete in next update + // direct deletion could cause crashes + m_SetDeleteThis = true; + // return to prevent addition to freeslotqueue + return; } + + // a player exited the battleground, so there are free slots. add to queue + this->AddToBGFreeSlotQueue(); } // this method is called when no players remains in battleground @@ -550,6 +764,8 @@ SetStartTime(0); SetEndTime(0); SetLastResurrectTime(0); + SetArenaType(0); + SetRated(false); m_Events = 0; @@ -558,11 +774,12 @@ m_InvitedAlliance = 0; m_InvitedHorde = 0; + m_InBGFreeSlotQueue = false; m_Players.clear(); m_PlayerScores.clear(); - // reset BGSubclass + // reset BGSubclass (this cleans up creatures and gos as well) this->ResetBGSubclass(); } @@ -575,52 +792,64 @@ WorldPacket data; BattleGround *bg = NULL; - - /// this code seems not correct: - if(GetTypeID() == BATTLEGROUND_AA) - { - // we must select one of running arena - uint8 arenas[3] = { BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL }; - uint8 arena_type = arenas[urand(0, 2)]; - - bg = sBattleGroundMgr.GetBattleGround(arena_type); - if(!bg) - return; - - bg->SetStatus(STATUS_WAIT_JOIN); - - bg->SetStartTime(0); - bg->SetLastResurrectTime(0); - } } -void BattleGround::AddPlayer(Player *plr) +void BattleGround::AddPlayer(Player *plr, uint32 team) { // score struct must be created in inherited class + if(team==0) + team = plr->GetTeam(); uint64 guid = plr->GetGUID(); BattleGroundPlayer bp; bp.LastOnlineTime = 0; - bp.Team = plr->GetTeam(); + bp.Team = team; // Add to list/maps m_Players[guid] = bp; - UpdatePlayersCountByTeam(plr->GetTeam(), false); // +1 player + UpdatePlayersCountByTeam(team, false); // +1 player WorldPacket data; sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr); - SendPacketToTeam(plr->GetTeam(), &data, plr, false); + SendPacketToTeam(team, &data, plr, false); + // add arena specific auras if(isArena()) { + plr->RemoveArenaSpellCooldowns(); + plr->RemoveArenaAuras(); + if(team == ALLIANCE && plr->GetTeam() == ALLIANCE) + plr->CastSpell(plr,SPELL_ALLIANCE_GOLD_FLAG,true); + else if(team == HORDE && plr->GetTeam() == ALLIANCE) + plr->CastSpell(plr,SPELL_ALLIANCE_GREEN_FLAG,true); + else if(team == ALLIANCE && plr->GetTeam() == HORDE) + plr->CastSpell(plr,SPELL_HORDE_GOLD_FLAG,true); + else + plr->CastSpell(plr,SPELL_HORDE_GREEN_FLAG,true); + plr->DestroyConjuredItems(true); + + Pet* pet = plr->GetPet(); + if(pet) + { + if(pet->getPetType() == SUMMON_PET || pet->getPetType() == HUNTER_PET) + { + (plr)->SetOldPetNumber(pet->GetCharmInfo()->GetPetNumber()); + (plr)->SetOldPetSpell(pet->GetUInt32Value(UNIT_CREATED_BY_SPELL)); + } + (plr)->RemovePet(NULL,PET_SAVE_NOT_IN_SLOT); + } + else + (plr)->SetOldPetNumber(0); + if(GetStatus() == STATUS_WAIT_JOIN) // not started yet { - // temporary disabled - //plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true); + plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true); + + plr->SetHealth(plr->GetMaxHealth()); + plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA)); } - plr->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP); } // Log @@ -630,13 +859,20 @@ /* This method should be called only once ... it adds pointer to queue */ void BattleGround::AddToBGFreeSlotQueue() { - sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this); + // make sure to add only once + if(!m_InBGFreeSlotQueue) + { + sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this); + m_InBGFreeSlotQueue = true; + } } /* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/ void BattleGround::RemoveFromBGFreeSlotQueue() { - /* uncomment this code when battlegrounds will work like instances + // set to be able to re-add if needed + m_InBGFreeSlotQueue = false; + // uncomment this code when battlegrounds will work like instances for (std::deque::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr) { if ((*itr)->GetInstanceID() == m_InstanceID) @@ -644,7 +880,7 @@ sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr); return; } - }*/ + } } /* @@ -667,6 +903,31 @@ return false; } +// get the number of free slots for team +// works in similar way that HasFreeSlotsForTeam +uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const +{ + //if BG is starting ... invite anyone: + if (GetStatus() == STATUS_WAIT_JOIN) + return (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0; + //if BG is already started .. do not allow to join too much players of one faction + uint32 otherTeam; + if (Team == ALLIANCE) + otherTeam = GetInvitedCount(HORDE); + else + otherTeam = GetInvitedCount(ALLIANCE); + if (GetStatus() == STATUS_IN_PROGRESS) + { + uint32 diff = (otherTeam <= GetInvitedCount(Team)) ? 0 : otherTeam - GetInvitedCount(Team); + + uint32 diff2 = (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0; + + return diff < diff2 ? diff : diff2 ; + } + + return 0; +} + /* this method isn't called already, it will be useful when more battlegrounds of one type will be available */ bool BattleGround::HasFreeSlots() const { @@ -693,7 +954,9 @@ itr->second->HonorableKills += value; break; case SCORE_BONUS_HONOR: // Honor bonus - itr->second->BonusHonor += value; + // do not add honor in arenas + if(isBattleGround()) + itr->second->BonusHonor += value; break; //used only in EY, but in MSG_PVP_LOG_DATA opcode case SCORE_DAMAGE_DONE: // Damage Done @@ -747,49 +1010,30 @@ bool BattleGround::AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 spawntime) { - GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(entry); - if(!goinfo) + GameObject * go = new GameObject(NULL); + go->SetInstanceId(GetInstanceID()); + if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry, GetMapId(),x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,1)) { - sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry); + sLog.outError("Cannot create gameobject template %u! BattleGround not created!", entry); + delete go; return false; } - uint32 guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT); - - GameObjectData& data = objmgr.NewGOData(guid); - - data.id = entry; - data.mapid = GetMapId(); - data.posX = x; - data.posY = y; - data.posZ = z; - data.orientation = o; - data.rotation0 = rotation0; - data.rotation1 = rotation1; - data.rotation2 = rotation2; - data.rotation3 = rotation3; - data.spawntimesecs = spawntime; - data.animprogress = 100; - data.go_state = 1; - objmgr.AddGameobjectToGrid(guid, &data); - - m_BgObjects[type] = MAKE_GUID(guid, HIGHGUID_GAMEOBJECT); - + m_BgObjects[type] = go; return true; } void BattleGround::DoorOpen(uint32 type) { - GameObject *obj = HashMapHolder::Find(m_BgObjects[type]); + GameObject *obj = m_BgObjects[type]; if(obj) { obj->SetUInt32Value(GAMEOBJECT_FLAGS,33); obj->SetUInt32Value(GAMEOBJECT_STATE,0); - //obj->SetLootState(GO_CLOSED); } else { - sLog.outError("Object not found"); + sLog.outError("Battleground: DoorOpen: Object not found"); } } @@ -797,32 +1041,31 @@ { if(respawntime == 0) { - GameObject *obj = HashMapHolder::Find(m_BgObjects[type]); + GameObject *obj = m_BgObjects[type]; if(obj) { //obj->Respawn(); // bugged obj->SetRespawnTime(0); - objmgr.SaveGORespawnTime(obj->GetGUIDLow(), 0, 0); + objmgr.SaveGORespawnTime(obj->GetGUIDLow(), GetInstanceID(), 0); + MapManager::Instance().GetMap(obj->GetMapId(), obj)->Add(obj); } - else - objmgr.SaveGORespawnTime(GUID_LOPART(m_BgObjects[type]), 0, 0); } else { - GameObject *obj = HashMapHolder::Find(m_BgObjects[type]); + GameObject *obj = m_BgObjects[type]; if(obj) { obj->SetRespawnTime(respawntime); obj->SetLootState(GO_LOOTED); + MapManager::Instance().GetMap(obj->GetMapId(), obj)->Add(obj); } - else - objmgr.SaveGORespawnTime(GUID_LOPART(m_BgObjects[type]), 0, time(NULL) + respawntime); } } -Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o) +Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o, uint32 respawntime) { Creature* pCreature = new Creature(NULL); + pCreature->SetInstanceId(GetInstanceID()); if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), GetMapId(), x, y, z, o, entry, teamval)) { sLog.outError("Can't create creature entry: %u",entry); @@ -833,17 +1076,42 @@ pCreature->AIM_Initialize(); //pCreature->SetDungeonDifficulty(0); - MapManager::Instance().GetMap(pCreature->GetMapId(), pCreature)->Add(pCreature); - m_BgCreatures[type] = pCreature->GetGUID(); + m_BgCreatures[type] = pCreature; + return pCreature; } +void BattleGround::SpawnBGCreature(uint32 type, uint32 respawntime) +{ + if(respawntime == 0) + { + Creature *obj = m_BgCreatures[type]; + if(obj) + { + //obj->Respawn(); // bugged + obj->SetRespawnTime(0); + objmgr.SaveCreatureRespawnTime(obj->GetGUIDLow(), GetInstanceID(), 0); + MapManager::Instance().GetMap(obj->GetMapId(), obj)->Add(obj); + } + } + else + { + Creature *obj = m_BgCreatures[type]; + if(obj) + { + obj->setDeathState(DEAD); + obj->SetRespawnTime(respawntime); + MapManager::Instance().GetMap(obj->GetMapId(), obj)->Add(obj); + } + } +} + bool BattleGround::DelCreature(uint32 type) { - Creature *cr = HashMapHolder::Find(m_BgCreatures[type]); + Creature *cr = m_BgCreatures[type]; if(!cr) { - sLog.outError("Can't find creature guid: %u",m_BgCreatures[type]); + sLog.outError("Can't find creature."); return false; } cr->CleanupsBeforeDelete(); @@ -854,10 +1122,10 @@ bool BattleGround::DelObject(uint32 type) { - GameObject *obj = HashMapHolder::Find(m_BgObjects[type]); + GameObject *obj = m_BgObjects[type]; if(!obj) { - sLog.outError("Can't find gobject guid: %u",m_BgObjects[type]); + sLog.outError("Can't find gobject."); return false; } obj->SetRespawnTime(0); // not save respawn time @@ -927,3 +1195,28 @@ { return objmgr.GetMangosString(entry); } + +// return the player's team based on battlegroundplayer info +// used in same faction arena matches mainly +uint32 BattleGround::GetPlayerTeam(uint64 guid) +{ + std::map::const_iterator itr = m_Players.find(guid); + if(itr!=m_Players.end()) + return itr->second.Team; + return 0; +} + +uint32 BattleGround::GetAlivePlayersCountByTeam(uint32 Team) const +{ + int count = 0; + for(std::map::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if(itr->second.Team == Team) + { + Player * pl = objmgr.GetPlayer(itr->first); + if(pl && pl->isAlive()) + ++count; + } + } + return count; +} Index: src/game/BattleGround.h =================================================================== --- src/game/BattleGround.h (revision 5306) +++ src/game/BattleGround.h (working copy) @@ -51,7 +51,11 @@ SPELL_SPIRIT_HEAL_CHANNEL = 22011, SPELL_SPIRIT_HEAL = 22012, SPELL_RESURRECTION_VISUAL = 24171, - SPELL_ARENA_PREPARATION = 32727 + SPELL_ARENA_PREPARATION = 32728, // use this one, 32727 causes problems with creature visibility updates, thus client crashes. + SPELL_ALLIANCE_GOLD_FLAG = 32724, + SPELL_ALLIANCE_GREEN_FLAG = 32725, + SPELL_HORDE_GOLD_FLAG = 35774, + SPELL_HORDE_GREEN_FLAG = 35775 }; enum BattleGroundTimeIntervals @@ -115,6 +119,18 @@ BATTLEGROUND_RL = 8 }; +// handle the queue types and bg types separately to enable joining queue for different sized arenas at the same time +enum BattleGroundQueueTypeId +{ + BATTLEGROUND_QUEUE_AV = 1, + BATTLEGROUND_QUEUE_WS = 2, + BATTLEGROUND_QUEUE_AB = 3, + BATTLEGROUND_QUEUE_EY = 4, + BATTLEGROUND_QUEUE_2v2 = 5, + BATTLEGROUND_QUEUE_3v3 = 6, + BATTLEGROUND_QUEUE_5v5 = 7, +}; + enum ScoreType { SCORE_KILLING_BLOWS = 1, @@ -194,6 +210,7 @@ public: /* Construction */ BattleGround(); + /*BattleGround(const BattleGround& bg);*/ virtual ~BattleGround(); virtual void Update(time_t diff); // must be implemented in BG subclass of BG specific update code, but must in begginning call parent version virtual bool SetupBattleGround() // must be implemented in BG subclass @@ -263,6 +280,7 @@ } bool HasFreeSlotsForTeam(uint32 Team) const; bool HasFreeSlots() const; + uint32 GetFreeSlotsForTeam(uint32 Team) const; bool isArena() const { return m_IsArena; } bool isBattleGround() const { return !m_IsArena; } @@ -328,6 +346,7 @@ uint8 GetTeamIndexByTeamId(uint32 Team) const { return Team == ALLIANCE ? BG_TEAM_ALLIANCE : BG_TEAM_HORDE; } uint32 GetPlayersCountByTeam(uint32 Team) const { return m_PlayersCount[GetTeamIndexByTeamId(Team)]; } + uint32 GetAlivePlayersCountByTeam(uint32 Team) const; // used in arenas to correctly handle death in spirit of redemption / last stand etc. (killer = killed) cases void UpdatePlayersCountByTeam(uint32 Team, bool remove) { if(remove) @@ -336,6 +355,12 @@ m_PlayersCount[GetTeamIndexByTeamId(Team)]++; } + // used for rated arena battles + void SetArenaTeamIdForTeam(uint32 Team, uint32 ArenaTeamId) { m_ArenaTeamIds[ Team == ALLIANCE ? 0 : 1] = ArenaTeamId; } + uint32 GetArenaTeamIdForTeam(uint32 Team) const { return m_ArenaTeamIds[ Team == ALLIANCE ? 0 : 1]; } + void SetArenaTeamRatingChangeForTeam(uint32 Team, int32 ArenaTeamId) { m_ArenaTeamRatingChanges[ Team == ALLIANCE ? 0 : 1] = ArenaTeamId; } + uint32 GetArenaTeamRatingChangeForTeam(uint32 Team) const { return m_ArenaTeamRatingChanges[ Team == ALLIANCE ? 0 : 1]; } + /* Triggers handle */ // must be implemented in BG subclass virtual void HandleAreaTrigger(Player* /*Source*/, uint32 /*Trigger*/) {} @@ -344,18 +369,19 @@ // must be implemented in BG subclass if need virtual void HandleDropFlag(Player* /*player*/) {} - virtual void AddPlayer(Player *plr); // must be implemented in BG subclass + virtual void AddPlayer(Player *plr, uint32 team = 0); // must be implemented in BG subclass virtual void RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket); // can be extended in in BG subclass // TODO: make this protected: - typedef std::vector BGObjects; - typedef std::vector BGCreatures; + typedef std::vector BGObjects; + typedef std::vector BGCreatures; BGObjects m_BgObjects; BGCreatures m_BgCreatures; void SpawnBGObject(uint32 type, uint32 respawntime); + void SpawnBGCreature(uint32 type, uint32 respawntime); bool AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawntime = 0); - Creature* AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o); + Creature* AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o, uint32 respawntime = 0); bool DelCreature(uint32 type); bool DelObject(uint32 type); bool AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team); @@ -363,6 +389,11 @@ void DoorOpen(uint32 type); const char *GetMangosString(uint32 entry); + // since arenas can be AvA or Hvh, we have to get the "temporary" team of a player + uint32 GetPlayerTeam(uint64 guid); + + void SetDeleteThis() {m_SetDeleteThis = true;} + protected: //this method is called, when BG cannot spawn its own spirit guide, or something is wrong, It correctly ends BattleGround void EndNow(); @@ -390,6 +421,8 @@ uint32 m_LastResurrectTime; uint32 m_Queue_type; uint8 m_ArenaType; // 2=2v2, 3=3v3, 5=5v5 + bool m_InBGFreeSlotQueue; // used to make sure that BG is only once inserted into the BattleGroundMgr.BGFreeSlotQueue[bgTypeId] deque + bool m_SetDeleteThis; // used for safe deletion of the bg after end / all players leave // this variable is not used .... it can be found in many other ways... but to store it in BG object instance is useless //uint8 m_BattleGroundType; // 3=BG, 4=arena //instead of uint8 (in previous line) is bool used @@ -415,6 +448,11 @@ /* Players count by team */ uint32 m_PlayersCount[2]; + /* Arena team ids by team */ + uint32 m_ArenaTeamIds[2]; + + int32 m_ArenaTeamRatingChanges[2]; + /* Limits */ uint32 m_LevelMin; uint32 m_LevelMax; Index: src/game/BattleGroundAA.cpp =================================================================== --- src/game/BattleGroundAA.cpp (revision 5306) +++ src/game/BattleGroundAA.cpp (working copy) @@ -35,9 +35,9 @@ BattleGround::Update(diff); } -void BattleGroundAA::AddPlayer(Player *plr) +void BattleGroundAA::AddPlayer(Player *plr, uint32 team) { - BattleGround::AddPlayer(plr); + BattleGround::AddPlayer(plr,team); //create score and add it to map, default values are set in constructor BattleGroundAAScore* sc = new BattleGroundAAScore; Index: src/game/BattleGroundAA.h =================================================================== --- src/game/BattleGroundAA.h (revision 5306) +++ src/game/BattleGroundAA.h (working copy) @@ -38,7 +38,7 @@ void Update(time_t diff); /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); + virtual void AddPlayer(Player *plr, uint32 team = 0); void RemovePlayer(Player *plr, uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); bool SetupBattleGround(); Index: src/game/BattleGroundAB.cpp =================================================================== --- src/game/BattleGroundAB.cpp (revision 5306) +++ src/game/BattleGroundAB.cpp (working copy) @@ -36,6 +36,33 @@ BattleGroundAB::~BattleGroundAB() { + // despawn objects / creatures here too, maybe the BG ends when the last not-ported invitee leaves the queue + sLog.outDebug("Arathi Basin: BG ended, objects despawning..."); + + // delete everything (for times, when BG will instanced) + for(uint32 i = 0; i < BG_AB_OBJECT_MAX; i++) + if (m_bgobjects[i]) + { + m_bgobjects[i]->SetRespawnTime(0); + m_bgobjects[i]->Delete(); + m_bgobjects[i] = NULL; + } + + for(uint32 node = 0; node < BG_AB_NODES_COUNT; node++) + if (m_bgbuffobjects[node].object) + { + m_bgbuffobjects[node].object->SetRespawnTime(0); + m_bgbuffobjects[node].object->Delete(); + m_bgbuffobjects[node].object = NULL; + } + + for(uint32 node = 0; node < 7; node++) + if (m_spiritGuides[node]) + { + m_spiritGuides[node]->CleanupsBeforeDelete(); + ObjectAccessor::Instance().AddObjectToRemoveList(m_spiritGuides[node]); + m_spiritGuides[node] = NULL; + } } void BattleGroundAB::Update(time_t diff) @@ -205,6 +232,7 @@ { uint8 buff = urand(0, 2); m_bgbuffobjects[node].object = new GameObject(NULL); + m_bgbuffobjects[node].object->SetInstanceId(GetInstanceID()); if (!m_bgbuffobjects[node].object->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), BG_AB_BuffObjects[buff][0], GetMapId(), BG_AB_BuffPositions[node][0], BG_AB_BuffPositions[node][1], BG_AB_BuffPositions[node][2], BG_AB_BuffPositions[node][3], 0, 0, sin(BG_AB_BuffPositions[node][3]/2), cos(BG_AB_BuffPositions[node][3]/2), 0, 0)) ASSERT(0); m_bgbuffobjects[node].spellId = BG_AB_BuffObjects[buff][1]; @@ -226,6 +254,7 @@ if (spell) plr->CastSpell(plr, spell, true, 0); m_bgbuffobjects[node].timer = BG_AB_BUFF_RESPAWN_TIME; + m_bgbuffobjects[node].object->SetRespawnTime(0); m_bgbuffobjects[node].object->Delete(); m_bgbuffobjects[node].object = NULL; break; @@ -237,9 +266,9 @@ } } -void BattleGroundAB::AddPlayer(Player *plr) +void BattleGroundAB::AddPlayer(Player *plr, uint32 team) { - BattleGround::AddPlayer(plr); + BattleGround::AddPlayer(plr, team); //create score and add it to map, default values are set in the constructor BattleGroundABScore* sc = new BattleGroundABScore; @@ -249,7 +278,7 @@ void BattleGroundAB::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) { - if(GetPlayersSize()==0) + if(GetPlayersSize()==0 && GetInvitedCount(ALLIANCE)==0 && GetInvitedCount(HORDE)==0) { sLog.outDebug("Arathi Basin: BG ended, objects despawning..."); @@ -257,6 +286,7 @@ for(uint32 i = 0; i < BG_AB_OBJECT_MAX; i++) if (m_bgobjects[i]) { + m_bgobjects[i]->SetRespawnTime(0); m_bgobjects[i]->Delete(); m_bgobjects[i] = NULL; } @@ -264,6 +294,7 @@ for(uint32 node = 0; node < BG_AB_NODES_COUNT; node++) if (m_bgbuffobjects[node].object) { + m_bgbuffobjects[node].object->SetRespawnTime(0); m_bgbuffobjects[node].object->Delete(); m_bgbuffobjects[node].object = NULL; } @@ -276,9 +307,10 @@ m_spiritGuides[node] = NULL; } + // no use, the bg will be deleted soonish // re-setup - Reset(); - SetupBattleGround(); + // Reset(); + // SetupBattleGround(); // Only change back to STATUS_WAIT_QUEUE if successfully re-setuped } } @@ -342,6 +374,7 @@ else goId = BG_AB_OBJECTID_BANNER_H; m_bgobjects[obj] = new GameObject(NULL); + m_bgobjects[obj]->SetInstanceId(GetInstanceID()); m_bgobjects[obj]->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), goId, GetMapId(), BG_AB_NodePositions[node][0], BG_AB_NodePositions[node][1], BG_AB_NodePositions[node][2], BG_AB_NodePositions[node][3], 0, 0, sin(BG_AB_NodePositions[node][3]/2), cos(BG_AB_NodePositions[node][3]/2), 100, 1); MapManager::Instance().GetMap(GetMapId(), m_bgobjects[obj])->Add(m_bgobjects[obj]); @@ -349,7 +382,8 @@ if (!type) return; obj = node*8 + ((type == 3) ? (5 + teamIndex) : 7); - MapManager::Instance().GetMap(GetMapId(), m_bgobjects[obj])->Add(m_bgobjects[obj]); + if(m_bgobjects[obj]) + MapManager::Instance().GetMap(GetMapId(), m_bgobjects[obj])->Add(m_bgobjects[obj]); } void BattleGroundAB::_DelBanner(uint8 node, uint8 type, uint8 teamIndex) @@ -357,13 +391,16 @@ uint8 obj = node*8 + type + teamIndex; if (!m_bgobjects[obj]) return; + + m_bgobjects[obj]->SetRespawnTime(0); m_bgobjects[obj]->Delete(); m_bgobjects[obj] = NULL; // handle aura with banner if (!type) return; obj = node*8 + ((type == 3) ? (5 + teamIndex) : 7); - MapManager::Instance().GetMap(GetMapId(), m_bgobjects[obj])->Remove(m_bgobjects[obj], false); + if(m_bgobjects[obj]) + MapManager::Instance().GetMap(GetMapId(), m_bgobjects[obj])->Remove(m_bgobjects[obj], false); } const char* BattleGroundAB::_GetNodeName(uint8 node) @@ -463,6 +500,7 @@ if (m_spiritGuides[node]) return; m_spiritGuides[node] = new Creature(NULL); + m_spiritGuides[node]->SetInstanceId(GetInstanceID()); if (!m_spiritGuides[node]->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), 529, BG_AB_SpiritGuidePos[node][0], BG_AB_SpiritGuidePos[node][1], BG_AB_SpiritGuidePos[node][2], BG_AB_SpiritGuidePos[node][3], (node>4)?BG_AB_SpiritGuideId[node-5]:BG_AB_SpiritGuideId[m_Nodes[node]-3], (node>4)?BG_AB_SpiritGuideTeamId[node-5]:BG_AB_SpiritGuideTeamId[m_Nodes[node]-3])) ASSERT(0); m_spiritGuides[node]->setDeathState(DEAD); @@ -506,6 +544,7 @@ // despawn buff object if ((node < BG_AB_NODES_COUNT) && (m_bgbuffobjects[node].object)) { + m_bgbuffobjects[node].object->SetRespawnTime(0); m_bgbuffobjects[node].object->Delete(); m_bgbuffobjects[node].object = NULL; } @@ -626,6 +665,7 @@ for (int banner = BG_AB_OBJECT_BANNER_NEUTRAL, i = 0; i < 5; banner += 8, ++i) { m_bgobjects[banner] = new GameObject(NULL); + m_bgobjects[banner]->SetInstanceId(GetInstanceID()); if(!m_bgobjects[banner]->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), BG_AB_OBJECTID_NODE_BANNER_0+i, GetMapId(), BG_AB_NodePositions[i][0], BG_AB_NodePositions[i][1], BG_AB_NodePositions[i][2], BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2), 100, 1)) return false; } @@ -634,27 +674,32 @@ for (int banner = BG_AB_OBJECT_AURA_ALLY, i = 0; i < 5; banner += 8, ++i) { m_bgobjects[banner] = new GameObject(NULL); + m_bgobjects[banner]->SetInstanceId(GetInstanceID()); if(!m_bgobjects[banner]->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), BG_AB_OBJECTID_AURA_A, GetMapId(), BG_AB_NodePositions[i][0], BG_AB_NodePositions[i][1], BG_AB_NodePositions[i][2], BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2), 0, 0)) return false; } for (int banner = BG_AB_OBJECT_AURA_HORDE, i = 0; i < 5; banner += 8, ++i) { m_bgobjects[banner] = new GameObject(NULL); + m_bgobjects[banner]->SetInstanceId(GetInstanceID()); if(!m_bgobjects[banner]->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), BG_AB_OBJECTID_AURA_H, GetMapId(), BG_AB_NodePositions[i][0], BG_AB_NodePositions[i][1], BG_AB_NodePositions[i][2], BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2), 0, 0)) return false; } for (int banner = BG_AB_OBJECT_AURA_CONTESTED, i = 0; i < 5; banner += 8, ++i) { m_bgobjects[banner] = new GameObject(NULL); + m_bgobjects[banner]->SetInstanceId(GetInstanceID()); if(!m_bgobjects[banner]->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), BG_AB_OBJECTID_AURA_C, GetMapId(), BG_AB_NodePositions[i][0], BG_AB_NodePositions[i][1], BG_AB_NodePositions[i][2], BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2), 0, 0)) return false; } // Starting gates m_bgobjects[BG_AB_OBJECT_GATE_A] = new GameObject(NULL); + m_bgobjects[BG_AB_OBJECT_GATE_A]->SetInstanceId(GetInstanceID()); if(!m_bgobjects[BG_AB_OBJECT_GATE_A]->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), BG_AB_OBJECTID_GATE_A, GetMapId(), BG_AB_DoorPositions[0][0], BG_AB_DoorPositions[0][1], BG_AB_DoorPositions[0][2], BG_AB_DoorPositions[0][3], BG_AB_DoorPositions[0][4], BG_AB_DoorPositions[0][5], BG_AB_DoorPositions[0][6], BG_AB_DoorPositions[0][7], 100, 1)) return false; m_bgobjects[BG_AB_OBJECT_GATE_H] = new GameObject(NULL); + m_bgobjects[BG_AB_OBJECT_GATE_H]->SetInstanceId(GetInstanceID()); if(!m_bgobjects[BG_AB_OBJECT_GATE_H]->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), BG_AB_OBJECTID_GATE_H, GetMapId(), BG_AB_DoorPositions[1][0], BG_AB_DoorPositions[1][1], BG_AB_DoorPositions[1][2], BG_AB_DoorPositions[1][3], BG_AB_DoorPositions[1][4], BG_AB_DoorPositions[1][5], BG_AB_DoorPositions[1][6], BG_AB_DoorPositions[1][7], 100, 1)) return false; @@ -757,3 +802,7 @@ + + + + Index: src/game/BattleGroundAB.h =================================================================== --- src/game/BattleGroundAB.h (revision 5306) +++ src/game/BattleGroundAB.h (working copy) @@ -222,7 +222,7 @@ ~BattleGroundAB(); void Update(time_t diff); - void AddPlayer(Player *plr); + void AddPlayer(Player *plr, uint32 team = 0); void RemovePlayer(Player *plr,uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); virtual bool SetupBattleGround(); Index: src/game/BattleGroundAV.cpp =================================================================== --- src/game/BattleGroundAV.cpp (revision 5306) +++ src/game/BattleGroundAV.cpp (working copy) @@ -39,9 +39,9 @@ BattleGround::Update(diff); } -void BattleGroundAV::AddPlayer(Player *plr) +void BattleGroundAV::AddPlayer(Player *plr, uint32 team) { - BattleGround::AddPlayer(plr); + BattleGround::AddPlayer(plr, team); //create score and add it to map, default values are set in constructor BattleGroundAVScore* sc = new BattleGroundAVScore; Index: src/game/BattleGroundAV.h =================================================================== --- src/game/BattleGroundAV.h (revision 5306) +++ src/game/BattleGroundAV.h (working copy) @@ -45,7 +45,7 @@ void Update(time_t diff); /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); + virtual void AddPlayer(Player *plr, uint32 team = 0); void RemovePlayer(Player *plr,uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); Index: src/game/BattleGroundBE.cpp =================================================================== --- src/game/BattleGroundBE.cpp (revision 5306) +++ src/game/BattleGroundBE.cpp (working copy) @@ -89,6 +89,11 @@ if(plr) plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); } + + if(!GetPlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetPlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } } @@ -98,9 +103,9 @@ }*/ } -void BattleGroundBE::AddPlayer(Player *plr) +void BattleGroundBE::AddPlayer(Player *plr, uint32 team) { - BattleGround::AddPlayer(plr); + BattleGround::AddPlayer(plr, team); //create score and add it to map, default values are set in constructor BattleGroundBEScore* sc = new BattleGroundBEScore; @@ -109,7 +114,13 @@ void BattleGroundBE::RemovePlayer(Player *plr, uint64 guid) { + if(GetStatus() == STATUS_WAIT_LEAVE) + return; + if(!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } void BattleGroundBE::HandleKillPlayer(Player *player, Player *killer) @@ -123,15 +134,16 @@ return; } - uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam()); - - m_TeamKills[killer_team_index]++; // add kills to killer's team - - if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(player->GetTeam())) + if(!GetAlivePlayersCountByTeam(ALLIANCE)) { // all opponents killed - EndBattleGround(killer->GetTeam()); + EndBattleGround(HORDE); } + else if(!GetAlivePlayersCountByTeam(HORDE)) + { + // all opponents killed + EndBattleGround(ALLIANCE); + } } void BattleGroundBE::HandleAreaTrigger(Player *Source, uint32 Trigger) @@ -141,14 +153,14 @@ return; uint32 SpellId = 0; - uint64 buff_guid = 0; + GameObject* obj = 0; switch(Trigger) { case 4538: // buff trigger? - buff_guid = m_BgObjects[BG_BE_OBJECT_BUFF_1]; + obj = m_BgObjects[BG_BE_OBJECT_BUFF_1]; break; case 4539: // buff trigger? - buff_guid = m_BgObjects[BG_BE_OBJECT_BUFF_2]; + obj = m_BgObjects[BG_BE_OBJECT_BUFF_2]; break; default: sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); @@ -156,11 +168,8 @@ break; } - if(buff_guid) + if(obj) { - GameObject *obj = HashMapHolder::Find(buff_guid); - if(obj) - { if(!obj->isSpawned()) return; // buff not spawned yet obj->SetRespawnTime(BUFF_RESPAWN_TIME); @@ -168,14 +177,12 @@ SpellId = obj->GetGOInfo()->data3; if(SpellId) Source->CastSpell(Source, SpellId, true); - } } } void BattleGroundBE::ResetBGSubclass() { - m_TeamKills[BG_TEAM_ALLIANCE] = 0; - m_TeamKills[BG_TEAM_HORDE] = 0; + } bool BattleGroundBE::SetupBattleGround() Index: src/game/BattleGroundBE.h =================================================================== --- src/game/BattleGroundBE.h (revision 5306) +++ src/game/BattleGroundBE.h (working copy) @@ -58,7 +58,7 @@ void Update(time_t diff); /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); + virtual void AddPlayer(Player *plr, uint32 team = 0); void RemovePlayer(Player *plr, uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); @@ -68,8 +68,5 @@ /* Scorekeeping */ void UpdatePlayerScore(Player *Source, uint32 type, uint32 value); - - private: - uint32 m_TeamKills[2]; // count of kills for each team }; #endif Index: src/game/BattleGroundEY.cpp =================================================================== --- src/game/BattleGroundEY.cpp (revision 5306) +++ src/game/BattleGroundEY.cpp (working copy) @@ -39,9 +39,9 @@ BattleGround::Update(diff); } -void BattleGroundEY::AddPlayer(Player *plr) +void BattleGroundEY::AddPlayer(Player *plr, uint32 team) { - BattleGround::AddPlayer(plr); + BattleGround::AddPlayer(plr, team); //create score and add it to map BattleGroundEYScore* sc = new BattleGroundEYScore; Index: src/game/BattleGroundEY.h =================================================================== --- src/game/BattleGroundEY.h (revision 5306) +++ src/game/BattleGroundEY.h (working copy) @@ -39,7 +39,7 @@ void Update(time_t diff); /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); + virtual void AddPlayer(Player *plr, uint32 team = 0); void RemovePlayer(Player *plr,uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); Index: src/game/BattleGroundHandler.cpp =================================================================== --- src/game/BattleGroundHandler.cpp (revision 5306) +++ src/game/BattleGroundHandler.cpp (working copy) @@ -26,9 +26,12 @@ #include "MapManager.h" #include "ObjectAccessor.h" #include "Object.h" +#include "Chat.h" +#include "Language.h" #include "BattleGroundMgr.h" #include "BattleGroundWS.h" #include "BattleGround.h" +#include "ArenaTeam.h" void WorldSession::HandleBattleGroundHelloOpcode( WorldPacket & recv_data ) { @@ -78,8 +81,17 @@ recv_data >> instanceId; // instance id, 0 if First Available selected recv_data >> joinAsGroup; // join as group + if(bgTypeId >= MAX_BATTLEGROUND_TYPES) + { + sLog.outError("bgport: invalid bgtype received. possible cheater? player guid %u",_player->GetGUIDLow()); + return; + } + sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from: " I64FMT, guid); + // can do this, since it's battleground, not arena + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId, 0); + // ignore if we already in BG or BG queue if(_player->InBattleGround()) return; @@ -100,19 +112,39 @@ return; } - // check existence - BattleGround *bg = sBattleGroundMgr.GetBattleGround(bgTypeId); - if(!bg) + // get bg instance or bg template if instance not found + BattleGround * bg = 0; + if(instanceId) + BattleGround *bg = sBattleGroundMgr.GetBattleGround(instanceId); + + if(!bg && !(bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId))) + { + sLog.outError("Battleground: no available bg / template found"); return; + } if(joinAsGroup && _player->GetGroup()) { Group *grp = _player->GetGroup(); + if(grp->GetMembersCount() > bg->GetMaxPlayersPerTeam()) + { + WorldPacket data; + ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, objmgr.GetMangosString(LANG_BG_GROUP_TOO_LARGE), NULL); + SendPacket(&data); + return; + } + GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, 0, false, 0); for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) { Player *member = itr->getSource(); if(!member) continue; + if( member->GetBattleGroundQueueIdFromLevel() != _player->GetBattleGroundQueueIdFromLevel() ) + continue; + + if( member->GetTeam() != _player->GetTeam() ) + continue; + if( !member->CanJoinToBattleground() ) { WorldPacket data(SMSG_GROUP_JOINED_BATTLEGROUND, 4); @@ -120,12 +152,12 @@ _player->GetSession()->SendPacket(&data); continue; } - if (member->GetBattleGroundQueueIndex(bgTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) + if (member->GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) //player is already in this queue continue; WorldPacket data; - uint32 queueSlot = member->AddBattleGroundQueueId(bgTypeId); // add to queue + uint32 queueSlot = member->AddBattleGroundQueueId(bgQueueTypeId); // add to queue if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES) { // fill data packet @@ -134,22 +166,23 @@ } // store entry point coords (same as leader entry point) - member->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); + member->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetInstanceId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); // send status packet (in queue) sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, member->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0); member->GetSession()->SendPacket(&data); sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, bgTypeId); member->GetSession()->SendPacket(&data); - sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].AddPlayer(member, bgTypeId); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(member, ginfo); } + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel()); } else { - if (_player->GetBattleGroundQueueIndex(bgTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) + if (_player->GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) //player is already in this queue return; - uint32 queueSlot = _player->AddBattleGroundQueueId(bgTypeId); + uint32 queueSlot = _player->AddBattleGroundQueueId(bgQueueTypeId); if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES) { WorldPacket data; @@ -159,13 +192,16 @@ } // store entry point coords - _player->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); + _player->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetInstanceId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); WorldPacket data; // send status packet (in queue) sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0); SendPacket(&data); - sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].AddPlayer(_player, bgTypeId); + + GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, 0, false, 0); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(_player, ginfo); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel()); } } @@ -247,6 +283,12 @@ uint32 bgTypeId; recv_data >> bgTypeId; // id from DBC + if(bgTypeId >= MAX_BATTLEGROUND_TYPES) + { + sLog.outError("Battleground: invalid bgtype received."); + return; + } + BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId); if(!bl) @@ -263,39 +305,151 @@ sLog.outDebug( "WORLD: Recvd CMSG_BATTLEFIELD_PORT Message"); - uint8 unk1; + uint8 type; // arenatype if arena uint8 unk2; // unk, can be 0x0 (may be if was invited?) and 0x1 + uint32 instanceId; uint32 bgTypeId; // type id from dbc uint16 unk; // 0x1F90 constant? uint8 action; // enter battle 0x1, leave queue 0x0 - recv_data >> unk1 >> unk2 >> bgTypeId >> unk >> action; + recv_data >> type >> unk2 >> bgTypeId >> unk >> action; - BattleGround *bg = sBattleGroundMgr.GetBattleGround(bgTypeId); + if(bgTypeId >= MAX_BATTLEGROUND_TYPES) + { + sLog.outError("Battleground: invalid bgtype received."); + // update battleground slots for the player to fix his UI and sent data. + // this is a HACK, I don't know why the client starts sending invalid packets in the first place. + // it usually happens with extremely high latency (if debugging / stepping in the code for example) + if(_player->InBattleGroundQueue()) + { + // update all queues, send invitation info if player is invited, queue info if queued + for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + { + uint32 queue_id = _player->GetBattleGroundQueueId(i); + if(!queue_id) + continue; + BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); + // if the player is not in queue, contine + if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) + continue; + + // no group information, this should never happen + if(!itrPlayerStatus->second.GroupInfo) + continue; + + BattleGround * bg = NULL; + + // get possibly needed data from groupinfo + bgTypeId = itrPlayerStatus->second.GroupInfo->BgTypeId; + uint8 arenatype = itrPlayerStatus->second.GroupInfo->ArenaType; + uint8 israted = itrPlayerStatus->second.GroupInfo->IsRated; + uint8 status = 0; + + + if(!itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID) + { + // not invited to bg, get template + bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); + status = STATUS_WAIT_QUEUE; + } + else + { + // get the bg we're invited to + BattleGround * bg = sBattleGroundMgr.GetBattleGround(itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID); + status = STATUS_WAIT_JOIN; + } + + // if bg not found, then continue + if(!bg) + continue; + + // don't invite if already in the instance + if(_player->InBattleGround() && _player->GetBattleGround() && _player->GetBattleGround()->GetInstanceID() == bg->GetInstanceID()) + continue; + + // re - invite player with proper data + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, itrPlayerStatus->second.GroupInfo->Team?itrPlayerStatus->second.GroupInfo->Team:_player->GetTeam(), i, status, INVITE_ACCEPT_WAIT_TIME, 0, arenatype, israted); + SendPacket(&data); + } + } + return; + } + + uint32 bgQueueTypeId = 0; + // get the bg what we were invited to + BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus; + bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId,type); + itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); + + if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) + { + sLog.outError("Battleground: itrplayerstatus not found."); + return; + } + instanceId = itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID; + + // if action == 1, then instanceId is _required_ + if(!instanceId && action == 1) + { + sLog.outError("Battleground: instance not found."); + return; + } + + BattleGround *bg = sBattleGroundMgr.GetBattleGround(instanceId); + + // bg template might and must be used in case of leaving queue, when instance is not created yet + if(!bg && action == 0) + bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); + + if(!bg) + { + sLog.outError("Battleground: bg not found."); + return; + } + + bgTypeId = bg->GetTypeID(); if(bg) { if(_player->InBattleGroundQueue()) { uint32 queueSlot = 0; + uint32 team = 0; + uint32 arenatype = 0; + uint32 israted = 0; + // get the team info from the queue + BattleGroundQueue::QueuedPlayersMap::iterator pitr = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); + if(pitr !=sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end() + && pitr->second.GroupInfo ) + { + team = pitr->second.GroupInfo->Team; + arenatype = pitr->second.GroupInfo->ArenaType; + israted = pitr->second.GroupInfo->IsRated; + } + else + { + sLog.outError("Battleground: Invalid player queue info!"); + return; + } WorldPacket data; switch(action) { case 1: // port to battleground _player->RemoveFromGroup(); - queueSlot = _player->GetBattleGroundQueueIndex(bgTypeId); + queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime()); _player->GetSession()->SendPacket(&data); // remove battleground queue status from BGmgr - sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].RemovePlayer(_player->GetGUID(), false); - // check if player is not deserter - if( !_player->CanJoinToBattleground() ) + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(_player->GetGUID(), false); + // check if player is not deserter -- check in signup, not here! + /*if( !_player->CanJoinToBattleground() ) { WorldPacket data2; data2.Initialize(SMSG_GROUP_JOINED_BATTLEGROUND, 4); data2 << (uint32) 0xFFFFFFFE; SendPacket(&data2); return; - } + }*/ // if player is in battleground already, remove him and port him to new battleground if ( _player->InBattleGround() ) { @@ -303,15 +457,15 @@ if (currentBg) currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true); } - _player->SetBattleGroundId(bg->GetTypeID()); - sBattleGroundMgr.SendToBattleGround(_player, bgTypeId); - bg->AddPlayer(_player); + _player->SetBattleGroundId(bg->GetInstanceID()); + sBattleGroundMgr.SendToBattleGround(_player, instanceId, team); + bg->AddPlayer(_player,team); break; case 0: // leave queue - queueSlot = _player->GetBattleGroundQueueIndex(bgTypeId); - _player->RemoveBattleGroundQueueId(bgTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs + queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); + _player->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_NONE, 0, 0); - sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].RemovePlayer(_player->GetGUID(), true); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(_player->GetGUID(), true); SendPacket(&data); break; default: @@ -350,7 +504,8 @@ BattleGround *bg = _player->GetBattleGround(); if(bg) { - uint32 queueSlot = _player->GetBattleGroundQueueIndex(bg->GetTypeID()); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + uint32 queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); if((bg->GetStatus() <= STATUS_IN_PROGRESS)) { sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime()); @@ -358,15 +513,25 @@ } for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - uint32 queue_id = _player->GetBattleGroundQueueId(i); - if (i == queueSlot || !queue_id) + uint32 queue_id = _player->GetBattleGroundQueueId(i); // battlegroundqueueid stores the type id, not the instance id, so this is definitely wrong + uint8 arenatype = sBattleGroundMgr.BGArenaType(queue_id); + uint8 isRated = 0; + if (i == queueSlot || !queue_id) // we need to get the instance ids continue; - BattleGround *bg2 = sBattleGroundMgr.GetBattleGround(queue_id); + BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); + if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) + continue; + if(itrPlayerStatus->second.GroupInfo) + { + arenatype = itrPlayerStatus->second.GroupInfo->ArenaType; + isRated = itrPlayerStatus->second.GroupInfo->IsRated; + } + BattleGround *bg2 = sBattleGroundMgr.GetBattleGroundTemplate(sBattleGroundMgr.BGTemplateId(queue_id)); // try this if(bg2) { //in this call is small bug, this call should be filled by player's waiting time in queue //this call nulls all timers for client : - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg2, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg2, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0,arenatype,isRated); SendPacket(&data); } } @@ -378,18 +543,34 @@ for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { uint32 queue_id = _player->GetBattleGroundQueueId(i); - BattleGround *bg = sBattleGroundMgr.GetBattleGround(queue_id); + if(!queue_id) + continue; + uint32 bgTypeId = sBattleGroundMgr.BGTemplateId(queue_id); + uint8 arenatype = sBattleGroundMgr.BGArenaType(queue_id); + uint8 isRated = 0; + BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); + BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); + if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) + continue; + if(itrPlayerStatus->second.GroupInfo) + { + arenatype = itrPlayerStatus->second.GroupInfo->ArenaType; + isRated = itrPlayerStatus->second.GroupInfo->IsRated; + } if(bg && queue_id) { - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0, arenatype, isRated); SendPacket(&data); } } } - /*else // not sure if it needed... +/* else // not sure if it needed... { - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, NULL, _player->GetTeam(), STATUS_NONE, 0, 0); - SendPacket(&data); + for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + { + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, NULL, _player->GetTeam(),i , STATUS_NONE, 0, 0); + SendPacket(&data); + } }*/ } @@ -453,7 +634,7 @@ recv_data.hexlike(); // ignore if we already in BG or BG queue - if(_player->InBattleGround() || _player->InBattleGroundQueue()) + if(_player->InBattleGround()) return; uint64 guid; // arena Battlemaster guid @@ -470,6 +651,7 @@ return; uint8 arenatype = 0; + uint32 arenaRating = 0; switch(type) { @@ -487,23 +669,72 @@ break; } - if(isRated && !_player->GetArenaTeamId(type)) // player not in arena team of that size + if(isRated) // player not in arena team of that size { - _player->GetSession()->SendNotInArenaTeamPacket(arenatype); - return; + uint32 ateamId = _player->GetArenaTeamId(type); + if(!ateamId) + { + _player->GetSession()->SendNotInArenaTeamPacket(arenatype); + return; + } + ArenaTeam * at = objmgr.GetArenaTeamById(ateamId); + if(!at) + { + _player->GetSession()->SendNotInArenaTeamPacket(arenatype); + return; + } + arenaRating = at->GetRating(); + Group *grp = _player->GetGroup(); + if(grp) + { + for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *member = itr->getSource(); + + // offline member or not in team -> err + if(!member || member->GetArenaTeamId(type) != ateamId) + { + WorldPacket data; + ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, objmgr.GetMangosString(LANG_ARENA_YOUR_TEAM_ONLY), NULL); + SendPacket(&data); + return; + } + } + if(grp->GetMembersCount() < arenatype) + { + WorldPacket data; + ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, objmgr.GetMangosString(LANG_ARENA_NOT_ENOUGH_PLAYERS), NULL); + SendPacket(&data); + return; + } + } } if(asGroup && !_player->GetGroup()) // player not in group return; - // check existence - BattleGround *bg = sBattleGroundMgr.GetBattleGround(BATTLEGROUND_AA); - if(!bg) + //check existance + BattleGround* bg = NULL; + if( !(bg = sBattleGroundMgr.GetBattleGroundTemplate(BATTLEGROUND_AA)) ) + { + sLog.outError("Battleground: template bg (all arenas) not found"); return; + } + uint8 bgTypeId = bg->GetTypeID(); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId, arenatype); + if(asGroup && _player->GetGroup()) { Group *grp = _player->GetGroup(); + if(grp->GetMembersCount() > arenatype) + { + WorldPacket data; + ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, objmgr.GetMangosString(LANG_ARENA_GROUP_TOO_LARGE), NULL); + SendPacket(&data); + return; + } + GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, arenatype, isRated, arenaRating); for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) { Player *member = itr->getSource(); @@ -513,11 +744,19 @@ //player has deserter aura .. do nothing */ - if (member->GetBattleGroundQueueIndex(BATTLEGROUND_AA) < PLAYER_MAX_BATTLEGROUND_QUEUES) + if( member->GetBattleGroundQueueIdFromLevel() != _player->GetBattleGroundQueueIdFromLevel() ) + continue; + + if( member->GetTeam() != _player->GetTeam() ) + continue; + + if (member->GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) + { //player is already in this queue continue; + } - uint32 queueSlot = member->AddBattleGroundQueueId(BATTLEGROUND_AA);// add to queue + uint32 queueSlot = member->AddBattleGroundQueueId(bgQueueTypeId);// add to queue if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES) { WorldPacket data; @@ -527,16 +766,17 @@ } // store entry point coords (same as leader entry point) - member->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); + member->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetInstanceId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); WorldPacket data; // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, member->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, member->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0, arenatype, isRated); member->GetSession()->SendPacket(&data); - sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, BATTLEGROUND_AA); + sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, bgTypeId); member->GetSession()->SendPacket(&data); - sBattleGroundMgr.m_BattleGroundQueues[BATTLEGROUND_AA].AddPlayer(member, BATTLEGROUND_AA); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(member, ginfo); } + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(), arenatype, isRated, arenaRating); } else { @@ -544,11 +784,13 @@ //player has deserter aura .. do nothing */ - if (_player->GetBattleGroundQueueIndex(BATTLEGROUND_AA) < PLAYER_MAX_BATTLEGROUND_QUEUES) + if (_player->GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) + { //player is already in this queue return; + } - uint32 queueSlot = _player->AddBattleGroundQueueId(BATTLEGROUND_AA); + uint32 queueSlot = _player->AddBattleGroundQueueId(bgQueueTypeId); if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES) { WorldPacket data; @@ -558,12 +800,14 @@ } // store entry point coords - _player->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); + _player->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetInstanceId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); WorldPacket data; // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0, arenatype, isRated); SendPacket(&data); - sBattleGroundMgr.m_BattleGroundQueues[BATTLEGROUND_AA].AddPlayer(_player, BATTLEGROUND_AA); + GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, arenatype, isRated, arenaRating); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(_player, ginfo); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(), arenatype, isRated, arenaRating); } } Index: src/game/BattleGroundMgr.cpp =================================================================== --- src/game/BattleGroundMgr.cpp (revision 5306) +++ src/game/BattleGroundMgr.cpp (working copy) @@ -30,9 +30,12 @@ #include "SharedDefines.h" #include "Policies/SingletonImp.h" #include "MapManager.h" +#include "Map.h" +#include "MapInstanced.h" #include "ObjectMgr.h" #include "ProgressBar.h" #include "World.h" +#include "ArenaTeam.h" INSTANTIATE_SINGLETON_1( BattleGroundMgr ); @@ -43,12 +46,12 @@ BattleGroundQueue::BattleGroundQueue() { //queues are empty, we don't have to call clear() - for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) +/* for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) { - m_QueuedPlayers[i].Horde = 0; - m_QueuedPlayers[i].Alliance = 0; + //m_QueuedPlayers[i].Horde = 0; + //m_QueuedPlayers[i].Alliance = 0; //m_QueuedPlayers[i].AverageTime = 0; - } + }*/ } BattleGroundQueue::~BattleGroundQueue() @@ -56,35 +59,187 @@ for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) { m_QueuedPlayers[i].clear(); + for(QueuedGroupsList::iterator itr = m_QueuedGroups[i].begin(); itr!= m_QueuedGroups[i].end(); ++itr) + { + delete (*itr); + } + m_QueuedGroups[i].clear(); } } -void BattleGroundQueue::AddPlayer(Player *plr, uint32 bgTypeId) +// initialize eligible groups from the given source matching the given specifications +void BattleGroundQueue::EligibleGroups::Init(BattleGroundQueue::QueuedGroupsList *source, uint32 BgTypeId, uint32 side, uint32 MaxPlayers, uint8 ArenaType, bool IsRated, uint32 MinRating, uint32 MaxRating, uint32 DisregardTime) { + // clear from prev initialization + clear(); + BattleGroundQueue::QueuedGroupsList::iterator itr, next; + // iterate through the source + for(itr = source->begin(); itr!= source->end(); itr = next) + { + next = itr; + ++next; + if( (*itr)->BgTypeId == BgTypeId && + (*itr)->ArenaType == ArenaType && + (*itr)->IsRated == IsRated && + (*itr)->IsInvitedToBGInstanceGUID == 0 && + (*itr)->Team == side && + (*itr)->Players.size() <= MaxPlayers && + ((*itr)->JoinTime <= DisregardTime // pass if disregard time is greater than join time + || (*itr)->ArenaTeamRating == 0 // pass if no rating info + || ((*itr)->ArenaTeamRating >= MinRating + && ((*itr)->ArenaTeamRating <= MaxRating))) ) // pass if matches the rating range + { + // the group matches the conditions + // insert it in order of groupsize, and join time + uint32 size = (*itr)->Players.size(); + uint32 jointime = (*itr)->JoinTime; + bool inserted = false; + + for(std::list::iterator elig_itr = begin(); elig_itr != end(); ++elig_itr) + { + // if the next one's size is smaller, then insert + // also insert if the next one's size is equal, but it joined the queue later + if( ((*elig_itr)->Players.size()Players.size() == size && (*elig_itr)->JoinTime > jointime) ) + { + insert(elig_itr,(*itr)); + inserted = true; + break; + } + } + // if not inserted -> this is the smallest group -> push_back + if(!inserted) + { + push_back((*itr)); + } + } + } +} + +// remove group from eligible groups +// used when building selection pools +void BattleGroundQueue::EligibleGroups::RemoveGroup(GroupQueueInfo * ginfo) +{ + for(std::list::iterator itr = begin(); itr != end(); ++itr) + { + if((*itr)==ginfo) + { + erase(itr); + return; + } + } +} + +// selection pool initialization, used to clean up from prev selection +void BattleGroundQueue::SelectionPool::Init() +{ + SelectedGroups.clear(); + MaxGroup = 0; + PlayerCount = 0; +} + +// get the maximal group from the selection pool +// used when building the pool, and have to remove the largest +GroupQueueInfo * BattleGroundQueue::SelectionPool::GetMaximalGroup() +{ + if(SelectedGroups.empty()) + { + sLog.outError("Getting max group when selection pool is empty, this should never happen."); + MaxGroup = NULL; + return 0; + } + // actually select the max group if it's not set + if(MaxGroup==0 && !SelectedGroups.empty()) + { + uint32 max_size = 0; + for(std::list::iterator itr = SelectedGroups.begin(); itr != SelectedGroups.end(); ++itr) + { + if(max_size<(*itr)->Players.size()) + { + MaxGroup =(*itr); + max_size = MaxGroup->Players.size(); + } + } + } + return MaxGroup; +} + +// remove group info from selection pool +// used when building selection pools and have to remove maximal group +void BattleGroundQueue::SelectionPool::RemoveGroup(GroupQueueInfo *ginfo) +{ + // uninitiate max group info if needed + if(MaxGroup == ginfo) + MaxGroup = 0; + // find what to remove + for(std::list::iterator itr = SelectedGroups.begin(); itr != SelectedGroups.end(); ++itr) + { + if((*itr)==ginfo) + { + SelectedGroups.erase(itr); + // decrease selected players count + PlayerCount -= ginfo->Players.size(); + return; + } + } +} + +// add group to selection +// used when building selection pools +void BattleGroundQueue::SelectionPool::AddGroup(GroupQueueInfo * ginfo) +{ + SelectedGroups.push_back(ginfo); + // increase selected players count + PlayerCount+=ginfo->Players.size(); + if(!MaxGroup || ginfo->Players.size() > MaxGroup->Players.size()) + { + // update max group info if needed + MaxGroup = ginfo; + } +} + +// add group to bg queue with the given leader and bg specifications +GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, uint32 BgTypeId, uint8 ArenaType, bool isRated, uint32 arenaRating) +{ + uint32 queue_id = leader->GetBattleGroundQueueIdFromLevel(); + + // create new ginfo + // cannot use the method like in addplayer, because that could modify an in-queue group's stats + // (e.g. leader leaving queue then joining as individual again) + GroupQueueInfo* ginfo = new GroupQueueInfo; + ginfo->BgTypeId = BgTypeId; + ginfo->ArenaType = ArenaType; + ginfo->IsRated = isRated; + ginfo->IsInvitedToBGInstanceGUID = 0; // maybe this should be modifiable by function arguments to enable selection of running instances? + ginfo->JoinTime = getMSTime(); + ginfo->Team = leader->GetTeam(); + + if(sBattleGroundMgr.GetMaxRatingDifference()) // if max difference is set, then store rating info for queue + ginfo->ArenaTeamRating = arenaRating; + else + ginfo->ArenaTeamRating = 0; // don't if it doesn't matter + + ginfo->Players.clear(); + + m_QueuedGroups[queue_id].push_back(ginfo); + + // return ginfo, because it is needed to add players to this group info + return ginfo; +} + +void BattleGroundQueue::AddPlayer(Player *plr, GroupQueueInfo *ginfo) +{ uint32 queue_id = plr->GetBattleGroundQueueIdFromLevel(); //if player isn't in queue, he is added, if already is, then values are overwritten, no memory leak PlayerQueueInfo& info = m_QueuedPlayers[queue_id][plr->GetGUID()]; info.InviteTime = 0; - info.IsInvitedToBGInstanceGUID = 0; info.LastInviteTime = 0; info.LastOnlineTime = getMSTime(); - info.Team = plr->GetTeam(); + info.GroupInfo = ginfo; - //add player to waiting order queue - m_PlayersSortedByWaitTime[queue_id].push_back(plr->GetGUID()); - - if(plr->GetTeam() == ALLIANCE) - m_QueuedPlayers[queue_id].Alliance++; - else - m_QueuedPlayers[queue_id].Horde++; - - /* temporary BG queue status message - std::ostringstream str; - str << "Battleground queue invited status: horde: " << m_QueuedPlayers[queue_id].Horde << ", aliance: " << m_QueuedPlayers[queue_id].Alliance; - plr->Say(str.str(), LANG_UNIVERSAL);*/ - - this->Update(bgTypeId, queue_id); + // add the pinfo to ginfo's list + ginfo->Players[plr->GetGUID()] = &info; } void BattleGroundQueue::RemovePlayer(uint64 guid, bool decreaseInvitedCount) @@ -93,11 +248,22 @@ uint32 queue_id = 0; QueuedPlayersMap::iterator itr; + GroupQueueInfo * group; + QueuedGroupsList::iterator group_itr; bool IsSet = false; - if(!plr) - { //player is offline, we need to find him somewhere in queues - /// there is something wrong if this code is run, because we have in queue only online players! - sLog.outError("Battleground: removing offline player from BG queue - this might not happen, but it should not cause crash"); + if(plr) + { + queue_id = plr->GetBattleGroundQueueIdFromLevel(); + + itr = m_QueuedPlayers[queue_id].find(guid); + if(itr != m_QueuedPlayers[queue_id].end()) + IsSet = true; + } + + if(!IsSet) + { + // either player is offline, or he levelled up to another queue category + // sLog.outError("Battleground: removing offline player from BG queue - this might not happen, but it should not cause crash"); for (uint32 i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) { itr = m_QueuedPlayers[i].find(guid); @@ -109,44 +275,228 @@ } } } - else - { //player is online, we have his level, so we can find exact queue from his level - queue_id = plr->GetBattleGroundQueueIdFromLevel(); - itr = m_QueuedPlayers[queue_id].find(guid); - IsSet = true; + + // couldn't find the player in bg queue, return + if(!IsSet) + { + sLog.outError("Battleground: couldn't find player to remove."); + return; } - //all variables are set, so remove player - //remove player from time queue - m_PlayersSortedByWaitTime[queue_id].remove(guid); + group = itr->second.GroupInfo; - if (IsSet && itr != m_QueuedPlayers[queue_id].end()) + for(group_itr=m_QueuedGroups[queue_id].begin(); group_itr != m_QueuedGroups[queue_id].end(); ++group_itr) { - if (!itr->second.IsInvitedToBGInstanceGUID) + if(group == (GroupQueueInfo*)(*group_itr)) + break; + } + + // variables are set (what about leveling up when in queue????) + // remove player from group + // if only player there, remove group + + // remove player queue info from group queue info + std::map::iterator pitr = group->Players.find(guid); + + if(pitr != group->Players.end()) + group->Players.erase(pitr); + + // check for iterator correctness + if (group_itr != m_QueuedGroups[queue_id].end() && itr != m_QueuedPlayers[queue_id].end()) + { + // used when player left the queue, NOT used when porting to bg + if (decreaseInvitedCount) { - if(itr->second.Team == ALLIANCE) - m_QueuedPlayers[queue_id].Alliance--; - else - m_QueuedPlayers[queue_id].Horde--; - } - else - { - if (decreaseInvitedCount) + // if invited to bg, and should decrease invited count, then do it + if(group->IsInvitedToBGInstanceGUID) { - BattleGround* bg = sBattleGroundMgr.GetBattleGround(itr->second.IsInvitedToBGInstanceGUID); + BattleGround* bg = sBattleGroundMgr.GetBattleGround(group->IsInvitedToBGInstanceGUID); if (bg) - bg->DecreaseInvitedCount(itr->second.Team); + bg->DecreaseInvitedCount(group->Team); + if (!bg->GetPlayersSize() && !bg->GetInvitedCount(ALLIANCE) && !bg->GetInvitedCount(HORDE)) + { + // no more players on battleground, set delete it + bg->SetDeleteThis(); + } } + // update the join queue, maybe now the player's group fits in a queue! + // not yet implemented (should store bgTypeId in group queue info?) } + // remove player queue info m_QueuedPlayers[queue_id].erase(itr); + // remove group queue info if needed + if(group->Players.empty()) + { + m_QueuedGroups[queue_id].erase(group_itr); + delete group; + } } } +bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side) +{ + // set side if needed + if(side) + ginfo->Team = side; + + if(!ginfo->IsInvitedToBGInstanceGUID) + { + // not yet invited + // set invitation + ginfo->IsInvitedToBGInstanceGUID = bg->GetInstanceID(); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + // loop through the players + for(std::map::iterator itr = ginfo->Players.begin(); itr != ginfo->Players.end(); ++itr) + { + // set status + itr->second->InviteTime = getMSTime(); + itr->second->LastInviteTime = getMSTime(); + + // get the player + Player* plr = objmgr.GetPlayer(itr->first); + // if offline, skip him + if(!plr) + continue; + + // invite the player + sBattleGroundMgr.InvitePlayer(plr, bg->GetInstanceID(),ginfo->Team); + + WorldPacket data; + + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); + + sLog.outDebug("Battleground: invite plr %u queueindex %u bgtype %u",plr->GetGUIDLow(),queueSlot,bg->GetTypeID()); + + // send status packet + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, side?side:plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0); + plr->GetSession()->SendPacket(&data); + } + return true; + } + + return false; +} + +// this function is responsible for the selection of queued groups when trying to create new battlegrounds +bool BattleGroundQueue::BuildSelectionPool(uint32 bgTypeId, uint32 queue_id, uint32 MinPlayers, uint32 MaxPlayers, SelectionPoolBuildMode mode, uint8 ArenaType, bool isRated, uint32 MinRating, uint32 MaxRating, uint32 DisregardTime) +{ + uint32 side; + switch(mode) + { + case NORMAL_ALLIANCE: + case ONESIDE_ALLIANCE_TEAM1: + case ONESIDE_ALLIANCE_TEAM2: + side = ALLIANCE; + break; + case NORMAL_HORDE: + case ONESIDE_HORDE_TEAM1: + case ONESIDE_HORDE_TEAM2: + side = HORDE; + break; + default: + //unknown mode, return false + sLog.outDebug("Battleground: unknown selection pool build mode, returning..."); + return false; + break; + } + + // inititate the groups eligible to create the bg + m_EligibleGroups.Init(&(m_QueuedGroups[queue_id]), bgTypeId, side, MaxPlayers, ArenaType, isRated, MinRating, MaxRating, DisregardTime); + // init the selected groups (clear) + m_SelectionPools[mode].Init(); + while(!(m_EligibleGroups.empty())) + { + sLog.outDebug("m_EligibleGroups is not empty, continue building selection pool"); + // in decreasing group size, add groups to join if they fit in the MaxPlayersPerTeam players + for(EligibleGroups::iterator itr= m_EligibleGroups.begin(); itr!=m_EligibleGroups.end(); ++itr) + { + // get the maximal not yet checked group + GroupQueueInfo * MaxGroup = (*itr); + // if it fits in the maxplayer size, add it + if( (m_SelectionPools[mode].GetPlayerCount() + MaxGroup->Players.size()) <= MaxPlayers ) + { + m_SelectionPools[mode].AddGroup(MaxGroup); + } + } + if(m_SelectionPools[mode].GetPlayerCount()>=MinPlayers) + { + // the selection pool is set, return + sLog.outDebug("pool build succeeded, return true"); + return true; + } + // if the selection pool's not set, then remove the group with the highest player count, and try again with the rest. + GroupQueueInfo * MaxGroup = m_SelectionPools[mode].GetMaximalGroup(); + m_EligibleGroups.RemoveGroup(MaxGroup); + m_SelectionPools[mode].RemoveGroup(MaxGroup); + } + // failed to build a selection pool matching the given values + return false; +} + +// used to remove the Enter Battle window if the battle has already, but someone still has it +// (this can happen in arenas mainly, since the preparation is shorter than the timer for the bgqueueremove event +void BattleGroundQueue::BGEndedRemoveInvites(BattleGround *bg) +{ + uint32 queue_id = bg->GetQueueType(); + uint32 bgInstanceId = bg->GetInstanceID(); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + QueuedGroupsList::iterator itr, next; + for(itr = m_QueuedGroups[queue_id].begin(); itr != m_QueuedGroups[queue_id].end(); itr = next) + { + // must do this way, because the groupinfo will be deleted when all playerinfos are removed + GroupQueueInfo * ginfo = (*itr); + next = itr; + ++next; + // if group was invited to this bg instance, then remove all references + if(ginfo->IsInvitedToBGInstanceGUID == bgInstanceId) + { + // after removing this much playerinfos, the ginfo will be deleted, so we'll use a for loop + uint32 to_remove = ginfo->Players.size(); + uint32 team = ginfo->Team; + for(int i = 0; i < to_remove; ++i) + { + // always remove the first one in the group + std::map::iterator itr2 = ginfo->Players.begin(); + if(itr2 == ginfo->Players.end()) + { + sLog.outError("Empty Players in ginfo, this should never happen!"); + return; + } + + // get the player + Player * plr = objmgr.GetPlayer(itr2->first); + if(!plr) + { + sLog.outError("Player offline when trying to remove from GroupQueueInfo, this should never happen."); + continue; + } + + // get the queueslot + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); + if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue + { + plr->RemoveBattleGroundQueueId(bgQueueTypeId); + // remove player from queue, this might delete the ginfo as well! don't use that pointer after this! + RemovePlayer(itr2->first, true); + // this is probably unneeded, since this player was already invited -> does not fit when initing eligible groups + // but updateing the queue can't hurt + Update(bgQueueTypeId, bg->GetQueueType()); + // send info to client + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, team, queueSlot, STATUS_NONE, 0, 0); + plr->GetSession()->SendPacket(&data); + } + } + } + } +} + /* -this method is called when player is inserted, or removed from BG Queue - there is only one player's status changed, so we don't use while(true) cycles to invite whole queue -add method calls this by itself, the remove method could works in other way, so you have to call this method from other code after calling remove method +this method is called when group is inserted, or player / group is removed from BG Queue - there is only one player's status changed, so we don't use while(true) cycles to invite whole queue +it must be called after fully adding the members of a group to ensure group joining +should be called after removeplayer functions in some cases */ -void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id) +void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id, uint8 arenatype, bool isRated, uint32 arenaRating) { if (queue_id >= MAX_BATTLEGROUND_QUEUES) { @@ -156,158 +506,300 @@ } //if no players in queue ... do nothing - if (this->m_QueuedPlayers[queue_id].Alliance == 0 && this->m_QueuedPlayers[queue_id].Horde == 0) + if (this->m_QueuedGroups[queue_id].size() == 0) return; + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId, arenatype); + //battleground with free slot for player should be always the last in this queue - BGFreeSlotQueueType::iterator itr; - for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); ++itr) + BGFreeSlotQueueType::iterator itr, next; + for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); itr = next) { + next = itr; + ++next; // battleground is running, so if: // DO NOT allow queue manager to invite new player to running arena - if ((*itr)->isBattleGround() && (*itr)->GetQueueType() == queue_id && (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE) + if ((*itr)->isBattleGround() && (*itr)->GetTypeID() == bgTypeId && (*itr)->GetQueueType() == queue_id && (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE) { //we must check both teams BattleGround* bg = *itr; //we have to store battleground pointer here, because when battleground is full, it is removed from free queue (not yet implemented!!) // and iterator is invalid - //check if there are some players in queue - if (m_QueuedPlayers[queue_id].Alliance > 0 || m_QueuedPlayers[queue_id].Horde > 0) + for(QueuedGroupsList::iterator itr = m_QueuedGroups[queue_id].begin(); itr != m_QueuedGroups[queue_id].end(); ++itr) { - for (PlayerGuidsSortedByTimeQueue::iterator itr2 = m_PlayersSortedByWaitTime[queue_id].begin(); itr2 != m_PlayersSortedByWaitTime[queue_id].end();) + // did the group join for this bg type? + if((*itr)->BgTypeId != bgTypeId) + continue; + // if so, check if fits in + if(bg->GetFreeSlotsForTeam((*itr)->Team) >= (*itr)->Players.size()) { - Player* plr = objmgr.GetPlayer(*itr2); - if (!plr) - { - //something is wrong!, kick player from queue - sLog.outError("BATTLEGROUND: problem with inviting offline player to Battleground queue .... pls report bug"); - uint64 oldval = *itr2; - itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); - this->RemovePlayer(oldval, true); - continue; - } + // if group fits in, invite it + InviteGroupToBG((*itr),bg,(*itr)->Team); + } + } - // player will be invited, if in bg there is a free slot for him - if (bg->HasFreeSlotsForTeam(plr->GetTeam())) - { - // iterator to player's queue status - QueuedPlayersMap::iterator itrPlayerStatus = m_QueuedPlayers[queue_id].find(*itr2); + if (!bg->HasFreeSlots()) + { + //remove BG from BGFreeSlotQueue + bg->RemoveFromBGFreeSlotQueue(); + } + } + } - // remove him from time queue - itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); + // finished iterating through the bgs with free slots, maybe we need to create a new bg - // only check to be sure ... but this condition shouldn't be true (if it is true, then there is a bug somewhere and pls report it) - if (itrPlayerStatus == m_QueuedPlayers[queue_id].end()) - continue; + BattleGround * bg_template = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); + if(!bg_template) + { + sLog.outError("Battleground: Update: bg template not found for %u", bgTypeId); + return; + } - // check if player is not already invited - if (!itrPlayerStatus->second.IsInvitedToBGInstanceGUID) - { - itrPlayerStatus->second.IsInvitedToBGInstanceGUID = bg->GetInstanceID(); - itrPlayerStatus->second.InviteTime = getMSTime(); - itrPlayerStatus->second.LastInviteTime = getMSTime(); - if(itrPlayerStatus->second.Team == ALLIANCE) - m_QueuedPlayers[queue_id].Alliance--; - else - m_QueuedPlayers[queue_id].Horde--; - sBattleGroundMgr.InvitePlayer(plr, bg->GetInstanceID()); + // get the min. players per team, properly for larger arenas as well. (must have full teams for arena matches!) + uint32 MinPlayersPerTeam = bg_template->GetMinPlayersPerTeam(); + uint32 MaxPlayersPerTeam = bg_template->GetMaxPlayersPerTeam(); + if(bg_template->isArena()) + { + switch(arenatype) + { + case ARENA_TYPE_2v2: + MaxPlayersPerTeam = 2; + MinPlayersPerTeam = 2; + break; + case ARENA_TYPE_3v3: + MaxPlayersPerTeam = 3; + MinPlayersPerTeam = 3; + break; + case ARENA_TYPE_5v5: + MaxPlayersPerTeam = 5; + MinPlayersPerTeam = 5; + break; + } + } - WorldPacket data; - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgTypeId); - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0); - plr->GetSession()->SendPacket(&data); - } - } - else - ++itr2; + uint32 arenaMinRating = (arenaRating <= sBattleGroundMgr.GetMaxRatingDifference()) ? 0 : arenaRating - sBattleGroundMgr.GetMaxRatingDifference(); + uint32 arenaMaxRating = (arenaRating == 0)? 0 : arenaRating + sBattleGroundMgr.GetMaxRatingDifference(); + uint32 discardTime = 0; + if(sBattleGroundMgr.GetMaxRatingDifference() && getMSTime() >= sBattleGroundMgr.GetRatingDiscardTimer()) + discardTime = getMSTime() - sBattleGroundMgr.GetRatingDiscardTimer(); - //if battleground is FULL, then it is removed from free slot queue - not yet implemented! - if (!bg->HasFreeSlots()) - { - //if bg is full, there is no need to invite other players, so break - break; - //remove BG from BGFreeSlotQueue - not used now, in this system we don't remove BGs from free queue - //bg->RemoveFromBGFreeSlotQueue() --- do not uncomment this - not yet implemented - } - } + // try to build the selection pools + bool bAllyOK = BuildSelectionPool(bgTypeId, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam, NORMAL_ALLIANCE, arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); + if(bAllyOK) + sLog.outDebug("Battleground: ally pool succesfully build"); + else + sLog.outDebug("Battleground: ally pool wasn't created"); + bool bHordeOK = BuildSelectionPool(bgTypeId, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam, NORMAL_HORDE, arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); + if(bHordeOK) + sLog.outDebug("Battleground: horde pool succesfully built"); + else + sLog.outDebug("Battleground: horde pool wasn't created"); + + // if selection pools are ready, create the new bg + if (bAllyOK && bHordeOK) + { + BattleGround * bg2 = 0; + // special handling for arenas + if(bg_template->isArena()) + { + // Find a random arena, that can be created + uint8 arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL}; + uint32 arena_num = urand(0,2); + if( !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[arena_num%3])) && + !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+1)%3])) && + !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+2)%3])) ) + { + sLog.outError("Battleground: couldn't create arena"); + return; } + + // set the MaxPlayersPerTeam values based on arenatype + // setting the min player values isn't needed, since we won't be using that value later on. + switch(arenatype) + { + case ARENA_TYPE_2v2: + bg2->SetMaxPlayersPerTeam(2); + bg2->SetMaxPlayers(4); + break; + case ARENA_TYPE_3v3: + bg2->SetMaxPlayersPerTeam(3); + bg2->SetMaxPlayers(6); + break; + case ARENA_TYPE_5v5: + bg2->SetMaxPlayersPerTeam(5); + bg2->SetMaxPlayers(10); + break; + default: + break; + } } - } + else + { + // create new battleground + bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId); + } - /* THIS IS A CASE THAT IN QUEUE THERE IS ENOUGHT PLAYERS TO START NEW BG */ - //itr->end is the last BG - template, which is not already started! + if(!bg2) + { + sLog.outError("Battleground: couldn't create bg %u",bgTypeId); + return; + } - /* here will be a most of change, when we create battlegrounds instanciated */ - /* if (there is enought players to start new BG) - Battleground* newbg = sBattleGroundMgr.CreateNewBattleGround(bgTypeId) - - that function will use the COPY constructor on BattleGround class ( in bg manager we should have one battleground as a template - (battleground template will be used only to create new BGs, it will be an instance of BG class, but it won't ever start) */ - - /* folowing code is working with current Battleground system and it should be removed, when BGs will work like instances */ - BattleGround* bg2 = sBattleGroundMgr.GetBattleGround(bgTypeId); - if (bg2->GetQueueType() != MAX_BATTLEGROUND_QUEUES || bg2->GetStatus() != STATUS_WAIT_QUEUE) - return; - if (m_QueuedPlayers[queue_id].Alliance >= bg2->GetMinPlayersPerTeam() && m_QueuedPlayers[queue_id].Horde >= bg2->GetMinPlayersPerTeam()) - { + // start the joining of the bg bg2->SetStatus(STATUS_WAIT_JOIN); bg2->SetQueueType(queue_id); + // initialize arena / rating info + bg2->SetArenaType(arenatype); + // set rating + bg2->SetRated(isRated); - for (PlayerGuidsSortedByTimeQueue::iterator itr2 = m_PlayersSortedByWaitTime[queue_id].begin(); itr2 != m_PlayersSortedByWaitTime[queue_id].end();) + std::list::iterator itr; + + // invite groups from horde selection pool + for(itr = m_SelectionPools[NORMAL_HORDE].SelectedGroups.begin(); itr != m_SelectionPools[NORMAL_HORDE].SelectedGroups.end(); ++itr) { - Player* plr = objmgr.GetPlayer(*itr2); - if (!plr) + InviteGroupToBG((*itr),bg2,HORDE); + } + + // invite groups from ally selection pools + for(itr = m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.begin(); itr != m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.end(); ++itr) + { + InviteGroupToBG((*itr),bg2,ALLIANCE); + } + + // start the battleground + bg2->StartBattleGround(0); + } + + // there weren't enough players for a "normal" match + // if arena, enable horde versus horde or alliance versus alliance teams here + + else if(bg_template->isArena()) + { + bool bOneSideHordeTeam1 = false, bOneSideHordeTeam2 = false; + bool bOneSideAllyTeam1 = false, bOneSideAllyTeam2 = false; + bOneSideHordeTeam1 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_HORDE_TEAM1,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); + if(bOneSideHordeTeam1) + { + // one team has been selected, find out if other can be selected too + std::list::iterator itr; + // temporarily change the team side to enable building the next pool excluding the already selected groups + for(itr = m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.end(); ++itr) + (*itr)->Team=ALLIANCE; + + bOneSideHordeTeam2 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_HORDE_TEAM2,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); + + // change back the team to horde + for(itr = m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.end(); ++itr) + (*itr)->Team=HORDE; + + if(!bOneSideHordeTeam2) + bOneSideHordeTeam1 = false; + } + if(!bOneSideHordeTeam1) + { + // check for one sided ally + bOneSideAllyTeam1 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_ALLIANCE_TEAM1,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); + if(bOneSideAllyTeam1) { - //something is wrong!, kick player from queue - sLog.outError("BATTLEGROUND: problem with inviting offline player to Battleground queue .... pls report bug"); - uint64 oldval = *itr2; - itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); - this->RemovePlayer(oldval, true); - continue; + // one team has been selected, find out if other can be selected too + std::list::iterator itr; + // temporarily change the team side to enable building the next pool excluding the already selected groups + for(itr = m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.end(); ++itr) + (*itr)->Team=HORDE; + + bOneSideAllyTeam2 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_ALLIANCE_TEAM2,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); + + // change back the team to ally + for(itr = m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.end(); ++itr) + (*itr)->Team=ALLIANCE; } - /* TODO: (i'm not sure this code will be useful: - here should be some condition like if (bg2->isArena() && bg2->isRated()) + if(!bOneSideAllyTeam2) + bOneSideAllyTeam1 = false; + } + // 1-sided BuildSelectionPool() will work, because the MinPlayersPerTeam == MaxPlayersPerTeam in every arena!!!! + if( (bOneSideHordeTeam1 && bOneSideHordeTeam2) || + (bOneSideAllyTeam1 && bOneSideAllyTeam2) ) + { + // create random arena + uint8 arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL}; + uint32 arena_num = urand(0,2); + BattleGround* bg2 = NULL; + if( !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[arena_num%3])) && + !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+1)%3])) && + !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+2)%3])) ) { - invite players from 1 certain group on each faction to play arena match - } else if ....and existing code - */ - // player will be invited, if in bg there is a free slot for him - if (bg2->HasFreeSlotsForTeam(plr->GetTeam())) + sLog.outError("Could not create arena."); + return; + } + + sLog.outDebug("Arena created."); + // init stats + switch(arenatype) { - // iterator to player's queue status - QueuedPlayersMap::iterator itrPlayerStatus = m_QueuedPlayers[queue_id].find(*itr2); + case ARENA_TYPE_2v2: + bg2->SetMaxPlayersPerTeam(2); + bg2->SetMaxPlayers(4); + break; + case ARENA_TYPE_3v3: + bg2->SetMaxPlayersPerTeam(3); + bg2->SetMaxPlayers(6); + break; + case ARENA_TYPE_5v5: + bg2->SetMaxPlayersPerTeam(5); + bg2->SetMaxPlayers(10); + break; + default: + break; + } - // remove him from time queue - itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); + bg2->SetRated(isRated); + // which side has enough players? + uint32 side = 0; + SelectionPoolBuildMode mode1, mode2; + // find out what pools are we using + if(bOneSideAllyTeam1 && bOneSideAllyTeam2) + { + side = ALLIANCE; + mode1 = ONESIDE_ALLIANCE_TEAM1; + mode2 = ONESIDE_ALLIANCE_TEAM2; + } + else + { + side = HORDE; + mode1 = ONESIDE_HORDE_TEAM1; + mode2 = ONESIDE_HORDE_TEAM2; + } + // assigned team of the other group + uint32 other_side; + if(side == ALLIANCE) + other_side = HORDE; + else + other_side = ALLIANCE; - // only check to be sure ... but this condition shouldn't be true (if it is true, then there is a bug somewhere and report it) - if (itrPlayerStatus == m_QueuedPlayers[queue_id].end()) - continue; + // start the joining of the bg + bg2->SetStatus(STATUS_WAIT_JOIN); + bg2->SetQueueType(queue_id); + // initialize arena / rating info + bg2->SetArenaType(arenatype); - //check if player is not already invited - if (!itrPlayerStatus->second.IsInvitedToBGInstanceGUID) - { - itrPlayerStatus->second.IsInvitedToBGInstanceGUID = bg2->GetInstanceID(); - itrPlayerStatus->second.InviteTime = getMSTime(); - itrPlayerStatus->second.LastInviteTime = getMSTime(); + std::list::iterator itr; - if(itrPlayerStatus->second.Team == ALLIANCE) - m_QueuedPlayers[queue_id].Alliance--; - else - m_QueuedPlayers[queue_id].Horde--; + // invite players from the first group as horde players (actually green team) + for(itr = m_SelectionPools[mode1].SelectedGroups.begin(); itr != m_SelectionPools[mode1].SelectedGroups.end(); ++itr) + { + InviteGroupToBG((*itr),bg2,HORDE); + } - sBattleGroundMgr.InvitePlayer(plr, bg2->GetInstanceID()); + // invite players from the second group as ally players (actually gold team) + for(itr = m_SelectionPools[mode2].SelectedGroups.begin(); itr != m_SelectionPools[mode2].SelectedGroups.end(); ++itr) + { + InviteGroupToBG((*itr),bg2,ALLIANCE); + } - WorldPacket data; - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgTypeId); - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg2, plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0); - plr->GetSession()->SendPacket(&data); - } - } - else - ++itr2; + bg2->StartBattleGround(0); } - bg2->StartBattleGround(0); } } @@ -330,16 +822,17 @@ BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID); if (bg) { - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID()); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue { // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems - BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()]; + BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()]; BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid); - if (qItr != qpMap.end() && qItr->second.IsInvitedToBGInstanceGUID == m_BgInstanceGUID) + if (qItr != qpMap.end() && qItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID) { WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, BG_REMIND_INVITE_TIME, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, qItr->second.GroupInfo->Team, queueSlot, STATUS_WAIT_JOIN, BG_REMIND_INVITE_TIME, 0); plr->GetSession()->SendPacket(&data); } } @@ -366,17 +859,18 @@ BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID); if (bg) { - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID()); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue { // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems - if (sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()].find(m_PlayerGuid)->second.IsInvitedToBGInstanceGUID == m_BgInstanceGUID) + if (sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()].find(m_PlayerGuid)->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID) { - plr->RemoveBattleGroundQueueId(bg->GetTypeID()); - sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].RemovePlayer(m_PlayerGuid, true); - sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].Update(bg->GetTypeID(), bg->GetQueueType()); + plr->RemoveBattleGroundQueueId(bgQueueTypeId); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(m_PlayerGuid, true); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgQueueTypeId, bg->GetQueueType()); WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr->GetTeam(), queueSlot, STATUS_NONE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, m_PlayersTeam, queueSlot, STATUS_NONE, 0, 0); plr->GetSession()->SendPacket(&data); } } @@ -398,22 +892,78 @@ BattleGroundMgr::BattleGroundMgr() { m_BattleGrounds.clear(); + m_AutoDistributePoints = (bool)sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS); + m_MaxRatingDifference = sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE); + m_RatingDiscardTimer = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); + m_NextRatingDiscardUpdate = m_RatingDiscardTimer; + m_AutoDistributionTimeChecker = 0; } BattleGroundMgr::~BattleGroundMgr() { - for(std::map::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); ++itr) - delete itr->second; + BattleGroundSet::iterator itr, next; + for(itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); itr = next) + { + next = itr; + ++next; + BattleGround * bg = itr->second; + m_BattleGrounds.erase(itr); + delete bg; + } m_BattleGrounds.clear(); } +// used to update running battlegrounds, and delete finished ones void BattleGroundMgr::Update(time_t diff) { - for(BattleGroundSet::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); ++itr) + BattleGroundSet::iterator itr, next; + for(itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); itr = next) + { + next = itr; + ++next; itr->second->Update(diff); + // use the SetDeleteThis variable + // direct deletion caused crashes + if(itr->second->m_SetDeleteThis) + { + BattleGround * bg = itr->second; + m_BattleGrounds.erase(itr); + delete bg; + } + } + // if rating difference counts, maybe force-update queues + if(m_MaxRatingDifference) + { + // it's time to force update + if(m_NextRatingDiscardUpdate < diff) + { + // forced update for level 70 rated arenas + m_BattleGroundQueues[BATTLEGROUND_QUEUE_2v2].Update(BATTLEGROUND_AA,6,ARENA_TYPE_2v2,true,0); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_3v3].Update(BATTLEGROUND_AA,6,ARENA_TYPE_3v3,true,0); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_5v5].Update(BATTLEGROUND_AA,6,ARENA_TYPE_5v5,true,0); + m_NextRatingDiscardUpdate = m_RatingDiscardTimer; + } + else + m_NextRatingDiscardUpdate -= diff; + } + if(m_AutoDistributePoints) + { + if(m_AutoDistributionTimeChecker < diff) + { + if(time(NULL) > m_NextAutoDistributionTime) + { + DistributeArenaPoints(); + m_NextAutoDistributionTime = time(NULL) + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_WEEK; + CharacterDatabase.PExecute("UPDATE `saved_variables` SET `NextArenaPointDistributionTime` = FROM_UNIXTIME('"I64FMTD"')",(uint64)m_NextAutoDistributionTime); + } + m_AutoDistributionTimeChecker = 600000; // check 10 minutes + } + else + m_AutoDistributionTimeChecker -= diff; + } } -void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2) +void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint32 arenatype, uint8 israted) { // we can be in 3 queues in same time... if(StatusID == 0) @@ -427,7 +977,7 @@ data->Initialize(SMSG_BATTLEFIELD_STATUS, (4+1+1+4+2+4+1+4+4+4)); *data << uint32(QueueSlot); // queue id (0...2) - player can be in 3 queues in time - *data << uint8(bg->GetArenaType()); // team type (0=BG, 2=2x2, 3=3x3, 5=5x5), for arenas + *data << uint8(arenatype ? arenatype : bg->GetArenaType()); // team type (0=BG, 2=2x2, 3=3x3, 5=5x5), for arenas // NOT PROPER VALUE IF ARENA ISN'T RUNNING YET!!!! switch(bg->GetTypeID()) // value depends on bg id { case BATTLEGROUND_AV: @@ -460,9 +1010,9 @@ } if(bg->isArena() && (StatusID == STATUS_WAIT_QUEUE)) - *data << uint32(BATTLEGROUND_AA); // all arenas + *data << uint32(BATTLEGROUND_AA); // all arenas I don't think so. else - *data << uint32(bg->GetTypeID()); // BG id from DBC + *data << uint32(bg->GetTypeID()); // BG id from DBC *data << uint16(0x1F90); // unk value 8080 *data << uint32(bg->GetInstanceID()); // instance id @@ -470,7 +1020,7 @@ if(bg->isBattleGround()) *data << uint8(bg->GetTeamIndexByTeamId(team)); // team else - *data << uint8(bg->isRated()); // is rated battle + *data << uint8(israted?israted:bg->isRated()); // is rated battle *data << uint32(StatusID); // status switch(StatusID) @@ -505,7 +1055,7 @@ { for(uint8 i = 0; i < 2; i++) { - *data << uint32(3000+1+i); // rating change: showed value - 3000 + *data << uint32(3000+bg->m_ArenaTeamRatingChanges[i]); // rating change: showed value - 3000 *data << uint8(0); // string } } @@ -536,11 +1086,13 @@ { // that part probably wrong Player *plr = objmgr.GetPlayer(itr->first); + uint32 team = bg->GetPlayerTeam(plr->GetGUID()); + if(!team) team = plr->GetTeam(); if(plr) { - if(plr->GetTeam() == HORDE) + if(team == HORDE) *data << uint8(0); - else if(plr->GetTeam() == ALLIANCE) + else if(team == ALLIANCE) *data << uint8(1); else *data << uint8(0); @@ -624,20 +1176,133 @@ *data << uint64(plr->GetGUID()); } -void BattleGroundMgr::InvitePlayer(Player* plr, uint32 bgInstanceGUID) +void BattleGroundMgr::InvitePlayer(Player* plr, uint32 bgInstanceGUID, uint32 team) { // set invited player counters: BattleGround* bg = this->GetBattleGround(bgInstanceGUID); - bg->IncreaseInvitedCount(plr->GetTeam()); + bg->IncreaseInvitedCount(team); + // set the arena teams for rated matches + if(bg->isArena() && bg->isRated()) + { + switch(bg->GetArenaType()) + { + case ARENA_TYPE_2v2: + bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(0)); + break; + case ARENA_TYPE_3v3: + bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(1)); + break; + case ARENA_TYPE_5v5: + bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(2)); + break; + default: + break; + } + } + // create invite events: //add events to player's counters ---- this is not good way - there should be something like global event processor, where we should add those events BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), bgInstanceGUID); plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(BG_REMIND_INVITE_TIME)); - BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), bgInstanceGUID, plr->GetTeam()); + BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), bgInstanceGUID, team); plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(2*BG_REMIND_INVITE_TIME)); } +BattleGround * BattleGroundMgr::GetBattleGroundTemplate(uint32 bgTypeId) +{ + return BGFreeSlotQueue[bgTypeId].empty() ? NULL : BGFreeSlotQueue[bgTypeId].back(); +} + +// create a new battleground that will really be used to play +BattleGround * BattleGroundMgr::CreateNewBattleGround(uint32 bgTypeId) +{ + BattleGround *bg = NULL; + + // get the template BG + BattleGround *bg_template = GetBattleGroundTemplate(bgTypeId); + + if(!bg_template) + { + sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId); + return 0; + } + + // create a copy of the BG template + switch(bgTypeId) + { + case BATTLEGROUND_AV: + bg = new BattleGroundAV(*(BattleGroundAV*)bg_template); + break; + case BATTLEGROUND_WS: + bg = new BattleGroundWS(*(BattleGroundWS*)bg_template); + break; + case BATTLEGROUND_AB: + bg = new BattleGroundAB(*(BattleGroundAB*)bg_template); + break; + case BATTLEGROUND_NA: + bg = new BattleGroundNA(*(BattleGroundNA*)bg_template); + break; + case BATTLEGROUND_BE: + bg = new BattleGroundBE(*(BattleGroundBE*)bg_template); + break; + case BATTLEGROUND_AA: + bg = new BattleGroundAA(*(BattleGroundAA*)bg_template); + break; + case BATTLEGROUND_EY: + bg = new BattleGroundEY(*(BattleGroundEY*)bg_template); + break; + case BATTLEGROUND_RL: + bg = new BattleGroundRL(*(BattleGroundRL*)bg_template); + break; + default: + //bg = new BattleGround; + return 0; + break; // placeholder for non implemented BG + } + + // generate a new instance id + bg->SetInstanceID(MapManager::Instance().GenerateInstanceId()); // set instance id + +/* UNCOMMENT THIS IF MAP SYSTEM IS UPDATED + // get the map + Map * m = MapManager::Instance().GetMap(bg->GetMapId(),NULL); + if(!m) + { + // no map found, delete bg and return null + delete bg; + return 0; + } + + // create new instance if the map is instanceable + if(m->Instanceable()) + { + // generate a new instance id + bg->SetInstanceID(MapManager::Instance().GenerateInstanceId()); // set instance id + ((MapInstanced*)m)->CreateInstance(bg->GetInstanceID(),m); + } +*/ + + // reset the new bg (set status to status_wait_queue from status_none) + bg->Reset(); + + if(!(bg->SetupBattleGround())) + { + sLog.outError("BattleGround: CreateNewBattleGround: SetupBattleGround failed for bg %u", bgTypeId); + delete bg; + return 0; + } + + // add BG to free slot queue + bg->AddToBGFreeSlotQueue(); + + // add bg to update list + AddBattleGround(bg->GetInstanceID(), bg); + + return bg; +} + +// used to create the BG templates uint32 BattleGroundMgr::CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO) { // Create the BG @@ -657,12 +1322,12 @@ } bg->SetMapId(MapID); + + // set unloadflag to false + Map* map = const_cast(MapManager::Instance().GetBaseMap(MapID)); + if(map) map->SetUnloadMap(false); + bg->Reset(); - if(!bg->SetupBattleGround()) - { - delete bg; - return 0; - } BattlemasterListEntry const *bl = sBattlemasterListStore.LookupEntry(bgTypeId); //in previous method is checked if exists entry in sBattlemasterListStore, so no check needed @@ -672,7 +1337,7 @@ } bg->SetTypeID(bgTypeId); - bg->SetInstanceID(bgTypeId); // temporary + bg->SetInstanceID(0); // template bg, instance id is 0 bg->SetMinPlayersPerTeam(MinPlayersPerTeam); bg->SetMaxPlayersPerTeam(MaxPlayersPerTeam); bg->SetMinPlayers(MinPlayersPerTeam*2); @@ -681,12 +1346,14 @@ bg->SetTeamStartLoc(ALLIANCE, Team1StartLocX, Team1StartLocY, Team1StartLocZ, Team1StartLocO); bg->SetTeamStartLoc(HORDE, Team2StartLocX, Team2StartLocY, Team2StartLocZ, Team2StartLocO); bg->SetLevelRange(LevelMin, LevelMax); - //add BaggleGround instance to FreeSlotQueue + + //add BattleGround instance to FreeSlotQueue (.back() will return the template!) bg->AddToBGFreeSlotQueue(); - AddBattleGround(bg->GetInstanceID(), bg); - //sLog.outDetail("BattleGroundMgr: Created new battleground: %u %s (Map %u, %u players per team, Levels %u-%u)", bg_TypeID, bg->m_Name, bg->m_MapId, bg->m_MaxPlayersPerTeam, bg->m_LevelMin, bg->m_LevelMax); - return bg->GetInstanceID(); + // do NOT add to update list, since this is a template battleground! + + // return some not-null value, bgTypeId is good enough for me + return bgTypeId; } void BattleGroundMgr::CreateInitialBattleGrounds() @@ -806,6 +1473,86 @@ sLog.outString( ">> Loaded %u battlegrounds", count ); } +void BattleGroundMgr::InitAutomaticArenaPointDistribution() +{ + if(m_AutoDistributePoints) + { + sLog.outDebug("Initializing Automatic Arena Point Distribution"); + QueryResult * result = CharacterDatabase.Query("SELECT UNIX_TIMESTAMP(`NextArenaPointDistributionTime`) FROM `saved_variables`"); + if(!result) + { + sLog.outDebug("Battleground: Next arena point distribution time not found in SavedVariables, reseting it now."); + m_NextAutoDistributionTime = time(NULL) + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_WEEK; + CharacterDatabase.PExecute("INSERT INTO `saved_variables` (`NextArenaPointDistributionTime`) VALUES ( FROM_UNIXTIME('"I64FMTD"') )",(uint64)m_NextAutoDistributionTime); + } + else + { + m_NextAutoDistributionTime = (*result)[0].GetUInt64(); + delete result; + } + sLog.outDebug("Automatic Arena Point Distribution initialized."); + } +} + +void BattleGroundMgr::DistributeArenaPoints() +{ + // used to distribute arena points based on last week's stats + sWorld.SendWorldText("Flushing Arena points based on team ratings, this may take a few minutes. Please stand by...", NULL); + + sWorld.SendWorldText("Distributing arena points to online players...", NULL); + + //TODO: Guard Player map + HashMapHolder::MapType& m = ObjectAccessor::Instance().GetPlayers(); + for(HashMapHolder::MapType::iterator itr = m.begin(); itr != m.end(); ++itr) + { + uint64 guid = itr->second->GetGUID(); + Player * pl = itr->second; + if(!pl) + continue; + uint32 mod = 0; + for(int i=0; i<3; ++i) + { + ArenaTeam * at = objmgr.GetArenaTeamById(pl->GetArenaTeamId(i)); + if(at && mod < at->GetMemberPoints(guid)) + mod = at->GetMemberPoints(guid); + // reset weekly played match count for team + if(at) + pl->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 6 * at->GetSlot() + 2, 0); + + } + mod += pl->GetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY); + if(mod > 5000) + mod = 5000; + + pl->SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, mod); + + CharacterDatabase.PExecute("UPDATE `arena_team_member` SET `points_to_add` = '0' WHERE `guid` = '%u'",guid); + } + + sWorld.SendWorldText("Finished distributing arena points to online players. Now saving... ", NULL); + + ObjectAccessor::Instance().SaveAllPlayers(); + + sWorld.SendWorldText("Online players saved.", NULL); + + sWorld.SendWorldText("Distributing arena points to offline players...", NULL); + + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("UPDATE `character` b, `arena_team_member` a SET b.`data` = CONCAT( SUBSTRING_INDEX(b.`data`, ' ', '%u'),' ', CAST( IF ( ((CAST( SUBSTRING( b.`data` FROM (CHAR_LENGTH(SUBSTRING_INDEX(b.`data`,' ','%u')) + 2) FOR (CHAR_LENGTH(SUBSTRING_INDEX(b.`data`,' ','%u')) - CHAR_LENGTH(SUBSTRING_INDEX(b.`data`,' ','%u')) - 1) ) AS UNSIGNED) + (SELECT MAX(c.`points_to_add`) FROM `arena_team_member` c WHERE c.`guid` = b.`guid` GROUP BY c.`guid`) ) < '5000'), CAST(SUBSTRING(b.`data` FROM (CHAR_LENGTH(SUBSTRING_INDEX(b.`data`,' ','%u')) + 2) FOR (CHAR_LENGTH(SUBSTRING_INDEX(b.`data`,' ','%u')) - CHAR_LENGTH(SUBSTRING_INDEX(b.`data`,' ','%u')) - 1) ) AS UNSIGNED) + (SELECT MAX(d.`points_to_add`) FROM `arena_team_member` d WHERE d.`guid` = b.`guid` GROUP BY d.`guid`), '5000') AS CHAR),' ',SUBSTRING(b.`data` FROM (CHAR_LENGTH(SUBSTRING_INDEX(b.`data`,' ','%u')) + 2))) WHERE b.`guid` = a.`guid`",PLAYER_FIELD_ARENA_CURRENCY, PLAYER_FIELD_ARENA_CURRENCY, PLAYER_FIELD_ARENA_CURRENCY+1, PLAYER_FIELD_ARENA_CURRENCY, PLAYER_FIELD_ARENA_CURRENCY, PLAYER_FIELD_ARENA_CURRENCY+1, PLAYER_FIELD_ARENA_CURRENCY, PLAYER_FIELD_ARENA_CURRENCY+1); + for(int i=0; i<3; ++i) + { + // reset weekly played matches + uint32 position = PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 6 * i + 2; + CharacterDatabase.PExecute("UPDATE `character` SET `data` = CONCAT( SUBSTRING_INDEX(`data`,' ','%u'),' ','0',' ',SUBSTRING(`data` FROM (CHAR_LENGTH(SUBSTRING_INDEX(`data`,' ','%u')) + 2)))",position, position + 1); + } + CharacterDatabase.Execute("UPDATE `arena_team_member` SET `points_to_add` = '0', `played_week` = '0', `wons_week` = '0'"); + CharacterDatabase.Execute("UPDATE `arena_team_stats` SET `games` = '0', `wins` = '0'"); + CharacterDatabase.CommitTransaction(); + + sWorld.SendWorldText("Finished distributing arena points to offline players.", NULL); + sWorld.SendWorldText("Done flushing Arena points.", NULL); +} + void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, uint64 guid, Player* plr, uint32 bgTypeId) { uint32 PlayerLevel = 10; @@ -841,18 +1588,27 @@ } } -void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 bgTypeId) +void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId, uint32 team) { - BattleGround *bg = GetBattleGround(bgTypeId); + BattleGround *bg = GetBattleGround(instanceId); if(bg) { uint32 mapid = bg->GetMapId(); float x, y, z, O; - bg->GetTeamStartLoc(pl->GetTeam(), x, y, z, O); + if(team==0) + team = pl->GetTeam(); + bg->GetTeamStartLoc(team, x, y, z, O); sLog.outDetail("BATTLEGROUND: Sending %s to map %u, X %f, Y %f, Z %f, O %f", pl->GetName(), mapid, x, y, z, O); - pl->TeleportTo(mapid, x, y, z, O); + pl->SetInstanceId(instanceId); + pl->m_BoundInstances[mapid]=std::pair(instanceId, pl->GetGUIDLow()); + // teleport as gm command + pl->TeleportTo(mapid, x, y, z, O, true, true, false, true); } + else + { + sLog.outError("player %u trying to port to non-existent bg instance %u",pl->GetGUIDLow(), instanceId); + } } void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, uint64 guid) @@ -864,3 +1620,91 @@ data << guid << time_; pl->GetSession()->SendPacket(&data); } + +void BattleGroundMgr::RemoveBattleGround(uint32 instanceID) +{ + BattleGroundSet::iterator itr = m_BattleGrounds.find(instanceID); + if(itr!=m_BattleGrounds.end()) + m_BattleGrounds.erase(itr); +} + +bool BattleGroundMgr::IsArenaType(uint32 bgTypeId) const +{ + return ( bgTypeId == BATTLEGROUND_AA || + bgTypeId == BATTLEGROUND_BE || + bgTypeId == BATTLEGROUND_NA || + bgTypeId == BATTLEGROUND_RL ); +} + +bool BattleGroundMgr::IsBattleGroundType(uint32 bgTypeId) const +{ + return !IsArenaType(bgTypeId); +} + +uint32 BattleGroundMgr::BGQueueTypeId(uint32 bgTypeId, uint8 arenaType) const +{ + switch(bgTypeId) + { + case BATTLEGROUND_WS: + return BATTLEGROUND_QUEUE_WS; + case BATTLEGROUND_AB: + return BATTLEGROUND_QUEUE_AB; + case BATTLEGROUND_AV: + return BATTLEGROUND_QUEUE_AV; + case BATTLEGROUND_EY: + return BATTLEGROUND_QUEUE_EY; + case BATTLEGROUND_AA: + case BATTLEGROUND_NA: + case BATTLEGROUND_RL: + case BATTLEGROUND_BE: + switch(arenaType) + { + case ARENA_TYPE_2v2: + return BATTLEGROUND_QUEUE_2v2; + case ARENA_TYPE_3v3: + return BATTLEGROUND_QUEUE_3v3; + case ARENA_TYPE_5v5: + return BATTLEGROUND_QUEUE_5v5; + default: + return 0; + } + default: + return 0; + } +} + +uint32 BattleGroundMgr::BGTemplateId(uint32 bgQueueTypeId) const +{ + switch(bgQueueTypeId) + { + case BATTLEGROUND_QUEUE_WS: + return BATTLEGROUND_WS; + case BATTLEGROUND_QUEUE_AB: + return BATTLEGROUND_AB; + case BATTLEGROUND_QUEUE_AV: + return BATTLEGROUND_AV; + case BATTLEGROUND_QUEUE_EY: + return BATTLEGROUND_EY; + case BATTLEGROUND_QUEUE_2v2: + case BATTLEGROUND_QUEUE_3v3: + case BATTLEGROUND_QUEUE_5v5: + return BATTLEGROUND_AA; + default: + return 0; + } +} + +uint8 BattleGroundMgr::BGArenaType(uint32 bgQueueTypeId) const +{ + switch(bgQueueTypeId) + { + case BATTLEGROUND_QUEUE_2v2: + return ARENA_TYPE_2v2; + case BATTLEGROUND_QUEUE_3v3: + return ARENA_TYPE_3v3; + case BATTLEGROUND_QUEUE_5v5: + return ARENA_TYPE_5v5; + default: + return 0; + } +} Index: src/game/BattleGroundMgr.h =================================================================== --- src/game/BattleGroundMgr.h (revision 5306) +++ src/game/BattleGroundMgr.h (working copy) @@ -34,53 +34,99 @@ #define MAX_BATTLEGROUND_TYPES 9 // each BG type will be in array +#define MAX_BATTLEGROUND_QUEUE_TYPES 8 + #define BG_REMIND_INVITE_TIME 60000 // when this time pass, player get invitation request again -struct PlayerQueueInfo +#define BATTLEGROUND_ARENA_POINT_DISTRIBUTION_WEEK 604800 // seconds in a week + +struct GroupQueueInfo; // type predefinition +struct PlayerQueueInfo // stores information for players in queue { uint32 InviteTime; // first invite time uint32 LastInviteTime; // last invite time - uint32 IsInvitedToBGInstanceGUID; // was invited to certain BG uint32 LastOnlineTime; // for tracking and removing offline players from queue after 5 minutes - uint32 Team; // Player team (ALLIANCE/HORDE) - bool IsRated; - bool AsGroup; // uint32 GroupId; - uint8 ArenaType; + GroupQueueInfo * GroupInfo; // pointer to the associated groupqueueinfo }; -struct PlayersCount +struct GroupQueueInfo // stores information about the group in queue (also used when joined as solo!) { - uint32 Alliance; - uint32 Horde; + std::map Players; // player queue info map + uint32 Team; // Player team (ALLIANCE/HORDE) + bool IsRated; // rated + uint32 BgTypeId; // battleground type id + uint8 ArenaType; // 2v2, 3v3, 5v5 or 0 when BG + uint32 JoinTime; // time when group was added + uint32 IsInvitedToBGInstanceGUID; // was invited to certain BG + uint32 ArenaTeamRating; // if rated match, inited to the rating of the team }; -template class bgqueue: public std::map<_Kty, _Ty> -{ - public: - uint32 Alliance; - uint32 Horde; - //bool Ready; // not used now - //uint32 AverageTime; //not already implemented (it should be average time in queue for last 10 players) -}; - +class BattleGround; class BattleGroundQueue { public: BattleGroundQueue(); ~BattleGroundQueue(); -/* - uint32 GetType(); - void SetType(uint32 type);*/ - void Update(uint32 bgTypeId, uint32 queue_id); + void Update(uint32 bgTypeId, uint32 queue_id, uint8 arenatype = 0, bool isRated = false, uint32 minRating = 0); - void AddPlayer(Player *plr, uint32 bgTypeId); + GroupQueueInfo * AddGroup(Player * leader, uint32 BgTypeId, uint8 ArenaType, bool isRated, uint32 ArenaRating); + void AddPlayer(Player *plr, GroupQueueInfo * ginfo); void RemovePlayer(uint64 guid, bool decreaseInvitedCount); + void DecreaseGroupLength(uint32 queueId, uint32 AsGroup); + void BGEndedRemoveInvites(BattleGround * bg); - typedef bgqueue QueuedPlayersMap; + typedef std::map QueuedPlayersMap; QueuedPlayersMap m_QueuedPlayers[MAX_BATTLEGROUND_QUEUES]; - typedef std::list PlayerGuidsSortedByTimeQueue; - PlayerGuidsSortedByTimeQueue m_PlayersSortedByWaitTime[MAX_BATTLEGROUND_QUEUES]; + + typedef std::list QueuedGroupsList; + QueuedGroupsList m_QueuedGroups[MAX_BATTLEGROUND_QUEUES]; + + // class to hold pointers to the groups eligible for a specific selection pool building mode + class EligibleGroups : public std::list + { + public: + void Init(QueuedGroupsList * source, uint32 BgTypeId, uint32 side, uint32 MaxPlayers, uint8 ArenaType = 0, bool IsRated = false, uint32 MinRating = 0, uint32 MaxRating = 0, uint32 DisregardTime = 0); + void RemoveGroup(GroupQueueInfo * ginfo); + }; + + EligibleGroups m_EligibleGroups; + + // class to select and invite groups to bg + class SelectionPool + { + public: + void Init(); + void AddGroup(GroupQueueInfo * group); + GroupQueueInfo * GetMaximalGroup(); + void RemoveGroup(GroupQueueInfo * group); + uint32 GetPlayerCount() const {return PlayerCount;} + public: + std::list SelectedGroups; + private: + uint32 PlayerCount; + GroupQueueInfo * MaxGroup; + }; + + enum SelectionPoolBuildMode + { + NORMAL_ALLIANCE, + NORMAL_HORDE, + ONESIDE_ALLIANCE_TEAM1, + ONESIDE_ALLIANCE_TEAM2, + ONESIDE_HORDE_TEAM1, + ONESIDE_HORDE_TEAM2, + + NUM_SELECTION_POOL_TYPES + }; + + SelectionPool m_SelectionPools[NUM_SELECTION_POOL_TYPES]; + + bool BuildSelectionPool(uint32 bgTypeId, uint32 queue_id, uint32 MinPlayers, uint32 MaxPlayers, SelectionPoolBuildMode mode, uint8 ArenaType = 0, bool isRated = false, uint32 MinRating = 0, uint32 MaxRating = 0, uint32 DisregardTime = 0); + + private: + + bool InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side); }; /* @@ -98,7 +144,6 @@ private: uint64 m_PlayerGuid; uint32 m_BgInstanceGUID; - }; /* @@ -118,7 +163,6 @@ uint32 m_PlayersTeam; }; - class BattleGroundMgr { public: @@ -134,18 +178,18 @@ void BuildGroupJoinedBattlegroundPacket(WorldPacket *data, uint32 bgTypeId); void BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value); void BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg); - void BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2); + void BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint32 arenatype = 0, uint8 israted = 0); void BuildPlaySoundPacket(WorldPacket *data, uint32 soundid); /* Player invitation */ // called from Queue update, or from Addplayer to queue - void InvitePlayer(Player* plr, uint32 bgInstanceGUID); + void InvitePlayer(Player* plr, uint32 bgInstanceGUID, uint32 team); /* Battlegrounds */ BattleGroundSet::iterator GetBattleGroundsBegin() { return m_BattleGrounds.begin(); }; BattleGroundSet::iterator GetBattleGroundsEnd() { return m_BattleGrounds.end(); }; - BattleGround* GetBattleGround(uint8 ID) + BattleGround* GetBattleGround(uint32 ID) { BattleGroundSet::iterator i = m_BattleGrounds.find(ID); if(i != m_BattleGrounds.end()) @@ -154,26 +198,48 @@ return NULL; }; + BattleGround * GetBattleGroundTemplate(uint32 bgTypeId); + BattleGround * CreateNewBattleGround(uint32 bgTypeId); + uint32 CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO); inline void AddBattleGround(uint32 ID, BattleGround* BG) { m_BattleGrounds[ID] = BG; }; + void RemoveBattleGround(uint32 instanceID); void CreateInitialBattleGrounds(); - void SendToBattleGround(Player *pl, uint32 bgTypeId); + void SendToBattleGround(Player *pl, uint32 bgTypeId, uint32 team = 0); /* Battleground queues */ //these queues are instantiated when creating BattlegroundMrg - BattleGroundQueue m_BattleGroundQueues[MAX_BATTLEGROUND_TYPES]; // public, because we need to access them in BG handler code + BattleGroundQueue m_BattleGroundQueues[MAX_BATTLEGROUND_QUEUE_TYPES]; // public, because we need to access them in BG handler code BGFreeSlotQueueType BGFreeSlotQueue[MAX_BATTLEGROUND_TYPES]; void SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, uint64 guid); + bool IsArenaType(uint32 bgTypeId) const; + bool IsBattleGroundType(uint32 bgTypeId) const; + uint32 BGQueueTypeId(uint32 bgTypeId, uint8 arenaType) const; + uint32 BGTemplateId(uint32 bgQueueTypeId) const; + uint8 BGArenaType(uint32 bgQueueTypeId) const; + + uint32 GetMaxRatingDifference() const {return m_MaxRatingDifference;} + uint32 GetRatingDiscardTimer() const {return m_RatingDiscardTimer;} + + void InitAutomaticArenaPointDistribution(); + void DistributeArenaPoints(); + private: /* Battlegrounds */ BattleGroundSet m_BattleGrounds; + uint32 m_MaxRatingDifference; + uint32 m_RatingDiscardTimer; + uint32 m_NextRatingDiscardUpdate; + bool m_AutoDistributePoints; + uint64 m_NextAutoDistributionTime; + uint32 m_AutoDistributionTimeChecker; }; #define sBattleGroundMgr MaNGOS::Singleton::Instance() Index: src/game/BattleGroundNA.cpp =================================================================== --- src/game/BattleGroundNA.cpp (revision 5306) +++ src/game/BattleGroundNA.cpp (working copy) @@ -73,6 +73,9 @@ for(uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_2; i++) DoorOpen(i); + for(uint32 i = BG_NA_OBJECT_BUFF_1; i <= BG_NA_OBJECT_BUFF_2; i++) + SpawnBGObject(i, 60); + SendMessageToAll(LANG_ARENA_BEGUN); SetStatus(STATUS_IN_PROGRESS); SetStartDelayTime(0); @@ -83,6 +86,11 @@ if(plr) plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); } + + if(!GetPlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetPlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } } @@ -92,9 +100,9 @@ }*/ } -void BattleGroundNA::AddPlayer(Player *plr) +void BattleGroundNA::AddPlayer(Player *plr, uint32 team) { - BattleGround::AddPlayer(plr); + BattleGround::AddPlayer(plr, team); //create score and add it to map, default values are set in constructor BattleGroundNAScore* sc = new BattleGroundNAScore; @@ -103,7 +111,13 @@ void BattleGroundNA::RemovePlayer(Player *plr, uint64 guid) { + if(GetStatus() == STATUS_WAIT_LEAVE) + return; + if(!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } void BattleGroundNA::HandleKillPlayer(Player *player, Player *killer) @@ -117,15 +131,16 @@ return; } - uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam()); - - m_TeamKills[killer_team_index]++; // add kills to killer's team - - if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(player->GetTeam())) + if(!GetAlivePlayersCountByTeam(ALLIANCE)) { // all opponents killed - EndBattleGround(killer->GetTeam()); + EndBattleGround(HORDE); } + else if(!GetAlivePlayersCountByTeam(HORDE)) + { + // all opponents killed + EndBattleGround(ALLIANCE); + } } void BattleGroundNA::HandleAreaTrigger(Player *Source, uint32 Trigger) @@ -134,11 +149,14 @@ return; uint32 SpellId = 0; - uint64 buff_guid = 0; + GameObject* obj = 0; switch(Trigger) { case 4536: // buff trigger? + obj = m_BgObjects[BG_NA_OBJECT_BUFF_1]; + break; case 4537: // buff trigger? + obj = m_BgObjects[BG_NA_OBJECT_BUFF_2]; break; default: sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); @@ -146,11 +164,8 @@ break; } - if(buff_guid) + if(obj) { - GameObject *obj = HashMapHolder::Find(buff_guid); - if(obj) - { if(!obj->isSpawned()) return; // buff not spawned yet obj->SetRespawnTime(BUFF_RESPAWN_TIME); @@ -158,14 +173,12 @@ SpellId = obj->GetGOInfo()->data3; if(SpellId) Source->CastSpell(Source, SpellId, true); - } } } void BattleGroundNA::ResetBGSubclass() { - m_TeamKills[BG_TEAM_ALLIANCE] = 0; - m_TeamKills[BG_TEAM_HORDE] = 0; + } bool BattleGroundNA::SetupBattleGround() @@ -174,7 +187,10 @@ if( !AddObject(BG_NA_OBJECT_DOOR_1, BG_NA_OBJECT_TYPE_DOOR_1, 4031.854, 2966.833, 12.6462, -2.648788, 0, 0, 0.9697962, -0.2439165, RESPAWN_IMMEDIATELY) || !AddObject(BG_NA_OBJECT_DOOR_2, BG_NA_OBJECT_TYPE_DOOR_2, 4081.179, 2874.97, 12.39171, 0.4928045, 0, 0, 0.2439165, 0.9697962, RESPAWN_IMMEDIATELY) || !AddObject(BG_NA_OBJECT_DOOR_3, BG_NA_OBJECT_TYPE_DOOR_3, 4023.709, 2981.777, 10.70117, -2.648788, 0, 0, 0.9697962, -0.2439165, RESPAWN_IMMEDIATELY) - || !AddObject(BG_NA_OBJECT_DOOR_4, BG_NA_OBJECT_TYPE_DOOR_4, 4090.064, 2858.438, 10.23631, 0.4928045, 0, 0, 0.2439165, 0.9697962, RESPAWN_IMMEDIATELY)) + || !AddObject(BG_NA_OBJECT_DOOR_4, BG_NA_OBJECT_TYPE_DOOR_4, 4090.064, 2858.438, 10.23631, 0.4928045, 0, 0, 0.2439165, 0.9697962, RESPAWN_IMMEDIATELY) + // buffs + || !AddObject(BG_NA_OBJECT_BUFF_1, BG_NA_OBJECT_TYPE_BUFF_1, 4009.189941, 2895.250000, 13.052700, -1.448624, 0, 0, 0.6626201, -0.7489557, 120) + || !AddObject(BG_NA_OBJECT_BUFF_2, BG_NA_OBJECT_TYPE_BUFF_2, 4103.330078, 2946.350098, 13.051300, -0.06981307, 0, 0, 0.03489945, -0.9993908, 120)) { sLog.outErrorDb("BatteGroundNA: Failed to spawn some object!"); return false; Index: src/game/BattleGroundNA.h =================================================================== --- src/game/BattleGroundNA.h (revision 5306) +++ src/game/BattleGroundNA.h (working copy) @@ -26,7 +26,9 @@ BG_NA_OBJECT_DOOR_2 = 1, BG_NA_OBJECT_DOOR_3 = 2, BG_NA_OBJECT_DOOR_4 = 3, - BG_NA_OBJECT_MAX = 4 + BG_NA_OBJECT_BUFF_1 = 4, + BG_NA_OBJECT_BUFF_2 = 5, + BG_NA_OBJECT_MAX = 6 }; enum BattleGroundNAObjects @@ -34,7 +36,9 @@ BG_NA_OBJECT_TYPE_DOOR_1 = 183978, BG_NA_OBJECT_TYPE_DOOR_2 = 183980, BG_NA_OBJECT_TYPE_DOOR_3 = 183977, - BG_NA_OBJECT_TYPE_DOOR_4 = 183979 + BG_NA_OBJECT_TYPE_DOOR_4 = 183979, + BG_NA_OBJECT_TYPE_BUFF_1 = 184663, + BG_NA_OBJECT_TYPE_BUFF_2 = 184664 }; class BattleGroundNAScore : public BattleGroundScore @@ -55,15 +59,12 @@ void Update(time_t diff); /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); + virtual void AddPlayer(Player *plr, uint32 team = 0); void RemovePlayer(Player *plr, uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); bool SetupBattleGround(); virtual void ResetBGSubclass(); void HandleKillPlayer(Player* player, Player *killer); - - private: - uint32 m_TeamKills[2]; // count of kills for each team }; #endif Index: src/game/BattleGroundRL.cpp =================================================================== --- src/game/BattleGroundRL.cpp (revision 5306) +++ src/game/BattleGroundRL.cpp (working copy) @@ -74,6 +74,9 @@ for(uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; i++) DoorOpen(i); + for(uint32 i = BG_RL_OBJECT_BUFF_1; i <= BG_RL_OBJECT_BUFF_2; i++) + SpawnBGObject(i, 60); + SendMessageToAll(LANG_ARENA_BEGUN); SetStatus(STATUS_IN_PROGRESS); SetStartDelayTime(0); @@ -84,6 +87,11 @@ if(plr) plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); } + + if(!GetPlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetPlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } } @@ -93,9 +101,9 @@ }*/ } -void BattleGroundRL::AddPlayer(Player *plr) +void BattleGroundRL::AddPlayer(Player *plr, uint32 team) { - BattleGround::AddPlayer(plr); + BattleGround::AddPlayer(plr, team); //create score and add it to map, default values are set in constructor BattleGroundRLScore* sc = new BattleGroundRLScore; @@ -104,7 +112,13 @@ void BattleGroundRL::RemovePlayer(Player *plr, uint64 guid) { + if(GetStatus() == STATUS_WAIT_LEAVE) + return; + if(!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } void BattleGroundRL::HandleKillPlayer(Player *player, Player *killer) @@ -118,15 +132,16 @@ return; } - uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam()); - - m_TeamKills[killer_team_index]++; // add kills to killer's team - - if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(player->GetTeam())) + if(!GetAlivePlayersCountByTeam(ALLIANCE)) { // all opponents killed - EndBattleGround(killer->GetTeam()); + EndBattleGround(HORDE); } + else if(!GetAlivePlayersCountByTeam(HORDE)) + { + // all opponents killed + EndBattleGround(ALLIANCE); + } } void BattleGroundRL::HandleAreaTrigger(Player *Source, uint32 Trigger) @@ -136,11 +151,14 @@ return; uint32 SpellId = 0; - uint64 buff_guid = 0; + GameObject* obj = 0; switch(Trigger) { case 4696: // buff trigger? + obj = m_BgObjects[BG_RL_OBJECT_BUFF_1]; + break; case 4697: // buff trigger? + obj = m_BgObjects[BG_RL_OBJECT_BUFF_2]; break; default: sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); @@ -148,11 +166,8 @@ break; } - if(buff_guid) + if(obj) { - GameObject *obj = HashMapHolder::Find(buff_guid); - if(obj) - { if(!obj->isSpawned()) return; // buff not spawned yet obj->SetRespawnTime(BUFF_RESPAWN_TIME); @@ -160,21 +175,22 @@ SpellId = obj->GetGOInfo()->data3; if(SpellId) Source->CastSpell(Source, SpellId, true); - } } } void BattleGroundRL::ResetBGSubclass() { - m_TeamKills[BG_TEAM_ALLIANCE] = 0; - m_TeamKills[BG_TEAM_HORDE] = 0; + } bool BattleGroundRL::SetupBattleGround() { // gates if( !AddObject(BG_RL_OBJECT_DOOR_1, BG_RL_OBJECT_TYPE_DOOR_1, 1293.561, 1601.938, 31.60557, -1.457349, 0, 0, -0.6658813, 0.7460576, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RL_OBJECT_DOOR_2, BG_RL_OBJECT_TYPE_DOOR_2, 1278.648, 1730.557, 31.60557, 1.684245, 0, 0, 0.7460582, 0.6658807, RESPAWN_IMMEDIATELY)) + || !AddObject(BG_RL_OBJECT_DOOR_2, BG_RL_OBJECT_TYPE_DOOR_2, 1278.648, 1730.557, 31.60557, 1.684245, 0, 0, 0.7460582, 0.6658807, RESPAWN_IMMEDIATELY) + // buffs + || !AddObject(BG_RL_OBJECT_BUFF_1, BG_RL_OBJECT_TYPE_BUFF_1, 1328.719971, 1632.719971, 36.730400, -1.448624, 0, 0, 0.6626201, -0.7489557, 120) + || !AddObject(BG_RL_OBJECT_BUFF_2, BG_RL_OBJECT_TYPE_BUFF_2, 1243.300049, 1699.170044, 34.872601, -0.06981307, 0, 0, 0.03489945, -0.9993908, 120)) { sLog.outErrorDb("BatteGroundRL: Failed to spawn some object!"); return false; Index: src/game/BattleGroundRL.h =================================================================== --- src/game/BattleGroundRL.h (revision 5306) +++ src/game/BattleGroundRL.h (working copy) @@ -24,13 +24,17 @@ { BG_RL_OBJECT_DOOR_1 = 0, BG_RL_OBJECT_DOOR_2 = 1, - BG_RL_OBJECT_MAX = 2 + BG_RL_OBJECT_BUFF_1 = 2, + BG_RL_OBJECT_BUFF_2 = 3, + BG_RL_OBJECT_MAX = 4 }; enum BattleGroundRLObjects { BG_RL_OBJECT_TYPE_DOOR_1 = 185918, - BG_RL_OBJECT_TYPE_DOOR_2 = 185917 + BG_RL_OBJECT_TYPE_DOOR_2 = 185917, + BG_RL_OBJECT_TYPE_BUFF_1 = 184663, + BG_RL_OBJECT_TYPE_BUFF_2 = 184664 }; class BattleGroundRLScore : public BattleGroundScore @@ -51,15 +55,12 @@ void Update(time_t diff); /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); + virtual void AddPlayer(Player *plr, uint32 team = 0); void RemovePlayer(Player *plr, uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); bool SetupBattleGround(); virtual void ResetBGSubclass(); void HandleKillPlayer(Player* player, Player *killer); - - private: - uint32 m_TeamKills[2]; // count of kills for each team }; #endif Index: src/game/BattleGroundWS.cpp =================================================================== --- src/game/BattleGroundWS.cpp (revision 5306) +++ src/game/BattleGroundWS.cpp (working copy) @@ -50,6 +50,9 @@ { m_Events |= 0x01; + for(uint32 i = WS_SPIRIT_MAIN_ALLIANCE; i <= WS_SPIRIT_MAIN_HORDE; i++) + SpawnBGCreature(i, RESPAWN_IMMEDIATELY); + for(uint32 i = BG_WS_OBJECT_DOOR_A_1; i <= BG_WS_OBJECT_DOOR_H_4; i++) SpawnBGObject(i, RESPAWN_IMMEDIATELY); @@ -111,9 +114,9 @@ } } -void BattleGroundWS::AddPlayer(Player *plr) +void BattleGroundWS::AddPlayer(Player *plr, uint32 team) { - BattleGround::AddPlayer(plr); + BattleGround::AddPlayer(plr, team); //create score and add it to map, default values are set in constructor BattleGroundWGScore* sc = new BattleGroundWGScore; @@ -388,7 +391,7 @@ return; uint32 SpellId = 0; - uint64 buff_guid = 0; + GameObject* buff_guid = 0; switch(Trigger) { case 3686: // Alliance elixir of speed spawn. Trigger not working, because located inside other areatrigger, can be replaced by IsWithinDist(object, dist) in BattleGround::Update(). @@ -432,7 +435,7 @@ if(buff_guid) { - GameObject *obj = HashMapHolder::Find(buff_guid); + GameObject *obj = buff_guid; if(obj) { if(!obj->isSpawned()) @@ -504,11 +507,13 @@ m_TeamScores[BG_TEAM_ALLIANCE] = 0; m_TeamScores[BG_TEAM_HORDE] = 0; +/* do not do this here, useless anyway if(m_BgCreatures[WS_SPIRIT_MAIN_ALLIANCE]) DelCreature(WS_SPIRIT_MAIN_ALLIANCE); if(m_BgCreatures[WS_SPIRIT_MAIN_HORDE]) DelCreature(WS_SPIRIT_MAIN_HORDE); +*/ } void BattleGroundWS::HandleKillPlayer(Player *player, Player *killer) Index: src/game/BattleGroundWS.h =================================================================== --- src/game/BattleGroundWS.h (revision 5306) +++ src/game/BattleGroundWS.h (working copy) @@ -122,7 +122,7 @@ void Update(time_t diff); /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); + virtual void AddPlayer(Player *plr, uint32 team = 0); /* BG Flags */ uint64 GetAllianceFlagPickerGUID() const { return m_FlagKeepers[BG_TEAM_ALLIANCE]; } Index: src/game/Chat.cpp =================================================================== --- src/game/Chat.cpp (revision 5306) +++ src/game/Chat.cpp (working copy) @@ -387,6 +387,7 @@ { "cast", SEC_ADMINISTRATOR, &ChatHandler::HandleCastCommand, "", NULL }, { "castback", SEC_ADMINISTRATOR, &ChatHandler::HandleCastBackCommand, "", NULL }, { "casttarget", SEC_ADMINISTRATOR, &ChatHandler::HandleCastTargetCommand, "", NULL }, + { "flusharenapoints", SEC_ADMINISTRATOR, &ChatHandler::HandleFlushArenaPointsCommand, "", NULL }, //! Development Commands { "setvalue", SEC_ADMINISTRATOR, &ChatHandler::HandleSetValue, "", NULL }, Index: src/game/Chat.h =================================================================== --- src/game/Chat.h (revision 5306) +++ src/game/Chat.h (working copy) @@ -329,6 +329,7 @@ bool HandleCastCommand(const char *args); bool HandleCastBackCommand(const char *args); bool HandleCastTargetCommand(const char *args); + bool HandleFlushArenaPointsCommand(const char *args); //! Development Commands bool HandleSetValue(const char* args); Index: src/game/ItemPrototype.h =================================================================== --- src/game/ItemPrototype.h (revision 5306) +++ src/game/ItemPrototype.h (working copy) @@ -536,7 +536,7 @@ uint32 socketBonus; // id from SpellItemEnchantment.dbc uint32 GemProperties; // id from GemProperties.dbc uint32 ExtendedCost; // id from ItemExtendedCost.dbc - uint32 RequiredArenaRank; + uint32 RequiredArenaRank; // id from ItemCondExtCosts.dbc // note: this field should be renamed accordingly uint32 RequiredDisenchantSkill; float ArmorDamageModifier; char* ScriptName; Index: src/game/Language.h =================================================================== --- src/game/Language.h (revision 5306) +++ src/game/Language.h (working copy) @@ -528,6 +528,13 @@ LANG_PLAYER_AFK = 708, LANG_PLAYER_DND_DEFAULT = 709, LANG_PLAYER_AFK_DEFAULT = 710, + + LANG_BG_GROUP_TOO_LARGE = 7007, // "Your group is too large for this battleground. Please regroup to join." + LANG_ARENA_GROUP_TOO_LARGE = 7008, // "Your group is too large for this arena. Please regroup to join." + LANG_ARENA_YOUR_TEAM_ONLY = 7009, // "Your group has members not in your arena team. Please regroup to join." + LANG_ARENA_NOT_ENOUGH_PLAYERS = 7010, // "Your group does not have enough players to join this match." + LANG_ARENA_GOLD_WINS = 7011, // "The Gold Team wins!" + LANG_ARENA_GREEN_WINS = 7012 // "The Green Team wins!" }; /* NOT USED VALUES Index: src/game/Level3.cpp =================================================================== --- src/game/Level3.cpp (revision 5306) +++ src/game/Level3.cpp (working copy) @@ -42,6 +42,7 @@ #include "SkillExtraItems.h" #include "SystemConfig.h" #include "Config/ConfigEnv.h" +#include "BattleGroundMgr.h" //reload commands bool ChatHandler::HandleReloadCommand(const char* arg) @@ -4673,3 +4674,9 @@ return true; } + +bool ChatHandler::HandleFlushArenaPointsCommand(const char * /*args*/) +{ + sBattleGroundMgr.DistributeArenaPoints(); + return true; +} Index: src/game/Map.cpp =================================================================== --- src/game/Map.cpp (revision 5306) +++ src/game/Map.cpp (working copy) @@ -271,6 +271,10 @@ else i_data = NULL; + // flags if gridunloading is enabled / disabled on this map + // needed, because bg objects aren't in the db, but added in runtime, and unloading the grids would destroy them + i_unloadmap = sWorld.getConfig(CONFIG_GRID_UNLOAD); + i_Players.clear(); } @@ -393,7 +397,7 @@ Guard guard(*this); if(!getNGrid(p.x_coord, p.y_coord)) { - setNGrid(new NGridType(p.x_coord*MAX_NUMBER_OF_GRIDS + p.y_coord, p.x_coord, p.y_coord, i_gridExpiry, sWorld.getConfig(CONFIG_GRID_UNLOAD)), + setNGrid(new NGridType(p.x_coord*MAX_NUMBER_OF_GRIDS + p.y_coord, p.x_coord, p.y_coord, i_gridExpiry, i_unloadmap), p.x_coord, p.y_coord); // build a linkage between this map and NGridType @@ -725,8 +729,9 @@ { // Don't unload grids if it's battleground, since we may have manually added GOs,creatures, those doesn't load from DB at grid re-load ! // This isn't really bother us, since as soon as we have instanced BG-s, the whole map unloads as the BG gets ended - if ((i_id == 489) || (i_id == 529)) // WS, AB - return; + // no longer needed, the unloadmap flag will prevent unloading bg map grids + // if ((i_id == 489) || (i_id == 529)) // WS, AB + // return; for (GridRefManager::iterator i = GridRefManager::begin(); i != GridRefManager::end(); ) { Index: src/game/Map.h =================================================================== --- src/game/Map.h (revision 5306) +++ src/game/Map.h (working copy) @@ -149,6 +149,8 @@ bool GetUnloadFlag(const GridPair &p) const { return getNGrid(p.x_coord, p.y_coord)->getUnloadFlag(); } void SetUnloadFlag(const GridPair &p, bool unload) { getNGrid(p.x_coord, p.y_coord)->setUnloadFlag(unload); } + void SetUnloadMap(bool unload){ i_unloadmap = unload; } + bool GetUnloadMap() const { return i_unloadmap; } void LoadGrid(const Cell& cell, bool no_unload = false); bool UnloadGrid(const uint32 &x, const uint32 &y, bool pForce); virtual void UnloadAll(bool pForce); @@ -207,7 +209,7 @@ bool CanEnter(Player* player) const; const char* GetMapName() const; - bool Instanceable() const { return(i_mapEntry && ((i_mapEntry->map_type == MAP_INSTANCE) || (i_mapEntry->map_type == MAP_RAID))); } + bool Instanceable() const { return(i_mapEntry && ((i_mapEntry->map_type == MAP_INSTANCE) || (i_mapEntry->map_type == MAP_RAID) || (i_mapEntry->map_type == MAP_BATTLEGROUND) || (i_mapEntry->map_type == MAP_ARENA))); } // NOTE: this duplicate of Instanceable(), but Instanceable() can be changed when BG also will be instanceable bool IsDungeon() const { return(i_mapEntry && ((i_mapEntry->map_type == MAP_INSTANCE) || (i_mapEntry->map_type == MAP_RAID))); } bool IsRaid() const { return(i_mapEntry && (i_mapEntry->map_type == MAP_RAID)); } @@ -295,6 +297,7 @@ uint32 i_resetDelayTime; uint32 i_InstanceId; uint32 i_maxPlayers; + bool i_unloadmap; InstanceData* i_data; std::string i_script; Index: src/game/MapInstanced.cpp =================================================================== --- src/game/MapInstanced.cpp (revision 5306) +++ src/game/MapInstanced.cpp (working copy) @@ -44,6 +44,7 @@ { if (i->second->GetPlayersCount() == 0) { + DEBUG_LOG("reseting instance in update for instance id = %u and map id = %u named %s",this->GetInstanceId(),this->GetId(),this->GetMapName()); i->second->Reset(); // avoid doing ++ on invalid data InstancedMaps::iterator i_old = i; @@ -187,7 +188,7 @@ if (!instantiator && instantiator_online) { - sLog.outDebug("MAPINSTANCED: Player '%s' is not in group, instantiating map for player", player->GetName()); + sLog.outDebug("MAPINSTANCED: Player '%s' is not in group, instantiating map %u for player", player->GetName(), GetId()); instantiator = player; } @@ -230,11 +231,13 @@ // check, if we have to generate new instance if (!instantiator_bound || (InstanceId == 0)) { + sLog.outDebug("MAPINSTANCED: Generating new instance id"); // yes, new instance id has to be generated InstanceId = MapManager::Instance().GenerateInstanceId(); } else { + sLog.outDebug("MAPINSTANCED: Instantiator bound, finding map..."); // find map of the designated instance and verify it map = _FindMap(InstanceId); } @@ -242,7 +245,7 @@ // now create the instance CreateInstance(InstanceId, map); - + sLog.outDebug("MAPINSTANCED::GetInstance() Instance Created"); // we do need to bind instance, if the instance we use is valid, not temporary if (player->m_InstanceValid) { @@ -304,6 +307,7 @@ // map for the instance is present, but the instance itself is no more valid, destroy map Guard guard(*this); + DEBUG_LOG("reseting instance in createinstance for instance id = %u and map id = %u named %s",this->GetInstanceId(),this->GetId(),this->GetMapName()); map->Reset(); m_InstancedMaps.erase(InstanceId); delete map; @@ -314,10 +318,13 @@ // verify, if we have the map created, and create it if needed if (!map) { + sLog.outDebug("MAPINSTANCED: CreateInstance, map not found, creating it"); // map does not exist, create it Guard guard(*this); map = new Map(GetId(), GetGridExpiry(), InstanceId); + // set the unload map flag according to the base map's flag + map->SetUnloadMap(GetUnloadMap()); m_InstancedMaps[InstanceId] = map; } } Index: src/game/MapManager.cpp =================================================================== --- src/game/MapManager.cpp (revision 5306) +++ src/game/MapManager.cpp (working copy) @@ -104,7 +104,7 @@ Guard guard(*this); const MapEntry* entry = sMapStore.LookupEntry(id); - if (entry && ((entry->map_type == MAP_INSTANCE) || (entry->map_type == MAP_RAID))) + if (entry && ((entry->map_type == MAP_INSTANCE) || (entry->map_type == MAP_RAID) || (entry->map_type == MAP_BATTLEGROUND) || (entry->map_type == MAP_ARENA))) { m = new MapInstanced(id, i_gridCleanUpDelay, 0); } Index: src/game/MovementHandler.cpp =================================================================== --- src/game/MovementHandler.cpp (revision 5306) +++ src/game/MovementHandler.cpp (working copy) @@ -93,17 +93,21 @@ { if(bg->GetMapId() == _player->GetMapId()) // we teleported to bg { - if(!bg->GetBgRaid(_player->GetTeam())) // first player joined + // get the team this way, because arenas might 'override' the teams. + uint32 team = bg->GetPlayerTeam(_player->GetGUID()); + if(!team) + team = _player->GetTeam(); + if(!bg->GetBgRaid(team)) // first player joined { Group *group = new Group; - bg->SetBgRaid(_player->GetTeam(), group); + bg->SetBgRaid(team, group); group->ConvertToRaid(); group->AddMember(_player->GetGUID(), _player->GetName()); group->ChangeLeader(_player->GetGUID()); } else // raid already exist { - bg->GetBgRaid(_player->GetTeam())->AddMember(_player->GetGUID(), _player->GetName()); + bg->GetBgRaid(team)->AddMember(_player->GetGUID(), _player->GetName()); } } } @@ -504,3 +508,11 @@ _player->SummonIfPossible(); } + + + + + + + + Index: src/game/ObjectMgr.cpp =================================================================== --- src/game/ObjectMgr.cpp (revision 5306) +++ src/game/ObjectMgr.cpp (working copy) @@ -1340,6 +1340,10 @@ if(proto->ExtendedCost && !sItemExtendedCostStore.LookupEntry(proto->ExtendedCost)) sLog.outErrorDb("Item (Entry: %u) has wrong ExtendedCost (%u)",i,proto->ExtendedCost); + // check if the extended cost and the required arena rank pair is valid (does it have entry in ItemCondExtCosts.dbc?) + if(proto->ExtendedCost && proto->RequiredArenaRank && !GetItemCondExtCostsMask(proto->RequiredArenaRank, proto->ExtendedCost)) + sLog.outErrorDb("Item (Entry: %u) has wrong ExtendedCost - RequiredArenaRank pair (%u, %u)",i,proto->ExtendedCost,proto->RequiredArenaRank); + if(proto->FoodType >= MAX_PET_DIET) { sLog.outErrorDb("Item (Entry: %u) has wrong FoodType value (%u)",i,proto->FoodType); Index: src/game/PetitionsHandler.cpp =================================================================== --- src/game/PetitionsHandler.cpp (revision 5306) +++ src/game/PetitionsHandler.cpp (working copy) @@ -49,9 +49,6 @@ { CHECK_PACKET_SIZE(recv_data, 8+8+4+1+5*8+2+1+4+4); - if(_player->GetGuildId()) - return; - sLog.outDebug("Received opcode CMSG_PETITION_BUY"); //recv_data.hexlike(); @@ -95,6 +92,11 @@ uint32 type = 0; if(pCreature->isTabardDesigner()) { + // if tabard designer, then trying to buy a guild charter. + // do not let if already in guild. + if(_player->GetGuildId()) + return; + charterid = GUILD_CHARTER; cost = GUILD_CHARTER_COST; type = 9; @@ -102,7 +104,7 @@ else { // TODO: find correct opcode - if(_player->getLevel() != 70) + if(_player->getLevel() < 70) { SendNotification(objmgr.GetMangosString(LANG_ARENA_ONE_TOOLOW, GetSessionLocaleIndex()), 70); return; @@ -188,10 +190,31 @@ charter->SetState(ITEM_CHANGED, _player); _player->SendNewItem(charter, 1, true, false); + // a petition is invalid, if both the owner and the type matches + QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE ownerguid = '%u' AND type = '%u'", _player->GetGUIDLow(), type); + + std::ostringstream ssInvalidPetitionGUIDs; + + if (result) + { + + do + { + Field *fields = result->Fetch(); + ssInvalidPetitionGUIDs << "'" << fields[0].GetUInt32() << "' , "; + } while (result->NextRow()); + + delete result; + } + + // delete petitions with the same guid as this one + ssInvalidPetitionGUIDs << "'" << charter->GetGUIDLow() << "'"; + + sLog.outDebug("Invalid petition GUIDs: %s", ssInvalidPetitionGUIDs.str().c_str()); CharacterDatabase.escape_string(name); CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM petition WHERE ownerguid = '%u' OR petitionguid = '%u' and type = '%u'", _player->GetGUIDLow(), charter->GetGUIDLow(),type); - CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE ownerguid = '%u' or petitionguid = '%u'", _player->GetGUIDLow(), charter->GetGUIDLow()); + CharacterDatabase.PExecute("DELETE FROM petition WHERE petitionguid IN ( %s )", ssInvalidPetitionGUIDs.str().c_str()); + CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE petitionguid IN ( %s )", ssInvalidPetitionGUIDs.str().c_str()); CharacterDatabase.PExecute("INSERT INTO petition (ownerguid, petitionguid, name, type) VALUES ('%u', '%u', '%s', '%u')", _player->GetGUIDLow(), charter->GetGUIDLow(), name.c_str(), type); CharacterDatabase.CommitTransaction(); @@ -205,9 +228,6 @@ sLog.outDebug("Received opcode CMSG_PETITION_SHOW_SIGNATURES"); //recv_data.hexlike(); - if(_player->GetGuildId()) - return; - uint8 signs = 0; uint64 petitionguid; recv_data >> petitionguid; // petition guid @@ -215,14 +235,18 @@ // solve (possible) some strange compile problems with explicit use GUID_LOPART(petitionguid) at some GCC versions (wrong code optimization in compiler?) uint32 petitionguid_low = GUID_LOPART(petitionguid); - QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE petitionguid = '%u'", petitionguid_low); + QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid, type FROM petition WHERE petitionguid = '%u'", petitionguid_low); if(!result) { sLog.outError("any petition on server..."); return; } - + Field *fields = result->Fetch(); + uint32 type = fields[1].GetUInt32(); delete result; + // if guild petition and has guild => error, return; + if(type==9 && _player->GetGuildId()) + return; result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", petitionguid_low); @@ -317,13 +341,14 @@ { data << uint32(9); data << uint32(9); + data << uint32(0); // bypass client - side limitation, a different value is needed here for each petition } else { data << type-1; data << type-1; + data << type; // bypass client - side limitation, a different value is needed here for each petition } - data << uint32(0); // 4 data << uint32(0); // 5 data << uint32(0); // 6 data << uint32(0); // 7 @@ -457,13 +482,20 @@ return; } + if(type != 9 && _player->getLevel() < 70) + { + // player is too low level to join an arena team + SendNotification("You must be level 70 to join an arena team!"); + return; + } + signs += 1; if(signs > type) // client signs maximum return; //client doesn't allow to sign petition two times by one character, but not check sign by another character from same account //not allow sign another player from already sign player account - result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE player_account = '%u'", GetAccountId()); + result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE player_account = '%u' AND petitionguid = '%u'", GetAccountId(), GUID_LOPART(petitionguid)); if(result) { @@ -638,16 +670,29 @@ } else { - for(uint8 i = 0; i < 3; i++) + uint32 type_slot; + switch(type) { - if(_player->GetArenaTeamId(i)) - { + case ARENA_TEAM_2v2: + type_slot = 0; + break; + case ARENA_TEAM_3v3: + type_slot = 1; + break; + case ARENA_TEAM_5v5: + type_slot = 2; + break; + default: + type_slot = 0; + break; + } + if(_player->GetArenaTeamId(type_slot)) + { //data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4); //data << (uint32)PETITION_TURN_ALREADY_IN_GUILD; // already in guild //_player->GetSession()->SendPacket(&data); SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM); return; - } } } @@ -735,6 +780,7 @@ ArenaTeam* at = new ArenaTeam; if(!at->create(_player->GetGUID(), type, name)) { + sLog.outError("PetitionsHandler: arena team create failed."); delete at; delete result; return; @@ -748,11 +794,13 @@ // register team and add captain objmgr.AddArenaTeam(at); + sLog.outDebug("PetitonsHandler: arena team added to objmrg"); // add members for(uint8 i = 0; i < signs; ++i) { Field* fields = result->Fetch(); + sLog.outDebug("PetitionsHandler: adding arena member %u", fields[0].GetUInt64()); at->AddMember(fields[0].GetUInt64()); result->NextRow(); } @@ -820,21 +868,21 @@ data << uint32(ARENA_TEAM_CHARTER_2v2); // charter entry data << uint32(16161); // charter display id data << uint32(ARENA_TEAM_CHARTER_2v2_COST); // charter cost - data << uint32(0); // unknown + data << uint32(2); // unknown data << uint32(2); // required signs? // 3v3 data << uint32(2); // index data << uint32(ARENA_TEAM_CHARTER_3v3); // charter entry data << uint32(16161); // charter display id data << uint32(ARENA_TEAM_CHARTER_3v3_COST); // charter cost - data << uint32(0); // unknown + data << uint32(3); // unknown data << uint32(3); // required signs? // 5v5 data << uint32(3); // index data << uint32(ARENA_TEAM_CHARTER_5v5); // charter entry data << uint32(16161); // charter display id data << uint32(ARENA_TEAM_CHARTER_5v5_COST); // charter cost - data << uint32(0); // unknown + data << uint32(5); // unknown data << uint32(5); // required signs? } //for(uint8 i = 0; i < count; i++) Index: src/game/Player.cpp =================================================================== --- src/game/Player.cpp (revision 5306) +++ src/game/Player.cpp (working copy) @@ -1252,8 +1252,8 @@ bool state = HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK); - // afk player not allowed in battleground - if(state && InBattleGround()) + // afk player not allowed in battleground ( do net check this in arena ) + if(state && InBattleGround() && GetBattleGround() && GetBattleGround()->isBattleGround()) LeaveBattleground(); return state; @@ -1436,7 +1436,7 @@ sLog.outDebug( "WORLD: Sent (SMSG_IGNORE_LIST)" ); } -void Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, bool outofrange, bool ignore_transport, bool is_gm_command) +void Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, bool outofrange, bool ignore_transport, bool is_gm_command, bool battleground_port) { // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later) Pet* pet = GetPet(); @@ -1596,7 +1596,8 @@ Map* map = MapManager::Instance().GetMap(mapid, this); // verify, if it does want to homebind us (but only if we are not in GM state, // GMs are allowed to teleport everywhere they want to be) - if (!m_InstanceValid && (!isGameMaster() || !is_gm_command)) + // battlegrounds do NOT homebind + if (!m_InstanceValid && (!isGameMaster() || !is_gm_command) && !battleground_port) { // yes, we are going to be homebind, avoid this action :) SetInstanceId(0); // reset instance id @@ -2727,6 +2728,32 @@ removeSpell(itr->second.spell); } +void Player::RemoveArenaSpellCooldowns() +{ + // remove cooldowns on spells that has < 15 min CD + SpellCooldowns::iterator itr, next; + // iterate spell cooldowns + for(itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end(); itr = next) + { + next = itr; + ++next; + SpellEntry const * entry = sSpellStore.LookupEntry(itr->first); + // check if spellentry is present and if the cooldown is less than 15 mins + if( entry && + (entry->RecoveryTime <= 15 * 60000) && + (entry->CategoryRecoveryTime <= 15 * 60000)) + { + // notify player + WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8+4)); + data << uint32(itr->first); + data << GetGUID(); + GetSession()->SendPacket(&data); + // remove cooldown + m_spellCooldowns.erase(itr); + } + } +} + void Player::RemoveAllSpellCooldown() { if(!m_spellCooldowns.empty()) @@ -5538,6 +5565,10 @@ uint32 now = time(NULL); UpdateHonorFields(); + // do not reward honor in arenas + if(InBattleGround() && GetBattleGround() && GetBattleGround()->isArena()) + return false; + if(honor <= 0) { if(!uVictim || uVictim == this || uVictim->GetAura(2479, 0)) @@ -5659,27 +5690,33 @@ return 0; } -uint32 Player::GetArenaTeamIdFromDB(uint64 guid, uint8 slot) +uint32 Player::GetArenaTeamIdFromDB(uint64 guid, uint8 type) { // need fix it! QueryResult *result = CharacterDatabase.PQuery("SELECT arenateamid FROM arena_team_member WHERE guid='%u'", GUID_LOPART(guid)); if(result) { - uint32 id = (*result)[0].GetUInt32(); + // init id to 0, check the arena type before assigning a value to id + uint32 id = 0; do { QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM arena_team WHERE arenateamid='%u'", id); if(result2) { - uint8 dbslot = (*result2)[0].GetUInt32(); + uint8 dbtype = (*result2)[0].GetUInt32(); delete result2; - if(dbslot == slot) + if(dbtype == type) + { + // if the type matches, we've found the id + id = (*result)[0].GetUInt32(); break; + } } } while(result->NextRow()); delete result; return id; } + // no arenateam for the specified guid, return 0 return 0; } @@ -8725,6 +8762,13 @@ pProto->SubClass != ITEM_SUBCLASS_ARMOR_SHIELD && pProto->InventoryType != INVTYPE_RELIC) return EQUIP_ERR_NOT_IN_COMBAT; + // do not allow gear change during in-progress arena matches, only weapons / offhands / relics / ammo + if( InBattleGround() && GetBattleGround() && GetBattleGround()->isArena() && + (GetBattleGround()->GetStatus() == STATUS_IN_PROGRESS) && + (pProto->Class != ITEM_CLASS_WEAPON) && (pProto->Class != ITEM_CLASS_PROJECTILE) && + (pProto->SubClass != ITEM_SUBCLASS_ARMOR_SHIELD) && (pProto->InventoryType != INVTYPE_RELIC)) + return EQUIP_ERR_NOT_DURING_ARENA_MATCH; + if(isInCombat()&& pProto->Class == ITEM_CLASS_WEAPON && m_weaponChangeTimer != 0) return EQUIP_ERR_CANT_DO_RIGHT_NOW; // maybe exist better err @@ -8853,6 +8897,13 @@ pProto->SubClass != ITEM_SUBCLASS_ARMOR_SHIELD && pProto->InventoryType != INVTYPE_RELIC ) return EQUIP_ERR_NOT_IN_COMBAT; + // do not allow unequipping gear in in-progress arenas, except weapons, offhands, projectiles, relics + if( InBattleGround() && GetBattleGround() && GetBattleGround()->isArena() && + (GetBattleGround()->GetStatus() == STATUS_IN_PROGRESS) && + (pProto->Class != ITEM_CLASS_WEAPON) && (pProto->Class != ITEM_CLASS_PROJECTILE) && + (pProto->SubClass != ITEM_SUBCLASS_ARMOR_SHIELD) && (pProto->InventoryType != INVTYPE_RELIC)) + return EQUIP_ERR_NOT_DURING_ARENA_MATCH; + if(!swap && pItem->IsBag() && !((Bag*)pItem)->IsEmpty()) return EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS; @@ -9887,6 +9938,54 @@ } } +void Player::DestroyConjuredItems( bool update ) +{ + // used when entering arena + // distroys all conjured items + sLog.outDebug( "STORAGE: DestroyConjuredItems" ); + + // in inventory + for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++) + { + Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); + if( pItem && pItem->GetProto() && + (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) && + (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) ) + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); + } + + // in inventory bags + for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) + { + Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ); + if( pBag ) + { + ItemPrototype const *pBagProto = pBag->GetProto(); + if( pBagProto ) + { + for(uint32 j = 0; j < pBagProto->ContainerSlots; j++) + { + Item* pItem = pBag->GetItemByPos(j); + if( pItem && pItem->GetProto() && + (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) && + (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) ) + DestroyItem( i, j, update); + } + } + } + } + + // in equipment and bag list + for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++) + { + Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); + if( pItem && pItem->GetProto() && + (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) && + (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) ) + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); + } +} + void Player::DestroyItemCount( Item* pItem, uint32 &count, bool update ) { if(!pItem) @@ -14894,11 +14993,15 @@ void Player::RemovePetitionsAndSigns(uint64 guid, uint32 type) { - QueryResult *result = CharacterDatabase.PQuery("SELECT ownerguid,petitionguid FROM petition_sign WHERE playerguid = '%u'", GUID_LOPART(guid)); + QueryResult *result = NULL; + if(type==10) + result = CharacterDatabase.PQuery("SELECT ownerguid,petitionguid FROM petition_sign WHERE playerguid = '%u'", GUID_LOPART(guid)); + else + result = CharacterDatabase.PQuery("SELECT ownerguid,petitionguid FROM petition_sign WHERE playerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type); if(result) { - do - { + do // this part effectively does nothing, since the deletion / modification only takes place _after_ the PetitionQuery. Though I don't know if the result remains intact if I execute the delete query beforehand. + { // and SendPetitionQueryOpcode reads data from the DB Field *fields = result->Fetch(); uint64 ownerguid = MAKE_GUID(fields[0].GetUInt32(), HIGHGUID_PLAYER); uint64 petitionguid = MAKE_GUID(fields[1].GetUInt32(), HIGHGUID_ITEM); @@ -14912,15 +15015,23 @@ delete result; - CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE playerguid = '%u'", GUID_LOPART(guid)); + if(type==10) + CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE playerguid = '%u'", GUID_LOPART(guid)); + else + CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE playerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type); } CharacterDatabase.BeginTransaction(); if(type == 10) + { CharacterDatabase.PExecute("DELETE FROM petition WHERE ownerguid = '%u'", GUID_LOPART(guid)); + CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE ownerguid = '%u'", GUID_LOPART(guid)); + } else + { CharacterDatabase.PExecute("DELETE FROM petition WHERE ownerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type); - CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE ownerguid = '%u'", GUID_LOPART(guid)); + CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE ownerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type); + } CharacterDatabase.CommitTransaction(); } @@ -15280,6 +15391,9 @@ if(pProto->ExtendedCost) { ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(pProto->ExtendedCost); + uint32 mask = 0; + if(pProto->RequiredArenaRank) + mask = GetItemCondExtCostsMask(pProto->RequiredArenaRank, pProto->ExtendedCost); if(iece) { if(GetHonorPoints() < (iece->reqhonorpoints * count)) @@ -15299,6 +15413,13 @@ SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL); return false; } + // check for personal arena rating requirement + if( (mask & (1<<3)) && (GetMaxPersonalArenaRatingRequirement() < iece->reqpersonalarenarating) ) + { + // probably not the proper equip err + SendEquipError(EQUIP_ERR_CANT_EQUIP_RANK,NULL,NULL); + return false; + } } else { @@ -15434,6 +15555,26 @@ return false; } +uint32 Player::GetMaxPersonalArenaRatingRequirement() +{ + // returns the maximal personal arena rating that can be used to purchase items requiring this condition + // the personal rating of the arena team must match the required limit as well + // so return max[in arenateams](min(personalrating[teamtype], teamrating[teamtype])) + uint32 max_personal_rating = 0; + for(int i = 0; i < 3; ++i) + { + if(ArenaTeam * at = objmgr.GetArenaTeamById(GetArenaTeamId(i))) + { + uint32 p_rating = GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (i * 6) + 5); + uint32 t_rating = at->GetRating(); + p_rating = p_ratingHasAura(SPELL_ARENA_PREPARATION, 0); + for(uint32 x=0;x<8;x++) { if(m_spellInfo->Reagent[x] <= 0) continue; uint32 itemid = m_spellInfo->Reagent[x]; + + // don't take soul shards if arena prep is on + if(bArenaPrep && (itemid == 6265)) + continue; + uint32 itemcount = m_spellInfo->ReagentCount[x]; // if CastItem is also spell reagent @@ -2675,6 +2683,15 @@ if((m_spellInfo->AttributesEx3 & 0x800) != 0) return SPELL_FAILED_ONLY_BATTLEGROUNDS; + // do not allow spells with greater than 15 min CD to be cast in arenas + if( m_caster->GetTypeId() == TYPEID_PLAYER && + ((Player*)m_caster)->InBattleGround() && + ((Player*)m_caster)->GetBattleGround() && + ((Player*)m_caster)->GetBattleGround()->isArena() && + (m_spellInfo->RecoveryTime > 15 * 60000 || + m_spellInfo->CategoryRecoveryTime > 15 * 60000) ) + return SPELL_FAILED_NOT_IN_ARENA; + // zone check uint32 zone_id = m_caster->GetZoneId(); @@ -3626,6 +3643,9 @@ focusObject = ok; // game object found in range } + // check if arena prep is up + bool bArenaPrep = p_caster->HasAura(SPELL_ARENA_PREPARATION, 0); + for(uint32 i=0;i<8;i++) { if(m_spellInfo->Reagent[i] <= 0) @@ -3633,26 +3653,36 @@ itemid = m_spellInfo->Reagent[i]; itemcount = m_spellInfo->ReagentCount[i]; - - // if CastItem is also spell reagent - if( m_CastItem && m_CastItem->GetEntry() == itemid ) + + if( m_CastItem ) { - ItemPrototype const *proto = m_CastItem->GetProto(); - if(!proto) - return SPELL_FAILED_ITEM_NOT_READY; - for(int s=0;s<5;s++) + // if CastItem is also spell reagent + if( m_CastItem->GetEntry() == itemid ) { - // CastItem will be used up and does not count as reagent - int32 charges = m_CastItem->GetSpellCharges(s); - if (proto->Spells[s].SpellCharges < 0 && abs(charges) < 2) + ItemPrototype const *proto = m_CastItem->GetProto(); + if(!proto) + return SPELL_FAILED_ITEM_NOT_READY; + for(int s=0;s<5;s++) { - itemcount++; - break; + // CastItem will be used up and does not count as reagent + int32 charges = m_CastItem->GetSpellCharges(s); + if (proto->Spells[s].SpellCharges < 0 && abs(charges) < 2) + { + itemcount++; + break; + } } } + if( !p_caster->HasItemCount(itemid,itemcount) ) + return (uint8)SPELL_FAILED_ITEM_NOT_READY; //0x54 } - if( !p_caster->HasItemCount(itemid,itemcount) ) - return (uint8)SPELL_FAILED_ITEM_NOT_READY; //0x54 + // if arena preparation is up, don't take soul shards, but take other reagents + // this cannot happen in case of a m_CastItem, so it's enough to check here + else if (!bArenaPrep || itemid != 6265) + { + if( !p_caster->HasItemCount(itemid,itemcount) ) + return (uint8)SPELL_FAILED_ITEM_NOT_READY; //0x54 + } } uint32 totems = 2; Index: src/game/SpellHandler.cpp =================================================================== --- src/game/SpellHandler.cpp (revision 5306) +++ src/game/SpellHandler.cpp (working copy) @@ -66,6 +66,15 @@ return; } + // only allow conjured consumable, bandage + if ( pUser->InBattleGround() && pUser->GetBattleGround() && pUser->GetBattleGround()->isArena() + && (proto->Class == ITEM_CLASS_CONSUMABLE) && !(proto->Flags & ITEM_FLAGS_CONJURED) && !(proto->SubClass == ITEM_SUBCLASS_BANDAGE)) + { + //send use error: not in arena + pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH,pItem,NULL); + return; + } + if (pUser->isInCombat()) { for(int i = 0; i < 5; ++i) @@ -468,7 +477,8 @@ if(((BattleGroundWS*)bg)->GetFlagState(ALLIANCE) != BG_WS_FLAG_STATE_ON_BASE) return; // check if it's correct flag - if(((BattleGroundWS*)bg)->m_BgObjects[BG_WS_OBJECT_A_FLAG] != obj->GetGUID()) + if(!(((BattleGroundWS*)bg)->m_BgObjects[BG_WS_OBJECT_A_FLAG]) || + (((BattleGroundWS*)bg)->m_BgObjects[BG_WS_OBJECT_A_FLAG]->GetGUID() != obj->GetGUID())) return; // check player team if(_player->GetTeam() == ALLIANCE) @@ -483,7 +493,8 @@ if(((BattleGroundWS*)bg)->GetFlagState(HORDE) != BG_WS_FLAG_STATE_ON_BASE) return; // check if it's correct flag - if(((BattleGroundWS*)bg)->m_BgObjects[BG_WS_OBJECT_H_FLAG] != obj->GetGUID()) + if(!(((BattleGroundWS*)bg)->m_BgObjects[BG_WS_OBJECT_H_FLAG]) || + (((BattleGroundWS*)bg)->m_BgObjects[BG_WS_OBJECT_H_FLAG]->GetGUID() != obj->GetGUID())) return; // check player team if(_player->GetTeam() == HORDE) Index: src/game/Unit.cpp =================================================================== --- src/game/Unit.cpp (revision 5306) +++ src/game/Unit.cpp (working copy) @@ -566,36 +566,6 @@ if (health <= damage) { - // battleground things - if(pVictim->GetTypeId() == TYPEID_PLAYER && (((Player*)pVictim)->InBattleGround())) - { - Player *killed = ((Player*)pVictim); - Player *killer = NULL; - if(GetTypeId() == TYPEID_PLAYER) - killer = ((Player*)this); - else if(GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet()) - { - Unit *owner = GetOwner(); - if(owner && owner->GetTypeId() == TYPEID_PLAYER) - killer = ((Player*)owner); - } - - if(killer) - { - if(BattleGround *bg = killed->GetBattleGround()) - { - bg->HandleKillPlayer(killed, killer); // drop flags and etc - // add +1 deaths - bg->UpdatePlayerScore(killed, SCORE_DEATHS, 1); - if(killer) - // add +1 kills - bg->UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1); - // to be able to remove insignia - killed->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE ); - } - } - } - DEBUG_LOG("DealDamage: victim just died"); DEBUG_LOG("DealDamageAttackStop"); @@ -839,6 +809,39 @@ he->DuelComplete(DUEL_INTERUPTED); } + + // battleground things (do this at the end, so the death state flag will be properly set to handle in the bg->handlekill) + if(pVictim->GetTypeId() == TYPEID_PLAYER && (((Player*)pVictim)->InBattleGround())) + { + Player *killed = ((Player*)pVictim); + Player *killer = NULL; + if(GetTypeId() == TYPEID_PLAYER) + killer = ((Player*)this); + else if(GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet()) + { + Unit *owner = GetOwner(); + if(owner && owner->GetTypeId() == TYPEID_PLAYER) + killer = ((Player*)owner); + } + + if(killer) + { + if(BattleGround *bg = killed->GetBattleGround()) + { + bg->HandleKillPlayer(killed, killer); // drop flags and etc + // add +1 deaths + bg->UpdatePlayerScore(killed, SCORE_DEATHS, 1); + if(killer) + { + // add +1 kills + bg->UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1); + bg->UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1); + } + // to be able to remove insignia + killed->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE ); + } + } + } } else // if (health <= damage) { @@ -3900,6 +3903,19 @@ } } +void Unit::RemoveArenaAuras(bool onleave) +{ + // in join, remove positive buffs, on end, remove negative + // used to remove positive visible auras in arenas + for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();) + { + if (!iter->second->IsPassive() && (iter->second->IsPositive() ^ onleave)) + RemoveAura(iter, true); + else + ++iter; + } +} + void Unit::RemoveAllAurasOnDeath() { // used just after dieing to remove all visible auras Index: src/game/Unit.h =================================================================== --- src/game/Unit.h (revision 5306) +++ src/game/Unit.h (working copy) @@ -819,6 +819,7 @@ void RemoveAreaAurasByOthers(uint64 guid = 0); void RemoveAllAuras(); + void RemoveArenaAuras(bool onleave = false); void RemoveAllAurasOnDeath(); void DelayAura(uint32 spellId, uint32 effindex, int32 delaytime); bool SetAurDuration(uint32 spellId, uint32 effindex, uint32 duration); Index: src/game/World.cpp =================================================================== --- src/game/World.cpp (revision 5306) +++ src/game/World.cpp (working copy) @@ -491,6 +491,10 @@ m_configs[CONFIG_TALENTS_INSPECTING] = sConfig.GetBoolDefault("TalentsInspecting", true); m_configs[CONFIG_CHAT_FAKE_MESSAGE_PREVENTING] = sConfig.GetBoolDefault("ChatFakeMessagePreventing", false); + m_configs[CONFIG_ARENA_MAX_RATING_DIFFERENCE] = sConfig.GetIntDefault("Arena.MaxRatingDifference", 0); + m_configs[CONFIG_ARENA_RATING_DISCARD_TIMER] = sConfig.GetIntDefault("Arena.RatingDiscardTimer",300000); + m_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS] = sConfig.GetBoolDefault("Arena.AutoDistributePoints", false); + m_VisibleUnitGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Unit", 1); if(m_VisibleUnitGreyDistance > MAX_VISIBILITY_DISTANCE) { @@ -827,6 +831,7 @@ ///- Initialize Battlegrounds sLog.outString( "Starting BattleGround System" ); sBattleGroundMgr.CreateInitialBattleGrounds(); + sBattleGroundMgr.InitAutomaticArenaPointDistribution(); //Not sure if this can be moved up in the sequence (with static data loading) as it uses MapManager sLog.outString( "Loading Transports..." ); Index: src/game/World.h =================================================================== --- src/game/World.h (revision 5306) +++ src/game/World.h (working copy) @@ -118,6 +118,9 @@ CONFIG_TALENTS_INSPECTING, CONFIG_CHAT_FAKE_MESSAGE_PREVENTING, CONFIG_TCP_NO_DELAY, + CONFIG_ARENA_MAX_RATING_DIFFERENCE, + CONFIG_ARENA_RATING_DISCARD_TIMER, + CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS, CONFIG_VALUE_COUNT }; Index: src/mangosd/mangosd.conf.in =================================================================== --- src/mangosd/mangosd.conf.in (revision 5306) +++ src/mangosd/mangosd.conf.in (working copy) @@ -538,3 +538,16 @@ # Default: 0 (false) # 1 (true) Event.Announce = 0 + +# Rated arena matches config +# MaxRatingDifference: the maximum rating difference between two groups in rated matches +# Default: 0 (disable, rating difference is discarded) +# RatingDiscardTimer: after the specified milliseconds has passed, rating information will be discarded when selecting teams for matches +# also initiates an update by this timer +# Default: 60000 +# AutoDistributePoints: set if arena points should be distributed weekly automatically, or by GM command +# Default: 0 (disable) (recommended): use gm command or sql query to distribute the points +# 1 (enable): arena points are distributed automatically +Arena.MaxRatingDifference = 0 +Arena.RatingDiscardTimer = 60000 +Arena.AutoDistributePoints = 0 Index: src/shared/Database/DBCfmt.cpp =================================================================== --- src/shared/Database/DBCfmt.cpp (revision 5306) +++ src/shared/Database/DBCfmt.cpp (working copy) @@ -42,7 +42,8 @@ const char GtRegenHPPerSptfmt[]="f"; const char GtRegenMPPerSptfmt[]="f"; const char ItemDisplayTemplateEntryfmt[]="nxxxxxxxxxxixxxxxxxxxxx"; -const char ItemExtendedCostEntryfmt[]="niiiiixxiiixxx"; +const char ItemCondExtCostsEntryfmt[]="xiii"; +const char ItemExtendedCostEntryfmt[]="niiiiixxiiixxi"; const char ItemRandomPropertiesfmt[]="nxiiixxxxxxxxxxxxxxxxxxx"; const char ItemRandomSuffixfmt[]="nxxxxxxxxxxxxxxxxxxiiiiii"; const char ItemSetEntryfmt[]="dssssssssssssssssxxxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiiiii"; Index: src/shared/Database/DBCStores.cpp =================================================================== --- src/shared/Database/DBCStores.cpp (revision 5306) +++ src/shared/Database/DBCStores.cpp (working copy) @@ -66,6 +66,7 @@ DBCStorage sItemSetStore(ItemSetEntryfmt); //DBCStorage sItemDisplayInfoStore(ItemDisplayTemplateEntryfmt); -- not used currently +DBCStorage sItemCondExtCostsStore(ItemCondExtCostsEntryfmt); DBCStorage sItemExtendedCostStore(ItemExtendedCostEntryfmt); DBCStorage sItemRandomPropertiesStore(ItemRandomPropertiesfmt); DBCStorage sItemRandomSuffixStore(ItemRandomSuffixfmt); @@ -170,7 +171,7 @@ { std::string tmpPath=""; - const uint32 DBCFilesCount = 48; + const uint32 DBCFilesCount = 49; barGoLink bar( DBCFilesCount ); @@ -230,6 +231,7 @@ LoadDBC(bar,bad_dbc_files,sGtRegenMPPerSptStore, dataPath+"dbc/gtRegenMPPerSpt.dbc"); //LoadDBC(bar,bad_dbc_files,sItemDisplayInfoStore, dataPath+"dbc/ItemDisplayInfo.dbc"); -- not used currently + LoadDBC(bar,bad_dbc_files,sItemCondExtCostsStore, dataPath+"dbc/ItemCondExtCosts.dbc"); LoadDBC(bar,bad_dbc_files,sItemExtendedCostStore, dataPath+"dbc/ItemExtendedCost.dbc"); LoadDBC(bar,bad_dbc_files,sItemRandomPropertiesStore,dataPath+"dbc/ItemRandomProperties.dbc"); LoadDBC(bar,bad_dbc_files,sItemRandomSuffixStore, dataPath+"dbc/ItemRandomSuffix.dbc"); @@ -542,6 +544,19 @@ return sTalentTabPages[cls]; } +uint32 GetItemCondExtCostsMask(uint32 requiredarenarank, uint32 itemextendedcost) +{ + // create and return mask for (RequiredArenaRank, ItemExtendedCost) pair + uint32 mask = 0; + for(uint32 i = 0; i < sItemCondExtCostsStore.nCount; ++i) + { + ItemCondExtCostsEntry const* icece = sItemCondExtCostsStore.LookupEntry(i); + if(icece && icece->requiredarenarank == requiredarenarank && icece->itemextendedcostentry == itemextendedcost) + mask |= (1 << icece->requirementtype); + } + return mask; +} + // script support functions MANGOS_DLL_SPEC DBCStorage const* GetSpellStore() { return &sSpellStore; } MANGOS_DLL_SPEC DBCStorage const* GetSpellRangeStore() { return &sSpellRangeStore; } Index: src/shared/Database/DBCStores.h =================================================================== --- src/shared/Database/DBCStores.h (revision 5306) +++ src/shared/Database/DBCStores.h (working copy) @@ -44,6 +44,7 @@ uint32 GetTalentInspectBitPosInTab(uint32 talentId); uint32 GetTalentTabInspectBitSize(uint32 talentTabId); uint32 const* /*[3]*/ GetTalentTabPages(uint32 cls); +uint32 GetItemCondExtCostsMask(uint32 requiredarenarank, uint32 itemextendedcost); template class DBCStorage Index: src/shared/Database/DBCStructure.h =================================================================== --- src/shared/Database/DBCStructure.h (revision 5306) +++ src/shared/Database/DBCStructure.h (working copy) @@ -283,6 +283,16 @@ uint32 randomPropertyChance; }; +struct ItemCondExtCostsEntry +{ + // uint32 ID; + uint32 requiredarenarank; // ItemPrototype::RequiredArenaRank + uint32 itemextendedcostentry; // ItemPrototype::ExtendedCost + uint32 requirementtype; // 1 - honor and/or arena point from itemextendedcost + // 2 - required item(s) from itemextendedcost + // 3 - required personal arena rating from itemextendedcost +}; + struct ItemExtendedCostEntry { uint32 ID; // 0 extended-cost entry id @@ -296,7 +306,7 @@ uint32 reqitemcount2; // 9 required count of 2st item uint32 reqitemcount3; // 10 required count of 3st item // 11-12 not used, always 0 (4-5 item count?) - // 13 not used + uint32 reqpersonalarenarating; // 13 required personal arena rating }; struct ItemRandomPropertiesEntry