Updated Copyright year to 2013
[getmangos.git] / src / game / BattleGround / BattleGround.cpp
blobf45e7d253341ed452fc29ec296ec4ff031b2ea6d
1 /*
2 * Copyright (C) 2005-2013 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "Object.h"
20 #include "Player.h"
21 #include "BattleGround.h"
22 #include "BattleGroundMgr.h"
23 #include "Creature.h"
24 #include "MapManager.h"
25 #include "Language.h"
26 #include "SpellAuras.h"
27 #include "ArenaTeam.h"
28 #include "World.h"
29 #include "Group.h"
30 #include "ObjectGuid.h"
31 #include "ObjectMgr.h"
32 #include "Mail.h"
33 #include "WorldPacket.h"
34 #include "Util.h"
35 #include "Formulas.h"
36 #include "GridNotifiersImpl.h"
38 namespace MaNGOS
40 class BattleGroundChatBuilder
42 public:
43 BattleGroundChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, va_list* args = NULL)
44 : i_msgtype(msgtype), i_textId(textId), i_source(source), i_args(args) {}
45 void operator()(WorldPacket& data, int32 loc_idx)
47 char const* text = sObjectMgr.GetMangosString(i_textId, loc_idx);
49 if (i_args)
51 // we need copy va_list before use or original va_list will corrupted
52 va_list ap;
53 va_copy(ap, *i_args);
55 char str [2048];
56 vsnprintf(str, 2048, text, ap);
57 va_end(ap);
59 do_helper(data, &str[0]);
61 else
62 do_helper(data, text);
64 private:
65 void do_helper(WorldPacket& data, char const* text)
67 ObjectGuid targetGuid = i_source ? i_source ->GetObjectGuid() : ObjectGuid();
69 data << uint8(i_msgtype);
70 data << uint32(LANG_UNIVERSAL);
71 data << ObjectGuid(targetGuid); // there 0 for BG messages
72 data << uint32(0); // can be chat msg group or something
73 data << ObjectGuid(targetGuid);
74 data << uint32(strlen(text) + 1);
75 data << text;
76 data << uint8(i_source ? i_source->GetChatTag() : uint8(CHAT_TAG_NONE));
79 ChatMsg i_msgtype;
80 int32 i_textId;
81 Player const* i_source;
82 va_list* i_args;
85 class BattleGroundYellBuilder
87 public:
88 BattleGroundYellBuilder(uint32 language, int32 textId, Creature const* source, va_list* args = NULL)
89 : i_language(language), i_textId(textId), i_source(source), i_args(args) {}
90 void operator()(WorldPacket& data, int32 loc_idx)
92 char const* text = sObjectMgr.GetMangosString(i_textId, loc_idx);
94 if (i_args)
96 // we need copy va_list before use or original va_list will corrupted
97 va_list ap;
98 va_copy(ap, *i_args);
100 char str [2048];
101 vsnprintf(str, 2048, text, ap);
102 va_end(ap);
104 do_helper(data, &str[0]);
106 else
107 do_helper(data, text);
109 private:
110 void do_helper(WorldPacket& data, char const* text)
112 // copyied from BuildMonsterChat
113 data << uint8(CHAT_MSG_MONSTER_YELL);
114 data << uint32(i_language);
115 data << ObjectGuid(i_source->GetObjectGuid());
116 data << uint32(0); // 2.1.0
117 data << uint32(strlen(i_source->GetName()) + 1);
118 data << i_source->GetName();
119 data << ObjectGuid(); // Unit Target - isn't important for bgs
120 data << uint32(strlen(text) + 1);
121 data << text;
122 data << uint8(0); // ChatTag - for bgs allways 0?
125 uint32 i_language;
126 int32 i_textId;
127 Creature const* i_source;
128 va_list* i_args;
132 class BattleGround2ChatBuilder
134 public:
135 BattleGround2ChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, int32 arg1, int32 arg2)
136 : i_msgtype(msgtype), i_textId(textId), i_source(source), i_arg1(arg1), i_arg2(arg2) {}
137 void operator()(WorldPacket& data, int32 loc_idx)
139 char const* text = sObjectMgr.GetMangosString(i_textId, loc_idx);
140 char const* arg1str = i_arg1 ? sObjectMgr.GetMangosString(i_arg1, loc_idx) : "";
141 char const* arg2str = i_arg2 ? sObjectMgr.GetMangosString(i_arg2, loc_idx) : "";
143 char str [2048];
144 snprintf(str, 2048, text, arg1str, arg2str);
146 ObjectGuid targetGuid = i_source ? i_source ->GetObjectGuid() : ObjectGuid();
148 data << uint8(i_msgtype);
149 data << uint32(LANG_UNIVERSAL);
150 data << ObjectGuid(targetGuid); // there 0 for BG messages
151 data << uint32(0); // can be chat msg group or something
152 data << ObjectGuid(targetGuid);
153 data << uint32(strlen(str) + 1);
154 data << str;
155 data << uint8(i_source ? i_source->GetChatTag() : uint8(CHAT_TAG_NONE));
157 private:
159 ChatMsg i_msgtype;
160 int32 i_textId;
161 Player const* i_source;
162 int32 i_arg1;
163 int32 i_arg2;
166 class BattleGround2YellBuilder
168 public:
169 BattleGround2YellBuilder(uint32 language, int32 textId, Creature const* source, int32 arg1, int32 arg2)
170 : i_language(language), i_textId(textId), i_source(source), i_arg1(arg1), i_arg2(arg2) {}
171 void operator()(WorldPacket& data, int32 loc_idx)
173 char const* text = sObjectMgr.GetMangosString(i_textId, loc_idx);
174 char const* arg1str = i_arg1 ? sObjectMgr.GetMangosString(i_arg1, loc_idx) : "";
175 char const* arg2str = i_arg2 ? sObjectMgr.GetMangosString(i_arg2, loc_idx) : "";
177 char str [2048];
178 snprintf(str, 2048, text, arg1str, arg2str);
179 // copyied from BuildMonsterChat
180 data << uint8(CHAT_MSG_MONSTER_YELL);
181 data << uint32(i_language);
182 data << ObjectGuid(i_source->GetObjectGuid());
183 data << uint32(0); // 2.1.0
184 data << uint32(strlen(i_source->GetName()) + 1);
185 data << i_source->GetName();
186 data << ObjectGuid(); // Unit Target - isn't important for bgs
187 data << uint32(strlen(str) + 1);
188 data << str;
189 data << uint8(0); // ChatTag - for bgs allways 0?
191 private:
193 uint32 i_language;
194 int32 i_textId;
195 Creature const* i_source;
196 int32 i_arg1;
197 int32 i_arg2;
199 } // namespace MaNGOS
201 template<class Do>
202 void BattleGround::BroadcastWorker(Do& _do)
204 for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
205 if (Player* plr = ObjectAccessor::FindPlayer(itr->first))
206 _do(plr);
209 BattleGround::BattleGround()
211 m_TypeID = BattleGroundTypeId(0);
212 m_Status = STATUS_NONE;
213 m_ClientInstanceID = 0;
214 m_EndTime = 0;
215 m_BracketId = BG_BRACKET_ID_TEMPLATE;
216 m_InvitedAlliance = 0;
217 m_InvitedHorde = 0;
218 m_ArenaType = ARENA_TYPE_NONE;
219 m_IsArena = false;
220 m_Winner = TEAM_NONE;
221 m_StartTime = 0;
222 m_Events = 0;
223 m_IsRated = false;
224 m_BuffChange = false;
225 m_Name = "";
226 m_LevelMin = 0;
227 m_LevelMax = 0;
228 m_InBGFreeSlotQueue = false;
230 m_MaxPlayersPerTeam = 0;
231 m_MaxPlayers = 0;
232 m_MinPlayersPerTeam = 0;
233 m_MinPlayers = 0;
235 m_MapId = 0;
236 m_Map = NULL;
238 m_TeamStartLocX[BG_TEAM_ALLIANCE] = 0;
239 m_TeamStartLocX[BG_TEAM_HORDE] = 0;
241 m_TeamStartLocY[BG_TEAM_ALLIANCE] = 0;
242 m_TeamStartLocY[BG_TEAM_HORDE] = 0;
244 m_TeamStartLocZ[BG_TEAM_ALLIANCE] = 0;
245 m_TeamStartLocZ[BG_TEAM_HORDE] = 0;
247 m_TeamStartLocO[BG_TEAM_ALLIANCE] = 0;
248 m_TeamStartLocO[BG_TEAM_HORDE] = 0;
250 m_ArenaTeamIds[BG_TEAM_ALLIANCE] = 0;
251 m_ArenaTeamIds[BG_TEAM_HORDE] = 0;
253 m_ArenaTeamRatingChanges[BG_TEAM_ALLIANCE] = 0;
254 m_ArenaTeamRatingChanges[BG_TEAM_HORDE] = 0;
256 m_BgRaids[BG_TEAM_ALLIANCE] = NULL;
257 m_BgRaids[BG_TEAM_HORDE] = NULL;
259 m_PlayersCount[BG_TEAM_ALLIANCE] = 0;
260 m_PlayersCount[BG_TEAM_HORDE] = 0;
262 m_TeamScores[BG_TEAM_ALLIANCE] = 0;
263 m_TeamScores[BG_TEAM_HORDE] = 0;
265 m_PrematureCountDown = false;
266 m_PrematureCountDownTimer = 0;
268 m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_2M;
269 m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_1M;
270 m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_30S;
271 m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE;
272 // we must set to some default existing values
273 m_StartMessageIds[BG_STARTING_EVENT_FIRST] = 0;
274 m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE;
275 m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE;
276 m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN;
279 BattleGround::~BattleGround()
281 // remove objects and creatures
282 // (this is done automatically in mapmanager update, when the instance is reset after the reset time)
284 int size = m_BgObjects.size();
285 for (int i = 0; i < size; ++i)
286 DelObject(i);
288 sBattleGroundMgr.RemoveBattleGround(GetInstanceID(), GetTypeID());
290 // skip template bgs as they were never added to visible bg list
291 BattleGroundBracketId bracketId = GetBracketId();
292 if (bracketId != BG_BRACKET_ID_TEMPLATE)
293 sBattleGroundMgr.DeleteClientVisibleInstanceId(GetTypeID(), bracketId, GetClientInstanceID());
295 // unload map
296 // map can be null at bg destruction
297 if (m_Map)
298 m_Map->SetUnload();
300 // remove from bg free slot queue
301 this->RemoveFromBGFreeSlotQueue();
303 for (BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr)
304 delete itr->second;
307 void BattleGround::Update(uint32 diff)
309 if (!GetPlayersSize())
311 // BG is empty
312 // if there are no players invited, delete BG
313 // this will delete arena or bg object, where any player entered
314 // [[ but if you use battleground object again (more battles possible to be played on 1 instance)
315 // then this condition should be removed and code:
316 // if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
317 // this->AddToFreeBGObjectsQueue(); // not yet implemented
318 // should be used instead of current
319 // ]]
320 // BattleGround Template instance cannot be updated, because it would be deleted
321 if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
322 delete this;
324 return;
327 // remove offline players from bg after 5 minutes
328 if (!m_OfflineQueue.empty())
330 BattleGroundPlayerMap::iterator itr = m_Players.find(*(m_OfflineQueue.begin()));
331 if (itr != m_Players.end())
333 if (itr->second.OfflineRemoveTime <= sWorld.GetGameTime())
335 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
336 m_OfflineQueue.pop_front(); // remove from offline queue
337 // do not use itr for anything, because it is erased in RemovePlayerAtLeave()
342 /*********************************************************/
343 /*** BATTLEGROUND BALLANCE SYSTEM ***/
344 /*********************************************************/
346 // if less then minimum players are in on one side, then start premature finish timer
347 if (GetStatus() == STATUS_IN_PROGRESS && !isArena() && sBattleGroundMgr.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE) < GetMinPlayersPerTeam()))
349 if (!m_PrematureCountDown)
351 m_PrematureCountDown = true;
352 m_PrematureCountDownTimer = sBattleGroundMgr.GetPrematureFinishTime();
354 else if (m_PrematureCountDownTimer < diff)
356 // time's up!
357 Team winner = TEAM_NONE;
358 if (GetPlayersCountByTeam(ALLIANCE) >= GetMinPlayersPerTeam())
359 winner = ALLIANCE;
360 else if (GetPlayersCountByTeam(HORDE) >= GetMinPlayersPerTeam())
361 winner = HORDE;
363 EndBattleGround(winner);
364 m_PrematureCountDown = false;
366 else if (!sBattleGroundMgr.isTesting())
368 uint32 newtime = m_PrematureCountDownTimer - diff;
369 // announce every minute
370 if (newtime > (MINUTE * IN_MILLISECONDS))
372 if (newtime / (MINUTE * IN_MILLISECONDS) != m_PrematureCountDownTimer / (MINUTE * IN_MILLISECONDS))
373 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / (MINUTE * IN_MILLISECONDS)));
375 else
377 // announce every 15 seconds
378 if (newtime / (15 * IN_MILLISECONDS) != m_PrematureCountDownTimer / (15 * IN_MILLISECONDS))
379 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / IN_MILLISECONDS));
381 m_PrematureCountDownTimer = newtime;
385 else if (m_PrematureCountDown)
386 m_PrematureCountDown = false;
388 /*********************************************************/
389 /*** ARENA BUFF OBJECT SPAWNING ***/
390 /*********************************************************/
391 if (isArena() && !m_ArenaBuffSpawned)
393 // 60 seconds after start the buffobjects in arena should get spawned
394 if (m_StartTime > uint32(m_StartDelayTimes[BG_STARTING_EVENT_FIRST] + ARENA_SPAWN_BUFF_OBJECTS))
396 SpawnEvent(ARENA_BUFF_EVENT, 0, true);
397 m_ArenaBuffSpawned = true;
401 /*********************************************************/
402 /*** BATTLEGROUND STARTING SYSTEM ***/
403 /*********************************************************/
405 if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize())
407 ModifyStartDelayTime(diff);
409 if (!(m_Events & BG_STARTING_EVENT_1))
411 m_Events |= BG_STARTING_EVENT_1;
413 // setup here, only when at least one player has ported to the map
414 if (!SetupBattleGround())
416 EndNow();
417 return;
420 StartingEventCloseDoors();
421 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FIRST]);
422 // first start warning - 2 or 1 minute, only if defined
423 if (m_StartMessageIds[BG_STARTING_EVENT_FIRST])
424 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FIRST], CHAT_MSG_BG_SYSTEM_NEUTRAL);
426 // After 1 minute or 30 seconds, warning is signalled
427 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_SECOND] && !(m_Events & BG_STARTING_EVENT_2))
429 m_Events |= BG_STARTING_EVENT_2;
430 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_SECOND], CHAT_MSG_BG_SYSTEM_NEUTRAL);
432 // After 30 or 15 seconds, warning is signalled
433 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_THIRD] && !(m_Events & BG_STARTING_EVENT_3))
435 m_Events |= BG_STARTING_EVENT_3;
436 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_THIRD], CHAT_MSG_BG_SYSTEM_NEUTRAL);
438 // delay expired (atfer 2 or 1 minute)
439 else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4))
441 m_Events |= BG_STARTING_EVENT_4;
443 StartingEventOpenDoors();
445 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FOURTH], CHAT_MSG_BG_SYSTEM_NEUTRAL);
446 SetStatus(STATUS_IN_PROGRESS);
447 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FOURTH]);
449 // remove preparation
450 if (isArena())
452 for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
454 if (Player* player = sObjectMgr.GetPlayer(itr->first))
455 player->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION);
458 CheckArenaWinConditions();
460 else
463 PlaySoundToAll(SOUND_BG_START);
465 for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
466 if (Player* plr = sObjectMgr.GetPlayer(itr->first))
467 plr->RemoveAurasDueToSpell(SPELL_PREPARATION);
468 // Announce BG starting
469 if (sWorld.getConfig(CONFIG_BOOL_BATTLEGROUND_QUEUE_ANNOUNCER_START))
471 sWorld.SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD, GetName(), GetMinLevel(), GetMaxLevel());
477 /*********************************************************/
478 /*** BATTLEGROUND ENDING SYSTEM ***/
479 /*********************************************************/
481 if (GetStatus() == STATUS_WAIT_LEAVE)
483 // remove all players from battleground after 2 minutes
484 m_EndTime -= diff;
485 if (m_EndTime <= 0)
487 m_EndTime = 0;
488 BattleGroundPlayerMap::iterator itr, next;
489 for (itr = m_Players.begin(); itr != m_Players.end(); itr = next)
491 next = itr;
492 ++next;
493 // itr is erased here!
494 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
495 // do not change any battleground's private variables
500 // update start time
501 m_StartTime += diff;
504 void BattleGround::SetTeamStartLoc(Team team, float X, float Y, float Z, float O)
506 BattleGroundTeamIndex teamIdx = GetTeamIndexByTeamId(team);
507 m_TeamStartLocX[teamIdx] = X;
508 m_TeamStartLocY[teamIdx] = Y;
509 m_TeamStartLocZ[teamIdx] = Z;
510 m_TeamStartLocO[teamIdx] = O;
513 void BattleGround::SendPacketToAll(WorldPacket* packet)
515 for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
517 if (itr->second.OfflineRemoveTime)
518 continue;
520 if (Player* plr = sObjectMgr.GetPlayer(itr->first))
521 plr->GetSession()->SendPacket(packet);
522 else
523 sLog.outError("BattleGround:SendPacketToAll: %s not found!", itr->first.GetString().c_str());
527 void BattleGround::SendPacketToTeam(Team teamId, WorldPacket* packet, Player* sender, bool self)
529 for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
531 if (itr->second.OfflineRemoveTime)
532 continue;
534 Player* plr = sObjectMgr.GetPlayer(itr->first);
535 if (!plr)
537 sLog.outError("BattleGround:SendPacketToTeam: %s not found!", itr->first.GetString().c_str());
538 continue;
541 if (!self && sender == plr)
542 continue;
544 Team team = itr->second.PlayerTeam;
545 if (!team) team = plr->GetTeam();
547 if (team == teamId)
548 plr->GetSession()->SendPacket(packet);
552 void BattleGround::PlaySoundToAll(uint32 SoundID)
554 WorldPacket data;
555 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
556 SendPacketToAll(&data);
559 void BattleGround::PlaySoundToTeam(uint32 SoundID, Team teamId)
561 WorldPacket data;
563 for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
565 if (itr->second.OfflineRemoveTime)
566 continue;
568 Player* plr = sObjectMgr.GetPlayer(itr->first);
569 if (!plr)
571 sLog.outError("BattleGround:PlaySoundToTeam: %s not found!", itr->first.GetString().c_str());
572 continue;
575 Team team = itr->second.PlayerTeam;
576 if (!team) team = plr->GetTeam();
578 if (team == teamId)
580 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
581 plr->GetSession()->SendPacket(&data);
586 void BattleGround::CastSpellOnTeam(uint32 SpellID, Team teamId)
588 for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
590 if (itr->second.OfflineRemoveTime)
591 continue;
593 Player* plr = sObjectMgr.GetPlayer(itr->first);
595 if (!plr)
597 sLog.outError("BattleGround:CastSpellOnTeam: %s not found!", itr->first.GetString().c_str());
598 continue;
601 Team team = itr->second.PlayerTeam;
602 if (!team) team = plr->GetTeam();
604 if (team == teamId)
605 plr->CastSpell(plr, SpellID, true);
609 void BattleGround::RewardHonorToTeam(uint32 Honor, Team teamId)
611 for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
613 if (itr->second.OfflineRemoveTime)
614 continue;
616 Player* plr = sObjectMgr.GetPlayer(itr->first);
618 if (!plr)
620 sLog.outError("BattleGround:RewardHonorToTeam: %s not found!", itr->first.GetString().c_str());
621 continue;
624 Team team = itr->second.PlayerTeam;
625 if (!team) team = plr->GetTeam();
627 if (team == teamId)
628 UpdatePlayerScore(plr, SCORE_BONUS_HONOR, Honor);
632 void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, Team teamId)
634 FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
636 if (!factionEntry)
637 return;
639 for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
641 if (itr->second.OfflineRemoveTime)
642 continue;
644 Player* plr = sObjectMgr.GetPlayer(itr->first);
646 if (!plr)
648 sLog.outError("BattleGround:RewardReputationToTeam: %s not found!", itr->first.GetString().c_str());
649 continue;
652 Team team = itr->second.PlayerTeam;
653 if (!team) team = plr->GetTeam();
655 if (team == teamId)
656 plr->GetReputationMgr().ModifyReputation(factionEntry, Reputation);
660 void BattleGround::UpdateWorldState(uint32 Field, uint32 Value)
662 WorldPacket data;
663 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
664 SendPacketToAll(&data);
667 void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player* Source)
669 WorldPacket data;
670 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
671 Source->GetSession()->SendPacket(&data);
674 void BattleGround::EndBattleGround(Team winner)
676 this->RemoveFromBGFreeSlotQueue();
678 ArenaTeam* winner_arena_team = NULL;
679 ArenaTeam* loser_arena_team = NULL;
680 uint32 loser_rating = 0;
681 uint32 winner_rating = 0;
682 WorldPacket data;
683 int32 winmsg_id = 0;
685 if (winner == ALLIANCE)
687 winmsg_id = isBattleGround() ? LANG_BG_A_WINS : LANG_ARENA_GOLD_WINS;
689 PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound
691 else if (winner == HORDE)
693 winmsg_id = isBattleGround() ? LANG_BG_H_WINS : LANG_ARENA_GREEN_WINS;
695 PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound
698 SetWinner(winner);
700 SetStatus(STATUS_WAIT_LEAVE);
701 // we must set it this way, because end time is sent in packet!
702 m_EndTime = TIME_TO_AUTOREMOVE;
704 // arena rating calculation
705 if (isArena() && isRated())
707 winner_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(winner));
708 loser_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(winner)));
709 if (winner_arena_team && loser_arena_team)
711 loser_rating = loser_arena_team->GetStats().rating;
712 winner_rating = winner_arena_team->GetStats().rating;
713 int32 winner_change = winner_arena_team->WonAgainst(loser_rating);
714 int32 loser_change = loser_arena_team->LostAgainst(winner_rating);
715 DEBUG_LOG("--- Winner rating: %u, Loser rating: %u, Winner change: %i, Loser change: %i ---", winner_rating, loser_rating, winner_change, loser_change);
716 SetArenaTeamRatingChangeForTeam(winner, winner_change);
717 SetArenaTeamRatingChangeForTeam(GetOtherTeam(winner), loser_change);
719 else
721 SetArenaTeamRatingChangeForTeam(ALLIANCE, 0);
722 SetArenaTeamRatingChangeForTeam(HORDE, 0);
726 for (BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
728 Team team = itr->second.PlayerTeam;
730 if (itr->second.OfflineRemoveTime)
732 // if rated arena match - make member lost!
733 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
735 if (team == winner)
736 winner_arena_team->OfflineMemberLost(itr->first, loser_rating);
737 else
738 loser_arena_team->OfflineMemberLost(itr->first, winner_rating);
740 continue;
743 Player* plr = sObjectMgr.GetPlayer(itr->first);
744 if (!plr)
746 sLog.outError("BattleGround:EndBattleGround %s not found!", itr->first.GetString().c_str());
747 continue;
750 // should remove spirit of redemption
751 if (plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
752 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
754 if (!plr->isAlive())
756 plr->ResurrectPlayer(1.0f);
757 plr->SpawnCorpseBones();
759 else
761 // needed cause else in av some creatures will kill the players at the end
762 plr->CombatStop();
763 plr->getHostileRefManager().deleteReferences();
766 // this line is obsolete - team is set ALWAYS
767 // if(!team) team = plr->GetTeam();
769 // per player calculation
770 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
772 if (team == winner)
774 // update achievement BEFORE personal rating update
775 ArenaTeamMember* member = winner_arena_team->GetMember(plr->GetObjectGuid());
776 if (member)
777 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, member->personal_rating);
779 winner_arena_team->MemberWon(plr, loser_rating);
780 plr->ModifyCurrencyCount(CURRENCY_CONQUEST_ARENA_META, sWorld.getConfig(CONFIG_UINT32_CURRENCY_ARENA_CONQUEST_POINTS_REWARD));
782 if (member)
784 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_PERSONAL_RATING, GetArenaType(), member->personal_rating);
785 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING, GetArenaType(), winner_arena_team->GetStats().rating);
788 else
790 loser_arena_team->MemberLost(plr, winner_rating);
792 // Arena lost => reset the win_rated_arena having the "no_loose" condition
793 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE);
797 if (team == winner)
799 RewardMark(plr, ITEM_WINNER_COUNT);
800 RewardQuestComplete(plr);
801 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_BG, 1);
803 else
804 RewardMark(plr, ITEM_LOSER_COUNT);
806 plr->CombatStopWithPets(true);
808 BlockMovement(plr);
810 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
811 plr->GetSession()->SendPacket(&data);
813 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
814 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType());
815 plr->GetSession()->SendPacket(&data);
816 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1);
819 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
821 // save the stat changes
822 winner_arena_team->SaveToDB();
823 loser_arena_team->SaveToDB();
824 // send updated arena team stats to players
825 // this way all arena team members will get notified, not only the ones who participated in this match
826 winner_arena_team->NotifyStatsChanged();
827 loser_arena_team->NotifyStatsChanged();
830 if (winmsg_id)
831 SendMessageToAll(winmsg_id, CHAT_MSG_BG_SYSTEM_NEUTRAL);
834 uint32 BattleGround::GetBonusHonorFromKill(uint32 kills) const
836 // variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill)
837 return (uint32)MaNGOS::Honor::hk_honor_at_level(GetMaxLevel(), kills);
840 uint32 BattleGround::GetBattlemasterEntry() const
842 switch (GetTypeID())
844 case BATTLEGROUND_AV: return 15972;
845 case BATTLEGROUND_WS: return 14623;
846 case BATTLEGROUND_AB: return 14879;
847 case BATTLEGROUND_EY: return 22516;
848 case BATTLEGROUND_NA: return 20200;
849 default: return 0;
853 void BattleGround::RewardMark(Player* plr, uint32 count)
855 switch (GetTypeID())
857 case BATTLEGROUND_AV:
858 if (count == ITEM_WINNER_COUNT)
859 RewardSpellCast(plr, SPELL_AV_MARK_WINNER);
860 else
861 RewardSpellCast(plr, SPELL_AV_MARK_LOSER);
862 break;
863 case BATTLEGROUND_WS:
864 if (count == ITEM_WINNER_COUNT)
865 RewardSpellCast(plr, SPELL_WS_MARK_WINNER);
866 else
867 RewardSpellCast(plr, SPELL_WS_MARK_LOSER);
868 break;
869 case BATTLEGROUND_AB:
870 if (count == ITEM_WINNER_COUNT)
871 RewardSpellCast(plr, SPELL_AB_MARK_WINNER);
872 else
873 RewardSpellCast(plr, SPELL_AB_MARK_LOSER);
874 break;
875 case BATTLEGROUND_EY: // no rewards
876 default:
877 break;
881 void BattleGround::RewardSpellCast(Player* plr, uint32 spell_id)
883 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
884 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
885 return;
887 SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id);
888 if (!spellInfo)
890 sLog.outError("Battleground reward casting spell %u not exist.", spell_id);
891 return;
894 plr->CastSpell(plr, spellInfo, true);
897 void BattleGround::RewardItem(Player* plr, uint32 item_id, uint32 count)
899 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
900 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
901 return;
903 ItemPosCountVec dest;
904 uint32 no_space_count = 0;
905 uint8 msg = plr->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item_id, count, &no_space_count);
907 if (msg == EQUIP_ERR_ITEM_NOT_FOUND)
909 sLog.outErrorDb("Battleground reward item (Entry %u) not exist in `item_template`.", item_id);
910 return;
913 if (msg != EQUIP_ERR_OK) // convert to possible store amount
914 count -= no_space_count;
916 if (count != 0 && !dest.empty()) // can add some
917 if (Item* item = plr->StoreNewItem(dest, item_id, true, 0))
918 plr->SendNewItem(item, count, true, false);
920 if (no_space_count > 0)
921 SendRewardMarkByMail(plr, item_id, no_space_count);
924 void BattleGround::SendRewardMarkByMail(Player* plr, uint32 mark, uint32 count)
926 uint32 bmEntry = GetBattlemasterEntry();
927 if (!bmEntry)
928 return;
930 ItemPrototype const* markProto = ObjectMgr::GetItemPrototype(mark);
931 if (!markProto)
932 return;
934 if (Item* markItem = Item::CreateItem(mark, count, plr))
936 // save new item before send
937 markItem->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
939 int loc_idx = plr->GetSession()->GetSessionDbLocaleIndex();
941 // subject: item name
942 std::string subject = markProto->Name1;
943 sObjectMgr.GetItemLocaleStrings(markProto->ItemId, loc_idx, &subject);
945 // text
946 std::string textFormat = plr->GetSession()->GetMangosString(LANG_BG_MARK_BY_MAIL);
947 char textBuf[300];
948 snprintf(textBuf, 300, textFormat.c_str(), GetName(), GetName());
950 MailDraft(subject, textBuf)
951 .AddItem(markItem)
952 .SendMailTo(plr, MailSender(MAIL_CREATURE, bmEntry));
956 void BattleGround::RewardQuestComplete(Player* plr)
958 uint32 quest;
959 switch (GetTypeID())
961 case BATTLEGROUND_AV:
962 quest = SPELL_AV_QUEST_REWARD;
963 break;
964 case BATTLEGROUND_WS:
965 quest = SPELL_WS_QUEST_REWARD;
966 break;
967 case BATTLEGROUND_AB:
968 quest = SPELL_AB_QUEST_REWARD;
969 break;
970 case BATTLEGROUND_EY:
971 quest = SPELL_EY_QUEST_REWARD;
972 break;
973 default:
974 return;
977 RewardSpellCast(plr, quest);
980 void BattleGround::BlockMovement(Player* plr)
982 plr->SetClientControl(plr, 0); // movement disabled NOTE: the effect will be automatically removed by client when the player is teleported from the battleground, so no need to send with uint8(1) in RemovePlayerAtLeave()
985 void BattleGround::RemovePlayerAtLeave(ObjectGuid guid, bool Transport, bool SendPacket)
987 Team team = GetPlayerTeam(guid);
988 bool participant = false;
989 // Remove from lists/maps
990 BattleGroundPlayerMap::iterator itr = m_Players.find(guid);
991 if (itr != m_Players.end())
993 UpdatePlayersCountByTeam(team, true); // -1 player
994 m_Players.erase(itr);
995 // check if the player was a participant of the match, or only entered through gm command (goname)
996 participant = true;
999 BattleGroundScoreMap::iterator itr2 = m_PlayerScores.find(guid);
1000 if (itr2 != m_PlayerScores.end())
1002 delete itr2->second; // delete player's score
1003 m_PlayerScores.erase(itr2);
1006 Player* plr = sObjectMgr.GetPlayer(guid);
1008 if (plr)
1010 // should remove spirit of redemption
1011 if (plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
1012 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
1014 plr->RemoveAurasDueToSpell(isArena() ? SPELL_ARENA_DAMPENING : SPELL_BATTLEGROUND_DAMPENING);
1016 if (!plr->isAlive()) // resurrect on exit
1018 plr->ResurrectPlayer(1.0f);
1019 plr->SpawnCorpseBones();
1023 RemovePlayer(plr, guid); // BG subclass specific code
1025 if (participant) // if the player was a match participant, remove auras, calc rating, update queue
1027 BattleGroundTypeId bgTypeId = GetTypeID();
1028 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1029 if (plr)
1031 plr->ClearAfkReports();
1033 if (!team) team = plr->GetTeam();
1035 // if arena, remove the specific arena auras
1036 if (isArena())
1038 plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out
1039 bgTypeId = BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing)
1041 // unsummon current and summon old pet if there was one and there isn't a current pet
1042 plr->RemovePet(PET_SAVE_NOT_IN_SLOT);
1043 plr->ResummonPetTemporaryUnSummonedIfAny();
1045 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
1047 // left a rated match while the encounter was in progress, consider as loser
1048 ArenaTeam* winner_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
1049 ArenaTeam* loser_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
1050 if (winner_arena_team && loser_arena_team)
1051 loser_arena_team->MemberLost(plr, winner_arena_team->GetRating());
1054 if (SendPacket)
1056 WorldPacket data;
1057 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0, ARENA_TYPE_NONE);
1058 plr->GetSession()->SendPacket(&data);
1061 // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg
1062 plr->RemoveBattleGroundQueueId(bgQueueTypeId);
1064 else
1065 // removing offline participant
1067 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
1069 // left a rated match while the encounter was in progress, consider as loser
1070 ArenaTeam* others_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
1071 ArenaTeam* players_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
1072 if (others_arena_team && players_arena_team)
1073 players_arena_team->OfflineMemberLost(guid, others_arena_team->GetRating());
1077 // remove from raid group if player is member
1078 if (Group* group = GetBgRaid(team))
1080 if (!group->RemoveMember(guid, 0)) // group was disbanded
1082 SetBgRaid(team, NULL);
1083 delete group;
1086 DecreaseInvitedCount(team);
1087 // we should update battleground queue, but only if bg isn't ending
1088 if (isBattleGround() && GetStatus() < STATUS_WAIT_LEAVE)
1090 // a player has left the battleground, so there are free slots -> add to queue
1091 AddToBGFreeSlotQueue();
1092 sBattleGroundMgr.ScheduleQueueUpdate(0, ARENA_TYPE_NONE, bgQueueTypeId, bgTypeId, GetBracketId());
1095 // Let others know
1096 WorldPacket data;
1097 sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, guid);
1098 SendPacketToTeam(team, &data, plr, false);
1101 if (plr)
1103 // Do next only if found in battleground
1104 plr->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG.
1105 // reset destination bg team
1106 plr->SetBGTeam(TEAM_NONE);
1108 if (Transport)
1109 plr->TeleportToBGEntryPoint();
1111 DETAIL_LOG("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName());
1114 // battleground object will be deleted next BattleGround::Update() call
1117 // this method is called when no players remains in battleground
1118 void BattleGround::Reset()
1120 SetWinner(TEAM_NONE);
1121 SetStatus(STATUS_WAIT_QUEUE);
1122 SetStartTime(0);
1123 SetEndTime(0);
1124 SetArenaType(ARENA_TYPE_NONE);
1125 SetRated(false);
1127 m_Events = 0;
1129 // door-event2 is always 0
1130 m_ActiveEvents[BG_EVENT_DOOR] = 0;
1131 if (isArena())
1133 m_ActiveEvents[ARENA_BUFF_EVENT] = BG_EVENT_NONE;
1134 m_ArenaBuffSpawned = false;
1137 if (m_InvitedAlliance > 0 || m_InvitedHorde > 0)
1138 sLog.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde);
1140 m_InvitedAlliance = 0;
1141 m_InvitedHorde = 0;
1142 m_InBGFreeSlotQueue = false;
1144 m_Players.clear();
1146 for (BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr)
1147 delete itr->second;
1148 m_PlayerScores.clear();
1151 void BattleGround::StartBattleGround()
1153 SetStartTime(0);
1155 // add BG to free slot queue
1156 AddToBGFreeSlotQueue();
1158 // add bg to update list
1159 // This must be done here, because we need to have already invited some players when first BG::Update() method is executed
1160 // and it doesn't matter if we call StartBattleGround() more times, because m_BattleGrounds is a map and instance id never changes
1161 sBattleGroundMgr.AddBattleGround(GetInstanceID(), GetTypeID(), this);
1164 void BattleGround::StartTimedAchievement(AchievementCriteriaTypes type, uint32 entry)
1166 for (BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
1167 if (Player* pPlayer = GetBgMap()->GetPlayer(itr->first))
1168 pPlayer->GetAchievementMgr().StartTimedAchievementCriteria(type, entry);
1171 void BattleGround::AddPlayer(Player* plr)
1173 // remove afk from player
1174 if (plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK))
1175 plr->ToggleAFK();
1177 // score struct must be created in inherited class
1179 ObjectGuid guid = plr->GetObjectGuid();
1180 Team team = plr->GetBGTeam();
1182 BattleGroundPlayer bp;
1183 bp.OfflineRemoveTime = 0;
1184 bp.PlayerTeam = team;
1186 // Add to list/maps
1187 m_Players[guid] = bp;
1189 UpdatePlayersCountByTeam(team, false); // +1 player
1191 WorldPacket data;
1192 sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr);
1193 SendPacketToTeam(team, &data, plr, false);
1195 // add arena specific auras
1196 if (isArena())
1198 plr->RemoveArenaSpellCooldowns();
1199 plr->RemoveArenaAuras();
1200 plr->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT);
1201 if (team == ALLIANCE) // gold
1203 if (plr->GetTeam() == HORDE)
1204 plr->CastSpell(plr, SPELL_HORDE_GOLD_FLAG, true);
1205 else
1206 plr->CastSpell(plr, SPELL_ALLIANCE_GOLD_FLAG, true);
1208 else // green
1210 if (plr->GetTeam() == HORDE)
1211 plr->CastSpell(plr, SPELL_HORDE_GREEN_FLAG, true);
1212 else
1213 plr->CastSpell(plr, SPELL_ALLIANCE_GREEN_FLAG, true);
1216 plr->DestroyConjuredItems(true);
1217 plr->UnsummonPetTemporaryIfAny();
1219 if (GetStatus() == STATUS_WAIT_JOIN) // not started yet
1220 plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true);
1222 plr->CastSpell(plr, SPELL_ARENA_DAMPENING, true);
1224 else
1226 if (GetStatus() == STATUS_WAIT_JOIN) // not started yet
1227 plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells.
1229 plr->CastSpell(plr, SPELL_BATTLEGROUND_DAMPENING, true);
1232 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1233 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1235 // setup BG group membership
1236 PlayerAddedToBGCheckIfBGIsRunning(plr);
1237 AddOrSetPlayerToCorrectBgGroup(plr, guid, team);
1239 // Log
1240 DETAIL_LOG("BATTLEGROUND: Player %s joined the battle.", plr->GetName());
1243 /* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */
1244 void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player* plr, ObjectGuid plr_guid, Team team)
1246 if (Group* group = GetBgRaid(team)) // raid already exist
1248 if (group->IsMember(plr_guid))
1250 uint8 subgroup = group->GetMemberGroup(plr_guid);
1251 plr->SetBattleGroundRaid(group, subgroup);
1253 else
1255 group->AddMember(plr_guid, plr->GetName());
1256 if (Group* originalGroup = plr->GetOriginalGroup())
1257 if (originalGroup->IsLeader(plr_guid))
1258 group->ChangeLeader(plr_guid);
1261 else // first player joined
1263 group = new Group;
1264 SetBgRaid(team, group);
1265 group->Create(plr_guid, plr->GetName());
1269 // This method should be called when player logs into running battleground
1270 void BattleGround::EventPlayerLoggedIn(Player* player, ObjectGuid plr_guid)
1272 // player is correct pointer
1273 for (OfflineQueue::iterator itr = m_OfflineQueue.begin(); itr != m_OfflineQueue.end(); ++itr)
1275 if (*itr == plr_guid)
1277 m_OfflineQueue.erase(itr);
1278 break;
1281 m_Players[plr_guid].OfflineRemoveTime = 0;
1282 PlayerAddedToBGCheckIfBGIsRunning(player);
1283 // if battleground is starting, then add preparation aura
1284 // we don't have to do that, because preparation aura isn't removed when player logs out
1287 // This method should be called when player logs out from running battleground
1288 void BattleGround::EventPlayerLoggedOut(Player* player)
1290 // player is correct pointer, it is checked in WorldSession::LogoutPlayer()
1291 m_OfflineQueue.push_back(player->GetObjectGuid());
1292 m_Players[player->GetObjectGuid()].OfflineRemoveTime = sWorld.GetGameTime() + MAX_OFFLINE_TIME;
1293 if (GetStatus() == STATUS_IN_PROGRESS)
1295 // drop flag and handle other cleanups
1296 RemovePlayer(player, player->GetObjectGuid());
1298 // 1 player is logging out, if it is the last, then end arena!
1299 if (isArena())
1300 if (GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam())))
1301 EndBattleGround(GetOtherTeam(player->GetTeam()));
1305 /* This method should be called only once ... it adds pointer to queue */
1306 void BattleGround::AddToBGFreeSlotQueue()
1308 // make sure to add only once
1309 if (!m_InBGFreeSlotQueue && isBattleGround())
1311 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this);
1312 m_InBGFreeSlotQueue = true;
1316 /* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/
1317 void BattleGround::RemoveFromBGFreeSlotQueue()
1319 // set to be able to re-add if needed
1320 m_InBGFreeSlotQueue = false;
1321 for (BGFreeSlotQueueType::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr)
1323 if ((*itr)->GetInstanceID() == GetInstanceID())
1325 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr);
1326 return;
1331 // get the number of free slots for team
1332 // returns the number how many players can join battleground to MaxPlayersPerTeam
1333 uint32 BattleGround::GetFreeSlotsForTeam(Team team) const
1335 // return free slot count to MaxPlayerPerTeam
1336 if (GetStatus() == STATUS_WAIT_JOIN || GetStatus() == STATUS_IN_PROGRESS)
1337 return (GetInvitedCount(team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(team) : 0;
1339 return 0;
1342 bool BattleGround::HasFreeSlots() const
1344 return GetPlayersSize() < GetMaxPlayers();
1347 void BattleGround::UpdatePlayerScore(Player* Source, uint32 type, uint32 value)
1349 // this procedure is called from virtual function implemented in bg subclass
1350 BattleGroundScoreMap::const_iterator itr = m_PlayerScores.find(Source->GetObjectGuid());
1352 if (itr == m_PlayerScores.end()) // player not found...
1353 return;
1355 switch (type)
1357 case SCORE_KILLING_BLOWS: // Killing blows
1358 itr->second->KillingBlows += value;
1359 break;
1360 case SCORE_DEATHS: // Deaths
1361 itr->second->Deaths += value;
1362 break;
1363 case SCORE_HONORABLE_KILLS: // Honorable kills
1364 itr->second->HonorableKills += value;
1365 break;
1366 case SCORE_BONUS_HONOR: // Honor bonus
1367 // do not add honor in arenas
1368 if (isBattleGround())
1370 // reward honor instantly
1371 if (Source->RewardHonor(NULL, 1, (float)value))
1372 itr->second->BonusHonor += value;
1374 break;
1375 // used only in EY, but in SMSG_PVP_LOG_DATA opcode
1376 case SCORE_DAMAGE_DONE: // Damage Done
1377 itr->second->DamageDone += value;
1378 break;
1379 case SCORE_HEALING_DONE: // Healing Done
1380 itr->second->HealingDone += value;
1381 break;
1382 default:
1383 sLog.outError("BattleGround: Unknown player score type %u", type);
1384 break;
1388 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*/)
1390 // must be created this way, adding to godatamap would add it to the base map of the instance
1391 // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
1392 // so we must create it specific for this instance
1393 GameObject* go = new GameObject;
1394 if (!go->Create(GetBgMap()->GenerateLocalLowGuid(HIGHGUID_GAMEOBJECT), entry, GetBgMap(),
1395 PHASEMASK_NORMAL, x, y, z, o, QuaternionData(rotation0, rotation1, rotation2, rotation3)))
1397 sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry);
1398 sLog.outError("Cannot create gameobject template %u! BattleGround not created!", entry);
1399 delete go;
1400 return false;
1403 uint32 guid = go->GetGUIDLow();
1405 // without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata
1406 // 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
1407 GameObjectData& data = sObjectMgr.NewGOData(guid);
1409 data.id = entry;
1410 data.mapid = GetMapId();
1411 data.posX = x;
1412 data.posY = y;
1413 data.posZ = z;
1414 data.orientation = o;
1415 data.rotation0 = rotation0;
1416 data.rotation1 = rotation1;
1417 data.rotation2 = rotation2;
1418 data.rotation3 = rotation3;
1419 data.spawntimesecs = respawnTime;
1420 data.spawnMask = 1;
1421 data.animprogress = 100;
1422 data.go_state = 1;
1424 // add to world, so it can be later looked up from HashMapHolder
1425 go->AddToWorld();
1426 m_BgObjects[type] = go->GetObjectGuid();
1427 return true;
1430 // some doors aren't despawned so we cannot handle their closing in gameobject::update()
1431 // it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
1432 void BattleGround::DoorClose(ObjectGuid guid)
1434 GameObject* obj = GetBgMap()->GetGameObject(guid);
1435 if (obj)
1437 // if doors are open, close it
1438 if (obj->getLootState() == GO_ACTIVATED && obj->GetGoState() != GO_STATE_READY)
1440 // change state to allow door to be closed
1441 obj->SetLootState(GO_READY);
1442 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1445 else
1446 sLog.outError("BattleGround: Door %s not found (cannot close doors)", guid.GetString().c_str());
1449 void BattleGround::DoorOpen(ObjectGuid guid)
1451 GameObject* obj = GetBgMap()->GetGameObject(guid);
1452 if (obj)
1454 // change state to be sure they will be opened
1455 obj->SetLootState(GO_READY);
1456 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1458 else
1459 sLog.outError("BattleGround: Door %s not found! - doors will be closed.", guid.GetString().c_str());
1462 void BattleGround::OnObjectDBLoad(Creature* creature)
1464 const BattleGroundEventIdx eventId = sBattleGroundMgr.GetCreatureEventIndex(creature->GetGUIDLow());
1465 if (eventId.event1 == BG_EVENT_NONE)
1466 return;
1467 m_EventObjects[MAKE_PAIR32(eventId.event1, eventId.event2)].creatures.push_back(creature->GetObjectGuid());
1468 if (!IsActiveEvent(eventId.event1, eventId.event2))
1469 SpawnBGCreature(creature->GetObjectGuid(), RESPAWN_ONE_DAY);
1472 ObjectGuid BattleGround::GetSingleCreatureGuid(uint8 event1, uint8 event2)
1474 GuidVector::const_iterator itr = m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.begin();
1475 if (itr != m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.end())
1476 return *itr;
1477 return ObjectGuid();
1480 void BattleGround::OnObjectDBLoad(GameObject* obj)
1482 const BattleGroundEventIdx eventId = sBattleGroundMgr.GetGameObjectEventIndex(obj->GetGUIDLow());
1483 if (eventId.event1 == BG_EVENT_NONE)
1484 return;
1485 m_EventObjects[MAKE_PAIR32(eventId.event1, eventId.event2)].gameobjects.push_back(obj->GetObjectGuid());
1486 if (!IsActiveEvent(eventId.event1, eventId.event2))
1488 SpawnBGObject(obj->GetObjectGuid(), RESPAWN_ONE_DAY);
1490 else
1492 // it's possible, that doors aren't spawned anymore (wsg)
1493 if (GetStatus() >= STATUS_IN_PROGRESS && IsDoor(eventId.event1, eventId.event2))
1494 DoorOpen(obj->GetObjectGuid());
1498 bool BattleGround::IsDoor(uint8 event1, uint8 event2)
1500 if (event1 == BG_EVENT_DOOR)
1502 if (event2 > 0)
1504 sLog.outError("BattleGround too high event2 for event1:%i", event1);
1505 return false;
1507 return true;
1509 return false;
1512 void BattleGround::OpenDoorEvent(uint8 event1, uint8 event2 /*=0*/)
1514 if (!IsDoor(event1, event2))
1516 sLog.outError("BattleGround:OpenDoorEvent this is no door event1:%u event2:%u", event1, event2);
1517 return;
1519 if (!IsActiveEvent(event1, event2)) // maybe already despawned (eye)
1521 sLog.outError("BattleGround:OpenDoorEvent this event isn't active event1:%u event2:%u", event1, event2);
1522 return;
1524 GuidVector::const_iterator itr = m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.begin();
1525 for (; itr != m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.end(); ++itr)
1526 DoorOpen(*itr);
1529 void BattleGround::SpawnEvent(uint8 event1, uint8 event2, bool spawn)
1531 // stop if we want to spawn something which was already spawned
1532 // or despawn something which was already despawned
1533 if (event2 == BG_EVENT_NONE || (spawn && m_ActiveEvents[event1] == event2)
1534 || (!spawn && m_ActiveEvents[event1] != event2))
1535 return;
1537 if (spawn)
1539 // if event gets spawned, the current active event mus get despawned
1540 SpawnEvent(event1, m_ActiveEvents[event1], false);
1541 m_ActiveEvents[event1] = event2; // set this event to active
1543 else
1544 m_ActiveEvents[event1] = BG_EVENT_NONE; // no event active if event2 gets despawned
1546 GuidVector::const_iterator itr = m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.begin();
1547 for (; itr != m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.end(); ++itr)
1548 SpawnBGCreature(*itr, (spawn) ? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY);
1549 GuidVector::const_iterator itr2 = m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.begin();
1550 for (; itr2 != m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.end(); ++itr2)
1551 SpawnBGObject(*itr2, (spawn) ? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY);
1554 void BattleGround::SpawnBGObject(ObjectGuid guid, uint32 respawntime)
1556 Map* map = GetBgMap();
1558 GameObject* obj = map->GetGameObject(guid);
1559 if (!obj)
1560 return;
1561 if (respawntime == 0)
1563 // we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again
1564 if (obj->getLootState() == GO_JUST_DEACTIVATED)
1565 obj->SetLootState(GO_READY);
1566 obj->SetRespawnTime(0);
1567 map->Add(obj);
1569 else
1571 map->Add(obj);
1572 obj->SetRespawnTime(respawntime);
1573 obj->SetLootState(GO_JUST_DEACTIVATED);
1577 void BattleGround::SpawnBGCreature(ObjectGuid guid, uint32 respawntime)
1579 Map* map = GetBgMap();
1581 Creature* obj = map->GetCreature(guid);
1582 if (!obj)
1583 return;
1584 if (respawntime == 0)
1586 obj->Respawn();
1587 map->Add(obj);
1589 else
1591 map->Add(obj);
1592 obj->SetRespawnDelay(respawntime);
1593 obj->SetDeathState(JUST_DIED);
1594 obj->RemoveCorpse();
1598 bool BattleGround::DelObject(uint32 type)
1600 if (!m_BgObjects[type])
1601 return true;
1603 GameObject* obj = GetBgMap()->GetGameObject(m_BgObjects[type]);
1604 if (!obj)
1606 sLog.outError("Can't find gobject: %s", m_BgObjects[type].GetString().c_str());
1607 return false;
1610 obj->SetRespawnTime(0); // not save respawn time
1611 obj->Delete();
1612 m_BgObjects[type].Clear();
1613 return true;
1616 void BattleGround::SendMessageToAll(int32 entry, ChatMsg type, Player const* source)
1618 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source);
1619 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1620 BroadcastWorker(bg_do);
1623 void BattleGround::SendYellToAll(int32 entry, uint32 language, ObjectGuid guid)
1625 Creature* source = GetBgMap()->GetCreature(guid);
1626 if (!source)
1627 return;
1628 MaNGOS::BattleGroundYellBuilder bg_builder(language, entry, source);
1629 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundYellBuilder> bg_do(bg_builder);
1630 BroadcastWorker(bg_do);
1633 void BattleGround::PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...)
1635 va_list ap;
1636 va_start(ap, source);
1638 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source, &ap);
1639 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1640 BroadcastWorker(bg_do);
1642 va_end(ap);
1645 void BattleGround::SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 arg1, int32 arg2)
1647 MaNGOS::BattleGround2ChatBuilder bg_builder(type, entry, source, arg1, arg2);
1648 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGround2ChatBuilder> bg_do(bg_builder);
1649 BroadcastWorker(bg_do);
1652 void BattleGround::SendYell2ToAll(int32 entry, uint32 language, ObjectGuid guid, int32 arg1, int32 arg2)
1654 Creature* source = GetBgMap()->GetCreature(guid);
1655 if (!source)
1656 return;
1657 MaNGOS::BattleGround2YellBuilder bg_builder(language, entry, source, arg1, arg2);
1658 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGround2YellBuilder> bg_do(bg_builder);
1659 BroadcastWorker(bg_do);
1662 void BattleGround::EndNow()
1664 RemoveFromBGFreeSlotQueue();
1665 SetStatus(STATUS_WAIT_LEAVE);
1666 SetEndTime(0);
1670 important notice:
1671 buffs aren't spawned/despawned when players captures anything
1672 buffs are in their positions when battleground starts
1674 void BattleGround::HandleTriggerBuff(ObjectGuid go_guid)
1676 GameObject* obj = GetBgMap()->GetGameObject(go_guid);
1677 if (!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned())
1678 return;
1680 // static buffs are already handled just by database and don't need
1681 // battleground code
1682 if (!m_BuffChange)
1684 obj->SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
1685 return;
1688 // change buff type, when buff is used:
1689 // TODO this can be done when poolsystem works for instances
1690 int32 index = m_BgObjects.size() - 1;
1691 while (index >= 0 && m_BgObjects[index] != go_guid)
1692 --index;
1693 if (index < 0)
1695 sLog.outError("BattleGround (Type: %u) has buff trigger %s GOType: %u but it hasn't that object in its internal data",
1696 GetTypeID(), go_guid.GetString().c_str(), obj->GetGoType());
1697 return;
1700 // randomly select new buff
1701 uint8 buff = urand(0, 2);
1702 uint32 entry = obj->GetEntry();
1703 if (m_BuffChange && entry != Buff_Entries[buff])
1705 // despawn current buff
1706 SpawnBGObject(m_BgObjects[index], RESPAWN_ONE_DAY);
1707 // set index for new one
1708 for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex)
1710 if (entry == Buff_Entries[currBuffTypeIndex])
1712 index -= currBuffTypeIndex;
1713 index += buff;
1718 SpawnBGObject(m_BgObjects[index], BUFF_RESPAWN_TIME);
1721 void BattleGround::HandleKillPlayer(Player* player, Player* killer)
1723 // add +1 deaths
1724 UpdatePlayerScore(player, SCORE_DEATHS, 1);
1726 // add +1 kills to group and +1 killing_blows to killer
1727 if (killer)
1729 UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1);
1730 UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1);
1732 for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1734 Player* plr = sObjectMgr.GetPlayer(itr->first);
1736 if (!plr || plr == killer)
1737 continue;
1739 if (plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player))
1740 UpdatePlayerScore(plr, SCORE_HONORABLE_KILLS, 1);
1744 // to be able to remove insignia -- ONLY IN BattleGrounds
1745 if (!isArena())
1746 player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
1749 // return the player's team based on battlegroundplayer info
1750 // used in same faction arena matches mainly
1751 Team BattleGround::GetPlayerTeam(ObjectGuid guid)
1753 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1754 if (itr != m_Players.end())
1755 return itr->second.PlayerTeam;
1756 return TEAM_NONE;
1759 bool BattleGround::IsPlayerInBattleGround(ObjectGuid guid)
1761 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1762 if (itr != m_Players.end())
1763 return true;
1764 return false;
1767 void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player* plr)
1769 if (GetStatus() != STATUS_WAIT_LEAVE)
1770 return;
1772 WorldPacket data;
1773 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1775 BlockMovement(plr);
1777 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
1778 plr->GetSession()->SendPacket(&data);
1780 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType());
1781 plr->GetSession()->SendPacket(&data);
1784 uint32 BattleGround::GetAlivePlayersCountByTeam(Team team) const
1786 int count = 0;
1787 for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1789 if (itr->second.PlayerTeam == team)
1791 Player* pl = sObjectMgr.GetPlayer(itr->first);
1792 if (pl && pl->isAlive())
1793 ++count;
1796 return count;
1799 void BattleGround::CheckArenaWinConditions()
1801 if (!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE))
1802 EndBattleGround(HORDE);
1803 else if (GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE))
1804 EndBattleGround(ALLIANCE);
1807 void BattleGround::SetBgRaid(Team team, Group* bg_raid)
1809 Group*& old_raid = m_BgRaids[GetTeamIndexByTeamId(team)];
1811 if (old_raid)
1812 old_raid->SetBattlegroundGroup(NULL);
1814 if (bg_raid)
1815 bg_raid->SetBattlegroundGroup(this);
1817 old_raid = bg_raid;
1820 WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard(Player* player)
1822 return sObjectMgr.GetClosestGraveYard(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam());
1825 bool BattleGround::IsTeamScoreInRange(Team team, uint32 minScore, uint32 maxScore) const
1827 BattleGroundTeamIndex team_idx = GetTeamIndexByTeamId(team);
1828 uint32 score = (m_TeamScores[team_idx] < 0) ? 0 : uint32(m_TeamScores[team_idx]);
1829 return score >= minScore && score <= maxScore;
1832 void BattleGround::SetBracket(PvPDifficultyEntry const* bracketEntry)
1834 m_BracketId = bracketEntry->GetBracketId();
1835 SetLevelRange(bracketEntry->minLevel, bracketEntry->maxLevel);