From a89080730dbdf0d6372f6b85dddbf7f7cfbf8acc Mon Sep 17 00:00:00 2001 From: VladimirMangos Date: Mon, 15 Dec 2008 03:08:01 +0300 Subject: [PATCH 1/9] [6909] Correctly equip all possible amount non stackable items from custom initial items with >1 amount instead equip single and ignore other. Also corrently report error at buy to equip slot not single item (impossible using normal client way) instead silencely ingnore amount and equip single by stack price. --- src/game/Player.cpp | 46 +++++++++++++++++++++++++--------------- src/game/Player.h | 6 +++--- src/shared/revision_nr.h | 2 +- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 3b5494057..60f89f5ce 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -668,12 +668,12 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 } } - StoreNewItemInBestSlot(item_id, count); + StoreNewItemInBestSlots(item_id, count); } } for (PlayerCreateInfoItems::const_iterator item_id_itr = info->item.begin(); item_id_itr!=info->item.end(); ++item_id_itr++) - StoreNewItemInBestSlot(item_id_itr->item_id, item_id_itr->item_amount); + StoreNewItemInBestSlots(item_id_itr->item_id, item_id_itr->item_amount); // bags and main-hand weapon must equipped at this moment // now second pass for not equipped (offhand weapon/shield if it attempt equipped before main-hand weapon) @@ -713,24 +713,30 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 return true; } -bool Player::StoreNewItemInBestSlot(uint32 titem_id, uint32 titem_amount) +bool Player::StoreNewItemInBestSlots(uint32 titem_id, uint32 titem_amount) { sLog.outDebug("STORAGE: Creating initial item, itemId = %u, count = %u",titem_id, titem_amount); - // attempt equip - uint16 eDest; - uint8 msg = CanEquipNewItem( NULL_SLOT, eDest, titem_id, titem_amount, false ); - if( msg == EQUIP_ERR_OK ) + // attempt equip by one + while(titem_amount > 0) { - EquipNewItem( eDest, titem_id, titem_amount, true); + uint16 eDest; + uint8 msg = CanEquipNewItem( NULL_SLOT, eDest, titem_id, false ); + if( msg != EQUIP_ERR_OK ) + break; + + EquipNewItem( eDest, titem_id, true); AutoUnequipOffhandIfNeed(); - return true; // equipped + --titem_amount; } + if(titem_amount == 0) + return true; // equipped + // attempt store ItemPosCountVec sDest; // store in main bag to simplify second pass (special bags can be not equipped yet at this moment) - msg = CanStoreNewItem( INVENTORY_SLOT_BAG_0, NULL_SLOT, sDest, titem_id, titem_amount ); + uint8 msg = CanStoreNewItem( INVENTORY_SLOT_BAG_0, NULL_SLOT, sDest, titem_id, titem_amount ); if( msg == EQUIP_ERR_OK ) { StoreNewItem( sDest, titem_id, true, Item::GenerateItemRandomPropertyId(titem_id) ); @@ -9307,10 +9313,10 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const } ////////////////////////////////////////////////////////////////////////// -uint8 Player::CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, uint32 count, bool swap ) const +uint8 Player::CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, bool swap ) const { dest = 0; - Item *pItem = Item::CreateItem( item, count, this ); + Item *pItem = Item::CreateItem( item, 1, this ); if( pItem ) { uint8 result = CanEquipItem(slot, dest, pItem, swap ); @@ -9974,12 +9980,12 @@ Item* Player::_StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, boo } } -Item* Player::EquipNewItem( uint16 pos, uint32 item, uint32 count, bool update ) +Item* Player::EquipNewItem( uint16 pos, uint32 item, bool update ) { - Item *pItem = Item::CreateItem( item, count, this ); + Item *pItem = Item::CreateItem( item, 1, this ); if( pItem ) { - ItemAddedQuestCheck( item, count ); + ItemAddedQuestCheck( item, 1 ); Item * retItem = EquipItem( pos, pItem, update ); return retItem; @@ -16636,8 +16642,14 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint } else if( IsEquipmentPos( bag, slot ) ) { + if(pProto->BuyCount * count != 1) + { + SendEquipError( EQUIP_ERR_ITEM_CANT_BE_EQUIPPED, NULL, NULL ); + return false; + } + uint16 dest; - uint8 msg = CanEquipNewItem( slot, dest, item, pProto->BuyCount * count, false ); + uint8 msg = CanEquipNewItem( slot, dest, item, false ); if( msg != EQUIP_ERR_OK ) { SendEquipError( msg, NULL, NULL ); @@ -16659,7 +16671,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint } } - if(Item *it = EquipNewItem( dest, item, pProto->BuyCount * count, true )) + if(Item *it = EquipNewItem( dest, item, true )) { uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,pProto->BuyCount * count); diff --git a/src/game/Player.h b/src/game/Player.h index 89bc27b99..93ec74775 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1053,7 +1053,7 @@ class MANGOS_DLL_SPEC Player : public Unit } uint8 CanStoreItems( Item **pItem,int count) const; - uint8 CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, uint32 count, bool swap ) const; + uint8 CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, bool swap ) const; uint8 CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bool not_loading = true ) const; uint8 CanUnequipItems( uint32 item, uint32 count ) const; uint8 CanUnequipItem( uint16 src, bool swap ) const; @@ -1064,10 +1064,10 @@ class MANGOS_DLL_SPEC Player : public Unit uint8 CanUseAmmo( uint32 item ) const; Item* StoreNewItem( ItemPosCountVec const& pos, uint32 item, bool update,int32 randomPropertyId = 0 ); Item* StoreItem( ItemPosCountVec const& pos, Item *pItem, bool update ); - Item* EquipNewItem( uint16 pos, uint32 item, uint32 count, bool update ); + Item* EquipNewItem( uint16 pos, uint32 item, bool update ); Item* EquipItem( uint16 pos, Item *pItem, bool update ); void AutoUnequipOffhandIfNeed(); - bool StoreNewItemInBestSlot(uint32 item_id, uint32 item_count); + bool StoreNewItemInBestSlots(uint32 item_id, uint32 item_count); uint8 _CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count = NULL) const; uint8 _CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 entry, uint32 count, Item *pItem = NULL, bool swap = false, uint32* no_space_count = NULL ) const; diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 2d7234568..945fa5396 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "6908" + #define REVISION_NR "6909" #endif // __REVISION_NR_H__ From c51a6bc59e823c5d5476e5a4994980c1c34c7cf9 Mon Sep 17 00:00:00 2001 From: ApoC Date: Mon, 15 Dec 2008 14:31:31 +0100 Subject: [PATCH 2/9] Fixed runtime abort in WorldSession::HandleChangePlayerNameOpcodeCallBack . Signed-off-by: ApoC --- src/game/CharacterHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index 50c6df71f..cc93b4369 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -978,7 +978,7 @@ void WorldSession::HandleChangePlayerNameOpcodeCallBack(QueryResult *result, uin CharacterDatabase.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), uint32(AT_LOGIN_RENAME),guidLow); CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", guidLow); - sLog.outChar("Account: %d (IP: %s) Character:[%s] (guid:%u) Changed name to: %s",session->GetAccountId(),session->GetRemoteAddress(),oldname.c_str(),guidLow,newname.c_str()); + sLog.outChar("Account: %d (IP: %s) Character:[%s] (guid:%u) Changed name to: %s",session->GetAccountId(),session->GetRemoteAddress().c_str(),oldname.c_str(),guidLow,newname.c_str()); WorldPacket data(SMSG_CHAR_RENAME,1+8+(newname.size()+1)); data << (uint8)RESPONSE_SUCCESS; From a5331ab517202d1bab1417e02fc171ba2b3d1dab Mon Sep 17 00:00:00 2001 From: ApoC Date: Mon, 15 Dec 2008 23:52:46 +0100 Subject: [PATCH 3/9] [6910] Implemented rewritten arenas. Original author w12x@getmangos.com Many thanks also to: Balrok@github.com for long time updating. Triply@github.com for great work in rewriting process. All who I forget to name. Signed-off-by: ApoC --- .../2008_12_15_01_character_arenas.sql | 6 + sql/updates/2008_12_15_01_mangos_arenas.sql | 26 + src/game/ArenaTeam.cpp | 355 ++-- src/game/ArenaTeam.h | 91 +- src/game/ArenaTeamHandler.cpp | 75 +- src/game/BattleGround.cpp | 527 ++++-- src/game/BattleGround.h | 58 +- src/game/BattleGroundAB.cpp | 8 + src/game/BattleGroundBE.cpp | 55 +- src/game/BattleGroundBE.h | 5 +- src/game/BattleGroundEY.cpp | 23 +- src/game/BattleGroundHandler.cpp | 605 ++++--- src/game/BattleGroundMgr.cpp | 1430 ++++++++++++++--- src/game/BattleGroundMgr.h | 141 +- src/game/BattleGroundNA.cpp | 69 +- src/game/BattleGroundNA.h | 13 +- src/game/BattleGroundRL.cpp | 66 +- src/game/BattleGroundRL.h | 13 +- src/game/BattleGroundWS.cpp | 37 +- src/game/CharacterHandler.cpp | 6 +- src/game/Chat.cpp | 2 + src/game/Chat.h | 2 + src/game/GameEvent.cpp | 2 +- src/game/Group.cpp | 48 + src/game/Group.h | 1 + src/game/Language.h | 25 +- src/game/Level1.cpp | 31 +- src/game/Level3.cpp | 7 + src/game/MapInstanced.cpp | 12 +- src/game/MapManager.cpp | 2 +- src/game/MovementHandler.cpp | 45 +- src/game/ObjectMgr.cpp | 40 +- src/game/ObjectMgr.h | 18 +- src/game/PetitionsHandler.cpp | 147 +- src/game/Player.cpp | 123 +- src/game/Player.h | 68 +- src/game/SpellAuras.cpp | 3 +- src/game/SpellEffects.cpp | 2 +- src/game/Unit.cpp | 48 +- src/game/Unit.h | 1 + src/game/World.cpp | 29 +- src/game/World.h | 6 + src/game/WorldSession.h | 3 +- src/game/debugcmds.cpp | 7 + src/mangosd/mangosd.conf.dist.in | 42 + src/shared/Database/DBCStructure.h | 5 +- src/shared/revision_nr.h | 2 +- 47 files changed, 3406 insertions(+), 924 deletions(-) create mode 100644 sql/updates/2008_12_15_01_character_arenas.sql create mode 100644 sql/updates/2008_12_15_01_mangos_arenas.sql diff --git a/sql/updates/2008_12_15_01_character_arenas.sql b/sql/updates/2008_12_15_01_character_arenas.sql new file mode 100644 index 000000000..2976d343e --- /dev/null +++ b/sql/updates/2008_12_15_01_character_arenas.sql @@ -0,0 +1,6 @@ +CREATE TABLE `saved_variables` ( + `NextArenaPointDistributionTime` bigint(40) UNSIGNED NOT NULL DEFAULT '0' +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Variable Saves'; + +ALTER TABLE `arena_team_member` ADD COLUMN `personal_rating` int(10) UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `characters` ADD COLUMN `arena_pending_points` int(10) UNSIGNED NOT NULL default '0'; diff --git a/sql/updates/2008_12_15_01_mangos_arenas.sql b/sql/updates/2008_12_15_01_mangos_arenas.sql new file mode 100644 index 000000000..f57ea8a3f --- /dev/null +++ b/sql/updates/2008_12_15_01_mangos_arenas.sql @@ -0,0 +1,26 @@ +DELETE FROM `command` WHERE `name` = "flusharenapoints"; +INSERT INTO `command` (`name`, `security`, `help`) VALUES +('flusharenapoints','3','Syntax: .flusharenapoints\r\n\r\nUse it to distribute arena points based on arena team ratings, and start a new week.'); + +DELETE FROM mangos_string WHERE entry BETWEEN 7007 AND 7023; + +INSERT INTO mangos_string (entry, content_default) VALUES + (7007,'Your group is too large for this battleground. Please regroup to join.'), + (7008,'Your group is too large for this arena. Please regroup to join.'), + (7009,'Your group has members not in your arena team. Please regroup to join.'), + (7010,'Your group does not have enough players to join this match.'), + (7011,'The Gold Team wins!'), + (7012,'The Green Team wins!'), + (7013, 'There aren\'t enough players in this battleground. It will end soon unless some more players join to balance the fight.'), + (7014, 'Your group has an offline member. Please remove him before joining.'), + (7015, 'Your group has players from the opposing faction. You can\'t join the battleground as a group.'), + (7016, 'Your group has players from different battleground brakets. You can\'t join as group.'), + (7017, 'Someone in your party is already in this battleground queue. (S)he must leave it before joining as group.'), + (7018, 'Someone in your party is Deserter. You can\'t join as group.'), + (7019, 'Someone in your party is already in three battleground queues. You cannot join as group.'), + (7020, 'You cannot teleport to a battleground or arena map.'), + (7021, 'You cannot summon players to a battleground or arena map.'), + (7022, 'You must be in GM mode to teleport to a player in a battleground.'), + (7023, 'You cannot teleport to a battleground from another battleground. Please leave the current battleground first.'); + +DELETE FROM mangos_string WHERE entry = 714 OR entry = 716; diff --git a/src/game/ArenaTeam.cpp b/src/game/ArenaTeam.cpp index 72ed8d679..b6df34f98 100644 --- a/src/game/ArenaTeam.cpp +++ b/src/game/ArenaTeam.cpp @@ -44,7 +44,7 @@ ArenaTeam::~ArenaTeam() } -bool ArenaTeam::create(uint64 captainGuid, uint32 type, std::string ArenaTeamName) +bool ArenaTeam::Create(uint64 captainGuid, uint32 type, std::string ArenaTeamName) { if(!objmgr.GetPlayer(captainGuid)) // player not exist return false; @@ -67,9 +67,9 @@ bool ArenaTeam::create(uint64 captainGuid, uint32 type, std::string ArenaTeamNam CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid='%u'", Id); CharacterDatabase.PExecute("INSERT INTO arena_team (arenateamid,name,captainguid,type,BackgroundColor,EmblemStyle,EmblemColor,BorderStyle,BorderColor) " "VALUES('%u','%s','%u','%u','%u','%u','%u','%u','%u')", - Id, ArenaTeamName.c_str(), GUID_LOPART(CaptainGuid), Type, BackgroundColor,EmblemStyle,EmblemColor,BorderStyle,BorderColor); + Id, ArenaTeamName.c_str(), GUID_LOPART(CaptainGuid), Type, BackgroundColor, EmblemStyle, EmblemColor, BorderStyle, BorderColor); CharacterDatabase.PExecute("INSERT INTO arena_team_stats (arenateamid, rating, games, wins, played, wins2, rank) VALUES " - "('%u', '%u', '%u', '%u', '%u', '%u', '%u')", Id,stats.rating,stats.games_week,stats.wins_week,stats.games_season,stats.wins_season,stats.rank); + "('%u', '%u', '%u', '%u', '%u', '%u', '%u')", Id, stats.rating, stats.games_week, stats.wins_week, stats.games_season, stats.wins_season, stats.rank); CharacterDatabase.CommitTransaction(); @@ -77,7 +77,7 @@ bool ArenaTeam::create(uint64 captainGuid, uint32 type, std::string ArenaTeamNam return true; } -bool ArenaTeam::AddMember(uint64 PlayerGuid) +bool ArenaTeam::AddMember(const uint64& PlayerGuid) { std::string plName; uint8 plClass; @@ -132,39 +132,23 @@ bool ArenaTeam::AddMember(uint64 PlayerGuid) newmember.personal_rating = 1500; members.push_back(newmember); - CharacterDatabase.PExecute("INSERT INTO arena_team_member (arenateamid,guid) VALUES ('%u', '%u')", Id, GUID_LOPART(newmember.guid)); + CharacterDatabase.PExecute("INSERT INTO arena_team_member (arenateamid, guid, personal_rating) VALUES ('%u', '%u', '%u')", Id, GUID_LOPART(newmember.guid), newmember.personal_rating ); if(pl) { pl->SetInArenaTeam(Id, GetSlot()); pl->SetArenaTeamIdInvited(0); + pl->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot()*6) + 5, newmember.personal_rating ); // hide promote/remove buttons if(CaptainGuid != PlayerGuid) - pl->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1); - } - else - { - Tokens tokens; - if(Player::LoadValuesArrayFromDB(tokens,PlayerGuid)) - { - Player::SetUInt32ValueInArray(tokens,PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6), Id); - // hide promote/remove buttons - if(CaptainGuid != PlayerGuid) - Player::SetUInt32ValueInArray(tokens,PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1); - - Player::SaveValuesArrayInDB(tokens,PlayerGuid); - } + pl->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6) + 1, 1); } return true; } bool ArenaTeam::LoadArenaTeamFromDB(uint32 ArenaTeamId) { - LoadStatsFromDB(ArenaTeamId); - LoadMembersFromDB(ArenaTeamId); - - // 0 1 2 3 4 5 6 7 8 QueryResult *result = CharacterDatabase.PQuery("SELECT arenateamid,name,captainguid,type,BackgroundColor,EmblemStyle,EmblemColor,BorderStyle,BorderColor FROM arena_team WHERE arenateamid = '%u'", ArenaTeamId); if(!result) @@ -184,6 +168,22 @@ bool ArenaTeam::LoadArenaTeamFromDB(uint32 ArenaTeamId) delete result; + // only load here, so additional checks can be made + LoadStatsFromDB(ArenaTeamId); + LoadMembersFromDB(ArenaTeamId); + + if(Empty()) + { + // arena team is empty, delete from db + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid = '%u'", ArenaTeamId); + CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u'", ArenaTeamId); + CharacterDatabase.PExecute("DELETE FROM arena_team_stats WHERE arenateamid = '%u'", ArenaTeamId); + CharacterDatabase.CommitTransaction(); + // return false + return false; + } + return true; } @@ -209,49 +209,37 @@ void ArenaTeam::LoadStatsFromDB(uint32 ArenaTeamId) void ArenaTeam::LoadMembersFromDB(uint32 ArenaTeamId) { - Field *fields; - - QueryResult *result = CharacterDatabase.PQuery("SELECT guid,played_week,wons_week,played_season,wons_season FROM arena_team_member WHERE arenateamid = '%u'", ArenaTeamId); + // 0 1 2 3 4 5 6 7 + QueryResult *result = CharacterDatabase.PQuery("SELECT member.guid,played_week,wons_week,played_season,wons_season,personal_rating,name,class " + "FROM arena_team_member member " + "INNER JOIN characters chars on member.guid = chars.guid " + "WHERE member.arenateamid = '%u'", ArenaTeamId); if(!result) return; do { - fields = result->Fetch(); + Field *fields = result->Fetch(); ArenaTeamMember newmember; newmember.guid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER); - LoadPlayerStats(&newmember); newmember.games_week = fields[1].GetUInt32(); newmember.wins_week = fields[2].GetUInt32(); newmember.games_season = fields[3].GetUInt32(); newmember.wins_season = fields[4].GetUInt32(); + newmember.personal_rating = fields[5].GetUInt32(); + newmember.name = fields[6].GetCppString(); + newmember.Class = fields[7].GetUInt8(); members.push_back(newmember); }while( result->NextRow() ); delete result; } -void ArenaTeam::LoadPlayerStats(ArenaTeamMember *member) -{ - Field *fields; - - QueryResult *result = CharacterDatabase.PQuery("SELECT name,class FROM characters WHERE guid = '%u'", GUID_LOPART(member->guid)); - if(!result) - return; - fields = result->Fetch(); - member->name = fields[0].GetCppString(); - member->Class = fields[1].GetUInt8(); - - delete result; -} - -void ArenaTeam::SetCaptain(uint64 guid) +void ArenaTeam::SetCaptain(const uint64& guid) { // disable remove/promote buttons Player *oldcaptain = objmgr.GetPlayer(GetCaptain()); if(oldcaptain) oldcaptain->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1); - else - Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1, GetCaptain()); // set new captain CaptainGuid = guid; @@ -263,14 +251,11 @@ void ArenaTeam::SetCaptain(uint64 guid) Player *newcaptain = objmgr.GetPlayer(guid); if(newcaptain) newcaptain->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 0); - else - Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 0, guid); } void ArenaTeam::DelMember(uint64 guid) { - MemberList::iterator itr; - for (itr = members.begin(); itr != members.end(); itr++) + for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) { if (itr->guid == guid) { @@ -280,17 +265,19 @@ void ArenaTeam::DelMember(uint64 guid) } Player *player = objmgr.GetPlayer(guid); + if(player) { player->SetInArenaTeam(0, GetSlot()); player->GetSession()->SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, GetName(), "", 0); - } - else - { - Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6), 0, guid); + // delete all info regarding this team + for(int i = 0; i < 6; ++i) + { + player->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6) + i, 0); + } } - CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE guid = '%u'", GUID_LOPART(guid)); + CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u' AND guid = '%u'", GetId(), GUID_LOPART(guid)); } void ArenaTeam::Disband(WorldSession *session) @@ -300,23 +287,15 @@ void ArenaTeam::Disband(WorldSession *session) session->BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_DISBANDED_S, 2, session->GetPlayerName(), GetName(), ""); BroadcastPacket(&data); - uint32 count = members.size(); - uint64 *memberGuids = new uint64[count]; - - MemberList::iterator itr; - uint32 i=0; - for(itr = members.begin(); itr != members.end(); itr++) + while (!members.empty()) { - memberGuids[i] = itr->guid; - ++i; + // Removing from members is done in DelMember. + DelMember(members.front().guid); } - for(uint32 j = 0; j < count; j++) - DelMember(memberGuids[j]); - delete[] memberGuids; - CharacterDatabase.BeginTransaction(); CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid = '%u'", Id); + CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u'", Id); //< this should be alredy done by calling DelMember(memberGuids[j]); for each member CharacterDatabase.PExecute("DELETE FROM arena_team_stats WHERE arenateamid = '%u'", Id); CharacterDatabase.CommitTransaction(); objmgr.RemoveArenaTeam(this); @@ -334,34 +313,18 @@ void ArenaTeam::Roster(WorldSession *session) for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) { pl = objmgr.GetPlayer(itr->guid); - if(pl) - { - data << uint64(pl->GetGUID()); // guid - data << uint8(1); // online flag - data << pl->GetName(); // member name - data << uint32(itr->guid == GetCaptain() ? 0 : 1);// unknown - data << uint8(pl->getLevel()); // unknown, probably level - data << uint8(pl->getClass()); // class - data << uint32(itr->games_week); // played this week - data << uint32(itr->wins_week); // wins this week - data << uint32(itr->games_season); // played this season - data << uint32(itr->wins_season); // wins this season - data << uint32(itr->personal_rating); // personal rating - } - else - { - data << uint64(itr->guid); // guid - data << uint8(0); // online flag - data << itr->name; // member name - data << uint32(itr->guid == GetCaptain() ? 0 : 1);// unknown - data << uint8(0); // unknown, level? - data << uint8(itr->Class); // class - data << uint32(itr->games_week); // played this week - data << uint32(itr->wins_week); // wins this week - data << uint32(itr->games_season); // played this season - data << uint32(itr->wins_season); // wins this season - data << uint32(itr->personal_rating); // personal rating - } + + data << uint64(itr->guid); // guid + data << uint8((pl ? 1 : 0)); // online flag + data << itr->name; // member name + data << uint32((itr->guid == GetCaptain() ? 0 : 1));// captain flag 0 captain 1 member + data << uint8((pl ? pl->getLevel() : 0)); // unknown, level? + data << uint8(itr->Class); // class + data << uint32(itr->games_week); // played this week + data << uint32(itr->wins_week); // wins this week + data << uint32(itr->games_season); // played this season + data << uint32(itr->wins_season); // wins this season + data << uint32(itr->personal_rating); // personal rating } session->SendPacket(&data); sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_ROSTER"); @@ -395,6 +358,18 @@ void ArenaTeam::Stats(WorldSession *session) session->SendPacket(&data); } +void ArenaTeam::NotifyStatsChanged() +{ + // this is called after a rated match ended + // updates arena team stats for every member of the team (not only the ones who participated!) + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + Player * plr = objmgr.GetPlayer(itr->guid); + if(plr) + Stats(plr->GetSession()); + } +} + void ArenaTeam::InspectStats(WorldSession *session, uint64 guid) { ArenaTeamMember* member = GetMember(guid); @@ -408,8 +383,8 @@ void ArenaTeam::InspectStats(WorldSession *session, uint64 guid) data << uint32(stats.rating); // rating data << uint32(stats.games_season); // season played data << uint32(stats.wins_season); // season wins - data << member->games_season; // played (count of all games, that the inspected member participated...) - data << member->personal_rating; // personal rating + data << uint32(member->games_season); // played (count of all games, that the inspected member participated...) + data << uint32(member->personal_rating); // personal rating session->SendPacket(&data); } @@ -458,18 +433,6 @@ void ArenaTeam::SetStats(uint32 stat_type, uint32 value) } } -uint8 ArenaTeam::GetSlot() const -{ - uint8 slot = GetSlotByType(GetType()); - if(slot >= MAX_ARENA_SLOT) - { - sLog.outError("Unknown arena team type %u for arena team %u", uint32(GetType()), GetId()); - return 0; // better return existed slot to prevent untelated data curruption - } - - return slot; -} - void ArenaTeam::BroadcastPacket(WorldPacket *packet) { for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) @@ -490,18 +453,184 @@ uint8 ArenaTeam::GetSlotByType( uint32 type ) default: break; } + sLog.outError("FATAL: Unknown arena team type %u for some arena team", type); return 0xFF; } -bool ArenaTeam::HaveMember( uint64 guid ) const +bool ArenaTeam::HaveMember( const uint64& guid ) const { for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr) - if(itr->guid==guid) + if(itr->guid == guid) return true; return false; } +uint32 ArenaTeam::GetPoints(uint32 MemberRating) +{ + // returns how many points would be awarded with this team type with this rating + float points; + + uint32 rating = MemberRating + 150 < stats.rating ? MemberRating : stats.rating; + + if(rating<=1500) + points = (float)rating * 0.22f + 14.0f; + else + points = 1511.26f / (1.0f + 1639.28f * exp(-0.00412f * (float)rating)); + + // type penalties for <5v5 teams + if(Type == ARENA_TEAM_2v2) + points *= 0.76f; + else if(Type == ARENA_TEAM_3v3) + points *= 0.88f; + + return (uint32) points; +} + +float ArenaTeam::GetChanceAgainst(uint32 own_rating, uint32 enemy_rating) +{ + // returns the chance to win against a team with the given rating, used in the rating adjustment calculation + // ELO system + return 1.0f/(1.0f+exp(log(10.0f)*(float)((float)enemy_rating - (float)own_rating)/400.0f)); +} + +int32 ArenaTeam::WonAgainst(uint32 againstRating) +{ + // called when the team has won + //'chance' calculation - to beat the opponent + float chance = GetChanceAgainst(stats.rating,againstRating); + // calculate the rating modification (ELO system with k=32) + int32 mod = (int32)floor(32.0f * (1.0f - chance)); + // modify the team stats accordingly + stats.rating += mod; + stats.games_week += 1; + stats.wins_week += 1; + stats.games_season += 1; + stats.wins_season += 1; + //update team's rank + stats.rank = 1; + ObjectMgr::ArenaTeamMap::iterator i = objmgr.GetArenaTeamMapBegin(); + for ( ; i != objmgr.GetArenaTeamMapEnd(); ++i) + { + if (i->second->GetType() == this->Type && i->second->GetStats().rating > stats.rating) + ++stats.rank; + } + + // return the rating change, used to display it on the results screen + return mod; +} + +int32 ArenaTeam::LostAgainst(uint32 againstRating) +{ + // called when the team has lost + //'chance' calculation - to loose to the opponent + float chance = GetChanceAgainst(stats.rating,againstRating); + // calculate the rating modification (ELO system with k=32) + int32 mod = (int32)ceil(32.0f * (0.0f - chance)); + // modify the team stats accordingly + stats.rating += mod; + stats.games_week += 1; + stats.games_season += 1; + //update team's rank + + stats.rank = 1; + ObjectMgr::ArenaTeamMap::iterator i = objmgr.GetArenaTeamMapBegin(); + for ( ; i != objmgr.GetArenaTeamMapEnd(); ++i) + { + if (i->second->GetType() == this->Type && i->second->GetStats().rating > stats.rating) + ++stats.rank; + } + + // return the rating change, used to display it on the results screen + return mod; +} + +void ArenaTeam::MemberLost(Player * plr, uint32 againstRating) +{ + // called for each participant of a match after losing + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + if(itr->guid == plr->GetGUID()) + { + // update personal rating + float chance = GetChanceAgainst(itr->personal_rating, againstRating); + int32 mod = (int32)ceil(32.0f * (0.0f - chance)); + itr->ModifyPersonalRating(plr, mod, GetSlot()); + // update personal played stats + itr->games_week +=1; + itr->games_season +=1; + // update the unit fields + plr->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 6 * GetSlot() + 2, itr->games_week); + plr->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 6 * GetSlot() + 3, itr->games_season); + return; + } + } +} + +void ArenaTeam::MemberWon(Player * plr, uint32 againstRating) +{ + // called for each participant after winning a match + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + if(itr->guid == plr->GetGUID()) + { + // update personal rating + float chance = GetChanceAgainst(itr->personal_rating, againstRating); + int32 mod = (int32)floor(32.0f * (1.0f - chance)); + itr->ModifyPersonalRating(plr, mod, GetSlot()); + // update personal stats + itr->games_week +=1; + itr->games_season +=1; + itr->wins_season += 1; + itr->wins_week += 1; + // update unit fields + plr->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 6 * GetSlot() + 2, itr->games_week); + plr->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 6 * GetSlot() + 3, itr->games_season); + return; + } + } +} + +void ArenaTeam::UpdateArenaPointsHelper(std::map& PlayerPoints) +{ + // called after a match has ended and the stats are already modified + // helper function for arena point distribution (this way, when distributing, no actual calculation is required, just a few comparisons) + // 10 played games per week is a minimum + if (stats.games_week < 10) + return; + // to get points, a player has to participate in at least 30% of the matches + uint32 min_plays = (uint32) ceil(stats.games_week * 0.3); + for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + // the player participated in enough games, update his points + uint32 points_to_add = 0; + if (itr->games_week >= min_plays) + points_to_add = GetPoints(itr->personal_rating); + // OBSOLETE : CharacterDatabase.PExecute("UPDATE arena_team_member SET points_to_add = '%u' WHERE arenateamid = '%u' AND guid = '%u'", points_to_add, Id, itr->guid); + + std::map::iterator plr_itr = PlayerPoints.find(GUID_LOPART(itr->guid)); + if (plr_itr != PlayerPoints.end()) + { + //check if there is already more points + if (plr_itr->second < points_to_add) + PlayerPoints[GUID_LOPART(itr->guid)] = points_to_add; + } + else + PlayerPoints[GUID_LOPART(itr->guid)] = points_to_add; + } +} + +void ArenaTeam::SaveToDB() +{ + // save team and member stats to db + // called after a match has ended, or when calculating arena_points + 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_week, stats.games_season, stats.rank, stats.wins_week, stats.wins_season, 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', personal_rating = '%u' WHERE arenateamid = '%u' AND guid = '%u'", itr->games_week, itr->wins_week, itr->games_season, itr->wins_season, itr->personal_rating, Id, itr->guid); + } +} + void ArenaTeam::FinishWeek() { stats.games_week = 0; // played this week @@ -519,18 +648,18 @@ arenateam fields (id from 2.3.3 client): 1415 - 0=captain, 1=member 1416 - played this week 1417 - played this season -1418 - unk +1418 - unk - rank? 1419 - personal arena rating 1420 - arena team id 3v3 1421 - 0=captain, 1=member 1422 - played this week 1423 - played this season -1424 - unk +1424 - unk - rank? 1425 - personal arena rating 1426 - arena team id 5v5 1427 - 0=captain, 1=member 1428 - played this week 1429 - played this season -1430 - unk +1430 - unk - rank? 1431 - personal arena rating */ diff --git a/src/game/ArenaTeam.h b/src/game/ArenaTeam.h index 43adca888..ba19c53f9 100644 --- a/src/game/ArenaTeam.h +++ b/src/game/ArenaTeam.h @@ -43,7 +43,9 @@ enum ArenaTeamCommandErrors ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM = 0x09, ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM_SS = 0x0A, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S = 0x0B, - ERR_ARENA_TEAM_NOT_ALLIED = 0x0C + ERR_ARENA_TEAM_NOT_ALLIED = 0x0C, + ERR_ARENA_TEAM_PLAYER_TO_LOW = 0x15, + ERR_ARENA_TEAM_FULL = 0x16 }; enum ArenaTeamEvents @@ -85,14 +87,22 @@ struct ArenaTeamMember { uint64 guid; std::string name; - //uint32 unk2; - //uint8 unk1; uint8 Class; uint32 games_week; uint32 wins_week; uint32 games_season; uint32 wins_season; uint32 personal_rating; + + void ModifyPersonalRating(Player* plr, int32 mod, uint32 slot) + { + if (personal_rating + mod < 0) + personal_rating = 0; + else + personal_rating += mod; + if(plr) + plr->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot*6) + 5, personal_rating); + } }; struct ArenaTeamStats @@ -113,58 +123,78 @@ class ArenaTeam ArenaTeam(); ~ArenaTeam(); - bool create(uint64 CaptainGuid, uint32 type, std::string ArenaTeamName); + bool Create(uint64 CaptainGuid, uint32 type, std::string ArenaTeamName); void Disband(WorldSession *session); typedef std::list MemberList; - uint32 GetId() const { return Id; } - uint32 GetType() const { return Type; } - uint8 GetSlot() const; + uint32 GetId() const { return Id; } + uint32 GetType() const { return Type; } + uint8 GetSlot() const { return GetSlotByType(GetType()); } static uint8 GetSlotByType(uint32 type); - const uint64& GetCaptain() const { return CaptainGuid; } - std::string GetName() const { return Name; } + const uint64& GetCaptain() const { return CaptainGuid; } + std::string GetName() const { return Name; } const ArenaTeamStats& GetStats() const { return stats; } void SetStats(uint32 stat_type, uint32 value); - uint32 GetRating() const { return stats.rating; } + uint32 GetRating() const { return stats.rating; } - uint32 GetEmblemStyle() const { return EmblemStyle; } - uint32 GetEmblemColor() const { return EmblemColor; } - uint32 GetBorderStyle() const { return BorderStyle; } - uint32 GetBorderColor() const { return BorderColor; } + uint32 GetEmblemStyle() const { return EmblemStyle; } + uint32 GetEmblemColor() const { return EmblemColor; } + uint32 GetBorderStyle() const { return BorderStyle; } + uint32 GetBorderColor() const { return BorderColor; } uint32 GetBackgroundColor() const { return BackgroundColor; } - void SetCaptain(uint64 guid); - bool AddMember(uint64 PlayerGuid); + void SetCaptain(const uint64& guid); + bool AddMember(const uint64& PlayerGuid); + + // Shouldn't be const uint64& ed, because than can reference guid from members on Disband + // and this method removes given record from list. So invalid reference can happen. void DelMember(uint64 guid); void SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor); - uint32 GetMembersSize() const { return members.size(); } - MemberList::iterator membersbegin(){ return members.begin(); } - MemberList::iterator membersEnd(){ return members.end(); } - bool HaveMember(uint64 guid) const; - ArenaTeamMember* GetMember(uint64 guid) + size_t GetMembersSize() const { return members.size(); } + bool Empty() const { return members.empty(); } + MemberList::iterator membersBegin() { return members.begin(); } + MemberList::iterator membersEnd() { return members.end(); } + bool HaveMember(const uint64& guid) const; + + ArenaTeamMember* GetMember(const uint64& guid) { for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) - if(itr->guid==guid) + if(itr->guid == guid) return &(*itr); return NULL; } - ArenaTeamMember* GetMember(std::string& name) + + ArenaTeamMember* GetMember(const std::string& name) { for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) - if(itr->name==name) + if(itr->name == name) return &(*itr); return NULL; } + bool IsFighting() const + { + for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr) + { + if (Player *p = objmgr.GetPlayer(itr->guid)) + { + if (p->GetMap()->IsBattleArena()) + return true; + } + } + return false; + } + bool LoadArenaTeamFromDB(uint32 ArenaTeamId); void LoadMembersFromDB(uint32 ArenaTeamId); void LoadStatsFromDB(uint32 ArenaTeamId); - void LoadPlayerStats(ArenaTeamMember* member); + + void SaveToDB(); void BroadcastPacket(WorldPacket *packet); @@ -173,6 +203,17 @@ class ArenaTeam void Stats(WorldSession *session); void InspectStats(WorldSession *session, uint64 guid); + uint32 GetPoints(uint32 MemberRating); + float GetChanceAgainst(uint32 own_rating, uint32 enemy_rating); + int32 WonAgainst(uint32 againstRating); + void MemberWon(Player * plr, uint32 againstRating); + int32 LostAgainst(uint32 againstRating); + void MemberLost(Player * plr, uint32 againstRating); + + void UpdateArenaPointsHelper(std::map & PlayerPoints); + + void NotifyStatsChanged(); + void FinishWeek(); protected: diff --git a/src/game/ArenaTeamHandler.cpp b/src/game/ArenaTeamHandler.cpp index 16784bbcb..d0e095237 100644 --- a/src/game/ArenaTeamHandler.cpp +++ b/src/game/ArenaTeamHandler.cpp @@ -30,7 +30,6 @@ void WorldSession::HandleInspectArenaStatsOpcode(WorldPacket & recv_data) { sLog.outDebug("MSG_INSPECT_ARENA_TEAMS"); - //recv_data.hexlike(); CHECK_PACKET_SIZE(recv_data, 8); @@ -54,7 +53,6 @@ void WorldSession::HandleInspectArenaStatsOpcode(WorldPacket & recv_data) void WorldSession::HandleArenaTeamQueryOpcode(WorldPacket & recv_data) { sLog.outDebug( "WORLD: Received CMSG_ARENA_TEAM_QUERY" ); - //recv_data.hexlike(); CHECK_PACKET_SIZE(recv_data, 4); @@ -72,7 +70,6 @@ void WorldSession::HandleArenaTeamQueryOpcode(WorldPacket & recv_data) void WorldSession::HandleArenaTeamRosterOpcode(WorldPacket & recv_data) { sLog.outDebug( "WORLD: Received CMSG_ARENA_TEAM_ROSTER" ); - //recv_data.hexlike(); CHECK_PACKET_SIZE(recv_data, 4); @@ -89,7 +86,6 @@ void WorldSession::HandleArenaTeamRosterOpcode(WorldPacket & recv_data) void WorldSession::HandleArenaTeamAddMemberOpcode(WorldPacket & recv_data) { sLog.outDebug("CMSG_ARENA_TEAM_ADD_MEMBER"); - //recv_data.hexlike(); CHECK_PACKET_SIZE(recv_data, 4+1); @@ -110,15 +106,13 @@ void WorldSession::HandleArenaTeamAddMemberOpcode(WorldPacket & recv_data) if(!player) { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", Invitedname, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S); + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", Invitedname, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S); return; } if(player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) { - //SendArenaTeamCommandResult(ARENA_TEAM_INVITE_SS,"",Invitedname,ARENA_TEAM_PLAYER_NOT_FOUND_S); - // can't find related opcode - SendNotification(LANG_HIS_ARENA_LEVEL_REQ_ERROR, player->GetName()); + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", player->GetName(), ERR_ARENA_TEAM_PLAYER_TO_LOW); return; } @@ -141,21 +135,19 @@ void WorldSession::HandleArenaTeamAddMemberOpcode(WorldPacket & recv_data) if(player->GetArenaTeamId(arenateam->GetSlot())) { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, player->GetName(), "", ERR_ALREADY_IN_ARENA_TEAM_S); + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", player->GetName(), ERR_ALREADY_IN_ARENA_TEAM_S); return; } if(player->GetArenaTeamIdInvited()) { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, player->GetName(), "", ERR_ALREADY_INVITED_TO_ARENA_TEAM_S); + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S); return; } if(arenateam->GetMembersSize() >= arenateam->GetType() * 2) { - // should send an "arena team is full" or the likes message, I just don't know the proper values so... ERR_INTERNAL -// SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_INTERNAL); - SendNotification(LANG_YOUR_ARENA_TEAM_FULL, player->GetName()); + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S,arenateam->GetName(),"",ERR_ARENA_TEAM_FULL); return; } @@ -177,23 +169,25 @@ void WorldSession::HandleArenaTeamInviteAcceptOpcode(WorldPacket & /*recv_data*/ ArenaTeam *at = objmgr.GetArenaTeamById(_player->GetArenaTeamIdInvited()); if(!at) + return; + + if(_player->GetArenaTeamIdFromDB(_player->GetGUIDLow(), at->GetType())) { - // arena team not exist + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S,"","",ERR_ALREADY_IN_ARENA_TEAM); // already in arena team that size return; } - if(_player->GetArenaTeamId(at->GetSlot())) - { - // already in arena team that size - return; - } - - // not let enemies sign petition if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && _player->GetTeam() != objmgr.GetPlayerTeamByGUID(at->GetCaptain())) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S,"","",ERR_ARENA_TEAM_NOT_ALLIED);// not let enemies sign petition return; + } if(!at->AddMember(_player->GetGUID())) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S,"","",ERR_ARENA_TEAM_INTERNAL);// arena team not found return; + } // event WorldPacket data; @@ -211,7 +205,6 @@ void WorldSession::HandleArenaTeamInviteDeclineOpcode(WorldPacket & /*recv_data* void WorldSession::HandleArenaTeamLeaveOpcode(WorldPacket & recv_data) { sLog.outDebug("CMSG_ARENA_TEAM_LEAVE"); - //recv_data.hexlike(); CHECK_PACKET_SIZE(recv_data, 4); @@ -220,10 +213,7 @@ void WorldSession::HandleArenaTeamLeaveOpcode(WorldPacket & recv_data) ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); if(!at) - { - // send command result return; - } if(_player->GetGUID() == at->GetCaptain() && at->GetMembersSize() > 1) { // check for correctness @@ -245,13 +235,13 @@ void WorldSession::HandleArenaTeamLeaveOpcode(WorldPacket & recv_data) BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_LEAVE_SS, 2, _player->GetName(), at->GetName(), ""); at->BroadcastPacket(&data); - //SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, at->GetName(), "", 0); + //send you are no longer member of team + SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, at->GetName(), "", 0); } void WorldSession::HandleArenaTeamDisbandOpcode(WorldPacket & recv_data) { sLog.outDebug("CMSG_ARENA_TEAM_DISBAND"); - //recv_data.hexlike(); CHECK_PACKET_SIZE(recv_data, 4); @@ -260,16 +250,13 @@ void WorldSession::HandleArenaTeamDisbandOpcode(WorldPacket & recv_data) ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); if(!at) - { - // arena team not found return; - } if(at->GetCaptain() != _player->GetGUID()) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS); return; - } + + if (at->IsFighting()) + return; at->Disband(this); delete at; @@ -278,7 +265,6 @@ void WorldSession::HandleArenaTeamDisbandOpcode(WorldPacket & recv_data) void WorldSession::HandleArenaTeamRemoveFromTeamOpcode(WorldPacket & recv_data) { sLog.outDebug("CMSG_ARENA_TEAM_REMOVE_FROM_TEAM"); - //recv_data.hexlike(); CHECK_PACKET_SIZE(recv_data, 4+1); @@ -303,11 +289,14 @@ void WorldSession::HandleArenaTeamRemoveFromTeamOpcode(WorldPacket & recv_data) ArenaTeamMember* member = at->GetMember(name); if(!member) // member not found + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", name, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S); return; + } if(at->GetCaptain() == member->guid) { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S); + SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S); return; } @@ -322,7 +311,6 @@ void WorldSession::HandleArenaTeamRemoveFromTeamOpcode(WorldPacket & recv_data) void WorldSession::HandleArenaTeamPromoteToCaptainOpcode(WorldPacket & recv_data) { sLog.outDebug("CMSG_ARENA_TEAM_PROMOTE_TO_CAPTAIN"); - //recv_data.hexlike(); CHECK_PACKET_SIZE(recv_data, 4+1); @@ -347,7 +335,10 @@ void WorldSession::HandleArenaTeamPromoteToCaptainOpcode(WorldPacket & recv_data ArenaTeamMember* member = at->GetMember(name); if(!member) // member not found + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", name, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S); return; + } if(at->GetCaptain() == member->guid) // target player already captain return; @@ -360,13 +351,13 @@ void WorldSession::HandleArenaTeamPromoteToCaptainOpcode(WorldPacket & recv_data at->BroadcastPacket(&data); } -void WorldSession::SendArenaTeamCommandResult(uint32 unk1, const std::string& str1, const std::string& str2, uint32 unk3) +void WorldSession::SendArenaTeamCommandResult(uint32 team_action, const std::string& team, const std::string& player, uint32 error_id) { - WorldPacket data(SMSG_ARENA_TEAM_COMMAND_RESULT, 4+str1.length()+1+str2.length()+1+4); - data << unk1; - data << str1; - data << str2; - data << unk3; + WorldPacket data(SMSG_ARENA_TEAM_COMMAND_RESULT, 4+team.length()+1+player.length()+1+4); + data << team_action; + data << team; + data << player; + data << error_id; SendPacket(&data); } diff --git a/src/game/BattleGround.cpp b/src/game/BattleGround.cpp index 1a3d70fb6..5c2ca717a 100644 --- a/src/game/BattleGround.cpp +++ b/src/game/BattleGround.cpp @@ -24,6 +24,7 @@ #include "Language.h" #include "Chat.h" #include "SpellAuras.h" +#include "ArenaTeam.h" #include "World.h" #include "Util.h" @@ -47,6 +48,8 @@ BattleGround::BattleGround() m_Name = ""; m_LevelMin = 0; m_LevelMax = 0; + m_InBGFreeSlotQueue = false; + m_SetDeleteThis = false; m_MaxPlayersPerTeam = 0; m_MaxPlayers = 0; @@ -67,21 +70,54 @@ BattleGround::BattleGround() 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; m_PlayersCount[BG_TEAM_ALLIANCE] = 0; m_PlayersCount[BG_TEAM_HORDE] = 0; + + m_PrematureCountDown = false; + m_PrematureCountDown = 0; } BattleGround::~BattleGround() { + // remove objects and creatures + // (this is done automatically in mapmanager update, when the instance is reset after the reset time) + int size = m_BgCreatures.size(); + for(int i = 0; i < size; ++i) + { + DelCreature(i); + } + size = m_BgObjects.size(); + for(int i = 0; i < size; ++i) + { + DelObject(i); + } + // delete 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()); + // unload map + if(Map * map = MapManager::Instance().FindMap(GetMapId(), GetInstanceID())) + if(map->IsBattleGroundOrArena()) + ((BattleGroundMap*)map)->SetUnload(); + // remove from bg free slot queue + this->RemoveFromBGFreeSlotQueue(); } void BattleGround::Update(time_t diff) { - if(!GetPlayersSize() && !GetRemovedPlayersSize() && !GetReviveQueueSize()) //BG is empty return; @@ -188,6 +224,33 @@ void BattleGround::Update(time_t diff) m_ResurrectQueue.clear(); } + // if less then minimum players are in on one side, then start premature finish timer + if(GetStatus() == STATUS_IN_PROGRESS && !isArena() && sBattleGroundMgr.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE) < GetMinPlayersPerTeam())) + { + if(!m_PrematureCountDown) + { + m_PrematureCountDown = true; + m_PrematureCountDownTimer = sBattleGroundMgr.GetPrematureFinishTime(); + SendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING); + } + else if(m_PrematureCountDownTimer < diff) + { + // time's up! + EndBattleGround(0); // noone wins + m_PrematureCountDown = false; + } + else + { + uint32 newtime = m_PrematureCountDownTimer - diff; + // announce every minute + if(m_PrematureCountDownTimer != sBattleGroundMgr.GetPrematureFinishTime() && newtime / 60000 != m_PrematureCountDownTimer / 60000) + SendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING); + m_PrematureCountDownTimer = newtime; + } + } + else if (m_PrematureCountDown) + m_PrematureCountDown = false; + if(GetStatus() == STATUS_WAIT_LEAVE) { // remove all players from battleground after 2 minutes @@ -239,7 +302,10 @@ void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player * 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); } } @@ -265,7 +331,10 @@ void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID) 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); @@ -285,7 +354,10 @@ void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID) 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); } } @@ -302,7 +374,10 @@ void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID) 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); } } @@ -324,7 +399,10 @@ void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, 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); } } @@ -345,30 +423,84 @@ void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player void BattleGround::EndBattleGround(uint32 winner) { + this->RemoveFromBGFreeSlotQueue(); + + ArenaTeam * winner_arena_team = NULL; + ArenaTeam * loser_arena_team = NULL; + uint32 loser_rating = 0; + uint32 winner_rating = 0; WorldPacket data; Player *Source = NULL; const char *winmsg = ""; if(winner == ALLIANCE) { - winmsg = GetMangosString(LANG_BG_A_WINS); + if(isBattleGround()) + winmsg = GetMangosString(LANG_BG_A_WINS); + else + winmsg = GetMangosString(LANG_ARENA_GOLD_WINS); PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound SetWinner(WINNER_ALLIANCE); } - else + else if(winner == HORDE) { - winmsg = GetMangosString(LANG_BG_H_WINS); + if(isBattleGround()) + winmsg = GetMangosString(LANG_BG_H_WINS); + else + winmsg = GetMangosString(LANG_ARENA_GREEN_WINS); PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound SetWinner(WINNER_HORDE); } + else + { + SetWinner(3); + } SetStatus(STATUS_WAIT_LEAVE); m_EndTime = 0; + // arena rating calculation + if(isArena() && isRated()) + { + if(winner == ALLIANCE) + { + winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(ALLIANCE)); + loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(HORDE)); + } + else if(winner == HORDE) + { + winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(HORDE)); + loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(ALLIANCE)); + } + if(winner_arena_team && loser_arena_team) + { + loser_rating = loser_arena_team->GetStats().rating; + winner_rating = winner_arena_team->GetStats().rating; + int32 winner_change = winner_arena_team->WonAgainst(loser_rating); + int32 loser_change = loser_arena_team->LostAgainst(winner_rating); + sLog.outDebug("--- Winner rating: %u, Loser rating: %u, Winner change: %u, Losser change: %u ---", winner_rating, loser_rating, winner_change, loser_change); + if(winner == ALLIANCE) + { + SetArenaTeamRatingChangeForTeam(ALLIANCE, winner_change); + SetArenaTeamRatingChangeForTeam(HORDE, loser_change); + } + else + { + SetArenaTeamRatingChangeForTeam(HORDE, winner_change); + SetArenaTeamRatingChangeForTeam(ALLIANCE, loser_change); + } + } + else + { + SetArenaTeamRatingChangeForTeam(ALLIANCE, 0); + SetArenaTeamRatingChangeForTeam(HORDE, 0); + } + } + for(std::map::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { Player *plr = objmgr.GetPlayer(itr->first); @@ -378,13 +510,29 @@ void BattleGround::EndBattleGround(uint32 winner) 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; + if(!team) team = plr->GetTeam(); + + // per player calculation + if(isArena() && isRated() && winner_arena_team && loser_arena_team) + { + if(team == winner) + winner_arena_team->MemberWon(plr,loser_rating); + else + loser_arena_team->MemberLost(plr,winner_rating); + } + + if(team == winner) { if(!Source) Source = plr; @@ -404,10 +552,28 @@ void BattleGround::EndBattleGround(uint32 winner) sBattleGroundMgr.BuildPvpLogDataPacket(&data, this); plr->GetSession()->SendPacket(&data); - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(m_TypeID), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime()); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(GetTypeID(), GetArenaType()); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime()); plr->GetSession()->SendPacket(&data); } + if(isArena() && isRated() && winner_arena_team && loser_arena_team) + { + // update arena points only after increasing the player's match count! + //obsolete: winner_arena_team->UpdateArenaPointsHelper(); + //obsolete: loser_arena_team->UpdateArenaPointsHelper(); + // save the stat changes + winner_arena_team->SaveToDB(); + loser_arena_team->SaveToDB(); + // send updated arena team stats to players + // this way all arena team members will get notified, not only the ones who participated in this match + winner_arena_team->NotifyStatsChanged(); + loser_arena_team->NotifyStatsChanged(); + } + + // inform invited players about the removal + sBattleGroundMgr.m_BattleGroundQueues[sBattleGroundMgr.BGQueueTypeId(GetTypeID(), GetArenaType())].BGEndedRemoveInvites(this); + if(Source) { ChatHandler(Source).FillMessageData(&data, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, Source->GetGUID(), winmsg); @@ -558,12 +724,16 @@ void BattleGround::BlockMovement(Player *plr) void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket) { + uint32 team = GetPlayerTeam(guid); + bool participant = false; // 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); + // check if the player was a participant of the match, or only entered through gm command (goname) + participant = true; } std::map::iterator itr2 = m_PlayerScores.find(guid); @@ -577,6 +747,10 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac 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); @@ -589,66 +763,106 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac { plr->ClearAfkReports(); - if(isArena()) + if(participant) // if the player was a match participant, remove auras, calc rating, update queue { - if(!sWorld.IsFFAPvPRealm()) - plr->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP); - } + if(!team) team = plr->GetTeam(); - WorldPacket data; - if(SendPacket) - { - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(m_TypeID), 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); - - DecreaseInvitedCount(plr->GetTeam()); - //we should update battleground queue, but only if bg isn't ending - if (GetQueueType() < MAX_BATTLEGROUND_QUEUES) - sBattleGroundMgr.m_BattleGroundQueues[GetTypeID()].Update(GetTypeID(), GetQueueType()); - - if(!plr->GetBattleGroundId()) - return; - - Group * group = plr->GetGroup(); - - // remove from raid group if exist - if(group && group == GetBgRaid(plr->GetTeam())) - { - if(!group->RemoveMember(guid, 0)) // group was disbanded + uint32 bgTypeId = GetTypeID(); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(GetTypeID(), GetArenaType()); + // if arena, remove the specific arena auras + if(isArena()) { - SetBgRaid(plr->GetTeam(), NULL); - delete group; + plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out + bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing) + + // summon old pet if there was one and there isn't a current pet + if(!plr->GetPet() && plr->GetTemporaryUnsummonedPetNumber()) + { + Pet* NewPet = new Pet; + if(!NewPet->LoadPetFromDB(plr, 0, (plr)->GetTemporaryUnsummonedPetNumber(), true)) + delete NewPet; + + (plr)->SetTemporaryUnsummonedPetNumber(0); + } + + if(isRated() && GetStatus() == STATUS_IN_PROGRESS) + { + //left a rated match while the encounter was in progress, consider as loser + ArenaTeam * winner_arena_team = 0; + ArenaTeam * loser_arena_team = 0; + if(team == HORDE) + { + winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(ALLIANCE)); + loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(HORDE)); + } + else + { + winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(HORDE)); + loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(ALLIANCE)); + } + if(winner_arena_team && loser_arena_team) + { + loser_arena_team->MemberLost(plr,winner_arena_team->GetRating()); + } + } } + + WorldPacket data; + if(SendPacket) + { + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, team, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0); + plr->GetSession()->SendPacket(&data); + } + + // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg + plr->RemoveBattleGroundQueueId(bgQueueTypeId); + + DecreaseInvitedCount(team); + //we should update battleground queue, but only if bg isn't ending + if (GetQueueType() < MAX_BATTLEGROUND_QUEUES) + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, GetQueueType()); + + Group * group = plr->GetGroup(); + // remove from raid group if exist + if(group && group == GetBgRaid(team)) + { + if(!group->RemoveMember(guid, 0)) // group was disbanded + { + SetBgRaid(team, NULL); + delete group; + } + } + + // Let others know + sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, plr); + SendPacketToTeam(team, &data, plr, false); } // Do next only if found in battleground plr->SetBattleGroundId(0); // We're not in BG. - - // Let others know - sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, plr); - SendPacketToTeam(plr->GetTeam(), &data, plr, false); + // reset destination bg team + plr->SetBGTeam(0); if(Transport) { plr->TeleportTo(plr->GetBattleGroundEntryPointMap(), plr->GetBattleGroundEntryPointX(), plr->GetBattleGroundEntryPointY(), plr->GetBattleGroundEntryPointZ(), plr->GetBattleGroundEntryPointO()); - //sLog.outDetail("BATTLEGROUND: Sending %s to %f,%f,%f,%f", pl->GetName(), x,y,z,O); } // Log sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName()); } - /// there will be code which will add battleground to BGFreeSlotQueue , when battleground instance will exist - // we always should check if BG is in that queue before adding.. - - if(!GetPlayersSize()) + if(!GetPlayersSize() && !GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE)) { - Reset(); + // if no players left AND no invitees left, set this bg to delete in next update + // direct deletion could cause crashes + m_SetDeleteThis = true; + // return to prevent addition to freeslotqueue + return; } + + // a player exited the battleground, so there are free slots. add to queue + this->AddToBGFreeSlotQueue(); } // this method is called when no players remains in battleground @@ -660,6 +874,8 @@ void BattleGround::Reset() SetStartTime(0); SetEndTime(0); SetLastResurrectTime(0); + SetArenaType(0); + SetRated(false); m_Events = 0; @@ -668,6 +884,7 @@ void BattleGround::Reset() m_InvitedAlliance = 0; m_InvitedHorde = 0; + m_InBGFreeSlotQueue = false; m_Players.clear(); m_PlayerScores.clear(); @@ -704,10 +921,11 @@ void BattleGround::AddPlayer(Player *plr) sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr); SendPacketToTeam(team, &data, plr, false); + // add arena specific auras if(isArena()) { plr->RemoveArenaSpellCooldowns(); - //plr->RemoveArenaAuras(); + plr->RemoveArenaAuras(); plr->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT); if(team == ALLIANCE) // gold { @@ -726,6 +944,19 @@ void BattleGround::AddPlayer(Player *plr) plr->DestroyConjuredItems(true); + Pet* pet = plr->GetPet(); + if(pet) + { + if(pet->getPetType() == SUMMON_PET || pet->getPetType() == HUNTER_PET) + { + (plr)->SetTemporaryUnsummonedPetNumber(pet->GetCharmInfo()->GetPetNumber()); + (plr)->SetOldPetSpell(pet->GetUInt32Value(UNIT_CREATED_BY_SPELL)); + } + (plr)->RemovePet(NULL,PET_SAVE_NOT_IN_SLOT); + } + else + (plr)->SetTemporaryUnsummonedPetNumber(0); + if(GetStatus() == STATUS_WAIT_JOIN) // not started yet { plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true); @@ -740,9 +971,6 @@ void BattleGround::AddPlayer(Player *plr) plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells. } - if(isArena()) - plr->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP); - // Log sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName()); } @@ -750,13 +978,20 @@ void BattleGround::AddPlayer(Player *plr) /* This method should be called only once ... it adds pointer to queue */ void BattleGround::AddToBGFreeSlotQueue() { - sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this); + // make sure to add only once + if(!m_InBGFreeSlotQueue) + { + sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this); + m_InBGFreeSlotQueue = true; + } } /* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/ void BattleGround::RemoveFromBGFreeSlotQueue() { - /* uncomment this code when battlegrounds will work like instances + // set to be able to re-add if needed + m_InBGFreeSlotQueue = false; + // uncomment this code when battlegrounds will work like instances for (std::deque::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr) { if ((*itr)->GetInstanceID() == m_InstanceID) @@ -764,30 +999,69 @@ void BattleGround::RemoveFromBGFreeSlotQueue() sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr); return; } - }*/ + } } -/* -this method should decide, if we can invite new player of certain team to BG, it is based on BATTLEGROUND_STATUS -*/ -bool BattleGround::HasFreeSlotsForTeam(uint32 Team) const +// get the number of free slots for team +// works in similar way that HasFreeSlotsForTeam did, but this is needed for join as group +uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const { - //if BG is starting ... invite anyone: + //if BG is starting ... invite anyone if (GetStatus() == STATUS_WAIT_JOIN) - return GetInvitedCount(Team) < GetMaxPlayersPerTeam(); + 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; + uint32 otherIn; if (Team == ALLIANCE) + { otherTeam = GetInvitedCount(HORDE); + otherIn = GetPlayersCountByTeam(HORDE); + } else + { otherTeam = GetInvitedCount(ALLIANCE); + otherIn = GetPlayersCountByTeam(ALLIANCE); + } if (GetStatus() == STATUS_IN_PROGRESS) - return (GetInvitedCount(Team) <= otherTeam && GetInvitedCount(Team) < GetMaxPlayersPerTeam()); + { + // difference based on ppl invited (not necessarily entered battle) + // default: allow 0 + uint32 diff = 0; + // allow join one person if the sides are equal (to fill up bg to minplayersperteam) + if (otherTeam == GetInvitedCount(Team)) + diff = 1; + // allow join more ppl if the other side has more players + else if(otherTeam > GetInvitedCount(Team)) + diff = otherTeam - GetInvitedCount(Team); - return false; + // difference based on max players per team (don't allow inviting more) + uint32 diff2 = (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0; + + // difference based on players who already entered + // default: allow 0 + uint32 diff3 = 0; + // allow join one person if the sides are equal (to fill up bg minplayersperteam) + if (otherIn == GetPlayersCountByTeam(Team)) + diff3 = 1; + // allow join more ppl if the other side has more players + else if (otherIn > GetPlayersCountByTeam(Team)) + diff3 = otherIn - GetPlayersCountByTeam(Team); + // or other side has less than minPlayersPerTeam + else if (GetInvitedCount(Team) <= GetMinPlayersPerTeam()) + diff3 = GetMinPlayersPerTeam() - GetInvitedCount(Team) + 1; + + // return the minimum of the 3 differences + + // min of diff and diff 2 + diff = diff < diff2 ? diff : diff2; + + // min of diff, diff2 and diff3 + return diff < diff3 ? diff : diff3 ; + } + + 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 { return GetPlayersSize() < GetMaxPlayers(); @@ -813,9 +1087,13 @@ void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value) itr->second->HonorableKills += value; break; case SCORE_BONUS_HONOR: // Honor bonus - // reward honor instantly - if(Source->RewardHonor(NULL, 1, value)) - itr->second->BonusHonor += value; + // do not add honor in arenas + if(isBattleGround()) + { + // reward honor instantly + if(Source->RewardHonor(NULL, 1, value)) + itr->second->BonusHonor += value; + } break; //used only in EY, but in MSG_PVP_LOG_DATA opcode case SCORE_DAMAGE_DONE: // Damage Done @@ -871,15 +1149,26 @@ void BattleGround::RemovePlayerFromResurrectQueue(uint64 player_guid) bool BattleGround::AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime) { - GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(entry); - if(!goinfo) + Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID()); + if(!map) + return false; + + // must be created this way, adding to godatamap would add it to the base map of the instance + // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created + // so we must create it specific for this instance + GameObject * go = new GameObject; + if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry, map,x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,1)) { sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry); + sLog.outError("Cannot create gameobject template %u! BattleGround not created!", entry); + delete go; return false; } +/* + uint32 guid = go->GetGUIDLow(); - uint32 guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT); - + // without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata + // iirc that was changed, so adding to go data map is no longer required if that was the only function using godata from GameObject without checking if it existed GameObjectData& data = objmgr.NewGOData(guid); data.id = entry; @@ -893,13 +1182,13 @@ bool BattleGround::AddObject(uint32 type, uint32 entry, float x, float y, float data.rotation2 = rotation2; data.rotation3 = rotation3; data.spawntimesecs = respawnTime; + data.spawnMask = 1; data.animprogress = 100; data.go_state = 1; - data.spawnMask = 1; - objmgr.AddGameobjectToGrid(guid, &data); - - m_BgObjects[type] = MAKE_NEW_GUID(guid, entry, HIGHGUID_GAMEOBJECT); - +*/ + // add to world, so it can be later looked up from HashMapHolder + go->AddToWorld(); + m_BgObjects[type] = go->GetGUID(); return true; } @@ -941,6 +1230,9 @@ void BattleGround::DoorOpen(uint32 type) void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime) { + Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID()); + if(!map) + return; if( respawntime == 0 ) { GameObject *obj = HashMapHolder::Find(m_BgObjects[type]); @@ -949,30 +1241,27 @@ void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime) //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again if( obj->getLootState() == GO_JUST_DEACTIVATED ) obj->SetLootState(GO_READY); - obj->Respawn(); + obj->SetRespawnTime(0); + map->Add(obj); } - else - objmgr.SaveGORespawnTime(GUID_LOPART(m_BgObjects[type]), 0, 0); } else { GameObject *obj = HashMapHolder::Find(m_BgObjects[type]); if(obj) { + map->Add(obj); obj->SetRespawnTime(respawntime); obj->SetLootState(GO_JUST_DEACTIVATED); } - else - objmgr.SaveGORespawnTime(GUID_LOPART(m_BgObjects[type]), 0, time(NULL) + respawntime); } } -Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o) +Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o, uint32 respawntime) { - // note: this should normally be FindMap - // but it's a hack to allow the battlegrounds to initialize at server startup - Map * map = MapManager::Instance().GetMap(GetMapId(), 0); - if(!map) return NULL; + Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID()); + if(!map) + return NULL; Creature* pCreature = new Creature; if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, entry, teamval)) @@ -996,9 +1285,39 @@ Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, f map->Add(pCreature); m_BgCreatures[type] = pCreature->GetGUID(); + return pCreature; } +/* +void BattleGround::SpawnBGCreature(uint32 type, uint32 respawntime) +{ + Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceId()); + if(!map) + return false; + if(respawntime == 0) + { + Creature *obj = HashMapHolder::Find(m_BgCreatures[type]); + if(obj) + { + //obj->Respawn(); // bugged + obj->SetRespawnTime(0); + objmgr.SaveCreatureRespawnTime(obj->GetGUIDLow(), GetInstanceID(), 0); + map->Add(obj); + } + } + else + { + Creature *obj = HashMapHolder::Find(m_BgCreatures[type]); + if(obj) + { + obj->setDeathState(DEAD); + obj->SetRespawnTime(respawntime); + map->Add(obj); + } + } +} +*/ bool BattleGround::DelCreature(uint32 type) { Creature *cr = HashMapHolder::Find(m_BgCreatures[type]); @@ -1079,8 +1398,11 @@ void BattleGround::SendMessageToAll(int32 entry) void BattleGround::EndNow() { + RemoveFromBGFreeSlotQueue(); SetStatus(STATUS_WAIT_LEAVE); SetEndTime(TIME_TO_AUTOREMOVE); + // inform invited players about the removal + sBattleGroundMgr.m_BattleGroundQueues[sBattleGroundMgr.BGQueueTypeId(GetTypeID(), GetArenaType())].BGEndedRemoveInvites(this); } // Battleground messages are localized using the dbc lang, they are not client language dependent @@ -1158,3 +1480,28 @@ void BattleGround::HandleKillPlayer( Player *player, Player *killer ) // to be able to remove insignia player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE ); } + +// return the player's team based on battlegroundplayer info +// used in same faction arena matches mainly +uint32 BattleGround::GetPlayerTeam(uint64 guid) +{ + std::map::const_iterator itr = m_Players.find(guid); + if(itr!=m_Players.end()) + return itr->second.Team; + return 0; +} + +uint32 BattleGround::GetAlivePlayersCountByTeam(uint32 Team) const +{ + int count = 0; + for(std::map::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if(itr->second.Team == Team) + { + Player * pl = objmgr.GetPlayer(itr->first); + if(pl && pl->isAlive()) + ++count; + } + } + return count; +} diff --git a/src/game/BattleGround.h b/src/game/BattleGround.h index 9e41e661b..bff2eb64d 100644 --- a/src/game/BattleGround.h +++ b/src/game/BattleGround.h @@ -18,6 +18,7 @@ #ifndef __BATTLEGROUND_H #define __BATTLEGROUND_H +#define ARENA_PATCH #include "Common.h" #include "WorldPacket.h" @@ -83,7 +84,7 @@ enum BattleGroundTimeIntervals { RESURRECTION_INTERVAL = 30000, // ms REMIND_INTERVAL = 30000, // ms - INVITE_ACCEPT_WAIT_TIME = 120000, // ms + INVITE_ACCEPT_WAIT_TIME = 80000, // ms TIME_TO_AUTOREMOVE = 120000, // ms MAX_OFFLINE_TIME = 300000, // ms START_DELAY0 = 120000, // ms @@ -143,6 +144,18 @@ enum BattleGroundTypeId BATTLEGROUND_RL = 8 }; +// handle the queue types and bg types separately to enable joining queue for different sized arenas at the same time +enum BattleGroundQueueTypeId +{ + BATTLEGROUND_QUEUE_AV = 1, + BATTLEGROUND_QUEUE_WS = 2, + BATTLEGROUND_QUEUE_AB = 3, + BATTLEGROUND_QUEUE_EY = 4, + BATTLEGROUND_QUEUE_2v2 = 5, + BATTLEGROUND_QUEUE_3v3 = 6, + BATTLEGROUND_QUEUE_5v5 = 7, +}; + enum ScoreType { SCORE_KILLING_BLOWS = 1, @@ -195,6 +208,20 @@ enum BattleGroundTeamId BG_TEAM_HORDE = 1 }; +enum BattleGroundJoinError +{ + BG_JOIN_ERR_OK = 0, + BG_JOIN_ERR_OFFLINE_MEMBER = 1, + BG_JOIN_ERR_GROUP_TOO_MANY = 2, + BG_JOIN_ERR_MIXED_FACTION = 3, + BG_JOIN_ERR_MIXED_LEVELS = 4, + BG_JOIN_ERR_MIXED_ARENATEAM = 5, + BG_JOIN_ERR_GROUP_MEMBER_ALREADY_IN_QUEUE = 6, + BG_JOIN_ERR_GROUP_DESERTER = 7, + BG_JOIN_ERR_ALL_QUEUES_USED = 8, + BG_JOIN_ERR_GROUP_NOT_ENOUGH = 9 +}; + class BattleGroundScore { public: @@ -224,6 +251,7 @@ class BattleGround 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 @@ -296,6 +324,7 @@ class BattleGround } 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; } @@ -366,6 +395,7 @@ class BattleGround uint8 GetTeamIndexByTeamId(uint32 Team) const { return Team == ALLIANCE ? BG_TEAM_ALLIANCE : BG_TEAM_HORDE; } uint32 GetPlayersCountByTeam(uint32 Team) const { return m_PlayersCount[GetTeamIndexByTeamId(Team)]; } + uint32 GetAlivePlayersCountByTeam(uint32 Team) const; // used in arenas to correctly handle death in spirit of redemption / last stand etc. (killer = killed) cases void UpdatePlayersCountByTeam(uint32 Team, bool remove) { if(remove) @@ -374,6 +404,12 @@ class BattleGround ++m_PlayersCount[GetTeamIndexByTeamId(Team)]; } + // used for rated arena battles + void SetArenaTeamIdForTeam(uint32 Team, uint32 ArenaTeamId) { m_ArenaTeamIds[GetTeamIndexByTeamId(Team)] = ArenaTeamId; } + uint32 GetArenaTeamIdForTeam(uint32 Team) const { return m_ArenaTeamIds[GetTeamIndexByTeamId(Team)]; } + void SetArenaTeamRatingChangeForTeam(uint32 Team, int32 RatingChange) { m_ArenaTeamRatingChanges[GetTeamIndexByTeamId(Team)] = RatingChange; } + int32 GetArenaTeamRatingChangeForTeam(uint32 Team) const { return m_ArenaTeamRatingChanges[GetTeamIndexByTeamId(Team)]; } + /* Triggers handle */ // must be implemented in BG subclass virtual void HandleAreaTrigger(Player* /*Source*/, uint32 /*Trigger*/) {} @@ -390,6 +426,7 @@ class BattleGround virtual WorldSafeLocsEntry const* GetClosestGraveYard(float /*x*/, float /*y*/, float /*z*/, uint32 /*team*/) { return NULL; } virtual void AddPlayer(Player *plr); // must be implemented in BG subclass + virtual void RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket); // can be extended in in BG subclass @@ -402,7 +439,8 @@ class BattleGround BGCreatures m_BgCreatures; void SpawnBGObject(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); +// void SpawnBGCreature(uint32 type, uint32 respawntime); + 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); @@ -411,6 +449,13 @@ class BattleGround void DoorClose(uint32 type); const char *GetMangosString(int32 entry); + virtual bool HandlePlayerUnderMap(Player * plr) {return false;} + + // since arenas can be AvA or Hvh, we have to get the "temporary" team of a player + uint32 GetPlayerTeam(uint64 guid); + + void SetDeleteThis() {m_SetDeleteThis = true;} + protected: //this method is called, when BG cannot spawn its own spirit guide, or something is wrong, It correctly ends BattleGround void EndNow(); @@ -443,6 +488,8 @@ class BattleGround uint32 m_LastResurrectTime; uint32 m_Queue_type; uint8 m_ArenaType; // 2=2v2, 3=3v3, 5=5v5 + bool m_InBGFreeSlotQueue; // used to make sure that BG is only once inserted into the BattleGroundMgr.BGFreeSlotQueue[bgTypeId] deque + bool m_SetDeleteThis; // used for safe deletion of the bg after end / all players leave // this variable is not used .... it can be found in many other ways... but to store it in BG object instance is useless //uint8 m_BattleGroundType; // 3=BG, 4=arena //instead of uint8 (in previous line) is bool used @@ -450,6 +497,8 @@ class BattleGround uint8 m_Winner; // 0=alliance, 1=horde, 2=none int32 m_StartDelayTime; bool m_IsRated; // is this battle rated? + bool m_PrematureCountDown; + uint32 m_PrematureCountDownTimer; char const *m_Name; /* Player lists */ @@ -468,6 +517,11 @@ class BattleGround /* 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; diff --git a/src/game/BattleGroundAB.cpp b/src/game/BattleGroundAB.cpp index dcf23ccff..5200a3f5f 100644 --- a/src/game/BattleGroundAB.cpp +++ b/src/game/BattleGroundAB.cpp @@ -50,6 +50,13 @@ void BattleGroundAB::Update(time_t diff) { m_Events |= 0x01; + // setup here, only when at least one player has ported to the map + if(!SetupBattleGround()) + { + EndNow(); + return; + } + sLog.outDebug("Arathi Basin: entering state STATUS_WAIT_JOIN ..."); // despawn banners, auras and buffs @@ -377,6 +384,7 @@ void BattleGroundAB::_NodeOccupied(uint8 node,Team team) { if( !AddSpiritGuide(node, BG_AB_SpiritGuidePos[node][0], BG_AB_SpiritGuidePos[node][1], BG_AB_SpiritGuidePos[node][2], BG_AB_SpiritGuidePos[node][3], team) ) sLog.outError("Failed to spawn spirit guide! point: %u, team: %u,", node, team); +// SpawnBGCreature(node,RESPAWN_IMMEDIATELY); uint8 capturedNodes = 0; for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) diff --git a/src/game/BattleGroundBE.cpp b/src/game/BattleGroundBE.cpp index 4ad71988e..81e47c721 100644 --- a/src/game/BattleGroundBE.cpp +++ b/src/game/BattleGroundBE.cpp @@ -47,6 +47,12 @@ void BattleGroundBE::Update(time_t diff) if (!(m_Events & 0x01)) { m_Events |= 0x01; + // setup here, only when at least one player has ported to the map + if(!SetupBattleGround()) + { + EndNow(); + return; + } for(uint32 i = BG_BE_OBJECT_DOOR_1; i <= BG_BE_OBJECT_DOOR_4; i++) SpawnBGObject(i, RESPAWN_IMMEDIATELY); @@ -86,6 +92,11 @@ void BattleGroundBE::Update(time_t diff) for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) if(Player *plr = objmgr.GetPlayer(itr->first)) plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); + + if(!GetPlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetPlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } } @@ -102,11 +113,23 @@ void BattleGroundBE::AddPlayer(Player *plr) BattleGroundBEScore* sc = new BattleGroundBEScore; m_PlayerScores[plr->GetGUID()] = sc; + + UpdateWorldState(0x9f1, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0x9f0, GetAlivePlayersCountByTeam(HORDE)); } void BattleGroundBE::RemovePlayer(Player* /*plr*/, uint64 /*guid*/) { + if(GetStatus() == STATUS_WAIT_LEAVE) + return; + UpdateWorldState(0x9f1, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0x9f0, GetAlivePlayersCountByTeam(HORDE)); + + if(!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } void BattleGroundBE::HandleKillPlayer(Player *player, Player *killer) @@ -120,17 +143,27 @@ void BattleGroundBE::HandleKillPlayer(Player *player, Player *killer) return; } - BattleGround::HandleKillPlayer(player, killer); + BattleGround::HandleKillPlayer(player,killer); - uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam()); + UpdateWorldState(0x9f1, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0x9f0, GetAlivePlayersCountByTeam(HORDE)); - ++m_TeamKills[killer_team_index]; // add kills to killer's team - - if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(player->GetTeam())) + if(!GetAlivePlayersCountByTeam(ALLIANCE)) { // all opponents killed - EndBattleGround(killer->GetTeam()); + EndBattleGround(HORDE); } + else if(!GetAlivePlayersCountByTeam(HORDE)) + { + // all opponents killed + EndBattleGround(ALLIANCE); + } +} + +bool BattleGroundBE::HandlePlayerUnderMap(Player *player) +{ + player->TeleportTo(GetMapId(),6238.930176,262.963470,0.889519,player->GetOrientation(),false); + return true; } void BattleGroundBE::HandleAreaTrigger(Player *Source, uint32 Trigger) @@ -159,10 +192,16 @@ void BattleGroundBE::HandleAreaTrigger(Player *Source, uint32 Trigger) // HandleTriggerBuff(buff_guid,Source); } +void BattleGroundBE::FillInitialWorldStates(WorldPacket &data) +{ + data << uint32(0x9f1) << uint32(GetAlivePlayersCountByTeam(ALLIANCE)); // 7 + data << uint32(0x9f0) << uint32(GetAlivePlayersCountByTeam(HORDE)); // 8 + data << uint32(0x9f3) << uint32(1); // 9 +} + void BattleGroundBE::ResetBGSubclass() { - m_TeamKills[BG_TEAM_ALLIANCE] = 0; - m_TeamKills[BG_TEAM_HORDE] = 0; + } bool BattleGroundBE::SetupBattleGround() diff --git a/src/game/BattleGroundBE.h b/src/game/BattleGroundBE.h index f74f0a648..df46efbfd 100644 --- a/src/game/BattleGroundBE.h +++ b/src/game/BattleGroundBE.h @@ -64,12 +64,11 @@ class BattleGroundBE : public BattleGround void HandleAreaTrigger(Player *Source, uint32 Trigger); bool SetupBattleGround(); void ResetBGSubclass(); + virtual void FillInitialWorldStates(WorldPacket &d); void HandleKillPlayer(Player* player, Player *killer); + bool HandlePlayerUnderMap(Player * plr); /* Scorekeeping */ void UpdatePlayerScore(Player *Source, uint32 type, uint32 value); - - private: - uint32 m_TeamKills[2]; // count of kills for each team }; #endif diff --git a/src/game/BattleGroundEY.cpp b/src/game/BattleGroundEY.cpp index 241ad5b98..279816b34 100644 --- a/src/game/BattleGroundEY.cpp +++ b/src/game/BattleGroundEY.cpp @@ -54,9 +54,18 @@ void BattleGroundEY::Update(time_t diff) { m_Events |= 0x01; + // setup here, only when at least one player has ported to the map + if(!SetupBattleGround()) + { + EndNow(); + return; + } + SpawnBGObject(BG_EY_OBJECT_DOOR_A, RESPAWN_IMMEDIATELY); SpawnBGObject(BG_EY_OBJECT_DOOR_H, RESPAWN_IMMEDIATELY); +// SpawnBGCreature(EY_SPIRIT_MAIN_ALLIANCE, RESPAWN_IMMEDIATELY); +// SpawnBGCreature(EY_SPIRIT_MAIN_HORDE, RESPAWN_IMMEDIATELY); for(uint32 i = BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER; i < BG_EY_OBJECT_MAX; ++i) SpawnBGObject(i, RESPAWN_ONE_DAY); @@ -572,7 +581,17 @@ void BattleGroundEY::HandleKillPlayer(Player *player, Player *killer) void BattleGroundEY::EventPlayerDroppedFlag(Player *Source) { - // Drop allowed in any BG state + if(GetStatus() != STATUS_IN_PROGRESS) + { + // if not running, do not cast things at the dropper player, neither send unnecessary messages + // just take off the aura + if(IsFlagPickedup() && GetFlagPickerGUID() == Source->GetGUID()) + { + SetFlagPicker(0); + Source->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL); + } + return; + } if(!IsFlagPickedup()) return; @@ -744,6 +763,8 @@ void BattleGroundEY::EventTeamCapturedPoint(Player *Source, uint32 Point) sLog.outError("BatteGroundEY: Failed to spawn spirit guide! point: %u, team: %u, graveyard_id: %u", Point, Team, m_CapturingPointTypes[Point].GraveYardId); +// SpawnBGCreature(Point,RESPAWN_IMMEDIATELY); + UpdatePointsIcons(Team, Point); UpdatePointsCount(Team); } diff --git a/src/game/BattleGroundHandler.cpp b/src/game/BattleGroundHandler.cpp index 3b744f043..13fd1d539 100644 --- a/src/game/BattleGroundHandler.cpp +++ b/src/game/BattleGroundHandler.cpp @@ -26,9 +26,11 @@ #include "MapManager.h" #include "ObjectAccessor.h" #include "Object.h" +#include "Chat.h" #include "BattleGroundMgr.h" #include "BattleGroundWS.h" #include "BattleGround.h" +#include "ArenaTeam.h" #include "Language.h" void WorldSession::HandleBattleGroundHelloOpcode( WorldPacket & recv_data ) @@ -76,18 +78,25 @@ void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data ) uint32 bgTypeId; uint32 instanceId; uint8 joinAsGroup; + Group * grp; recv_data >> guid; // battlemaster guid recv_data >> bgTypeId; // battleground type id (DBC id) recv_data >> instanceId; // instance id, 0 if First Available selected recv_data >> joinAsGroup; // join as group - sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from: " I64FMT " for BG (Type: %u)", guid, bgTypeId); - - if(bgTypeId >= MAX_BATTLEGROUND_TYPES) // cheating? + if(bgTypeId >= MAX_BATTLEGROUND_TYPES) + { + sLog.outError("Battleground: invalid bgtype received. possible cheater? player guid %u",_player->GetGUIDLow()); return; + } - // ignore if we already in BG or BG queue + sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from: " I64FMT, guid); + + // can do this, since it's battleground, not arena + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId, 0); + + // ignore if player is already in BG if(_player->InBattleGround()) return; @@ -98,74 +107,82 @@ void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data ) if(!unit->isBattleMaster()) // it's not battlemaster return; - // check Deserter debuff - if( !_player->CanJoinToBattleground() ) + // get bg instance or bg template if instance not found + BattleGround * bg = 0; + if(instanceId) + BattleGround *bg = sBattleGroundMgr.GetBattleGround(instanceId); + + if(!bg && !(bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId))) { - WorldPacket data(SMSG_GROUP_JOINED_BATTLEGROUND, 4); - data << (uint32) 0xFFFFFFFE; - _player->GetSession()->SendPacket(&data); + sLog.outError("Battleground: no available bg / template found"); return; } - // check existence - BattleGround *bg = sBattleGroundMgr.GetBattleGround(bgTypeId); - if(!bg) - return; - - if(joinAsGroup && _player->GetGroup()) + // check queueing conditions + if(!joinAsGroup) { - Group *grp = _player->GetGroup(); + // check Deserter debuff + if( !_player->CanJoinToBattleground() ) + { + WorldPacket data(SMSG_GROUP_JOINED_BATTLEGROUND, 4); + data << (uint32) 0xFFFFFFFE; + _player->GetSession()->SendPacket(&data); + return; + } + // check if already in queue + if (_player->GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) + //player is already in this queue + return; + // check if has free queue slots + if(!_player->HasFreeBattleGroundQueueId()) + return; + } + else + { + grp = _player->GetGroup(); + // no group found, error + if(!grp) + return; + uint32 err = grp->CanJoinBattleGroundQueue(bgTypeId, bgQueueTypeId, 0, bg->GetMaxPlayersPerTeam(), false, 0); + if (err != BG_JOIN_ERR_OK) + { + SendBattleGroundOrArenaJoinError(err); + return; + } + } + // if we're here, then the conditions to join a bg are met. We can proceed in joining. + + // _player->GetGroup() was already checked, grp is already initialized + if(joinAsGroup /* && _player->GetGroup()*/) + { + sLog.outDebug("Battleground: the following players are joining as group:"); + GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, 0, false, 0); for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) { Player *member = itr->getSource(); - if(!member) continue; + if(!member) continue; // this should never happen - if( !member->CanJoinToBattleground() ) - { - WorldPacket data(SMSG_GROUP_JOINED_BATTLEGROUND, 4); - data << (uint32) 0xFFFFFFFE; - _player->GetSession()->SendPacket(&data); - continue; - } - if (member->InBattleGroundQueueForBattleGroundType(bgTypeId)) - //player is already in this queue - continue; - - WorldPacket data; - // add to queue - uint32 queueSlot = member->AddBattleGroundQueueId(bgTypeId); - if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES) - { - // fill data packet - //member->GetSession()->SendPacket(data); - continue; - } + uint32 queueSlot = member->AddBattleGroundQueueId(bgQueueTypeId); // add to queue // store entry point coords (same as leader entry point) member->SetBattleGroundEntryPoint(_player->GetMapId(),_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, bgTypeId); member->GetSession()->SendPacket(&data); - sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].AddPlayer(member, bgTypeId); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(member, ginfo); + sLog.outDebug("Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,member->GetGUIDLow(), member->GetName()); } + sLog.outDebug("Battleground: group end"); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel()); } else { - if (_player->InBattleGroundQueueForBattleGroundType(bgTypeId)) - //player is already in this queue - return; - uint32 queueSlot = _player->AddBattleGroundQueueId(bgTypeId); - if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES) - { - WorldPacket data; - // fill data packet - //SendPacket(data); - return; - } - + // already checked if queueSlot is valid, now just get it + uint32 queueSlot = _player->AddBattleGroundQueueId(bgQueueTypeId); // store entry point coords _player->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); @@ -173,7 +190,11 @@ void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data ) // send status packet (in queue) sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0); SendPacket(&data); - sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].AddPlayer(_player, bgTypeId); + + GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, 0, false, 0); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(_player, ginfo); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel()); + sLog.outDebug("Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,_player->GetGUIDLow(), _player->GetName()); } } @@ -247,12 +268,11 @@ void WorldSession::HandleBattleGroundListOpcode( WorldPacket &recv_data ) uint32 bgTypeId; recv_data >> bgTypeId; // id from DBC - if(bgTypeId >= MAX_BATTLEGROUND_TYPES) // cheating? - return; - - // can't be received if player not in BG queue - if(!_player->InBattleGroundQueueForBattleGroundType(bgTypeId)) + if(bgTypeId >= MAX_BATTLEGROUND_TYPES) + { + sLog.outError("Battleground: invalid bgtype received."); return; + } BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId); @@ -270,80 +290,201 @@ void WorldSession::HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data ) sLog.outDebug( "WORLD: Recvd CMSG_BATTLEFIELD_PORT Message"); - uint8 unk1; + uint8 type; // arenatype if arena uint8 unk2; // unk, can be 0x0 (may be if was invited?) and 0x1 + uint32 instanceId; uint32 bgTypeId; // type id from dbc uint16 unk; // 0x1F90 constant? uint8 action; // enter battle 0x1, leave queue 0x0 - recv_data >> unk1 >> unk2 >> bgTypeId >> unk >> action; + recv_data >> type >> unk2 >> bgTypeId >> unk >> action; - if(bgTypeId >= MAX_BATTLEGROUND_TYPES) // cheating? - return; - - if(!_player->InBattleGroundQueueForBattleGroundType(bgTypeId)) - return; - - BattleGround *bg = sBattleGroundMgr.GetBattleGround(bgTypeId); - if(!bg) - return; - - uint32 queueSlot = 0; - WorldPacket data; - switch(action) + if(bgTypeId >= MAX_BATTLEGROUND_TYPES) { - case 1: // port to battleground - // cheating? - if(!_player->IsInvitedForBattleGroundType(bgTypeId)) - return; - - // check if player is not deserter - if( !_player->CanJoinToBattleground() ) + sLog.outError("Battleground: invalid bgtype received."); + // update battleground slots for the player to fix his UI and sent data. + // this is a HACK, I don't know why the client starts sending invalid packets in the first place. + // it usually happens with extremely high latency (if debugging / stepping in the code for example) + if(_player->InBattleGroundQueue()) + { + // update all queues, send invitation info if player is invited, queue info if queued + for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - WorldPacket data2; - data2.Initialize(SMSG_GROUP_JOINED_BATTLEGROUND, 4); - data2 << (uint32) 0xFFFFFFFE; - SendPacket(&data2); - return; + uint32 queue_id = _player->GetBattleGroundQueueId(i); + if(!queue_id) + continue; + BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); + // if the player is not in queue, contine + if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) + continue; + + // no group information, this should never happen + if(!itrPlayerStatus->second.GroupInfo) + continue; + + BattleGround * bg = NULL; + + // get possibly needed data from groupinfo + bgTypeId = itrPlayerStatus->second.GroupInfo->BgTypeId; + uint8 arenatype = itrPlayerStatus->second.GroupInfo->ArenaType; + uint8 israted = itrPlayerStatus->second.GroupInfo->IsRated; + uint8 status = 0; + + + if(!itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID) + { + // not invited to bg, get template + bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); + status = STATUS_WAIT_QUEUE; + } + else + { + // get the bg we're invited to + BattleGround * bg = sBattleGroundMgr.GetBattleGround(itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID); + status = STATUS_WAIT_JOIN; + } + + // if bg not found, then continue + if(!bg) + continue; + + // don't invite if already in the instance + if(_player->InBattleGround() && _player->GetBattleGround() && _player->GetBattleGround()->GetInstanceID() == bg->GetInstanceID()) + continue; + + // re - invite player with proper data + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, itrPlayerStatus->second.GroupInfo->Team?itrPlayerStatus->second.GroupInfo->Team:_player->GetTeam(), i, status, INVITE_ACCEPT_WAIT_TIME, 0, arenatype, israted); + SendPacket(&data); } + } + return; + } - // if the player is dead, resurrect him before teleport - if(!_player->isAlive()) - { - _player->ResurrectPlayer(1.0f); - _player->SpawnCorpseBones(); - } + uint32 bgQueueTypeId = 0; + // get the bg what we were invited to + BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus; + bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId,type); + itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); - // leave current group - _player->RemoveFromGroup(); + if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) + { + sLog.outError("Battleground: itrplayerstatus not found."); + return; + } + instanceId = itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID; - // packet to player about BG status - queueSlot = _player->GetBattleGroundQueueIndex(bgTypeId); - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime()); - _player->GetSession()->SendPacket(&data); + // if action == 1, then instanceId is _required_ + if(!instanceId && action == 1) + { + sLog.outError("Battleground: instance not found."); + return; + } - // remove battleground queue status from BGmgr - sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].RemovePlayer(_player->GetGUID(), false); + BattleGround *bg = sBattleGroundMgr.GetBattleGround(instanceId); - // this is still needed here if battleground "jumping" shouldn't add deserter debuff - // also this required to prevent stuck at old battleground after SetBattleGroundId set to new - if (BattleGround *currentBg = _player->GetBattleGround()) - currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true); + // bg template might and must be used in case of leaving queue, when instance is not created yet + if(!bg && action == 0) + bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - _player->SetBattleGroundId(bg->GetTypeID()); - sBattleGroundMgr.SendToBattleGround(_player, bgTypeId); - bg->AddPlayer(_player); - break; - case 0: // leave queue - queueSlot = _player->GetBattleGroundQueueIndex(bgTypeId); - _player->RemoveBattleGroundQueueId(bgTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_NONE, 0, 0); - sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].RemovePlayer(_player->GetGUID(), true); - SendPacket(&data); - break; - default: - sLog.outError("Battleground port: unknown action %u", action); - break; + if(!bg) + { + sLog.outError("Battleground: bg not found."); + return; + } + + bgTypeId = bg->GetTypeID(); + + if(_player->InBattleGroundQueue()) + { + uint32 queueSlot = 0; + uint32 team = 0; + uint32 arenatype = 0; + uint32 israted = 0; + uint32 rating = 0; + uint32 opponentsRating = 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; + rating = pitr->second.GroupInfo->ArenaTeamRating; + opponentsRating = pitr->second.GroupInfo->OpponentsTeamRating; + } + else + { + sLog.outError("Battleground: Invalid player queue info!"); + return; + } + WorldPacket data; + switch(action) + { + case 1: // port to battleground + if(!_player->IsInvitedForBattleGroundQueueType(bgQueueTypeId)) + return; // cheating? + // resurrect the player + if(!_player->isAlive()) + { + _player->ResurrectPlayer(1.0f); + _player->SpawnCorpseBones(); + } + // stop taxi flight at port + if(_player->isInFlight()) + { + _player->GetMotionMaster()->MovementExpired(); + _player->m_taxi.ClearTaxiDestinations(); + } + _player->RemoveFromGroup(); + queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime()); + _player->GetSession()->SendPacket(&data); + // remove battleground queue status from BGmgr + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(_player->GetGUID(), false); + // this is still needed here if battleground "jumping" shouldn't add deserter debuff + // also this required to prevent stuck at old battleground after SetBattleGroundId set to new + if( BattleGround *currentBg = _player->GetBattleGround() ) + currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true); + + // set the destination instance id + _player->SetBattleGroundId(bg->GetInstanceID()); + // set the destination team + _player->SetBGTeam(team); + // bg->HandleBeforeTeleportToBattleGround(_player); + sBattleGroundMgr.SendToBattleGround(_player, instanceId); + // add only in HandleMoveWorldPortAck() + // bg->AddPlayer(_player,team); + sLog.outDebug("Battleground: player %s (%u) joined battle for bg %u, bgtype %u, queue type %u.",_player->GetName(),_player->GetGUIDLow(),bg->GetInstanceID(),bg->GetTypeID(),bgQueueTypeId); + break; + case 0: // leave queue + queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); + /* + if player leaves rated arena match before match start, it is counted as he played but he lost + */ + if (israted) + { + ArenaTeam * at = objmgr.GetArenaTeamById(team); + if (at) + { + sLog.outDebug("UPDATING memberLost's personal arena rating for %u by opponents rating: %u, because he has left queue!", GUID_LOPART(_player->GetGUID()), opponentsRating); + at->MemberLost(_player, opponentsRating); + at->SaveToDB(); + } + } + _player->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_NONE, 0, 0); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(_player->GetGUID(), true); + // player left queue, we should update it, maybe now his group fits in + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId,_player->GetBattleGroundQueueIdFromLevel(),arenatype,israted,rating); + SendPacket(&data); + sLog.outDebug("Battleground: player %s (%u) left queue for bgtype %u, queue type %u.",_player->GetName(),_player->GetGUIDLow(),bg->GetTypeID(),bgQueueTypeId); + break; + default: + sLog.outError("Battleground port: unknown action %u", action); + break; + } } } @@ -384,7 +525,8 @@ void WorldSession::HandleBattlefieldStatusOpcode( WorldPacket & /*recv_data*/ ) BattleGround *bg = _player->GetBattleGround(); if(bg) { - uint32 queueSlot = _player->GetBattleGroundQueueIndex(bg->GetTypeID()); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + uint32 queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); if((bg->GetStatus() <= STATUS_IN_PROGRESS)) { sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime()); @@ -392,15 +534,25 @@ void WorldSession::HandleBattlefieldStatusOpcode( WorldPacket & /*recv_data*/ ) } for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - uint32 queue_id = _player->GetBattleGroundQueueId(i); - if (i == queueSlot || !queue_id) + uint32 queue_id = _player->GetBattleGroundQueueId(i); // battlegroundqueueid stores the type id, not the instance id, so this is definitely wrong + uint8 arenatype = sBattleGroundMgr.BGArenaType(queue_id); + uint8 isRated = 0; + if (i == queueSlot || !queue_id) // we need to get the instance ids continue; - BattleGround *bg2 = sBattleGroundMgr.GetBattleGround(queue_id); + BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); + if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) + continue; + if(itrPlayerStatus->second.GroupInfo) + { + arenatype = itrPlayerStatus->second.GroupInfo->ArenaType; + isRated = itrPlayerStatus->second.GroupInfo->IsRated; + } + BattleGround *bg2 = sBattleGroundMgr.GetBattleGroundTemplate(sBattleGroundMgr.BGTemplateId(queue_id)); // try this if(bg2) { //in this call is small bug, this call should be filled by player's waiting time in queue //this call nulls all timers for client : - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg2, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg2, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0,arenatype,isRated); SendPacket(&data); } } @@ -411,16 +563,36 @@ void WorldSession::HandleBattlefieldStatusOpcode( WorldPacket & /*recv_data*/ ) // we should update all queues? .. i'm not sure if this code is correct for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - if(uint32 queue_id = _player->GetBattleGroundQueueId(i)) + uint32 queue_id = _player->GetBattleGroundQueueId(i); + if(!queue_id) + continue; + uint32 bgTypeId = sBattleGroundMgr.BGTemplateId(queue_id); + uint8 arenatype = sBattleGroundMgr.BGArenaType(queue_id); + uint8 isRated = 0; + BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); + BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].find(_player->GetGUID()); + if(itrPlayerStatus == sBattleGroundMgr.m_BattleGroundQueues[queue_id].m_QueuedPlayers[_player->GetBattleGroundQueueIdFromLevel()].end()) + continue; + if(itrPlayerStatus->second.GroupInfo) { - if(BattleGround *bg = sBattleGroundMgr.GetBattleGround(queue_id)) - { - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0); - SendPacket(&data); - } + arenatype = itrPlayerStatus->second.GroupInfo->ArenaType; + isRated = itrPlayerStatus->second.GroupInfo->IsRated; + } + if(bg && queue_id) + { + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 0, 0, arenatype, isRated); + SendPacket(&data); } } } +/* else // not sure if it needed... + { + for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + { + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, NULL, _player->GetTeam(),i , STATUS_NONE, 0, 0); + SendPacket(&data); + } + }*/ } void WorldSession::HandleAreaSpiritHealerQueryOpcode( WorldPacket & recv_data ) @@ -480,16 +652,12 @@ void WorldSession::HandleBattleGroundArenaJoin( WorldPacket & recv_data ) if(_player->InBattleGround()) return; - for(int qId = 0; qId < PLAYER_MAX_BATTLEGROUND_QUEUES; ++qId) - { - if(_player->GetBattleGroundQueueId(qId) != 0) - return; - } - uint64 guid; // arena Battlemaster guid uint8 type; // 2v2, 3v3 or 5v5 uint8 asGroup; // asGroup uint8 isRated; // isRated + Group * grp; + recv_data >> guid >> type >> asGroup >> isRated; Creature *unit = ObjectAccessor::GetCreature(*_player, guid); @@ -500,6 +668,7 @@ void WorldSession::HandleBattleGroundArenaJoin( WorldPacket & recv_data ) return; uint8 arenatype = 0; + uint32 arenaRating = 0; switch(type) { @@ -517,88 +686,118 @@ void WorldSession::HandleBattleGroundArenaJoin( WorldPacket & recv_data ) return; } - if(isRated && !_player->GetArenaTeamId(type)) // player not in arena team of that size + //check existance + BattleGround* bg = NULL; + if( !(bg = sBattleGroundMgr.GetBattleGroundTemplate(BATTLEGROUND_AA)) ) { - _player->GetSession()->SendNotInArenaTeamPacket(arenatype); + sLog.outError("Battleground: template bg (all arenas) not found"); return; } - if(asGroup && !_player->GetGroup()) // player not in group - return; + uint8 bgTypeId = bg->GetTypeID(); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId, arenatype); - // check existence - BattleGround *bg = sBattleGroundMgr.GetBattleGround(BATTLEGROUND_AA); - if(!bg) - return; - - bg->SetArenaType(arenatype); - bg->SetRated(isRated); - - if(asGroup && _player->GetGroup()) + // check queueing conditions + if(!asGroup) { - Group *grp = _player->GetGroup(); + // check if already in queue + if (_player->GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) + //player is already in this queue + return; + // check if has free queue slots + if(!_player->HasFreeBattleGroundQueueId()) + return; + } + else + { + grp = _player->GetGroup(); + // no group found, error + if(!grp) + return; + uint32 err = grp->CanJoinBattleGroundQueue(bgTypeId, bgQueueTypeId, arenatype, arenatype, (bool)isRated, type); + if (err != BG_JOIN_ERR_OK) + { + SendBattleGroundOrArenaJoinError(err); + return; + } + } + + uint32 ateamId = 0; + + if(isRated) + { + ateamId = _player->GetArenaTeamId(type); + // check real arenateam existence only here (if it was moved to group->CanJoin .. () then we would ahve to get it twice) + ArenaTeam * at = objmgr.GetArenaTeamById(ateamId); + if(!at) + { + _player->GetSession()->SendNotInArenaTeamPacket(arenatype); + return; + } + // get the team rating for queueing + arenaRating = at->GetRating(); + // the arenateam id must match for everyone in the group + // get the personal ratings for queueing + uint32 avg_pers_rating = 0; + for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *member = itr->getSource(); + + // calc avg personal rating + avg_pers_rating += member->GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (type*6) + 5); + } + + if( arenatype ) + avg_pers_rating /= arenatype; + + // if avg personal rating is more than 150 points below the teams rating, the team will be queued against an opponent matching or similar to the average personal rating + if(avg_pers_rating + 150 < arenaRating) + arenaRating = avg_pers_rating; + } + + if(asGroup) + { + GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, arenatype, isRated, arenaRating, ateamId); + sLog.outDebug("Battleground: arena join as group start"); + if(isRated) + sLog.outDebug("Battleground: arena team id %u, leader %s queued with rating %u for type %u",_player->GetArenaTeamId(type),_player->GetName(),arenaRating,arenatype); for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) { Player *member = itr->getSource(); if(!member) continue; - /*if (!member->CanJoinToBattleground()) - //player has deserter aura .. do nothing - */ - - if (member->InBattleGroundQueueForBattleGroundType(BATTLEGROUND_AA)) - //player is already in this queue - continue; - - // add to queue - uint32 queueSlot = member->AddBattleGroundQueueId(BATTLEGROUND_AA); - if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES) - { - WorldPacket data; - //fill data - //member->GetSession()->SendPacket(data); - continue; - } + uint32 queueSlot = member->AddBattleGroundQueueId(bgQueueTypeId);// add to queue // store entry point coords (same as leader entry point) member->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); WorldPacket data; // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, member->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, member->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0, arenatype, isRated); member->GetSession()->SendPacket(&data); - sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, BATTLEGROUND_AA); + sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, bgTypeId); member->GetSession()->SendPacket(&data); - sBattleGroundMgr.m_BattleGroundQueues[BATTLEGROUND_AA].AddPlayer(member, BATTLEGROUND_AA); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(member, ginfo); + sLog.outDebug("Battleground: player joined queue for arena as group bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,member->GetGUIDLow(), member->GetName()); } + sLog.outDebug("Battleground: arena join as group end"); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(), arenatype, isRated, arenaRating); } else { - /*if (!member->CanJoinToBattleground()) - //player has deserter aura .. do nothing - */ - - if (_player->InBattleGroundQueueForBattleGroundType(BATTLEGROUND_AA)) - //player is already in this queue - return; - - uint32 queueSlot = _player->AddBattleGroundQueueId(BATTLEGROUND_AA); - if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES) - { - WorldPacket data; - //fill data (player is in 3 queues already) - //SendPacket(data); - return; - } + uint32 queueSlot = _player->AddBattleGroundQueueId(bgQueueTypeId); // store entry point coords _player->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); WorldPacket data; // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), queueSlot, STATUS_WAIT_QUEUE, 0, 0, arenatype, isRated); SendPacket(&data); - sBattleGroundMgr.m_BattleGroundQueues[BATTLEGROUND_AA].AddPlayer(_player, BATTLEGROUND_AA); + GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, arenatype, isRated, arenaRating); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(_player, ginfo); + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(), arenatype, isRated, arenaRating); + sLog.outDebug("Battleground: player joined queue for arena, skirmish, bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,_player->GetGUIDLow(), _player->GetName()); } } @@ -620,3 +819,41 @@ void WorldSession::HandleBattleGroundReportAFK( WorldPacket & recv_data ) reportedPlayer->ReportedAfkBy(_player); } + +void WorldSession::SendBattleGroundOrArenaJoinError(uint8 err) +{ + WorldPacket data; + int32 msg; + switch (err) + { + case BG_JOIN_ERR_OFFLINE_MEMBER: + msg = LANG_BG_GROUP_OFFLINE_MEMBER; + break; + case BG_JOIN_ERR_GROUP_TOO_MANY: + msg = LANG_BG_GROUP_TOO_LARGE; + break; + case BG_JOIN_ERR_MIXED_FACTION: + msg = LANG_BG_GROUP_MIXED_FACTION; + break; + case BG_JOIN_ERR_MIXED_LEVELS: + msg = LANG_BG_GROUP_MIXED_LEVELS; + break; + case BG_JOIN_ERR_GROUP_MEMBER_ALREADY_IN_QUEUE: + msg = LANG_BG_GROUP_MEMBER_ALREADY_IN_QUEUE; + break; + case BG_JOIN_ERR_GROUP_DESERTER: + msg = LANG_BG_GROUP_MEMBER_DESERTER; + break; + case BG_JOIN_ERR_ALL_QUEUES_USED: + msg = LANG_BG_GROUP_MEMBER_NO_FREE_QUEUE_SLOTS; + break; + case BG_JOIN_ERR_GROUP_NOT_ENOUGH: + case BG_JOIN_ERR_MIXED_ARENATEAM: + default: + return; + break; + } + ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, GetMangosString(msg), NULL); + SendPacket(&data); + return; +} diff --git a/src/game/BattleGroundMgr.cpp b/src/game/BattleGroundMgr.cpp index 9c3350bd1..9d3a5ea5c 100644 --- a/src/game/BattleGroundMgr.cpp +++ b/src/game/BattleGroundMgr.cpp @@ -30,10 +30,13 @@ #include "SharedDefines.h" #include "Policies/SingletonImp.h" #include "MapManager.h" +#include "Map.h" +#include "MapInstanced.h" #include "ObjectMgr.h" #include "ProgressBar.h" #include "World.h" #include "Chat.h" +#include "ArenaTeam.h" INSTANTIATE_SINGLETON_1( BattleGroundMgr ); @@ -44,12 +47,12 @@ INSTANTIATE_SINGLETON_1( BattleGroundMgr ); BattleGroundQueue::BattleGroundQueue() { //queues are empty, we don't have to call clear() - for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) +/* for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) { - m_QueuedPlayers[i].Horde = 0; - m_QueuedPlayers[i].Alliance = 0; + //m_QueuedPlayers[i].Horde = 0; + //m_QueuedPlayers[i].Alliance = 0; //m_QueuedPlayers[i].AverageTime = 0; - } + }*/ } BattleGroundQueue::~BattleGroundQueue() @@ -57,31 +60,188 @@ BattleGroundQueue::~BattleGroundQueue() for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) { m_QueuedPlayers[i].clear(); + for(QueuedGroupsList::iterator itr = m_QueuedGroups[i].begin(); itr!= m_QueuedGroups[i].end(); ++itr) + { + delete (*itr); + } + m_QueuedGroups[i].clear(); } } -void BattleGroundQueue::AddPlayer(Player *plr, uint32 bgTypeId) +// initialize eligible groups from the given source matching the given specifications +void BattleGroundQueue::EligibleGroups::Init(BattleGroundQueue::QueuedGroupsList *source, uint32 BgTypeId, uint32 side, uint32 MaxPlayers, uint8 ArenaType, bool IsRated, uint32 MinRating, uint32 MaxRating, uint32 DisregardTime, uint32 excludeTeam) +{ + // clear from prev initialization + clear(); + BattleGroundQueue::QueuedGroupsList::iterator itr, next; + // iterate through the source + for(itr = source->begin(); itr!= source->end(); itr = next) + { + next = itr; + ++next; + if( (*itr)->BgTypeId == BgTypeId && // bg type must match + (*itr)->ArenaType == ArenaType && // arena type must match + (*itr)->IsRated == IsRated && // israted must match + (*itr)->IsInvitedToBGInstanceGUID == 0 && // leave out already invited groups + (*itr)->Team == side && // match side + (*itr)->Players.size() <= MaxPlayers && // the group must fit in the bg + ( !excludeTeam || (*itr)->ArenaTeamId != excludeTeam ) && // if excludeTeam is specified, leave out those arena team ids + ( !IsRated || (*itr)->Players.size() == MaxPlayers ) && // if rated, then pass only if the player count is exact NEEDS TESTING! (but now this should never happen) + ( (*itr)->JoinTime <= DisregardTime // pass if disregard time is greater than join time + || (*itr)->ArenaTeamRating == 0 // pass if no rating info + || ( (*itr)->ArenaTeamRating >= MinRating // pass if matches the rating range + && (*itr)->ArenaTeamRating <= MaxRating ) ) ) + { + // the group matches the conditions + // insert it in order of groupsize, and join time + uint32 size = (*itr)->Players.size(); + uint32 jointime = (*itr)->JoinTime; + bool inserted = false; + + for(std::list::iterator elig_itr = begin(); elig_itr != end(); ++elig_itr) + { + // if the next one's size is smaller, then insert + // also insert if the next one's size is equal, but it joined the queue later + if( ((*elig_itr)->Players.size()Players.size() == size && (*elig_itr)->JoinTime > jointime) ) + { + insert(elig_itr,(*itr)); + inserted = true; + break; + } + } + // if not inserted -> this is the smallest group -> push_back + if(!inserted) + { + push_back((*itr)); + } + } + } +} + +// remove group from eligible groups +// used when building selection pools +void BattleGroundQueue::EligibleGroups::RemoveGroup(GroupQueueInfo * ginfo) +{ + for(std::list::iterator itr = begin(); itr != end(); ++itr) + { + if((*itr)==ginfo) + { + erase(itr); + return; + } + } +} + +// selection pool initialization, used to clean up from prev selection +void BattleGroundQueue::SelectionPool::Init() +{ + SelectedGroups.clear(); + MaxGroup = 0; + PlayerCount = 0; +} + +// get the maximal group from the selection pool +// used when building the pool, and have to remove the largest +GroupQueueInfo * BattleGroundQueue::SelectionPool::GetMaximalGroup() +{ + if(SelectedGroups.empty()) + { + sLog.outError("Getting max group when selection pool is empty, this should never happen."); + MaxGroup = NULL; + return 0; + } + // actually select the max group if it's not set + if(MaxGroup==0 && !SelectedGroups.empty()) + { + uint32 max_size = 0; + for(std::list::iterator itr = SelectedGroups.begin(); itr != SelectedGroups.end(); ++itr) + { + if(max_size<(*itr)->Players.size()) + { + MaxGroup =(*itr); + max_size = MaxGroup->Players.size(); + } + } + } + return MaxGroup; +} + +// remove group info from selection pool +// used when building selection pools and have to remove maximal group +void BattleGroundQueue::SelectionPool::RemoveGroup(GroupQueueInfo *ginfo) +{ + // uninitiate max group info if needed + if(MaxGroup == ginfo) + MaxGroup = 0; + // find what to remove + for(std::list::iterator itr = SelectedGroups.begin(); itr != SelectedGroups.end(); ++itr) + { + if((*itr)==ginfo) + { + SelectedGroups.erase(itr); + // decrease selected players count + PlayerCount -= ginfo->Players.size(); + return; + } + } +} + +// add group to selection +// used when building selection pools +void BattleGroundQueue::SelectionPool::AddGroup(GroupQueueInfo * ginfo) +{ + SelectedGroups.push_back(ginfo); + // increase selected players count + PlayerCount+=ginfo->Players.size(); + if(!MaxGroup || ginfo->Players.size() > MaxGroup->Players.size()) + { + // update max group info if needed + MaxGroup = ginfo; + } +} + +// add group to bg queue with the given leader and bg specifications +GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, uint32 BgTypeId, uint8 ArenaType, bool isRated, uint32 arenaRating, uint32 arenateamid) +{ + uint32 queue_id = leader->GetBattleGroundQueueIdFromLevel(); + + // create new ginfo + // cannot use the method like in addplayer, because that could modify an in-queue group's stats + // (e.g. leader leaving queue then joining as individual again) + GroupQueueInfo* ginfo = new GroupQueueInfo; + ginfo->BgTypeId = BgTypeId; + ginfo->ArenaType = ArenaType; + ginfo->ArenaTeamId = arenateamid; + ginfo->IsRated = isRated; + ginfo->IsInvitedToBGInstanceGUID = 0; // maybe this should be modifiable by function arguments to enable selection of running instances? + ginfo->JoinTime = getMSTime(); + ginfo->Team = leader->GetTeam(); + ginfo->ArenaTeamRating = arenaRating; + ginfo->OpponentsTeamRating = 0; //initialize it to 0 + + ginfo->Players.clear(); + + m_QueuedGroups[queue_id].push_back(ginfo); + + // return ginfo, because it is needed to add players to this group info + return ginfo; +} + +void BattleGroundQueue::AddPlayer(Player *plr, GroupQueueInfo *ginfo) { uint32 queue_id = plr->GetBattleGroundQueueIdFromLevel(); //if player isn't in queue, he is added, if already is, then values are overwritten, no memory leak PlayerQueueInfo& info = m_QueuedPlayers[queue_id][plr->GetGUID()]; info.InviteTime = 0; - info.IsInvitedToBGInstanceGUID = 0; info.LastInviteTime = 0; info.LastOnlineTime = getMSTime(); - info.Team = plr->GetTeam(); - - //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; - - Update(bgTypeId, queue_id); + info.GroupInfo = ginfo; + // add the pinfo to ginfo's list + ginfo->Players[plr->GetGUID()] = &info; +/* if( sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE) ) { BattleGround* bg = sBattleGroundMgr.GetBattleGround(bgTypeId); @@ -111,7 +271,8 @@ void BattleGroundQueue::AddPlayer(Player *plr, uint32 bgTypeId) sWorld.SendWorldText(LANG_BG_QUEUE_ANNOUNCE_WORLD, bgName, q_min_level, q_max_level, qAlliance, (MinPlayers > qAlliance) ? (MinPlayers - qAlliance) : 0, qHorde, (MinPlayers > qHorde) ? (MinPlayers - qHorde) : 0); } - } + + }*/ } void BattleGroundQueue::RemovePlayer(uint64 guid, bool decreaseInvitedCount) @@ -120,11 +281,22 @@ void BattleGroundQueue::RemovePlayer(uint64 guid, bool decreaseInvitedCount) uint32 queue_id = 0; QueuedPlayersMap::iterator itr; + GroupQueueInfo * group; + QueuedGroupsList::iterator group_itr; bool IsSet = false; - if(!plr) - { //player is offline, we need to find him somewhere in queues - /// there is something wrong if this code is run, because we have in queue only online players! - sLog.outError("Battleground: removing offline player from BG queue - this might not happen, but it should not cause crash"); + if(plr) + { + queue_id = plr->GetBattleGroundQueueIdFromLevel(); + + itr = m_QueuedPlayers[queue_id].find(guid); + if(itr != m_QueuedPlayers[queue_id].end()) + IsSet = true; + } + + if(!IsSet) + { + // either player is offline, or he levelled up to another queue category + // sLog.outError("Battleground: removing offline player from BG queue - this might not happen, but it should not cause crash"); for (uint32 i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) { itr = m_QueuedPlayers[i].find(guid); @@ -136,44 +308,248 @@ void BattleGroundQueue::RemovePlayer(uint64 guid, bool decreaseInvitedCount) } } } - else - { //player is online, we have his level, so we can find exact queue from his level - queue_id = plr->GetBattleGroundQueueIdFromLevel(); - itr = m_QueuedPlayers[queue_id].find(guid); - IsSet = true; + + // couldn't find the player in bg queue, return + if(!IsSet) + { + sLog.outError("Battleground: couldn't find player to remove."); + return; } - //all variables are set, so remove player - //remove player from time queue - m_PlayersSortedByWaitTime[queue_id].remove(guid); + group = itr->second.GroupInfo; - if (IsSet && itr != m_QueuedPlayers[queue_id].end()) + for(group_itr=m_QueuedGroups[queue_id].begin(); group_itr != m_QueuedGroups[queue_id].end(); ++group_itr) { - if (!itr->second.IsInvitedToBGInstanceGUID) + if(group == (GroupQueueInfo*)(*group_itr)) + break; + } + + // variables are set (what about leveling up when in queue????) + // remove player from group + // if only player there, remove group + + // remove player queue info from group queue info + std::map::iterator pitr = group->Players.find(guid); + + if(pitr != group->Players.end()) + group->Players.erase(pitr); + + // check for iterator correctness + if (group_itr != m_QueuedGroups[queue_id].end() && itr != m_QueuedPlayers[queue_id].end()) + { + // used when player left the queue, NOT used when porting to bg + if (decreaseInvitedCount) { - if(itr->second.Team == ALLIANCE) - --m_QueuedPlayers[queue_id].Alliance; - else - --m_QueuedPlayers[queue_id].Horde; - } - else - { - if (decreaseInvitedCount) + // if invited to bg, and should decrease invited count, then do it + if(group->IsInvitedToBGInstanceGUID) { - BattleGround* bg = sBattleGroundMgr.GetBattleGround(itr->second.IsInvitedToBGInstanceGUID); + BattleGround* bg = sBattleGroundMgr.GetBattleGround(group->IsInvitedToBGInstanceGUID); if (bg) - bg->DecreaseInvitedCount(itr->second.Team); + bg->DecreaseInvitedCount(group->Team); + if (bg && !bg->GetPlayersSize() && !bg->GetInvitedCount(ALLIANCE) && !bg->GetInvitedCount(HORDE)) + { + // no more players on battleground, set delete it + bg->SetDeleteThis(); + } + } + // update the join queue, maybe now the player's group fits in a queue! + // not yet implemented (should store bgTypeId in group queue info?) + } + // remove player queue info + m_QueuedPlayers[queue_id].erase(itr); + // remove group queue info if needed + if(group->Players.empty()) + { + m_QueuedGroups[queue_id].erase(group_itr); + delete group; + } + // NEEDS TESTING! + // group wasn't empty, so it wasn't deleted, and player have left a rated queue -> everyone from the group should leave too + // don't remove recursively if already invited to bg! + else if(!group->IsInvitedToBGInstanceGUID && decreaseInvitedCount && group->IsRated) + { + // remove next player, this is recursive + // first send removal information + if(Player *plr2 = objmgr.GetPlayer(group->Players.begin()->first)) + { + BattleGround * bg = sBattleGroundMgr.GetBattleGroundTemplate(group->BgTypeId); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(group->BgTypeId,group->ArenaType); + uint32 queueSlot = plr2->GetBattleGroundQueueIndex(bgQueueTypeId); + plr2->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr2->GetTeam(), queueSlot, STATUS_NONE, 0, 0); + plr2->GetSession()->SendPacket(&data); + } + // then actually delete, this may delete the group as well! + RemovePlayer(group->Players.begin()->first,decreaseInvitedCount); + } + } +} + +bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side) +{ + // set side if needed + if(side) + ginfo->Team = side; + + if(!ginfo->IsInvitedToBGInstanceGUID) + { + // not yet invited + // set invitation + ginfo->IsInvitedToBGInstanceGUID = bg->GetInstanceID(); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + // loop through the players + for(std::map::iterator itr = ginfo->Players.begin(); itr != ginfo->Players.end(); ++itr) + { + // set status + itr->second->InviteTime = getMSTime(); + itr->second->LastInviteTime = getMSTime(); + + // get the player + Player* plr = objmgr.GetPlayer(itr->first); + // if offline, skip him + if(!plr) + continue; + + // invite the player + sBattleGroundMgr.InvitePlayer(plr, bg->GetInstanceID(),ginfo->Team); + + WorldPacket data; + + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); + + sLog.outDebug("Battleground: invited plr %s (%u) to BG instance %u queueindex %u bgtype %u, I can't help it if they don't press the enter battle button.",plr->GetName(),plr->GetGUIDLow(),bg->GetInstanceID(),queueSlot,bg->GetTypeID()); + + // send status packet + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, side?side:plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0); + plr->GetSession()->SendPacket(&data); + } + return true; + } + + return false; +} + +// this function is responsible for the selection of queued groups when trying to create new battlegrounds +bool BattleGroundQueue::BuildSelectionPool(uint32 bgTypeId, uint32 queue_id, uint32 MinPlayers, uint32 MaxPlayers, SelectionPoolBuildMode mode, uint8 ArenaType, bool isRated, uint32 MinRating, uint32 MaxRating, uint32 DisregardTime, uint32 excludeTeam) +{ + uint32 side; + switch(mode) + { + case NORMAL_ALLIANCE: + case ONESIDE_ALLIANCE_TEAM1: + case ONESIDE_ALLIANCE_TEAM2: + side = ALLIANCE; + break; + case NORMAL_HORDE: + case ONESIDE_HORDE_TEAM1: + case ONESIDE_HORDE_TEAM2: + side = HORDE; + break; + default: + //unknown mode, return false + sLog.outDebug("Battleground: unknown selection pool build mode, returning..."); + return false; + break; + } + + // inititate the groups eligible to create the bg + m_EligibleGroups.Init(&(m_QueuedGroups[queue_id]), bgTypeId, side, MaxPlayers, ArenaType, isRated, MinRating, MaxRating, DisregardTime, excludeTeam); + // init the selected groups (clear) + m_SelectionPools[mode].Init(); + while(!(m_EligibleGroups.empty())) + { + sLog.outDebug("m_EligibleGroups is not empty, continue building selection pool"); + // in decreasing group size, add groups to join if they fit in the MaxPlayersPerTeam players + for(EligibleGroups::iterator itr= m_EligibleGroups.begin(); itr!=m_EligibleGroups.end(); ++itr) + { + // get the maximal not yet checked group + GroupQueueInfo * MaxGroup = (*itr); + // if it fits in the maxplayer size, add it + if( (m_SelectionPools[mode].GetPlayerCount() + MaxGroup->Players.size()) <= MaxPlayers ) + { + m_SelectionPools[mode].AddGroup(MaxGroup); + } + } + if(m_SelectionPools[mode].GetPlayerCount()>=MinPlayers) + { + // the selection pool is set, return + sLog.outDebug("pool build succeeded, return true"); + return true; + } + // if the selection pool's not set, then remove the group with the highest player count, and try again with the rest. + GroupQueueInfo * MaxGroup = m_SelectionPools[mode].GetMaximalGroup(); + m_EligibleGroups.RemoveGroup(MaxGroup); + m_SelectionPools[mode].RemoveGroup(MaxGroup); + } + // failed to build a selection pool matching the given values + return false; +} + +// used to remove the Enter Battle window if the battle has already, but someone still has it +// (this can happen in arenas mainly, since the preparation is shorter than the timer for the bgqueueremove event +void BattleGroundQueue::BGEndedRemoveInvites(BattleGround *bg) +{ + uint32 queue_id = bg->GetQueueType(); + uint32 bgInstanceId = bg->GetInstanceID(); + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + QueuedGroupsList::iterator itr, next; + for(itr = m_QueuedGroups[queue_id].begin(); itr != m_QueuedGroups[queue_id].end(); itr = next) + { + // must do this way, because the groupinfo will be deleted when all playerinfos are removed + GroupQueueInfo * ginfo = (*itr); + next = itr; + ++next; + // if group was invited to this bg instance, then remove all references + if(ginfo->IsInvitedToBGInstanceGUID == bgInstanceId) + { + // after removing this much playerinfos, the ginfo will be deleted, so we'll use a for loop + uint32 to_remove = ginfo->Players.size(); + uint32 team = ginfo->Team; + for(int i = 0; i < to_remove; ++i) + { + // always remove the first one in the group + std::map::iterator itr2 = ginfo->Players.begin(); + if(itr2 == ginfo->Players.end()) + { + sLog.outError("Empty Players in ginfo, this should never happen!"); + return; + } + + // get the player + Player * plr = objmgr.GetPlayer(itr2->first); + if(!plr) + { + sLog.outError("Player offline when trying to remove from GroupQueueInfo, this should never happen."); + continue; + } + + // get the queueslot + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); + if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue + { + plr->RemoveBattleGroundQueueId(bgQueueTypeId); + // remove player from queue, this might delete the ginfo as well! don't use that pointer after this! + RemovePlayer(itr2->first, true); + // this is probably unneeded, since this player was already invited -> does not fit when initing eligible groups + // but updateing the queue can't hurt + Update(bgQueueTypeId, bg->GetQueueType()); + // send info to client + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, team, queueSlot, STATUS_NONE, 0, 0); + plr->GetSession()->SendPacket(&data); + } } } - m_QueuedPlayers[queue_id].erase(itr); } } /* -this method is called when player is inserted, or removed from BG Queue - there is only one player's status changed, so we don't use while(true) cycles to invite whole queue -add method calls this by itself, the remove method could works in other way, so you have to call this method from other code after calling remove method +this method is called when group is inserted, or player / group is removed from BG Queue - there is only one player's status changed, so we don't use while(true) cycles to invite whole queue +it must be called after fully adding the members of a group to ensure group joining +should be called after removeplayer functions in some cases */ -void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id) +void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id, uint8 arenatype, bool isRated, uint32 arenaRating) { if (queue_id >= MAX_BATTLEGROUND_QUEUES) { @@ -183,158 +559,352 @@ void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id) } //if no players in queue ... do nothing - if (m_QueuedPlayers[queue_id].Alliance == 0 && m_QueuedPlayers[queue_id].Horde == 0) + if (m_QueuedGroups[queue_id].empty()) return; + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId, arenatype); + //battleground with free slot for player should be always the last in this queue - for (BGFreeSlotQueueType::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); ++itr) + BGFreeSlotQueueType::iterator itr, next; + for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); itr = next) { + next = itr; + ++next; // battleground is running, so if: // DO NOT allow queue manager to invite new player to running arena - if ((*itr)->isBattleGround() && (*itr)->GetQueueType() == queue_id && (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE) + if ((*itr)->isBattleGround() && (*itr)->GetTypeID() == bgTypeId && (*itr)->GetQueueType() == queue_id && (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE) { //we must check both teams BattleGround* bg = *itr; //we have to store battleground pointer here, because when battleground is full, it is removed from free queue (not yet implemented!!) // and iterator is invalid - //check if there are some players in queue - if (m_QueuedPlayers[queue_id].Alliance > 0 || m_QueuedPlayers[queue_id].Horde > 0) + for(QueuedGroupsList::iterator itr = m_QueuedGroups[queue_id].begin(); itr != m_QueuedGroups[queue_id].end(); ++itr) { - for (PlayerGuidsSortedByTimeQueue::iterator itr2 = m_PlayersSortedByWaitTime[queue_id].begin(); itr2 != m_PlayersSortedByWaitTime[queue_id].end();) + // did the group join for this bg type? + if((*itr)->BgTypeId != bgTypeId) + continue; + // if so, check if fits in + if(bg->GetFreeSlotsForTeam((*itr)->Team) >= (*itr)->Players.size()) { - Player* plr = objmgr.GetPlayer(*itr2); - if (!plr) - { - //something is wrong!, kick player from queue - sLog.outError("BATTLEGROUND: problem with inviting offline player to Battleground queue .... pls report bug"); - uint64 oldval = *itr2; - itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); - RemovePlayer(oldval, true); - continue; - } - - // 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); - - // remove him from time queue - itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); - - // 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; - - // 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()); - - 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; - - //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 group fits in, invite it + InviteGroupToBG((*itr),bg,(*itr)->Team); } } + + if (!bg->HasFreeSlots()) + { + //remove BG from BGFreeSlotQueue + bg->RemoveFromBGFreeSlotQueue(); + } } } - /* 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! + // finished iterating through the bgs with free slots, maybe we need to create a new bg - /* here will be a most of change, when we create battlegrounds instantiated */ - /* if (there is enough players to start new BG) - Battleground* newbg = sBattleGroundMgr.CreateNewBattleGround(bgTypeId) - - that function will use the COPY constructor on BattleGround class ( in bg manager we should have one battleground as a template - (battleground template will be used only to create new BGs, it will be an instance of BG class, but it won't ever start) */ - - /* following code is working with current Battleground system and it should be removed, when BGs will work like instances */ - BattleGround* bg2 = sBattleGroundMgr.GetBattleGround(bgTypeId); - if (bg2->GetQueueType() != MAX_BATTLEGROUND_QUEUES || bg2->GetStatus() != STATUS_WAIT_QUEUE) - return; - if (m_QueuedPlayers[queue_id].Alliance >= bg2->GetMinPlayersPerTeam() && m_QueuedPlayers[queue_id].Horde >= bg2->GetMinPlayersPerTeam()) + BattleGround * bg_template = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); + if(!bg_template) { - bg2->SetStatus(STATUS_WAIT_JOIN); - bg2->SetQueueType(queue_id); + sLog.outError("Battleground: Update: bg template not found for %u", bgTypeId); + return; + } - for (PlayerGuidsSortedByTimeQueue::iterator itr2 = m_PlayersSortedByWaitTime[queue_id].begin(); itr2 != m_PlayersSortedByWaitTime[queue_id].end();) + // get the min. players per team, properly for larger arenas as well. (must have full teams for arena matches!) + uint32 MinPlayersPerTeam = bg_template->GetMinPlayersPerTeam(); + uint32 MaxPlayersPerTeam = bg_template->GetMaxPlayersPerTeam(); + if(bg_template->isArena()) + { + if(sBattleGroundMgr.isArenaTesting()) { - Player* plr = objmgr.GetPlayer(*itr2); - if (!plr) + MaxPlayersPerTeam = 1; + MinPlayersPerTeam = 1; + } + else + { + switch(arenatype) { - //something is wrong!, kick player from queue - sLog.outError("BATTLEGROUND: problem with inviting offline player to Battleground queue .... pls report bug"); - uint64 oldval = *itr2; - itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); - RemovePlayer(oldval, true); - continue; + 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; + } + } + } + + // found out the minimum and maximum ratings the newly added team should battle against + // arenaRating is the rating of the latest joined team + uint32 arenaMinRating = (arenaRating <= sBattleGroundMgr.GetMaxRatingDifference()) ? 0 : arenaRating - sBattleGroundMgr.GetMaxRatingDifference(); + // if no rating is specified, set maxrating to 0 + uint32 arenaMaxRating = (arenaRating == 0)? 0 : arenaRating + sBattleGroundMgr.GetMaxRatingDifference(); + uint32 discardTime = 0; + // if max rating difference is set and the time past since server startup is greater than the rating discard time + // (after what time the ratings aren't taken into account when making teams) then + // the discard time is current_time - time_to_discard, teams that joined after that, will have their ratings taken into account + // else leave the discard time on 0, this way all ratings will be discarded + if(sBattleGroundMgr.GetMaxRatingDifference() && getMSTime() >= sBattleGroundMgr.GetRatingDiscardTimer()) + discardTime = getMSTime() - sBattleGroundMgr.GetRatingDiscardTimer(); + + // try to build the selection pools + bool bAllyOK = BuildSelectionPool(bgTypeId, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam, NORMAL_ALLIANCE, arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); + if(bAllyOK) + sLog.outDebug("Battleground: ally pool succesfully build"); + else + sLog.outDebug("Battleground: ally pool wasn't created"); + bool bHordeOK = BuildSelectionPool(bgTypeId, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam, NORMAL_HORDE, arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); + if(bHordeOK) + sLog.outDebug("Battleground: horde pool succesfully built"); + else + sLog.outDebug("Battleground: horde pool wasn't created"); + + // if selection pools are ready, create the new bg + if (bAllyOK && bHordeOK) + { + BattleGround * bg2 = 0; + // special handling for arenas + if(bg_template->isArena()) + { + // Find a random arena, that can be created + uint8 arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL}; + uint32 arena_num = urand(0,2); + if( !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[arena_num%3])) && + !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+1)%3])) && + !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+2)%3])) ) + { + sLog.outError("Battleground: couldn't create any arena instance!"); + return; } - /* TODO: (i'm not sure this code will be useful: - here should be some condition like if (bg2->isArena() && bg2->isRated()) + // 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. + if(sBattleGroundMgr.isArenaTesting()) { - 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())) - { - // iterator to player's queue status - QueuedPlayersMap::iterator itrPlayerStatus = m_QueuedPlayers[queue_id].find(*itr2); - - // remove him from time queue - itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); - - // 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; - - //check if player is not already invited - if (!itrPlayerStatus->second.IsInvitedToBGInstanceGUID) - { - itrPlayerStatus->second.IsInvitedToBGInstanceGUID = bg2->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, bg2->GetInstanceID()); - - 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); - } + bg2->SetMaxPlayersPerTeam(1); + bg2->SetMaxPlayers(2); } else - ++itr2; + { + 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); + } + + if(!bg2) + { + sLog.outError("Battleground: couldn't create bg %u",bgTypeId); + return; + } + + // 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); + + 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) + { + 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); + } + + if (isRated) + { + std::list::iterator itr_alliance = m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.begin(); + std::list::iterator itr_horde = m_SelectionPools[NORMAL_HORDE].SelectedGroups.begin(); + (*itr_alliance)->OpponentsTeamRating = (*itr_horde)->ArenaTeamRating; + sLog.outDebug("setting oposite teamrating for team %u to %u", (*itr_alliance)->ArenaTeamId, (*itr_alliance)->OpponentsTeamRating); + (*itr_horde)->OpponentsTeamRating = (*itr_alliance)->ArenaTeamRating; + sLog.outDebug("setting oposite teamrating for team %u to %u", (*itr_horde)->ArenaTeamId, (*itr_horde)->OpponentsTeamRating); + } + + // start the battleground bg2->StartBattleGround(); } + + // there weren't enough players for a "normal" match + // if arena, enable horde versus horde or alliance versus alliance teams here + + else if(bg_template->isArena()) + { + bool bOneSideHordeTeam1 = false, bOneSideHordeTeam2 = false; + bool bOneSideAllyTeam1 = false, bOneSideAllyTeam2 = false; + bOneSideHordeTeam1 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_HORDE_TEAM1,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); + if(bOneSideHordeTeam1) + { + // one team has been selected, find out if other can be selected too + std::list::iterator itr; + // temporarily change the team side to enable building the next pool excluding the already selected groups + for(itr = m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.end(); ++itr) + (*itr)->Team=ALLIANCE; + + bOneSideHordeTeam2 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_HORDE_TEAM2,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime, (*(m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.begin()))->ArenaTeamId); + + // change back the team to horde + for(itr = m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.end(); ++itr) + (*itr)->Team=HORDE; + + if(!bOneSideHordeTeam2) + bOneSideHordeTeam1 = false; + } + if(!bOneSideHordeTeam1) + { + // check for one sided ally + bOneSideAllyTeam1 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_ALLIANCE_TEAM1,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); + if(bOneSideAllyTeam1) + { + // one team has been selected, find out if other can be selected too + std::list::iterator itr; + // temporarily change the team side to enable building the next pool excluding the already selected groups + for(itr = m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.end(); ++itr) + (*itr)->Team=HORDE; + + bOneSideAllyTeam2 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_ALLIANCE_TEAM2,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime,(*(m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.begin()))->ArenaTeamId); + + // 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; + } + + if(!bOneSideAllyTeam2) + bOneSideAllyTeam1 = false; + } + // 1-sided BuildSelectionPool() will work, because the MinPlayersPerTeam == MaxPlayersPerTeam in every arena!!!! + if( (bOneSideHordeTeam1 && bOneSideHordeTeam2) || + (bOneSideAllyTeam1 && bOneSideAllyTeam2) ) + { + // which side has enough players? + uint32 side = 0; + SelectionPoolBuildMode mode1, mode2; + // find out what pools are we using + if(bOneSideAllyTeam1 && bOneSideAllyTeam2) + { + side = ALLIANCE; + mode1 = ONESIDE_ALLIANCE_TEAM1; + mode2 = ONESIDE_ALLIANCE_TEAM2; + } + else + { + side = HORDE; + mode1 = ONESIDE_HORDE_TEAM1; + mode2 = ONESIDE_HORDE_TEAM2; + } + + // create random arena + uint8 arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL}; + uint32 arena_num = urand(0,2); + BattleGround* bg2 = NULL; + if( !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[arena_num%3])) && + !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+1)%3])) && + !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+2)%3])) ) + { + sLog.outError("Could not create arena."); + return; + } + + sLog.outDebug("Battleground: One-faction arena created."); + // init stats + if(sBattleGroundMgr.isArenaTesting()) + { + bg2->SetMaxPlayersPerTeam(1); + bg2->SetMaxPlayers(2); + } + else + { + 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; + } + } + + bg2->SetRated(isRated); + + // assigned team of the other group + uint32 other_side; + if(side == ALLIANCE) + other_side = HORDE; + else + other_side = ALLIANCE; + + // start the joining of the bg + bg2->SetStatus(STATUS_WAIT_JOIN); + bg2->SetQueueType(queue_id); + // initialize arena / rating info + bg2->SetArenaType(arenatype); + + std::list::iterator itr; + + // invite players from the first group as horde players (actually green team) + for(itr = m_SelectionPools[mode1].SelectedGroups.begin(); itr != m_SelectionPools[mode1].SelectedGroups.end(); ++itr) + { + InviteGroupToBG((*itr),bg2,HORDE); + } + + // 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); + } + + if (isRated) + { + std::list::iterator itr_alliance = m_SelectionPools[mode1].SelectedGroups.begin(); + std::list::iterator itr_horde = m_SelectionPools[mode2].SelectedGroups.begin(); + (*itr_alliance)->OpponentsTeamRating = (*itr_horde)->ArenaTeamRating; + (*itr_horde)->OpponentsTeamRating = (*itr_alliance)->ArenaTeamRating; + } + + bg2->StartBattleGround(); + } + } } /*********************************************************/ @@ -361,14 +931,19 @@ bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) uint32 queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID()); if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue { - // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems - BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()]; - BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid); - if (qItr != qpMap.end() && qItr->second.IsInvitedToBGInstanceGUID == m_BgInstanceGUID) + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); + if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue { - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME/2, 0); - plr->GetSession()->SendPacket(&data); + // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems + BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()]; + BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid); + if (qItr != qpMap.end() && qItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID) + { + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, qItr->second.GroupInfo->Team, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME/2, 0); + plr->GetSession()->SendPacket(&data); + } } } return true; //event will be deleted @@ -387,32 +962,40 @@ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) // player logged off (we should do nothing, he is correctly removed from queue in another procedure) return true; - // Player can be in another BG queue and must be removed in normal way in any case - //if (plr->InBattleGround()) - // // player is already in battleground ... do nothing (battleground queue status is deleted when player is teleported to BG) - // return true; - BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID); if (!bg) return true; - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID()); - if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue (base at player data + sLog.outDebug("Battleground: removing player %u from bg queue for instance %u because of not pressing enter battle in time.",plr->GetGUIDLow(),m_BgInstanceGUID); + + uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); + if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue { // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems - BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()]; - BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid); - if (qItr!=qpMap.end() && qItr->second.IsInvitedToBGInstanceGUID == m_BgInstanceGUID) + BattleGroundQueue::QueuedPlayersMap::iterator qMapItr = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()].find(m_PlayerGuid); + if (qMapItr != sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()].end() && qMapItr->second.GroupInfo && qMapItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID) { - plr->RemoveBattleGroundQueueId(bg->GetTypeID()); - sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].RemovePlayer(m_PlayerGuid, true); - sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].Update(bg->GetTypeID(), bg->GetQueueType()); - + if (qMapItr->second.GroupInfo->IsRated) + { + ArenaTeam * at = objmgr.GetArenaTeamById(qMapItr->second.GroupInfo->ArenaTeamId); + if (at) + { + sLog.outDebug("UPDATING memberLost's personal arena rating for %u by opponents rating: %u", GUID_LOPART(plr->GetGUID()), qMapItr->second.GroupInfo->OpponentsTeamRating); + at->MemberLost(plr, qMapItr->second.GroupInfo->OpponentsTeamRating); + at->SaveToDB(); + } + } + 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); } } + else + sLog.outDebug("Battleground: Player was already removed from queue"); //event will be deleted return true; @@ -431,22 +1014,80 @@ void BGQueueRemoveEvent::Abort(uint64 /*e_time*/) BattleGroundMgr::BattleGroundMgr() { m_BattleGrounds.clear(); + m_AutoDistributePoints = (bool)sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS); + m_MaxRatingDifference = sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE); + m_RatingDiscardTimer = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); + m_PrematureFinishTimer = sWorld.getConfig(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER); + m_NextRatingDiscardUpdate = m_RatingDiscardTimer; + m_AutoDistributionTimeChecker = 0; + m_ArenaTesting = false; } BattleGroundMgr::~BattleGroundMgr() { - for(std::map::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); ++itr) - delete itr->second; + BattleGroundSet::iterator itr, next; + for(itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); itr = next) + { + next = itr; + ++next; + BattleGround * bg = itr->second; + m_BattleGrounds.erase(itr); + delete bg; + } m_BattleGrounds.clear(); } +// used to update running battlegrounds, and delete finished ones void BattleGroundMgr::Update(time_t diff) { - for(BattleGroundSet::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); ++itr) + BattleGroundSet::iterator itr, next; + for(itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); itr = next) + { + next = itr; + ++next; itr->second->Update(diff); + // use the SetDeleteThis variable + // direct deletion caused crashes + if(itr->second->m_SetDeleteThis) + { + BattleGround * bg = itr->second; + m_BattleGrounds.erase(itr); + delete bg; + } + } + // if rating difference counts, maybe force-update queues + if(m_MaxRatingDifference) + { + // it's time to force update + if(m_NextRatingDiscardUpdate < diff) + { + // forced update for level 70 rated arenas + m_BattleGroundQueues[BATTLEGROUND_QUEUE_2v2].Update(BATTLEGROUND_AA,6,ARENA_TYPE_2v2,true,0); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_3v3].Update(BATTLEGROUND_AA,6,ARENA_TYPE_3v3,true,0); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_5v5].Update(BATTLEGROUND_AA,6,ARENA_TYPE_5v5,true,0); + m_NextRatingDiscardUpdate = m_RatingDiscardTimer; + } + else + m_NextRatingDiscardUpdate -= diff; + } + if(m_AutoDistributePoints) + { + if(m_AutoDistributionTimeChecker < diff) + { + if(sWorld.GetGameTime() > m_NextAutoDistributionTime) + { + DistributeArenaPoints(); + m_NextAutoDistributionTime = sWorld.GetGameTime() + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY * sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS); + CharacterDatabase.PExecute("UPDATE saved_variables SET NextArenaPointDistributionTime = '"I64FMTD"'", m_NextAutoDistributionTime); + } + m_AutoDistributionTimeChecker = 600000; // check 10 minutes + } + else + m_AutoDistributionTimeChecker -= diff; + } } -void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2) +void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint32 arenatype, uint8 israted) { // we can be in 3 queues in same time... if(StatusID == 0) @@ -460,10 +1101,55 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro data->Initialize(SMSG_BATTLEFIELD_STATUS, (4+1+1+4+2+4+1+4+4+4)); *data << uint32(QueueSlot); // queue id (0...2) - player can be in 3 queues in time // uint64 in client - *data << uint64( uint64(bg->GetArenaType()) | (uint64(0x0D) << 8) | (uint64(bg->GetTypeID()) << 16) | (uint64(0x1F90) << 48) ); + *data << uint64( uint64(arenatype ? arenatype : bg->GetArenaType()) | (uint64(0x0D) << 8) | (uint64(bg->GetTypeID()) << 16) | (uint64(0x1F90) << 48) ); *data << uint32(0); // unknown // alliance/horde for BG and skirmish/rated for Arenas - *data << uint8(bg->isArena() ? (bg->isRated() ? 1 : 0) : bg->GetTeamIndexByTeamId(team)); + *data << uint8(bg->isArena() ? ( israted ? israted : bg->isRated() ) : bg->GetTeamIndexByTeamId(team)); +/* *data << uint8(arenatype ? arenatype : bg->GetArenaType()); // team type (0=BG, 2=2x2, 3=3x3, 5=5x5), for arenas // NOT PROPER VALUE IF ARENA ISN'T RUNNING YET!!!! + switch(bg->GetTypeID()) // value depends on bg id + { + case BATTLEGROUND_AV: + *data << uint8(1); + break; + case BATTLEGROUND_WS: + *data << uint8(2); + break; + case BATTLEGROUND_AB: + *data << uint8(3); + break; + case BATTLEGROUND_NA: + *data << uint8(4); + break; + case BATTLEGROUND_BE: + *data << uint8(5); + break; + case BATTLEGROUND_AA: + *data << uint8(6); + break; + case BATTLEGROUND_EY: + *data << uint8(7); + break; + case BATTLEGROUND_RL: + *data << uint8(8); + break; + default: // unknown + *data << uint8(0); + break; + } + + if(bg->isArena() && (StatusID == STATUS_WAIT_QUEUE)) + *data << uint32(BATTLEGROUND_AA); // all arenas I don't think so. + else + *data << uint32(bg->GetTypeID()); // BG id from DBC + + *data << uint16(0x1F90); // unk value 8080 + *data << uint32(bg->GetInstanceID()); // instance id + + if(bg->isBattleGround()) + *data << uint8(bg->GetTeamIndexByTeamId(team)); // team + else + *data << uint8(israted?israted:bg->isRated()); // is rated battle +*/ *data << uint32(StatusID); // status switch(StatusID) { @@ -493,13 +1179,24 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) // last check on 2.4.1 data->Initialize(MSG_PVP_LOG_DATA, (1+1+4+40*bg->GetPlayerScoresSize())); *data << uint8(type); // seems to be type (battleground=0/arena=1) + if(type) // arena { - for(uint8 i = 0; i < 2; i++) + // it seems this must be according to BG_WINNER_A/H and _NOT_ BG_TEAM_A/H + for(int i = 1; i >= 0; --i) { - *data << uint32(3000+1+i); // rating change: showed value - 3000 - *data << uint32(0); // 2.4.0, has some to do with rating change... - *data << uint8(0); // some unknown string + *data << uint32(3000-bg->m_ArenaTeamRatingChanges[i]); // rating change: showed value - 3000 + *data << uint32(3999); // huge thanks for TOM_RUS for this! + sLog.outDebug("rating change: %d", bg->m_ArenaTeamRatingChanges[i]); + } + for(int i = 1; i >= 0; --i) + { + uint32 at_id = bg->m_ArenaTeamIds[i]; + ArenaTeam * at = objmgr.GetArenaTeamById(at_id); + if(at) + *data << at->GetName(); + else//*/ + *data << (uint8)0; } } @@ -519,32 +1216,35 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) { *data << (uint64)itr->first; *data << (int32)itr->second->KillingBlows; - if(type) + Player *plr = objmgr.GetPlayer(itr->first); + uint32 team = bg->GetPlayerTeam(itr->first); + if(!team && plr) team = plr->GetTeam(); + if(type == 0) + { + *data << (int32)itr->second->HonorableKills; + *data << (int32)itr->second->Deaths; + *data << (int32)(itr->second->BonusHonor); + } + else { - // this value is team (green/gold)? // that part probably wrong - Player *plr = objmgr.GetPlayer(itr->first); if(plr) { - if(plr->GetTeam() == HORDE) + if(team == HORDE) *data << uint8(0); - else if(plr->GetTeam() == ALLIANCE) + else if(team == ALLIANCE) + { *data << uint8(1); + } else *data << uint8(0); } else *data << uint8(0); } - else - { - *data << (int32)itr->second->HonorableKills; - *data << (int32)itr->second->Deaths; - *data << (int32)itr->second->BonusHonor; // bonus honor - } - *data << (int32)itr->second->DamageDone; // damage done - *data << (int32)itr->second->HealingDone; // healing done - switch(bg->GetTypeID()) // battleground specific things + *data << (int32)itr->second->DamageDone; // damage done + *data << (int32)itr->second->HealingDone; // healing done + switch(bg->GetTypeID()) // battleground specific things { case BATTLEGROUND_AV: *data << (uint32)0x00000005; // count of next fields @@ -622,23 +1322,120 @@ void BattleGroundMgr::BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Pla *data << uint64(plr->GetGUID()); } -void BattleGroundMgr::InvitePlayer(Player* plr, uint32 bgInstanceGUID) +void BattleGroundMgr::InvitePlayer(Player* plr, uint32 bgInstanceGUID, uint32 team) { // set invited player counters: BattleGround* bg = GetBattleGround(bgInstanceGUID); if(!bg) return; + bg->IncreaseInvitedCount(team); + + plr->SetInviteForBattleGroundQueueType(BGQueueTypeId(bg->GetTypeID(),bg->GetArenaType()), bgInstanceGUID); + + // set the arena teams for rated matches + if(bg->isArena() && bg->isRated()) + { + switch(bg->GetArenaType()) + { + case ARENA_TYPE_2v2: + bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(0)); + break; + case ARENA_TYPE_3v3: + bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(1)); + break; + case ARENA_TYPE_5v5: + bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(2)); + break; + default: + break; + } + } - bg->IncreaseInvitedCount(plr->GetTeam()); - plr->SetInviteForBattleGroundType(bg->GetTypeID()); // create invite events: //add events to player's counters ---- this is not good way - there should be something like global event processor, where we should add those events BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), bgInstanceGUID); plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME/2)); - BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), bgInstanceGUID, plr->GetTeam()); + BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), bgInstanceGUID, team); plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME)); } +BattleGround * BattleGroundMgr::GetBattleGroundTemplate(uint32 bgTypeId) +{ + return BGFreeSlotQueue[bgTypeId].empty() ? NULL : BGFreeSlotQueue[bgTypeId].back(); +} + +// create a new battleground that will really be used to play +BattleGround * BattleGroundMgr::CreateNewBattleGround(uint32 bgTypeId) +{ + BattleGround *bg = NULL; + + // get the template BG + BattleGround *bg_template = GetBattleGroundTemplate(bgTypeId); + + if(!bg_template) + { + sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId); + return 0; + } + + // create a copy of the BG template + switch(bgTypeId) + { + case BATTLEGROUND_AV: + bg = new BattleGroundAV(*(BattleGroundAV*)bg_template); + break; + case BATTLEGROUND_WS: + bg = new BattleGroundWS(*(BattleGroundWS*)bg_template); + break; + case BATTLEGROUND_AB: + bg = new BattleGroundAB(*(BattleGroundAB*)bg_template); + break; + case BATTLEGROUND_NA: + bg = new BattleGroundNA(*(BattleGroundNA*)bg_template); + break; + case BATTLEGROUND_BE: + bg = new BattleGroundBE(*(BattleGroundBE*)bg_template); + break; + case BATTLEGROUND_AA: + bg = new BattleGroundAA(*(BattleGroundAA*)bg_template); + break; + case BATTLEGROUND_EY: + bg = new BattleGroundEY(*(BattleGroundEY*)bg_template); + break; + case BATTLEGROUND_RL: + bg = new BattleGroundRL(*(BattleGroundRL*)bg_template); + break; + default: + //bg = new BattleGround; + return 0; + break; // placeholder for non implemented BG + } + + // generate a new instance id + bg->SetInstanceID(MapManager::Instance().GenerateInstanceId()); // set instance id + + // reset the new bg (set status to status_wait_queue from status_none) + bg->Reset(); + + /* will be setup in BG::Update() when the first player is ported in + if(!(bg->SetupBattleGround())) + { + sLog.outError("BattleGround: CreateNewBattleGround: SetupBattleGround failed for bg %u", bgTypeId); + delete bg; + return 0; + } + */ + + // add BG to free slot queue + bg->AddToBGFreeSlotQueue(); + + // add bg to update list + AddBattleGround(bg->GetInstanceID(), bg); + + return bg; +} + +// used to create the BG templates uint32 BattleGroundMgr::CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO) { // Create the BG @@ -658,12 +1455,8 @@ uint32 BattleGroundMgr::CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPer } bg->SetMapId(MapID); + bg->Reset(); - if(!bg->SetupBattleGround()) - { - delete bg; - return 0; - } BattlemasterListEntry const *bl = sBattlemasterListStore.LookupEntry(bgTypeId); //in previous method is checked if exists entry in sBattlemasterListStore, so no check needed @@ -673,7 +1466,7 @@ uint32 BattleGroundMgr::CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPer } bg->SetTypeID(bgTypeId); - bg->SetInstanceID(bgTypeId); // temporary + bg->SetInstanceID(0); // template bg, instance id is 0 bg->SetMinPlayersPerTeam(MinPlayersPerTeam); bg->SetMaxPlayersPerTeam(MaxPlayersPerTeam); bg->SetMinPlayers(MinPlayersPerTeam*2); @@ -682,12 +1475,14 @@ uint32 BattleGroundMgr::CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPer bg->SetTeamStartLoc(ALLIANCE, Team1StartLocX, Team1StartLocY, Team1StartLocZ, Team1StartLocO); bg->SetTeamStartLoc(HORDE, Team2StartLocX, Team2StartLocY, Team2StartLocZ, Team2StartLocO); bg->SetLevelRange(LevelMin, LevelMax); - //add BaggleGround instance to FreeSlotQueue + + //add BattleGround instance to FreeSlotQueue (.back() will return the template!) bg->AddToBGFreeSlotQueue(); - AddBattleGround(bg->GetInstanceID(), bg); - //sLog.outDetail("BattleGroundMgr: Created new battleground: %u %s (Map %u, %u players per team, Levels %u-%u)", bg_TypeID, bg->m_Name, bg->m_MapId, bg->m_MaxPlayersPerTeam, bg->m_LevelMin, bg->m_LevelMax); - return bg->GetInstanceID(); + // do NOT add to update list, since this is a template battleground! + + // return some not-null value, bgTypeId is good enough for me + return bgTypeId; } void BattleGroundMgr::CreateInitialBattleGrounds() @@ -807,6 +1602,77 @@ void BattleGroundMgr::CreateInitialBattleGrounds() sLog.outString( ">> Loaded %u battlegrounds", count ); } +void BattleGroundMgr::InitAutomaticArenaPointDistribution() +{ + if(m_AutoDistributePoints) + { + sLog.outDebug("Initializing Automatic Arena Point Distribution"); + QueryResult * result = CharacterDatabase.Query("SELECT NextArenaPointDistributionTime FROM saved_variables"); + if(!result) + { + sLog.outDebug("Battleground: Next arena point distribution time not found in SavedVariables, reseting it now."); + m_NextAutoDistributionTime = sWorld.GetGameTime() + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY * sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS); + CharacterDatabase.PExecute("INSERT INTO saved_variables (NextArenaPointDistributionTime) VALUES ('"I64FMTD"')", m_NextAutoDistributionTime); + } + else + { + m_NextAutoDistributionTime = (*result)[0].GetUInt64(); + delete result; + } + sLog.outDebug("Automatic Arena Point Distribution initialized."); + } +} + +void BattleGroundMgr::DistributeArenaPoints() +{ + // used to distribute arena points based on last week's stats + sWorld.SendGlobalText("Flushing Arena points based on team ratings, this may take a few minutes. Please stand by...", NULL); + + sWorld.SendGlobalText("Distributing arena points to players...", NULL); + + //temporary structure for storing maximum points to add values for all players + std::map PlayerPoints; + + //at first update all points for all team members + for(ObjectMgr::ArenaTeamMap::iterator team_itr = objmgr.GetArenaTeamMapBegin(); team_itr != objmgr.GetArenaTeamMapEnd(); ++team_itr) + { + if(ArenaTeam * at = team_itr->second) + { + at->UpdateArenaPointsHelper(PlayerPoints); + } + } + + //cycle that gives points to all players + for (std::map::iterator plr_itr = PlayerPoints.begin(); plr_itr != PlayerPoints.end(); ++plr_itr) + { + //update to database + CharacterDatabase.PExecute("UPDATE characters SET arena_pending_points = '%u' WHERE `guid` = '%u'", plr_itr->second, plr_itr->first); + //add points if player is online + Player* pl = objmgr.GetPlayer(plr_itr->first); + if (pl) + pl->ModifyArenaPoints(plr_itr->second); + } + + PlayerPoints.clear(); + + sWorld.SendGlobalText("Finished setting arena points for online players.", NULL); + + sWorld.SendGlobalText("Modifying played count, arena points etc. for loaded arena teams, sending updated stats to online players...", NULL); + for(ObjectMgr::ArenaTeamMap::iterator titr = objmgr.GetArenaTeamMapBegin(); titr != objmgr.GetArenaTeamMapEnd(); ++titr) + { + if(ArenaTeam * at = titr->second) + { + at->FinishWeek(); // set played this week etc values to 0 in memory, too + at->SaveToDB(); // save changes + at->NotifyStatsChanged(); // notify the players of the changes + } + } + + sWorld.SendGlobalText("Modification done.", NULL); + + sWorld.SendGlobalText("Done flushing Arena points.", NULL); +} + void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, uint64 guid, Player* plr, uint32 bgTypeId) { uint32 PlayerLevel = 10; @@ -842,18 +1708,25 @@ void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, uint64 guid } } -void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 bgTypeId) +void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId) { - 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); + uint32 team = pl->GetBGTeam(); + if(team==0) + team = pl->GetTeam(); + bg->GetTeamStartLoc(team, x, y, z, O); sLog.outDetail("BATTLEGROUND: Sending %s to map %u, X %f, Y %f, Z %f, O %f", pl->GetName(), mapid, x, y, z, O); pl->TeleportTo(mapid, x, y, z, O); } + 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) @@ -865,3 +1738,100 @@ void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround * data << guid << time_; pl->GetSession()->SendPacket(&data); } + +void BattleGroundMgr::RemoveBattleGround(uint32 instanceID) +{ + BattleGroundSet::iterator itr = m_BattleGrounds.find(instanceID); + if(itr!=m_BattleGrounds.end()) + m_BattleGrounds.erase(itr); +} + +bool BattleGroundMgr::IsArenaType(uint32 bgTypeId) const +{ + return ( bgTypeId == BATTLEGROUND_AA || + bgTypeId == BATTLEGROUND_BE || + bgTypeId == BATTLEGROUND_NA || + bgTypeId == BATTLEGROUND_RL ); +} + +bool BattleGroundMgr::IsBattleGroundType(uint32 bgTypeId) const +{ + return !IsArenaType(bgTypeId); +} + +uint32 BattleGroundMgr::BGQueueTypeId(uint32 bgTypeId, uint8 arenaType) const +{ + switch(bgTypeId) + { + case BATTLEGROUND_WS: + return BATTLEGROUND_QUEUE_WS; + case BATTLEGROUND_AB: + return BATTLEGROUND_QUEUE_AB; + case BATTLEGROUND_AV: + return BATTLEGROUND_QUEUE_AV; + case BATTLEGROUND_EY: + return BATTLEGROUND_QUEUE_EY; + case BATTLEGROUND_AA: + case BATTLEGROUND_NA: + case BATTLEGROUND_RL: + case BATTLEGROUND_BE: + switch(arenaType) + { + case ARENA_TYPE_2v2: + return BATTLEGROUND_QUEUE_2v2; + case ARENA_TYPE_3v3: + return BATTLEGROUND_QUEUE_3v3; + case ARENA_TYPE_5v5: + return BATTLEGROUND_QUEUE_5v5; + default: + return 0; + } + default: + return 0; + } +} + +uint32 BattleGroundMgr::BGTemplateId(uint32 bgQueueTypeId) const +{ + switch(bgQueueTypeId) + { + case BATTLEGROUND_QUEUE_WS: + return BATTLEGROUND_WS; + case BATTLEGROUND_QUEUE_AB: + return BATTLEGROUND_AB; + case BATTLEGROUND_QUEUE_AV: + return BATTLEGROUND_AV; + case BATTLEGROUND_QUEUE_EY: + return BATTLEGROUND_EY; + case BATTLEGROUND_QUEUE_2v2: + case BATTLEGROUND_QUEUE_3v3: + case BATTLEGROUND_QUEUE_5v5: + return BATTLEGROUND_AA; + default: + return 0; + } +} + +uint8 BattleGroundMgr::BGArenaType(uint32 bgQueueTypeId) const +{ + switch(bgQueueTypeId) + { + case BATTLEGROUND_QUEUE_2v2: + return ARENA_TYPE_2v2; + case BATTLEGROUND_QUEUE_3v3: + return ARENA_TYPE_3v3; + case BATTLEGROUND_QUEUE_5v5: + return ARENA_TYPE_5v5; + default: + return 0; + } +} + +void BattleGroundMgr::ToggleArenaTesting() +{ + m_ArenaTesting = !m_ArenaTesting; + if(m_ArenaTesting) + sWorld.SendGlobalText("Arenas are set to 1v1 for debugging. So, don't join as group.", NULL); + else + sWorld.SendGlobalText("Arenas are set to normal playercount.", NULL); +} diff --git a/src/game/BattleGroundMgr.h b/src/game/BattleGroundMgr.h index 193e5f430..bb2a4faaf 100644 --- a/src/game/BattleGroundMgr.h +++ b/src/game/BattleGroundMgr.h @@ -34,51 +34,99 @@ typedef std::deque BGFreeSlotQueueType; #define MAX_BATTLEGROUND_TYPES 9 // each BG type will be in array -struct PlayerQueueInfo +#define MAX_BATTLEGROUND_QUEUE_TYPES 8 + +#define BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY 86400 // seconds in a day + +struct GroupQueueInfo; // type predefinition +struct PlayerQueueInfo // stores information for players in queue { uint32 InviteTime; // first invite time uint32 LastInviteTime; // last invite time - uint32 IsInvitedToBGInstanceGUID; // was invited to certain BG uint32 LastOnlineTime; // for tracking and removing offline players from queue after 5 minutes + GroupQueueInfo * GroupInfo; // pointer to the associated groupqueueinfo +}; + +struct GroupQueueInfo // stores information about the group in queue (also used when joined as solo!) +{ + std::map Players; // player queue info map uint32 Team; // Player team (ALLIANCE/HORDE) - bool IsRated; - bool AsGroup; // uint32 GroupId; - uint8 ArenaType; -}; - -struct PlayersCount -{ - uint32 Alliance; - uint32 Horde; -}; - -template class bgqueue: public std::map<_Kty, _Ty> -{ - public: - uint32 Alliance; - uint32 Horde; - //bool Ready; // not used now - //uint32 AverageTime; //not already implemented (it should be average time in queue for last 10 players) + uint32 BgTypeId; // battleground type id + bool IsRated; // rated + uint8 ArenaType; // 2v2, 3v3, 5v5 or 0 when BG + uint32 ArenaTeamId; // team id if rated match + uint32 JoinTime; // time when group was added + uint32 IsInvitedToBGInstanceGUID; // was invited to certain BG + uint32 ArenaTeamRating; // if rated match, inited to the rating of the team + uint32 OpponentsTeamRating; // for rated arena matches }; +class BattleGround; class BattleGroundQueue { public: BattleGroundQueue(); ~BattleGroundQueue(); -/* - uint32 GetType(); - void SetType(uint32 type);*/ - void Update(uint32 bgTypeId, uint32 queue_id); + void Update(uint32 bgTypeId, uint32 queue_id, uint8 arenatype = 0, bool isRated = false, uint32 minRating = 0); - void AddPlayer(Player *plr, uint32 bgTypeId); + GroupQueueInfo * AddGroup(Player * leader, uint32 BgTypeId, uint8 ArenaType, bool isRated, uint32 ArenaRating, uint32 ArenaTeamId = 0); + void AddPlayer(Player *plr, GroupQueueInfo *ginfo); void RemovePlayer(uint64 guid, bool decreaseInvitedCount); + void DecreaseGroupLength(uint32 queueId, uint32 AsGroup); + void BGEndedRemoveInvites(BattleGround * bg); - typedef bgqueue QueuedPlayersMap; + typedef std::map QueuedPlayersMap; QueuedPlayersMap m_QueuedPlayers[MAX_BATTLEGROUND_QUEUES]; - typedef std::list PlayerGuidsSortedByTimeQueue; - PlayerGuidsSortedByTimeQueue m_PlayersSortedByWaitTime[MAX_BATTLEGROUND_QUEUES]; + + typedef std::list QueuedGroupsList; + QueuedGroupsList m_QueuedGroups[MAX_BATTLEGROUND_QUEUES]; + + // class to hold pointers to the groups eligible for a specific selection pool building mode + class EligibleGroups : public std::list + { + public: + void Init(QueuedGroupsList * source, uint32 BgTypeId, uint32 side, uint32 MaxPlayers, uint8 ArenaType = 0, bool IsRated = false, uint32 MinRating = 0, uint32 MaxRating = 0, uint32 DisregardTime = 0, uint32 excludeTeam = 0); + void RemoveGroup(GroupQueueInfo * ginfo); + }; + + EligibleGroups m_EligibleGroups; + + // class to select and invite groups to bg + class SelectionPool + { + public: + void Init(); + void AddGroup(GroupQueueInfo * group); + GroupQueueInfo * GetMaximalGroup(); + void RemoveGroup(GroupQueueInfo * group); + uint32 GetPlayerCount() const {return PlayerCount;} + public: + std::list SelectedGroups; + private: + uint32 PlayerCount; + GroupQueueInfo * MaxGroup; + }; + + enum SelectionPoolBuildMode + { + NORMAL_ALLIANCE, + NORMAL_HORDE, + ONESIDE_ALLIANCE_TEAM1, + ONESIDE_ALLIANCE_TEAM2, + ONESIDE_HORDE_TEAM1, + ONESIDE_HORDE_TEAM2, + + NUM_SELECTION_POOL_TYPES + }; + + SelectionPool m_SelectionPools[NUM_SELECTION_POOL_TYPES]; + + bool BuildSelectionPool(uint32 bgTypeId, uint32 queue_id, uint32 MinPlayers, uint32 MaxPlayers, SelectionPoolBuildMode mode, uint8 ArenaType = 0, bool isRated = false, uint32 MinRating = 0, uint32 MaxRating = 0, uint32 DisregardTime = 0, uint32 excludeTeam = 0); + + private: + + bool InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side); }; /* @@ -96,7 +144,6 @@ class BGQueueInviteEvent : public BasicEvent private: uint64 m_PlayerGuid; uint32 m_BgInstanceGUID; - }; /* @@ -116,7 +163,6 @@ class BGQueueRemoveEvent : public BasicEvent uint32 m_PlayersTeam; }; - class BattleGroundMgr { public: @@ -132,18 +178,18 @@ class BattleGroundMgr void BuildGroupJoinedBattlegroundPacket(WorldPacket *data, uint32 bgTypeId); void BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value); void BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg); - void BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2); + void BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint32 arenatype = 0, uint8 israted = 0); void BuildPlaySoundPacket(WorldPacket *data, uint32 soundid); /* Player invitation */ // called from Queue update, or from Addplayer to queue - void InvitePlayer(Player* plr, uint32 bgInstanceGUID); + void InvitePlayer(Player* plr, uint32 bgInstanceGUID, uint32 team); /* Battlegrounds */ BattleGroundSet::iterator GetBattleGroundsBegin() { return m_BattleGrounds.begin(); }; BattleGroundSet::iterator GetBattleGroundsEnd() { return m_BattleGrounds.end(); }; - BattleGround* GetBattleGround(uint8 ID) + BattleGround* GetBattleGround(uint32 ID) { BattleGroundSet::iterator i = m_BattleGrounds.find(ID); if(i != m_BattleGrounds.end()) @@ -152,9 +198,13 @@ class BattleGroundMgr 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(); @@ -162,16 +212,39 @@ class BattleGroundMgr /* Battleground queues */ //these queues are instantiated when creating BattlegroundMrg - BattleGroundQueue m_BattleGroundQueues[MAX_BATTLEGROUND_TYPES]; // public, because we need to access them in BG handler code + BattleGroundQueue m_BattleGroundQueues[MAX_BATTLEGROUND_QUEUE_TYPES]; // public, because we need to access them in BG handler code BGFreeSlotQueueType BGFreeSlotQueue[MAX_BATTLEGROUND_TYPES]; void SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, uint64 guid); + bool IsArenaType(uint32 bgTypeId) const; + bool IsBattleGroundType(uint32 bgTypeId) const; + uint32 BGQueueTypeId(uint32 bgTypeId, uint8 arenaType) const; + uint32 BGTemplateId(uint32 bgQueueTypeId) const; + uint8 BGArenaType(uint32 bgQueueTypeId) const; + + uint32 GetMaxRatingDifference() const {return m_MaxRatingDifference;} + uint32 GetRatingDiscardTimer() const {return m_RatingDiscardTimer;} + + void InitAutomaticArenaPointDistribution(); + void DistributeArenaPoints(); + uint32 GetPrematureFinishTime() const {return m_PrematureFinishTimer;} + void ToggleArenaTesting(); + const bool isArenaTesting() const { return m_ArenaTesting; } + private: /* Battlegrounds */ BattleGroundSet m_BattleGrounds; + uint32 m_MaxRatingDifference; + uint32 m_RatingDiscardTimer; + uint32 m_NextRatingDiscardUpdate; + bool m_AutoDistributePoints; + uint64 m_NextAutoDistributionTime; + uint32 m_AutoDistributionTimeChecker; + uint32 m_PrematureFinishTimer; + bool m_ArenaTesting; }; #define sBattleGroundMgr MaNGOS::Singleton::Instance() diff --git a/src/game/BattleGroundNA.cpp b/src/game/BattleGroundNA.cpp index ac4705f5c..385de1211 100644 --- a/src/game/BattleGroundNA.cpp +++ b/src/game/BattleGroundNA.cpp @@ -47,6 +47,12 @@ void BattleGroundNA::Update(time_t diff) if (!(m_Events & 0x01)) { m_Events |= 0x01; + // setup here, only when at least one player has ported to the map + if(!SetupBattleGround()) + { + EndNow(); + return; + } for(uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_4; i++) SpawnBGObject(i, RESPAWN_IMMEDIATELY); @@ -73,6 +79,9 @@ void BattleGroundNA::Update(time_t diff) for(uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_2; i++) DoorOpen(i); + for(uint32 i = BG_NA_OBJECT_BUFF_1; i <= BG_NA_OBJECT_BUFF_2; i++) + SpawnBGObject(i, 60); + SendMessageToAll(LANG_ARENA_BEGUN); SetStatus(STATUS_IN_PROGRESS); SetStartDelayTime(0); @@ -80,6 +89,11 @@ void BattleGroundNA::Update(time_t diff) for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) if(Player *plr = objmgr.GetPlayer(itr->first)) plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); + + if(!GetPlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetPlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } } @@ -96,11 +110,23 @@ void BattleGroundNA::AddPlayer(Player *plr) BattleGroundNAScore* sc = new BattleGroundNAScore; m_PlayerScores[plr->GetGUID()] = sc; + + UpdateWorldState(0xa0f, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xa10, GetAlivePlayersCountByTeam(HORDE)); } void BattleGroundNA::RemovePlayer(Player* /*plr*/, uint64 /*guid*/) { + if(GetStatus() == STATUS_WAIT_LEAVE) + return; + UpdateWorldState(0xa0f, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xa10, GetAlivePlayersCountByTeam(HORDE)); + + if(!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } void BattleGroundNA::HandleKillPlayer(Player *player, Player *killer) @@ -114,17 +140,27 @@ void BattleGroundNA::HandleKillPlayer(Player *player, Player *killer) return; } - BattleGround::HandleKillPlayer(player, killer); + BattleGround::HandleKillPlayer(player,killer); - uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam()); + UpdateWorldState(0xa0f, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xa10, GetAlivePlayersCountByTeam(HORDE)); - ++m_TeamKills[killer_team_index]; // add kills to killer's team - - if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(player->GetTeam())) + if(!GetAlivePlayersCountByTeam(ALLIANCE)) { // all opponents killed - EndBattleGround(killer->GetTeam()); + EndBattleGround(HORDE); } + else if(!GetAlivePlayersCountByTeam(HORDE)) + { + // all opponents killed + EndBattleGround(ALLIANCE); + } +} + +bool BattleGroundNA::HandlePlayerUnderMap(Player *player) +{ + player->TeleportTo(GetMapId(),4055.504395,2919.660645,13.611241,player->GetOrientation(),false); + return true; } void BattleGroundNA::HandleAreaTrigger(Player *Source, uint32 Trigger) @@ -149,19 +185,28 @@ void BattleGroundNA::HandleAreaTrigger(Player *Source, uint32 Trigger) // HandleTriggerBuff(buff_guid,Source); } +void BattleGroundNA::FillInitialWorldStates(WorldPacket &data) +{ + data << uint32(0xa0f) << uint32(GetAlivePlayersCountByTeam(ALLIANCE)); // 7 + data << uint32(0xa10) << uint32(GetAlivePlayersCountByTeam(HORDE)); // 8 + data << uint32(0xa11) << uint32(1); // 9 +} + void BattleGroundNA::ResetBGSubclass() { - m_TeamKills[BG_TEAM_ALLIANCE] = 0; - m_TeamKills[BG_TEAM_HORDE] = 0; + } bool BattleGroundNA::SetupBattleGround() { // gates - if( !AddObject(BG_NA_OBJECT_DOOR_1, BG_NA_OBJECT_TYPE_DOOR_1, 4031.854f, 2966.833f, 12.6462f, -2.648788f, 0, 0, 0.9697962f, -0.2439165f, RESPAWN_IMMEDIATELY) - || !AddObject(BG_NA_OBJECT_DOOR_2, BG_NA_OBJECT_TYPE_DOOR_2, 4081.179f, 2874.97f, 12.39171f, 0.4928045f, 0, 0, 0.2439165f, 0.9697962f, RESPAWN_IMMEDIATELY) - || !AddObject(BG_NA_OBJECT_DOOR_3, BG_NA_OBJECT_TYPE_DOOR_3, 4023.709f, 2981.777f, 10.70117f, -2.648788f, 0, 0, 0.9697962f, -0.2439165f, RESPAWN_IMMEDIATELY) - || !AddObject(BG_NA_OBJECT_DOOR_4, BG_NA_OBJECT_TYPE_DOOR_4, 4090.064f, 2858.438f, 10.23631f, 0.4928045f, 0, 0, 0.2439165f, 0.9697962f, RESPAWN_IMMEDIATELY)) + if( !AddObject(BG_NA_OBJECT_DOOR_1, BG_NA_OBJECT_TYPE_DOOR_1, 4031.854, 2966.833, 12.6462, -2.648788, 0, 0, 0.9697962, -0.2439165, RESPAWN_IMMEDIATELY) + || !AddObject(BG_NA_OBJECT_DOOR_2, BG_NA_OBJECT_TYPE_DOOR_2, 4081.179, 2874.97, 12.39171, 0.4928045, 0, 0, 0.2439165, 0.9697962, RESPAWN_IMMEDIATELY) + || !AddObject(BG_NA_OBJECT_DOOR_3, BG_NA_OBJECT_TYPE_DOOR_3, 4023.709, 2981.777, 10.70117, -2.648788, 0, 0, 0.9697962, -0.2439165, RESPAWN_IMMEDIATELY) + || !AddObject(BG_NA_OBJECT_DOOR_4, BG_NA_OBJECT_TYPE_DOOR_4, 4090.064, 2858.438, 10.23631, 0.4928045, 0, 0, 0.2439165, 0.9697962, RESPAWN_IMMEDIATELY) + // buffs + || !AddObject(BG_NA_OBJECT_BUFF_1, BG_NA_OBJECT_TYPE_BUFF_1, 4009.189941, 2895.250000, 13.052700, -1.448624, 0, 0, 0.6626201, -0.7489557, 120) + || !AddObject(BG_NA_OBJECT_BUFF_2, BG_NA_OBJECT_TYPE_BUFF_2, 4103.330078, 2946.350098, 13.051300, -0.06981307, 0, 0, 0.03489945, -0.9993908, 120)) { sLog.outErrorDb("BatteGroundNA: Failed to spawn some object!"); return false; diff --git a/src/game/BattleGroundNA.h b/src/game/BattleGroundNA.h index 723e5b08e..a44460919 100644 --- a/src/game/BattleGroundNA.h +++ b/src/game/BattleGroundNA.h @@ -26,7 +26,9 @@ enum BattleGroundNAObjectTypes BG_NA_OBJECT_DOOR_2 = 1, BG_NA_OBJECT_DOOR_3 = 2, BG_NA_OBJECT_DOOR_4 = 3, - BG_NA_OBJECT_MAX = 4 + BG_NA_OBJECT_BUFF_1 = 4, + BG_NA_OBJECT_BUFF_2 = 5, + BG_NA_OBJECT_MAX = 6 }; enum BattleGroundNAObjects @@ -34,7 +36,9 @@ enum BattleGroundNAObjects BG_NA_OBJECT_TYPE_DOOR_1 = 183978, BG_NA_OBJECT_TYPE_DOOR_2 = 183980, BG_NA_OBJECT_TYPE_DOOR_3 = 183977, - BG_NA_OBJECT_TYPE_DOOR_4 = 183979 + BG_NA_OBJECT_TYPE_DOOR_4 = 183979, + BG_NA_OBJECT_TYPE_BUFF_1 = 184663, + BG_NA_OBJECT_TYPE_BUFF_2 = 184664 }; class BattleGroundNAScore : public BattleGroundScore @@ -61,9 +65,8 @@ class BattleGroundNA : public BattleGround void HandleAreaTrigger(Player *Source, uint32 Trigger); bool SetupBattleGround(); virtual void ResetBGSubclass(); + virtual void FillInitialWorldStates(WorldPacket &d); void HandleKillPlayer(Player* player, Player *killer); - - private: - uint32 m_TeamKills[2]; // count of kills for each team + bool HandlePlayerUnderMap(Player * plr); }; #endif diff --git a/src/game/BattleGroundRL.cpp b/src/game/BattleGroundRL.cpp index 4be135c70..bf3b5b029 100644 --- a/src/game/BattleGroundRL.cpp +++ b/src/game/BattleGroundRL.cpp @@ -47,6 +47,13 @@ void BattleGroundRL::Update(time_t diff) { m_Events |= 0x01; + // setup here, only when at least one player has ported to the map + if(!SetupBattleGround()) + { + EndNow(); + return; + } + for(uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; i++) SpawnBGObject(i, RESPAWN_IMMEDIATELY); @@ -73,6 +80,9 @@ void BattleGroundRL::Update(time_t diff) for(uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; i++) DoorOpen(i); + for(uint32 i = BG_RL_OBJECT_BUFF_1; i <= BG_RL_OBJECT_BUFF_2; i++) + SpawnBGObject(i, 60); + SendMessageToAll(LANG_ARENA_BEGUN); SetStatus(STATUS_IN_PROGRESS); SetStartDelayTime(0); @@ -80,6 +90,11 @@ void BattleGroundRL::Update(time_t diff) for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) if(Player *plr = objmgr.GetPlayer(itr->first)) plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); + + if(!GetPlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetPlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } } @@ -96,11 +111,23 @@ void BattleGroundRL::AddPlayer(Player *plr) BattleGroundRLScore* sc = new BattleGroundRLScore; m_PlayerScores[plr->GetGUID()] = sc; + + UpdateWorldState(0xbb8, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xbb9, GetAlivePlayersCountByTeam(HORDE)); } void BattleGroundRL::RemovePlayer(Player* /*plr*/, uint64 /*guid*/) { + if(GetStatus() == STATUS_WAIT_LEAVE) + return; + UpdateWorldState(0xbb8, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xbb9, GetAlivePlayersCountByTeam(HORDE)); + + if(!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if(GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); } void BattleGroundRL::HandleKillPlayer(Player *player, Player *killer) @@ -114,17 +141,27 @@ void BattleGroundRL::HandleKillPlayer(Player *player, Player *killer) return; } - BattleGround::HandleKillPlayer(player, killer); + BattleGround::HandleKillPlayer(player,killer); - uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam()); + UpdateWorldState(0xbb8, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(0xbb9, GetAlivePlayersCountByTeam(HORDE)); - ++m_TeamKills[killer_team_index]; // add kills to killer's team - - if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(player->GetTeam())) + if(!GetAlivePlayersCountByTeam(ALLIANCE)) { // all opponents killed - EndBattleGround(killer->GetTeam()); + EndBattleGround(HORDE); } + else if(!GetAlivePlayersCountByTeam(HORDE)) + { + // all opponents killed + EndBattleGround(ALLIANCE); + } +} + +bool BattleGroundRL::HandlePlayerUnderMap(Player *player) +{ + player->TeleportTo(GetMapId(),1285.810547,1667.896851,39.957642,player->GetOrientation(),false); + return true; } void BattleGroundRL::HandleAreaTrigger(Player *Source, uint32 Trigger) @@ -150,17 +187,26 @@ void BattleGroundRL::HandleAreaTrigger(Player *Source, uint32 Trigger) // HandleTriggerBuff(buff_guid,Source); } +void BattleGroundRL::FillInitialWorldStates(WorldPacket &data) +{ + data << uint32(0xbb8) << uint32(GetAlivePlayersCountByTeam(ALLIANCE)); // 7 + data << uint32(0xbb9) << uint32(GetAlivePlayersCountByTeam(HORDE)); // 8 + data << uint32(0xbba) << uint32(1); // 9 +} + void BattleGroundRL::ResetBGSubclass() { - m_TeamKills[BG_TEAM_ALLIANCE] = 0; - m_TeamKills[BG_TEAM_HORDE] = 0; + } bool BattleGroundRL::SetupBattleGround() { // gates - if( !AddObject(BG_RL_OBJECT_DOOR_1, BG_RL_OBJECT_TYPE_DOOR_1, 1293.561f, 1601.938f, 31.60557f, -1.457349f, 0, 0, -0.6658813f, 0.7460576f, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RL_OBJECT_DOOR_2, BG_RL_OBJECT_TYPE_DOOR_2, 1278.648f, 1730.557f, 31.60557f, 1.684245f, 0, 0, 0.7460582f, 0.6658807f, RESPAWN_IMMEDIATELY)) + if( !AddObject(BG_RL_OBJECT_DOOR_1, BG_RL_OBJECT_TYPE_DOOR_1, 1293.561, 1601.938, 31.60557, -1.457349, 0, 0, -0.6658813, 0.7460576, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RL_OBJECT_DOOR_2, BG_RL_OBJECT_TYPE_DOOR_2, 1278.648, 1730.557, 31.60557, 1.684245, 0, 0, 0.7460582, 0.6658807, RESPAWN_IMMEDIATELY) + // buffs + || !AddObject(BG_RL_OBJECT_BUFF_1, BG_RL_OBJECT_TYPE_BUFF_1, 1328.719971, 1632.719971, 36.730400, -1.448624, 0, 0, 0.6626201, -0.7489557, 120) + || !AddObject(BG_RL_OBJECT_BUFF_2, BG_RL_OBJECT_TYPE_BUFF_2, 1243.300049, 1699.170044, 34.872601, -0.06981307, 0, 0, 0.03489945, -0.9993908, 120)) { sLog.outErrorDb("BatteGroundRL: Failed to spawn some object!"); return false; diff --git a/src/game/BattleGroundRL.h b/src/game/BattleGroundRL.h index 0e9a38bca..dd000ce13 100644 --- a/src/game/BattleGroundRL.h +++ b/src/game/BattleGroundRL.h @@ -24,13 +24,17 @@ enum BattleGroundRLObjectTypes { BG_RL_OBJECT_DOOR_1 = 0, BG_RL_OBJECT_DOOR_2 = 1, - BG_RL_OBJECT_MAX = 2 + BG_RL_OBJECT_BUFF_1 = 2, + BG_RL_OBJECT_BUFF_2 = 3, + BG_RL_OBJECT_MAX = 4 }; enum BattleGroundRLObjects { BG_RL_OBJECT_TYPE_DOOR_1 = 185918, - BG_RL_OBJECT_TYPE_DOOR_2 = 185917 + BG_RL_OBJECT_TYPE_DOOR_2 = 185917, + BG_RL_OBJECT_TYPE_BUFF_1 = 184663, + BG_RL_OBJECT_TYPE_BUFF_2 = 184664 }; class BattleGroundRLScore : public BattleGroundScore @@ -57,9 +61,8 @@ class BattleGroundRL : public BattleGround void HandleAreaTrigger(Player *Source, uint32 Trigger); bool SetupBattleGround(); virtual void ResetBGSubclass(); + virtual void FillInitialWorldStates(WorldPacket &d); void HandleKillPlayer(Player* player, Player *killer); - - private: - uint32 m_TeamKills[2]; // count of kills for each team + bool HandlePlayerUnderMap(Player * plr); }; #endif diff --git a/src/game/BattleGroundWS.cpp b/src/game/BattleGroundWS.cpp index 23c90a763..53c67c16f 100644 --- a/src/game/BattleGroundWS.cpp +++ b/src/game/BattleGroundWS.cpp @@ -49,6 +49,16 @@ void BattleGroundWS::Update(time_t diff) { m_Events |= 0x01; + // setup here, only when at least one player has ported to the map + if(!SetupBattleGround()) + { + EndNow(); + return; + } + +// 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); @@ -285,7 +295,32 @@ void BattleGroundWS::EventPlayerCapturedFlag(Player *Source) void BattleGroundWS::EventPlayerDroppedFlag(Player *Source) { - // Drop allowed in any BG state + if(GetStatus() != STATUS_IN_PROGRESS) + { + // if not running, do not cast things at the dropper player (prevent spawning the "dropped" flag), neither send unnecessary messages + // just take off the aura + if(Source->GetTeam() == ALLIANCE) + { + if(!this->IsHordeFlagPickedup()) + return; + if(GetHordeFlagPickerGUID() == Source->GetGUID()) + { + SetHordeFlagPicker(0); + Source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG); + } + } + else + { + if(!this->IsAllianceFlagPickedup()) + return; + if(GetAllianceFlagPickerGUID() == Source->GetGUID()) + { + SetAllianceFlagPicker(0); + Source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG); + } + } + return; + } const char *message = ""; uint8 type = 0; diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index cc93b4369..ecde680d9 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -36,6 +36,7 @@ #include "PlayerDump.h" #include "SocialMgr.h" #include "Util.h" +#include "ArenaTeam.h" #include "Language.h" class LoginQueryHolder : public SqlQueryHolder @@ -59,7 +60,7 @@ bool LoginQueryHolder::Initialize() // NOTE: all fields in `characters` must be read to prevent lost character data at next save in case wrong DB structure. // !!! NOTE: including unused `zone`,`online` - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADFROM, "SELECT guid, account, data, name, race, class, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADFROM, "SELECT guid, account, data, name, race, class, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty, arena_pending_points FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT leaderGuid FROM group_member WHERE memberGuid ='%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS, "SELECT caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid)); @@ -79,6 +80,7 @@ bool LoginQueryHolder::Initialize() res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = '%u'",GUID_LOPART(m_guid)); // in other case still be dummy query res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGUILD, "SELECT guildid,rank FROM guild_member WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADARENAINFO, "SELECT arenateamid, played_week, played_season, personal_rating FROM arena_team_member WHERE guid='%u'", GUID_LOPART(m_guid)); return res; } @@ -411,7 +413,7 @@ void WorldSession::HandleCharDeleteOpcode( WorldPacket & recv_data ) } // is arena team captain - if(objmgr.GetArenaTeamByCapitan(guid)) + if(objmgr.GetArenaTeamByCaptain(guid)) { WorldPacket data(SMSG_CHAR_DELETE, 1); data << (uint8)CHAR_DELETE_FAILED_ARENA_CAPTAIN; diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index 3c21a28ca..64e4cd028 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -197,6 +197,7 @@ ChatCommand * ChatHandler::getCommandTable() { "Mod32Value", SEC_ADMINISTRATOR, false, &ChatHandler::HandleMod32Value, "", NULL }, { "anim", SEC_GAMEMASTER, false, &ChatHandler::HandleAnimCommand, "", NULL }, { "lootrecipient", SEC_GAMEMASTER, false, &ChatHandler::HandleGetLootRecipient, "", NULL }, + { "arena", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugArenaCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; @@ -560,6 +561,7 @@ ChatCommand * ChatHandler::getCommandTable() { "cometome", SEC_ADMINISTRATOR, false, &ChatHandler::HandleComeToMeCommand, "", NULL }, { "damage", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDamageCommand, "", NULL }, { "combatstop", SEC_GAMEMASTER, false, &ChatHandler::HandleCombatStopCommand, "", NULL }, + { "flusharenapoints", SEC_ADMINISTRATOR, false, &ChatHandler::HandleFlushArenaPointsCommand, "", NULL }, { "chardelete", SEC_CONSOLE, true, &ChatHandler::HandleCharacterDeleteCommand, "", NULL }, { "sendmessage", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendMessageCommand, "", NULL }, { "repairitems", SEC_GAMEMASTER, false, &ChatHandler::HandleRepairitemsCommand, "", NULL }, diff --git a/src/game/Chat.h b/src/game/Chat.h index bd66f6877..bacadbb85 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -415,6 +415,7 @@ class ChatHandler bool HandleComeToMeCommand(const char *args); bool HandleCombatStopCommand(const char *args); bool HandleSendMessageCommand(const char * args); + bool HandleFlushArenaPointsCommand(const char *args); bool HandleRepairitemsCommand(const char* args); bool HandleWaterwalkCommand(const char* args); @@ -429,6 +430,7 @@ class ChatHandler bool HandleSaveAllCommand(const char* args); bool HandleGetItemState(const char * args); bool HandleGetLootRecipient(const char * args); + bool HandleDebugArenaCommand(const char * args); Player* getSelectedPlayer(); Creature* getSelectedCreature(); diff --git a/src/game/GameEvent.cpp b/src/game/GameEvent.cpp index 5981fd00c..e94d7c1d7 100644 --- a/src/game/GameEvent.cpp +++ b/src/game/GameEvent.cpp @@ -363,7 +363,7 @@ uint32 GameEvent::Update() // return the next e { uint32 nextEventDelay = max_ge_check_delay; // 1 day uint32 calcDelay; - for (uint16 itr = 1; itr < mGameEvent.size(); itr++) + for (uint16 itr = 1; itr < mGameEvent.size(); ++itr) { //sLog.outErrorDb("Checking event %u",itr); if (CheckOneGameEvent(itr)) diff --git a/src/game/Group.cpp b/src/game/Group.cpp index de97b70b3..8279f4c82 100644 --- a/src/game/Group.cpp +++ b/src/game/Group.cpp @@ -1308,6 +1308,54 @@ void Group::UpdateLooterGuid( Creature* creature, bool ifneed ) SendUpdate(); } +uint32 Group::CanJoinBattleGroundQueue(uint32 bgTypeId, uint32 bgQueueType, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot) +{ + // check for min / max count + uint32 memberscount = GetMembersCount(); + if(memberscount < MinPlayerCount) + return BG_JOIN_ERR_GROUP_NOT_ENOUGH; + if(memberscount > MaxPlayerCount) + return BG_JOIN_ERR_GROUP_TOO_MANY; + + // get a player as reference, to compare other players' stats to (arena team id, queue id based on level, etc.) + Player * reference = GetFirstMember()->getSource(); + // no reference found, can't join this way + if(!reference) + return BG_JOIN_ERR_OFFLINE_MEMBER; + + uint32 bgQueueId = reference->GetBattleGroundQueueIdFromLevel(); + uint32 arenaTeamId = reference->GetArenaTeamId(arenaSlot); + uint32 team = reference->GetTeam(); + + // check every member of the group to be able to join + for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *member = itr->getSource(); + // offline member? don't let join + if(!member) + return BG_JOIN_ERR_OFFLINE_MEMBER; + // don't allow cross-faction join as group + if(member->GetTeam() != team) + return BG_JOIN_ERR_MIXED_FACTION; + // not in the same battleground level braket, don't let join + if(member->GetBattleGroundQueueIdFromLevel() != bgQueueId) + return BG_JOIN_ERR_MIXED_LEVELS; + // don't let join rated matches if the arena team id doesn't match + if(isRated && member->GetArenaTeamId(arenaSlot) != arenaTeamId) + return BG_JOIN_ERR_MIXED_ARENATEAM; + // don't let join if someone from the group is already in that bg queue + if(member->InBattleGroundQueueForBattleGroundQueueType(bgQueueType)) + return BG_JOIN_ERR_GROUP_MEMBER_ALREADY_IN_QUEUE; + // check for deserter debuff in case not arena queue + if(bgTypeId != BATTLEGROUND_AA && !member->CanJoinToBattleground()) + return BG_JOIN_ERR_GROUP_DESERTER; + // check if member can join any more battleground queues + if(!member->HasFreeBattleGroundQueueId()) + return BG_JOIN_ERR_ALL_QUEUES_USED; + } + return BG_JOIN_ERR_OK; +} + //=================================================== //============== Roll =============================== //=================================================== diff --git a/src/game/Group.h b/src/game/Group.h index a9e6a733f..84e215fc0 100644 --- a/src/game/Group.h +++ b/src/game/Group.h @@ -247,6 +247,7 @@ class MANGOS_DLL_SPEC Group void ConvertToRaid(); void SetBattlegroundGroup(BattleGround *bg) { m_bgGroup = bg; } + uint32 CanJoinBattleGroundQueue(uint32 bgTypeId, uint32 bgQueueType, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot); void ChangeMembersGroup(const uint64 &guid, const uint8 &group); void ChangeMembersGroup(Player *player, const uint8 &group); diff --git a/src/game/Language.h b/src/game/Language.h index ad5a2b43c..c51cf0f3e 100644 --- a/src/game/Language.h +++ b/src/game/Language.h @@ -641,11 +641,30 @@ enum MangosStrings LANG_BG_QUEUE_ANNOUNCE_SELF = 711, LANG_BG_QUEUE_ANNOUNCE_WORLD = 712, + LANG_YOUR_ARENA_LEVEL_REQ_ERROR = 713, - LANG_HIS_ARENA_LEVEL_REQ_ERROR = 714, +// LANG_HIS_ARENA_LEVEL_REQ_ERROR = 714, an opcode exists for this LANG_YOUR_BG_LEVEL_REQ_ERROR = 715, - LANG_YOUR_ARENA_TEAM_FULL = 716, - // Room for BG/ARENA 717-799 not used +// LANG_YOUR_ARENA_TEAM_FULL = 716, an opcode exists for this + + LANG_BG_GROUP_TOO_LARGE = 7007, // "Your group is too large for this battleground. Please regroup to join." + LANG_ARENA_GROUP_TOO_LARGE = 7008, // "Your group is too large for this arena. Please regroup to join." + LANG_ARENA_YOUR_TEAM_ONLY = 7009, // "Your group has members not in your arena team. Please regroup to join." + LANG_ARENA_NOT_ENOUGH_PLAYERS = 7010, // "Your group does not have enough players to join this match." + LANG_ARENA_GOLD_WINS = 7011, // "The Gold Team wins!" + LANG_ARENA_GREEN_WINS = 7012, // "The Green Team wins!" + LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING = 7013, // The battleground will end soon, because there aren't enough players. Get more ppl or win already! + LANG_BG_GROUP_OFFLINE_MEMBER = 7014, // "Your group has an offline member. Please remove him before joining." + LANG_BG_GROUP_MIXED_FACTION = 7015, // "Your group has players from the opposing faction. You can't join the battleground as a group." + LANG_BG_GROUP_MIXED_LEVELS = 7016, // "Your group has players from different battleground brakets. You can't join as group." + LANG_BG_GROUP_MEMBER_ALREADY_IN_QUEUE = 7017, // "Someone in your party is already in this battleground queue. (S)he must leave it before joining as group." + LANG_BG_GROUP_MEMBER_DESERTER = 7018, // "Someone in your party is Deserter. You can't join as group." + LANG_BG_GROUP_MEMBER_NO_FREE_QUEUE_SLOTS = 7019, // "Someone in your party is already in three battleground queues. You cannot join as group." + + LANG_CANNOT_TELE_TO_BG = 7020, // "You cannot teleport to a battleground or arena map." + LANG_CANNOT_SUMMON_TO_BG = 7021, // "You cannot summon players to a battleground or arena map." + LANG_CANNOT_GO_TO_BG_GM = 7022, // "You must be in GM mode to teleport to a player in a battleground." + LANG_CANNOT_GO_TO_BG_FROM_BG = 7023, // "You cannot teleport to a battleground from another battleground. Please leave the current battleground first." // in game strings // = 800, not used diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp index c73231635..26d5b15fe 100644 --- a/src/game/Level1.cpp +++ b/src/game/Level1.cpp @@ -351,7 +351,14 @@ bool ChatHandler::HandleNamegoCommand(const char* args) Map* pMap = m_session->GetPlayer()->GetMap(); - if(pMap->Instanceable()) + if(pMap->IsBattleGroundOrArena()) + { + // cannot summon to bg + PSendSysMessage(LANG_CANNOT_SUMMON_TO_BG,chr->GetName()); + SetSentErrorMessage(true); + return false; + } + else if(pMap->IsDungeon()) { Map* cMap = chr->GetMap(); if( cMap->Instanceable() && cMap->GetInstanceId() != pMap->GetInstanceId() ) @@ -435,6 +442,28 @@ bool ChatHandler::HandleGonameCommand(const char* args) Player *chr = objmgr.GetPlayer(name.c_str()); if (chr) { + Map* cMap = chr->GetMap(); + if(cMap->IsBattleGroundOrArena()) + { + // only allow if gm mode is on + if (!_player->isGameMaster()) + { + PSendSysMessage(LANG_CANNOT_GO_TO_BG_GM,chr->GetName()); + SetSentErrorMessage(true); + return false; + } + // if already in a bg, don't let port to other + else if (_player->GetBattleGroundId()) + { + PSendSysMessage(LANG_CANNOT_GO_TO_BG_FROM_BG,chr->GetName()); + SetSentErrorMessage(true); + return false; + } + // all's well, set bg id + // when porting out from the bg, it will be reset to 0 + _player->SetBattleGroundId(chr->GetBattleGroundId()); + } + else if(cMap->IsDungeon()) Map* cMap = chr->GetMap(); if(cMap->Instanceable()) { diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index fb2c79bce..e77401167 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -47,6 +47,7 @@ #include "Config/ConfigEnv.h" #include "Util.h" #include "ItemEnchantmentMgr.h" +#include "BattleGroundMgr.h" #include "InstanceSaveMgr.h" #include "InstanceData.h" @@ -6479,6 +6480,12 @@ bool ChatHandler::HandleSendMessageCommand(const char* args) return true; } +bool ChatHandler::HandleFlushArenaPointsCommand(const char * /*args*/) +{ + sBattleGroundMgr.DistributeArenaPoints(); + return true; +} + bool ChatHandler::HandleModifyGenderCommand(const char *args) { if(!*args) diff --git a/src/game/MapInstanced.cpp b/src/game/MapInstanced.cpp index 8037e907d..37ce4522a 100644 --- a/src/game/MapInstanced.cpp +++ b/src/game/MapInstanced.cpp @@ -141,7 +141,17 @@ Map* MapInstanced::GetInstance(const WorldObject* obj) uint32 NewInstanceId = 0; // instanceId of the resulting map Player* player = (Player*)obj; - // TODO: battlegrounds and arenas + if(IsBattleGroundOrArena()) + { + // instantiate or find existing bg map for player + // the instance id is set in battlegroundid + NewInstanceId = player->GetBattleGroundId(); + assert(NewInstanceId); + map = _FindMap(NewInstanceId); + if(!map) + map = CreateBattleGround(NewInstanceId); + return map; + } InstancePlayerBind *pBind = player->GetBoundInstance(GetId(), player->GetDifficulty()); InstanceSave *pSave = pBind ? pBind->save : NULL; diff --git a/src/game/MapManager.cpp b/src/game/MapManager.cpp index 8833b8322..7c9dad5f1 100644 --- a/src/game/MapManager.cpp +++ b/src/game/MapManager.cpp @@ -107,7 +107,7 @@ MapManager::_GetBaseMap(uint32 id) Guard guard(*this); const MapEntry* entry = sMapStore.LookupEntry(id); - if (entry && entry->IsDungeon()) + if (entry && entry->Instanceable()) { m = new MapInstanced(id, i_gridCleanUpDelay); } diff --git a/src/game/MovementHandler.cpp b/src/game/MovementHandler.cpp index 372818072..849e3a88d 100644 --- a/src/game/MovementHandler.cpp +++ b/src/game/MovementHandler.cpp @@ -130,22 +130,28 @@ void WorldSession::HandleMoveWorldportAckOpcode() _player->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); // battleground state prepare - if(_player->InBattleGround()) + // only add to bg group and object, if the player was invited (else he entered through command) + if(_player->InBattleGround() && _player->IsInvitedForBattleGroundInstance(_player->GetBattleGroundId())) { BattleGround *bg = _player->GetBattleGround(); if(bg) { + bg->AddPlayer(_player); if(bg->GetMapId() == _player->GetMapId()) // we teleported to bg { - if(!bg->GetBgRaid(_player->GetTeam())) // first player joined + // get the team this way, because arenas might 'override' the teams. + uint32 team = bg->GetPlayerTeam(_player->GetGUID()); + if(!team) + team = _player->GetTeam(); + if(!bg->GetBgRaid(team)) // first player joined { Group *group = new Group; - bg->SetBgRaid(_player->GetTeam(), group); + bg->SetBgRaid(team, group); group->Create(_player->GetGUIDLow(), _player->GetName()); } else // raid already exist { - bg->GetBgRaid(_player->GetTeam())->AddMember(_player->GetGUID(), _player->GetName()); + bg->GetBgRaid(team)->AddMember(_player->GetGUID(), _player->GetName()); } } } @@ -370,20 +376,29 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) if(movementInfo.z < -500.0f) { - // NOTE: this is actually called many times while falling - // even after the player has been teleported away - // TODO: discard movement packets after the player is rooted - if(GetPlayer()->isAlive()) + if(GetPlayer()->InBattleGround() + && GetPlayer()->GetBattleGround() + && GetPlayer()->GetBattleGround()->HandlePlayerUnderMap(_player)) { - GetPlayer()->EnvironmentalDamage(GetPlayer()->GetGUID(),DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); - // change the death state to CORPSE to prevent the death timer from - // starting in the next player update - GetPlayer()->KillPlayer(); - GetPlayer()->BuildPlayerRepop(); + // do nothing, the handle already did if returned true } + else + { + // NOTE: this is actually called many times while falling + // even after the player has been teleported away + // TODO: discard movement packets after the player is rooted + if(GetPlayer()->isAlive()) + { + GetPlayer()->EnvironmentalDamage(GetPlayer()->GetGUID(),DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); + // change the death state to CORPSE to prevent the death timer from + // starting in the next player update + GetPlayer()->KillPlayer(); + GetPlayer()->BuildPlayerRepop(); + } - // cancel the death timer here if started - GetPlayer()->RepopAtGraveyard(); + // cancel the death timer here if started + GetPlayer()->RepopAtGraveyard(); + } } } diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 040c3ce35..83b9d7d76 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -137,13 +137,13 @@ ObjectMgr::ObjectMgr() ObjectMgr::~ObjectMgr() { - for( QuestMap::iterator i = mQuestTemplates.begin( ); i != mQuestTemplates.end( ); ++ i ) + for( QuestMap::iterator i = mQuestTemplates.begin( ); i != mQuestTemplates.end( ); ++i ) { delete i->second; } mQuestTemplates.clear( ); - for( GossipTextMap::iterator i = mGossipText.begin( ); i != mGossipText.end( ); ++ i ) + for( GossipTextMap::iterator i = mGossipText.begin( ); i != mGossipText.end( ); ++i ) { delete i->second; } @@ -151,7 +151,7 @@ ObjectMgr::~ObjectMgr() mAreaTriggers.clear(); - for(PetLevelInfoMap::iterator i = petInfo.begin( ); i != petInfo.end( ); ++ i ) + for(PetLevelInfoMap::iterator i = petInfo.begin( ); i != petInfo.end( ); ++i ) { delete[] i->second; } @@ -226,33 +226,43 @@ Guild* ObjectMgr::GetGuildByLeader(const uint64 &guid) const return NULL; } -ArenaTeam* ObjectMgr::GetArenaTeamById(const uint32 ArenaTeamId) const +ArenaTeam* ObjectMgr::GetArenaTeamById(const uint32 arenateamid) const { - for(ArenaTeamSet::const_iterator itr = mArenaTeamSet.begin(); itr != mArenaTeamSet.end(); ++itr) - if ((*itr)->GetId() == ArenaTeamId) - return *itr; + ArenaTeamMap::const_iterator itr = mArenaTeamMap.find(arenateamid); + if (itr != mArenaTeamMap.end()) + return itr->second; return NULL; } ArenaTeam* ObjectMgr::GetArenaTeamByName(const std::string& arenateamname) const { - for(ArenaTeamSet::const_iterator itr = mArenaTeamSet.begin(); itr != mArenaTeamSet.end(); ++itr) - if ((*itr)->GetName() == arenateamname) - return *itr; + for(ArenaTeamMap::const_iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr) + if (itr->second->GetName() == arenateamname) + return itr->second; return NULL; } -ArenaTeam* ObjectMgr::GetArenaTeamByCapitan(uint64 const& guid) const +ArenaTeam* ObjectMgr::GetArenaTeamByCaptain(uint64 const& guid) const { - for(ArenaTeamSet::const_iterator itr = mArenaTeamSet.begin(); itr != mArenaTeamSet.end(); ++itr) - if ((*itr)->GetCaptain() == guid) - return *itr; + for(ArenaTeamMap::const_iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr) + if (itr->second->GetCaptain() == guid) + return itr->second; return NULL; } +void ObjectMgr::AddArenaTeam(ArenaTeam* arenaTeam) +{ + mArenaTeamMap[arenaTeam->GetId()] = arenaTeam; +} + +void ObjectMgr::RemoveArenaTeam(ArenaTeam* arenaTeam) +{ + mArenaTeamMap.erase( arenaTeam->GetId() ); +} + AuctionHouseObject * ObjectMgr::GetAuctionsMap( uint32 location ) { switch ( location ) @@ -4203,7 +4213,7 @@ void ObjectMgr::AddGossipText(GossipText *pGText) GossipText *ObjectMgr::GetGossipText(uint32 Text_ID) { GossipTextMap::const_iterator itr; - for (itr = mGossipText.begin(); itr != mGossipText.end(); itr++) + for (itr = mGossipText.begin(); itr != mGossipText.end(); ++itr) { if(itr->second->Text_ID == Text_ID) return itr->second; diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index fa594e055..48371b908 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -282,11 +282,11 @@ class ObjectMgr typedef std::set< Group * > GroupSet; typedef std::set< Guild * > GuildSet; - typedef std::set< ArenaTeam * > ArenaTeamSet; + + typedef UNORDERED_MAP ArenaTeamMap; typedef UNORDERED_MAP QuestMap; - typedef UNORDERED_MAP AreaTriggerMap; typedef UNORDERED_MAP AreaTriggerScriptMap; @@ -318,11 +318,13 @@ class ObjectMgr void AddGuild(Guild* guild) { mGuildSet.insert( guild ); } void RemoveGuild(Guild* guild) { mGuildSet.erase( guild ); } - ArenaTeam* GetArenaTeamById(const uint32 ArenaTeamId) const; - ArenaTeam* GetArenaTeamByName(const std::string& ArenaTeamName) const; - ArenaTeam* GetArenaTeamByCapitan(uint64 const& guid) const; - void AddArenaTeam(ArenaTeam* arenateam) { mArenaTeamSet.insert( arenateam ); } - void RemoveArenaTeam(ArenaTeam* arenateam) { mArenaTeamSet.erase( arenateam ); } + ArenaTeam* GetArenaTeamById(const uint32 arenateamid) const; + ArenaTeam* GetArenaTeamByName(const std::string& arenateamname) const; + ArenaTeam* GetArenaTeamByCaptain(uint64 const& guid) const; + void AddArenaTeam(ArenaTeam* arenaTeam); + void RemoveArenaTeam(ArenaTeam* arenaTeam); + ArenaTeamMap::iterator GetArenaTeamMapBegin() { return mArenaTeamMap.begin(); } + ArenaTeamMap::iterator GetArenaTeamMapEnd() { return mArenaTeamMap.end(); } static CreatureInfo const *GetCreatureTemplate( uint32 id ); CreatureModelInfo const *GetCreatureModelInfo( uint32 modelid ); @@ -801,7 +803,7 @@ class ObjectMgr GroupSet mGroupSet; GuildSet mGuildSet; - ArenaTeamSet mArenaTeamSet; + ArenaTeamMap mArenaTeamMap; ItemMap mItems; ItemMap mAitems; diff --git a/src/game/PetitionsHandler.cpp b/src/game/PetitionsHandler.cpp index 1064bd23f..857143749 100644 --- a/src/game/PetitionsHandler.cpp +++ b/src/game/PetitionsHandler.cpp @@ -115,14 +115,6 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data) return; } - for(uint8 i = 0; i < MAX_ARENA_SLOT; i++) - { - if(_player->GetArenaTeamId(i) && (i == (unk10-1))) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM); - return; - } - } switch(unk10) { case 1: @@ -144,6 +136,12 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data) sLog.outDebug("unknown selection at buy petition: %u", unk10); return; } + + if(_player->GetArenaTeamId(unk10-1)) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM); + return; + } } if(type == 9) @@ -153,12 +151,7 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data) SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_EXISTS); return; } - if(objmgr.IsReservedName(name)) - { - SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_INVALID); - return; - } - if(!ObjectMgr::IsValidCharterName(name)) + if(objmgr.IsReservedName(name) || !ObjectMgr::IsValidCharterName(name)) { SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_INVALID); return; @@ -171,12 +164,7 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data) SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S); return; } - if(objmgr.IsReservedName(name)) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_INVALID); - return; - } - if(!ObjectMgr::IsValidCharterName(name)) + if(objmgr.IsReservedName(name) || !ObjectMgr::IsValidCharterName(name)) { SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_INVALID); return; @@ -216,6 +204,8 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data) _player->SendNewItem(charter, 1, true, false); // a petition is invalid, if both the owner and the type matches + // we checked above, if this player is in an arenateam, so this must be + // datacorruption QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE ownerguid = '%u' AND type = '%u'", _player->GetGUIDLow(), type); std::ostringstream ssInvalidPetitionGUIDs; @@ -429,12 +419,7 @@ void WorldSession::HandlePetitionRenameOpcode(WorldPacket & recv_data) SendGuildCommandResult(GUILD_CREATE_S, newname, GUILD_NAME_EXISTS); return; } - if(objmgr.IsReservedName(newname)) - { - SendGuildCommandResult(GUILD_CREATE_S, newname, GUILD_NAME_INVALID); - return; - } - if(!ObjectMgr::IsValidCharterName(newname)) + if(objmgr.IsReservedName(newname) || !ObjectMgr::IsValidCharterName(newname)) { SendGuildCommandResult(GUILD_CREATE_S, newname, GUILD_NAME_INVALID); return; @@ -447,12 +432,7 @@ void WorldSession::HandlePetitionRenameOpcode(WorldPacket & recv_data) SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_EXISTS_S); return; } - if(objmgr.IsReservedName(newname)) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_INVALID); - return; - } - if(!ObjectMgr::IsValidCharterName(newname)) + if(objmgr.IsReservedName(newname) || !ObjectMgr::IsValidCharterName(newname)) { SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_INVALID); return; @@ -511,7 +491,13 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data) // not let enemies sign guild charter if(!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != objmgr.GetPlayerTeamByGUID(ownerguid)) + { + if(type != 9) + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED); + else + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_NOT_ALLIED); return; + } QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); @@ -527,10 +513,42 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data) return; } - if(type != 9 && _player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) + if(type != 9) { - // player is too low level to join an arena team - SendNotification(LANG_YOUR_ARENA_LEVEL_REQ_ERROR,sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)); + if(_player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", _player->GetName(), ERR_ARENA_TEAM_PLAYER_TO_LOW); + return; + } + + uint8 slot = ArenaTeam::GetSlotByType(type); + if(slot >= MAX_ARENA_SLOT) + return; + + if(_player->GetArenaTeamId(slot)) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_IN_ARENA_TEAM_S); + return; + } + + if(_player->GetArenaTeamIdInvited()) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S); + return; + } + } + else + { + if(_player->GetGuildId()) + { + SendGuildCommandResult(GUILD_INVITE_S, _player->GetName(), ALREADY_IN_GUILD); + return; + } + if(_player->GetGuildIdInvited()) + { + SendGuildCommandResult(GUILD_INVITE_S, _player->GetName(), ALREADY_INVITED_TO_GUILD); + return; + } return; } @@ -619,20 +637,63 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data) uint8 signs = 0; uint64 petitionguid, plguid; - uint32 petitiontype; + uint32 type; Player *player; - recv_data >> petitiontype; // 2.0.8 - petition type? + recv_data >> type; recv_data >> petitionguid; // petition guid recv_data >> plguid; // player guid - sLog.outDebug("OFFER PETITION: type %u, GUID1 %u, to player id: %u", petitiontype, GUID_LOPART(petitionguid), GUID_LOPART(plguid)); + sLog.outDebug("OFFER PETITION: type %u, GUID1 %u, to player id: %u", type, GUID_LOPART(petitionguid), GUID_LOPART(plguid)); player = ObjectAccessor::FindPlayer(plguid); - if(!player || player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow())) - return; - - // not let offer to enemies if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != player->GetTeam() ) + { + if(type != 9) + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED); + else + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_NOT_ALLIED); return; + } + + if(type != 9) + { + if(player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) + { + // player is too low level to join an arena team + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName(), "", ERR_ARENA_TEAM_PLAYER_TO_LOW); + return; + } + + uint8 slot = ArenaTeam::GetSlotByType(type); + if(slot >= MAX_ARENA_SLOT) + return; + + if(player->GetArenaTeamId(slot)) + { + // player is already in an arena team + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName(), "", ERR_ALREADY_IN_ARENA_TEAM_S); + return; + } + + if(player->GetArenaTeamIdInvited()) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S); + return; + } + } + else + { + if(player->GetGuildId()) + { + SendGuildCommandResult(GUILD_INVITE_S, _player->GetName(), ALREADY_IN_GUILD); + return; + } + + if(player->GetGuildIdInvited()) + { + SendGuildCommandResult(GUILD_INVITE_S, _player->GetName(), ALREADY_INVITED_TO_GUILD); + return; + } + } QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); if(!result) @@ -811,7 +872,7 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data) else // or arena team { ArenaTeam* at = new ArenaTeam; - if(!at->create(_player->GetGUID(), type, name)) + if(!at->Create(_player->GetGUID(), type, name)) { sLog.outError("PetitionsHandler: arena team create failed."); delete at; diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 60f89f5ce..5a5fd6450 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -346,8 +346,8 @@ Player::Player (WorldSession *session): Unit() m_bgBattleGroundID = 0; for (int j=0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; j++) { - m_bgBattleGroundQueueID[j].bgType = 0; - m_bgBattleGroundQueueID[j].invited = false; + m_bgBattleGroundQueueID[j].bgQueueType = 0; + m_bgBattleGroundQueueID[j].invitedToInstance = 0; } m_bgTeam = 0; @@ -1499,7 +1499,8 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati MapEntry const* mEntry = sMapStore.LookupEntry(mapid); // don't let enter battlegrounds without assigned battleground id (for example through areatrigger)... - if(!InBattleGround() && mEntry->IsBattleGround() && !GetSession()->GetSecurity()) + // don't let gm level > 1 either + if(!InBattleGround() && mEntry->IsBattleGroundOrArena()) return false; // client without expansion support @@ -3489,6 +3490,29 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC guild->DelMember(guid); } + // remove from arena teams + uint32 at_id = GetArenaTeamIdFromDB(playerguid,ARENA_TEAM_2v2); + if(at_id != 0) + { + ArenaTeam * at = objmgr.GetArenaTeamById(at_id); + if(at) + at->DelMember(playerguid); + } + at_id = GetArenaTeamIdFromDB(playerguid,ARENA_TEAM_3v3); + if(at_id != 0) + { + ArenaTeam * at = objmgr.GetArenaTeamById(at_id); + if(at) + at->DelMember(playerguid); + } + at_id = GetArenaTeamIdFromDB(playerguid,ARENA_TEAM_5v5); + if(at_id != 0) + { + ArenaTeam * at = objmgr.GetArenaTeamById(at_id); + if(at) + at->DelMember(playerguid); + } + // the player was uninvited already on logout so just remove from group QueryResult *resultGroup = CharacterDatabase.PQuery("SELECT leaderGuid FROM group_member WHERE memberGuid='%u'", guid); if(resultGroup) @@ -7737,19 +7761,34 @@ void Player::SendInitWorldStates() data << uint32(0xa5f) << uint32(0x0); // 35 break; case 3698: // Nagrand Arena - data << uint32(0xa0f) << uint32(0x0); // 7 - data << uint32(0xa10) << uint32(0x0); // 8 - data << uint32(0xa11) << uint32(0x0); // 9 + if (bg && bg->GetTypeID() == BATTLEGROUND_NA) + bg->FillInitialWorldStates(data); + else + { + data << uint32(0xa0f) << uint32(0x0); // 7 + data << uint32(0xa10) << uint32(0x0); // 8 + data << uint32(0xa11) << uint32(0x0); // 9 show + } break; case 3702: // Blade's Edge Arena - data << uint32(0x9f0) << uint32(0x0); // 7 - data << uint32(0x9f1) << uint32(0x0); // 8 - data << uint32(0x9f3) << uint32(0x0); // 9 + if (bg && bg->GetTypeID() == BATTLEGROUND_BE) + bg->FillInitialWorldStates(data); + else + { + data << uint32(0x9f0) << uint32(0x0); // 7 gold + data << uint32(0x9f1) << uint32(0x0); // 8 green + data << uint32(0x9f3) << uint32(0x0); // 9 show + } break; case 3968: // Ruins of Lordaeron - data << uint32(0xbb8) << uint32(0x0); // 7 - data << uint32(0xbb9) << uint32(0x0); // 8 - data << uint32(0xbba) << uint32(0x0); // 9 + if (bg && bg->GetTypeID() == BATTLEGROUND_RL) + bg->FillInitialWorldStates(data); + else + { + data << uint32(0xbb8) << uint32(0x0); // 7 gold + data << uint32(0xbb9) << uint32(0x0); // 8 green + data << uint32(0xbba) << uint32(0x0); // 9 show + } break; case 3703: // Shattrath City break; @@ -13334,6 +13373,36 @@ void Player::_LoadDeclinedNames(QueryResult* result) delete result; } +void Player::_LoadArenaTeamInfo(QueryResult *result) +{ + // arenateamid, played_week, played_season, personal_rating + memset((void*)&m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1], 0, sizeof(uint32)*18); + if (!result) + return; + + do + { + Field *fields = result->Fetch(); + + uint32 arenateamid = fields[0].GetUInt32(); + uint32 played_week = fields[1].GetUInt32(); + uint32 played_season = fields[2].GetUInt32(); + uint32 personal_rating = fields[3].GetUInt32(); + + ArenaTeam* aTeam = objmgr.GetArenaTeamById(arenateamid); + uint8 arenaSlot = aTeam->GetSlot(); + + m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + arenaSlot * 3] = arenateamid; // TeamID + m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + arenaSlot * 3 + 1] = ((aTeam->GetCaptain() == GetGUID()) ? (uint32)0 : (uint32)1); // Captain 0, member 1 + m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + arenaSlot * 3 + 2] = played_week; // Played Week + m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + arenaSlot * 3 + 3] = played_season; // Played Season + m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + arenaSlot * 3 + 4] = 0; // Unk + m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + arenaSlot * 3 + 5] = personal_rating; // Personal Rating + + }while (result->NextRow()); + delete result; +} + bool Player::LoadPositionFromDB(uint32& mapid, float& x,float& y,float& z,float& o, bool& in_flight, uint64 guid) { QueryResult *result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,taxi_path FROM characters WHERE guid = '%u'",GUID_LOPART(guid)); @@ -13405,8 +13474,8 @@ float Player::GetFloatValueFromDB(uint16 index, uint64 guid) bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) { - //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [28] [29] 30 31 32 - //QueryResult *result = CharacterDatabase.PQuery("SELECT guid, account, data, name, race, class, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty FROM characters WHERE guid = '%u'", guid); + //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [28] [29] 30 31 32 33 + //QueryResult *result = CharacterDatabase.PQuery("SELECT guid, account, data, name, race, class, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty, arena_pending_points FROM characters WHERE guid = '%u'", guid); QueryResult *result = holder->GetResult(PLAYER_LOGIN_QUERY_LOADFROM); if(!result) @@ -13496,6 +13565,14 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) _LoadGroup(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGROUP)); + _LoadArenaTeamInfo(holder->GetResult(PLAYER_LOGIN_QUERY_LOADARENAINFO)); + + uint32 arena_currency = GetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY) + fields[33].GetUInt32(); + if (arena_currency > sWorld.getConfig(CONFIG_MAX_ARENA_POINTS)) + arena_currency = sWorld.getConfig(CONFIG_MAX_ARENA_POINTS); + + SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, arena_currency); + // check arena teams integrity for(uint32 arena_slot = 0; arena_slot < MAX_ARENA_SLOT; ++arena_slot) { @@ -14818,8 +14895,10 @@ void Player::SaveToDB() // first save/honor gain after midnight will also update the player's honor fields UpdateHonorFields(); - // Must saved before enter into BattleGround - if(InBattleGround()) + // players aren't saved on battleground maps + uint32 mapid = IsBeingTeleported() ? GetTeleportDest().mapid : GetMapId(); + const MapEntry * me = sMapStore.LookupEntry(mapid); + if(!me || me->IsBattleGroundOrArena()) return; int is_save_resting = HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0; @@ -14857,7 +14936,7 @@ void Player::SaveToDB() "taximask, online, cinematic, " "totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, " "trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, " - "death_expire_time, taxi_path) VALUES (" + "death_expire_time, taxi_path, arena_pending_points) VALUES (" << GetGUIDLow() << ", " << GetSession()->GetAccountId() << ", '" << sql_name << "', " @@ -14956,7 +15035,7 @@ void Player::SaveToDB() ss << ", '"; ss << m_taxi.SaveTaxiDestinationsToString(); - ss << "' )"; + ss << "', '0' )"; CharacterDatabase.Execute( ss.str().c_str() ); @@ -17619,7 +17698,8 @@ bool Player::InArena() const bool Player::GetBGAccessByLevel(uint32 bgTypeId) const { - BattleGround *bg = sBattleGroundMgr.GetBattleGround(bgTypeId); + // get a template bg instead of running one + BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); if(!bg) return false; @@ -17657,6 +17737,11 @@ uint32 Player::GetBattleGroundQueueIdFromLevel() const return 6; else return level/10 - 1; // 20..29 -> 1, 30-39 -> 2, ... + /* + assert(bgTypeId < MAX_BATTLEGROUND_TYPES); + BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); + assert(bg); + return (getLevel() - bg->GetMinLevel()) / 10;*/ } float Player::GetReputationPriceDiscount( Creature const* pCreature ) const diff --git a/src/game/Player.h b/src/game/Player.h index 93ec74775..3ecba2780 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -809,9 +809,10 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS = 15, PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES = 16, PLAYER_LOGIN_QUERY_LOADGUILD = 17, -}; + PLAYER_LOGIN_QUERY_LOADARENAINFO = 18, -#define MAX_PLAYER_LOGIN_QUERY 18 + MAX_PLAYER_LOGIN_QUERY +}; // Player summoning auto-decline time (in secs) #define MAX_PLAYER_SUMMON_DELAY (2*MINUTE) @@ -1517,7 +1518,6 @@ class MANGOS_DLL_SPEC Player : public Unit void SetInArenaTeam(uint32 ArenaTeamId, uint8 slot) { SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * 6), ArenaTeamId); - SaveDataFieldToDB(); // needed? } uint32 GetArenaTeamId(uint8 slot) { return GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * 6)); } static uint32 GetArenaTeamIdFromDB(uint64 guid, uint8 slot); @@ -1804,24 +1804,32 @@ class MANGOS_DLL_SPEC Player : public Unit static uint32 GetMaxLevelForBattleGroundQueueId(uint32 queue_id); uint32 GetBattleGroundQueueIdFromLevel() const; - uint32 GetBattleGroundQueueId(uint32 index) const { return m_bgBattleGroundQueueID[index].bgType; } - uint32 GetBattleGroundQueueIndex(uint32 bgType) const + bool InBattleGroundQueue() const { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - if (m_bgBattleGroundQueueID[i].bgType == bgType) + if (m_bgBattleGroundQueueID[i].bgQueueType != 0) + return true; + return false; + } + + uint32 GetBattleGroundQueueId(uint32 index) const { return m_bgBattleGroundQueueID[index].bgQueueType; } + uint32 GetBattleGroundQueueIndex(uint32 bgQueueType) const + { + for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + if (m_bgBattleGroundQueueID[i].bgQueueType == bgQueueType) return i; return PLAYER_MAX_BATTLEGROUND_QUEUES; } - bool IsInvitedForBattleGroundType(uint32 bgType) const + bool IsInvitedForBattleGroundQueueType(uint32 bgQueueType) const { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - if (m_bgBattleGroundQueueID[i].bgType == bgType) - return m_bgBattleGroundQueueID[i].invited; + if (m_bgBattleGroundQueueID[i].bgQueueType == bgQueueType) + return m_bgBattleGroundQueueID[i].invitedToInstance != 0; return PLAYER_MAX_BATTLEGROUND_QUEUES; } - bool InBattleGroundQueueForBattleGroundType(uint32 bgType) const + bool InBattleGroundQueueForBattleGroundQueueType(uint32 bgQueueType) const { - return GetBattleGroundQueueIndex(bgType) < PLAYER_MAX_BATTLEGROUND_QUEUES; + return GetBattleGroundQueueIndex(bgQueueType) < PLAYER_MAX_BATTLEGROUND_QUEUES; } void SetBattleGroundId(uint32 val) { m_bgBattleGroundID = val; } @@ -1829,34 +1837,47 @@ class MANGOS_DLL_SPEC Player : public Unit { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - if (m_bgBattleGroundQueueID[i].bgType == 0 || m_bgBattleGroundQueueID[i].bgType == val) + if (m_bgBattleGroundQueueID[i].bgQueueType == 0 || m_bgBattleGroundQueueID[i].bgQueueType == val) { - m_bgBattleGroundQueueID[i].bgType = val; - m_bgBattleGroundQueueID[i].invited = false; + m_bgBattleGroundQueueID[i].bgQueueType = val; + m_bgBattleGroundQueueID[i].invitedToInstance = 0; return i; } } return PLAYER_MAX_BATTLEGROUND_QUEUES; } + bool HasFreeBattleGroundQueueId() + { + for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + if (m_bgBattleGroundQueueID[i].bgQueueType == 0) + return true; + return false; + } void RemoveBattleGroundQueueId(uint32 val) { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { - if (m_bgBattleGroundQueueID[i].bgType == val) + if (m_bgBattleGroundQueueID[i].bgQueueType == val) { - m_bgBattleGroundQueueID[i].bgType = 0; - m_bgBattleGroundQueueID[i].invited = false; + m_bgBattleGroundQueueID[i].bgQueueType = 0; + m_bgBattleGroundQueueID[i].invitedToInstance = 0; return; } } } - void SetInviteForBattleGroundType(uint32 bgType) + void SetInviteForBattleGroundQueueType(uint32 bgQueueType, uint32 instanceId) { for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - if (m_bgBattleGroundQueueID[i].bgType == bgType) - m_bgBattleGroundQueueID[i].invited = true; + if (m_bgBattleGroundQueueID[i].bgQueueType == bgQueueType) + m_bgBattleGroundQueueID[i].invitedToInstance = instanceId; + } + bool IsInvitedForBattleGroundInstance(uint32 instanceId) const + { + for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + if (m_bgBattleGroundQueueID[i].invitedToInstance == instanceId) + return true; + return false; } - uint32 GetBattleGroundEntryPointMap() const { return m_bgEntryPointMap; } float GetBattleGroundEntryPointX() const { return m_bgEntryPointX; } float GetBattleGroundEntryPointY() const { return m_bgEntryPointY; } @@ -2053,8 +2074,8 @@ class MANGOS_DLL_SPEC Player : public Unit */ struct BgBattleGroundQueueID_Rec { - uint32 bgType; - bool invited; + uint32 bgQueueType; + uint32 invitedToInstance; }; BgBattleGroundQueueID_Rec m_bgBattleGroundQueueID[PLAYER_MAX_BATTLEGROUND_QUEUES]; uint32 m_bgEntryPointMap; @@ -2099,6 +2120,7 @@ class MANGOS_DLL_SPEC Player : public Unit void _LoadFriendList(QueryResult *result); bool _LoadHomeBind(QueryResult *result); void _LoadDeclinedNames(QueryResult *result); + void _LoadArenaTeamInfo(QueryResult *result); /*********************************************************/ /*** SAVE SYSTEM ***/ diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index a9699c70a..a0a93c079 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -2147,7 +2147,8 @@ void Aura::HandleAuraDummy(bool apply, bool Real) return; // final heal - m_target->CastCustomSpell(m_target,33778,&m_modifier.m_amount,NULL,NULL,true,NULL,this,GetCasterGUID()); + if(m_target->IsInWorld()) + m_target->CastCustomSpell(m_target,33778,&m_modifier.m_amount,NULL,NULL,true,NULL,this,GetCasterGUID()); } return; } diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index 5634cc249..08ac6ee18 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -2649,7 +2649,7 @@ void Spell::DoCreateItem(uint32 i, uint32 itemtype) return; } - if(BattleGround* bg = sBattleGroundMgr.GetBattleGround(bgType)) + if(BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(bgType)) bg->SendRewardMarkByMail(player,newitemid,no_space); } } diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 0809a6404..5925b21c4 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -605,25 +605,6 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa ((Creature*)pVictim)->SetLootRecipient(this); if (health <= damage) { - // battleground things - if(pVictim->GetTypeId() == TYPEID_PLAYER && (((Player*)pVictim)->InBattleGround())) - { - Player *killed = ((Player*)pVictim); - Player *killer = NULL; - if(GetTypeId() == TYPEID_PLAYER) - killer = ((Player*)this); - else if(GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet()) - { - Unit *owner = GetOwner(); - if(owner && owner->GetTypeId() == TYPEID_PLAYER) - killer = ((Player*)owner); - } - - if(killer) - if(BattleGround *bg = killed->GetBattleGround()) - bg->HandleKillPlayer(killed, killer); // drop flags and etc - } - DEBUG_LOG("DealDamage: victim just died"); // find player: owner of controlled `this` or `this` itself maybe @@ -762,6 +743,19 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa he->DuelComplete(DUEL_INTERUPTED); } + + // battleground things (do this at the end, so the death state flag will be properly set to handle in the bg->handlekill) + if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->InBattleGround()) + { + Player *killed = ((Player*)pVictim); + if(BattleGround *bg = killed->GetBattleGround()) + if(player) + bg->HandleKillPlayer(killed, player); + //later we can add support for creature->player kills here i'm + //not sure, but i guess those kills also get counted in av + //else if(GetTypeId() == TYPEID_UNIT) + // bg->HandleKillPlayer(killed,(Creature*)this); + } } else // if (health <= damage) { @@ -4173,6 +4167,22 @@ void Unit::RemoveAllAuras() } } +void Unit::RemoveArenaAuras(bool onleave) +{ + // in join, remove positive buffs, on end, remove negative + // used to remove positive visible auras in arenas + for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();) + { + if ( !(iter->second->GetSpellProto()->AttributesEx4 & (1<<21)) // don't remove stances, shadowform, pally/hunter auras + && !iter->second->IsPassive() // don't remove passive auras + && (!(iter->second->GetSpellProto()->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) || !(iter->second->GetSpellProto()->Attributes & SPELL_ATTR_UNK8)) // not unaffected by invulnerability auras or not having that unknown flag (that seemed the most probable) + && (iter->second->IsPositive() ^ onleave)) // remove positive buffs on enter, negative buffs on leave + RemoveAura(iter); + else + ++iter; + } +} + void Unit::RemoveAllAurasOnDeath() { // used just after dieing to remove all visible auras diff --git a/src/game/Unit.h b/src/game/Unit.h index 45a31f668..c0c1753b4 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -1012,6 +1012,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void RemoveAurasWithDispelType( DispelType type ); void RemoveAllAuras(); + void RemoveArenaAuras(bool onleave = false); void RemoveAllAurasOnDeath(); void DelayAura(uint32 spellId, uint32 effindex, int32 delaytime); diff --git a/src/game/World.cpp b/src/game/World.cpp index e6623a29a..8556758e2 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -891,6 +891,12 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_LISTEN_RANGE_TEXTEMOTE] = sConfig.GetIntDefault("ListenRange.TextEmote", 25); m_configs[CONFIG_LISTEN_RANGE_YELL] = sConfig.GetIntDefault("ListenRange.Yell", 300); + m_configs[CONFIG_ARENA_MAX_RATING_DIFFERENCE] = sConfig.GetIntDefault("Arena.MaxRatingDifference", 0); + m_configs[CONFIG_ARENA_RATING_DISCARD_TIMER] = sConfig.GetIntDefault("Arena.RatingDiscardTimer",300000); + m_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS] = sConfig.GetBoolDefault("Arena.AutoDistributePoints", false); + m_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS] = sConfig.GetIntDefault("Arena.AutoDistributeInterval", 7); + + m_configs[CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER] = sConfig.GetIntDefault("BattleGround.PrematureFinishTimer", 0); m_configs[CONFIG_INSTANT_LOGOUT] = sConfig.GetIntDefault("InstantLogout", SEC_MODERATOR); m_VisibleUnitGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Unit", 1); @@ -1292,6 +1298,7 @@ void World::SetInitialWorldSettings() ///- Initialize Battlegrounds sLog.outString( "Starting BattleGround System" ); sBattleGroundMgr.CreateInitialBattleGrounds(); + sBattleGroundMgr.InitAutomaticArenaPointDistribution(); //Not sure if this can be moved up in the sequence (with static data loading) as it uses MapManager sLog.outString( "Loading Transports..." ); @@ -2169,7 +2176,7 @@ void World::ScriptsProcess() void World::SendGlobalMessage(WorldPacket *packet, WorldSession *self, uint32 team) { SessionMap::iterator itr; - for (itr = m_sessions.begin(); itr != m_sessions.end(); itr++) + for (itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) { if (itr->second && itr->second->GetPlayer() && @@ -2236,11 +2243,29 @@ void World::SendWorldText(int32 string_id, ...) delete data_cache[i][j]; } +/// Send a System Message to all players (except self if mentioned) +void World::SendGlobalText(const char* text, WorldSession *self) +{ + WorldPacket data; + + // need copy to prevent corruption by strtok call in LineFromMessage original string + char* buf = strdup(text); + char* pos = buf; + + while(char* line = ChatHandler::LineFromMessage(pos)) + { + ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, 0, line, NULL); + SendGlobalMessage(&data, self); + } + + free(buf); +} + /// Send a packet to all players (or players selected team) in the zone (except self if mentioned) void World::SendZoneMessage(uint32 zone, WorldPacket *packet, WorldSession *self, uint32 team) { SessionMap::iterator itr; - for (itr = m_sessions.begin(); itr != m_sessions.end(); itr++) + for (itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) { if (itr->second && itr->second->GetPlayer() && diff --git a/src/game/World.h b/src/game/World.h index d5b313503..db9cbeca4 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -176,6 +176,11 @@ enum WorldConfigs CONFIG_LISTEN_RANGE_SAY, CONFIG_LISTEN_RANGE_TEXTEMOTE, CONFIG_LISTEN_RANGE_YELL, + CONFIG_ARENA_MAX_RATING_DIFFERENCE, + CONFIG_ARENA_RATING_DISCARD_TIMER, + CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS, + CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS, + CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER, CONFIG_VALUE_COUNT }; @@ -396,6 +401,7 @@ class World void LoadConfigSettings(bool reload = false); void SendWorldText(int32 string_id, ...); + void SendGlobalText(const char* text, WorldSession *self); void SendGlobalMessage(WorldPacket *packet, WorldSession *self = 0, uint32 team = 0); void SendZoneMessage(uint32 zone, WorldPacket *packet, WorldSession *self = 0, uint32 team = 0); void SendZoneText(uint32 zone, const char *text, WorldSession *self = 0, uint32 team = 0); diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h index cd3a4e961..7e20ad4d8 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -177,11 +177,12 @@ class MANGOS_DLL_SPEC WorldSession // Guild/Arena Team void SendGuildCommandResult(uint32 typecmd, const std::string& str, uint32 cmdresult); - void SendArenaTeamCommandResult(uint32 unk1, const std::string& str1, const std::string& str2, uint32 unk3); + void SendArenaTeamCommandResult(uint32 team_action, const std::string& team, const std::string& player, uint32 error_id); void BuildArenaTeamEventPacket(WorldPacket *data, uint8 eventid, uint8 str_count, const std::string& str1, const std::string& str2, const std::string& str3); void SendNotInArenaTeamPacket(uint8 type); void SendPetitionShowList( uint64 guid ); void SendSaveGuildEmblem( uint32 msg ); + void SendBattleGroundOrArenaJoinError(uint8 err); // Looking For Group // TRUE values set by client sending CMSG_LFG_SET_AUTOJOIN and CMSG_LFM_CLEAR_AUTOFILL before player login diff --git a/src/game/debugcmds.cpp b/src/game/debugcmds.cpp index 298411fbd..1b77f9ac6 100644 --- a/src/game/debugcmds.cpp +++ b/src/game/debugcmds.cpp @@ -30,6 +30,7 @@ #include "GossipDef.h" #include "Language.h" #include "MapManager.h" +#include "BattleGroundMgr.h" #include #include "ObjectMgr.h" @@ -518,3 +519,9 @@ bool ChatHandler::HandleGetItemState(const char* args) return true; } + +bool ChatHandler::HandleDebugArenaCommand(const char * /*args*/) +{ + sBattleGroundMgr.ToggleArenaTesting(); + return true; +} diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index f57e25d5a..2c6ca9162 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -1074,6 +1074,48 @@ Death.SicknessLevel = 11 Death.CorpseReclaimDelay.PvP = 1 Death.CorpseReclaimDelay.PvE = 1 +################################################################################################################### +# +# Rated arena matches config +# +# MaxRatingDifference: the maximum rating difference between two groups in rated matches +# Default: 0 (disable, rating difference is discarded) +# +# RatingDiscardTimer: after the specified milliseconds has passed, +# rating information will be discarded when selecting teams for matches +# also initiates an update by this timer +# Default: 60000 +# +# AutoDistributePoints: set if arena points should be distributed automatically, or by GM command +# Default: 0 (disable) (recommended): use gm command or sql query to distribute the points +# 1 (enable): arena points are distributed automatically +# +# AutoDistributeInterval: how often should the distribution take place +# if automatic distribution is enabled +# in days +# Default: 7 (weekly) +# +################################################################################################################### + +Arena.MaxRatingDifference = 0 +Arena.RatingDiscardTimer = 60000 +Arena.AutoDistributePoints = 0 +Arena.AutoDistributeInterval = 7 + +################################################################################################################### +# +# Battleground config +# +# PrematureFinishTimer: the time to end the bg if there are less than minplayersperteam on one side +# in milliseconds +# Default: 300000 +# 0 - disable +# +################################################################################################################### + +BattleGround.PrematureFinishTimer = 300000 + + ################################################################################################################### # # NETWORK CONFIG diff --git a/src/shared/Database/DBCStructure.h b/src/shared/Database/DBCStructure.h index acabe18f5..57af26fd8 100644 --- a/src/shared/Database/DBCStructure.h +++ b/src/shared/Database/DBCStructure.h @@ -434,9 +434,10 @@ struct MapEntry // Helpers uint32 Expansion() const { return addon; } - bool Instanceable() const { return map_type == MAP_INSTANCE || map_type == MAP_RAID; } - // NOTE: this duplicate of Instanceable(), but Instanceable() can be changed when BG also will be instanceable + + bool IsDungeon() const { return map_type == MAP_INSTANCE || map_type == MAP_RAID; } + bool Instanceable() const { return map_type == MAP_INSTANCE || map_type == MAP_RAID || map_type == MAP_BATTLEGROUND || map_type == MAP_ARENA; } bool IsRaid() const { return map_type == MAP_RAID; } bool IsBattleGround() const { return map_type == MAP_BATTLEGROUND; } bool IsBattleArena() const { return map_type == MAP_ARENA; } diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 945fa5396..6b759d284 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "6909" + #define REVISION_NR "6910" #endif // __REVISION_NR_H__ From ef903e485c62c1383186265857958c9c17262d72 Mon Sep 17 00:00:00 2001 From: ApoC Date: Tue, 16 Dec 2008 00:32:09 +0100 Subject: [PATCH 4/9] [6911] Fixed typo. Signed-off-by: ApoC --- src/game/ArenaTeam.cpp | 1 - src/shared/revision_nr.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/game/ArenaTeam.cpp b/src/game/ArenaTeam.cpp index b6df34f98..5f1dfdb93 100644 --- a/src/game/ArenaTeam.cpp +++ b/src/game/ArenaTeam.cpp @@ -180,7 +180,6 @@ bool ArenaTeam::LoadArenaTeamFromDB(uint32 ArenaTeamId) CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u'", ArenaTeamId); CharacterDatabase.PExecute("DELETE FROM arena_team_stats WHERE arenateamid = '%u'", ArenaTeamId); CharacterDatabase.CommitTransaction(); - // return false return false; } diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 6b759d284..c052f58bf 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "6910" + #define REVISION_NR "6911" #endif // __REVISION_NR_H__ From 6bd2bc4125c7bc222e05555d9336bdd79945132f Mon Sep 17 00:00:00 2001 From: ApoC Date: Tue, 16 Dec 2008 00:45:28 +0100 Subject: [PATCH 5/9] [6912] Removed forgotten define. Signed-off-by: ApoC --- src/game/BattleGround.h | 1 - src/shared/revision_nr.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/game/BattleGround.h b/src/game/BattleGround.h index bff2eb64d..b18217d36 100644 --- a/src/game/BattleGround.h +++ b/src/game/BattleGround.h @@ -18,7 +18,6 @@ #ifndef __BATTLEGROUND_H #define __BATTLEGROUND_H -#define ARENA_PATCH #include "Common.h" #include "WorldPacket.h" diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index c052f58bf..0ec0c3ee2 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "6911" + #define REVISION_NR "6912" #endif // __REVISION_NR_H__ From 8ffb28a174fc10acdc909297bbbe0abb8cdca2b1 Mon Sep 17 00:00:00 2001 From: ApoC Date: Tue, 16 Dec 2008 02:03:23 +0100 Subject: [PATCH 6/9] Added missing changes from arena patch. Renumbered texts to be continuous. Signed-off-by: ApoC --- sql/characters.sql | 13 +++++++- sql/mangos.sql | 20 +++++++++++- sql/updates/2008_12_15_01_mangos_arenas.sql | 36 ++++++++++----------- sql/updates/Makefile.am | 4 +++ src/game/Language.h | 34 +++++++++---------- 5 files changed, 70 insertions(+), 37 deletions(-) diff --git a/sql/characters.sql b/sql/characters.sql index f73ca3c78..c5fd2a6bd 100644 --- a/sql/characters.sql +++ b/sql/characters.sql @@ -15,6 +15,15 @@ /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `saved_variables` +-- + +CREATE TABLE `saved_variables` ( + `NextArenaPointDistributionTime` bigint(40) UNSIGNED NOT NULL DEFAULT '0' +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Variable Saves'; + -- -- Table structure for table `character_db_version` -- @@ -73,7 +82,8 @@ CREATE TABLE `arena_team_member` ( `played_week` int(10) unsigned NOT NULL default '0', `wons_week` int(10) unsigned NOT NULL default '0', `played_season` int(10) unsigned NOT NULL default '0', - `wons_season` int(10) unsigned NOT NULL default '0' + `wons_season` int(10) unsigned NOT NULL default '0', + `personal_rating` int(10) UNSIGNED NOT NULL DEFAULT '0' ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- @@ -201,6 +211,7 @@ CREATE TABLE `characters` ( `zone` int(11) unsigned NOT NULL default '0', `death_expire_time` bigint(20) unsigned NOT NULL default '0', `taxi_path` text, + `arena_pending_points` int(10) UNSIGNED NOT NULL default '0', PRIMARY KEY (`guid`), KEY `idx_account` (`account`), KEY `idx_online` (`online`), diff --git a/sql/mangos.sql b/sql/mangos.sql index 7c5817f30..ab1d64f9b 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -251,6 +251,7 @@ INSERT INTO `command` VALUES ('event start',2,'Syntax: .event start #event_id\r\nStart event #event_id. Set start time for event to current moment (change not saved in DB).'), ('event stop',2,'Syntax: .event stop #event_id\r\nStop event #event_id. Set start time for event to time in past that make current moment is event stop time (change not saved in DB).'), ('explorecheat',3,'Syntax: .explorecheat #flag\r\n\r\nReveal or hide all maps for the selected player. If no player is selected, hide or reveal maps to you.\r\n\r\nUse a #flag of value 1 to reveal, use a #flag value of 0 to hide all maps.'), +('flusharenapoints','3','Syntax: .flusharenapoints\r\n\r\nUse it to distribute arena points based on arena team ratings, and start a new week.'), ('gm',1,'Syntax: .gm [on/off]\r\n\r\nEnable or Disable in game GM MODE or show current state of on/off not provided.'), ('gm chat',1,'Syntax: .gm chat [on/off]\r\n\r\nEnable or disable chat GM MODE (show gm badge in messages) or show current state of on/off not provided.'), ('gm fly',3,'Syntax: .gm fly on/off\r\nEnable/disable gm fly mode.'), @@ -2753,7 +2754,24 @@ INSERT INTO `mangos_string` VALUES (1118,'%d - guild: %s (guid: %u) %s',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1119,'You must use male or female as gender.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1120,'You change gender of %s to %s.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), -(1121,'Your gender changed to %s by %s.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); +(1121,'Your gender changed to %s by %s.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1122,'Your group is too large for this battleground. Please regroup to join.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1123,'Your group is too large for this arena. Please regroup to join.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1124,'Your group has members not in your arena team. Please regroup to join.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1125,'Your group does not have enough players to join this match.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1126,'The Gold Team wins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1127,'The Green Team wins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1128,'There aren\'t enough players in this battleground. It will end soon unless some more players join to balance the fight.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1129,'Your group has an offline member. Please remove him before joining.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1130,'Your group has players from the opposing faction. You can\'t join the battleground as a group.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1131,'Your group has players from different battleground brakets. You can\'t join as group.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1132,'Someone in your party is already in this battleground queue. (S)he must leave it before joining as group.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1133,'Someone in your party is Deserter. You can\'t join as group.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1134,'Someone in your party is already in three battleground queues. You cannot join as group.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1135,'You cannot teleport to a battleground or arena map.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1136,'You cannot summon players to a battleground or arena map.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1137,'You must be in GM mode to teleport to a player in a battleground.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(1138,'You cannot teleport to a battleground from another battleground. Please leave the current battleground first.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); /*!40000 ALTER TABLE `mangos_string` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/updates/2008_12_15_01_mangos_arenas.sql b/sql/updates/2008_12_15_01_mangos_arenas.sql index f57ea8a3f..0278724a5 100644 --- a/sql/updates/2008_12_15_01_mangos_arenas.sql +++ b/sql/updates/2008_12_15_01_mangos_arenas.sql @@ -2,25 +2,25 @@ DELETE FROM `command` WHERE `name` = "flusharenapoints"; INSERT INTO `command` (`name`, `security`, `help`) VALUES ('flusharenapoints','3','Syntax: .flusharenapoints\r\n\r\nUse it to distribute arena points based on arena team ratings, and start a new week.'); -DELETE FROM mangos_string WHERE entry BETWEEN 7007 AND 7023; +DELETE FROM mangos_string WHERE entry BETWEEN 1122 AND 1138; INSERT INTO mangos_string (entry, content_default) VALUES - (7007,'Your group is too large for this battleground. Please regroup to join.'), - (7008,'Your group is too large for this arena. Please regroup to join.'), - (7009,'Your group has members not in your arena team. Please regroup to join.'), - (7010,'Your group does not have enough players to join this match.'), - (7011,'The Gold Team wins!'), - (7012,'The Green Team wins!'), - (7013, 'There aren\'t enough players in this battleground. It will end soon unless some more players join to balance the fight.'), - (7014, 'Your group has an offline member. Please remove him before joining.'), - (7015, 'Your group has players from the opposing faction. You can\'t join the battleground as a group.'), - (7016, 'Your group has players from different battleground brakets. You can\'t join as group.'), - (7017, 'Someone in your party is already in this battleground queue. (S)he must leave it before joining as group.'), - (7018, 'Someone in your party is Deserter. You can\'t join as group.'), - (7019, 'Someone in your party is already in three battleground queues. You cannot join as group.'), - (7020, 'You cannot teleport to a battleground or arena map.'), - (7021, 'You cannot summon players to a battleground or arena map.'), - (7022, 'You must be in GM mode to teleport to a player in a battleground.'), - (7023, 'You cannot teleport to a battleground from another battleground. Please leave the current battleground first.'); + (1122,'Your group is too large for this battleground. Please regroup to join.'), + (1123,'Your group is too large for this arena. Please regroup to join.'), + (1124,'Your group has members not in your arena team. Please regroup to join.'), + (1125,'Your group does not have enough players to join this match.'), + (1126,'The Gold Team wins!'), + (1127,'The Green Team wins!'), + (1128, 'There aren\'t enough players in this battleground. It will end soon unless some more players join to balance the fight.'), + (1129, 'Your group has an offline member. Please remove him before joining.'), + (1130, 'Your group has players from the opposing faction. You can\'t join the battleground as a group.'), + (1131, 'Your group has players from different battleground brakets. You can\'t join as group.'), + (1132, 'Someone in your party is already in this battleground queue. (S)he must leave it before joining as group.'), + (1133, 'Someone in your party is Deserter. You can\'t join as group.'), + (1134, 'Someone in your party is already in three battleground queues. You cannot join as group.'), + (1135, 'You cannot teleport to a battleground or arena map.'), + (1136, 'You cannot summon players to a battleground or arena map.'), + (1127, 'You must be in GM mode to teleport to a player in a battleground.'), + (1138, 'You cannot teleport to a battleground from another battleground. Please leave the current battleground first.'); DELETE FROM mangos_string WHERE entry = 714 OR entry = 716; diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index c4ca950f1..6cebe667c 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -141,6 +141,8 @@ pkgdata_DATA = \ 2008_11_29_01_mangos_spell_proc_event.sql \ 2008_11_29_02_mangos_spell_elixir.sql \ 2008_12_03_01_character_guild_member.sql \ + 2008_12_15_01_character_arenas.sql \ + 2008_12_15_01_mangos_arenas.sql \ README ## Additional files to include when running 'make dist' @@ -263,4 +265,6 @@ EXTRA_DIST = \ 2008_11_29_01_mangos_spell_proc_event.sql \ 2008_11_29_02_mangos_spell_elixir.sql \ 2008_12_03_01_character_guild_member.sql \ + 2008_12_15_01_character_arenas.sql \ + 2008_12_15_01_mangos_arenas.sql \ README diff --git a/src/game/Language.h b/src/game/Language.h index c51cf0f3e..b82d2ed3b 100644 --- a/src/game/Language.h +++ b/src/game/Language.h @@ -647,24 +647,24 @@ enum MangosStrings LANG_YOUR_BG_LEVEL_REQ_ERROR = 715, // LANG_YOUR_ARENA_TEAM_FULL = 716, an opcode exists for this - LANG_BG_GROUP_TOO_LARGE = 7007, // "Your group is too large for this battleground. Please regroup to join." - LANG_ARENA_GROUP_TOO_LARGE = 7008, // "Your group is too large for this arena. Please regroup to join." - LANG_ARENA_YOUR_TEAM_ONLY = 7009, // "Your group has members not in your arena team. Please regroup to join." - LANG_ARENA_NOT_ENOUGH_PLAYERS = 7010, // "Your group does not have enough players to join this match." - LANG_ARENA_GOLD_WINS = 7011, // "The Gold Team wins!" - LANG_ARENA_GREEN_WINS = 7012, // "The Green Team wins!" - LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING = 7013, // The battleground will end soon, because there aren't enough players. Get more ppl or win already! - LANG_BG_GROUP_OFFLINE_MEMBER = 7014, // "Your group has an offline member. Please remove him before joining." - LANG_BG_GROUP_MIXED_FACTION = 7015, // "Your group has players from the opposing faction. You can't join the battleground as a group." - LANG_BG_GROUP_MIXED_LEVELS = 7016, // "Your group has players from different battleground brakets. You can't join as group." - LANG_BG_GROUP_MEMBER_ALREADY_IN_QUEUE = 7017, // "Someone in your party is already in this battleground queue. (S)he must leave it before joining as group." - LANG_BG_GROUP_MEMBER_DESERTER = 7018, // "Someone in your party is Deserter. You can't join as group." - LANG_BG_GROUP_MEMBER_NO_FREE_QUEUE_SLOTS = 7019, // "Someone in your party is already in three battleground queues. You cannot join as group." + LANG_BG_GROUP_TOO_LARGE = 1122, // "Your group is too large for this battleground. Please regroup to join." + LANG_ARENA_GROUP_TOO_LARGE = 1123, // "Your group is too large for this arena. Please regroup to join." + LANG_ARENA_YOUR_TEAM_ONLY = 1124, // "Your group has members not in your arena team. Please regroup to join." + LANG_ARENA_NOT_ENOUGH_PLAYERS = 1125, // "Your group does not have enough players to join this match." + LANG_ARENA_GOLD_WINS = 1126, // "The Gold Team wins!" + LANG_ARENA_GREEN_WINS = 1127, // "The Green Team wins!" + LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING = 1128, // The battleground will end soon, because there aren't enough players. Get more ppl or win already! + LANG_BG_GROUP_OFFLINE_MEMBER = 1129, // "Your group has an offline member. Please remove him before joining." + LANG_BG_GROUP_MIXED_FACTION = 1130, // "Your group has players from the opposing faction. You can't join the battleground as a group." + LANG_BG_GROUP_MIXED_LEVELS = 1131, // "Your group has players from different battleground brakets. You can't join as group." + LANG_BG_GROUP_MEMBER_ALREADY_IN_QUEUE = 1132, // "Someone in your party is already in this battleground queue. (S)he must leave it before joining as group." + LANG_BG_GROUP_MEMBER_DESERTER = 1133, // "Someone in your party is Deserter. You can't join as group." + LANG_BG_GROUP_MEMBER_NO_FREE_QUEUE_SLOTS = 1134, // "Someone in your party is already in three battleground queues. You cannot join as group." - LANG_CANNOT_TELE_TO_BG = 7020, // "You cannot teleport to a battleground or arena map." - LANG_CANNOT_SUMMON_TO_BG = 7021, // "You cannot summon players to a battleground or arena map." - LANG_CANNOT_GO_TO_BG_GM = 7022, // "You must be in GM mode to teleport to a player in a battleground." - LANG_CANNOT_GO_TO_BG_FROM_BG = 7023, // "You cannot teleport to a battleground from another battleground. Please leave the current battleground first." + LANG_CANNOT_TELE_TO_BG = 1135, // "You cannot teleport to a battleground or arena map." + LANG_CANNOT_SUMMON_TO_BG = 1136, // "You cannot summon players to a battleground or arena map." + LANG_CANNOT_GO_TO_BG_GM = 1137, // "You must be in GM mode to teleport to a player in a battleground." + LANG_CANNOT_GO_TO_BG_FROM_BG = 1138, // "You cannot teleport to a battleground from another battleground. Please leave the current battleground first." // in game strings // = 800, not used From d34842f7865b07bb57c749335d9822324683441f Mon Sep 17 00:00:00 2001 From: ApoC Date: Tue, 16 Dec 2008 02:14:48 +0100 Subject: [PATCH 7/9] Typo fix. Signed-off-by: ApoC --- sql/updates/2008_12_15_01_mangos_arenas.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/updates/2008_12_15_01_mangos_arenas.sql b/sql/updates/2008_12_15_01_mangos_arenas.sql index 0278724a5..589aacfb6 100644 --- a/sql/updates/2008_12_15_01_mangos_arenas.sql +++ b/sql/updates/2008_12_15_01_mangos_arenas.sql @@ -20,7 +20,7 @@ INSERT INTO mangos_string (entry, content_default) VALUES (1134, 'Someone in your party is already in three battleground queues. You cannot join as group.'), (1135, 'You cannot teleport to a battleground or arena map.'), (1136, 'You cannot summon players to a battleground or arena map.'), - (1127, 'You must be in GM mode to teleport to a player in a battleground.'), + (1137, 'You must be in GM mode to teleport to a player in a battleground.'), (1138, 'You cannot teleport to a battleground from another battleground. Please leave the current battleground first.'); DELETE FROM mangos_string WHERE entry = 714 OR entry = 716; From d43bd7efe9c2abf4955d2765980caec04fbf5949 Mon Sep 17 00:00:00 2001 From: ApoC Date: Tue, 16 Dec 2008 02:20:45 +0100 Subject: [PATCH 8/9] Added missing alters to db_version. Signed-off-by: ApoC --- sql/characters.sql | 2 +- sql/mangos.sql | 2 +- sql/updates/2008_12_15_01_character_arenas.sql | 2 ++ sql/updates/2008_12_15_01_mangos_arenas.sql | 2 ++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sql/characters.sql b/sql/characters.sql index c5fd2a6bd..fc4c3e95a 100644 --- a/sql/characters.sql +++ b/sql/characters.sql @@ -30,7 +30,7 @@ CREATE TABLE `saved_variables` ( DROP TABLE IF EXISTS `character_db_version`; CREATE TABLE `character_db_version` ( - `required_2008_12_03_01_character_guild_member` bit(1) default NULL + `required_2008_12_15_01_character_arenas` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB'; -- diff --git a/sql/mangos.sql b/sql/mangos.sql index ab1d64f9b..081d18fbd 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -22,7 +22,7 @@ DROP TABLE IF EXISTS `db_version`; CREATE TABLE `db_version` ( `version` varchar(120) default NULL, - `required_2008_11_29_02_mangos_spell_elixir` bit(1) default NULL + `required_2008_12_15_01_mangos_arenas` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- diff --git a/sql/updates/2008_12_15_01_character_arenas.sql b/sql/updates/2008_12_15_01_character_arenas.sql index 2976d343e..5fdf0870f 100644 --- a/sql/updates/2008_12_15_01_character_arenas.sql +++ b/sql/updates/2008_12_15_01_character_arenas.sql @@ -1,3 +1,5 @@ +ALTER TABLE character_db_version CHANGE COLUMN required_2008_12_03_01_character_guild_member required_2008_12_15_01_character_arenas bit; + CREATE TABLE `saved_variables` ( `NextArenaPointDistributionTime` bigint(40) UNSIGNED NOT NULL DEFAULT '0' ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Variable Saves'; diff --git a/sql/updates/2008_12_15_01_mangos_arenas.sql b/sql/updates/2008_12_15_01_mangos_arenas.sql index 589aacfb6..c8208b844 100644 --- a/sql/updates/2008_12_15_01_mangos_arenas.sql +++ b/sql/updates/2008_12_15_01_mangos_arenas.sql @@ -1,3 +1,5 @@ +ALTER TABLE db_version CHANGE COLUMN required_2008_11_29_02_mangos_spell_elixir required_2008_12_15_01_mangos_arenas bit; + DELETE FROM `command` WHERE `name` = "flusharenapoints"; INSERT INTO `command` (`name`, `security`, `help`) VALUES ('flusharenapoints','3','Syntax: .flusharenapoints\r\n\r\nUse it to distribute arena points based on arena team ratings, and start a new week.'); From 98281cc3898494a58288c35fa8abcabcbbeba76f Mon Sep 17 00:00:00 2001 From: ApoC Date: Tue, 16 Dec 2008 05:15:39 +0100 Subject: [PATCH 9/9] [6913] Fixed guild creation after adding stronger checks. Fixed _return_ typo in guild charter sign code. Merged some sql queries into one. Signed-off-by: ApoC --- src/game/PetitionsHandler.cpp | 60 +++++++++++++++-------------------- src/shared/revision_nr.h | 2 +- 2 files changed, 26 insertions(+), 36 deletions(-) diff --git a/src/game/PetitionsHandler.cpp b/src/game/PetitionsHandler.cpp index 857143749..e41036626 100644 --- a/src/game/PetitionsHandler.cpp +++ b/src/game/PetitionsHandler.cpp @@ -250,15 +250,16 @@ void WorldSession::HandlePetitionShowSignOpcode(WorldPacket & recv_data) // 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, type FROM petition WHERE petitionguid = '%u'", petitionguid_low); + QueryResult *result = CharacterDatabase.PQuery("SELECT 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(); + uint32 type = fields[0].GetUInt32(); delete result; + // if guild petition and has guild => error, return; if(type==9 && _player->GetGuildId()) return; @@ -317,6 +318,7 @@ void WorldSession::SendPetitionQueryOpcode(uint64 petitionguid) QueryResult *result = CharacterDatabase.PQuery( "SELECT ownerguid, name, " " (SELECT COUNT(playerguid) FROM petition_sign WHERE petition_sign.petitionguid = '%u') AS signs " + "type " "FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid), GUID_LOPART(petitionguid)); if(result) @@ -325,6 +327,7 @@ void WorldSession::SendPetitionQueryOpcode(uint64 petitionguid) ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER); name = fields[1].GetCppString(); signs = fields[2].GetUInt8(); + type = fields[3].GetUInt32(); delete result; } else @@ -333,20 +336,6 @@ void WorldSession::SendPetitionQueryOpcode(uint64 petitionguid) return; } - QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); - - if(result2) - { - Field* fields = result2->Fetch(); - type = fields[0].GetUInt32(); - delete result2; - } - else - { - sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid)); - return; - } - WorldPacket data(SMSG_PETITION_QUERY_RESPONSE, (4+8+name.size()+1+1+4*13)); data << GUID_LOPART(petitionguid); // guild/team guid (in mangos always same as GUID_LOPART(petition guid) data << ownerguid; // charter owner guid @@ -398,13 +387,13 @@ void WorldSession::HandlePetitionRenameOpcode(WorldPacket & recv_data) if(!item) return; - QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); + QueryResult *result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); - if(result2) + if(result) { - Field* fields = result2->Fetch(); + Field* fields = result->Fetch(); type = fields[0].GetUInt32(); - delete result2; + delete result; } else { @@ -549,11 +538,9 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data) SendGuildCommandResult(GUILD_INVITE_S, _player->GetName(), ALREADY_INVITED_TO_GUILD); return; } - return; } - signs += 1; - if(signs > type) // client signs maximum + if(++signs > type) // client signs maximum return; //client doesn't allow to sign petition two times by one character, but not check sign by another character from same account @@ -637,14 +624,26 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data) uint8 signs = 0; uint64 petitionguid, plguid; - uint32 type; + uint32 type, junk; Player *player; - recv_data >> type; + recv_data >> junk; // this is not petition type! recv_data >> petitionguid; // petition guid recv_data >> plguid; // player guid - sLog.outDebug("OFFER PETITION: type %u, GUID1 %u, to player id: %u", type, GUID_LOPART(petitionguid), GUID_LOPART(plguid)); player = ObjectAccessor::FindPlayer(plguid); + if (!player) + return; + + QueryResult *result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); + if (!result) + return; + + Field *fields = result->Fetch(); + type = fields[0].GetUInt32(); + delete result; + + sLog.outDebug("OFFER PETITION: type %u, GUID1 %u, to player id: %u", type, GUID_LOPART(petitionguid), GUID_LOPART(plguid)); + if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != player->GetTeam() ) { if(type != 9) @@ -695,15 +694,6 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data) } } - QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); - if(!result) - { - sLog.outError("any petition on server..."); - return; - } - - delete result; - result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); // result==NULL also correct charter without signs if(result) diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 0ec0c3ee2..b1580de0a 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "6912" + #define REVISION_NR "6913" #endif // __REVISION_NR_H__