Index: src/game/ArenaTeam.cpp =================================================================== --- src/game/ArenaTeam.cpp (revision 5703) +++ 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; @@ -130,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)); @@ -138,20 +139,52 @@ { pl->SetInArenaTeam(Id, GetSlot()); pl->SetArenaTeamIdInvited(0); + // personal rating + pl->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6) + 5, 1500); } - else +/* else//it's a no-no, and you like it { 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 if(CaptainGuid != PlayerGuid) { if(pl) pl->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1); - else - Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1, PlayerGuid); +// else// don't bother +// Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1, PlayerGuid); } + + // what an ugly hack, please replace it with some beautiful code + // for example, make setuint32valuesindb safe to use + Tokens tokens; + if(!Player::LoadValuesArrayFromDB(tokens,PlayerGuid)) + return false; + + // arena team id + uint16 index = PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6); + char buf[11]; + snprintf(buf,11,"%u",Id); + tokens[index] = buf; + // pers rating + index = PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6) + 5; + buf[11]; + snprintf(buf,11,"%u",1500); + tokens[index] = buf; + // hide promote/remove buttons + if(CaptainGuid != PlayerGuid) + { + index = PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6); + buf[11]; + snprintf(buf,11,"%u",1); + tokens[index] = buf; + } + + Player::SaveValuesArrayInDB(tokens,PlayerGuid); + return true; } @@ -207,7 +240,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; @@ -221,6 +254,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; @@ -275,17 +309,50 @@ } Player *player = objmgr.GetPlayer(guid); + // this will be ugly. because of the asynchronous sql handling, we have to set all the fields of the player at once, and save them at once, or else the save will only modify the last field. + // rip off of setuint32valueindb if(player) { 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); + } } + + // what an ugly hack, please replace it with some beautiful code + // for example, make setuint32valuesindb safe to use + Tokens tokens; + if(!Player::LoadValuesArrayFromDB(tokens,guid)) + return; + + for(int i = 0; i < 6; ++i) + { + uint16 index = PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6) + i; + char buf[11]; + snprintf(buf,11,"%u",0); + tokens[index] = buf; + } + + Player::SaveValuesArrayInDB(tokens,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) + { + uint16 index = PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6) + i; + sLog.outDebug("setting index %u",index); + Player::SetUInt32ValueInDB(index, 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) @@ -312,6 +379,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); @@ -341,7 +409,7 @@ data << uint32(itr->wons_week); // wins this week data << uint32(itr->played_season); // played this season data << uint32(itr->wons_season); // wins this season - data << uint32(0); // personal rating? + data << uint32(pl->GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + GetSlot() * 6 + 5)); // personal rating? } else { @@ -355,7 +423,7 @@ data << uint32(itr->wons_week); // wins this week data << uint32(itr->played_season); // played this season data << uint32(itr->wons_season); // wins this season - data << uint32(0); // personal rating? + data << uint32(Player::GetUInt32ValueFromDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + GetSlot() * 6 + 5, itr->guid)); // personal rating? } } session->SendPacket(&data); @@ -390,6 +458,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); @@ -397,10 +477,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); // 2.3.3 personal rating? + 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(Player::GetUInt32ValueFromDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + GetSlot() * 6 + 5, guid)); // unk, 2.3.3 (personal rating?) + session->SendPacket(&data); } @@ -477,24 +567,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)floor(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)ceil(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)ceil(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)floor(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 = ceil(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 5703) +++ 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 5703) +++ src/game/ArenaTeamHandler.cpp (working copy) @@ -180,7 +180,7 @@ return; } - if(_player->GetArenaTeamId(at->GetSlot())) + if(_player->GetArenaTeamIdFromDB(_player->GetGUIDLow(), at->GetType())) { // already in arena team that size return; Index: src/game/BattleGround.cpp =================================================================== --- src/game/BattleGround.cpp (revision 5703) +++ 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,48 @@ 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); + sLog.outDebug("--- %u ; %u ; %d ; %d ; %u ; %u ---",winner_rating,loser_rating,winner_chance,loser_chance,winner_change,loser_change); + 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 +470,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; RewardMark(plr,ITEM_WINNER_COUNT); @@ -402,10 +518,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_UNIVERSAL, Source->GetGUID(), winmsg); @@ -528,11 +659,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); } @@ -547,6 +679,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); @@ -557,41 +693,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 + } } } @@ -600,11 +798,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); } @@ -612,13 +811,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 @@ -630,6 +845,8 @@ SetStartTime(0); SetEndTime(0); SetLastResurrectTime(0); + SetArenaType(0); + SetRated(false); m_Events = 0; @@ -638,11 +855,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(); } @@ -653,51 +871,66 @@ SetLastResurrectTime(0); - /// 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)]; - - BattleGround *bg = sBattleGroundMgr.GetBattleGround(arena_type); - if(!bg) - return; - - bg->SetStatus(STATUS_WAIT_JOIN); - - bg->SetStartTime(0); - bg->SetLastResurrectTime(0); - } + WorldPacket data; + BattleGround *bg = NULL; } -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 @@ -707,13 +940,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) @@ -721,7 +961,7 @@ sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr); return; } - }*/ + } } /* @@ -744,6 +984,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 { @@ -770,9 +1035,15 @@ itr->second->HonorableKills += value; break; case SCORE_BONUS_HONOR: // Honor bonus - // reward honor instantly - Source->RewardHonor(NULL, 1, value); - itr->second->BonusHonor += value; + // do not add honor in arenas + if(isBattleGround()) + { + // reward honor instantly + Source->RewardHonor(NULL, 1, value); + itr->second->BonusHonor += value; + } + else + break; //used only in EY, but in MSG_PVP_LOG_DATA opcode case SCORE_DAMAGE_DONE: // Damage Done @@ -828,15 +1099,19 @@ 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); + uint32 guid = go->GetGUIDLow(); + // this shit is needed (? orly? everything worked fine until UseDoorOrButton() :S hopefully adding this won't insert the go in the gameobjects table), else it crashes when a door/button is used because of calling usedoororbuton with 0 param, thus accessing the go data directly, and unchecked. gratz to whoever thought that function is soooo extremely kewl. GameObjectData& data = objmgr.NewGOData(guid); data.id = entry; @@ -852,10 +1127,13 @@ data.spawntimesecs = spawntime; data.animprogress = 100; data.go_state = 1; - objmgr.AddGameobjectToGrid(guid, &data); - m_BgObjects[type] = MAKE_NEW_GUID(guid, entry, HIGHGUID_GAMEOBJECT); - + // add to world, so it can be later looked up from HashMapHolder + // (ofc, this could be easily omitted if the bg objects were stored as pointers instead of guids, but some guys just can't understand that it's as safe as the guid method (except of course the usedoororbutton function which imo is a joke), and doesn't require this silly AddToWorld hack, which obviously should be called from map::add) + // strange thing is, that the pointer method is in svn in battlegroundab, noone seems to worry about that though... + // consistency please + go->AddToWorld(); + m_BgObjects[type] = go->GetGUID(); return true; } @@ -868,7 +1146,7 @@ } else { - sLog.outError("Object not found"); + sLog.outError("Battleground: DoorOpen: Object not found"); } } @@ -880,28 +1158,29 @@ if(obj) { //obj->Respawn(); // bugged + obj->SetInstanceId(GetInstanceID()); obj->SetRespawnTime(0); //objmgr.SaveGORespawnTime(obj->GetGUIDLow(), 0, 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]); if(obj) { + obj->SetInstanceId(GetInstanceID()); obj->SetRespawnTime(respawntime); obj->SetLootState(GO_JUST_DEACTIVATED); + 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); @@ -912,17 +1191,44 @@ pCreature->AIM_Initialize(); //pCreature->SetDungeonDifficulty(0); - MapManager::Instance().GetMap(pCreature->GetMapId(), pCreature)->Add(pCreature); + // see rambling in AddGO. It is needed. + pCreature->AddToWorld(); m_BgCreatures[type] = pCreature->GetGUID(); + return pCreature; } +void BattleGround::SpawnBGCreature(uint32 type, uint32 respawntime) +{ + if(respawntime == 0) + { + Creature *obj = HashMapHolder::Find(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 = HashMapHolder::Find(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]); if(!cr) { - sLog.outError("Can't find creature guid: %u",m_BgCreatures[type]); + sLog.outError("Can't find creature."); return false; } cr->CleanupsBeforeDelete(); @@ -936,7 +1242,7 @@ GameObject *obj = HashMapHolder::Find(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 @@ -1025,3 +1331,28 @@ obj->SetRespawnTime(BUFF_RESPAWN_TIME); obj->SetLootState(GO_JUST_DEACTIVATED); } + +// 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 5703) +++ src/game/BattleGround.h (working copy) @@ -58,7 +58,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 @@ -122,6 +126,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, @@ -201,6 +217,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 @@ -271,6 +288,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; } @@ -338,6 +356,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) @@ -346,6 +365,12 @@ ++m_PlayersCount[GetTeamIndexByTeamId(Team)]; } + // used for rated arena battles + void SetArenaTeamIdForTeam(uint32 Team, uint32 ArenaTeamId) { m_ArenaTeamIds[GetTeamIndexByTeamId(Team)] = ArenaTeamId; } + uint32 GetArenaTeamIdForTeam(uint32 Team) const { return m_ArenaTeamIds[GetTeamIndexByTeamId(Team)]; } + void SetArenaTeamRatingChangeForTeam(uint32 Team, int32 RatingChange) { m_ArenaTeamRatingChanges[GetTeamIndexByTeamId(Team)] = RatingChange; } + int32 GetArenaTeamRatingChangeForTeam(uint32 Team) const { return m_ArenaTeamRatingChanges[GetTeamIndexByTeamId(Team)]; } + /* Triggers handle */ // must be implemented in BG subclass virtual void HandleAreaTrigger(Player* /*Source*/, uint32 /*Trigger*/) {} @@ -354,7 +379,7 @@ // 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: @@ -363,8 +388,9 @@ 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); @@ -374,6 +400,11 @@ void HandleTriggerBuff(uint64 const& go_guid); + // 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(); @@ -402,6 +433,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 @@ -427,6 +460,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 5703) +++ 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 5703) +++ 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 5703) +++ 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,10 @@ + + + + + + + Index: src/game/BattleGroundAB.h =================================================================== --- src/game/BattleGroundAB.h (revision 5703) +++ 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 5703) +++ 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 5703) +++ 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 5703) +++ 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,18 +103,30 @@ }*/ } -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; m_PlayerScores[plr->GetGUID()] = sc; + + UpdateWorldState(0x9f1, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0x9f0, GetAlivePlayersCountByTeam(HORDE)); } void BattleGroundBE::RemovePlayer(Player *plr, uint64 guid) { + if(GetStatus() == STATUS_WAIT_LEAVE) + return; + UpdateWorldState(0x9f1, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0x9f0, GetAlivePlayersCountByTeam(HORDE)); + + if(!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } void BattleGroundBE::HandleKillPlayer(Player *player, Player *killer) @@ -123,15 +140,19 @@ return; } - uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam()); + UpdateWorldState(0x9f1, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0x9f0, GetAlivePlayersCountByTeam(HORDE)); - ++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) @@ -160,10 +181,16 @@ // HandleTriggerBuff(buff_guid,Source); } +void BattleGroundBE::FillInitialWorldStates(WorldPacket &data) +{ + data << uint32(0x9f1) << uint32(GetAlivePlayersCountByTeam(ALLIANCE)); // 7 + data << uint32(0x9f0) << uint32(GetAlivePlayersCountByTeam(HORDE)); // 8 + data << uint32(0x9f3) << uint32(1); // 9 +} + 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 5703) +++ src/game/BattleGroundBE.h (working copy) @@ -58,18 +58,16 @@ 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(); void ResetBGSubclass(); + virtual void FillInitialWorldStates(WorldPacket &d); void HandleKillPlayer(Player* player, Player *killer); /* 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 5703) +++ 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 5703) +++ 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 5703) +++ 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,11 +81,17 @@ recv_data >> instanceId; // instance id, 0 if First Available selected recv_data >> joinAsGroup; // join as group - sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from: " I64FMT " for BG (Type: %u)", guid, bgTypeId); - - if(bgTypeId >= MAX_BATTLEGROUND_TYPES) // cheating? + 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; @@ -103,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); @@ -123,12 +152,12 @@ _player->GetSession()->SendPacket(&data); continue; } - if (member->InBattleGroundQueueForBattleGroundType(bgTypeId)) + 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 @@ -137,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->InBattleGroundQueueForBattleGroundType(bgTypeId)) + 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; @@ -162,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()); } } @@ -241,13 +274,12 @@ uint32 bgTypeId; recv_data >> bgTypeId; // id from DBC - if(bgTypeId >= MAX_BATTLEGROUND_TYPES) // cheating? + if(bgTypeId >= MAX_BATTLEGROUND_TYPES) + { + sLog.outError("Battleground: invalid bgtype received."); return; + } - // can't be received if player not in BG queue - if(!_player->InBattleGroundQueueForBattleGroundType(bgTypeId)) - return; - BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId); if(!bl) @@ -264,65 +296,165 @@ 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; - if(bgTypeId >= MAX_BATTLEGROUND_TYPES) // cheating? + 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; + } - if(!_player->InBattleGroundQueueForBattleGroundType(bgTypeId)) + 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; - BattleGround *bg = sBattleGroundMgr.GetBattleGround(bgTypeId); - if(!bg) + // if action == 1, then instanceId is _required_ + if(!instanceId && action == 1) + { + sLog.outError("Battleground: instance not found."); return; + } - uint32 queueSlot = 0; - WorldPacket data; - switch(action) + 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) { - case 1: // port to battleground - if(!_player->IsInvitedForBattleGroundType(bgTypeId)) - return; // cheating? + sLog.outError("Battleground: bg not found."); + return; + } - _player->RemoveFromGroup(); - queueSlot = _player->GetBattleGroundQueueIndex(bgTypeId); - 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() ) - { - 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 (BattleGround *currentBg = _player->GetBattleGround()) - currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true); + bgTypeId = bg->GetTypeID(); - _player->SetBattleGroundId(bg->GetTypeID()); - sBattleGroundMgr.SendToBattleGround(_player, bgTypeId); - bg->AddPlayer(_player); - 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 - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_NONE, 0, 0); - sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].RemovePlayer(_player->GetGUID(), true); - SendPacket(&data); - break; - default: - sLog.outError("Battleground port: unknown action %u", action); - break; + 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 + if(!_player->IsInvitedForBattleGroundQueueType(bgQueueTypeId)) + return; // cheating? + _player->RemoveFromGroup(); + 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[bgQueueTypeId].RemovePlayer(_player->GetGUID(), false); + // if player is in battleground already, remove him and port him to new battleground + if ( _player->InBattleGround() ) + { + BattleGround *currentBg = _player->GetBattleGround(); + if (currentBg) + currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true); + } + _player->SetBattleGroundId(bg->GetInstanceID()); + sBattleGroundMgr.SendToBattleGround(_player, instanceId, team); + bg->AddPlayer(_player,team); + break; + case 0: // leave queue + 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[bgQueueTypeId].RemovePlayer(_player->GetGUID(), true); + SendPacket(&data); + break; + default: + sLog.outError("Battleground port: unknown action %u", action); + break; + } } } @@ -357,7 +489,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()); @@ -365,15 +498,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); } } @@ -384,16 +527,36 @@ // we should update all queues? .. i'm not sure if this code is correct for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - if(uint32 queue_id = _player->GetBattleGroundQueueId(i)) + uint32 queue_id = _player->GetBattleGroundQueueId(i); + 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) { - if(BattleGround *bg = sBattleGroundMgr.GetBattleGround(queue_id)) - { - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0); - SendPacket(&data); - } + 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, arenatype, isRated); + SendPacket(&data); + } } } +/* else // not sure if it needed... + { + for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + { + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, NULL, _player->GetTeam(),i , STATUS_NONE, 0, 0); + SendPacket(&data); + } + }*/ } void WorldSession::HandleAreaSpiritHealerQueryOpcode( WorldPacket & recv_data ) @@ -453,12 +616,6 @@ if(_player->InBattleGround()) return; - for(int qId = 0; qId < PLAYER_MAX_BATTLEGROUND_QUEUES; ++qId) - { - if(_player->GetBattleGroundQueueId(qId) != 0) - return; - } - uint64 guid; // arena Battlemaster guid uint8 type; // 2v2, 3v3 or 5v5 uint8 asGroup; // asGroup @@ -473,6 +630,7 @@ return; uint8 arenatype = 0; + uint32 arenaRating = 0; switch(type) { @@ -490,26 +648,72 @@ return; } - 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; + } - bg->SetArenaType(arenatype); - bg->SetRated(isRated); + 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(); @@ -519,11 +723,19 @@ //player has deserter aura .. do nothing */ - if (member->InBattleGroundQueueForBattleGroundType(BATTLEGROUND_AA)) + 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; @@ -533,16 +745,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 { @@ -550,11 +763,13 @@ //player has deserter aura .. do nothing */ - if (_player->InBattleGroundQueueForBattleGroundType(BATTLEGROUND_AA)) + 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; @@ -564,12 +779,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 5703) +++ 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,157 +506,310 @@ } //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 - for (BGFreeSlotQueueType::iterator 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); - 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 instantiated */ - /* if (there is enough 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) */ - - /* following 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); - 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) ) + { + // which side has enough players? + uint32 side = 0; + SelectionPoolBuildMode mode1, mode2; + // find out what pools are we using + if(bOneSideAllyTeam1 && bOneSideAllyTeam2) { - 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())) + side = ALLIANCE; + mode1 = ONESIDE_ALLIANCE_TEAM1; + mode2 = ONESIDE_ALLIANCE_TEAM2; + } + else { - // iterator to player's queue status - QueuedPlayersMap::iterator itrPlayerStatus = m_QueuedPlayers[queue_id].find(*itr2); + side = HORDE; + mode1 = ONESIDE_HORDE_TEAM1; + mode2 = ONESIDE_HORDE_TEAM2; + } - // remove him from time queue - itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); + // extra check in rated case: the opposing groups must not be in the same arena team + if(isRated) + { + if( Player::GetArenaTeamIdFromDB((*m_SelectionPools[mode1].SelectedGroups.begin())->Players.begin()->first,arenatype) == + Player::GetArenaTeamIdFromDB((*m_SelectionPools[mode2].SelectedGroups.begin())->Players.begin()->first,arenatype) ) + return; + } - // 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; + // 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])) ) + { + sLog.outError("Could not create arena."); + return; + } - //check if player is not already invited - if (!itrPlayerStatus->second.IsInvitedToBGInstanceGUID) - { - itrPlayerStatus->second.IsInvitedToBGInstanceGUID = bg2->GetInstanceID(); - itrPlayerStatus->second.InviteTime = getMSTime(); - itrPlayerStatus->second.LastInviteTime = getMSTime(); + sLog.outDebug("Arena created."); + // init stats + 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; + } - if(itrPlayerStatus->second.Team == ALLIANCE) - --m_QueuedPlayers[queue_id].Alliance; - else - --m_QueuedPlayers[queue_id].Horde; + bg2->SetRated(isRated); - sBattleGroundMgr.InvitePlayer(plr, bg2->GetInstanceID()); + // assigned team of the other group + uint32 other_side; + if(side == ALLIANCE) + other_side = HORDE; + else + other_side = 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); - } + // start the joining of the bg + bg2->SetStatus(STATUS_WAIT_JOIN); + bg2->SetQueueType(queue_id); + // initialize arena / rating info + bg2->SetArenaType(arenatype); + + std::list::iterator itr; + + // 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); } - else - ++itr2; + + // 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); + } + + bg2->StartBattleGround(0); } - bg2->StartBattleGround(0); } } @@ -334,14 +837,19 @@ uint32 queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID()); 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_iterator qItr = qpMap.find(m_PlayerGuid); - if (qItr != qpMap.end() && qItr->second.IsInvitedToBGInstanceGUID == m_BgInstanceGUID) + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); + if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue { - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME/2, 0); - plr->GetSession()->SendPacket(&data); + // 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[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()]; + BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid); + if (qItr != qpMap.end() && qItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID) + { + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, qItr->second.GroupInfo->Team, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME/2, 0); + plr->GetSession()->SendPacket(&data); + } } } return true; //event will be deleted @@ -372,18 +880,21 @@ uint32 queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID()); if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue (base at player data { - // 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_iterator qItr = qpMap.find(m_PlayerGuid); - if (qItr!=qpMap.end() && qItr->second.IsInvitedToBGInstanceGUID == m_BgInstanceGUID) + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); + if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue { - plr->RemoveBattleGroundQueueId(bg->GetTypeID()); - sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].RemovePlayer(m_PlayerGuid, true); - sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].Update(bg->GetTypeID(), bg->GetQueueType()); - - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr->GetTeam(), queueSlot, STATUS_NONE, 0, 0); - plr->GetSession()->SendPacket(&data); + // 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::iterator qMapItr = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()].find(m_PlayerGuid); + if (qMapItr != sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()].end() && qMapItr->second.GroupInfo && qMapItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID) + { + 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, m_PlayersTeam, queueSlot, STATUS_NONE, 0, 0); + plr->GetSession()->SendPacket(&data); + } } } @@ -404,22 +915,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) @@ -433,10 +1000,55 @@ 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 // uint64 in client - *data << uint64( uint64(bg->GetArenaType()) | (uint64(0x0D) << 8) | (uint64(bg->GetTypeID()) << 16) | (uint64(0x1F90) << 48) ); + *data << uint64( uint64(arenatype ? arenatype : bg->GetArenaType()) | (uint64(0x0D) << 8) | (uint64(bg->GetTypeID()) << 16) | (uint64(0x1F90) << 48) ); *data << uint32(0); // unknown // alliance/horde for BG and skirmish/rated for Arenas - *data << uint8(bg->isArena() ? (bg->isRated() ? 1 : 0) : bg->GetTeamIndexByTeamId(team)); + *data << uint8(bg->isArena() ? ( israted ? israted : bg->isRated() ) : bg->GetTeamIndexByTeamId(team)); +/* *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: + *data << uint8(1); + break; + case BATTLEGROUND_WS: + *data << uint8(2); + break; + case BATTLEGROUND_AB: + *data << uint8(3); + break; + case BATTLEGROUND_NA: + *data << uint8(4); + break; + case BATTLEGROUND_BE: + *data << uint8(5); + break; + case BATTLEGROUND_AA: + *data << uint8(6); + break; + case BATTLEGROUND_EY: + *data << uint8(7); + break; + case BATTLEGROUND_RL: + *data << uint8(8); + break; + default: // unknown + *data << uint8(0); + break; + } + + if(bg->isArena() && (StatusID == STATUS_WAIT_QUEUE)) + *data << uint32(BATTLEGROUND_AA); // all arenas I don't think so. + else + *data << uint32(bg->GetTypeID()); // BG id from DBC + + *data << uint16(0x1F90); // unk value 8080 + *data << uint32(bg->GetInstanceID()); // instance id + + if(bg->isBattleGround()) + *data << uint8(bg->GetTeamIndexByTeamId(team)); // team + else + *data << uint8(israted?israted:bg->isRated()); // is rated battle +*/ *data << uint32(StatusID); // status switch(StatusID) { @@ -468,11 +1080,36 @@ *data << uint8(type); // seems to be type (battleground=0/arena=1) if(type) // arena { - for(uint8 i = 0; i < 2; i++) + // it seems this must be according to BG_WINNER_A/H and _NOT_ BG_TEAM_A/H + for(int i = 1; i >= 0; --i) { - *data << uint32(3000+1+i); // rating change: showed value - 3000 - *data << uint32(0); // 2.4.0, unknown - *data << uint8(0); // some unknown string + + uint32 at_id = bg->m_ArenaTeamIds[i]; + ArenaTeam * at = objmgr.GetArenaTeamById(at_id); + sLog.outDebug("rating change: %d", bg->m_ArenaTeamRatingChanges[i]); + /* + alright, this is crazy. seems like an at least 2-variable function in the client + results with a few tests: sent values (winner;loser), displayed values + (if the current rating of the teams is taken into account too, then this is completely useless) + (3000,3000)->(0,-31) + (1500,1500)->(0,-31) + (0,0)->(16,0) + (32,32)->(14,-17) + (28,27)->(13,-8) + (27,21)->(14,-5) + (26,22)->(14,-6) + (25,23)->(14,-7) + (24,24)->(14,-8) + (23,25)->(14,-9) + (22,26)->(14,-10) + (10,38)->(-----,-----) (no rating showed up) + */ + *data << uint32(0); // rating change: showed value - 3000 + *data << uint32(0); + if(at) + *data << at->GetName(); + else//*/ + *data << (uint8)0; } } @@ -492,32 +1129,36 @@ { *data << (uint64)itr->first; *data << (int32)itr->second->KillingBlows; - if(type) + Player *plr = objmgr.GetPlayer(itr->first); + uint32 team = bg->GetPlayerTeam(itr->first); + if(!team && plr) team = plr->GetTeam(); + if(type == 0) { + *data << (int32)itr->second->HonorableKills; + *data << (int32)itr->second->Deaths; + *data << (int32)(itr->second->BonusHonor*10); + } + else + { // that part probably wrong - Player *plr = objmgr.GetPlayer(itr->first); 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); } else *data << uint8(0); } - else + *data << (int32)itr->second->DamageDone; // damage done + *data << (int32)itr->second->HealingDone; // healing done + switch(bg->GetTypeID()) // battleground specific things { - *data << (int32)itr->second->HonorableKills; - *data << (int32)itr->second->Deaths; - *data << (int32)(itr->second->BonusHonor*10); // multiplier not required in 2.4.x? - } - *data << (int32)itr->second->DamageDone; // damage done - *data << (int32)itr->second->HealingDone; // healing done - switch(bg->GetTypeID()) // battleground specific things - { case BATTLEGROUND_AV: *data << (uint32)0x00000005; // count of next fields *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsAssaulted; // GraveyardsAssaulted @@ -540,7 +1181,7 @@ case BATTLEGROUND_BE: case BATTLEGROUND_AA: case BATTLEGROUND_RL: - *data << (int32)0; // 0 + *data << (int32)0; // 0 break; default: sLog.outDebug("Unhandled MSG_PVP_LOG_DATA for BG id %u", bg->GetTypeID()); @@ -590,23 +1231,138 @@ *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); if(!bg) return; + bg->IncreaseInvitedCount(team); + + plr->SetInviteForBattleGroundQueueType(BGQueueTypeId(bg->GetTypeID(),bg->GetArenaType())); - bg->IncreaseInvitedCount(plr->GetTeam()); - plr->SetInviteForBattleGroundType(bg->GetTypeID()); + // set the arena teams for rated matches + if(bg->isArena() && bg->isRated()) + { + switch(bg->GetArenaType()) + { + case ARENA_TYPE_2v2: + sLog.outDebug("setting team id for team %u to id %u, player guid %u",team,plr->GetArenaTeamId(0), plr->GetGUIDLow()); + 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(INVITE_ACCEPT_WAIT_TIME/2)); - 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(INVITE_ACCEPT_WAIT_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 @@ -626,12 +1382,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 @@ -641,7 +1397,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); @@ -650,12 +1406,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() @@ -775,6 +1533,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 `characters` 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 `characters` 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; @@ -810,18 +1648,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) @@ -833,3 +1680,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 5703) +++ src/game/BattleGroundMgr.h (working copy) @@ -34,51 +34,97 @@ #define MAX_BATTLEGROUND_TYPES 9 // each BG type will be in array -struct PlayerQueueInfo +#define MAX_BATTLEGROUND_QUEUE_TYPES 8 + +#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); }; /* @@ -96,7 +142,6 @@ private: uint64 m_PlayerGuid; uint32 m_BgInstanceGUID; - }; /* @@ -116,7 +161,6 @@ uint32 m_PlayersTeam; }; - class BattleGroundMgr { public: @@ -132,18 +176,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()) @@ -152,26 +196,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 5703) +++ 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,18 +100,30 @@ }*/ } -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; m_PlayerScores[plr->GetGUID()] = sc; + + UpdateWorldState(0xa0f, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xa10, GetAlivePlayersCountByTeam(HORDE)); } void BattleGroundNA::RemovePlayer(Player *plr, uint64 guid) { + if(GetStatus() == STATUS_WAIT_LEAVE) + return; + UpdateWorldState(0xa0f, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xa10, GetAlivePlayersCountByTeam(HORDE)); + + if(!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } void BattleGroundNA::HandleKillPlayer(Player *player, Player *killer) @@ -117,15 +137,19 @@ return; } - uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam()); + UpdateWorldState(0xa0f, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xa10, GetAlivePlayersCountByTeam(HORDE)); - ++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) @@ -150,10 +174,16 @@ // HandleTriggerBuff(buff_guid,Source); } +void BattleGroundNA::FillInitialWorldStates(WorldPacket &data) +{ + data << uint32(0xa0f) << uint32(GetAlivePlayersCountByTeam(ALLIANCE)); // 7 + data << uint32(0xa10) << uint32(GetAlivePlayersCountByTeam(HORDE)); // 8 + data << uint32(0xa11) << uint32(1); // 9 +} + void BattleGroundNA::ResetBGSubclass() { - m_TeamKills[BG_TEAM_ALLIANCE] = 0; - m_TeamKills[BG_TEAM_HORDE] = 0; + } bool BattleGroundNA::SetupBattleGround() @@ -162,7 +192,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 5703) +++ 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,13 @@ 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(); + virtual void FillInitialWorldStates(WorldPacket &d); 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 5703) +++ src/game/BattleGroundRL.cpp (working copy) @@ -73,6 +73,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); @@ -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,18 +100,30 @@ }*/ } -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; m_PlayerScores[plr->GetGUID()] = sc; + + UpdateWorldState(0xbb8, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xbb9, GetAlivePlayersCountByTeam(HORDE)); } void BattleGroundRL::RemovePlayer(Player *plr, uint64 guid) { + if(GetStatus() == STATUS_WAIT_LEAVE) + return; + UpdateWorldState(0xbb8, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xbb9, GetAlivePlayersCountByTeam(HORDE)); + + if(!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } void BattleGroundRL::HandleKillPlayer(Player *player, Player *killer) @@ -117,15 +137,19 @@ return; } - uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam()); + UpdateWorldState(0xbb8, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xbb9, GetAlivePlayersCountByTeam(HORDE)); - ++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) @@ -151,17 +175,26 @@ // HandleTriggerBuff(buff_guid,Source); } +void BattleGroundRL::FillInitialWorldStates(WorldPacket &data) +{ + data << uint32(0xbb8) << uint32(GetAlivePlayersCountByTeam(ALLIANCE)); // 7 + data << uint32(0xbb9) << uint32(GetAlivePlayersCountByTeam(HORDE)); // 8 + data << uint32(0xbba) << uint32(1); // 9 +} + 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 5703) +++ 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,13 @@ 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(); + virtual void FillInitialWorldStates(WorldPacket &d); 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 5703) +++ 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); @@ -139,9 +142,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; @@ -553,11 +556,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 5703) +++ src/game/BattleGroundWS.h (working copy) @@ -123,7 +123,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 5703) +++ src/game/Chat.cpp (working copy) @@ -384,6 +384,8 @@ { "castback", SEC_ADMINISTRATOR, &ChatHandler::HandleCastBackCommand, "", NULL }, { "casttarget", SEC_ADMINISTRATOR, &ChatHandler::HandleCastTargetCommand, "", NULL }, { "cometome", SEC_ADMINISTRATOR, &ChatHandler::HandleComeToMeCommand, "", NULL }, + { "flusharenapoints", SEC_ADMINISTRATOR, &ChatHandler::HandleFlushArenaPointsCommand, "", NULL }, + { "rostertest", SEC_ADMINISTRATOR, &ChatHandler::HandleRosterTestCommand, "", NULL }, //! Development Commands { "setvalue", SEC_ADMINISTRATOR, &ChatHandler::HandleSetValue, "", NULL }, Index: src/game/Chat.h =================================================================== --- src/game/Chat.h (revision 5703) +++ src/game/Chat.h (working copy) @@ -333,6 +333,8 @@ bool HandleCastBackCommand(const char *args); bool HandleCastTargetCommand(const char *args); bool HandleComeToMeCommand(const char *args); + bool HandleFlushArenaPointsCommand(const char *args); + bool HandleRosterTestCommand(const char * args); //! Development Commands bool HandleSetValue(const char* args); Index: src/game/ItemPrototype.h =================================================================== --- src/game/ItemPrototype.h (revision 5703) +++ src/game/ItemPrototype.h (working copy) @@ -95,6 +95,7 @@ ITEM_FLAGS_QUEST_OBJECTIVE = 0x00000800, ITEM_FLAGS_CHARTER = 0x00002000, // arena/guild charter ITEM_FLAGS_UNIQUE_EQUIPPED = 0x00080000, + ITEM_FLAGS_USEABLE_IN_ARENA = 0x00200000, ITEM_FLAGS_THROWABLE = 0x00400000, // not used in game for check trow possibility, only for item in game tooltip ITEM_FLAGS_SPECIALUSE = 0x00800000 // last used flag in 2.3.0 }; Index: src/game/Language.h =================================================================== --- src/game/Language.h (revision 5703) +++ src/game/Language.h (working copy) @@ -531,6 +531,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 5703) +++ src/game/Level3.cpp (working copy) @@ -44,6 +44,7 @@ #include "SkillExtraItems.h" #include "SystemConfig.h" #include "Config/ConfigEnv.h" +#include "BattleGroundMgr.h" //reload commands bool ChatHandler::HandleReloadCommand(const char* arg) @@ -4659,3 +4660,92 @@ return true; } + +bool ChatHandler::HandleFlushArenaPointsCommand(const char * /*args*/) +{ + sBattleGroundMgr.DistributeArenaPoints(); + return true; +} + +bool ChatHandler::HandleRosterTestCommand(const char * args) +{ + if(!args) + return false; + + char* carg1 = strtok((char*)args, " "); + if(!carg1) + return false; + uint32 arg1 = atoi(carg1); + char* carg2 = strtok(NULL, " "); + if(!carg2) + return false; + uint32 arg2 = atoi(carg2); + char* carg3 = strtok(NULL, " "); + if(!carg3) + return false; + uint32 arg3 = atoi(carg3); + char* carg4 = strtok(NULL, " "); + if(!carg4) + return false; + uint32 arg4 = atoi(carg4); + char* carg5 = strtok(NULL, " "); + if(!carg5) + return false; + uint32 arg5 = atoi(carg5); + char* carg6 = strtok(NULL, " "); + if(!carg6) + return false; + uint32 arg6 = atoi(carg6); + char* carg7 = strtok(NULL, " "); + if(!carg7) + return false; + uint32 arg7 = atoi(carg7); + char* carg8 = strtok(NULL, " "); + if(!carg8) + return false; + uint32 arg8 = atoi(carg8); + char* carg9 = strtok(NULL, " "); + if(!carg9) + return false; + uint32 arg9 = atoi(carg9); + char* carg10 = strtok(NULL, " "); + if(!carg10) + return false; + uint32 arg10 = atoi(carg10); + char* carg11 = strtok(NULL, " "); + if(!carg11) + return false; + uint32 arg11 = atoi(carg11); + char* carg12 = strtok(NULL, " "); + if(!carg12) + return false; + uint32 arg12 = atoi(carg12); + char* carg13 = strtok(NULL, " "); + if(!carg13) + return false; + uint32 arg13 = atoi(carg13); + + WorldPacket data(SMSG_ARENA_TEAM_ROSTER, 100); + + data << uint8(arg1); // slot + data << uint32(arg2); // members count + data << uint32(arg3); // arena team type? + + data << uint64(arg4); // guid + data << uint8(arg5); // online flag + data << m_session->GetPlayer()->GetName(); // member name + data << uint32(arg6);// unknown + data << uint8(arg7); // unknown, probably level + data << uint8(arg8); // class + data << uint32(arg9); // played this week + data << uint32(arg10); // wins this week + data << uint32(arg11); // played this season + data << uint32(arg12); // wins this season + data << uint32(arg13); // personal rating? + + + m_session->SendPacket(&data); + sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_ROSTER"); + + return true; +} Index: src/game/LootMgr.cpp =================================================================== --- src/game/LootMgr.cpp (revision 5703) +++ src/game/LootMgr.cpp (working copy) @@ -770,7 +770,7 @@ LootItem &item = l.quest_items[qi->index]; if (!qi->is_looted && !item.is_looted) { - b << uint8(l.items.size() + (qi - lv.qlist->begin())); + b << uint8(l.items.size() + (qi - lv.qlist->begin())) << item; b << item; b << uint8(0); // allow loot ++itemsShown; Index: src/game/Map.cpp =================================================================== --- src/game/Map.cpp (revision 5703) +++ 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 @@ -726,8 +730,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 5703) +++ src/game/Map.h (working copy) @@ -151,6 +151,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); @@ -210,7 +212,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)); } @@ -288,6 +290,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 5703) +++ src/game/MapInstanced.cpp (working copy) @@ -44,6 +44,7 @@ { if (!i->second->HavePlayers()) { + 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 5703) +++ 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 5703) +++ src/game/MovementHandler.cpp (working copy) @@ -95,17 +95,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()); } } } @@ -511,3 +515,13 @@ } + + + + + + + + + + Index: src/game/Player.cpp =================================================================== --- src/game/Player.cpp (revision 5703) +++ src/game/Player.cpp (working copy) @@ -308,7 +308,7 @@ m_bgBattleGroundID = 0; for (int j=0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; j++) { - m_bgBattleGroundQueueID[j].bgType = 0; + m_bgBattleGroundQueueID[j].bgQueueType = 0; m_bgBattleGroundQueueID[j].invited = false; } @@ -1342,8 +1342,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; @@ -1373,7 +1373,7 @@ return 0; } -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(); @@ -1536,7 +1536,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 @@ -2725,6 +2726,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()) @@ -5483,6 +5510,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->HasAuraType(SPELL_AURA_NO_PVP_CREDIT)) @@ -5643,8 +5674,9 @@ QueryResult *result = CharacterDatabase.PQuery("SELECT arenateamid FROM arena_team_member WHERE guid='%u'", GUID_LOPART(guid)); if(result) { - // init id to 0, check the arena type before assigning a value to id - uint32 id = 0; + bool found = false; + // init id to find the type of the arenateam + uint32 id = (*result)[0].GetUInt32(); do { QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM arena_team WHERE arenateamid='%u'", id); @@ -5655,13 +5687,13 @@ if(dbtype == type) { // if the type matches, we've found the id - id = (*result)[0].GetUInt32(); + found = true; break; } } } while(result->NextRow()); delete result; - return id; + if(found) return id; } // no arenateam for the specified guid, return 0 return 0; @@ -7300,19 +7332,34 @@ data << uint32(0xa5f) << uint32(0x0); // 35 break; case 3698: // Nagrand Arena - data << uint32(0xa0f) << uint32(0x0); // 7 - data << uint32(0xa10) << uint32(0x0); // 8 - data << uint32(0xa11) << uint32(0x0); // 9 + if (bg && bg->GetTypeID() == BATTLEGROUND_NA) + bg->FillInitialWorldStates(data); + else + { + data << uint32(0xa0f) << uint32(0x0); // 7 + data << uint32(0xa10) << uint32(0x0); // 8 + data << uint32(0xa11) << uint32(0x0); // 9 show + } break; case 3702: // Blade's Edge Arena - data << uint32(0x9f0) << uint32(0x0); // 7 - data << uint32(0x9f1) << uint32(0x0); // 8 - data << uint32(0x9f3) << uint32(0x0); // 9 + if (bg && bg->GetTypeID() == BATTLEGROUND_BE) + bg->FillInitialWorldStates(data); + else + { + data << uint32(0x9f0) << uint32(0x0); // 7 gold + data << uint32(0x9f1) << uint32(0x0); // 8 green + data << uint32(0x9f3) << uint32(0x0); // 9 show + } break; case 3968: // Ruins of Lordaeron - data << uint32(0xbb8) << uint32(0x0); // 7 - data << uint32(0xbb9) << uint32(0x0); // 8 - data << uint32(0xbba) << uint32(0x0); // 9 + if (bg && bg->GetTypeID() == BATTLEGROUND_RL) + bg->FillInitialWorldStates(data); + else + { + data << uint32(0xbb8) << uint32(0x0); // 7 gold + data << uint32(0xbb9) << uint32(0x0); // 8 green + data << uint32(0xbba) << uint32(0x0); // 9 show + } break; case 3703: // Shattrath City break; @@ -8934,6 +8981,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 @@ -9061,6 +9115,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; @@ -10007,6 +10068,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) @@ -16258,7 +16367,8 @@ bool Player::GetBGAccessByLevel(uint32 bgTypeId) const { - BattleGround *bg = sBattleGroundMgr.GetBattleGround(bgTypeId); + // get a template bg instead of running one + BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); if(!bg) return false; Index: src/game/Player.h =================================================================== --- src/game/Player.h (revision 5703) +++ src/game/Player.h (working copy) @@ -922,7 +922,7 @@ void AddToWorld(); void RemoveFromWorld(); - void TeleportTo(uint32 mapid, float x, float y, float z, float orientation, bool outofrange = true, bool ignore_transport = true, bool is_gm_command = false); + void TeleportTo(uint32 mapid, float x, float y, float z, float orientation, bool outofrange = true, bool ignore_transport = true, bool is_gm_command = false, bool battleground_port = false); void SetSummonPoint(uint32 mapid, float x, float y, float z) { @@ -1099,6 +1099,7 @@ void DestroyItem( uint8 bag, uint8 slot, bool update ); void DestroyItemCount( uint32 item, uint32 count, bool update, bool unequip_check = false); void DestroyItemCount( Item* item, uint32& count, bool update ); + void DestroyConjuredItems( bool update ); void DestroyZoneLimitedItem( bool update, uint32 new_zone ); void SplitItem( uint16 src, uint16 dst, uint32 count ); void SwapItem( uint16 src, uint16 dst ); @@ -1437,6 +1438,7 @@ void AddSpellCooldown(uint32 spell_id, uint32 itemid, time_t end_time); void ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs ); void RemoveSpellCooldown(uint32 spell_id) { m_spellCooldowns.erase(spell_id); } + void RemoveArenaSpellCooldowns(); void RemoveAllSpellCooldown(); void _LoadSpellCooldowns(QueryResult *result); void _SaveSpellCooldowns(); @@ -1759,24 +1761,32 @@ uint32 GetBattleGroundQueueIdFromLevel() const; - uint32 GetBattleGroundQueueId(uint32 index) const { return m_bgBattleGroundQueueID[index].bgType; } - uint32 GetBattleGroundQueueIndex(uint32 bgType) const + bool InBattleGroundQueue() const + { + for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + if (m_bgBattleGroundQueueID[i].bgQueueType != 0) + return true; + return false; + } + + uint32 GetBattleGroundQueueId(uint32 index) const { return m_bgBattleGroundQueueID[index].bgQueueType; } + uint32 GetBattleGroundQueueIndex(uint32 bgQueueType) const { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - if (m_bgBattleGroundQueueID[i].bgType == bgType) + if (m_bgBattleGroundQueueID[i].bgQueueType == bgQueueType) return i; return PLAYER_MAX_BATTLEGROUND_QUEUES; } - bool IsInvitedForBattleGroundType(uint32 bgType) const + bool IsInvitedForBattleGroundQueueType(uint32 bgQueueType) const { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - if (m_bgBattleGroundQueueID[i].bgType == bgType) + if (m_bgBattleGroundQueueID[i].bgQueueType == bgQueueType) return m_bgBattleGroundQueueID[i].invited; return PLAYER_MAX_BATTLEGROUND_QUEUES; } - bool InBattleGroundQueueForBattleGroundType(uint32 bgType) const + bool InBattleGroundQueueForBattleGroundQueueType(uint32 bgQueueType) const { - return GetBattleGroundQueueIndex(bgType) < PLAYER_MAX_BATTLEGROUND_QUEUES; + return GetBattleGroundQueueIndex(bgQueueType) < PLAYER_MAX_BATTLEGROUND_QUEUES; } void SetBattleGroundId(uint32 val) { m_bgBattleGroundID = val; } @@ -1784,9 +1794,9 @@ { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - if (m_bgBattleGroundQueueID[i].bgType == 0 || m_bgBattleGroundQueueID[i].bgType == val) + if (m_bgBattleGroundQueueID[i].bgQueueType == 0 || m_bgBattleGroundQueueID[i].bgQueueType == val) { - m_bgBattleGroundQueueID[i].bgType = val; + m_bgBattleGroundQueueID[i].bgQueueType = val; m_bgBattleGroundQueueID[i].invited = false; return i; } @@ -1797,18 +1807,18 @@ { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - if (m_bgBattleGroundQueueID[i].bgType == val) + if (m_bgBattleGroundQueueID[i].bgQueueType == val) { - m_bgBattleGroundQueueID[i].bgType = 0; + m_bgBattleGroundQueueID[i].bgQueueType = 0; m_bgBattleGroundQueueID[i].invited = false; return; } } } - void SetInviteForBattleGroundType(uint32 bgType) + void SetInviteForBattleGroundQueueType(uint32 bgQueueType) { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - if (m_bgBattleGroundQueueID[i].bgType == bgType) + if (m_bgBattleGroundQueueID[i].bgQueueType == bgQueueType) m_bgBattleGroundQueueID[i].invited = true; } @@ -1817,9 +1827,11 @@ float GetBattleGroundEntryPointY() const { return m_bgEntryPointY; } float GetBattleGroundEntryPointZ() const { return m_bgEntryPointZ; } float GetBattleGroundEntryPointO() const { return m_bgEntryPointO; } - void SetBattleGroundEntryPoint(uint32 Map, float PosX, float PosY, float PosZ, float PosO ) + uint32 GetBattleGroundEntryPointInstanceId() const { return m_bgEntryPointInstanceId; } + void SetBattleGroundEntryPoint(uint32 Map, uint32 InstanceId, float PosX, float PosY, float PosZ, float PosO ) { m_bgEntryPointMap = Map; + m_bgEntryPointInstanceId = InstanceId; m_bgEntryPointX = PosX; m_bgEntryPointY = PosY; m_bgEntryPointZ = PosZ; @@ -1969,11 +1981,12 @@ */ struct BgBattleGroundQueueID_Rec { - uint32 bgType; + uint32 bgQueueType; bool invited; }; BgBattleGroundQueueID_Rec m_bgBattleGroundQueueID[PLAYER_MAX_BATTLEGROUND_QUEUES]; uint32 m_bgEntryPointMap; + uint32 m_bgEntryPointInstanceId; float m_bgEntryPointX; float m_bgEntryPointY; float m_bgEntryPointZ; Index: src/game/Spell.cpp =================================================================== --- src/game/Spell.cpp (revision 5703) +++ src/game/Spell.cpp (working copy) @@ -2834,12 +2834,20 @@ Player* p_caster = (Player*)m_caster; + // if arena preparation is on, don't take soul shards + bool bArenaPrep = p_caster->HasAura(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 @@ -3067,6 +3075,17 @@ 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()) && // check if inside arena + (((m_spellInfo->RecoveryTime > 15 * 60000 || + m_spellInfo->CategoryRecoveryTime > 15 * 60000) && + !(m_spellInfo->AttributesExEx & (1<<17))) || // more than 15 mins cd and not overriden by useable in arena attribute + (m_spellInfo->AttributesExEx & (1<<16))) ) // overriden by not useable in arena attribute + return SPELL_FAILED_NOT_IN_ARENA; + // zone check if(!IsSpellAllowedInLocation(m_spellInfo,m_caster->GetMapId(),m_caster->GetZoneId(),m_caster->GetAreaId())) return SPELL_FAILED_REQUIRES_AREA; @@ -4069,6 +4088,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) @@ -4077,25 +4099,35 @@ 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/SpellEffects.cpp =================================================================== --- src/game/SpellEffects.cpp (revision 5703) +++ src/game/SpellEffects.cpp (working copy) @@ -4180,6 +4180,9 @@ if(pTarget->isInFlight()) return; + if(pTarget->InBattleGround()) + pTarget->LeaveBattleground(); + //homebind location is loaded always pTarget->TeleportTo(pTarget->m_homebindMapId,pTarget->m_homebindX,pTarget->m_homebindY,pTarget->m_homebindZ,pTarget->GetOrientation()); Index: src/game/SpellHandler.cpp =================================================================== --- src/game/SpellHandler.cpp (revision 5703) +++ src/game/SpellHandler.cpp (working copy) @@ -67,6 +67,16 @@ 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) + && !(proto->Flags & ITEM_FLAGS_USEABLE_IN_ARENA) ) + { + //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) @@ -556,7 +566,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] != obj->GetGUID())) return; // check player team if(_player->GetTeam() == ALLIANCE) @@ -571,7 +582,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] != obj->GetGUID())) return; // check player team if(_player->GetTeam() == HORDE) Index: src/game/Unit.cpp =================================================================== --- src/game/Unit.cpp (revision 5703) +++ src/game/Unit.cpp (working copy) @@ -570,36 +570,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"); // find player: owner of controlled `this` or `this` itself maybe @@ -747,6 +717,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) { @@ -4223,6 +4226,21 @@ } } +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->GetSpellProto()->AttributesExEx & (1<<21)) // don't remove stances, shadowform, pally/hunter auras + && !iter->second->IsPassive() // don't remove passive auras + && (iter->second->IsPositive() ^ onleave)) // remove positive buffs on enter, negative buffs on leave + RemoveAura(iter); + else + ++iter; + } +} + void Unit::RemoveAllAurasOnDeath() { // used just after dieing to remove all visible auras Index: src/game/Unit.h =================================================================== --- src/game/Unit.h (revision 5703) +++ src/game/Unit.h (working copy) @@ -892,6 +892,7 @@ void RemoveAurasWithInterruptFlags(uint32 flags); void RemoveAllAuras(); + void RemoveArenaAuras(bool onleave = false); void RemoveAllAurasOnDeath(); void DelayAura(uint32 spellId, uint32 effindex, int32 delaytime); Index: src/game/World.cpp =================================================================== --- src/game/World.cpp (revision 5703) +++ src/game/World.cpp (working copy) @@ -510,6 +510,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) { @@ -850,6 +854,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 5703) +++ src/game/World.h (working copy) @@ -121,6 +121,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 5703) +++ src/mangosd/mangosd.conf.in (working copy) @@ -761,3 +761,15 @@ Ra.Port = 3443 Ra.MinLevel = 3 Ra.Secure = 1 +# 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