diff --git a/sql/characters.sql b/sql/characters.sql index 0dc980ebb..d18ac6132 100644 --- a/sql/characters.sql +++ b/sql/characters.sql @@ -15,13 +15,22 @@ /*!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` -- 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'; -- @@ -95,7 +104,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; -- @@ -223,6 +233,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 d64e0c5d0..a10084869 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'; -- @@ -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_character_arenas.sql b/sql/updates/2008_12_15_01_character_arenas.sql new file mode 100644 index 000000000..5fdf0870f --- /dev/null +++ b/sql/updates/2008_12_15_01_character_arenas.sql @@ -0,0 +1,8 @@ +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'; + +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..c8208b844 --- /dev/null +++ b/sql/updates/2008_12_15_01_mangos_arenas.sql @@ -0,0 +1,28 @@ +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.'); + +DELETE FROM mangos_string WHERE entry BETWEEN 1122 AND 1138; + +INSERT INTO mangos_string (entry, content_default) VALUES + (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.'), + (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; 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/ArenaTeam.cpp b/src/game/ArenaTeam.cpp index 72ed8d679..5f1dfdb93 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,21 @@ 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 true; } @@ -209,49 +208,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 +250,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 +264,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 +286,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 +312,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 +357,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 +382,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 +432,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 +452,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 +647,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 6143c2f72..ebb95536e 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,11 +552,29 @@ 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); plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1); } + 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); @@ -559,12 +725,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); @@ -578,6 +748,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); @@ -590,66 +764,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 @@ -661,6 +875,8 @@ void BattleGround::Reset() SetStartTime(0); SetEndTime(0); SetLastResurrectTime(0); + SetArenaType(0); + SetRated(false); m_Events = 0; @@ -669,6 +885,7 @@ void BattleGround::Reset() m_InvitedAlliance = 0; m_InvitedHorde = 0; + m_InBGFreeSlotQueue = false; m_Players.clear(); m_PlayerScores.clear(); @@ -705,10 +922,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 { @@ -727,6 +945,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); @@ -741,9 +972,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()); } @@ -751,13 +979,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) @@ -765,30 +1000,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(); @@ -814,9 +1088,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 @@ -872,15 +1150,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; @@ -894,13 +1183,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; } @@ -942,6 +1231,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]); @@ -950,30 +1242,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)) @@ -997,9 +1286,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]); @@ -1080,8 +1399,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 @@ -1159,3 +1481,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 f1afbca3f..1e92dfaea 100644 --- a/src/game/BattleGround.h +++ b/src/game/BattleGround.h @@ -83,7 +83,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 @@ -144,6 +144,18 @@ enum BattleGroundTypeId BATTLEGROUND_RV = 11 }; +// 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, @@ -196,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: @@ -225,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 @@ -297,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; } @@ -367,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) @@ -375,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*/) {} @@ -391,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 @@ -403,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); @@ -412,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(); @@ -444,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 @@ -451,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 */ @@ -469,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 58bd4575b..ce456f6d1 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 @@ -625,23 +1325,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 @@ -661,12 +1458,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 @@ -676,7 +1469,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); @@ -685,12 +1478,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() @@ -810,6 +1605,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; @@ -845,18 +1711,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) @@ -868,3 +1741,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 981468dd2..2998efed8 100644 --- a/src/game/BattleGroundMgr.h +++ b/src/game/BattleGroundMgr.h @@ -34,51 +34,99 @@ typedef std::deque BGFreeSlotQueueType; #define MAX_BATTLEGROUND_TYPES 12 // 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 a77d17998..ed65d37e3 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)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS, "SELECT achievement, date FROM character_achievement WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS,"SELECT criteria, counter, date FROM character_achievement_progress WHERE guid = '%u'", GUID_LOPART(m_guid)); @@ -438,7 +440,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 dba83eb46..5013e3605 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -198,6 +198,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 } }; @@ -563,6 +564,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 35c417d97..804e3c9bd 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -417,6 +417,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); @@ -431,6 +432,7 @@ class ChatHandler bool HandleSaveAllCommand(const char* args); bool HandleGetItemState(const char * args); bool HandleGetLootRecipient(const char * args); + bool HandleDebugArenaCommand(const char * args); bool HandleSpawnVehicle(const char * args); Player* getSelectedPlayer(); 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 3f893e31b..94fbb9d72 100644 --- a/src/game/Group.h +++ b/src/game/Group.h @@ -248,6 +248,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 7b30279c9..0c2096820 100644 --- a/src/game/Language.h +++ b/src/game/Language.h @@ -643,11 +643,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 = 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 = 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 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 32ab12bfa..09af98247 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" @@ -6490,6 +6491,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 626087397..3c89d20e9 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 01b217f1c..8d34e7ec7 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()); } } } @@ -325,20 +331,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 6c2775218..33137fdae 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -138,13 +138,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; } @@ -152,7 +152,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; } @@ -227,33 +227,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 ) @@ -4256,7 +4266,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 68d2346db..825237bfc 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -284,11 +284,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; @@ -320,11 +320,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 ); @@ -808,7 +810,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 3c368c30b..26caec61e 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; @@ -260,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; @@ -327,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) @@ -335,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 @@ -343,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 @@ -408,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 { @@ -429,12 +408,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 +421,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 +480,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,15 +502,45 @@ 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)); - return; + 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; + } } - 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 @@ -619,29 +624,75 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data) uint8 signs = 0; uint64 petitionguid, plguid; - uint32 petitiontype; + uint32 type, junk; Player *player; - recv_data >> petitiontype; // 2.0.8 - petition 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", petitiontype, GUID_LOPART(petitionguid), GUID_LOPART(plguid)); player = ObjectAccessor::FindPlayer(plguid); - if(!player || player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow())) + if (!player) return; - // not let offer to enemies + 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() ) - return; - - QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); - if(!result) { - sLog.outError("any petition on server..."); + if(type != 9) + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED); + else + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_NOT_ALLIED); return; } - delete result; + 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; + } + } result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); // result==NULL also correct charter without signs @@ -811,7 +862,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 ead650591..a37a7634a 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -354,8 +354,8 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this) 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; @@ -713,12 +713,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) @@ -758,24 +758,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) ); @@ -1490,7 +1496,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 @@ -3502,6 +3509,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) @@ -7807,19 +7837,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; @@ -9633,10 +9678,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 ); @@ -10309,12 +10354,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; @@ -13667,6 +13712,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)); @@ -13738,8 +13813,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) @@ -13829,6 +13904,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) { @@ -15151,8 +15234,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; @@ -15190,7 +15275,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 << "', " @@ -15289,7 +15374,7 @@ void Player::SaveToDB() ss << ", '"; ss << m_taxi.SaveTaxiDestinationsToString(); - ss << "' )"; + ss << "', '0' )"; CharacterDatabase.Execute( ss.str().c_str() ); @@ -16992,8 +17077,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 ); @@ -17015,7 +17106,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); @@ -18018,7 +18109,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; @@ -18056,6 +18148,11 @@ uint32 Player::GetBattleGroundQueueIdFromLevel() const return 7; 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 3380c7fa7..ba9c251c1 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -869,9 +869,10 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS = 15, PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES = 16, PLAYER_LOGIN_QUERY_LOADGUILD = 17, - PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS = 18, - PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS = 19, - MAX_PLAYER_LOGIN_QUERY = 20 + PLAYER_LOGIN_QUERY_LOADARENAINFO = 18, + PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS = 19, + PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS = 20, + MAX_PLAYER_LOGIN_QUERY = 21 }; @@ -1117,7 +1118,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; @@ -1128,10 +1129,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; @@ -1591,7 +1592,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); @@ -1880,24 +1880,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; } @@ -1905,34 +1913,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; } @@ -2146,8 +2167,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; @@ -2192,6 +2213,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 a95af3fcb..fb6fe333a 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -2213,7 +2213,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 044fd8970..09236f84e 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -2653,7 +2653,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 43d993b2a..767808962 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -606,25 +606,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 @@ -772,6 +753,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) { @@ -4182,6 +4176,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 b92f2b37f..b447d3b1c 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -1045,6 +1045,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 8da99d209..d7ed8de6b 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -912,6 +912,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); @@ -1322,6 +1328,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..." ); @@ -2205,7 +2212,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() && @@ -2272,11 +2279,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 7af514469..ea2d67892 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -177,7 +177,16 @@ 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_SKILL_MILLING, + CONFIG_ARENA_RATING_DISCARD_TIMER, + CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS, + CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS, + CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER, CONFIG_VALUE_COUNT }; @@ -399,6 +408,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 21e8cf553..0fb31d75c 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -195,11 +195,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 41d74b2d8..a67ef3203 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" @@ -520,6 +521,12 @@ bool ChatHandler::HandleGetItemState(const char* args) return true; } +bool ChatHandler::HandleDebugArenaCommand(const char * /*args*/) +{ + sBattleGroundMgr.ToggleArenaTesting(); + return true; +} + bool ChatHandler::HandleSpawnVehicle(const char* args) { if(!args) diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index c42da5204..08d0dbc01 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -1087,6 +1087,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 cf08161c3..839687f3d 100644 --- a/src/shared/Database/DBCStructure.h +++ b/src/shared/Database/DBCStructure.h @@ -921,9 +921,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 2d7234568..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 "6908" + #define REVISION_NR "6913" #endif // __REVISION_NR_H__