[9290] Some cleanups in realmd, no functional changes
[getmangos.git] / src / game / BattleGround.cpp
blob241e4bfbde050a602370373cd96b44d3a45ed372
1 /*
2 * Copyright (C) 2005-2010 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 "ObjectDefines.h"
31 #include "ObjectMgr.h"
32 #include "WorldPacket.h"
33 #include "Util.h"
34 #include "Formulas.h"
35 #include "GridNotifiersImpl.h"
37 namespace MaNGOS
39 class BattleGroundChatBuilder
41 public:
42 BattleGroundChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, va_list* args = NULL)
43 : i_msgtype(msgtype), i_textId(textId), i_source(source), i_args(args) {}
44 void operator()(WorldPacket& data, int32 loc_idx)
46 char const* text = sObjectMgr.GetMangosString(i_textId,loc_idx);
48 if (i_args)
50 // we need copy va_list before use or original va_list will corrupted
51 va_list ap;
52 va_copy(ap,*i_args);
54 char str [2048];
55 vsnprintf(str,2048,text, ap );
56 va_end(ap);
58 do_helper(data,&str[0]);
60 else
61 do_helper(data,text);
63 private:
64 void do_helper(WorldPacket& data, char const* text)
66 uint64 target_guid = i_source ? i_source ->GetGUID() : 0;
68 data << uint8(i_msgtype);
69 data << uint32(LANG_UNIVERSAL);
70 data << uint64(target_guid); // there 0 for BG messages
71 data << uint32(0); // can be chat msg group or something
72 data << uint64(target_guid);
73 data << uint32(strlen(text)+1);
74 data << text;
75 data << uint8(i_source ? i_source->chatTag() : uint8(0));
78 ChatMsg i_msgtype;
79 int32 i_textId;
80 Player const* i_source;
81 va_list* i_args;
84 class BattleGroundYellBuilder
86 public:
87 BattleGroundYellBuilder(uint32 language, int32 textId, Creature const* source, va_list* args = NULL)
88 : i_language(language), i_textId(textId), i_source(source), i_args(args) {}
89 void operator()(WorldPacket& data, int32 loc_idx)
91 char const* text = sObjectMgr.GetMangosString(i_textId,loc_idx);
93 if(i_args)
95 // we need copy va_list before use or original va_list will corrupted
96 va_list ap;
97 va_copy(ap,*i_args);
99 char str [2048];
100 vsnprintf(str,2048,text, ap );
101 va_end(ap);
103 do_helper(data,&str[0]);
105 else
106 do_helper(data,text);
108 private:
109 void do_helper(WorldPacket& data, char const* text)
111 //copyied from BuildMonsterChat
112 data << (uint8)CHAT_MSG_MONSTER_YELL;
113 data << (uint32)i_language;
114 data << (uint64)i_source->GetGUID();
115 data << (uint32)0; //2.1.0
116 data << (uint32)(strlen(i_source->GetName())+1);
117 data << i_source->GetName();
118 data << (uint64)0; //Unit Target - isn't important for bgs
119 data << (uint32)strlen(text)+1;
120 data << text;
121 data << (uint8)0; // ChatTag - for bgs allways 0?
124 uint32 i_language;
125 int32 i_textId;
126 Creature const* i_source;
127 va_list* i_args;
131 class BattleGround2ChatBuilder
133 public:
134 BattleGround2ChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, int32 arg1, int32 arg2)
135 : i_msgtype(msgtype), i_textId(textId), i_source(source), i_arg1(arg1), i_arg2(arg2) {}
136 void operator()(WorldPacket& data, int32 loc_idx)
138 char const* text = sObjectMgr.GetMangosString(i_textId,loc_idx);
139 char const* arg1str = i_arg1 ? sObjectMgr.GetMangosString(i_arg1,loc_idx) : "";
140 char const* arg2str = i_arg2 ? sObjectMgr.GetMangosString(i_arg2,loc_idx) : "";
142 char str [2048];
143 snprintf(str,2048,text, arg1str, arg2str );
145 uint64 target_guid = i_source ? i_source ->GetGUID() : 0;
147 data << uint8(i_msgtype);
148 data << uint32(LANG_UNIVERSAL);
149 data << uint64(target_guid); // there 0 for BG messages
150 data << uint32(0); // can be chat msg group or something
151 data << uint64(target_guid);
152 data << uint32(strlen(str)+1);
153 data << str;
154 data << uint8(i_source ? i_source->chatTag() : uint8(0));
156 private:
158 ChatMsg i_msgtype;
159 int32 i_textId;
160 Player const* i_source;
161 int32 i_arg1;
162 int32 i_arg2;
165 class BattleGround2YellBuilder
167 public:
168 BattleGround2YellBuilder(uint32 language, int32 textId, Creature const* source, int32 arg1, int32 arg2)
169 : i_language(language), i_textId(textId), i_source(source), i_arg1(arg1), i_arg2(arg2) {}
170 void operator()(WorldPacket& data, int32 loc_idx)
172 char const* text = sObjectMgr.GetMangosString(i_textId,loc_idx);
173 char const* arg1str = i_arg1 ? sObjectMgr.GetMangosString(i_arg1,loc_idx) : "";
174 char const* arg2str = i_arg2 ? sObjectMgr.GetMangosString(i_arg2,loc_idx) : "";
176 char str [2048];
177 snprintf(str,2048,text, arg1str, arg2str );
178 //copyied from BuildMonsterChat
179 data << (uint8)CHAT_MSG_MONSTER_YELL;
180 data << (uint32)i_language;
181 data << (uint64)i_source->GetGUID();
182 data << (uint32)0; //2.1.0
183 data << (uint32)(strlen(i_source->GetName())+1);
184 data << i_source->GetName();
185 data << (uint64)0; //Unit Target - isn't important for bgs
186 data << (uint32)strlen(str)+1;
187 data << str;
188 data << (uint8)0; // ChatTag - for bgs allways 0?
190 private:
192 uint32 i_language;
193 int32 i_textId;
194 Creature const* i_source;
195 int32 i_arg1;
196 int32 i_arg2;
198 } // namespace MaNGOS
200 template<class Do>
201 void BattleGround::BroadcastWorker(Do& _do)
203 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
204 if (Player *plr = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER)))
205 _do(plr);
208 BattleGround::BattleGround()
210 m_TypeID = BattleGroundTypeId(0);
211 m_InstanceID = 0;
212 m_Status = STATUS_NONE;
213 m_ClientInstanceID = 0;
214 m_EndTime = 0;
215 m_BracketId = BG_BRACKET_ID_FIRST;
216 m_InvitedAlliance = 0;
217 m_InvitedHorde = 0;
218 m_ArenaType = 0;
219 m_IsArena = false;
220 m_Winner = 2;
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;
229 m_SetDeleteThis = false;
231 m_MaxPlayersPerTeam = 0;
232 m_MaxPlayers = 0;
233 m_MinPlayersPerTeam = 0;
234 m_MinPlayers = 0;
236 m_MapId = 0;
237 m_Map = NULL;
239 m_TeamStartLocX[BG_TEAM_ALLIANCE] = 0;
240 m_TeamStartLocX[BG_TEAM_HORDE] = 0;
242 m_TeamStartLocY[BG_TEAM_ALLIANCE] = 0;
243 m_TeamStartLocY[BG_TEAM_HORDE] = 0;
245 m_TeamStartLocZ[BG_TEAM_ALLIANCE] = 0;
246 m_TeamStartLocZ[BG_TEAM_HORDE] = 0;
248 m_TeamStartLocO[BG_TEAM_ALLIANCE] = 0;
249 m_TeamStartLocO[BG_TEAM_HORDE] = 0;
251 m_ArenaTeamIds[BG_TEAM_ALLIANCE] = 0;
252 m_ArenaTeamIds[BG_TEAM_HORDE] = 0;
254 m_ArenaTeamRatingChanges[BG_TEAM_ALLIANCE] = 0;
255 m_ArenaTeamRatingChanges[BG_TEAM_HORDE] = 0;
257 m_BgRaids[BG_TEAM_ALLIANCE] = NULL;
258 m_BgRaids[BG_TEAM_HORDE] = NULL;
260 m_PlayersCount[BG_TEAM_ALLIANCE] = 0;
261 m_PlayersCount[BG_TEAM_HORDE] = 0;
263 m_TeamScores[BG_TEAM_ALLIANCE] = 0;
264 m_TeamScores[BG_TEAM_HORDE] = 0;
266 m_PrematureCountDown = false;
267 m_PrematureCountDown = 0;
269 m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_2M;
270 m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_1M;
271 m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_30S;
272 m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE;
273 //we must set to some default existing values
274 m_StartMessageIds[BG_STARTING_EVENT_FIRST] = 0;
275 m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE;
276 m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE;
277 m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN;
280 BattleGround::~BattleGround()
282 // remove objects and creatures
283 // (this is done automatically in mapmanager update, when the instance is reset after the reset time)
285 int size = m_BgObjects.size();
286 for(int i = 0; i < size; ++i)
287 DelObject(i);
289 if (GetInstanceID()) // not spam by useless queries in case BG templates
291 // delete creature and go respawn times
292 WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'",GetInstanceID());
293 WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'",GetInstanceID());
294 // delete instance from db
295 CharacterDatabase.PExecute("DELETE FROM instance WHERE id = '%u'",GetInstanceID());
296 // remove from battlegrounds
299 sBattleGroundMgr.RemoveBattleGround(GetInstanceID(), GetTypeID());
301 // unload map
302 // map can be null at bg destruction
303 if (m_Map)
304 m_Map->SetUnload();
306 // remove from bg free slot queue
307 this->RemoveFromBGFreeSlotQueue();
309 for(BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr)
310 delete itr->second;
313 void BattleGround::Update(uint32 diff)
315 if (!GetPlayersSize())
317 // BG is empty
318 // if there are no players invited, delete BG
319 // this will delete arena or bg object, where any player entered
320 // [[ but if you use battleground object again (more battles possible to be played on 1 instance)
321 // then this condition should be removed and code:
322 // if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
323 // this->AddToFreeBGObjectsQueue(); // not yet implemented
324 // should be used instead of current
325 // ]]
326 // BattleGround Template instance cannot be updated, because it would be deleted
327 if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
328 m_SetDeleteThis = true;
329 return;
332 // remove offline players from bg after 5 minutes
333 if (!m_OfflineQueue.empty())
335 BattleGroundPlayerMap::iterator itr = m_Players.find(*(m_OfflineQueue.begin()));
336 if (itr != m_Players.end())
338 if (itr->second.OfflineRemoveTime <= sWorld.GetGameTime())
340 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
341 m_OfflineQueue.pop_front(); // remove from offline queue
342 //do not use itr for anything, because it is erased in RemovePlayerAtLeave()
347 /*********************************************************/
348 /*** BATTLEGROUND BALLANCE SYSTEM ***/
349 /*********************************************************/
351 // if less then minimum players are in on one side, then start premature finish timer
352 if (GetStatus() == STATUS_IN_PROGRESS && !isArena() && sBattleGroundMgr.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE) < GetMinPlayersPerTeam()))
354 if (!m_PrematureCountDown)
356 m_PrematureCountDown = true;
357 m_PrematureCountDownTimer = sBattleGroundMgr.GetPrematureFinishTime();
359 else if (m_PrematureCountDownTimer < diff)
361 // time's up!
362 uint32 winner = 0;
363 if (GetPlayersCountByTeam(ALLIANCE) >= GetMinPlayersPerTeam())
364 winner = ALLIANCE;
365 else if (GetPlayersCountByTeam(HORDE) >= GetMinPlayersPerTeam())
366 winner = HORDE;
368 EndBattleGround(winner);
369 m_PrematureCountDown = false;
371 else
373 uint32 newtime = m_PrematureCountDownTimer - diff;
374 // announce every minute
375 if (newtime > (MINUTE * IN_MILISECONDS))
377 if (newtime / (MINUTE * IN_MILISECONDS) != m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS))
378 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS)));
380 else
382 //announce every 15 seconds
383 if (newtime / (15 * IN_MILISECONDS) != m_PrematureCountDownTimer / (15 * IN_MILISECONDS))
384 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / IN_MILISECONDS));
386 m_PrematureCountDownTimer = newtime;
390 else if (m_PrematureCountDown)
391 m_PrematureCountDown = false;
393 /*********************************************************/
394 /*** ARENA BUFF OBJECT SPAWNING ***/
395 /*********************************************************/
396 if (isArena() && !m_ArenaBuffSpawned)
398 // 60 seconds after start the buffobjects in arena should get spawned
399 if (m_StartTime > uint32(m_StartDelayTimes[BG_STARTING_EVENT_FIRST] + ARENA_SPAWN_BUFF_OBJECTS))
401 SpawnEvent(ARENA_BUFF_EVENT, 0, true);
402 m_ArenaBuffSpawned = true;
406 /*********************************************************/
407 /*** BATTLEGROUND STARTING SYSTEM ***/
408 /*********************************************************/
410 if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize())
412 ModifyStartDelayTime(diff);
414 if (!(m_Events & BG_STARTING_EVENT_1))
416 m_Events |= BG_STARTING_EVENT_1;
418 // setup here, only when at least one player has ported to the map
419 if (!SetupBattleGround())
421 EndNow();
422 return;
425 StartingEventCloseDoors();
426 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FIRST]);
427 //first start warning - 2 or 1 minute, only if defined
428 if (m_StartMessageIds[BG_STARTING_EVENT_FIRST])
429 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FIRST], CHAT_MSG_BG_SYSTEM_NEUTRAL);
431 // After 1 minute or 30 seconds, warning is signalled
432 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_SECOND] && !(m_Events & BG_STARTING_EVENT_2))
434 m_Events |= BG_STARTING_EVENT_2;
435 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_SECOND], CHAT_MSG_BG_SYSTEM_NEUTRAL);
437 // After 30 or 15 seconds, warning is signalled
438 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_THIRD] && !(m_Events & BG_STARTING_EVENT_3))
440 m_Events |= BG_STARTING_EVENT_3;
441 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_THIRD], CHAT_MSG_BG_SYSTEM_NEUTRAL);
443 // delay expired (atfer 2 or 1 minute)
444 else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4))
446 m_Events |= BG_STARTING_EVENT_4;
448 StartingEventOpenDoors();
450 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FOURTH], CHAT_MSG_BG_SYSTEM_NEUTRAL);
451 SetStatus(STATUS_IN_PROGRESS);
452 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FOURTH]);
454 //remove preparation
455 if (isArena())
457 //TODO : add arena sound PlaySoundToAll(SOUND_ARENA_START);
459 for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
460 if (Player *plr = sObjectMgr.GetPlayer(itr->first))
461 plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION);
463 CheckArenaWinConditions();
465 else
468 PlaySoundToAll(SOUND_BG_START);
470 for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
471 if (Player* plr = sObjectMgr.GetPlayer(itr->first))
472 plr->RemoveAurasDueToSpell(SPELL_PREPARATION);
473 //Announce BG starting
474 if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE))
476 sWorld.SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD, GetName(), GetMinLevel(), GetMaxLevel());
482 /*********************************************************/
483 /*** BATTLEGROUND ENDING SYSTEM ***/
484 /*********************************************************/
486 if (GetStatus() == STATUS_WAIT_LEAVE)
488 // remove all players from battleground after 2 minutes
489 m_EndTime -= diff;
490 if (m_EndTime <= 0)
492 m_EndTime = 0;
493 BattleGroundPlayerMap::iterator itr, next;
494 for(itr = m_Players.begin(); itr != m_Players.end(); itr = next)
496 next = itr;
497 ++next;
498 //itr is erased here!
499 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
500 // do not change any battleground's private variables
505 //update start time
506 m_StartTime += diff;
509 void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O)
511 BattleGroundTeamId idx = GetTeamIndexByTeamId(TeamID);
512 m_TeamStartLocX[idx] = X;
513 m_TeamStartLocY[idx] = Y;
514 m_TeamStartLocZ[idx] = Z;
515 m_TeamStartLocO[idx] = O;
518 void BattleGround::SendPacketToAll(WorldPacket *packet)
520 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
522 if (itr->second.OfflineRemoveTime)
523 continue;
524 Player *plr = sObjectMgr.GetPlayer(itr->first);
525 if (plr)
526 plr->GetSession()->SendPacket(packet);
527 else
528 sLog.outError("BattleGround:SendPacketToAll: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
532 void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender, bool self)
534 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
536 if (itr->second.OfflineRemoveTime)
537 continue;
538 Player *plr = sObjectMgr.GetPlayer(itr->first);
539 if (!plr)
541 sLog.outError("BattleGround:SendPacketToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
542 continue;
545 if (!self && sender == plr)
546 continue;
548 uint32 team = itr->second.Team;
549 if(!team) team = plr->GetTeam();
551 if (team == TeamID)
552 plr->GetSession()->SendPacket(packet);
556 void BattleGround::PlaySoundToAll(uint32 SoundID)
558 WorldPacket data;
559 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
560 SendPacketToAll(&data);
563 void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID)
565 WorldPacket data;
567 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
569 if (itr->second.OfflineRemoveTime)
570 continue;
571 Player *plr = sObjectMgr.GetPlayer(itr->first);
573 if (!plr)
575 sLog.outError("BattleGround:PlaySoundToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
576 continue;
579 uint32 team = itr->second.Team;
580 if(!team) team = plr->GetTeam();
582 if (team == TeamID)
584 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
585 plr->GetSession()->SendPacket(&data);
590 void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID)
592 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
594 if (itr->second.OfflineRemoveTime)
595 continue;
596 Player *plr = sObjectMgr.GetPlayer(itr->first);
598 if (!plr)
600 sLog.outError("BattleGround:CastSpellOnTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
601 continue;
604 uint32 team = itr->second.Team;
605 if(!team) team = plr->GetTeam();
607 if (team == TeamID)
608 plr->CastSpell(plr, SpellID, true);
612 void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID)
614 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
616 if (itr->second.OfflineRemoveTime)
617 continue;
618 Player *plr = sObjectMgr.GetPlayer(itr->first);
620 if (!plr)
622 sLog.outError("BattleGround:RewardHonorToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
623 continue;
626 uint32 team = itr->second.Team;
627 if(!team) team = plr->GetTeam();
629 if (team == TeamID)
630 UpdatePlayerScore(plr, SCORE_BONUS_HONOR, Honor);
634 void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID)
636 FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
638 if (!factionEntry)
639 return;
641 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
643 if (itr->second.OfflineRemoveTime)
644 continue;
645 Player *plr = sObjectMgr.GetPlayer(itr->first);
647 if (!plr)
649 sLog.outError("BattleGround:RewardReputationToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
650 continue;
653 uint32 team = itr->second.Team;
654 if(!team) team = plr->GetTeam();
656 if (team == TeamID)
657 plr->GetReputationMgr().ModifyReputation(factionEntry, Reputation);
661 void BattleGround::UpdateWorldState(uint32 Field, uint32 Value)
663 WorldPacket data;
664 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
665 SendPacketToAll(&data);
668 void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source)
670 WorldPacket data;
671 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
672 Source->GetSession()->SendPacket(&data);
675 void BattleGround::EndBattleGround(uint32 winner)
677 this->RemoveFromBGFreeSlotQueue();
679 ArenaTeam * winner_arena_team = NULL;
680 ArenaTeam * loser_arena_team = NULL;
681 uint32 loser_rating = 0;
682 uint32 winner_rating = 0;
683 WorldPacket data;
684 int32 winmsg_id = 0;
686 if (winner == ALLIANCE)
688 winmsg_id = isBattleGround() ? LANG_BG_A_WINS : LANG_ARENA_GOLD_WINS;
690 PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound
692 SetWinner(WINNER_ALLIANCE);
694 else if (winner == HORDE)
696 winmsg_id = isBattleGround() ? LANG_BG_H_WINS : LANG_ARENA_GREEN_WINS;
698 PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound
700 SetWinner(WINNER_HORDE);
702 else
704 SetWinner(3);
707 SetStatus(STATUS_WAIT_LEAVE);
708 //we must set it this way, because end time is sent in packet!
709 m_EndTime = TIME_TO_AUTOREMOVE;
711 // arena rating calculation
712 if (isArena() && isRated())
714 winner_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(winner));
715 loser_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(winner)));
716 if (winner_arena_team && loser_arena_team)
718 loser_rating = loser_arena_team->GetStats().rating;
719 winner_rating = winner_arena_team->GetStats().rating;
720 int32 winner_change = winner_arena_team->WonAgainst(loser_rating);
721 int32 loser_change = loser_arena_team->LostAgainst(winner_rating);
722 sLog.outDebug("--- Winner rating: %u, Loser rating: %u, Winner change: %u, Losser change: %u ---", winner_rating, loser_rating, winner_change, loser_change);
723 SetArenaTeamRatingChangeForTeam(winner, winner_change);
724 SetArenaTeamRatingChangeForTeam(GetOtherTeam(winner), loser_change);
726 else
728 SetArenaTeamRatingChangeForTeam(ALLIANCE, 0);
729 SetArenaTeamRatingChangeForTeam(HORDE, 0);
733 for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
735 uint32 team = itr->second.Team;
737 if (itr->second.OfflineRemoveTime)
739 //if rated arena match - make member lost!
740 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
742 if (team == winner)
743 winner_arena_team->OfflineMemberLost(itr->first, loser_rating);
744 else
745 loser_arena_team->OfflineMemberLost(itr->first, winner_rating);
747 continue;
749 Player *plr = sObjectMgr.GetPlayer(itr->first);
750 if (!plr)
752 sLog.outError("BattleGround:EndBattleGround Player (GUID: %u) not found!", GUID_LOPART(itr->first));
753 continue;
756 // should remove spirit of redemption
757 if (plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
758 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
760 if (!plr->isAlive())
762 plr->ResurrectPlayer(1.0f);
763 plr->SpawnCorpseBones();
765 else
767 //needed cause else in av some creatures will kill the players at the end
768 plr->CombatStop();
769 plr->getHostileRefManager().deleteReferences();
772 //this line is obsolete - team is set ALWAYS
773 //if(!team) team = plr->GetTeam();
775 // per player calculation
776 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
778 if (team == winner)
780 // update achievement BEFORE personal rating update
781 ArenaTeamMember* member = winner_arena_team->GetMember(plr->GetGUID());
782 if (member)
783 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, member->personal_rating);
785 winner_arena_team->MemberWon(plr,loser_rating);
787 else
789 loser_arena_team->MemberLost(plr,winner_rating);
791 // Arena lost => reset the win_rated_arena having the "no_loose" condition
792 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE);
796 if (team == winner)
798 RewardMark(plr,ITEM_WINNER_COUNT);
799 RewardQuestComplete(plr);
801 else
802 RewardMark(plr,ITEM_LOSER_COUNT);
804 plr->CombatStopWithPets(true);
806 BlockMovement(plr);
808 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
809 plr->GetSession()->SendPacket(&data);
811 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
812 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType());
813 plr->GetSession()->SendPacket(&data);
814 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1);
817 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
819 // update arena points only after increasing the player's match count!
820 //obsolete: winner_arena_team->UpdateArenaPointsHelper();
821 //obsolete: loser_arena_team->UpdateArenaPointsHelper();
822 // save the stat changes
823 winner_arena_team->SaveToDB();
824 loser_arena_team->SaveToDB();
825 // send updated arena team stats to players
826 // this way all arena team members will get notified, not only the ones who participated in this match
827 winner_arena_team->NotifyStatsChanged();
828 loser_arena_team->NotifyStatsChanged();
831 if (winmsg_id)
832 SendMessageToAll(winmsg_id, CHAT_MSG_BG_SYSTEM_NEUTRAL);
835 uint32 BattleGround::GetBonusHonorFromKill(uint32 kills) const
837 //variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill)
838 return MaNGOS::Honor::hk_honor_at_level(GetMaxLevel(), kills);
841 uint32 BattleGround::GetBattlemasterEntry() const
843 switch(GetTypeID())
845 case BATTLEGROUND_AV: return 15972;
846 case BATTLEGROUND_WS: return 14623;
847 case BATTLEGROUND_AB: return 14879;
848 case BATTLEGROUND_EY: return 22516;
849 case BATTLEGROUND_NA: return 20200;
850 default: return 0;
854 void BattleGround::RewardMark(Player *plr,uint32 count)
856 BattleGroundMarks mark;
857 bool IsSpell;
858 switch(GetTypeID())
860 case BATTLEGROUND_AV:
861 IsSpell = true;
862 if (count == ITEM_WINNER_COUNT)
863 mark = SPELL_AV_MARK_WINNER;
864 else
865 mark = SPELL_AV_MARK_LOSER;
866 break;
867 case BATTLEGROUND_WS:
868 IsSpell = true;
869 if (count == ITEM_WINNER_COUNT)
870 mark = SPELL_WS_MARK_WINNER;
871 else
872 mark = SPELL_WS_MARK_LOSER;
873 break;
874 case BATTLEGROUND_AB:
875 IsSpell = true;
876 if (count == ITEM_WINNER_COUNT)
877 mark = SPELL_AB_MARK_WINNER;
878 else
879 mark = SPELL_AB_MARK_LOSER;
880 break;
881 case BATTLEGROUND_EY:
882 IsSpell = false;
883 mark = ITEM_EY_MARK_OF_HONOR;
884 break;
885 default:
886 return;
889 if (IsSpell)
890 RewardSpellCast(plr,mark);
891 else
892 RewardItem(plr,mark,count);
895 void BattleGround::RewardSpellCast(Player *plr, uint32 spell_id)
897 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
898 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
899 return;
901 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
902 if(!spellInfo)
904 sLog.outError("Battleground reward casting spell %u not exist.",spell_id);
905 return;
908 plr->CastSpell(plr, spellInfo, true);
911 void BattleGround::RewardItem(Player *plr, uint32 item_id, uint32 count)
913 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
914 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
915 return;
917 ItemPosCountVec dest;
918 uint32 no_space_count = 0;
919 uint8 msg = plr->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item_id, count, &no_space_count );
921 if( msg == EQUIP_ERR_ITEM_NOT_FOUND)
923 sLog.outErrorDb("Battleground reward item (Entry %u) not exist in `item_template`.",item_id);
924 return;
927 if( msg != EQUIP_ERR_OK ) // convert to possible store amount
928 count -= no_space_count;
930 if( count != 0 && !dest.empty()) // can add some
931 if (Item* item = plr->StoreNewItem( dest, item_id, true, 0))
932 plr->SendNewItem(item,count,true,false);
934 if (no_space_count > 0)
935 SendRewardMarkByMail(plr,item_id,no_space_count);
938 void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count)
940 uint32 bmEntry = GetBattlemasterEntry();
941 if (!bmEntry)
942 return;
944 ItemPrototype const* markProto = ObjectMgr::GetItemPrototype(mark);
945 if (!markProto)
946 return;
948 if (Item* markItem = Item::CreateItem(mark,count,plr))
950 // save new item before send
951 markItem->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
953 // subject: item name
954 std::string subject = markProto->Name1;
955 int loc_idx = plr->GetSession()->GetSessionDbLocaleIndex();
956 if (loc_idx >= 0 )
957 if (ItemLocale const *il = sObjectMgr.GetItemLocale(markProto->ItemId))
958 if (il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty())
959 subject = il->Name[loc_idx];
961 // text
962 std::string textFormat = plr->GetSession()->GetMangosString(LANG_BG_MARK_BY_MAIL);
963 char textBuf[300];
964 snprintf(textBuf,300,textFormat.c_str(),GetName(),GetName());
965 uint32 itemTextId = sObjectMgr.CreateItemText( textBuf );
967 MailDraft(subject, itemTextId)
968 .AddItem(markItem)
969 .SendMailTo(plr, MailSender(MAIL_CREATURE, bmEntry));
973 void BattleGround::RewardQuestComplete(Player *plr)
975 uint32 quest;
976 switch(GetTypeID())
978 case BATTLEGROUND_AV:
979 quest = SPELL_AV_QUEST_REWARD;
980 break;
981 case BATTLEGROUND_WS:
982 quest = SPELL_WS_QUEST_REWARD;
983 break;
984 case BATTLEGROUND_AB:
985 quest = SPELL_AB_QUEST_REWARD;
986 break;
987 case BATTLEGROUND_EY:
988 quest = SPELL_EY_QUEST_REWARD;
989 break;
990 default:
991 return;
994 RewardSpellCast(plr, quest);
997 void BattleGround::BlockMovement(Player *plr)
999 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()
1002 void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket)
1004 uint32 team = GetPlayerTeam(guid);
1005 bool participant = false;
1006 // Remove from lists/maps
1007 BattleGroundPlayerMap::iterator itr = m_Players.find(guid);
1008 if (itr != m_Players.end())
1010 UpdatePlayersCountByTeam(team, true); // -1 player
1011 m_Players.erase(itr);
1012 // check if the player was a participant of the match, or only entered through gm command (goname)
1013 participant = true;
1016 BattleGroundScoreMap::iterator itr2 = m_PlayerScores.find(guid);
1017 if (itr2 != m_PlayerScores.end())
1019 delete itr2->second; // delete player's score
1020 m_PlayerScores.erase(itr2);
1023 Player *plr = sObjectMgr.GetPlayer(guid);
1025 // should remove spirit of redemption
1026 if (plr && plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
1027 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
1029 if(plr && !plr->isAlive()) // resurrect on exit
1031 plr->ResurrectPlayer(1.0f);
1032 plr->SpawnCorpseBones();
1035 RemovePlayer(plr, guid); // BG subclass specific code
1037 if(participant) // if the player was a match participant, remove auras, calc rating, update queue
1039 BattleGroundTypeId bgTypeId = GetTypeID();
1040 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1041 if (plr)
1043 plr->ClearAfkReports();
1045 if(!team) team = plr->GetTeam();
1047 // if arena, remove the specific arena auras
1048 if (isArena())
1050 plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out
1051 bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing)
1053 // unsummon current and summon old pet if there was one and there isn't a current pet
1054 plr->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT);
1055 plr->ResummonPetTemporaryUnSummonedIfAny();
1057 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
1059 //left a rated match while the encounter was in progress, consider as loser
1060 ArenaTeam * winner_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
1061 ArenaTeam * loser_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
1062 if (winner_arena_team && loser_arena_team)
1063 loser_arena_team->MemberLost(plr,winner_arena_team->GetRating());
1066 if (SendPacket)
1068 WorldPacket data;
1069 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0, 0);
1070 plr->GetSession()->SendPacket(&data);
1073 // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg
1074 plr->RemoveBattleGroundQueueId(bgQueueTypeId);
1076 else
1077 // removing offline participant
1079 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
1081 //left a rated match while the encounter was in progress, consider as loser
1082 ArenaTeam * others_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
1083 ArenaTeam * players_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
1084 if (others_arena_team && players_arena_team)
1085 players_arena_team->OfflineMemberLost(guid, others_arena_team->GetRating());
1089 // remove from raid group if player is member
1090 if (Group *group = GetBgRaid(team))
1092 if( !group->RemoveMember(guid, 0) ) // group was disbanded
1094 SetBgRaid(team, NULL);
1095 delete group;
1098 DecreaseInvitedCount(team);
1099 //we should update battleground queue, but only if bg isn't ending
1100 if (isBattleGround() && GetStatus() < STATUS_WAIT_LEAVE)
1102 // a player has left the battleground, so there are free slots -> add to queue
1103 AddToBGFreeSlotQueue();
1104 sBattleGroundMgr.ScheduleQueueUpdate(0, 0, bgQueueTypeId, bgTypeId, GetBracketId());
1107 // Let others know
1108 WorldPacket data;
1109 sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, guid);
1110 SendPacketToTeam(team, &data, plr, false);
1113 if (plr)
1115 // Do next only if found in battleground
1116 plr->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG.
1117 // reset destination bg team
1118 plr->SetBGTeam(0);
1120 if (Transport)
1121 plr->TeleportToBGEntryPoint();
1123 sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName());
1126 //battleground object will be deleted next BattleGround::Update() call
1129 // this method is called when no players remains in battleground
1130 void BattleGround::Reset()
1132 SetWinner(WINNER_NONE);
1133 SetStatus(STATUS_WAIT_QUEUE);
1134 SetStartTime(0);
1135 SetEndTime(0);
1136 SetArenaType(0);
1137 SetRated(false);
1139 m_Events = 0;
1141 // door-event2 is always 0
1142 m_ActiveEvents[BG_EVENT_DOOR] = 0;
1143 if (isArena())
1145 m_ActiveEvents[ARENA_BUFF_EVENT] = BG_EVENT_NONE;
1146 m_ArenaBuffSpawned = false;
1149 if (m_InvitedAlliance > 0 || m_InvitedHorde > 0)
1150 sLog.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde);
1152 m_InvitedAlliance = 0;
1153 m_InvitedHorde = 0;
1154 m_InBGFreeSlotQueue = false;
1156 m_Players.clear();
1158 for(BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr)
1159 delete itr->second;
1160 m_PlayerScores.clear();
1163 void BattleGround::StartBattleGround()
1165 SetStartTime(0);
1167 // add BG to free slot queue
1168 AddToBGFreeSlotQueue();
1170 // add bg to update list
1171 // This must be done here, because we need to have already invited some players when first BG::Update() method is executed
1172 // and it doesn't matter if we call StartBattleGround() more times, because m_BattleGrounds is a map and instance id never changes
1173 sBattleGroundMgr.AddBattleGround(GetInstanceID(), GetTypeID(), this);
1176 void BattleGround::AddPlayer(Player *plr)
1178 // remove afk from player
1179 if (plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK))
1180 plr->ToggleAFK();
1182 // score struct must be created in inherited class
1184 uint64 guid = plr->GetGUID();
1185 uint32 team = plr->GetBGTeam();
1187 BattleGroundPlayer bp;
1188 bp.OfflineRemoveTime = 0;
1189 bp.Team = team;
1191 // Add to list/maps
1192 m_Players[guid] = bp;
1194 UpdatePlayersCountByTeam(team, false); // +1 player
1196 WorldPacket data;
1197 sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr);
1198 SendPacketToTeam(team, &data, plr, false);
1200 // add arena specific auras
1201 if (isArena())
1203 plr->RemoveArenaSpellCooldowns();
1204 plr->RemoveArenaAuras();
1205 plr->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT);
1206 if(team == ALLIANCE) // gold
1208 if (plr->GetTeam() == HORDE)
1209 plr->CastSpell(plr, SPELL_HORDE_GOLD_FLAG,true);
1210 else
1211 plr->CastSpell(plr, SPELL_ALLIANCE_GOLD_FLAG,true);
1213 else // green
1215 if (plr->GetTeam() == HORDE)
1216 plr->CastSpell(plr, SPELL_HORDE_GREEN_FLAG,true);
1217 else
1218 plr->CastSpell(plr, SPELL_ALLIANCE_GREEN_FLAG,true);
1221 plr->DestroyConjuredItems(true);
1222 plr->UnsummonPetTemporaryIfAny();
1224 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1226 plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true);
1228 plr->SetHealth(plr->GetMaxHealth());
1229 plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA));
1232 else
1234 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1235 plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells.
1238 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1239 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1241 // setup BG group membership
1242 PlayerAddedToBGCheckIfBGIsRunning(plr);
1243 AddOrSetPlayerToCorrectBgGroup(plr, guid, team);
1245 // Log
1246 sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName());
1249 /* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */
1250 void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team)
1252 Group* group = GetBgRaid(team);
1253 if(!group) // first player joined
1255 group = new Group;
1256 SetBgRaid(team, group);
1257 group->Create(plr_guid, plr->GetName());
1259 else // raid already exist
1261 if (group->IsMember(plr_guid))
1263 uint8 subgroup = group->GetMemberGroup(plr_guid);
1264 plr->SetBattleGroundRaid(group, subgroup);
1266 else
1268 group->AddMember(plr_guid, plr->GetName());
1269 if (Group* originalGroup = plr->GetOriginalGroup())
1270 if (originalGroup->IsLeader(plr_guid))
1271 group->ChangeLeader(plr_guid);
1276 // This method should be called when player logs into running battleground
1277 void BattleGround::EventPlayerLoggedIn(Player* player, uint64 plr_guid)
1279 // player is correct pointer
1280 for(std::deque<uint64>::iterator itr = m_OfflineQueue.begin(); itr != m_OfflineQueue.end(); ++itr)
1282 if (*itr == plr_guid)
1284 m_OfflineQueue.erase(itr);
1285 break;
1288 m_Players[plr_guid].OfflineRemoveTime = 0;
1289 PlayerAddedToBGCheckIfBGIsRunning(player);
1290 // if battleground is starting, then add preparation aura
1291 // we don't have to do that, because preparation aura isn't removed when player logs out
1294 // This method should be called when player logs out from running battleground
1295 void BattleGround::EventPlayerLoggedOut(Player* player)
1297 // player is correct pointer, it is checked in WorldSession::LogoutPlayer()
1298 m_OfflineQueue.push_back(player->GetGUID());
1299 m_Players[player->GetGUID()].OfflineRemoveTime = sWorld.GetGameTime() + MAX_OFFLINE_TIME;
1300 if (GetStatus() == STATUS_IN_PROGRESS)
1302 // drop flag and handle other cleanups
1303 RemovePlayer(player, player->GetGUID());
1305 // 1 player is logging out, if it is the last, then end arena!
1306 if (isArena())
1307 if (GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam())))
1308 EndBattleGround(GetOtherTeam(player->GetTeam()));
1312 /* This method should be called only once ... it adds pointer to queue */
1313 void BattleGround::AddToBGFreeSlotQueue()
1315 // make sure to add only once
1316 if (!m_InBGFreeSlotQueue && isBattleGround())
1318 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this);
1319 m_InBGFreeSlotQueue = true;
1323 /* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/
1324 void BattleGround::RemoveFromBGFreeSlotQueue()
1326 // set to be able to re-add if needed
1327 m_InBGFreeSlotQueue = false;
1328 // uncomment this code when battlegrounds will work like instances
1329 for (BGFreeSlotQueueType::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr)
1331 if ((*itr)->GetInstanceID() == m_InstanceID)
1333 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr);
1334 return;
1339 // get the number of free slots for team
1340 // returns the number how many players can join battleground to MaxPlayersPerTeam
1341 uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const
1343 //return free slot count to MaxPlayerPerTeam
1344 if (GetStatus() == STATUS_WAIT_JOIN || GetStatus() == STATUS_IN_PROGRESS)
1345 return (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0;
1347 return 0;
1350 bool BattleGround::HasFreeSlots() const
1352 return GetPlayersSize() < GetMaxPlayers();
1355 void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value)
1357 //this procedure is called from virtual function implemented in bg subclass
1358 BattleGroundScoreMap::const_iterator itr = m_PlayerScores.find(Source->GetGUID());
1360 if(itr == m_PlayerScores.end()) // player not found...
1361 return;
1363 switch(type)
1365 case SCORE_KILLING_BLOWS: // Killing blows
1366 itr->second->KillingBlows += value;
1367 break;
1368 case SCORE_DEATHS: // Deaths
1369 itr->second->Deaths += value;
1370 break;
1371 case SCORE_HONORABLE_KILLS: // Honorable kills
1372 itr->second->HonorableKills += value;
1373 break;
1374 case SCORE_BONUS_HONOR: // Honor bonus
1375 // do not add honor in arenas
1376 if (isBattleGround())
1378 // reward honor instantly
1379 if (Source->RewardHonor(NULL, 1, value))
1380 itr->second->BonusHonor += value;
1382 break;
1383 //used only in EY, but in MSG_PVP_LOG_DATA opcode
1384 case SCORE_DAMAGE_DONE: // Damage Done
1385 itr->second->DamageDone += value;
1386 break;
1387 case SCORE_HEALING_DONE: // Healing Done
1388 itr->second->HealingDone += value;
1389 break;
1390 default:
1391 sLog.outError("BattleGround: Unknown player score type %u", type);
1392 break;
1396 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)
1398 // must be created this way, adding to godatamap would add it to the base map of the instance
1399 // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
1400 // so we must create it specific for this instance
1401 GameObject * go = new GameObject;
1402 if(!go->Create(sObjectMgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry, GetBgMap(),
1403 PHASEMASK_NORMAL, x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,GO_STATE_READY))
1405 sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry);
1406 sLog.outError("Cannot create gameobject template %u! BattleGround not created!", entry);
1407 delete go;
1408 return false;
1411 uint32 guid = go->GetGUIDLow();
1413 // without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata
1414 // 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
1415 GameObjectData& data = sObjectMgr.NewGOData(guid);
1417 data.id = entry;
1418 data.mapid = GetMapId();
1419 data.posX = x;
1420 data.posY = y;
1421 data.posZ = z;
1422 data.orientation = o;
1423 data.rotation0 = rotation0;
1424 data.rotation1 = rotation1;
1425 data.rotation2 = rotation2;
1426 data.rotation3 = rotation3;
1427 data.spawntimesecs = respawnTime;
1428 data.spawnMask = 1;
1429 data.animprogress = 100;
1430 data.go_state = 1;
1432 // add to world, so it can be later looked up from HashMapHolder
1433 go->AddToWorld();
1434 m_BgObjects[type] = go->GetGUID();
1435 return true;
1438 //some doors aren't despawned so we cannot handle their closing in gameobject::update()
1439 //it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
1440 void BattleGround::DoorClose(uint64 const& guid)
1442 GameObject *obj = GetBgMap()->GetGameObject(guid);
1443 if (obj)
1445 //if doors are open, close it
1446 if (obj->getLootState() == GO_ACTIVATED && obj->GetGoState() != GO_STATE_READY)
1448 //change state to allow door to be closed
1449 obj->SetLootState(GO_READY);
1450 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1453 else
1455 sLog.outError("BattleGround: Door object not found (cannot close doors)");
1459 void BattleGround::DoorOpen(uint64 const& guid)
1461 GameObject *obj = GetBgMap()->GetGameObject(guid);
1462 if (obj)
1464 //change state to be sure they will be opened
1465 obj->SetLootState(GO_READY);
1466 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1468 else
1470 sLog.outError("BattleGround: Door object not found! - doors will be closed.");
1474 void BattleGround::OnObjectDBLoad(Creature* creature)
1476 const BattleGroundEventIdx eventId = sBattleGroundMgr.GetCreatureEventIndex(creature->GetDBTableGUIDLow());
1477 if (eventId.event1 == BG_EVENT_NONE)
1478 return;
1479 m_EventObjects[MAKE_PAIR32(eventId.event1, eventId.event2)].creatures.push_back(creature->GetGUID());
1480 if (!IsActiveEvent(eventId.event1, eventId.event2))
1481 SpawnBGCreature(creature->GetGUID(), RESPAWN_ONE_DAY);
1484 uint64 BattleGround::GetSingleCreatureGuid(uint8 event1, uint8 event2)
1486 BGCreatures::const_iterator itr = m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.begin();
1487 if (itr != m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.end())
1488 return *itr;
1489 return 0;
1492 void BattleGround::OnObjectDBLoad(GameObject* obj)
1494 const BattleGroundEventIdx eventId = sBattleGroundMgr.GetGameObjectEventIndex(obj->GetDBTableGUIDLow());
1495 if (eventId.event1 == BG_EVENT_NONE)
1496 return;
1497 m_EventObjects[MAKE_PAIR32(eventId.event1, eventId.event2)].gameobjects.push_back(obj->GetGUID());
1498 if (!IsActiveEvent(eventId.event1, eventId.event2))
1500 SpawnBGObject(obj->GetGUID(), RESPAWN_ONE_DAY);
1502 else
1504 // it's possible, that doors aren't spawned anymore (wsg)
1505 if (GetStatus() >= STATUS_IN_PROGRESS && IsDoor(eventId.event1, eventId.event2))
1506 DoorOpen(obj->GetGUID());
1510 bool BattleGround::IsDoor(uint8 event1, uint8 event2)
1512 if (event1 == BG_EVENT_DOOR)
1514 if (event2 > 0)
1516 sLog.outError("BattleGround too high event2 for event1:%i", event1);
1517 return false;
1519 return true;
1521 return false;
1524 void BattleGround::OpenDoorEvent(uint8 event1, uint8 event2 /*=0*/)
1526 if (!IsDoor(event1, event2))
1528 sLog.outError("BattleGround:OpenDoorEvent this is no door event1:%u event2:%u", event1, event2);
1529 return;
1531 if (!IsActiveEvent(event1, event2)) // maybe already despawned (eye)
1533 sLog.outError("BattleGround:OpenDoorEvent this event isn't active event1:%u event2:%u", event1, event2);
1534 return;
1536 BGObjects::const_iterator itr = m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.begin();
1537 for(; itr != m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.end(); ++itr)
1538 DoorOpen(*itr);
1541 void BattleGround::SpawnEvent(uint8 event1, uint8 event2, bool spawn)
1543 // stop if we want to spawn something which was already spawned
1544 // or despawn something which was already despawned
1545 if (event2 == BG_EVENT_NONE || (spawn && m_ActiveEvents[event1] == event2)
1546 || (!spawn && m_ActiveEvents[event1] != event2))
1547 return;
1549 if (spawn)
1551 // if event gets spawned, the current active event mus get despawned
1552 SpawnEvent(event1, m_ActiveEvents[event1], false);
1553 m_ActiveEvents[event1] = event2; // set this event to active
1555 else
1556 m_ActiveEvents[event1] = BG_EVENT_NONE; // no event active if event2 gets despawned
1558 BGCreatures::const_iterator itr = m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.begin();
1559 for(; itr != m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.end(); ++itr)
1560 SpawnBGCreature(*itr, (spawn) ? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY);
1561 BGObjects::const_iterator itr2 = m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.begin();
1562 for(; itr2 != m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.end(); ++itr2)
1563 SpawnBGObject(*itr2, (spawn) ? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY);
1566 void BattleGround::SpawnBGObject(uint64 const& guid, uint32 respawntime)
1568 Map* map = GetBgMap();
1570 GameObject *obj = map->GetGameObject(guid);
1571 if(!obj)
1572 return;
1573 if (respawntime == 0)
1575 //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again
1576 if (obj->getLootState() == GO_JUST_DEACTIVATED)
1577 obj->SetLootState(GO_READY);
1578 obj->SetRespawnTime(0);
1579 map->Add(obj);
1581 else
1583 map->Add(obj);
1584 obj->SetRespawnTime(respawntime);
1585 obj->SetLootState(GO_JUST_DEACTIVATED);
1589 void BattleGround::SpawnBGCreature(uint64 const& guid, uint32 respawntime)
1591 Map* map = GetBgMap();
1593 Creature* obj = map->GetCreature(guid);
1594 if (!obj)
1595 return;
1596 if (respawntime == 0)
1598 obj->Respawn();
1599 map->Add(obj);
1601 else
1603 map->Add(obj);
1604 obj->setDeathState(JUST_DIED);
1605 obj->SetRespawnDelay(respawntime);
1606 obj->RemoveCorpse();
1610 bool BattleGround::DelObject(uint32 type)
1612 if (!m_BgObjects[type])
1613 return true;
1615 GameObject *obj = GetBgMap()->GetGameObject(m_BgObjects[type]);
1616 if (!obj)
1618 sLog.outError("Can't find gobject guid: %u",GUID_LOPART(m_BgObjects[type]));
1619 return false;
1622 obj->SetRespawnTime(0); // not save respawn time
1623 obj->Delete();
1624 m_BgObjects[type] = 0;
1625 return true;
1628 void BattleGround::SendMessageToAll(int32 entry, ChatMsg type, Player const* source)
1630 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source);
1631 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1632 BroadcastWorker(bg_do);
1635 void BattleGround::SendYellToAll(int32 entry, uint32 language, uint64 const& guid)
1637 Creature* source = GetBgMap()->GetCreature(guid);
1638 if(!source)
1639 return;
1640 MaNGOS::BattleGroundYellBuilder bg_builder(language, entry, source);
1641 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundYellBuilder> bg_do(bg_builder);
1642 BroadcastWorker(bg_do);
1645 void BattleGround::PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...)
1647 va_list ap;
1648 va_start(ap, source);
1650 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source, &ap);
1651 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1652 BroadcastWorker(bg_do);
1654 va_end(ap);
1657 void BattleGround::SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 arg1, int32 arg2)
1659 MaNGOS::BattleGround2ChatBuilder bg_builder(type, entry, source, arg1, arg2);
1660 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGround2ChatBuilder> bg_do(bg_builder);
1661 BroadcastWorker(bg_do);
1664 void BattleGround::SendYell2ToAll(int32 entry, uint32 language, uint64 const& guid, int32 arg1, int32 arg2)
1666 Creature* source = GetBgMap()->GetCreature(guid);
1667 if(!source)
1668 return;
1669 MaNGOS::BattleGround2YellBuilder bg_builder(language, entry, source, arg1, arg2);
1670 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGround2YellBuilder> bg_do(bg_builder);
1671 BroadcastWorker(bg_do);
1674 void BattleGround::EndNow()
1676 RemoveFromBGFreeSlotQueue();
1677 SetStatus(STATUS_WAIT_LEAVE);
1678 SetEndTime(0);
1682 important notice:
1683 buffs aren't spawned/despawned when players captures anything
1684 buffs are in their positions when battleground starts
1686 void BattleGround::HandleTriggerBuff(uint64 const& go_guid)
1688 GameObject *obj = GetBgMap()->GetGameObject(go_guid);
1689 if (!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned())
1690 return;
1692 // static buffs are already handled just by database and don't need
1693 // battleground code
1694 if (!m_BuffChange)
1696 obj->SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
1697 return;
1700 // change buff type, when buff is used:
1701 // TODO this can be done when poolsystem works for instances
1702 int32 index = m_BgObjects.size() - 1;
1703 while (index >= 0 && m_BgObjects[index] != go_guid)
1704 index--;
1705 if (index < 0)
1707 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());
1708 return;
1711 //randomly select new buff
1712 uint8 buff = urand(0, 2);
1713 uint32 entry = obj->GetEntry();
1714 if (m_BuffChange && entry != Buff_Entries[buff])
1716 //despawn current buff
1717 SpawnBGObject(m_BgObjects[index], RESPAWN_ONE_DAY);
1718 //set index for new one
1719 for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex)
1721 if (entry == Buff_Entries[currBuffTypeIndex])
1723 index -= currBuffTypeIndex;
1724 index += buff;
1729 SpawnBGObject(m_BgObjects[index], BUFF_RESPAWN_TIME);
1732 void BattleGround::HandleKillPlayer( Player *player, Player *killer )
1734 // add +1 deaths
1735 UpdatePlayerScore(player, SCORE_DEATHS, 1);
1737 // add +1 kills to group and +1 killing_blows to killer
1738 if (killer)
1740 UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1);
1741 UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1);
1743 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1745 Player *plr = sObjectMgr.GetPlayer(itr->first);
1747 if (!plr || plr == killer)
1748 continue;
1750 if (plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player))
1751 UpdatePlayerScore(plr, SCORE_HONORABLE_KILLS, 1);
1755 // to be able to remove insignia -- ONLY IN BattleGrounds
1756 if (!isArena())
1757 player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE );
1760 // return the player's team based on battlegroundplayer info
1761 // used in same faction arena matches mainly
1762 uint32 BattleGround::GetPlayerTeam(uint64 guid)
1764 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1765 if (itr!=m_Players.end())
1766 return itr->second.Team;
1767 return 0;
1770 bool BattleGround::IsPlayerInBattleGround(uint64 guid)
1772 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1773 if (itr != m_Players.end())
1774 return true;
1775 return false;
1778 void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player* plr)
1780 if (GetStatus() != STATUS_WAIT_LEAVE)
1781 return;
1783 WorldPacket data;
1784 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1786 BlockMovement(plr);
1788 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
1789 plr->GetSession()->SendPacket(&data);
1791 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType());
1792 plr->GetSession()->SendPacket(&data);
1795 uint32 BattleGround::GetAlivePlayersCountByTeam(uint32 Team) const
1797 int count = 0;
1798 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1800 if (itr->second.Team == Team)
1802 Player * pl = sObjectMgr.GetPlayer(itr->first);
1803 if (pl && pl->isAlive())
1804 ++count;
1807 return count;
1810 void BattleGround::CheckArenaWinConditions()
1812 if (!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE))
1813 EndBattleGround(HORDE);
1814 else if (GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE))
1815 EndBattleGround(ALLIANCE);
1818 void BattleGround::SetBgRaid( uint32 TeamID, Group *bg_raid )
1820 Group* &old_raid = TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE];
1821 if(old_raid) old_raid->SetBattlegroundGroup(NULL);
1822 if(bg_raid) bg_raid->SetBattlegroundGroup(this);
1823 old_raid = bg_raid;
1826 WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard( Player* player )
1828 return sObjectMgr.GetClosestGraveYard( player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam() );
1831 bool BattleGround::IsTeamScoreInRange(uint32 team, uint32 minScore, uint32 maxScore) const
1833 BattleGroundTeamId team_idx = GetTeamIndexByTeamId(team);
1834 uint32 score = (m_TeamScores[team_idx] < 0) ? 0 : uint32(m_TeamScores[team_idx]);
1835 return score >= minScore && score <= maxScore;
1838 void BattleGround::SetBracket( PvPDifficultyEntry const* bracketEntry )
1840 m_BracketId = bracketEntry->GetBracketId();
1841 SetLevelRange(bracketEntry->minLevel,bracketEntry->maxLevel);