Don't use singleton to access static functions.
[getmangos.git] / src / game / BattleGround.cpp
blob1480bc76949ebfabdd40dbd6e27cf9b092dc9432
1 /*
2 * Copyright (C) 2005-2009 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 "ObjectMgr.h"
31 #include "WorldPacket.h"
32 #include "Util.h"
33 #include "Formulas.h"
34 #include "GridNotifiersImpl.h"
36 namespace MaNGOS
38 class BattleGroundChatBuilder
40 public:
41 BattleGroundChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, va_list* args = NULL)
42 : i_msgtype(msgtype), i_textId(textId), i_source(source), i_args(args) {}
43 void operator()(WorldPacket& data, int32 loc_idx)
45 char const* text = objmgr.GetMangosString(i_textId,loc_idx);
47 if (i_args)
49 // we need copy va_list before use or original va_list will corrupted
50 va_list ap;
51 va_copy(ap,*i_args);
53 char str [2048];
54 vsnprintf(str,2048,text, ap );
55 va_end(ap);
57 do_helper(data,&str[0]);
59 else
60 do_helper(data,text);
62 private:
63 void do_helper(WorldPacket& data, char const* text)
65 uint64 target_guid = i_source ? i_source ->GetGUID() : 0;
67 data << uint8(i_msgtype);
68 data << uint32(LANG_UNIVERSAL);
69 data << uint64(target_guid); // there 0 for BG messages
70 data << uint32(0); // can be chat msg group or something
71 data << uint64(target_guid);
72 data << uint32(strlen(text)+1);
73 data << text;
74 data << uint8(i_source ? i_source->chatTag() : uint8(0));
77 ChatMsg i_msgtype;
78 int32 i_textId;
79 Player const* i_source;
80 va_list* i_args;
83 class BattleGroundYellBuilder
85 public:
86 BattleGroundYellBuilder(uint32 language, int32 textId, Creature const* source, va_list* args = NULL)
87 : i_language(language), i_textId(textId), i_source(source), i_args(args) {}
88 void operator()(WorldPacket& data, int32 loc_idx)
90 char const* text = objmgr.GetMangosString(i_textId,loc_idx);
92 if(i_args)
94 // we need copy va_list before use or original va_list will corrupted
95 va_list ap;
96 va_copy(ap,*i_args);
98 char str [2048];
99 vsnprintf(str,2048,text, ap );
100 va_end(ap);
102 do_helper(data,&str[0]);
104 else
105 do_helper(data,text);
107 private:
108 void do_helper(WorldPacket& data, char const* text)
110 //copyied from BuildMonsterChat
111 data << (uint8)CHAT_MSG_MONSTER_YELL;
112 data << (uint32)i_language;
113 data << (uint64)i_source->GetGUID();
114 data << (uint32)0; //2.1.0
115 data << (uint32)(strlen(i_source->GetName())+1);
116 data << i_source->GetName();
117 data << (uint64)0; //Unit Target - isn't important for bgs
118 data << (uint32)strlen(text)+1;
119 data << text;
120 data << (uint8)0; // ChatTag - for bgs allways 0?
123 uint32 i_language;
124 int32 i_textId;
125 Creature const* i_source;
126 va_list* i_args;
130 class BattleGround2ChatBuilder
132 public:
133 BattleGround2ChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, int32 arg1, int32 arg2)
134 : i_msgtype(msgtype), i_textId(textId), i_source(source), i_arg1(arg1), i_arg2(arg2) {}
135 void operator()(WorldPacket& data, int32 loc_idx)
137 char const* text = objmgr.GetMangosString(i_textId,loc_idx);
138 char const* arg1str = i_arg1 ? objmgr.GetMangosString(i_arg1,loc_idx) : "";
139 char const* arg2str = i_arg2 ? objmgr.GetMangosString(i_arg2,loc_idx) : "";
141 char str [2048];
142 snprintf(str,2048,text, arg1str, arg2str );
144 uint64 target_guid = i_source ? i_source ->GetGUID() : 0;
146 data << uint8(i_msgtype);
147 data << uint32(LANG_UNIVERSAL);
148 data << uint64(target_guid); // there 0 for BG messages
149 data << uint32(0); // can be chat msg group or something
150 data << uint64(target_guid);
151 data << uint32(strlen(str)+1);
152 data << str;
153 data << uint8(i_source ? i_source->chatTag() : uint8(0));
155 private:
157 ChatMsg i_msgtype;
158 int32 i_textId;
159 Player const* i_source;
160 int32 i_arg1;
161 int32 i_arg2;
164 class BattleGround2YellBuilder
166 public:
167 BattleGround2YellBuilder(uint32 language, int32 textId, Creature const* source, int32 arg1, int32 arg2)
168 : i_language(language), i_textId(textId), i_source(source), i_arg1(arg1), i_arg2(arg2) {}
169 void operator()(WorldPacket& data, int32 loc_idx)
171 char const* text = objmgr.GetMangosString(i_textId,loc_idx);
172 char const* arg1str = i_arg1 ? objmgr.GetMangosString(i_arg1,loc_idx) : "";
173 char const* arg2str = i_arg2 ? objmgr.GetMangosString(i_arg2,loc_idx) : "";
175 char str [2048];
176 snprintf(str,2048,text, arg1str, arg2str );
177 //copyied from BuildMonsterChat
178 data << (uint8)CHAT_MSG_MONSTER_YELL;
179 data << (uint32)i_language;
180 data << (uint64)i_source->GetGUID();
181 data << (uint32)0; //2.1.0
182 data << (uint32)(strlen(i_source->GetName())+1);
183 data << i_source->GetName();
184 data << (uint64)0; //Unit Target - isn't important for bgs
185 data << (uint32)strlen(str)+1;
186 data << str;
187 data << (uint8)0; // ChatTag - for bgs allways 0?
189 private:
191 uint32 i_language;
192 int32 i_textId;
193 Creature const* i_source;
194 int32 i_arg1;
195 int32 i_arg2;
197 } // namespace MaNGOS
199 template<class Do>
200 void BattleGround::BroadcastWorker(Do& _do)
202 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
203 if (Player *plr = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER)))
204 _do(plr);
207 BattleGround::BattleGround()
209 m_TypeID = BattleGroundTypeId(0);
210 m_InstanceID = 0;
211 m_Status = STATUS_NONE;
212 m_ClientInstanceID = 0;
213 m_EndTime = 0;
214 m_QueueId = QUEUE_ID_MAX_LEVEL_19;
215 m_InvitedAlliance = 0;
216 m_InvitedHorde = 0;
217 m_ArenaType = 0;
218 m_IsArena = false;
219 m_Winner = 2;
220 m_StartTime = 0;
221 m_Events = 0;
222 m_IsRated = false;
223 m_BuffChange = false;
224 m_Name = "";
225 m_LevelMin = 0;
226 m_LevelMax = 0;
227 m_InBGFreeSlotQueue = false;
228 m_SetDeleteThis = 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_PrematureCountDown = 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] = LANG_BG_WS_START_TWO_MINUTES;
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 if (GetInstanceID()) // not spam by useless queries in case BG templates
290 // delete creature and go respawn times
291 WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'",GetInstanceID());
292 WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'",GetInstanceID());
293 // delete instance from db
294 CharacterDatabase.PExecute("DELETE FROM instance WHERE id = '%u'",GetInstanceID());
295 // remove from battlegrounds
298 sBattleGroundMgr.RemoveBattleGround(GetInstanceID(), GetTypeID());
300 // unload map
301 // map can be null at bg destruction
302 if (m_Map)
303 m_Map->SetUnload();
305 // remove from bg free slot queue
306 this->RemoveFromBGFreeSlotQueue();
308 for(BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr)
309 delete itr->second;
312 void BattleGround::Update(uint32 diff)
314 if (!GetPlayersSize())
316 // BG is empty
317 // if there are no players invited, delete BG
318 // this will delete arena or bg object, where any player entered
319 // [[ but if you use battleground object again (more battles possible to be played on 1 instance)
320 // then this condition should be removed and code:
321 // if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
322 // this->AddToFreeBGObjectsQueue(); // not yet implemented
323 // should be used instead of current
324 // ]]
325 // BattleGround Template instance cannot be updated, because it would be deleted
326 if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
327 m_SetDeleteThis = true;
328 return;
331 // remove offline players from bg after 5 minutes
332 if (!m_OfflineQueue.empty())
334 BattleGroundPlayerMap::iterator itr = m_Players.find(*(m_OfflineQueue.begin()));
335 if (itr != m_Players.end())
337 if (itr->second.OfflineRemoveTime <= sWorld.GetGameTime())
339 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
340 m_OfflineQueue.pop_front(); // remove from offline queue
341 //do not use itr for anything, because it is erased in RemovePlayerAtLeave()
346 /*********************************************************/
347 /*** BATTLEGROUND BALLANCE SYSTEM ***/
348 /*********************************************************/
350 // if less then minimum players are in on one side, then start premature finish timer
351 if (GetStatus() == STATUS_IN_PROGRESS && !isArena() && sBattleGroundMgr.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE) < GetMinPlayersPerTeam()))
353 if (!m_PrematureCountDown)
355 m_PrematureCountDown = true;
356 m_PrematureCountDownTimer = sBattleGroundMgr.GetPrematureFinishTime();
358 else if (m_PrematureCountDownTimer < diff)
360 // time's up!
361 uint32 winner = 0;
362 if (GetPlayersCountByTeam(ALLIANCE) >= GetMinPlayersPerTeam())
363 winner = ALLIANCE;
364 else if (GetPlayersCountByTeam(HORDE) >= GetMinPlayersPerTeam())
365 winner = HORDE;
367 EndBattleGround(winner);
368 m_PrematureCountDown = false;
370 else
372 uint32 newtime = m_PrematureCountDownTimer - diff;
373 // announce every minute
374 if (newtime > (MINUTE * IN_MILISECONDS))
376 if (newtime / (MINUTE * IN_MILISECONDS) != m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS))
377 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS)));
379 else
381 //announce every 15 seconds
382 if (newtime / (15 * IN_MILISECONDS) != m_PrematureCountDownTimer / (15 * IN_MILISECONDS))
383 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / IN_MILISECONDS));
385 m_PrematureCountDownTimer = newtime;
389 else if (m_PrematureCountDown)
390 m_PrematureCountDown = false;
392 /*********************************************************/
393 /*** ARENA BUFF OBJECT SPAWNING ***/
394 /*********************************************************/
395 if (isArena() && !m_ArenaBuffSpawned)
397 // 60 seconds after start the buffobjects in arena should get spawned
398 if (m_StartTime > uint32(m_StartDelayTimes[BG_STARTING_EVENT_FIRST] + ARENA_SPAWN_BUFF_OBJECTS))
400 SpawnEvent(ARENA_BUFF_EVENT, 0, true);
401 m_ArenaBuffSpawned = true;
405 /*********************************************************/
406 /*** BATTLEGROUND STARTING SYSTEM ***/
407 /*********************************************************/
409 if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize())
411 ModifyStartDelayTime(diff);
413 if (!(m_Events & BG_STARTING_EVENT_1))
415 m_Events |= BG_STARTING_EVENT_1;
417 // setup here, only when at least one player has ported to the map
418 if (!SetupBattleGround())
420 EndNow();
421 return;
424 StartingEventCloseDoors();
425 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FIRST]);
426 //first start warning - 2 or 1 minute
427 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FIRST], CHAT_MSG_BG_SYSTEM_NEUTRAL);
429 // After 1 minute or 30 seconds, warning is signalled
430 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_SECOND] && !(m_Events & BG_STARTING_EVENT_2))
432 m_Events |= BG_STARTING_EVENT_2;
433 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_SECOND], CHAT_MSG_BG_SYSTEM_NEUTRAL);
435 // After 30 or 15 seconds, warning is signalled
436 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_THIRD] && !(m_Events & BG_STARTING_EVENT_3))
438 m_Events |= BG_STARTING_EVENT_3;
439 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_THIRD], CHAT_MSG_BG_SYSTEM_NEUTRAL);
441 // delay expired (atfer 2 or 1 minute)
442 else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4))
444 m_Events |= BG_STARTING_EVENT_4;
446 StartingEventOpenDoors();
448 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FOURTH], CHAT_MSG_BG_SYSTEM_NEUTRAL);
449 SetStatus(STATUS_IN_PROGRESS);
450 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FOURTH]);
452 //remove preparation
453 if (isArena())
455 //TODO : add arena sound PlaySoundToAll(SOUND_ARENA_START);
457 for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
458 if (Player *plr = objmgr.GetPlayer(itr->first))
459 plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION);
461 CheckArenaWinConditions();
463 else
466 PlaySoundToAll(SOUND_BG_START);
468 for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
469 if (Player* plr = objmgr.GetPlayer(itr->first))
470 plr->RemoveAurasDueToSpell(SPELL_PREPARATION);
471 //Announce BG starting
472 if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE))
474 sWorld.SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD, GetName(), GetMinLevel(), GetMaxLevel());
480 /*********************************************************/
481 /*** BATTLEGROUND ENDING SYSTEM ***/
482 /*********************************************************/
484 if (GetStatus() == STATUS_WAIT_LEAVE)
486 // remove all players from battleground after 2 minutes
487 m_EndTime -= diff;
488 if (m_EndTime <= 0)
490 m_EndTime = 0;
491 BattleGroundPlayerMap::iterator itr, next;
492 for(itr = m_Players.begin(); itr != m_Players.end(); itr = next)
494 next = itr;
495 ++next;
496 //itr is erased here!
497 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
498 // do not change any battleground's private variables
503 //update start time
504 m_StartTime += diff;
507 void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O)
509 BattleGroundTeamId idx = GetTeamIndexByTeamId(TeamID);
510 m_TeamStartLocX[idx] = X;
511 m_TeamStartLocY[idx] = Y;
512 m_TeamStartLocZ[idx] = Z;
513 m_TeamStartLocO[idx] = O;
516 void BattleGround::SendPacketToAll(WorldPacket *packet)
518 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
520 Player *plr = objmgr.GetPlayer(itr->first);
521 if (plr)
522 plr->GetSession()->SendPacket(packet);
523 else
524 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
528 void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender, bool self)
530 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
532 Player *plr = objmgr.GetPlayer(itr->first);
534 if (!plr)
536 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
537 continue;
540 if (!self && sender == plr)
541 continue;
543 uint32 team = itr->second.Team;
544 if(!team) team = plr->GetTeam();
546 if (team == TeamID)
547 plr->GetSession()->SendPacket(packet);
551 void BattleGround::PlaySoundToAll(uint32 SoundID)
553 WorldPacket data;
554 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
555 SendPacketToAll(&data);
558 void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID)
560 WorldPacket data;
562 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
564 Player *plr = objmgr.GetPlayer(itr->first);
566 if (!plr)
568 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
569 continue;
572 uint32 team = itr->second.Team;
573 if(!team) team = plr->GetTeam();
575 if (team == TeamID)
577 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
578 plr->GetSession()->SendPacket(&data);
583 void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID)
585 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
587 Player *plr = objmgr.GetPlayer(itr->first);
589 if (!plr)
591 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
592 continue;
595 uint32 team = itr->second.Team;
596 if(!team) team = plr->GetTeam();
598 if (team == TeamID)
599 plr->CastSpell(plr, SpellID, true);
603 void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID)
605 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
607 Player *plr = objmgr.GetPlayer(itr->first);
609 if (!plr)
611 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
612 continue;
615 uint32 team = itr->second.Team;
616 if(!team) team = plr->GetTeam();
618 if (team == TeamID)
619 UpdatePlayerScore(plr, SCORE_BONUS_HONOR, Honor);
623 void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID)
625 FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
627 if (!factionEntry)
628 return;
630 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
632 Player *plr = objmgr.GetPlayer(itr->first);
634 if (!plr)
636 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
637 continue;
640 uint32 team = itr->second.Team;
641 if(!team) team = plr->GetTeam();
643 if (team == TeamID)
644 plr->GetReputationMgr().ModifyReputation(factionEntry, Reputation);
648 void BattleGround::UpdateWorldState(uint32 Field, uint32 Value)
650 WorldPacket data;
651 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
652 SendPacketToAll(&data);
655 void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source)
657 WorldPacket data;
658 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
659 Source->GetSession()->SendPacket(&data);
662 void BattleGround::EndBattleGround(uint32 winner)
664 this->RemoveFromBGFreeSlotQueue();
666 ArenaTeam * winner_arena_team = NULL;
667 ArenaTeam * loser_arena_team = NULL;
668 uint32 loser_rating = 0;
669 uint32 winner_rating = 0;
670 WorldPacket data;
671 int32 winmsg_id = 0;
673 if (winner == ALLIANCE)
675 winmsg_id = isBattleGround() ? LANG_BG_A_WINS : LANG_ARENA_GOLD_WINS;
677 PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound
679 SetWinner(WINNER_ALLIANCE);
681 else if (winner == HORDE)
683 winmsg_id = isBattleGround() ? LANG_BG_H_WINS : LANG_ARENA_GREEN_WINS;
685 PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound
687 SetWinner(WINNER_HORDE);
689 else
691 SetWinner(3);
694 SetStatus(STATUS_WAIT_LEAVE);
695 //we must set it this way, because end time is sent in packet!
696 m_EndTime = TIME_TO_AUTOREMOVE;
698 // arena rating calculation
699 if (isArena() && isRated())
701 winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(winner));
702 loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(winner)));
703 if (winner_arena_team && loser_arena_team)
705 loser_rating = loser_arena_team->GetStats().rating;
706 winner_rating = winner_arena_team->GetStats().rating;
707 int32 winner_change = winner_arena_team->WonAgainst(loser_rating);
708 int32 loser_change = loser_arena_team->LostAgainst(winner_rating);
709 sLog.outDebug("--- Winner rating: %u, Loser rating: %u, Winner change: %u, Losser change: %u ---", winner_rating, loser_rating, winner_change, loser_change);
710 SetArenaTeamRatingChangeForTeam(winner, winner_change);
711 SetArenaTeamRatingChangeForTeam(GetOtherTeam(winner), loser_change);
713 else
715 SetArenaTeamRatingChangeForTeam(ALLIANCE, 0);
716 SetArenaTeamRatingChangeForTeam(HORDE, 0);
720 for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
722 Player *plr = objmgr.GetPlayer(itr->first);
723 uint32 team = itr->second.Team;
725 if (!plr)
727 //if rated arena match - make member lost!
728 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
730 if (team == winner)
731 winner_arena_team->OfflineMemberLost(itr->first, loser_rating);
732 else
733 loser_arena_team->OfflineMemberLost(itr->first, winner_rating);
735 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
736 continue;
739 // should remove spirit of redemption
740 if (plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
741 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
743 if (!plr->isAlive())
745 plr->ResurrectPlayer(1.0f);
746 plr->SpawnCorpseBones();
748 else
750 //needed cause else in av some creatures will kill the players at the end
751 plr->CombatStop();
752 plr->getHostileRefManager().deleteReferences();
755 //this line is obsolete - team is set ALWAYS
756 //if(!team) team = plr->GetTeam();
758 // per player calculation
759 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
761 if (team == winner)
763 // update achievement BEFORE personal rating update
764 ArenaTeamMember* member = winner_arena_team->GetMember(plr->GetGUID());
765 if (member)
766 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, member->personal_rating);
768 winner_arena_team->MemberWon(plr,loser_rating);
770 else
772 loser_arena_team->MemberLost(plr,winner_rating);
774 // Arena lost => reset the win_rated_arena having the "no_loose" condition
775 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE);
779 if (team == winner)
781 RewardMark(plr,ITEM_WINNER_COUNT);
782 RewardQuestComplete(plr);
784 else
785 RewardMark(plr,ITEM_LOSER_COUNT);
787 plr->CombatStopWithPets(true);
789 BlockMovement(plr);
791 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
792 plr->GetSession()->SendPacket(&data);
794 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
795 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType());
796 plr->GetSession()->SendPacket(&data);
797 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1);
800 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
802 // update arena points only after increasing the player's match count!
803 //obsolete: winner_arena_team->UpdateArenaPointsHelper();
804 //obsolete: loser_arena_team->UpdateArenaPointsHelper();
805 // save the stat changes
806 winner_arena_team->SaveToDB();
807 loser_arena_team->SaveToDB();
808 // send updated arena team stats to players
809 // this way all arena team members will get notified, not only the ones who participated in this match
810 winner_arena_team->NotifyStatsChanged();
811 loser_arena_team->NotifyStatsChanged();
814 if (winmsg_id)
815 SendMessageToAll(winmsg_id, CHAT_MSG_BG_SYSTEM_NEUTRAL);
818 uint32 BattleGround::GetBonusHonorFromKill(uint32 kills) const
820 //variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill)
821 return MaNGOS::Honor::hk_honor_at_level(GetMaxLevel(), kills);
824 uint32 BattleGround::GetBattlemasterEntry() const
826 switch(GetTypeID())
828 case BATTLEGROUND_AV: return 15972;
829 case BATTLEGROUND_WS: return 14623;
830 case BATTLEGROUND_AB: return 14879;
831 case BATTLEGROUND_EY: return 22516;
832 case BATTLEGROUND_NA: return 20200;
833 default: return 0;
837 void BattleGround::RewardMark(Player *plr,uint32 count)
839 BattleGroundMarks mark;
840 bool IsSpell;
841 switch(GetTypeID())
843 case BATTLEGROUND_AV:
844 IsSpell = true;
845 if (count == ITEM_WINNER_COUNT)
846 mark = SPELL_AV_MARK_WINNER;
847 else
848 mark = SPELL_AV_MARK_LOSER;
849 break;
850 case BATTLEGROUND_WS:
851 IsSpell = true;
852 if (count == ITEM_WINNER_COUNT)
853 mark = SPELL_WS_MARK_WINNER;
854 else
855 mark = SPELL_WS_MARK_LOSER;
856 break;
857 case BATTLEGROUND_AB:
858 IsSpell = true;
859 if (count == ITEM_WINNER_COUNT)
860 mark = SPELL_AB_MARK_WINNER;
861 else
862 mark = SPELL_AB_MARK_LOSER;
863 break;
864 case BATTLEGROUND_EY:
865 IsSpell = false;
866 mark = ITEM_EY_MARK_OF_HONOR;
867 break;
868 default:
869 return;
872 if (IsSpell)
873 RewardSpellCast(plr,mark);
874 else
875 RewardItem(plr,mark,count);
878 void BattleGround::RewardSpellCast(Player *plr, uint32 spell_id)
880 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
881 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
882 return;
884 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
885 if(!spellInfo)
887 sLog.outError("Battleground reward casting spell %u not exist.",spell_id);
888 return;
891 plr->CastSpell(plr, spellInfo, true);
894 void BattleGround::RewardItem(Player *plr, uint32 item_id, uint32 count)
896 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
897 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
898 return;
900 ItemPosCountVec dest;
901 uint32 no_space_count = 0;
902 uint8 msg = plr->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item_id, count, &no_space_count );
904 if( msg == EQUIP_ERR_ITEM_NOT_FOUND)
906 sLog.outErrorDb("Battleground reward item (Entry %u) not exist in `item_template`.",item_id);
907 return;
910 if( msg != EQUIP_ERR_OK ) // convert to possible store amount
911 count -= no_space_count;
913 if( count != 0 && !dest.empty()) // can add some
914 if (Item* item = plr->StoreNewItem( dest, item_id, true, 0))
915 plr->SendNewItem(item,count,true,false);
917 if (no_space_count > 0)
918 SendRewardMarkByMail(plr,item_id,no_space_count);
921 void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count)
923 uint32 bmEntry = GetBattlemasterEntry();
924 if (!bmEntry)
925 return;
927 ItemPrototype const* markProto = ObjectMgr::GetItemPrototype(mark);
928 if (!markProto)
929 return;
931 if (Item* markItem = Item::CreateItem(mark,count,plr))
933 // save new item before send
934 markItem->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
936 // subject: item name
937 std::string subject = markProto->Name1;
938 int loc_idx = plr->GetSession()->GetSessionDbLocaleIndex();
939 if (loc_idx >= 0 )
940 if (ItemLocale const *il = objmgr.GetItemLocale(markProto->ItemId))
941 if (il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty())
942 subject = il->Name[loc_idx];
944 // text
945 std::string textFormat = plr->GetSession()->GetMangosString(LANG_BG_MARK_BY_MAIL);
946 char textBuf[300];
947 snprintf(textBuf,300,textFormat.c_str(),GetName(),GetName());
948 uint32 itemTextId = objmgr.CreateItemText( textBuf );
950 MailDraft(subject, itemTextId)
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(uint64 guid, bool Transport, bool SendPacket)
987 uint32 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 = objmgr.GetPlayer(guid);
1008 // should remove spirit of redemption
1009 if (plr && plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
1010 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
1012 if(plr && !plr->isAlive()) // resurrect on exit
1014 plr->ResurrectPlayer(1.0f);
1015 plr->SpawnCorpseBones();
1018 RemovePlayer(plr, guid); // BG subclass specific code
1020 if(participant) // if the player was a match participant, remove auras, calc rating, update queue
1022 BattleGroundTypeId bgTypeId = GetTypeID();
1023 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1024 if (plr)
1026 plr->ClearAfkReports();
1028 if(!team) team = plr->GetTeam();
1030 // if arena, remove the specific arena auras
1031 if (isArena())
1033 plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out
1034 bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing)
1036 // unsummon current and summon old pet if there was one and there isn't a current pet
1037 plr->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT);
1038 plr->ResummonPetTemporaryUnSummonedIfAny();
1040 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
1042 //left a rated match while the encounter was in progress, consider as loser
1043 ArenaTeam * winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
1044 ArenaTeam * loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
1045 if (winner_arena_team && loser_arena_team)
1046 loser_arena_team->MemberLost(plr,winner_arena_team->GetRating());
1049 if (SendPacket)
1051 WorldPacket data;
1052 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0, 0);
1053 plr->GetSession()->SendPacket(&data);
1056 // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg
1057 plr->RemoveBattleGroundQueueId(bgQueueTypeId);
1059 else
1060 // removing offline participant
1062 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
1064 //left a rated match while the encounter was in progress, consider as loser
1065 ArenaTeam * others_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
1066 ArenaTeam * players_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
1067 if (others_arena_team && players_arena_team)
1068 players_arena_team->OfflineMemberLost(guid, others_arena_team->GetRating());
1072 // remove from raid group if player is member
1073 if (Group *group = GetBgRaid(team))
1075 if( !group->RemoveMember(guid, 0) ) // group was disbanded
1077 SetBgRaid(team, NULL);
1078 delete group;
1081 DecreaseInvitedCount(team);
1082 //we should update battleground queue, but only if bg isn't ending
1083 if (isBattleGround() && GetStatus() < STATUS_WAIT_LEAVE)
1085 // a player has left the battleground, so there are free slots -> add to queue
1086 AddToBGFreeSlotQueue();
1087 sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, GetQueueId());
1090 // Let others know
1091 WorldPacket data;
1092 sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, guid);
1093 SendPacketToTeam(team, &data, plr, false);
1096 if (plr)
1098 // Do next only if found in battleground
1099 plr->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG.
1100 // reset destination bg team
1101 plr->SetBGTeam(0);
1103 if (Transport)
1104 plr->TeleportToBGEntryPoint();
1106 sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName());
1109 //battleground object will be deleted next BattleGround::Update() call
1112 // this method is called when no players remains in battleground
1113 void BattleGround::Reset()
1115 SetQueueId(QUEUE_ID_MAX_LEVEL_19);
1116 SetWinner(WINNER_NONE);
1117 SetStatus(STATUS_WAIT_QUEUE);
1118 SetStartTime(0);
1119 SetEndTime(0);
1120 SetArenaType(0);
1121 SetRated(false);
1123 m_Events = 0;
1125 // door-event2 is always 0
1126 m_ActiveEvents[BG_EVENT_DOOR] = 0;
1127 if (isArena())
1129 m_ActiveEvents[ARENA_BUFF_EVENT] = BG_EVENT_NONE;
1130 m_ArenaBuffSpawned = false;
1133 if (m_InvitedAlliance > 0 || m_InvitedHorde > 0)
1134 sLog.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde);
1136 m_InvitedAlliance = 0;
1137 m_InvitedHorde = 0;
1138 m_InBGFreeSlotQueue = false;
1140 m_Players.clear();
1142 for(BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr)
1143 delete itr->second;
1144 m_PlayerScores.clear();
1147 void BattleGround::StartBattleGround()
1149 SetStartTime(0);
1151 // add BG to free slot queue
1152 AddToBGFreeSlotQueue();
1154 // add bg to update list
1155 // This must be done here, because we need to have already invited some players when first BG::Update() method is executed
1156 // and it doesn't matter if we call StartBattleGround() more times, because m_BattleGrounds is a map and instance id never changes
1157 sBattleGroundMgr.AddBattleGround(GetInstanceID(), GetTypeID(), this);
1160 void BattleGround::AddPlayer(Player *plr)
1162 // remove afk from player
1163 if (plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK))
1164 plr->ToggleAFK();
1166 // score struct must be created in inherited class
1168 uint64 guid = plr->GetGUID();
1169 uint32 team = plr->GetBGTeam();
1171 BattleGroundPlayer bp;
1172 bp.OfflineRemoveTime = 0;
1173 bp.Team = team;
1175 // Add to list/maps
1176 m_Players[guid] = bp;
1178 UpdatePlayersCountByTeam(team, false); // +1 player
1180 WorldPacket data;
1181 sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr);
1182 SendPacketToTeam(team, &data, plr, false);
1184 // add arena specific auras
1185 if (isArena())
1187 plr->RemoveArenaSpellCooldowns();
1188 plr->RemoveArenaAuras();
1189 plr->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT);
1190 if(team == ALLIANCE) // gold
1192 if (plr->GetTeam() == HORDE)
1193 plr->CastSpell(plr, SPELL_HORDE_GOLD_FLAG,true);
1194 else
1195 plr->CastSpell(plr, SPELL_ALLIANCE_GOLD_FLAG,true);
1197 else // green
1199 if (plr->GetTeam() == HORDE)
1200 plr->CastSpell(plr, SPELL_HORDE_GREEN_FLAG,true);
1201 else
1202 plr->CastSpell(plr, SPELL_ALLIANCE_GREEN_FLAG,true);
1205 plr->DestroyConjuredItems(true);
1206 plr->UnsummonPetTemporaryIfAny();
1208 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1210 plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true);
1212 plr->SetHealth(plr->GetMaxHealth());
1213 plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA));
1216 else
1218 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1219 plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells.
1222 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1223 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1225 // setup BG group membership
1226 PlayerAddedToBGCheckIfBGIsRunning(plr);
1227 AddOrSetPlayerToCorrectBgGroup(plr, guid, team);
1229 // Log
1230 sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName());
1233 /* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */
1234 void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team)
1236 Group* group = GetBgRaid(team);
1237 if(!group) // first player joined
1239 group = new Group;
1240 SetBgRaid(team, group);
1241 group->Create(plr_guid, plr->GetName());
1243 else // raid already exist
1245 if (group->IsMember(plr_guid))
1247 uint8 subgroup = group->GetMemberGroup(plr_guid);
1248 plr->SetBattleGroundRaid(group, subgroup);
1250 else
1252 group->AddMember(plr_guid, plr->GetName());
1253 if (Group* originalGroup = plr->GetOriginalGroup())
1254 if (originalGroup->IsLeader(plr_guid))
1255 group->ChangeLeader(plr_guid);
1260 // This method should be called when player logs into running battleground
1261 void BattleGround::EventPlayerLoggedIn(Player* player, uint64 plr_guid)
1263 // player is correct pointer
1264 for(std::deque<uint64>::iterator itr = m_OfflineQueue.begin(); itr != m_OfflineQueue.end(); ++itr)
1266 if (*itr == plr_guid)
1268 m_OfflineQueue.erase(itr);
1269 break;
1272 m_Players[plr_guid].OfflineRemoveTime = 0;
1273 PlayerAddedToBGCheckIfBGIsRunning(player);
1274 // if battleground is starting, then add preparation aura
1275 // we don't have to do that, because preparation aura isn't removed when player logs out
1278 // This method should be called when player logs out from running battleground
1279 void BattleGround::EventPlayerLoggedOut(Player* player)
1281 // player is correct pointer, it is checked in WorldSession::LogoutPlayer()
1282 m_OfflineQueue.push_back(player->GetGUID());
1283 m_Players[player->GetGUID()].OfflineRemoveTime = sWorld.GetGameTime() + MAX_OFFLINE_TIME;
1284 if (GetStatus() == STATUS_IN_PROGRESS)
1286 if (isBattleGround())
1287 EventPlayerDroppedFlag(player);
1288 else
1290 //1 player is logging out, if it is the last, then end arena!
1291 if (GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam())))
1292 EndBattleGround(GetOtherTeam(player->GetTeam()));
1297 /* This method should be called only once ... it adds pointer to queue */
1298 void BattleGround::AddToBGFreeSlotQueue()
1300 // make sure to add only once
1301 if (!m_InBGFreeSlotQueue && isBattleGround())
1303 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this);
1304 m_InBGFreeSlotQueue = true;
1308 /* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/
1309 void BattleGround::RemoveFromBGFreeSlotQueue()
1311 // set to be able to re-add if needed
1312 m_InBGFreeSlotQueue = false;
1313 // uncomment this code when battlegrounds will work like instances
1314 for (BGFreeSlotQueueType::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr)
1316 if ((*itr)->GetInstanceID() == m_InstanceID)
1318 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr);
1319 return;
1324 // get the number of free slots for team
1325 // returns the number how many players can join battleground to MaxPlayersPerTeam
1326 uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const
1328 //return free slot count to MaxPlayerPerTeam
1329 if (GetStatus() == STATUS_WAIT_JOIN || GetStatus() == STATUS_IN_PROGRESS)
1330 return (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0;
1332 return 0;
1335 bool BattleGround::HasFreeSlots() const
1337 return GetPlayersSize() < GetMaxPlayers();
1340 void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value)
1342 //this procedure is called from virtual function implemented in bg subclass
1343 BattleGroundScoreMap::const_iterator itr = m_PlayerScores.find(Source->GetGUID());
1345 if(itr == m_PlayerScores.end()) // player not found...
1346 return;
1348 switch(type)
1350 case SCORE_KILLING_BLOWS: // Killing blows
1351 itr->second->KillingBlows += value;
1352 break;
1353 case SCORE_DEATHS: // Deaths
1354 itr->second->Deaths += value;
1355 break;
1356 case SCORE_HONORABLE_KILLS: // Honorable kills
1357 itr->second->HonorableKills += value;
1358 break;
1359 case SCORE_BONUS_HONOR: // Honor bonus
1360 // do not add honor in arenas
1361 if (isBattleGround())
1363 // reward honor instantly
1364 if (Source->RewardHonor(NULL, 1, value))
1365 itr->second->BonusHonor += value;
1367 break;
1368 //used only in EY, but in MSG_PVP_LOG_DATA opcode
1369 case SCORE_DAMAGE_DONE: // Damage Done
1370 itr->second->DamageDone += value;
1371 break;
1372 case SCORE_HEALING_DONE: // Healing Done
1373 itr->second->HealingDone += value;
1374 break;
1375 default:
1376 sLog.outError("BattleGround: Unknown player score type %u", type);
1377 break;
1381 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)
1383 // must be created this way, adding to godatamap would add it to the base map of the instance
1384 // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
1385 // so we must create it specific for this instance
1386 GameObject * go = new GameObject;
1387 if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry, GetBgMap(),
1388 PHASEMASK_NORMAL, x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,GO_STATE_READY))
1390 sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry);
1391 sLog.outError("Cannot create gameobject template %u! BattleGround not created!", entry);
1392 delete go;
1393 return false;
1396 uint32 guid = go->GetGUIDLow();
1398 // without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata
1399 // 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
1400 GameObjectData& data = objmgr.NewGOData(guid);
1402 data.id = entry;
1403 data.mapid = GetMapId();
1404 data.posX = x;
1405 data.posY = y;
1406 data.posZ = z;
1407 data.orientation = o;
1408 data.rotation0 = rotation0;
1409 data.rotation1 = rotation1;
1410 data.rotation2 = rotation2;
1411 data.rotation3 = rotation3;
1412 data.spawntimesecs = respawnTime;
1413 data.spawnMask = 1;
1414 data.animprogress = 100;
1415 data.go_state = 1;
1417 // add to world, so it can be later looked up from HashMapHolder
1418 go->AddToWorld();
1419 m_BgObjects[type] = go->GetGUID();
1420 return true;
1423 //some doors aren't despawned so we cannot handle their closing in gameobject::update()
1424 //it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
1425 void BattleGround::DoorClose(uint64 const& guid)
1427 GameObject *obj = GetBgMap()->GetGameObject(guid);
1428 if (obj)
1430 //if doors are open, close it
1431 if (obj->getLootState() == GO_ACTIVATED && obj->GetGoState() != GO_STATE_READY)
1433 //change state to allow door to be closed
1434 obj->SetLootState(GO_READY);
1435 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1438 else
1440 sLog.outError("BattleGround: Door object not found (cannot close doors)");
1444 void BattleGround::DoorOpen(uint64 const& guid)
1446 GameObject *obj = GetBgMap()->GetGameObject(guid);
1447 if (obj)
1449 //change state to be sure they will be opened
1450 obj->SetLootState(GO_READY);
1451 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1453 else
1455 sLog.outError("BattleGround: Door object not found! - doors will be closed.");
1459 void BattleGround::OnObjectDBLoad(Creature* creature)
1461 const BattleGroundEventIdx eventId = sBattleGroundMgr.GetCreatureEventIndex(creature->GetDBTableGUIDLow());
1462 if (eventId.event1 == BG_EVENT_NONE)
1463 return;
1464 m_EventObjects[MAKE_PAIR32(eventId.event1, eventId.event2)].creatures.push_back(creature->GetGUID());
1465 if (!IsActiveEvent(eventId.event1, eventId.event2))
1466 SpawnBGCreature(creature->GetGUID(), RESPAWN_ONE_DAY);
1469 uint64 BattleGround::GetSingleCreatureGuid(uint8 event1, uint8 event2)
1471 BGCreatures::const_iterator itr = m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.begin();
1472 if (itr != m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.end())
1473 return *itr;
1474 return 0;
1477 void BattleGround::OnObjectDBLoad(GameObject* obj)
1479 const BattleGroundEventIdx eventId = sBattleGroundMgr.GetGameObjectEventIndex(obj->GetDBTableGUIDLow());
1480 if (eventId.event1 == BG_EVENT_NONE)
1481 return;
1482 m_EventObjects[MAKE_PAIR32(eventId.event1, eventId.event2)].gameobjects.push_back(obj->GetGUID());
1483 if (!IsActiveEvent(eventId.event1, eventId.event2))
1485 SpawnBGObject(obj->GetGUID(), RESPAWN_ONE_DAY);
1487 else
1489 // it's possible, that doors aren't spawned anymore (wsg)
1490 if (GetStatus() >= STATUS_IN_PROGRESS && IsDoor(eventId.event1, eventId.event2))
1491 DoorOpen(obj->GetGUID());
1495 bool BattleGround::IsDoor(uint8 event1, uint8 event2)
1497 if (event1 == BG_EVENT_DOOR)
1499 if (event2 > 0)
1501 sLog.outError("BattleGround too high event2 for event1:%i", event1);
1502 return false;
1504 return true;
1506 return false;
1509 void BattleGround::OpenDoorEvent(uint8 event1, uint8 event2 /*=0*/)
1511 if (!IsDoor(event1, event2))
1513 sLog.outError("BattleGround:OpenDoorEvent this is no door event1:%u event2:%u", event1, event2);
1514 return;
1516 if (!IsActiveEvent(event1, event2)) // maybe already despawned (eye)
1518 sLog.outError("BattleGround:OpenDoorEvent this event isn't active event1:%u event2:%u", event1, event2);
1519 return;
1521 BGObjects::const_iterator itr = m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.begin();
1522 for(; itr != m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.end(); ++itr)
1523 DoorOpen(*itr);
1526 void BattleGround::SpawnEvent(uint8 event1, uint8 event2, bool spawn)
1528 // stop if we want to spawn something which was already spawned
1529 // or despawn something which was already despawned
1530 if (event2 == BG_EVENT_NONE || (spawn && m_ActiveEvents[event1] == event2)
1531 || (!spawn && m_ActiveEvents[event1] != event2))
1532 return;
1534 if (spawn)
1536 // if event gets spawned, the current active event mus get despawned
1537 SpawnEvent(event1, m_ActiveEvents[event1], false);
1538 m_ActiveEvents[event1] = event2; // set this event to active
1540 else
1541 m_ActiveEvents[event1] = BG_EVENT_NONE; // no event active if event2 gets despawned
1543 BGCreatures::const_iterator itr = m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.begin();
1544 for(; itr != m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.end(); ++itr)
1545 SpawnBGCreature(*itr, (spawn) ? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY);
1546 BGObjects::const_iterator itr2 = m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.begin();
1547 for(; itr2 != m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.end(); ++itr2)
1548 SpawnBGObject(*itr2, (spawn) ? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY);
1551 void BattleGround::SpawnBGObject(uint64 const& guid, uint32 respawntime)
1553 Map* map = GetBgMap();
1555 GameObject *obj = map->GetGameObject(guid);
1556 if(!obj)
1557 return;
1558 if (respawntime == 0)
1560 //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again
1561 if (obj->getLootState() == GO_JUST_DEACTIVATED)
1562 obj->SetLootState(GO_READY);
1563 obj->SetRespawnTime(0);
1564 map->Add(obj);
1566 else
1568 map->Add(obj);
1569 obj->SetRespawnTime(respawntime);
1570 obj->SetLootState(GO_JUST_DEACTIVATED);
1574 void BattleGround::SpawnBGCreature(uint64 const& guid, uint32 respawntime)
1576 Map* map = GetBgMap();
1578 Creature* obj = map->GetCreature(guid);
1579 if (!obj)
1580 return;
1581 if (respawntime == 0)
1583 obj->Respawn();
1584 map->Add(obj);
1586 else
1588 map->Add(obj);
1589 obj->setDeathState(JUST_DIED);
1590 obj->SetRespawnDelay(respawntime);
1591 obj->RemoveCorpse();
1595 bool BattleGround::DelObject(uint32 type)
1597 if (!m_BgObjects[type])
1598 return true;
1600 GameObject *obj = GetBgMap()->GetGameObject(m_BgObjects[type]);
1601 if (!obj)
1603 sLog.outError("Can't find gobject guid: %u",GUID_LOPART(m_BgObjects[type]));
1604 return false;
1607 obj->SetRespawnTime(0); // not save respawn time
1608 obj->Delete();
1609 m_BgObjects[type] = 0;
1610 return true;
1613 void BattleGround::SendMessageToAll(int32 entry, ChatMsg type, Player const* source)
1615 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source);
1616 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1617 BroadcastWorker(bg_do);
1620 void BattleGround::SendYellToAll(int32 entry, uint32 language, uint64 const& guid)
1622 Creature* source = GetBgMap()->GetCreature(guid);
1623 if(!source)
1624 return;
1625 MaNGOS::BattleGroundYellBuilder bg_builder(language, entry, source);
1626 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundYellBuilder> bg_do(bg_builder);
1627 BroadcastWorker(bg_do);
1630 void BattleGround::PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...)
1632 va_list ap;
1633 va_start(ap, source);
1635 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source, &ap);
1636 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1637 BroadcastWorker(bg_do);
1639 va_end(ap);
1642 void BattleGround::SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 arg1, int32 arg2)
1644 MaNGOS::BattleGround2ChatBuilder bg_builder(type, entry, source, arg1, arg2);
1645 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGround2ChatBuilder> bg_do(bg_builder);
1646 BroadcastWorker(bg_do);
1649 void BattleGround::SendYell2ToAll(int32 entry, uint32 language, uint64 const& guid, int32 arg1, int32 arg2)
1651 Creature* source = GetBgMap()->GetCreature(guid);
1652 if(!source)
1653 return;
1654 MaNGOS::BattleGround2YellBuilder bg_builder(language, entry, source, arg1, arg2);
1655 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGround2YellBuilder> bg_do(bg_builder);
1656 BroadcastWorker(bg_do);
1659 void BattleGround::EndNow()
1661 RemoveFromBGFreeSlotQueue();
1662 SetStatus(STATUS_WAIT_LEAVE);
1663 SetEndTime(0);
1667 important notice:
1668 buffs aren't spawned/despawned when players captures anything
1669 buffs are in their positions when battleground starts
1671 void BattleGround::HandleTriggerBuff(uint64 const& go_guid)
1673 GameObject *obj = GetBgMap()->GetGameObject(go_guid);
1674 if (!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned())
1675 return;
1677 // static buffs are already handled just by database and don't need
1678 // battleground code
1679 if (!m_BuffChange)
1681 obj->SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
1682 return;
1685 // change buff type, when buff is used:
1686 // TODO this can be done when poolsystem works for instances
1687 int32 index = m_BgObjects.size() - 1;
1688 while (index >= 0 && m_BgObjects[index] != go_guid)
1689 index--;
1690 if (index < 0)
1692 sLog.outError("BattleGround (Type: %u) has buff gameobject (Guid: %u Entry: %u Type:%u) but it hasn't that object in its internal data",GetTypeID(),GUID_LOPART(go_guid),obj->GetEntry(),obj->GetGoType());
1693 return;
1696 //randomly select new buff
1697 uint8 buff = urand(0, 2);
1698 uint32 entry = obj->GetEntry();
1699 if (m_BuffChange && entry != Buff_Entries[buff])
1701 //despawn current buff
1702 SpawnBGObject(m_BgObjects[index], RESPAWN_ONE_DAY);
1703 //set index for new one
1704 for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex)
1706 if (entry == Buff_Entries[currBuffTypeIndex])
1708 index -= currBuffTypeIndex;
1709 index += buff;
1714 SpawnBGObject(m_BgObjects[index], BUFF_RESPAWN_TIME);
1717 void BattleGround::HandleKillPlayer( Player *player, Player *killer )
1719 // add +1 deaths
1720 UpdatePlayerScore(player, SCORE_DEATHS, 1);
1722 // add +1 kills to group and +1 killing_blows to killer
1723 if (killer)
1725 UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1);
1726 UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1);
1728 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1730 Player *plr = objmgr.GetPlayer(itr->first);
1732 if (!plr || plr == killer)
1733 continue;
1735 if (plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player))
1736 UpdatePlayerScore(plr, SCORE_HONORABLE_KILLS, 1);
1740 // to be able to remove insignia -- ONLY IN BattleGrounds
1741 if (!isArena())
1742 player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE );
1745 // return the player's team based on battlegroundplayer info
1746 // used in same faction arena matches mainly
1747 uint32 BattleGround::GetPlayerTeam(uint64 guid)
1749 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1750 if (itr!=m_Players.end())
1751 return itr->second.Team;
1752 return 0;
1755 bool BattleGround::IsPlayerInBattleGround(uint64 guid)
1757 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1758 if (itr != m_Players.end())
1759 return true;
1760 return false;
1763 void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player* plr)
1765 if (GetStatus() != STATUS_WAIT_LEAVE)
1766 return;
1768 WorldPacket data;
1769 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1771 BlockMovement(plr);
1773 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
1774 plr->GetSession()->SendPacket(&data);
1776 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType());
1777 plr->GetSession()->SendPacket(&data);
1780 uint32 BattleGround::GetAlivePlayersCountByTeam(uint32 Team) const
1782 int count = 0;
1783 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1785 if (itr->second.Team == Team)
1787 Player * pl = objmgr.GetPlayer(itr->first);
1788 if (pl && pl->isAlive())
1789 ++count;
1792 return count;
1795 void BattleGround::CheckArenaWinConditions()
1797 if (!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE))
1798 EndBattleGround(HORDE);
1799 else if (GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE))
1800 EndBattleGround(ALLIANCE);
1803 void BattleGround::SetBgRaid( uint32 TeamID, Group *bg_raid )
1805 Group* &old_raid = TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE];
1806 if(old_raid) old_raid->SetBattlegroundGroup(NULL);
1807 if(bg_raid) bg_raid->SetBattlegroundGroup(this);
1808 old_raid = bg_raid;
1811 WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard( Player* player )
1813 return objmgr.GetClosestGraveYard( player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam() );
1816 bool BattleGround::IsTeamScoreInRange(uint32 team, uint32 minScore, uint32 maxScore) const
1818 BattleGroundTeamId team_idx = GetTeamIndexByTeamId(team);
1819 uint32 score = (m_TeamScores[team_idx] < 0) ? 0 : uint32(m_TeamScores[team_idx]);
1820 return score >= minScore && score <= maxScore;