Index: src/game/ArenaTeam.cpp =================================================================== --- src/game/ArenaTeam.cpp (revision 5116) +++ src/game/ArenaTeam.cpp (working copy) @@ -34,7 +34,7 @@ stats.games = 0; stats.played = 0; stats.rank = 0; - stats.rating = 0; + stats.rating = 1500; stats.wins = 0; stats.wins2 = 0; } @@ -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; @@ -74,7 +74,7 @@ CharacterDatabase.PExecute("INSERT INTO `arena_team` (`arenateamid`,`name`,`captainguid`,`type`,`EmblemStyle`,`EmblemColor`,`BorderStyle`,`BorderColor`,`BackgroundColor`) " "VALUES('%u','%s','%u','%u','%u','%u','%u','%u','%u')", Id, ArenaTeamName.c_str(), GUID_LOPART(CaptainGuid), Type, EmblemStyle, EmblemColor, BorderStyle, BorderColor, BackgroundColor); - CharacterDatabase.PExecute("INSERT INTO `arena_team_stats` (`arenateamid`, `rating`, `games`, `wins`, `played`, `wins2`, `rank`) VALUES ('%u', '0', '0', '0', '0', '0', '0')", Id); + CharacterDatabase.PExecute("INSERT INTO `arena_team_stats` (`arenateamid`, `rating`, `games`, `wins`, `played`, `wins2`, `rank`) VALUES ('%u', '1500', '0', '0', '0', '0', '0')", Id); CharacterDatabase.CommitTransaction(); AddMember(CaptainGuid); @@ -89,8 +89,11 @@ if(!objmgr.GetPlayerNameByGUID(PlayerGuid, plName)) // player doesnt exist return false; // player already in arenateam of that size - if(Player::GetArenaTeamIdFromDB(PlayerGuid, GetSlot()) != 0) + if(Player::GetArenaTeamIdFromDB(PlayerGuid, GetType()) != 0) + { + sLog.outError("Arena::AddMember() : player already in this sized team"); return false; + } // remove all player signs from another petitions // this will be prevent attempt joining player to many arenateams and corrupt arena team data integrity @@ -118,6 +121,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)); @@ -195,7 +199,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; @@ -209,6 +213,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; @@ -329,6 +334,7 @@ data << itr->wons_week; // wins this week data << itr->played_season; // played this season data << itr->wons_season; // wins this season + data << uint32(0); // unknown } else { @@ -342,6 +348,7 @@ data << itr->wons_week; // wins this week data << itr->played_season; // played this season data << itr->wons_season; // wins this season + data << uint32(0); // unknown } } session->SendPacket(&data); @@ -365,7 +372,7 @@ void ArenaTeam::Stats(WorldSession *session) { - WorldPacket data(SMSG_ARENA_TEAM_STATS, 4*7); + WorldPacket data(SMSG_ARENA_TEAM_STATS, 4*9); data << GetId(); // arena team id data << stats.rating; // rating data << stats.games; // games @@ -373,6 +380,19 @@ data << stats.played; // played data << stats.wins2; // wins(again o_O) data << stats.rank; // rank + uint64 guid = session->GetPlayer()->GetGUID(); + float played_percent = 0; + for(MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr) + { + if(itr->guid == guid) + { + if(stats.games) + played_percent = itr->played_week / stats.games; + break; + } + } + data << played_percent; // played % per player + data << played_percent; session->SendPacket(&data); } @@ -463,6 +483,145 @@ } } +uint32 ArenaTeam::GetPoints() +{ + 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)); + } + + 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) +{ + return 1.0f/(1.0f+exp(log(10.0f)*(float)(stats.rating - rating)/400.0f)); +} + +int32 ArenaTeam::WonAgainstChance(float chance) +{ + int32 mod = (int32)(32.0f * (1.0f - chance)); + stats.rating += mod; + stats.games += 1; + stats.wins += 1; + stats.played += 1; + stats.wins2 += 1; +// SetStats(STAT_TYPE_RATING, stats.rating + mod); +// SetStats(STAT_TYPE_GAMES, stats.games+1); +// SetStats(STAT_TYPE_WINS, stats.wins+1); +// SetStats(STAT_TYPE_PLAYED, stats.played+1); +// SetStats(STAT_TYPE_WINS2, stats.wins2+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; + } +// SetStats(STAT_TYPE_RATING, higher_rating + 1); + stats.rank = higher_rank + 1; + return mod; +} + +int32 ArenaTeam::LostAgainstChance(float chance) +{ + int32 mod = (int32)(32.0f * (0.0f - chance)); + stats.rating += mod; + stats.games += 1; + stats.played += 1; +// SetStats(STAT_TYPE_RATING, stats.rating + mod); +// SetStats(STAT_TYPE_GAMES, stats.games+1); +// SetStats(STAT_TYPE_PLAYED, 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; + } +// SetStats(STAT_TYPE_RATING, higher_rating + 1); + stats.rank = higher_rank + 1; + return mod; +} + +void ArenaTeam::MemberLost(uint64 guid) +{ + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + if(itr->guid == guid) + { + itr->played_week +=1; + itr->played_season +=1; + return; + } + } +} + +void ArenaTeam::MemberWon(uint64 guid) +{ + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + if(itr->guid == guid) + { + itr->played_week +=1; + itr->played_season +=1; + itr->wons_season += 1; + itr->wons_week += 1; + return; + } + } +} + +uint32 ArenaTeam::GetMemberPoints(uint64 guid) +{ + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + if(itr->guid == guid) + { + return itr->points_to_add; + } + } + return 0; +} + +void ArenaTeam::UpdateArenaPointsHelper() +{ + if(stats.games < 10) + return; + uint32 min_plays = stats.games * 0.3; + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + if(itr->played_week >= min_plays) + { + 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); + } + else + { + itr->points_to_add = 0; + } + } +} + +void ArenaTeam::SaveToDB() +{ + 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.1.0 client): 1405 - arena team id 2v2 Index: src/game/ArenaTeam.h =================================================================== --- src/game/ArenaTeam.h (revision 5116) +++ 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 @@ -144,6 +145,8 @@ void LoadStatsFromDB(uint32 ArenaTeamId); void LoadPlayerStats(ArenaTeamMember* member); + void SaveToDB(); + void BroadcastPacket(WorldPacket *packet); void Roster(WorldSession *session); @@ -151,6 +154,16 @@ void Stats(WorldSession *session); void InspectStats(WorldSession *session); + uint32 GetPoints(); + float GetChanceAgainst(uint32 rating); + int32 WonAgainstChance(float chance); + void MemberWon(uint64 guid); + int32 LostAgainstChance(float chance); + void MemberLost(uint64 guid); + + void UpdateArenaPointsHelper(); + uint32 GetMemberPoints(uint64 guid); + protected: uint32 Id; Index: src/game/ArenaTeamHandler.cpp =================================================================== --- src/game/ArenaTeamHandler.cpp (revision 5116) +++ src/game/ArenaTeamHandler.cpp (working copy) @@ -141,6 +141,7 @@ if(player->GetArenaTeamId(team_slot)) { + sLog.outDebug("ALREADY IN ARENA TEAM OF THAT SIZE!!!!! team id %u for team slot %u", player->GetArenaTeamId(team_slot), team_slot); SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, player->GetName(), "", ERR_ALREADY_IN_ARENA_TEAM_S); return; } Index: src/game/BattleGround.cpp =================================================================== --- src/game/BattleGround.cpp (revision 5116) +++ src/game/BattleGround.cpp (working copy) @@ -25,11 +25,13 @@ #include "Language.h" // for chat messages #include "Chat.h" #include "SpellAuras.h" +#include "ArenaTeam.h" #include "World.h" BattleGround::BattleGround() { m_TypeID = 0; + m_TeamType = 0; m_InstanceID = 0; m_Status = 0; m_EndTime = 0; @@ -46,6 +48,8 @@ m_Name = ""; m_LevelMin = 0; m_LevelMax = 0; + m_InBGFreeSlotQueue = false; + m_SetDeleteThis = false; m_MaxPlayersPerTeam = 0; m_MaxPlayers = 0; @@ -66,6 +70,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; @@ -75,7 +85,28 @@ BattleGround::~BattleGround() { + // remove objects and creatures +/* for(BGObjects::iterator itr = m_BgObjects.begin(); itr != m_BgObjects.end(); ++itr) + { + if(*itr) (*itr)->RemoveFromWorld(); + } + for(BGCreatures::iterator itr = m_BgCreatures.begin(); itr != m_BgCreatures.end(); ++itr) + { + if(*itr) (*itr)->RemoveFromWorld(); + } +*/ + // 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) @@ -237,7 +268,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); } } @@ -263,7 +297,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); @@ -283,7 +320,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); } } @@ -300,7 +340,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); } } @@ -322,7 +365,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); } } @@ -336,13 +382,20 @@ void BattleGround::EndBattleGround(uint32 winner) { + this->RemoveFromBGFreeSlotQueue(); + + ArenaTeam * winner_arena_team = NULL; + ArenaTeam * loser_arena_team = NULL; WorldPacket data; Player *Source = NULL; const char *winmsg = ""; if(winner == ALLIANCE) { - winmsg = LANG_BG_A_WINS; + if(isBattleGround()) + winmsg = LANG_BG_A_WINS; + else + winmsg = LANG_ARENA_GOLD_WINS; PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound @@ -350,13 +403,56 @@ } else { - winmsg = LANG_BG_H_WINS; + if(isBattleGround()) + winmsg = LANG_BG_H_WINS; + else + winmsg = LANG_ARENA_GREEN_WINS; PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound SetWinner(WINNER_HORDE); } + 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) + { + uint32 loser_rating = loser_arena_team->GetStats().rating; + uint32 winner_rating = winner_arena_team->GetStats().rating; + float winner_chance = winner_arena_team->GetChanceAgainst(loser_rating); + float loser_chance = loser_arena_team->GetChanceAgainst(winner_rating); + int32 winner_change = winner_arena_team->WonAgainstChance(winner_chance); + int32 loser_change = loser_arena_team->LostAgainstChance(loser_chance); + if(winner == ALLIANCE) + { + SetArenaTeamRatingChangeForTeam(ALLIANCE, winner_change); + SetArenaTeamRatingChangeForTeam(HORDE, loser_change); + } + else + { + SetArenaTeamRatingChangeForTeam(HORDE, winner_change); + SetArenaTeamRatingChangeForTeam(ALLIANCE, loser_change); + } + winner_arena_team->UpdateArenaPointsHelper(); + loser_arena_team->UpdateArenaPointsHelper(); + } + else + { + SetArenaTeamRatingChangeForTeam(ALLIANCE, 0); + SetArenaTeamRatingChangeForTeam(HORDE, 0); + } + } + SetStatus(STATUS_WAIT_LEAVE); m_EndTime = 0; @@ -371,14 +467,35 @@ 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(); + + if(isArena() && isRated() && winner_arena_team && loser_arena_team) { + if(team == winner) + { + winner_arena_team->MemberWon(itr->first); + winner_arena_team->Stats(plr->GetSession()); + } + else + { + loser_arena_team->MemberLost(itr->first); + loser_arena_team->Stats(plr->GetSession()); + } + } + + if(team == winner) + { if(!Source) Source = plr; switch(GetTypeID()) @@ -429,10 +546,21 @@ 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 bgTypeId = sBattleGroundMgr.BGQueueTypeId(m_TypeID); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(bgTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime()); plr->GetSession()->SendPacket(&data); } + if(isArena() && isRated() && winner_arena_team && loser_arena_team) + { + winner_arena_team->SaveToDB(); + loser_arena_team->SaveToDB(); + } + + // inform invited players about the removal + sBattleGroundMgr.m_BattleGroundQueues[GetTypeID()].BGEndedRemoveInvites(this); + + if(Source) { ChatHandler(Source).FillMessageData(&data, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_GLOBAL, Source->GetGUID(), winmsg); @@ -449,11 +577,13 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket) { + sLog.outDebug("BG:Removing player at leave"); + 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); } @@ -468,6 +598,10 @@ Player *plr = objmgr.GetPlayer(guid); + // should remove spirit of redemption + if(plr && plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) + plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT); + if(plr && !plr->isAlive()) // resurrect on exit { plr->ResurrectPlayer(1.0f); @@ -478,41 +612,78 @@ if(plr) { + if(!team) team = plr->GetTeam(); + + uint32 bgTypeId = GetTypeID(); if(isArena()) { - if(!sWorld.IsFFAPvPRealm()) - plr->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP); + plr->RemoveArenaAuras(true); + 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; - plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); + Pet* NewPet = new Pet(plr); + if(!NewPet->LoadPetFromDB(plr, 0, (plr)->GetOldPetNumber(), true)) + delete NewPet; + + (plr)->SetOldPetNumber(0); } 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(bgTypeId), 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(bgTypeId); - 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[bgTypeId].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(); + 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 *group = new Group; + SetBgRaid(team, group); + group->ConvertToRaid(); + group->AddMember(grpGuy->GetGUID(), grpGuy->GetName()); + group->ChangeLeader(grpGuy->GetGUID()); + } } } @@ -521,11 +692,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); } @@ -533,13 +705,32 @@ 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()) { - Reset(); + // // no one left, cleanup and reinit instance + // Reset(); + // if(!SetupBattleGround()) + // { + // // setup failed, remove battleground. + m_SetDeleteThis = true; + 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 @@ -551,6 +742,9 @@ SetStartTime(0); SetEndTime(0); SetLastResurrectTime(0); + SetTeamType(BG_TEAM_TYPE_NORMAL); + SetArenaType(0); + SetRated(false); m_Events = 0; @@ -559,6 +753,7 @@ m_InvitedAlliance = 0; m_InvitedHorde = 0; + m_InBGFreeSlotQueue = false; m_Players.clear(); m_PlayerScores.clear(); @@ -576,52 +771,64 @@ WorldPacket data; BattleGround *bg = NULL; - - /// this code seems not correct: - if(GetTypeID() == BATTLEGROUND_AA) - { - // we must select one of running arena - uint8 arenas[3] = { BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL }; - uint8 arena_type = arenas[urand(0, 2)]; - - bg = sBattleGroundMgr.GetBattleGround(arena_type); - if(!bg) - return; - - bg->SetStatus(STATUS_WAIT_JOIN); - - bg->SetStartTime(0); - bg->SetLastResurrectTime(0); - } } -void BattleGround::AddPlayer(Player *plr) +void BattleGround::AddPlayer(Player *plr, uint32 team) { + sLog.outDebug("BGAddPlayer %u",plr->GetGUIDLow()); // 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); 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 @@ -631,13 +838,18 @@ /* This method should be called only once ... it adds pointer to queue */ void BattleGround::AddToBGFreeSlotQueue() { - sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this); + 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 + 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) @@ -645,7 +857,7 @@ sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr); return; } - }*/ + } } /* @@ -668,6 +880,30 @@ return false; } +// get the number of free slots for team +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 { @@ -748,7 +984,7 @@ 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); +/* GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(entry); if(!goinfo) { sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry); @@ -772,21 +1008,40 @@ data.spawntimesecs = spawntime; data.animprogress = 100; data.go_state = 1; - objmgr.AddGameobjectToGrid(guid, &data); +// objmgr.AddGameobjectToGrid(guid, &data); + m_BgObjects[type] = MAKE_GUID(guid, HIGHGUID_GAMEOBJECT); +/* GameObject * go = ObjectAccessor::Instance().GetObjectInWorld(m_BgObjects[type], (GameObject*)NULL); + go->SetInstanceId(GetInstanceID());//*/ + + 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("Cannot create gameobject template %u! BattleGround not created!", entry); + delete go; + return false; + } + +// objmgr.SaveGORespawnTime(go->GetGUIDLow(),go->GetInstanceId(),spawntime); +// MapManager::Instance().GetMap(go->GetMapId(), go)->Add(go); + + m_BgObjects[type] = go;//*/ + sLog.outDebug("GO created in bg type %u, insta %u, go entry %u",GetTypeID(), GetInstanceID(), entry); return true; } void BattleGround::DoorOpen(uint32 type) { - GameObject *obj = HashMapHolder::Find(m_BgObjects[type]); + GameObject *obj = m_BgObjects[type]; if(obj) { obj->SetUInt32Value(GAMEOBJECT_FLAGS,33); obj->SetUInt32Value(GAMEOBJECT_STATE,0); //obj->SetLootState(GO_CLOSED); + sLog.outDebug("BG Door opened"); } else { @@ -798,32 +1053,37 @@ { if(respawntime == 0) { - GameObject *obj = HashMapHolder::Find(m_BgObjects[type]); + GameObject *obj = m_BgObjects[type]; if(obj) { //obj->Respawn(); // bugged obj->SetRespawnTime(0); - objmgr.SaveGORespawnTime(obj->GetGUIDLow(), 0, 0); + objmgr.SaveGORespawnTime(obj->GetGUIDLow(), GetInstanceID(), 0); + MapManager::Instance().GetMap(obj->GetMapId(), obj)->Add(obj); + sLog.outDebug("BG Object spawned"); } - else - objmgr.SaveGORespawnTime(GUID_LOPART(m_BgObjects[type]), 0, 0); +// else +// objmgr.SaveGORespawnTime(GUID_LOPART(m_BgObjects[type]), GetInstanceID(), 0); } else { - GameObject *obj = HashMapHolder::Find(m_BgObjects[type]); + GameObject *obj = m_BgObjects[type]; if(obj) { obj->SetRespawnTime(respawntime); obj->SetLootState(GO_LOOTED); + MapManager::Instance().GetMap(obj->GetMapId(), obj)->Add(obj); + sLog.outDebug("BG Object spawned 2"); } - else - objmgr.SaveGORespawnTime(GUID_LOPART(m_BgObjects[type]), 0, time(NULL) + respawntime); +// else +// objmgr.SaveGORespawnTime(GUID_LOPART(m_BgObjects[type]), GetInstanceID(), 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); @@ -834,17 +1094,50 @@ pCreature->AIM_Initialize(); //pCreature->SetDungeonDifficulty(0); - MapManager::Instance().GetMap(pCreature->GetMapId(), pCreature)->Add(pCreature); - m_BgCreatures[type] = pCreature->GetGUID(); +// objmgr.SaveCreatureRespawnTime(pCreature->GetGUIDLow(), pCreature->GetInstanceId(), respawntime); +// MapManager::Instance().GetMap(pCreature->GetMapId(), pCreature)->Add(pCreature); + m_BgCreatures[type] = pCreature; + sLog.outDebug("BG Creature created in bg type %u, insta %u, go entry %u",GetTypeID(), GetInstanceID(), entry); return pCreature; } +void BattleGround::SpawnBGCreature(uint32 type, uint32 respawntime) +{ + if(respawntime == 0) + { + Creature *obj = m_BgCreatures[type]; + if(obj) + { + //obj->Respawn(); // bugged + obj->SetRespawnTime(0); + objmgr.SaveCreatureRespawnTime(obj->GetGUIDLow(), GetInstanceID(), 0); + MapManager::Instance().GetMap(obj->GetMapId(), obj)->Add(obj); + sLog.outDebug("BG Creature spawned"); + } + // else + // objmgr.SaveCreatureRespawnTime(GUID_LOPART(m_BgCreatures[type]), GetInstanceID(), 0); + } + else + { + Creature *obj = m_BgCreatures[type]; + if(obj) + { + obj->setDeathState(DEAD); + obj->SetRespawnTime(respawntime); + MapManager::Instance().GetMap(obj->GetMapId(), obj)->Add(obj); + sLog.outDebug("BG Creature spawned 2"); + } + // else + // objmgr.SaveCreatureRespawnTime(GUID_LOPART(m_BgCreatures[type]), GetInstanceID(), time(NULL) + respawntime); + } +} + bool BattleGround::DelCreature(uint32 type) { - Creature *cr = HashMapHolder::Find(m_BgCreatures[type]); + Creature *cr = m_BgCreatures[type]; if(!cr) { - sLog.outError("Can't find creature guid: %u",m_BgCreatures[type]); + sLog.outError("Can't find creature."); return false; } cr->CleanupsBeforeDelete(); @@ -855,10 +1148,10 @@ bool BattleGround::DelObject(uint32 type) { - GameObject *obj = HashMapHolder::Find(m_BgObjects[type]); + GameObject *obj = m_BgObjects[type]; if(!obj) { - sLog.outError("Can't find gobject guid: %u",m_BgObjects[type]); + sLog.outError("Can't find gobject."); return false; } obj->SetRespawnTime(0); // not save respawn time @@ -914,3 +1207,11 @@ SetStatus(STATUS_WAIT_LEAVE); SetEndTime(TIME_TO_AUTOREMOVE); } + +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; +} Index: src/game/BattleGround.h =================================================================== --- src/game/BattleGround.h (revision 5116) +++ src/game/BattleGround.h (working copy) @@ -50,7 +50,11 @@ SPELL_SPIRIT_HEAL_CHANNEL = 22011, SPELL_SPIRIT_HEAL = 22012, SPELL_RESURRECTION_VISUAL = 24171, - SPELL_ARENA_PREPARATION = 32727 + SPELL_ARENA_PREPARATION = 32727, + SPELL_ALLIANCE_GOLD_FLAG = 32724, + SPELL_ALLIANCE_GREEN_FLAG = 32725, + SPELL_HORDE_GOLD_FLAG = 35774, + SPELL_HORDE_GREEN_FLAG = 35775 }; enum BattleGroundTimeIntervals @@ -61,9 +65,9 @@ TIME_TO_AUTOREMOVE = 120000, // ms MAX_OFFLINE_TIME = 300000, // ms START_DELAY0 = 120000, // ms - START_DELAY1 = 60000, // ms - START_DELAY2 = 30000, // ms - START_DELAY3 = 15000, // ms used only in arena + START_DELAY1 = 10000, // ms + START_DELAY2 = 5000, // ms + START_DELAY3 = 1000, // ms used only in arena RESPAWN_ONE_DAY = 86400, // secs RESPAWN_IMMEDIATELY = 0, // secs BUFF_RESPAWN_TIME = 180 // secs @@ -166,6 +170,13 @@ BG_TEAM_HORDE = 1 }; +enum BattleGroundTeamType +{ + BG_TEAM_TYPE_NORMAL = 0, // one side is ally, other is horde + BG_TEAM_TYPE_ALLIANCE_ONLY = 1, // both sides are alliance + BG_TEAM_TYPE_HORDE_ONLY = 2 // both sides are horde +}; + class BattleGroundScore { public: @@ -193,6 +204,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 @@ -262,6 +274,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; } @@ -334,6 +347,11 @@ m_PlayersCount[GetTeamIndexByTeamId(Team)]++; } + void SetArenaTeamIdForTeam(uint32 Team, uint32 ArenaTeamId) { m_ArenaTeamIds[ Team == ALLIANCE ? 0 : 1] = ArenaTeamId; } + uint32 GetArenaTeamIdForTeam(uint32 Team) const { return m_ArenaTeamIds[ Team == ALLIANCE ? 0 : 1]; } + void SetArenaTeamRatingChangeForTeam(uint32 Team, int32 ArenaTeamId) { m_ArenaTeamRatingChanges[ Team == ALLIANCE ? 0 : 1] = ArenaTeamId; } + uint32 GetArenaTeamRatingChangeForTeam(uint32 Team) const { return m_ArenaTeamRatingChanges[ Team == ALLIANCE ? 0 : 1]; } + /* Triggers handle */ // must be implemented in BG subclass virtual void HandleAreaTrigger(Player* /*Source*/, uint32 /*Trigger*/) {} @@ -342,24 +360,30 @@ // must be implemented in BG subclass if need virtual void HandleDropFlag(Player* /*player*/) {} - virtual void AddPlayer(Player *plr); // must be implemented in BG subclass + virtual void AddPlayer(Player *plr, uint32 team = 0); // must be implemented in BG subclass virtual void RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket); // can be extended in in BG subclass // TODO: make this protected: - typedef std::vector BGObjects; - typedef std::vector BGCreatures; + typedef std::vector BGObjects; + typedef std::vector BGCreatures; BGObjects m_BgObjects; BGCreatures m_BgCreatures; void SpawnBGObject(uint32 type, uint32 respawntime); + void SpawnBGCreature(uint32 type, uint32 respawntime); bool AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawntime = 0); - Creature* AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o); + Creature* AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o, uint32 respawntime = 0); bool DelCreature(uint32 type); bool DelObject(uint32 type); bool AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team); void DoorOpen(uint32 type); + uint32 GetPlayerTeam(uint64 guid); + + uint8 GetTeamType() const {return m_TeamType;} + void SetTeamType(uint8 type){m_TeamType=type;} + protected: //this method is called, when BG cannot spawn its own spirit guide, or something is wrong, It correctly ends BattleGround void EndNow(); @@ -387,6 +411,9 @@ uint32 m_LastResurrectTime; uint32 m_Queue_type; uint8 m_ArenaType; // 2=2v2, 3=3v3, 5=5v5 + uint8 m_TeamType; + bool m_InBGFreeSlotQueue; + bool m_SetDeleteThis; // 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 @@ -412,6 +439,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 5116) +++ 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 5116) +++ 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 5116) +++ src/game/BattleGroundAB.cpp (working copy) @@ -237,9 +237,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; @@ -342,6 +342,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]); @@ -463,6 +464,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); @@ -626,6 +628,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 +637,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; @@ -755,3 +763,6 @@ } + + + Index: src/game/BattleGroundAB.h =================================================================== --- src/game/BattleGroundAB.h (revision 5116) +++ 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 5116) +++ 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 5116) +++ 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 5116) +++ src/game/BattleGroundBE.cpp (working copy) @@ -98,9 +98,9 @@ }*/ } -void BattleGroundBE::AddPlayer(Player *plr) +void BattleGroundBE::AddPlayer(Player *plr, uint32 team) { - BattleGround::AddPlayer(plr); + BattleGround::AddPlayer(plr, team); //create score and add it to map, default values are set in constructor BattleGroundBEScore* sc = new BattleGroundBEScore; @@ -109,7 +109,13 @@ void BattleGroundBE::RemovePlayer(Player *plr, uint64 guid) { + if(GetStatus() == STATUS_WAIT_LEAVE) + return; + if(!GetPlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetPlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } void BattleGroundBE::HandleKillPlayer(Player *player, Player *killer) @@ -123,14 +129,20 @@ return; } - uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam()); + uint32 killer_team = GetPlayerTeam(killer->GetGUID()); + if(!killer_team) killer_team = killer->GetTeam(); + uint32 killer_team_index = GetTeamIndexByTeamId(killer_team); + m_TeamKills[killer_team_index]++; // add kills to killer's team - if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(player->GetTeam())) + uint32 team = GetPlayerTeam(player->GetGUID()); + if(!team) team = player->GetTeam(); + + if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(team)) { // all opponents killed - EndBattleGround(killer->GetTeam()); + EndBattleGround(killer_team); } } @@ -141,14 +153,14 @@ return; uint32 SpellId = 0; - uint64 buff_guid = 0; + GameObject* obj = 0; switch(Trigger) { case 4538: // buff trigger? - buff_guid = m_BgObjects[BG_BE_OBJECT_BUFF_1]; + obj = m_BgObjects[BG_BE_OBJECT_BUFF_1]; break; case 4539: // buff trigger? - buff_guid = m_BgObjects[BG_BE_OBJECT_BUFF_2]; + obj = m_BgObjects[BG_BE_OBJECT_BUFF_2]; break; default: sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); @@ -156,11 +168,8 @@ break; } - if(buff_guid) + if(obj) { - GameObject *obj = HashMapHolder::Find(buff_guid); - if(obj) - { if(!obj->isSpawned()) return; // buff not spawned yet obj->SetRespawnTime(BUFF_RESPAWN_TIME); @@ -168,7 +177,6 @@ SpellId = obj->GetGOInfo()->data3; if(SpellId) Source->CastSpell(Source, SpellId, true); - } } } Index: src/game/BattleGroundBE.h =================================================================== --- src/game/BattleGroundBE.h (revision 5116) +++ src/game/BattleGroundBE.h (working copy) @@ -58,7 +58,7 @@ void Update(time_t diff); /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); + virtual void AddPlayer(Player *plr, uint32 team = 0); void RemovePlayer(Player *plr, uint64 guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); Index: src/game/BattleGroundEY.cpp =================================================================== --- src/game/BattleGroundEY.cpp (revision 5116) +++ 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 5116) +++ 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 5116) +++ src/game/BattleGroundHandler.cpp (working copy) @@ -26,6 +26,8 @@ #include "MapManager.h" #include "ObjectAccessor.h" #include "Object.h" +#include "Chat.h" +#include "Language.h" #include "BattleGroundMgr.h" #include "BattleGroundWS.h" #include "BattleGround.h" @@ -78,6 +80,12 @@ recv_data >> instanceId; // instance id, 0 if First Available selected recv_data >> joinAsGroup; // join as group + if(bgTypeId >= MAX_BATTLEGROUND_TYPES) + { + sLog.outError("bgport: invalid bgtype received. possible cheater? player guid %u",_player->GetGUIDLow()); + return; + } + sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from: " I64FMT, guid); // ignore if we already in BG or BG queue @@ -100,19 +108,38 @@ return; } - // check existence - BattleGround *bg = sBattleGroundMgr.GetBattleGround(bgTypeId); - if(!bg) + BattleGround * bg = 0; + if(instanceId) + BattleGround *bg = sBattleGroundMgr.GetBattleGround(instanceId); + + if(!bg && !(bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId))) + { + sLog.outDebug("bgjoin: 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, LANG_BG_GROUP_TOO_LARGE, NULL); + SendPacket(&data); + return; + } + GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].AddGroup(_player, 0, false); 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); @@ -134,15 +161,16 @@ } // 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[bgTypeId].AddPlayer(member, ginfo); } + sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel()); } else { @@ -159,13 +187,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[bgTypeId].AddGroup(_player, 0, false); + sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].AddPlayer(_player, ginfo); + sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel()); } } @@ -247,6 +278,12 @@ uint32 bgTypeId; recv_data >> bgTypeId; // id from DBC + if(bgTypeId >= MAX_BATTLEGROUND_TYPES) + { + sLog.outError("bgport: invalid bgtype received. possible cheater? player guid %u",_player->GetGUIDLow()); + return; + } + BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId); if(!bl) @@ -265,37 +302,83 @@ uint8 unk1; 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; - BattleGround *bg = sBattleGroundMgr.GetBattleGround(bgTypeId); + if(bgTypeId >= MAX_BATTLEGROUND_TYPES) + { + sLog.outError("bgport: invalid bgtype received. possible cheater? player guid %u",_player->GetGUIDLow()); + return; + } + + // get the bg what we were invited to + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId); + BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); + if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) + { + sLog.outDebug("bgport: itrplayerstatus not found."); + return; + } + instanceId = itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID;//*/ + + if(!instanceId && action == 1) + { + sLog.outDebug("bgport: instance not found."); + return; + } + + BattleGround *bg = sBattleGroundMgr.GetBattleGround(instanceId); + + if(!bg && action == 0) + bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); + + if(!bg) + { + sLog.outDebug("bgport: bg not found."); + return; + } + + bgTypeId = bg->GetTypeID(); if(bg) { if(_player->InBattleGroundQueue()) { uint32 queueSlot = 0; + uint32 team = 0; + uint32 arenatype = 0; + uint32 israted = 0; + // get the team info from the queue + BattleGroundQueue::QueuedPlayersMap::iterator pitr = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); + if(pitr !=sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end() + && pitr->second.GroupInfo ) + { + team = pitr->second.GroupInfo->Team; + arenatype = pitr->second.GroupInfo->ArenaType; + israted = pitr->second.GroupInfo->IsRated; + } WorldPacket data; switch(action) { case 1: // port to battleground - _player->RemoveFromGroup(); - queueSlot = _player->GetBattleGroundQueueIndex(bgTypeId); + //_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[bgTypeId].RemovePlayer(_player->GetGUID(), false); - // check if player is not deserter - if( !_player->CanJoinToBattleground() ) + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(_player->GetGUID(), false); + // check if player is not deserter -- check in signup, not here! + /*if( !_player->CanJoinToBattleground() ) { WorldPacket data2; data2.Initialize(SMSG_GROUP_JOINED_BATTLEGROUND, 4); data2 << (uint32) 0xFFFFFFFE; SendPacket(&data2); return; - } + }*/ // if player is in battleground already, remove him and port him to new battleground if ( _player->InBattleGround() ) { @@ -303,15 +386,16 @@ if (currentBg) currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true); } - _player->SetBattleGroundId(bg->GetTypeID()); - sBattleGroundMgr.SendToBattleGround(_player, bgTypeId); - bg->AddPlayer(_player); + _player->SetBattleGroundId(bg->GetInstanceID()); // really bg type, or rather instance id? + // for now, it works with typeid, but should be instance id. + sBattleGroundMgr.SendToBattleGround(_player, instanceId, team); + bg->AddPlayer(_player,team); break; case 0: // leave queue - queueSlot = _player->GetBattleGroundQueueIndex(bgTypeId); - _player->RemoveBattleGroundQueueId(bgTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs + queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); + _player->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_NONE, 0, 0); - sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].RemovePlayer(_player->GetGUID(), true); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(_player->GetGUID(), true); SendPacket(&data); break; default: @@ -350,7 +434,8 @@ BattleGround *bg = _player->GetBattleGround(); if(bg) { - uint32 queueSlot = _player->GetBattleGroundQueueIndex(bg->GetTypeID()); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID()); + uint32 queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); if((bg->GetStatus() <= STATUS_IN_PROGRESS)) { sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime()); @@ -358,10 +443,10 @@ } 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 + if (i == queueSlot || !queue_id) // we need to get the instance ids continue; - BattleGround *bg2 = sBattleGroundMgr.GetBattleGround(queue_id); + BattleGround *bg2 = sBattleGroundMgr.GetBattleGroundTemplate(queue_id); // try this if(bg2) { //in this call is small bug, this call should be filled by player's waiting time in queue @@ -377,8 +462,8 @@ // we should update all queues? .. i'm not sure if this code is correct for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - uint32 queue_id = _player->GetBattleGroundQueueId(i); - BattleGround *bg = sBattleGroundMgr.GetBattleGround(queue_id); + uint32 queue_id = sBattleGroundMgr.BGQueueTypeId(_player->GetBattleGroundQueueId(i)); + BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(queue_id); if(bg && queue_id) { sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0); @@ -453,7 +538,7 @@ recv_data.hexlike(); // ignore if we already in BG or BG queue - if(_player->InBattleGround() || _player->InBattleGroundQueue()) + if(_player->InBattleGround()) return; uint64 guid; // arena Battlemaster guid @@ -487,23 +572,57 @@ break; } - if(isRated && !_player->GetArenaTeamId(type)) // player not in arena team of that size + if(isRated) // player not in arena team of that size { - _player->GetSession()->SendNotInArenaTeamPacket(arenatype); - return; + uint32 ateamId = _player->GetArenaTeamId(type); + if(!ateamId) + { + _player->GetSession()->SendNotInArenaTeamPacket(arenatype); + return; + } + 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, LANG_ARENA_YOUR_TEAM_ONLY, 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.outDebug("arena_join template bg (all arenas) not found"); return; + } + uint8 bgTypeId = bg->GetTypeID(); + 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, LANG_ARENA_GROUP_TOO_LARGE, NULL); + SendPacket(&data); + return; + } + GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].AddGroup(_player, arenatype, isRated); for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) { Player *member = itr->getSource(); @@ -513,13 +632,23 @@ //player has deserter aura .. do nothing */ - if (member->GetBattleGroundQueueIndex(BATTLEGROUND_AA) < PLAYER_MAX_BATTLEGROUND_QUEUES) + if( member->GetBattleGroundQueueIdFromLevel() != _player->GetBattleGroundQueueIdFromLevel() ) + continue; + + if( member->GetTeam() != _player->GetTeam() ) + continue; + + if (member->GetBattleGroundQueueIndex(bgTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) + { + sLog.outDebug("all arenas: player is already in this queue"); //player is already in this queue continue; + } - uint32 queueSlot = member->AddBattleGroundQueueId(BATTLEGROUND_AA);// add to queue + uint32 queueSlot = member->AddBattleGroundQueueId(bgTypeId);// add to queue if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES) { + sLog.outDebug("arena_join player max bg queues are filled. error"); WorldPacket data; //fill data //member->GetSession()->SendPacket(data); @@ -527,16 +656,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); 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[bgTypeId].AddPlayer(member, ginfo); } + sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(), arenatype, isRated); } else { @@ -544,13 +674,17 @@ //player has deserter aura .. do nothing */ - if (_player->GetBattleGroundQueueIndex(BATTLEGROUND_AA) < PLAYER_MAX_BATTLEGROUND_QUEUES) + if (_player->GetBattleGroundQueueIndex(bgTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) + { + sLog.outDebug("player is already in all_arenas queue"); //player is already in this queue return; + } - uint32 queueSlot = _player->AddBattleGroundQueueId(BATTLEGROUND_AA); + uint32 queueSlot = _player->AddBattleGroundQueueId(bgTypeId); if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES) { + sLog.outDebug("arenajoin player queues are already full"); WorldPacket data; //fill data (player is in 3 queues already) //SendPacket(data); @@ -558,12 +692,15 @@ } // 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[BATTLEGROUND_AA].AddPlayer(_player, BATTLEGROUND_AA); + GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].AddGroup(_player,arenatype,isRated); + sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].AddPlayer(_player, ginfo); + sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(), arenatype, isRated); +// sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].AddPlayer(_player, bgTypeId, 0, 0, arenatype, isRated); } } Index: src/game/BattleGroundMgr.cpp =================================================================== --- src/game/BattleGroundMgr.cpp (revision 5116) +++ src/game/BattleGroundMgr.cpp (working copy) @@ -42,112 +42,456 @@ BattleGroundQueue::BattleGroundQueue() { + sLog.outDebug("Creating battleground queue"); //queues are empty, we don't have to call clear() 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() { + sLog.outDebug("Destroying battleground queue"); 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) +void BattleGroundQueue::EligibleGroups::Init(BattleGroundQueue::QueuedGroupsList *source, uint32 side, uint32 MaxPlayers, uint8 ArenaType, bool IsRated) { + sLog.outDebug("initializing eligible groups for side %u, maxplayers %u, arenatype %u, rated %u",side,MaxPlayers,ArenaType,IsRated); + clear(); + BattleGroundQueue::QueuedGroupsList::iterator itr, next; + for(itr = source->begin(); itr!= source->end(); itr = next) + { + next = itr; + ++next; + if( (*itr)->ArenaType == ArenaType && + (*itr)->IsRated == IsRated && + (*itr)->IsInvitedToBGInstanceGUID == 0 && + (*itr)->Team == side && + (*itr)->Players.size() <= MaxPlayers ) + { + sLog.outDebug("Found an eligible group. Inserting it."); + // the group matches the conditions + // insert it in order of groupsize + 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) ) + { + sLog.outDebug("inserting inside list"); + insert(elig_itr,(*itr)); + inserted = true; + break; + } + } + // if not inserted -> this is the smallest group -> push_back + if(!inserted) + { + sLog.outDebug("inserting at end of list"); + push_back((*itr)); + } + } + } +} + +void BattleGroundQueue::EligibleGroups::RemoveGroup(GroupQueueInfo * ginfo) +{ + sLog.outDebug("EligibleGroups::RemoveGroup"); + for(std::list::iterator itr = begin(); itr != end(); ++itr) + { + if((*itr)=ginfo) + { + sLog.outDebug("Group found, erase"); + erase(itr); + return; + } + } +} + +void BattleGroundQueue::SelectionPool::Init() +{ + sLog.outDebug("Initializing selection pool"); + SelectedGroups.clear(); + MaxGroup = 0; + PlayerCount = 0; +} + +GroupQueueInfo * BattleGroundQueue::SelectionPool::GetMaximalGroup() +{ + sLog.outDebug("Getting maximal group from SelectionPool"); + if(SelectedGroups.empty()) + { + sLog.outError("Getting max group when selection pool is empty, this should never happen."); + MaxGroup = NULL; + return 0; + } + 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; +} + +void BattleGroundQueue::SelectionPool::RemoveGroup(GroupQueueInfo *ginfo) +{ + sLog.outDebug("Removing group from SelectionPool"); + if(MaxGroup == ginfo) + MaxGroup = 0; + for(std::list::iterator itr = SelectedGroups.begin(); itr != SelectedGroups.end(); ++itr) + { + if((*itr)=ginfo) + { + SelectedGroups.erase(itr); + PlayerCount -= ginfo->Players.size(); + return; + } + } +} + +void BattleGroundQueue::SelectionPool::AddGroup(GroupQueueInfo * ginfo) +{ + sLog.outDebug("Adding group to SelectionPool"); + SelectedGroups.push_back(ginfo); + PlayerCount+=ginfo->Players.size(); + if(!MaxGroup || ginfo->Players.size() > MaxGroup->Players.size()) + { + sLog.outDebug("Setting the newly added group as MaxGroup"); + MaxGroup = ginfo; + } +} + + +GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, uint8 ArenaType, bool isRated) +{ + sLog.outDebug("Adding Group to BGQueue with leader %u",leader->GetGUIDLow()); + uint32 queue_id = leader->GetBattleGroundQueueIdFromLevel(); + + GroupQueueInfo* ginfo = new GroupQueueInfo; + ginfo->ArenaType = ArenaType; + ginfo->IsRated = isRated; + ginfo->IsInvitedToBGInstanceGUID = 0; + ginfo->JoinTime = getMSTime(); + ginfo->Team = leader->GetTeam(); + + ginfo->Players.clear(); + + m_QueuedGroups[queue_id].push_back(ginfo); + + return ginfo; +} + +void BattleGroundQueue::AddPlayer(Player *plr, GroupQueueInfo *ginfo) +{ + sLog.outDebug("Adding player %u to BGGroup in queue", plr->GetGUIDLow()); 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); + ginfo->Players[plr->GetGUID()] = &info; } void BattleGroundQueue::RemovePlayer(uint64 guid, bool decreaseInvitedCount) { + sLog.outDebug("removing player guid %u", guid); Player *plr = objmgr.GetPlayer(guid); 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) + { + sLog.outDebug("plr found"); + 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); if(itr != m_QueuedPlayers[i].end()) { + sLog.outDebug("player itr found."); queue_id = i; IsSet = true; break; } } } - 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()) + { + sLog.outDebug("player found, now remove"); + group->Players.erase(pitr); + } + + if(group->Players.empty()) + { + // player group is empty, remove group from wait queue + // not yet implemented, probably won't ever be :( + } + + if (group_itr != m_QueuedGroups[queue_id].end() && itr != m_QueuedPlayers[queue_id].end()) + { + if (!group->IsInvitedToBGInstanceGUID) { +/* // do nothing for now if(itr->second.Team == ALLIANCE) m_QueuedPlayers[queue_id].Alliance--; else - m_QueuedPlayers[queue_id].Horde--; + m_QueuedPlayers[queue_id].Horde--;*/ } else { if (decreaseInvitedCount) { - BattleGround* bg = sBattleGroundMgr.GetBattleGround(itr->second.IsInvitedToBGInstanceGUID); + sLog.outDebug("BGQueue - decreasing invited count"); + BattleGround* bg = sBattleGroundMgr.GetBattleGround(group->IsInvitedToBGInstanceGUID); if (bg) - bg->DecreaseInvitedCount(itr->second.Team); + bg->DecreaseInvitedCount(group->Team); + // update the join queue, maybe now the player's group fits in a queue! } } + // remove player queue info m_QueuedPlayers[queue_id].erase(itr); + // remove group queue info if needed + if(group->Players.empty()) + { + sLog.outDebug("BGQueue:RemovePlayer:deleting group"); + m_QueuedGroups[queue_id].erase(group_itr); + delete group; + } } } +bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side) +{ + sLog.outDebug("Inviting group to bg, groupsize %u, bgtype %u, instance %u, as side %u",ginfo->Players.size(), bg->GetTypeID(), bg->GetInstanceID(), side); + // set side if needed + if(side) + ginfo->Team = side; + + if(!ginfo->IsInvitedToBGInstanceGUID) + { + // not yet invited + // set invitation + ginfo->IsInvitedToBGInstanceGUID = bg->GetInstanceID(); + // 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 = 0; + if(bg->isArena()) + queueSlot = plr->GetBattleGroundQueueIndex(BATTLEGROUND_AA); + else + queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID()); + + 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; + } + + sLog.outDebug("Group already invited to bg, doing nothing"); + return false; +} + +// this function is responsible for the selection of queued groups when trying to create new battlegrounds +bool BattleGroundQueue::BuildSelectionPool(uint32 queue_id, uint32 MinPlayers, uint32 MaxPlayers, SelectionPoolBuildMode mode, uint8 ArenaType, bool isRated) +{ + sLog.outDebug("Building selection pool, mode %u", mode); + 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]), side, MaxPlayers, ArenaType, isRated); + // 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; +} + +void BattleGroundQueue::BGEndedRemoveInvites(BattleGround *bg) +{ + sLog.outDebug("BG Ended, removing invited groups"); + uint32 queue_id = bg->GetQueueType(); + uint32 bgInstanceId = bg->GetInstanceID(); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID()); + 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); + Update(bgQueueTypeId, bg->GetQueueType()); + 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) { + sLog.outDebug("Battleground: Update with bgtype %u, queueid %u", bgTypeId, queue_id); if (queue_id >= MAX_BATTLEGROUND_QUEUES) { //this is error, that caused crashes (not in , but now it shouldn't) @@ -156,158 +500,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; //battleground with free slot for player should be always the last in this queue - BGFreeSlotQueueType::iterator itr; - for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); ++itr) + BGFreeSlotQueueType::iterator itr, next; + for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); itr = next) // iterate only till .back(), not .end()! { + sLog.outDebug("Iterating BGFreeSlotQueue"); + 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) { + sLog.outDebug("proper bg with free slots found"); //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();) + sLog.outDebug("Iterating through queued groups"); + if(bg->GetFreeSlotsForTeam((*itr)->Team) >= (*itr)->Players.size()) { - Player* plr = objmgr.GetPlayer(*itr2); - if (!plr) - { - //something is wrong!, kick player from queue - sLog.outError("BATTLEGROUND: problem with inviting offline player to Battleground queue .... pls report bug"); - uint64 oldval = *itr2; - itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); - this->RemovePlayer(oldval, true); - continue; - } + sLog.outDebug("group fits in, inviting"); + 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()) + { + sLog.outDebug("bg type %u, instance %u is full, removing from free slot queue", bg->GetTypeID(), bg->GetInstanceID()); + //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 + //if bg is full, there is no need to invite other players, so break +// break; + } + } + } - // 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; + } + sLog.outDebug("BG Template found"); - // 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 rated 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; + // try to build the selection pools + bool bAllyOK = BuildSelectionPool(queue_id, MinPlayersPerTeam, MaxPlayersPerTeam, NORMAL_ALLIANCE, arenatype, isRated); + if(bAllyOK) + sLog.outDebug("Battleground: ally pool succesfully build"); + else + sLog.outDebug("Battleground: ally pool wasn't created"); + bool bHordeOK = BuildSelectionPool(queue_id, MinPlayersPerTeam, MaxPlayersPerTeam, NORMAL_HORDE, arenatype, isRated); + if(bHordeOK) + sLog.outDebug("Battleground: horde pool succesfully built"); + else + sLog.outDebug("Battleground: horde pool wasn't created"); - //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 - } - } + // if selection pools are ready, create the new bg + if (bAllyOK && bHordeOK) + { + BattleGround * bg2 = 0; + // special handling for arenas + if(bg_template->isArena()) + { + // Find a random arena, that can be created + uint8 arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL}; + uint32 arena_num = urand(0,2); + if( !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[arena_num%3])) && + !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+1)%3])) && + !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+2)%3])) ) + { + sLog.outError("Battleground: couldn't create arena"); + return; } + + // set the MaxPlayersPerTeam values based on arenatype + // setting the min player values isn't needed, since we won't be using that value later on. + switch(arenatype) + { + case ARENA_TYPE_2v2: + bg2->SetMaxPlayersPerTeam(2); + bg2->SetMaxPlayers(4); + break; + case ARENA_TYPE_3v3: + bg2->SetMaxPlayersPerTeam(3); + bg2->SetMaxPlayers(6); + break; + case ARENA_TYPE_5v5: + bg2->SetMaxPlayersPerTeam(5); + bg2->SetMaxPlayers(10); + break; + default: + break; + } } - } + else + { + // create new battleground + bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId); + } - /* THIS IS A CASE THAT IN QUEUE THERE IS ENOUGHT PLAYERS TO START NEW BG */ - //itr->end is the last BG - template, which is not already started! + if(!bg2) + { + sLog.outError("Battleground: couldn't create bg %u",bgTypeId); + return; + } - /* here will be a most of change, when we create battlegrounds instanciated */ - /* if (there is enought players to start new BG) - Battleground* newbg = sBattleGroundMgr.CreateNewBattleGround(bgTypeId) - - that function will use the COPY constructor on BattleGround class ( in bg manager we should have one battleground as a template - (battleground template will be used only to create new BGs, it will be an instance of BG class, but it won't ever start) */ + sLog.outDebug("BG Created."); - /* folowing code is working with current Battleground system and it should be removed, when BGs will work like instances */ - BattleGround* bg2 = sBattleGroundMgr.GetBattleGround(bgTypeId); - if (bg2->GetQueueType() != MAX_BATTLEGROUND_QUEUES || bg2->GetStatus() != STATUS_WAIT_QUEUE) - return; - if (m_QueuedPlayers[queue_id].Alliance >= bg2->GetMinPlayersPerTeam() && m_QueuedPlayers[queue_id].Horde >= bg2->GetMinPlayersPerTeam()) - { + // start the joining of the bg bg2->SetStatus(STATUS_WAIT_JOIN); bg2->SetQueueType(queue_id); + // initialize arena / rating info + bg2->SetArenaType(arenatype); + // set rating + bg2->SetRated(isRated); - for (PlayerGuidsSortedByTimeQueue::iterator itr2 = m_PlayersSortedByWaitTime[queue_id].begin(); itr2 != m_PlayersSortedByWaitTime[queue_id].end();) + std::list::iterator itr; + + // invite groups from horde selection pool + for(itr = m_SelectionPools[NORMAL_HORDE].SelectedGroups.begin(); itr != m_SelectionPools[NORMAL_HORDE].SelectedGroups.end(); ++itr) { - Player* plr = objmgr.GetPlayer(*itr2); - if (!plr) + InviteGroupToBG((*itr),bg2,HORDE); + } + + // invite groups from ally selection pools + for(itr = m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.begin(); itr != m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.end(); ++itr) + { + InviteGroupToBG((*itr),bg2,ALLIANCE); + } + + sLog.outDebug("Starting bg."); + // start the battleground + bg2->StartBattleGround(0); + sLog.outDebug("BG Started."); + } + + // 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(queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_HORDE_TEAM1,arenatype, isRated); + if(bOneSideHordeTeam1) + { + sLog.outDebug("one side horde team1 alright"); + // 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(queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_HORDE_TEAM2,arenatype, isRated); + + // 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; + else + sLog.outDebug("one side horde team2 alright"); + } + if(!bOneSideHordeTeam1) + { + // check for one sided ally + bOneSideAllyTeam1 = BuildSelectionPool(queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_ALLIANCE_TEAM1,arenatype, isRated); + if(bOneSideAllyTeam1) { - //something is wrong!, kick player from queue - sLog.outError("BATTLEGROUND: problem with inviting offline player to Battleground queue .... pls report bug"); - uint64 oldval = *itr2; - itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); - this->RemovePlayer(oldval, true); - continue; + // one team has been selected, find out if other can be selected too + std::list::iterator itr; + // temporarily change the team side to enable building the next pool excluding the already selected groups + for(itr = m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.end(); ++itr) + (*itr)->Team=HORDE; + + bOneSideAllyTeam2 = BuildSelectionPool(queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_ALLIANCE_TEAM2,arenatype, isRated); + + // change back the team to ally + for(itr = m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.end(); ++itr) + (*itr)->Team=ALLIANCE; } - /* TODO: (i'm not sure this code will be useful: - here should be some condition like if (bg2->isArena() && bg2->isRated()) + if(!bOneSideAllyTeam2) + bOneSideAllyTeam1 = false; + } + // 1-sided BuildSelectionPool() will work, because the MinPlayersPerTeam == MaxPlayersPerTeam in every arena!!!! + if( (bOneSideHordeTeam1 && bOneSideHordeTeam2) || + (bOneSideAllyTeam1 && bOneSideAllyTeam2) ) + { + // create random arena + uint8 arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL}; + uint32 arena_num = urand(0,2); + BattleGround* bg2 = NULL; + if( !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[arena_num%3])) && + !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+1)%3])) && + !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+2)%3])) ) { - invite players from 1 certain group on each faction to play arena match - } else if ....and existing code - */ - // player will be invited, if in bg there is a free slot for him - if (bg2->HasFreeSlotsForTeam(plr->GetTeam())) + sLog.outError("Could not create arena."); + return; + } + + sLog.outDebug("Arena created."); + // init stats + switch(arenatype) { - // iterator to player's queue status - QueuedPlayersMap::iterator itrPlayerStatus = m_QueuedPlayers[queue_id].find(*itr2); + case ARENA_TYPE_2v2: + bg2->SetMaxPlayersPerTeam(2); + bg2->SetMaxPlayers(4); + break; + case ARENA_TYPE_3v3: + bg2->SetMaxPlayersPerTeam(3); + bg2->SetMaxPlayers(6); + break; + case ARENA_TYPE_5v5: + bg2->SetMaxPlayersPerTeam(5); + bg2->SetMaxPlayers(10); + break; + default: + break; + } - // remove him from time queue - itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); + bg2->SetRated(isRated); + // which side has enough players? + uint32 side = 0; + SelectionPoolBuildMode mode1, mode2; + // find out what pools are we using + if(bOneSideAllyTeam1 && bOneSideAllyTeam2) + { + side = ALLIANCE; + mode1 = ONESIDE_ALLIANCE_TEAM1; + mode2 = ONESIDE_ALLIANCE_TEAM2; + } + else + { + side = HORDE; + mode1 = ONESIDE_HORDE_TEAM1; + mode2 = ONESIDE_HORDE_TEAM2; + } + // assigned team of the other group + uint32 other_side; + if(side == ALLIANCE) + other_side = HORDE; + else + other_side = ALLIANCE; - // only check to be sure ... but this condition shouldn't be true (if it is true, then there is a bug somewhere and report it) - if (itrPlayerStatus == m_QueuedPlayers[queue_id].end()) - continue; + // set the team type of the arena + if(side == ALLIANCE) + bg2->SetTeamType(BG_TEAM_TYPE_ALLIANCE_ONLY); + else + bg2->SetTeamType(BG_TEAM_TYPE_HORDE_ONLY); - //check if player is not already invited - if (!itrPlayerStatus->second.IsInvitedToBGInstanceGUID) - { - itrPlayerStatus->second.IsInvitedToBGInstanceGUID = bg2->GetInstanceID(); - itrPlayerStatus->second.InviteTime = getMSTime(); - itrPlayerStatus->second.LastInviteTime = getMSTime(); + // start the joining of the bg + bg2->SetStatus(STATUS_WAIT_JOIN); + bg2->SetQueueType(queue_id); + // initialize arena / rating info + bg2->SetArenaType(arenatype); - if(itrPlayerStatus->second.Team == ALLIANCE) - m_QueuedPlayers[queue_id].Alliance--; - else - m_QueuedPlayers[queue_id].Horde--; + std::list::iterator itr; - sBattleGroundMgr.InvitePlayer(plr, bg2->GetInstanceID()); + // 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); + } - 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); - } + // 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); } - else - ++itr2; + + sLog.outDebug("Starting arena"); + bg2->StartBattleGround(0); + sLog.outDebug("Arena started"); } - bg2->StartBattleGround(0); } } @@ -317,6 +813,7 @@ bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 p_time) { + sLog.outDebug("Executing BGInvite event"); Player* plr = objmgr.GetPlayer( m_PlayerGuid ); // player logged off (we should do nothing, he is correctly removed from queue in another procedure) @@ -330,16 +827,17 @@ BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID); if (bg) { - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID()); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID()); + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue { // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems - BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()]; + BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()]; BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid); - if (qItr != qpMap.end() && qItr->second.IsInvitedToBGInstanceGUID == m_BgInstanceGUID) + if (qItr != qpMap.end() && qItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID) { WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, BG_REMIND_INVITE_TIME, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, qItr->second.GroupInfo->Team, queueSlot, STATUS_WAIT_JOIN, BG_REMIND_INVITE_TIME, 0); plr->GetSession()->SendPacket(&data); } } @@ -355,6 +853,7 @@ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) { + sLog.outDebug("Executing BGRemove Event"); Player* plr = objmgr.GetPlayer( m_PlayerGuid ); if (!plr) // player logged off (we should do nothing, he is correctly removed from queue in another procedure) @@ -366,17 +865,18 @@ BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID); if (bg) { - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID()); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID()); + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue { // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems - if (sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()].find(m_PlayerGuid)->second.IsInvitedToBGInstanceGUID == m_BgInstanceGUID) + if (sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()].find(m_PlayerGuid)->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID) { - plr->RemoveBattleGroundQueueId(bg->GetTypeID()); - sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].RemovePlayer(m_PlayerGuid, true); - sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].Update(bg->GetTypeID(), bg->GetQueueType()); + plr->RemoveBattleGroundQueueId(bgQueueTypeId); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(m_PlayerGuid, true); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgQueueTypeId, bg->GetQueueType()); WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr->GetTeam(), queueSlot, STATUS_NONE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, m_PlayersTeam, queueSlot, STATUS_NONE, 0, 0); plr->GetSession()->SendPacket(&data); } } @@ -397,24 +897,46 @@ BattleGroundMgr::BattleGroundMgr() { + sLog.outDebug("Creating BattleGroundMgr"); m_BattleGrounds.clear(); } BattleGroundMgr::~BattleGroundMgr() { - for(std::map::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); ++itr) - delete itr->second; + sLog.outDebug("Destroying BattleGRoundMgr"); + 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(); } 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); + if(itr->second->m_SetDeleteThis) + { + BattleGround * bg = itr->second; + sLog.outDebug("deleting bg insta %u", bg->GetInstanceID()); + m_BattleGrounds.erase(itr); + delete bg; + } + } } void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2) { + sLog.outDebug("Building BGStatusPacket"); // we can be in 3 queues in same time... if(StatusID == 0) { @@ -427,7 +949,7 @@ data->Initialize(SMSG_BATTLEFIELD_STATUS, (4+1+1+4+2+4+1+4+4+4)); *data << uint32(QueueSlot); // queue id (0...2) - player can be in 3 queues in time - *data << uint8(bg->GetArenaType()); // team type (0=BG, 2=2x2, 3=3x3, 5=5x5), for arenas + *data << uint8(bg->GetArenaType()); // team type (0=BG, 2=2x2, 3=3x3, 5=5x5), for arenas // NOT PROPER VALUE IF ARENA ISN'T RUNNING YET!!!! switch(bg->GetTypeID()) // value depends on bg id { case BATTLEGROUND_AV: @@ -460,9 +982,9 @@ } if(bg->isArena() && (StatusID == STATUS_WAIT_QUEUE)) - *data << uint32(BATTLEGROUND_AA); // all arenas + *data << uint32(BATTLEGROUND_AA); // all arenas I don't think so. else - *data << uint32(bg->GetTypeID()); // BG id from DBC + *data << uint32(bg->GetTypeID()); // BG id from DBC *data << uint16(0x1F90); // unk value 8080 *data << uint32(bg->GetInstanceID()); // instance id @@ -497,6 +1019,7 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) { + sLog.outDebug("Building PvpLogPacket"); uint8 type = (bg->isArena() ? 1 : 0); // checked on 2.1.3 data->Initialize(MSG_PVP_LOG_DATA, (1+1+4+40*bg->GetPlayerScoresSize())); @@ -505,7 +1028,7 @@ { for(uint8 i = 0; i < 2; i++) { - *data << uint32(3000+1+i); // rating change: showed value - 3000 + *data << uint32(3000+bg->m_ArenaTeamRatingChanges[i]); // rating change: showed value - 3000 *data << uint8(0); // string } } @@ -536,11 +1059,13 @@ { // that part probably wrong Player *plr = objmgr.GetPlayer(itr->first); + uint32 team = bg->GetPlayerTeam(plr->GetGUID()); + if(!team) team = plr->GetTeam(); if(plr) { - if(plr->GetTeam() == HORDE) + if(team == HORDE) *data << uint8(0); - else if(plr->GetTeam() == ALLIANCE) + else if(team == ALLIANCE) *data << uint8(1); else *data << uint8(0); @@ -624,20 +1149,109 @@ *data << uint64(plr->GetGUID()); } -void BattleGroundMgr::InvitePlayer(Player* plr, uint32 bgInstanceGUID) +void BattleGroundMgr::InvitePlayer(Player* plr, uint32 bgInstanceGUID, uint32 team) { + sLog.outDebug("bgmgr::inviteplayer %u to %u", plr->GetGUIDLow(), bgInstanceGUID); // set invited player counters: BattleGround* bg = this->GetBattleGround(bgInstanceGUID); - bg->IncreaseInvitedCount(plr->GetTeam()); + bg->IncreaseInvitedCount(team); + if(bg->isArena() && bg->isRated()) + { + switch(bg->GetArenaType()) + { + case ARENA_TYPE_2v2: + bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(0)); + break; + case ARENA_TYPE_3v3: + bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(1)); + break; + case ARENA_TYPE_5v5: + bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(2)); + break; + default: + break; + } + } + // create invite events: //add events to player's counters ---- this is not good way - there should be something like global event processor, where we should add those events BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), bgInstanceGUID); plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(BG_REMIND_INVITE_TIME)); - BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), bgInstanceGUID, plr->GetTeam()); + BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), bgInstanceGUID, team); plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(2*BG_REMIND_INVITE_TIME)); } +BattleGround * BattleGroundMgr::GetBattleGroundTemplate(uint32 bgTypeId) +{ + sLog.outDebug("Getting bg template for %u", bgTypeId); + return BGFreeSlotQueue[bgTypeId].empty() ? NULL : BGFreeSlotQueue[bgTypeId].back(); +} + +BattleGround * BattleGroundMgr::CreateNewBattleGround(uint32 bgTypeId) +{ + sLog.outDebug("creating new bg %u", bgTypeId); + // Create the BG + BattleGround *bg = NULL; + + BattleGround *bg_template = GetBattleGroundTemplate(bgTypeId); + + if(!bg_template) + { + sLog.outDebug("CreateNewBG - bg template not found for %u", bgTypeId); + return 0; + } + + 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 + } + sLog.outDebug("generating instance id"); + bg->SetInstanceID(MapManager::Instance().GenerateInstanceId()); // set instance id + sLog.outDebug("id generated: %u",bg->GetInstanceID()); + bg->Reset(); + sLog.outDebug("bg reseted"); + if(!(bg->SetupBattleGround())) // setup the battleground in its update function to ensure that a player is present in the instance. Otherwise the objects won't be spawned :( + { + sLog.outDebug("CreateNewBattleGround - SetupBattleGround failed for bg %u", bgTypeId); + delete bg; + return 0; + } + sLog.outDebug("bg is set up"); + bg->AddToBGFreeSlotQueue(); + + AddBattleGround(bg->GetInstanceID(), bg); + sLog.outDebug("bg added to queue and update list"); + return bg; +} + uint32 BattleGroundMgr::CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO) { // Create the BG @@ -657,13 +1271,19 @@ } bg->SetMapId(MapID); + + // set unloadflag to false + Map* map = const_cast(MapManager::Instance().GetBaseMap(MapID)); +// Map * map = MapManager::Instance().GetMap(GetMapId(), m_bgobjects[BG_AB_OBJECT_GATE_H]); + if(map) map->SetUnloadMap(false); + bg->Reset(); - if(!bg->SetupBattleGround()) +/* if(!bg->SetupBattleGround()) do not setup for template bgs! { delete bg; return 0; } - +*/ BattlemasterListEntry const *bl = sBattlemasterListStore.LookupEntry(bgTypeId); //in previous method is checked if exists entry in sBattlemasterListStore, so no check needed if (bl) @@ -672,7 +1292,8 @@ } bg->SetTypeID(bgTypeId); - bg->SetInstanceID(bgTypeId); // temporary +// bg->SetInstanceID(bgTypeId); // temporary + bg->SetInstanceID(0); // template bg, instance id is 0 bg->SetMinPlayersPerTeam(MinPlayersPerTeam); bg->SetMaxPlayersPerTeam(MaxPlayersPerTeam); bg->SetMinPlayers(MinPlayersPerTeam*2); @@ -684,9 +1305,9 @@ //add BaggleGround instance to FreeSlotQueue bg->AddToBGFreeSlotQueue(); - AddBattleGround(bg->GetInstanceID(), bg); +// AddBattleGround(bg->GetInstanceID(), bg); // do not! we're instantiating! only working ones need to be added, but not the templates! //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(); + return bgTypeId; //bg->GetInstanceID(); } void BattleGroundMgr::CreateInitialBattleGrounds() @@ -841,18 +1462,28 @@ } } -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 %f, %f, %f, %f", pl->GetName(), x, y, z, O); - pl->TeleportTo(mapid, x, y, z, O); + // pl->SetInstanceId(instanceId); DO NOT DO THIS!!!! EVER!!!! (well, not here anyway) + // actually, thinking a bit more, this wasn't used at all. + pl->m_BoundInstances[mapid]=std::pair(instanceId, pl->GetGUIDLow()); + // teleport as gm command + pl->TeleportTo(mapid, x, y, z, O, true, true, true); } + else + { + sLog.outError("player %u trying to port to non-existent bg instance %u",pl->GetGUIDLow(), instanceId); + } } void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, uint64 guid) @@ -864,3 +1495,28 @@ 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) const +{ + return IsArenaType(bgTypeId)?BATTLEGROUND_AA:bgTypeId; +} Index: src/game/BattleGroundMgr.h =================================================================== --- src/game/BattleGroundMgr.h (revision 5116) +++ src/game/BattleGroundMgr.h (working copy) @@ -36,16 +36,23 @@ #define BG_REMIND_INVITE_TIME 60000 // when this time pass, player get invitation request again +struct GroupQueueInfo; struct PlayerQueueInfo { 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 + GroupQueueInfo * GroupInfo; +}; + +struct GroupQueueInfo +{ + std::map Players; uint32 Team; // Player team (ALLIANCE/HORDE) - bool IsRated; - bool AsGroup; // uint32 GroupId; - uint8 ArenaType; + bool IsRated; + uint8 ArenaType; + uint32 JoinTime; + uint32 IsInvitedToBGInstanceGUID; // was invited to certain BG }; struct PlayersCount @@ -63,6 +70,7 @@ //uint32 AverageTime; //not already implemented (it should be average time in queue for last 10 players) }; +class BattleGround; class BattleGroundQueue { public: @@ -72,15 +80,69 @@ uint32 GetType(); void SetType(uint32 type);*/ - void Update(uint32 bgTypeId, uint32 queue_id); + void Update(uint32 bgTypeId, uint32 queue_id, uint8 arenateam = 0, bool isRated = false); - void AddPlayer(Player *plr, uint32 bgTypeId); + GroupQueueInfo * AddGroup(Player * leader, uint8 ArenaType, bool isRated); + 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; +// uint32 GetPlayerCount(uint32 queueId, uint32 team, uint8 arenatype, bool isRated, uint32 instanceGUID = 0); + + 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]; + + typedef std::list GroupIdsSortedByTimeQueue; + GroupIdsSortedByTimeQueue m_GroupIdsSortedByWaitTime[MAX_BATTLEGROUND_QUEUES]; + + class EligibleGroups : public std::list + { + public: + void Init(QueuedGroupsList * source, uint32 side, uint32 MaxPlayers, uint8 ArenaType = 0, bool IsRated = false); + void RemoveGroup(GroupQueueInfo * ginfo); + }; + + EligibleGroups m_EligibleGroups; + + 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 queue_id, uint32 MinPlayers, uint32 MaxPlayers, SelectionPoolBuildMode mode, uint8 ArenaType = 0, bool isRated = false); + + private: + + bool InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side); +// bool InvitePlayerToBG(PlayerGuidsSortedByTimeQueue::iterator &itr, Player * plr, BattleGround * bg, uint32 side); }; /* @@ -139,13 +201,13 @@ /* Player invitation */ // called from Queue update, or from Addplayer to queue - void InvitePlayer(Player* plr, uint32 bgInstanceGUID); + void InvitePlayer(Player* plr, uint32 bgInstanceGUID, uint32 team); /* Battlegrounds */ BattleGroundSet::iterator GetBattleGroundsBegin() { return m_BattleGrounds.begin(); }; BattleGroundSet::iterator GetBattleGroundsEnd() { return m_BattleGrounds.end(); }; - BattleGround* GetBattleGround(uint8 ID) + BattleGround* GetBattleGround(uint32 ID) { BattleGroundSet::iterator i = m_BattleGrounds.find(ID); if(i != m_BattleGrounds.end()) @@ -154,13 +216,17 @@ 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 @@ -170,6 +236,10 @@ void SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, uint64 guid); + bool IsArenaType(uint32 bgTypeId) const; + bool IsBattleGroundType(uint32 bgTypeId) const; + uint32 BGQueueTypeId(uint32 bgTypeId) const; + private: /* Battlegrounds */ Index: src/game/BattleGroundNA.cpp =================================================================== --- src/game/BattleGroundNA.cpp (revision 5116) +++ src/game/BattleGroundNA.cpp (working copy) @@ -92,9 +92,9 @@ }*/ } -void BattleGroundNA::AddPlayer(Player *plr) +void BattleGroundNA::AddPlayer(Player *plr, uint32 team) { - BattleGround::AddPlayer(plr); + BattleGround::AddPlayer(plr, team); //create score and add it to map, default values are set in constructor BattleGroundNAScore* sc = new BattleGroundNAScore; @@ -103,7 +103,13 @@ void BattleGroundNA::RemovePlayer(Player *plr, uint64 guid) { + if(GetStatus() == STATUS_WAIT_LEAVE) + return; + if(!GetPlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetPlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } void BattleGroundNA::HandleKillPlayer(Player *player, Player *killer) @@ -117,14 +123,20 @@ return; } - uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam()); + uint32 killer_team = GetPlayerTeam(killer->GetGUID()); + if(!killer_team) killer_team = killer->GetTeam(); + uint32 killer_team_index = GetTeamIndexByTeamId(killer_team); + m_TeamKills[killer_team_index]++; // add kills to killer's team - if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(player->GetTeam())) + uint32 team = GetPlayerTeam(player->GetGUID()); + if(!team) team = player->GetTeam(); + + if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(team)) { // all opponents killed - EndBattleGround(killer->GetTeam()); + EndBattleGround(killer_team); } } Index: src/game/BattleGroundNA.h =================================================================== --- src/game/BattleGroundNA.h (revision 5116) +++ src/game/BattleGroundNA.h (working copy) @@ -55,7 +55,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/BattleGroundRL.cpp =================================================================== --- src/game/BattleGroundRL.cpp (revision 5116) +++ src/game/BattleGroundRL.cpp (working copy) @@ -93,9 +93,9 @@ }*/ } -void BattleGroundRL::AddPlayer(Player *plr) +void BattleGroundRL::AddPlayer(Player *plr, uint32 team) { - BattleGround::AddPlayer(plr); + BattleGround::AddPlayer(plr, team); //create score and add it to map, default values are set in constructor BattleGroundRLScore* sc = new BattleGroundRLScore; @@ -104,7 +104,13 @@ void BattleGroundRL::RemovePlayer(Player *plr, uint64 guid) { + if(GetStatus() == STATUS_WAIT_LEAVE) + return; + if(!GetPlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetPlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } void BattleGroundRL::HandleKillPlayer(Player *player, Player *killer) @@ -118,14 +124,20 @@ return; } - uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam()); + uint32 killer_team = GetPlayerTeam(killer->GetGUID()); + if(!killer_team) killer_team = killer->GetTeam(); + uint32 killer_team_index = GetTeamIndexByTeamId(killer_team); + m_TeamKills[killer_team_index]++; // add kills to killer's team - if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(player->GetTeam())) + uint32 team = GetPlayerTeam(player->GetGUID()); + if(!team) team = player->GetTeam(); + + if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(team)) { // all opponents killed - EndBattleGround(killer->GetTeam()); + EndBattleGround(killer_team); } } Index: src/game/BattleGroundRL.h =================================================================== --- src/game/BattleGroundRL.h (revision 5116) +++ src/game/BattleGroundRL.h (working copy) @@ -51,7 +51,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/BattleGroundWS.cpp =================================================================== --- src/game/BattleGroundWS.cpp (revision 5116) +++ src/game/BattleGroundWS.cpp (working copy) @@ -50,6 +50,9 @@ { m_Events |= 0x01; + for(uint32 i = WS_SPIRIT_MAIN_ALLIANCE; i <= WS_SPIRIT_MAIN_HORDE; i++) + SpawnBGCreature(i, RESPAWN_IMMEDIATELY); + for(uint32 i = BG_WS_OBJECT_DOOR_A_1; i <= BG_WS_OBJECT_DOOR_H_4; i++) SpawnBGObject(i, RESPAWN_IMMEDIATELY); @@ -111,9 +114,9 @@ } } -void BattleGroundWS::AddPlayer(Player *plr) +void BattleGroundWS::AddPlayer(Player *plr, uint32 team) { - BattleGround::AddPlayer(plr); + BattleGround::AddPlayer(plr, team); //create score and add it to map, default values are set in constructor BattleGroundWGScore* sc = new BattleGroundWGScore; @@ -388,7 +391,7 @@ return; uint32 SpellId = 0; - uint64 buff_guid = 0; + GameObject* buff_guid = 0; switch(Trigger) { case 3686: // Alliance elixir of speed spawn. Trigger not working, because located inside other areatrigger, can be replaced by IsWithinDist(object, dist) in BattleGround::Update(). @@ -432,7 +435,7 @@ if(buff_guid) { - GameObject *obj = HashMapHolder::Find(buff_guid); + GameObject *obj = buff_guid; if(obj) { if(!obj->isSpawned()) @@ -503,12 +506,13 @@ m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_BASE; m_TeamScores[BG_TEAM_ALLIANCE] = 0; m_TeamScores[BG_TEAM_HORDE] = 0; - +/* currently crashes the server, must find out the problem, till then, let the instance unloading solve this 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 5116) +++ src/game/BattleGroundWS.h (working copy) @@ -97,7 +97,7 @@ BG_WS_FLAG_STATE_ON_GROUND = 3 }; -enum BattleGroundGaveyardsWS +enum BattleGroundGraveyardsWS { WS_GRAVEYARD_MAIN_ALLIANCE = 771, WS_GRAVEYARD_MAIN_HORDE = 772 @@ -122,7 +122,7 @@ void Update(time_t diff); /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); + virtual void AddPlayer(Player *plr, uint32 team = 0); /* BG Flags */ uint64 GetAllianceFlagPickerGUID() const { return m_FlagKeepers[BG_TEAM_ALLIANCE]; } Index: src/game/Chat.cpp =================================================================== --- src/game/Chat.cpp (revision 5116) +++ src/game/Chat.cpp (working copy) @@ -385,6 +385,7 @@ { "movegens", SEC_ADMINISTRATOR, &ChatHandler::HandleMovegensCommand, "", NULL }, { "cast", SEC_ADMINISTRATOR, &ChatHandler::HandleCastCommand, "", NULL }, { "castback", SEC_ADMINISTRATOR, &ChatHandler::HandleCastBackCommand, "", NULL }, + { "flusharenapoints", SEC_ADMINISTRATOR, &ChatHandler::HandleFlushArenaPointsCommand, "", NULL }, //! Development Commands { "setvalue", SEC_ADMINISTRATOR, &ChatHandler::HandleSetValue, "", NULL }, Index: src/game/Chat.h =================================================================== --- src/game/Chat.h (revision 5116) +++ src/game/Chat.h (working copy) @@ -322,6 +322,7 @@ bool HandleWritePDumpCommand(const char *args); bool HandleCastCommand(const char *args); bool HandleCastBackCommand(const char *args); + bool HandleFlushArenaPointsCommand(const char *args); //! Development Commands bool HandleSetValue(const char* args); Index: src/game/GroupHandler.cpp =================================================================== --- src/game/GroupHandler.cpp (revision 5116) +++ src/game/GroupHandler.cpp (working copy) @@ -26,6 +26,8 @@ #include "ObjectMgr.h" #include "Player.h" #include "Group.h" +#include "Chat.h" +#include "Language.h" #include "ObjectAccessor.h" /* differeces from off: @@ -257,6 +259,15 @@ SendPartyResult(PARTY_OP_LEAVE, name, PARTY_RESULT_NOT_IN_YOUR_PARTY); return; } + + if(player && player->InBattleGround()) + { + WorldPacket data; + ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, LANG_BG_CANNOT_IN_BG, NULL); + SendPacket(&data); + + return; + } /********************/ // everything's fine, do it @@ -295,7 +306,15 @@ /** error handling **/ /********************/ + if(GetPlayer() && GetPlayer()->InBattleGround()) + { + WorldPacket data; + ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, LANG_BG_CANNOT_IN_BG, NULL); + SendPacket(&data); + return; + } + // everything's fine, do it SendPartyResult(PARTY_OP_LEAVE, GetPlayer()->GetName(), PARTY_RESULT_OK); Index: src/game/Language.h =================================================================== --- src/game/Language.h (revision 5116) +++ src/game/Language.h (working copy) @@ -535,6 +535,11 @@ #define LANG_INVISIBLE_INVISIBLE "You are now invisible." #define LANG_INVISIBLE_VISIBLE "You are now visible." +#define LANG_BG_CANNOT_IN_BG "You can't do that right now." +#define LANG_BG_GROUP_TOO_LARGE "Your group is too large for this battleground. Please regroup to join." +#define LANG_ARENA_GROUP_TOO_LARGE "Your group is too large for this arena. Please regroup to join." +#define LANG_ARENA_YOUR_TEAM_ONLY "Your group has members not in your arena team. Please regroup to join." + #define LANG_BG_A_WINS "The Alliance wins!" #define LANG_BG_H_WINS "The Horde wins!" #define LANG_BG_WS_ONE_MINUTE "The battle for Warsong Gulch begins in 1 minute." @@ -568,6 +573,8 @@ #define LANG_BG_AB_A_NEAR_VICTORY "The Alliance has gathered $1776W resources, and is near victory!" #define LANG_BG_AB_H_NEAR_VICTORY "The Horde has gathered $1777W resources, and is near victory!" +#define LANG_ARENA_GOLD_WINS "The Gold Team wins!" +#define LANG_ARENA_GREEN_WINS "The Green Team wins!" #define LANG_ARENA_ONE_TOOLOW "You must be level %u to form an arena team" #define LANG_ARENA_ONE_MINUTE "One minute until the Arena battle begins!" #define LANG_ARENA_THIRTY_SECONDS "Thirty seconds until the Arena battle begins!" Index: src/game/Level3.cpp =================================================================== --- src/game/Level3.cpp (revision 5116) +++ src/game/Level3.cpp (working copy) @@ -42,6 +42,7 @@ #include "SkillExtraItems.h" #include "SystemConfig.h" #include "Config/ConfigEnv.h" +#include "ArenaTeam.h" //reload commands bool ChatHandler::HandleReloadCommand(const char* arg) @@ -4616,3 +4617,52 @@ return true; } + +bool ChatHandler::HandleFlushArenaPointsCommand(const char * /*args*/) +{ + 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); + } + mod += pl->GetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY); + if(mod > 5000) + mod = 5000; + + pl->SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, mod); + + CharacterDatabase.PExecute("UPDATE `arena_team_member` SET `points_to_add` = '0' WHERE `guid` = '%u'",guid); + } + + sWorld.SendWorldText("Finished distributing arena points to online players. Now saving... ", NULL); + + ObjectAccessor::Instance().SaveAllPlayers(); + + sWorld.SendWorldText("Online players saved.", NULL); + + sWorld.SendWorldText("Distributing arena points to offline players...", NULL); + + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("UPDATE `character` b, `arena_team_member` a SET b.`data` = CONCAT( SUBSTRING_INDEX(b.`data`, ' ', '%u'),' ', CAST( IF ( ((CAST( SUBSTRING( b.`data` FROM (CHAR_LENGTH(SUBSTRING_INDEX(b.`data`,' ','%u')) + 2) FOR (CHAR_LENGTH(SUBSTRING_INDEX(b.`data`,' ','%u')) - CHAR_LENGTH(SUBSTRING_INDEX(b.`data`,' ','%u')) - 1) ) AS UNSIGNED) + (SELECT MAX(a.`points_to_add`) FROM `arena_team_member` WHERE a.`guid` = b.`guid` GROUP BY a.`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(a.`points_to_add`) FROM `arena_team_member` WHERE a.`guid` = b.`guid` GROUP BY a.`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); + 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); + return true; +} Index: src/game/Map.cpp =================================================================== --- src/game/Map.cpp (revision 5116) +++ src/game/Map.cpp (working copy) @@ -270,6 +270,8 @@ else i_data = NULL; + i_unloadmap = sWorld.getConfig(CONFIG_GRID_UNLOAD); + i_Players.clear(); } @@ -392,7 +394,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 Index: src/game/Map.h =================================================================== --- src/game/Map.h (revision 5116) +++ src/game/Map.h (working copy) @@ -148,6 +148,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); virtual void UnloadAll(); @@ -206,7 +208,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)); } @@ -294,6 +296,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 5116) +++ src/game/MapInstanced.cpp (working copy) @@ -44,6 +44,7 @@ { if (i->second->GetPlayersCount() == 0) { + DEBUG_LOG("reseting instance in update for instance id = %u and map id = %u named %s",this->GetInstanceId(),this->GetId(),this->GetMapName()); i->second->Reset(); // avoid doing ++ on invalid data InstancedMaps::iterator i_old = i; @@ -187,7 +188,7 @@ if (!instantiator && instantiator_online) { - sLog.outDebug("MAPINSTANCED: Player '%s' is not in group, instantiating map for player", player->GetName()); + sLog.outDebug("MAPINSTANCED: Player '%s' is not in group, instantiating map %u for player", player->GetName(), GetId()); instantiator = player; } @@ -230,11 +231,13 @@ // check, if we have to generate new instance if (!instantiator_bound || (InstanceId == 0)) { + sLog.outDebug("MAPINSTANCED: Generating new instance id"); // yes, new instance id has to be generated InstanceId = MapManager::Instance().GenerateInstanceId(); } else { + sLog.outDebug("MAPINSTANCED: Instantiator bound, finding map..."); // find map of the designated instance and verify it map = _FindMap(InstanceId); } @@ -242,7 +245,7 @@ // now create the instance CreateInstance(InstanceId, map); - + sLog.outDebug("MAPINSTANCED::GetInstance() Instance Created"); // we do need to bind instance, if the instance we use is valid, not temporary if (player->m_InstanceValid) { @@ -304,6 +307,7 @@ // map for the instance is present, but the instance itself is no more valid, destroy map Guard guard(*this); + DEBUG_LOG("reseting instance in createinstance for instance id = %u and map id = %u named %s",this->GetInstanceId(),this->GetId(),this->GetMapName()); map->Reset(); m_InstancedMaps.erase(InstanceId); delete map; @@ -314,10 +318,12 @@ // 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); + map->SetUnloadMap(GetUnloadMap()); m_InstancedMaps[InstanceId] = map; } } Index: src/game/MapManager.cpp =================================================================== --- src/game/MapManager.cpp (revision 5116) +++ 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 5116) +++ src/game/MovementHandler.cpp (working copy) @@ -79,17 +79,21 @@ { if(bg->GetMapId() == _player->GetMapId()) // we teleported to bg { - if(!bg->GetBgRaid(_player->GetTeam())) // first player joined + uint32 team = bg->GetPlayerTeam(_player->GetGUID()); + if(!team) + team = _player->GetTeam(); + _player->RemoveFromGroup(); + 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()); } } } @@ -485,3 +489,5 @@ _player->SummonIfPossible(); } + + Index: src/game/PetitionsHandler.cpp =================================================================== --- src/game/PetitionsHandler.cpp (revision 5116) +++ src/game/PetitionsHandler.cpp (working copy) @@ -49,9 +49,6 @@ { CHECK_PACKET_SIZE(recv_data, 8+8+4+1+5*8+2+1+4+4); - if(_player->GetGuildId()) - return; - sLog.outDebug("Received opcode CMSG_PETITION_BUY"); //recv_data.hexlike(); @@ -95,6 +92,9 @@ uint32 type = 0; if(pCreature->isTabardDesigner()) { + if(_player->GetGuildId()) + return; + charterid = GUILD_CHARTER; cost = GUILD_CHARTER_COST; type = 9; @@ -188,10 +188,31 @@ charter->SetState(ITEM_CHANGED, _player); _player->SendNewItem(charter, 1, true, false); + // a petition is invalid, if both the owner and the type matches + QueryResult *result = CharacterDatabase.PQuery("SELECT `petitionguid` FROM `petition` WHERE `ownerguid` = '%u' AND `type` = '%u'", _player->GetGUIDLow(), type); + + std::ostringstream ssInvalidPetitionGUIDs; + + if (result) + { + + do + { + Field *fields = result->Fetch(); + ssInvalidPetitionGUIDs << "'" << fields[0].GetUInt32() << "' , "; + } while (result->NextRow()); + + delete result; + } + + // delete petitions with the same guid as this one + ssInvalidPetitionGUIDs << "'" << charter->GetGUIDLow() << "'"; + + sLog.outDebug("Invalid petition GUIDs: %s", ssInvalidPetitionGUIDs.str().c_str()); CharacterDatabase.escape_string(name); CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM `petition` WHERE `ownerguid` = '%u' OR `petitionguid` = '%u' and `type` = '%u'", _player->GetGUIDLow(), charter->GetGUIDLow(),type); - CharacterDatabase.PExecute("DELETE FROM `petition_sign` WHERE `ownerguid` = '%u' or `petitionguid` = '%u'", _player->GetGUIDLow(), charter->GetGUIDLow()); + CharacterDatabase.PExecute("DELETE FROM `petition` WHERE `petitionguid` IN ( %s )", ssInvalidPetitionGUIDs.str().c_str()); + CharacterDatabase.PExecute("DELETE FROM `petition_sign` WHERE `petitionguid` IN ( %s )", ssInvalidPetitionGUIDs.str().c_str()); CharacterDatabase.PExecute("INSERT INTO `petition` (`ownerguid`, `petitionguid`, `name`, `type`) VALUES ('%u', '%u', '%s', '%u')", _player->GetGUIDLow(), charter->GetGUIDLow(), name.c_str(), type); CharacterDatabase.CommitTransaction(); @@ -205,9 +226,6 @@ sLog.outDebug("Received opcode CMSG_PETITION_SHOW_SIGNATURES"); //recv_data.hexlike(); - if(_player->GetGuildId()) - return; - uint8 signs = 0; uint64 petitionguid; recv_data >> petitionguid; // petition guid @@ -215,14 +233,17 @@ // solve (possible) some strange compile problems with explicit use GUID_LOPART(petitionguid) at some GCC versions (wrong code optimization in compiler?) uint32 petitionguid_low = GUID_LOPART(petitionguid); - QueryResult *result = CharacterDatabase.PQuery("SELECT `petitionguid` FROM `petition` WHERE `petitionguid` = '%u'", petitionguid_low); + QueryResult *result = CharacterDatabase.PQuery("SELECT `petitionguid`, `type` FROM `petition` WHERE `petitionguid` = '%u'", petitionguid_low); if(!result) { sLog.outError("any petition on server..."); return; } - + Field *fields = result->Fetch(); + uint32 type = fields[1].GetUInt32(); delete result; + if(type==9 && _player->GetGuildId()) + return; result = CharacterDatabase.PQuery("SELECT `playerguid` FROM `petition_sign` WHERE `petitionguid` = '%u'", petitionguid_low); @@ -317,13 +338,14 @@ { data << uint32(9); data << uint32(9); + data << uint32(0); } else { data << type-1; data << type-1; + data << type; } - data << uint32(0); // 4 data << uint32(0); // 5 data << uint32(0); // 6 data << uint32(0); // 7 @@ -463,7 +485,7 @@ //client doesn't allow to sign petition two times by one character, but not check sign by another character from same account //not allow sign another player from already sign player account - result = CharacterDatabase.PQuery("SELECT `playerguid` FROM `petition_sign` WHERE `player_account` = '%u'", GetAccountId()); + result = CharacterDatabase.PQuery("SELECT `playerguid` FROM `petition_sign` WHERE `player_account` = '%u' AND petitionguid = '%u'", GetAccountId(), GUID_LOPART(petitionguid)); if(result) { @@ -638,16 +660,29 @@ } else { - for(uint8 i = 0; i < 3; i++) + uint32 type_slot; + switch(type) { - if(_player->GetArenaTeamId(i)) - { + case ARENA_TEAM_2v2: + type_slot = 0; + break; + case ARENA_TEAM_3v3: + type_slot = 1; + break; + case ARENA_TEAM_5v5: + type_slot = 2; + break; + default: + type_slot = 0; + break; + } + if(_player->GetArenaTeamId(type_slot)) + { //data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4); //data << (uint32)PETITION_TURN_ALREADY_IN_GUILD; // already in guild //_player->GetSession()->SendPacket(&data); SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM); return; - } } } @@ -735,6 +770,7 @@ ArenaTeam* at = new ArenaTeam; if(!at->create(_player->GetGUID(), type, name)) { + sLog.outError("PetitionsHandler: arena team create failed."); delete at; delete result; return; @@ -748,11 +784,13 @@ // register team and add captain objmgr.AddArenaTeam(at); + sLog.outDebug("PetitonsHandler: arena team added to objmrg"); // add members for(uint8 i = 0; i < signs; ++i) { Field* fields = result->Fetch(); + sLog.outDebug("PetitionsHandler: adding arena member %u", fields[0].GetUInt64()); at->AddMember(fields[0].GetUInt64()); result->NextRow(); } @@ -820,21 +858,21 @@ data << uint32(ARENA_TEAM_CHARTER_2v2); // charter entry data << uint32(16161); // charter display id data << uint32(ARENA_TEAM_CHARTER_2v2_COST); // charter cost - data << uint32(0); // unknown + data << uint32(2); // unknown data << uint32(2); // required signs? // 3v3 data << uint32(2); // index data << uint32(ARENA_TEAM_CHARTER_3v3); // charter entry data << uint32(16161); // charter display id data << uint32(ARENA_TEAM_CHARTER_3v3_COST); // charter cost - data << uint32(0); // unknown + data << uint32(3); // unknown data << uint32(3); // required signs? // 5v5 data << uint32(3); // index data << uint32(ARENA_TEAM_CHARTER_5v5); // charter entry data << uint32(16161); // charter display id data << uint32(ARENA_TEAM_CHARTER_5v5_COST); // charter cost - data << uint32(0); // unknown + data << uint32(5); // unknown data << uint32(5); // required signs? } //for(uint8 i = 0; i < count; i++) Index: src/game/Player.cpp =================================================================== --- src/game/Player.cpp (revision 5116) +++ src/game/Player.cpp (working copy) @@ -104,6 +104,9 @@ m_zoneUpdateId = 0; m_zoneUpdateTimer = 0; + m_areaUpdateId = 0; + m_areaUpdateTimer = 0; + m_dismountCost = 0; m_nextSave = sWorld.getConfig(CONFIG_INTERVAL_SAVE); @@ -937,6 +940,20 @@ m_zoneUpdateTimer -= p_time; } + if (m_areaUpdateTimer > 0) + { + if(p_time >= m_areaUpdateTimer) + { + uint32 newarea = GetAreaId(); + if( m_areaUpdateId != newarea ) + UpdateArea(newarea); + else + m_areaUpdateTimer = ZONE_UPDATE_INTERVAL; + } + else + m_areaUpdateTimer -= p_time; + } + if (isAlive()) { RegenerateAll(); @@ -2726,6 +2743,27 @@ removeSpell(itr->second.spell); } +void Player::RemoveArenaSpellCooldowns() +{ + SpellCooldowns::iterator itr, next; + for(itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end(); itr = next) + { + next = itr; + ++next; + SpellEntry const * entry = sSpellStore.LookupEntry(itr->first); + if( entry && + (entry->RecoveryTime <= 15 * 60000) && + (entry->CategoryRecoveryTime <= 15 * 60000)) + { + WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8+4)); + data << uint32(itr->first); + data << GetGUID(); + GetSession()->SendPacket(&data); + m_spellCooldowns.erase(itr); + } + } +} + void Player::RemoveAllSpellCooldown() { if(!m_spellCooldowns.empty()) @@ -5665,8 +5703,27 @@ return zone; } +void Player::UpdateArea(uint32 newArea) +{ + m_areaUpdateId = newArea; + m_areaUpdateTimer = ZONE_UPDATE_INTERVAL; + + AreaTableEntry const* area = GetAreaEntryByAreaID(newArea); + + if(area && (area->flags & 0x80)) + { + SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP); + } + else + { + if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) && !sWorld.IsFFAPvPRealm()) + RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP); + } +} + void Player::UpdateZone(uint32 newZone) { + UpdateArea(GetAreaId()); m_zoneUpdateId = newZone; m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL; @@ -8627,6 +8684,12 @@ pProto->SubClass != ITEM_SUBCLASS_ARMOR_SHIELD && pProto->InventoryType != INVTYPE_RELIC) return EQUIP_ERR_NOT_IN_COMBAT; + 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 @@ -8760,6 +8823,12 @@ pProto->SubClass != ITEM_SUBCLASS_ARMOR_SHIELD && pProto->InventoryType != INVTYPE_RELIC ) return EQUIP_ERR_NOT_IN_COMBAT; + 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; @@ -9842,6 +9911,52 @@ } } +void Player::DestroyConjuredItems( bool update ) +{ + 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) @@ -14688,10 +14803,14 @@ void Player::RemovePetitionsAndSigns(uint64 guid, uint32 type) { - QueryResult *result = CharacterDatabase.PQuery("SELECT `ownerguid`,`petitionguid` FROM `petition_sign` WHERE `playerguid` = '%u'", GUID_LOPART(guid)); + QueryResult *result = NULL; + if(type==10) + result = CharacterDatabase.PQuery("SELECT `ownerguid`,`petitionguid` FROM `petition_sign` WHERE `playerguid` = '%u'", GUID_LOPART(guid)); + else + result = CharacterDatabase.PQuery("SELECT `ownerguid`,`petitionguid` FROM `petition_sign` WHERE `playerguid` = '%u' AND `type` = '%u'", GUID_LOPART(guid), type); if(result) { - do + do //this part effectively does nothing, since the deletion / modification only takes place _after_ the PetitionQuery. Though I don't know if the result remains intact if I execute the delete query beforehand. { Field *fields = result->Fetch(); uint64 ownerguid = MAKE_GUID(fields[0].GetUInt32(), HIGHGUID_PLAYER); @@ -14706,15 +14825,23 @@ delete result; - CharacterDatabase.PExecute("DELETE FROM `petition_sign` WHERE `playerguid` = '%u'", GUID_LOPART(guid)); + if(type==10) + CharacterDatabase.PExecute("DELETE FROM `petition_sign` WHERE `playerguid` = '%u'", GUID_LOPART(guid)); + else + CharacterDatabase.PExecute("DELETE FROM `petition_sign` WHERE `playerguid` = '%u' AND `type` = '%u'", GUID_LOPART(guid), type); } CharacterDatabase.BeginTransaction(); if(type == 10) + { CharacterDatabase.PExecute("DELETE FROM `petition` WHERE `ownerguid` = '%u'", GUID_LOPART(guid)); + CharacterDatabase.PExecute("DELETE FROM `petition_sign` WHERE `ownerguid` = '%u'", GUID_LOPART(guid)); + } else + { CharacterDatabase.PExecute("DELETE FROM `petition` WHERE `ownerguid` = '%u' AND `type` = '%u'", GUID_LOPART(guid), type); - CharacterDatabase.PExecute("DELETE FROM `petition_sign` WHERE `ownerguid` = '%u'", GUID_LOPART(guid)); + CharacterDatabase.PExecute("DELETE FROM `petition_sign` WHERE `ownerguid` = '%u' AND `type` = '%u'", GUID_LOPART(guid), type); + } CharacterDatabase.CommitTransaction(); } @@ -15970,7 +16097,7 @@ bool Player::GetBGAccessByLevel(uint32 bgTypeId) const { - BattleGround *bg = sBattleGroundMgr.GetBattleGround(bgTypeId); + BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); if(!bg) return false; Index: src/game/Player.h =================================================================== --- src/game/Player.h (revision 5116) +++ src/game/Player.h (working copy) @@ -1023,6 +1023,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 ); @@ -1332,6 +1333,7 @@ void AddSpellCooldown(uint32 spell_id, uint32 itemid, time_t end_time); void ProhibitSpellScholl(SpellSchools idSchool, uint32 unTimeMs ); void RemoveSpellCooldown(uint32 spell_id) { m_spellCooldowns.erase(spell_id); } + void RemoveArenaSpellCooldowns(); void RemoveAllSpellCooldown(); void _LoadSpellCooldowns(QueryResult *result); void _SaveSpellCooldowns(); @@ -1362,6 +1364,7 @@ PvPInfo pvpInfo; void UpdatePvP(bool state, bool ovrride=false); void UpdateZone(uint32 newZone); + void UpdateArea(uint32 newArea); void UpdatePvPFlag(time_t currTime); /** todo: -maybe move UpdateDuelFlag+DuelComplete to independent DuelHandler.. **/ @@ -1683,9 +1686,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; @@ -1830,6 +1835,7 @@ */ uint32 m_bgBattleGroundQueueID[PLAYER_MAX_BATTLEGROUND_QUEUES]; uint32 m_bgEntryPointMap; + uint32 m_bgEntryPointInstanceId; float m_bgEntryPointX; float m_bgEntryPointY; float m_bgEntryPointZ; @@ -1995,6 +2001,9 @@ uint32 m_zoneUpdateId; uint32 m_zoneUpdateTimer; + uint32 m_areaUpdateId; + uint32 m_areaUpdateTimer; + uint32 m_deathTimer; time_t m_resurrectingSicknessExpire; Index: src/game/Spell.cpp =================================================================== --- src/game/Spell.cpp (revision 5116) +++ src/game/Spell.cpp (working copy) @@ -2667,6 +2667,14 @@ if((m_spellInfo->AttributesEx3 & 0x800) != 0) return SPELL_FAILED_ONLY_BATTLEGROUNDS; + if( m_caster->GetTypeId() == TYPEID_PLAYER && + ((Player*)m_caster)->InBattleGround() && + ((Player*)m_caster)->GetBattleGround() && + ((Player*)m_caster)->GetBattleGround()->isArena() && + (m_spellInfo->RecoveryTime > 15 * 60000 || + m_spellInfo->CategoryRecoveryTime > 15 * 60000) ) + return SPELL_FAILED_NOT_IN_ARENA; + // zone check uint32 zone_id = m_caster->GetAreaId(); Index: src/game/SpellHandler.cpp =================================================================== --- src/game/SpellHandler.cpp (revision 5116) +++ src/game/SpellHandler.cpp (working copy) @@ -66,6 +66,14 @@ return; } + if ( pUser->InBattleGround() && pUser->GetBattleGround() && pUser->GetBattleGround()->isArena() && + (proto->Class == ITEM_CLASS_CONSUMABLE) && !(proto->Flags & ITEM_FLAGS_CONJURED) && !(proto->SubClass == ITEM_SUBCLASS_BANDAGE)) + { + //send use error: not in arena + pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH,pItem,NULL); + return; + } + if (pUser->isInCombat()) { for(int i = 0; i < 5; ++i) @@ -466,7 +474,8 @@ if(((BattleGroundWS*)bg)->GetFlagState(ALLIANCE) != BG_WS_FLAG_STATE_ON_BASE) return; // check if it's correct flag - if(((BattleGroundWS*)bg)->m_BgObjects[BG_WS_OBJECT_A_FLAG] != obj->GetGUID()) + if(!(((BattleGroundWS*)bg)->m_BgObjects[BG_WS_OBJECT_A_FLAG]) || + (((BattleGroundWS*)bg)->m_BgObjects[BG_WS_OBJECT_A_FLAG]->GetGUID() != obj->GetGUID())) return; // check player team if(_player->GetTeam() == ALLIANCE) @@ -481,7 +490,8 @@ if(((BattleGroundWS*)bg)->GetFlagState(HORDE) != BG_WS_FLAG_STATE_ON_BASE) return; // check if it's correct flag - if(((BattleGroundWS*)bg)->m_BgObjects[BG_WS_OBJECT_H_FLAG] != obj->GetGUID()) + if(!(((BattleGroundWS*)bg)->m_BgObjects[BG_WS_OBJECT_H_FLAG]) || + (((BattleGroundWS*)bg)->m_BgObjects[BG_WS_OBJECT_H_FLAG]->GetGUID() != obj->GetGUID())) return; // check player team if(_player->GetTeam() == HORDE) Index: src/game/Unit.cpp =================================================================== --- src/game/Unit.cpp (revision 5116) +++ src/game/Unit.cpp (working copy) @@ -591,8 +591,11 @@ // 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 ); } @@ -3799,6 +3802,19 @@ } } +void Unit::RemoveArenaAuras(bool onleave) +{ + // in join, remove positive buffs, on end, remove negative + // used to remove positive visible auras in arenas + for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();) + { + if (!iter->second->IsPassive() && (iter->second->IsPositive() ^ onleave)) + RemoveAura(iter, true); + else + ++iter; + } +} + void Unit::RemoveAllAurasOnDeath() { // used just after dieing to remove all visible auras Index: src/game/Unit.h =================================================================== --- src/game/Unit.h (revision 5116) +++ src/game/Unit.h (working copy) @@ -825,6 +825,7 @@ void RemoveAreaAurasByOthers(uint64 guid = 0); void RemoveAllAuras(); + void RemoveArenaAuras(bool onleave = false); void RemoveAllAurasOnDeath(); void DelayAura(uint32 spellId, uint32 effindex, int32 delaytime); bool SetAurDuration(uint32 spellId, uint32 effindex, uint32 duration); Index: src/game/WorldSession.cpp =================================================================== --- src/game/WorldSession.cpp (revision 5116) +++ src/game/WorldSession.cpp (working copy) @@ -656,7 +656,7 @@ { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - int32 bgTypeId = _player->GetBattleGroundQueueId(i); + int32 bgTypeId = sBattleGroundMgr.BGQueueTypeId(_player->GetBattleGroundQueueId(i)); if (bgTypeId != 0) { _player->RemoveBattleGroundQueueId(bgTypeId);