[8483] Implement glyph 43361.
[getmangos.git] / src / game / BattleGround.cpp
blob0e9e8549c37a3c01efe30646f43fd08aa44a412e
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 BattleGround2ChatBuilder
85 public:
86 BattleGround2ChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, int32 arg1, int32 arg2)
87 : i_msgtype(msgtype), i_textId(textId), i_source(source), i_arg1(arg1), i_arg2(arg2) {}
88 void operator()(WorldPacket& data, int32 loc_idx)
90 char const* text = objmgr.GetMangosString(i_textId,loc_idx);
91 char const* arg1str = i_arg1 ? objmgr.GetMangosString(i_arg1,loc_idx) : "";
92 char const* arg2str = i_arg2 ? objmgr.GetMangosString(i_arg2,loc_idx) : "";
94 char str [2048];
95 snprintf(str,2048,text, arg1str, arg2str );
97 uint64 target_guid = i_source ? i_source ->GetGUID() : 0;
99 data << uint8(i_msgtype);
100 data << uint32(LANG_UNIVERSAL);
101 data << uint64(target_guid); // there 0 for BG messages
102 data << uint32(0); // can be chat msg group or something
103 data << uint64(target_guid);
104 data << uint32(strlen(str)+1);
105 data << str;
106 data << uint8(i_source ? i_source->chatTag() : uint8(0));
108 private:
110 ChatMsg i_msgtype;
111 int32 i_textId;
112 Player const* i_source;
113 int32 i_arg1;
114 int32 i_arg2;
116 } // namespace MaNGOS
118 template<class Do>
119 void BattleGround::BroadcastWorker(Do& _do)
121 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
122 if (Player *plr = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER)))
123 _do(plr);
126 BattleGround::BattleGround()
128 m_TypeID = BattleGroundTypeId(0);
129 m_InstanceID = 0;
130 m_Status = STATUS_NONE;
131 m_ClientInstanceID = 0;
132 m_EndTime = 0;
133 m_LastResurrectTime = 0;
134 m_QueueId = QUEUE_ID_MAX_LEVEL_19;
135 m_InvitedAlliance = 0;
136 m_InvitedHorde = 0;
137 m_ArenaType = 0;
138 m_IsArena = false;
139 m_Winner = 2;
140 m_StartTime = 0;
141 m_Events = 0;
142 m_IsRated = false;
143 m_BuffChange = false;
144 m_Name = "";
145 m_LevelMin = 0;
146 m_LevelMax = 0;
147 m_InBGFreeSlotQueue = false;
148 m_SetDeleteThis = false;
150 m_MaxPlayersPerTeam = 0;
151 m_MaxPlayers = 0;
152 m_MinPlayersPerTeam = 0;
153 m_MinPlayers = 0;
155 m_MapId = 0;
157 m_TeamStartLocX[BG_TEAM_ALLIANCE] = 0;
158 m_TeamStartLocX[BG_TEAM_HORDE] = 0;
160 m_TeamStartLocY[BG_TEAM_ALLIANCE] = 0;
161 m_TeamStartLocY[BG_TEAM_HORDE] = 0;
163 m_TeamStartLocZ[BG_TEAM_ALLIANCE] = 0;
164 m_TeamStartLocZ[BG_TEAM_HORDE] = 0;
166 m_TeamStartLocO[BG_TEAM_ALLIANCE] = 0;
167 m_TeamStartLocO[BG_TEAM_HORDE] = 0;
169 m_ArenaTeamIds[BG_TEAM_ALLIANCE] = 0;
170 m_ArenaTeamIds[BG_TEAM_HORDE] = 0;
172 m_ArenaTeamRatingChanges[BG_TEAM_ALLIANCE] = 0;
173 m_ArenaTeamRatingChanges[BG_TEAM_HORDE] = 0;
175 m_BgRaids[BG_TEAM_ALLIANCE] = NULL;
176 m_BgRaids[BG_TEAM_HORDE] = NULL;
178 m_PlayersCount[BG_TEAM_ALLIANCE] = 0;
179 m_PlayersCount[BG_TEAM_HORDE] = 0;
181 m_TeamScores[BG_TEAM_ALLIANCE] = 0;
182 m_TeamScores[BG_TEAM_HORDE] = 0;
184 m_PrematureCountDown = false;
185 m_PrematureCountDown = 0;
187 m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_2M;
188 m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_1M;
189 m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_30S;
190 m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE;
191 //we must set to some default existing values
192 m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_WS_START_TWO_MINUTES;
193 m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE;
194 m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE;
195 m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN;
198 BattleGround::~BattleGround()
200 // remove objects and creatures
201 // (this is done automatically in mapmanager update, when the instance is reset after the reset time)
202 int size = m_BgCreatures.size();
203 for(int i = 0; i < size; ++i)
204 DelCreature(i);
206 size = m_BgObjects.size();
207 for(int i = 0; i < size; ++i)
208 DelObject(i);
210 if (GetInstanceID()) // not spam by useless queries in case BG templates
212 // delete creature and go respawn times
213 WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'",GetInstanceID());
214 WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'",GetInstanceID());
215 // delete instance from db
216 CharacterDatabase.PExecute("DELETE FROM instance WHERE id = '%u'",GetInstanceID());
217 // remove from battlegrounds
220 sBattleGroundMgr.RemoveBattleGround(GetInstanceID(), GetTypeID());
221 // unload map
222 if (Map * map = MapManager::Instance().FindMap(GetMapId(), GetInstanceID()))
223 if (map->IsBattleGroundOrArena())
224 ((BattleGroundMap*)map)->SetUnload();
225 // remove from bg free slot queue
226 this->RemoveFromBGFreeSlotQueue();
228 for(BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr)
229 delete itr->second;
232 void BattleGround::Update(uint32 diff)
234 if (!GetPlayersSize())
236 // BG is empty
237 // if there are no players invited, delete BG
238 // this will delete arena or bg object, where any player entered
239 // [[ but if you use battleground object again (more battles possible to be played on 1 instance)
240 // then this condition should be removed and code:
241 // if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
242 // this->AddToFreeBGObjectsQueue(); // not yet implemented
243 // should be used instead of current
244 // ]]
245 // BattleGround Template instance cannot be updated, because it would be deleted
246 if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
247 m_SetDeleteThis = true;
248 return;
251 // remove offline players from bg after 5 minutes
252 if (!m_OfflineQueue.empty())
254 BattleGroundPlayerMap::iterator itr = m_Players.find(*(m_OfflineQueue.begin()));
255 if (itr != m_Players.end())
257 if (itr->second.OfflineRemoveTime <= sWorld.GetGameTime())
259 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
260 m_OfflineQueue.pop_front(); // remove from offline queue
261 //do not use itr for anything, because it is erased in RemovePlayerAtLeave()
266 /*********************************************************/
267 /*** BATTLEGROUND RESSURECTION SYSTEM ***/
268 /*********************************************************/
270 //this should be handled by spell system
271 m_LastResurrectTime += diff;
272 if (m_LastResurrectTime >= RESURRECTION_INTERVAL)
274 if (GetReviveQueueSize())
276 for(std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
278 Creature *sh = NULL;
279 for(std::vector<uint64>::const_iterator itr2 = (itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
281 Player *plr = objmgr.GetPlayer(*itr2);
282 if (!plr)
283 continue;
285 if (!sh && plr->IsInWorld())
287 sh = plr->GetMap()->GetCreature(itr->first);
288 // only for visual effect
289 if (sh)
290 // Spirit Heal, effect 117
291 sh->CastSpell(sh, SPELL_SPIRIT_HEAL, true);
294 // Resurrection visual
295 plr->CastSpell(plr, SPELL_RESURRECTION_VISUAL, true);
296 m_ResurrectQueue.push_back(*itr2);
298 (itr->second).clear();
301 m_ReviveQueue.clear();
302 m_LastResurrectTime = 0;
304 else
305 // queue is clear and time passed, just update last resurrection time
306 m_LastResurrectTime = 0;
308 else if (m_LastResurrectTime > 500) // Resurrect players only half a second later, to see spirit heal effect on NPC
310 for(std::vector<uint64>::const_iterator itr = m_ResurrectQueue.begin(); itr != m_ResurrectQueue.end(); ++itr)
312 Player *plr = objmgr.GetPlayer(*itr);
313 if (!plr)
314 continue;
315 plr->ResurrectPlayer(1.0f);
316 plr->CastSpell(plr, SPELL_SPIRIT_HEAL_MANA, true);
317 ObjectAccessor::Instance().ConvertCorpseForPlayer(*itr);
319 m_ResurrectQueue.clear();
322 /*********************************************************/
323 /*** BATTLEGROUND BALLANCE SYSTEM ***/
324 /*********************************************************/
326 // if less then minimum players are in on one side, then start premature finish timer
327 if (GetStatus() == STATUS_IN_PROGRESS && !isArena() && sBattleGroundMgr.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE) < GetMinPlayersPerTeam()))
329 if (!m_PrematureCountDown)
331 m_PrematureCountDown = true;
332 m_PrematureCountDownTimer = sBattleGroundMgr.GetPrematureFinishTime();
334 else if (m_PrematureCountDownTimer < diff)
336 // time's up!
337 uint32 winner = 0;
338 if (GetPlayersCountByTeam(ALLIANCE) >= GetMinPlayersPerTeam())
339 winner = ALLIANCE;
340 else if (GetPlayersCountByTeam(HORDE) >= GetMinPlayersPerTeam())
341 winner = HORDE;
343 EndBattleGround(winner);
344 m_PrematureCountDown = false;
346 else
348 uint32 newtime = m_PrematureCountDownTimer - diff;
349 // announce every minute
350 if (newtime > (MINUTE * IN_MILISECONDS))
352 if (newtime / (MINUTE * IN_MILISECONDS) != m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS))
353 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS)));
355 else
357 //announce every 15 seconds
358 if (newtime / (15 * IN_MILISECONDS) != m_PrematureCountDownTimer / (15 * IN_MILISECONDS))
359 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / IN_MILISECONDS));
361 m_PrematureCountDownTimer = newtime;
364 else if (m_PrematureCountDown)
365 m_PrematureCountDown = false;
367 /*********************************************************/
368 /*** BATTLEGROUND STARTING SYSTEM ***/
369 /*********************************************************/
371 if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize())
373 ModifyStartDelayTime(diff);
375 if (!(m_Events & BG_STARTING_EVENT_1))
377 m_Events |= BG_STARTING_EVENT_1;
379 // setup here, only when at least one player has ported to the map
380 if (!SetupBattleGround())
382 EndNow();
383 return;
386 StartingEventCloseDoors();
387 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FIRST]);
388 //first start warning - 2 or 1 minute
389 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FIRST], CHAT_MSG_BG_SYSTEM_NEUTRAL);
391 // After 1 minute or 30 seconds, warning is signalled
392 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_SECOND] && !(m_Events & BG_STARTING_EVENT_2))
394 m_Events |= BG_STARTING_EVENT_2;
395 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_SECOND], CHAT_MSG_BG_SYSTEM_NEUTRAL);
397 // After 30 or 15 seconds, warning is signalled
398 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_THIRD] && !(m_Events & BG_STARTING_EVENT_3))
400 m_Events |= BG_STARTING_EVENT_3;
401 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_THIRD], CHAT_MSG_BG_SYSTEM_NEUTRAL);
403 // delay expired (atfer 2 or 1 minute)
404 else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4))
406 m_Events |= BG_STARTING_EVENT_4;
408 StartingEventOpenDoors();
410 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FOURTH], CHAT_MSG_BG_SYSTEM_NEUTRAL);
411 SetStatus(STATUS_IN_PROGRESS);
412 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FOURTH]);
414 //remove preparation
415 if (isArena())
417 //TODO : add arena sound PlaySoundToAll(SOUND_ARENA_START);
419 for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
420 if (Player *plr = objmgr.GetPlayer(itr->first))
421 plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION);
423 CheckArenaWinConditions();
425 else
428 PlaySoundToAll(SOUND_BG_START);
430 for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
431 if (Player* plr = objmgr.GetPlayer(itr->first))
432 plr->RemoveAurasDueToSpell(SPELL_PREPARATION);
433 //Announce BG starting
434 if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE))
436 sWorld.SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD, GetName(), GetMinLevel(), GetMaxLevel());
442 /*********************************************************/
443 /*** BATTLEGROUND ENDING SYSTEM ***/
444 /*********************************************************/
446 if (GetStatus() == STATUS_WAIT_LEAVE)
448 // remove all players from battleground after 2 minutes
449 m_EndTime -= diff;
450 if (m_EndTime <= 0)
452 m_EndTime = 0;
453 BattleGroundPlayerMap::iterator itr, next;
454 for(itr = m_Players.begin(); itr != m_Players.end(); itr = next)
456 next = itr;
457 ++next;
458 //itr is erased here!
459 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
460 // do not change any battleground's private variables
465 //update start time
466 m_StartTime += diff;
469 void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O)
471 BattleGroundTeamId idx = GetTeamIndexByTeamId(TeamID);
472 m_TeamStartLocX[idx] = X;
473 m_TeamStartLocY[idx] = Y;
474 m_TeamStartLocZ[idx] = Z;
475 m_TeamStartLocO[idx] = O;
478 void BattleGround::SendPacketToAll(WorldPacket *packet)
480 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
482 Player *plr = objmgr.GetPlayer(itr->first);
483 if (plr)
484 plr->GetSession()->SendPacket(packet);
485 else
486 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
490 void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender, bool self)
492 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
494 Player *plr = objmgr.GetPlayer(itr->first);
496 if (!plr)
498 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
499 continue;
502 if (!self && sender == plr)
503 continue;
505 uint32 team = itr->second.Team;
506 if(!team) team = plr->GetTeam();
508 if (team == TeamID)
509 plr->GetSession()->SendPacket(packet);
513 void BattleGround::PlaySoundToAll(uint32 SoundID)
515 WorldPacket data;
516 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
517 SendPacketToAll(&data);
520 void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID)
522 WorldPacket data;
524 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
526 Player *plr = objmgr.GetPlayer(itr->first);
528 if (!plr)
530 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
531 continue;
534 uint32 team = itr->second.Team;
535 if(!team) team = plr->GetTeam();
537 if (team == TeamID)
539 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
540 plr->GetSession()->SendPacket(&data);
545 void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID)
547 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
549 Player *plr = objmgr.GetPlayer(itr->first);
551 if (!plr)
553 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
554 continue;
557 uint32 team = itr->second.Team;
558 if(!team) team = plr->GetTeam();
560 if (team == TeamID)
561 plr->CastSpell(plr, SpellID, true);
565 void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID)
567 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
569 Player *plr = objmgr.GetPlayer(itr->first);
571 if (!plr)
573 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
574 continue;
577 uint32 team = itr->second.Team;
578 if(!team) team = plr->GetTeam();
580 if (team == TeamID)
581 UpdatePlayerScore(plr, SCORE_BONUS_HONOR, Honor);
585 void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID)
587 FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
589 if (!factionEntry)
590 return;
592 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
594 Player *plr = objmgr.GetPlayer(itr->first);
596 if (!plr)
598 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
599 continue;
602 uint32 team = itr->second.Team;
603 if(!team) team = plr->GetTeam();
605 if (team == TeamID)
606 plr->GetReputationMgr().ModifyReputation(factionEntry, Reputation);
610 void BattleGround::UpdateWorldState(uint32 Field, uint32 Value)
612 WorldPacket data;
613 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
614 SendPacketToAll(&data);
617 void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source)
619 WorldPacket data;
620 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
621 Source->GetSession()->SendPacket(&data);
624 void BattleGround::EndBattleGround(uint32 winner)
626 this->RemoveFromBGFreeSlotQueue();
628 ArenaTeam * winner_arena_team = NULL;
629 ArenaTeam * loser_arena_team = NULL;
630 uint32 loser_rating = 0;
631 uint32 winner_rating = 0;
632 WorldPacket data;
633 int32 winmsg_id = 0;
635 if (winner == ALLIANCE)
637 winmsg_id = isBattleGround() ? LANG_BG_A_WINS : LANG_ARENA_GOLD_WINS;
639 PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound
641 SetWinner(WINNER_ALLIANCE);
643 else if (winner == HORDE)
645 winmsg_id = isBattleGround() ? LANG_BG_H_WINS : LANG_ARENA_GREEN_WINS;
647 PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound
649 SetWinner(WINNER_HORDE);
651 else
653 SetWinner(3);
656 SetStatus(STATUS_WAIT_LEAVE);
657 //we must set it this way, because end time is sent in packet!
658 m_EndTime = TIME_TO_AUTOREMOVE;
660 // arena rating calculation
661 if (isArena() && isRated())
663 winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(winner));
664 loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(winner)));
665 if (winner_arena_team && loser_arena_team)
667 loser_rating = loser_arena_team->GetStats().rating;
668 winner_rating = winner_arena_team->GetStats().rating;
669 int32 winner_change = winner_arena_team->WonAgainst(loser_rating);
670 int32 loser_change = loser_arena_team->LostAgainst(winner_rating);
671 sLog.outDebug("--- Winner rating: %u, Loser rating: %u, Winner change: %u, Losser change: %u ---", winner_rating, loser_rating, winner_change, loser_change);
672 SetArenaTeamRatingChangeForTeam(winner, winner_change);
673 SetArenaTeamRatingChangeForTeam(GetOtherTeam(winner), loser_change);
675 else
677 SetArenaTeamRatingChangeForTeam(ALLIANCE, 0);
678 SetArenaTeamRatingChangeForTeam(HORDE, 0);
682 for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
684 Player *plr = objmgr.GetPlayer(itr->first);
685 uint32 team = itr->second.Team;
687 if (!plr)
689 //if rated arena match - make member lost!
690 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
692 if (team == winner)
693 winner_arena_team->OfflineMemberLost(itr->first, loser_rating);
694 else
695 loser_arena_team->OfflineMemberLost(itr->first, winner_rating);
697 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
698 continue;
701 // should remove spirit of redemption
702 if (plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
703 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
705 if (!plr->isAlive())
707 plr->ResurrectPlayer(1.0f);
708 plr->SpawnCorpseBones();
711 //this line is obsolete - team is set ALWAYS
712 //if(!team) team = plr->GetTeam();
714 // per player calculation
715 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
717 if (team == winner)
719 // update achievement BEFORE personal rating update
720 ArenaTeamMember* member = winner_arena_team->GetMember(plr->GetGUID());
721 if (member)
722 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, member->personal_rating);
724 winner_arena_team->MemberWon(plr,loser_rating);
726 else
728 loser_arena_team->MemberLost(plr,winner_rating);
730 // Arena lost => reset the win_rated_arena having the "no_loose" condition
731 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE);
735 if (team == winner)
737 RewardMark(plr,ITEM_WINNER_COUNT);
738 RewardQuestComplete(plr);
740 else
741 RewardMark(plr,ITEM_LOSER_COUNT);
743 plr->CombatStopWithPets(true);
745 BlockMovement(plr);
747 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
748 plr->GetSession()->SendPacket(&data);
750 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
751 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType());
752 plr->GetSession()->SendPacket(&data);
753 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1);
756 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
758 // update arena points only after increasing the player's match count!
759 //obsolete: winner_arena_team->UpdateArenaPointsHelper();
760 //obsolete: loser_arena_team->UpdateArenaPointsHelper();
761 // save the stat changes
762 winner_arena_team->SaveToDB();
763 loser_arena_team->SaveToDB();
764 // send updated arena team stats to players
765 // this way all arena team members will get notified, not only the ones who participated in this match
766 winner_arena_team->NotifyStatsChanged();
767 loser_arena_team->NotifyStatsChanged();
770 if (winmsg_id)
771 SendMessageToAll(winmsg_id, CHAT_MSG_BG_SYSTEM_NEUTRAL);
774 uint32 BattleGround::GetBonusHonorFromKill(uint32 kills) const
776 //variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill)
777 return MaNGOS::Honor::hk_honor_at_level(GetMaxLevel(), kills);
780 uint32 BattleGround::GetBattlemasterEntry() const
782 switch(GetTypeID())
784 case BATTLEGROUND_AV: return 15972;
785 case BATTLEGROUND_WS: return 14623;
786 case BATTLEGROUND_AB: return 14879;
787 case BATTLEGROUND_EY: return 22516;
788 case BATTLEGROUND_NA: return 20200;
789 default: return 0;
793 void BattleGround::RewardMark(Player *plr,uint32 count)
795 BattleGroundMarks mark;
796 bool IsSpell;
797 switch(GetTypeID())
799 case BATTLEGROUND_AV:
800 IsSpell = true;
801 if (count == ITEM_WINNER_COUNT)
802 mark = SPELL_AV_MARK_WINNER;
803 else
804 mark = SPELL_AV_MARK_LOSER;
805 break;
806 case BATTLEGROUND_WS:
807 IsSpell = true;
808 if (count == ITEM_WINNER_COUNT)
809 mark = SPELL_WS_MARK_WINNER;
810 else
811 mark = SPELL_WS_MARK_LOSER;
812 break;
813 case BATTLEGROUND_AB:
814 IsSpell = true;
815 if (count == ITEM_WINNER_COUNT)
816 mark = SPELL_AB_MARK_WINNER;
817 else
818 mark = SPELL_AB_MARK_LOSER;
819 break;
820 case BATTLEGROUND_EY:
821 IsSpell = false;
822 mark = ITEM_EY_MARK_OF_HONOR;
823 break;
824 default:
825 return;
828 if (IsSpell)
829 RewardSpellCast(plr,mark);
830 else
831 RewardItem(plr,mark,count);
834 void BattleGround::RewardSpellCast(Player *plr, uint32 spell_id)
836 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
837 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
838 return;
840 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
841 if(!spellInfo)
843 sLog.outError("Battleground reward casting spell %u not exist.",spell_id);
844 return;
847 plr->CastSpell(plr, spellInfo, true);
850 void BattleGround::RewardItem(Player *plr, uint32 item_id, uint32 count)
852 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
853 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
854 return;
856 ItemPosCountVec dest;
857 uint32 no_space_count = 0;
858 uint8 msg = plr->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item_id, count, &no_space_count );
860 if( msg == EQUIP_ERR_ITEM_NOT_FOUND)
862 sLog.outErrorDb("Battleground reward item (Entry %u) not exist in `item_template`.",item_id);
863 return;
866 if( msg != EQUIP_ERR_OK ) // convert to possible store amount
867 count -= no_space_count;
869 if( count != 0 && !dest.empty()) // can add some
870 if (Item* item = plr->StoreNewItem( dest, item_id, true, 0))
871 plr->SendNewItem(item,count,true,false);
873 if (no_space_count > 0)
874 SendRewardMarkByMail(plr,item_id,no_space_count);
877 void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count)
879 uint32 bmEntry = GetBattlemasterEntry();
880 if (!bmEntry)
881 return;
883 ItemPrototype const* markProto = objmgr.GetItemPrototype(mark);
884 if (!markProto)
885 return;
887 if (Item* markItem = Item::CreateItem(mark,count,plr))
889 // save new item before send
890 markItem->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
892 // item
893 MailItemsInfo mi;
894 mi.AddItem(markItem->GetGUIDLow(), markItem->GetEntry(), markItem);
896 // subject: item name
897 std::string subject = markProto->Name1;
898 int loc_idx = plr->GetSession()->GetSessionDbLocaleIndex();
899 if (loc_idx >= 0 )
900 if (ItemLocale const *il = objmgr.GetItemLocale(markProto->ItemId))
901 if (il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty())
902 subject = il->Name[loc_idx];
904 // text
905 std::string textFormat = plr->GetSession()->GetMangosString(LANG_BG_MARK_BY_MAIL);
906 char textBuf[300];
907 snprintf(textBuf,300,textFormat.c_str(),GetName(),GetName());
908 uint32 itemTextId = objmgr.CreateItemText( textBuf );
910 WorldSession::SendMailTo(plr, MAIL_CREATURE, MAIL_STATIONERY_NORMAL, bmEntry, plr->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE);
914 void BattleGround::RewardQuestComplete(Player *plr)
916 uint32 quest;
917 switch(GetTypeID())
919 case BATTLEGROUND_AV:
920 quest = SPELL_AV_QUEST_REWARD;
921 break;
922 case BATTLEGROUND_WS:
923 quest = SPELL_WS_QUEST_REWARD;
924 break;
925 case BATTLEGROUND_AB:
926 quest = SPELL_AB_QUEST_REWARD;
927 break;
928 case BATTLEGROUND_EY:
929 quest = SPELL_EY_QUEST_REWARD;
930 break;
931 default:
932 return;
935 RewardSpellCast(plr, quest);
938 void BattleGround::BlockMovement(Player *plr)
940 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()
943 void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket)
945 uint32 team = GetPlayerTeam(guid);
946 bool participant = false;
947 // Remove from lists/maps
948 BattleGroundPlayerMap::iterator itr = m_Players.find(guid);
949 if (itr != m_Players.end())
951 UpdatePlayersCountByTeam(team, true); // -1 player
952 m_Players.erase(itr);
953 // check if the player was a participant of the match, or only entered through gm command (goname)
954 participant = true;
957 BattleGroundScoreMap::iterator itr2 = m_PlayerScores.find(guid);
958 if (itr2 != m_PlayerScores.end())
960 delete itr2->second; // delete player's score
961 m_PlayerScores.erase(itr2);
964 RemovePlayerFromResurrectQueue(guid);
966 Player *plr = objmgr.GetPlayer(guid);
968 // should remove spirit of redemption
969 if (plr && plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
970 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
972 if(plr && !plr->isAlive()) // resurrect on exit
974 plr->ResurrectPlayer(1.0f);
975 plr->SpawnCorpseBones();
978 RemovePlayer(plr, guid); // BG subclass specific code
980 if(participant) // if the player was a match participant, remove auras, calc rating, update queue
982 BattleGroundTypeId bgTypeId = GetTypeID();
983 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
984 if (plr)
986 plr->ClearAfkReports();
988 if(!team) team = plr->GetTeam();
990 // if arena, remove the specific arena auras
991 if (isArena())
993 plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out
994 bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing)
996 // unsummon current and summon old pet if there was one and there isn't a current pet
997 plr->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT);
998 plr->ResummonPetTemporaryUnSummonedIfAny();
1000 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
1002 //left a rated match while the encounter was in progress, consider as loser
1003 ArenaTeam * winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
1004 ArenaTeam * loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
1005 if (winner_arena_team && loser_arena_team)
1006 loser_arena_team->MemberLost(plr,winner_arena_team->GetRating());
1009 if (SendPacket)
1011 WorldPacket data;
1012 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0, 0);
1013 plr->GetSession()->SendPacket(&data);
1016 // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg
1017 plr->RemoveBattleGroundQueueId(bgQueueTypeId);
1019 else
1020 // removing offline participant
1022 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
1024 //left a rated match while the encounter was in progress, consider as loser
1025 ArenaTeam * others_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
1026 ArenaTeam * players_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
1027 if (others_arena_team && players_arena_team)
1028 players_arena_team->OfflineMemberLost(guid, others_arena_team->GetRating());
1032 // remove from raid group if player is member
1033 if (Group *group = GetBgRaid(team))
1035 if( !group->RemoveMember(guid, 0) ) // group was disbanded
1037 SetBgRaid(team, NULL);
1038 delete group;
1041 DecreaseInvitedCount(team);
1042 //we should update battleground queue, but only if bg isn't ending
1043 if (isBattleGround() && GetStatus() < STATUS_WAIT_LEAVE)
1045 // a player has left the battleground, so there are free slots -> add to queue
1046 AddToBGFreeSlotQueue();
1047 sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, GetQueueId());
1050 // Let others know
1051 WorldPacket data;
1052 sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, guid);
1053 SendPacketToTeam(team, &data, plr, false);
1056 if (plr)
1058 // Do next only if found in battleground
1059 plr->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG.
1060 // reset destination bg team
1061 plr->SetBGTeam(0);
1063 if (Transport)
1064 plr->TeleportToBGEntryPoint();
1066 sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName());
1069 //battleground object will be deleted next BattleGround::Update() call
1072 // this method is called when no players remains in battleground
1073 void BattleGround::Reset()
1075 SetQueueId(QUEUE_ID_MAX_LEVEL_19);
1076 SetWinner(WINNER_NONE);
1077 SetStatus(STATUS_WAIT_QUEUE);
1078 SetStartTime(0);
1079 SetEndTime(0);
1080 SetLastResurrectTime(0);
1081 SetArenaType(0);
1082 SetRated(false);
1084 m_Events = 0;
1086 if (m_InvitedAlliance > 0 || m_InvitedHorde > 0)
1087 sLog.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde);
1089 m_InvitedAlliance = 0;
1090 m_InvitedHorde = 0;
1091 m_InBGFreeSlotQueue = false;
1093 m_Players.clear();
1095 for(BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr)
1096 delete itr->second;
1097 m_PlayerScores.clear();
1100 void BattleGround::StartBattleGround()
1102 SetStartTime(0);
1103 SetLastResurrectTime(0);
1105 // add BG to free slot queue
1106 AddToBGFreeSlotQueue();
1108 // add bg to update list
1109 // This must be done here, because we need to have already invited some players when first BG::Update() method is executed
1110 // and it doesn't matter if we call StartBattleGround() more times, because m_BattleGrounds is a map and instance id never changes
1111 sBattleGroundMgr.AddBattleGround(GetInstanceID(), GetTypeID(), this);
1114 void BattleGround::AddPlayer(Player *plr)
1116 // remove afk from player
1117 if (plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK))
1118 plr->ToggleAFK();
1120 // score struct must be created in inherited class
1122 uint64 guid = plr->GetGUID();
1123 uint32 team = plr->GetBGTeam();
1125 BattleGroundPlayer bp;
1126 bp.OfflineRemoveTime = 0;
1127 bp.Team = team;
1129 // Add to list/maps
1130 m_Players[guid] = bp;
1132 UpdatePlayersCountByTeam(team, false); // +1 player
1134 WorldPacket data;
1135 sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr);
1136 SendPacketToTeam(team, &data, plr, false);
1138 // add arena specific auras
1139 if (isArena())
1141 plr->RemoveArenaSpellCooldowns();
1142 plr->RemoveArenaAuras();
1143 plr->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT);
1144 if(team == ALLIANCE) // gold
1146 if (plr->GetTeam() == HORDE)
1147 plr->CastSpell(plr, SPELL_HORDE_GOLD_FLAG,true);
1148 else
1149 plr->CastSpell(plr, SPELL_ALLIANCE_GOLD_FLAG,true);
1151 else // green
1153 if (plr->GetTeam() == HORDE)
1154 plr->CastSpell(plr, SPELL_HORDE_GREEN_FLAG,true);
1155 else
1156 plr->CastSpell(plr, SPELL_ALLIANCE_GREEN_FLAG,true);
1159 plr->DestroyConjuredItems(true);
1160 plr->UnsummonPetTemporaryIfAny();
1162 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1164 plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true);
1166 plr->SetHealth(plr->GetMaxHealth());
1167 plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA));
1170 else
1172 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1173 plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells.
1176 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1177 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1179 // setup BG group membership
1180 PlayerAddedToBGCheckIfBGIsRunning(plr);
1181 AddOrSetPlayerToCorrectBgGroup(plr, guid, team);
1183 // Log
1184 sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName());
1187 /* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */
1188 void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team)
1190 Group* group = GetBgRaid(team);
1191 if(!group) // first player joined
1193 group = new Group;
1194 SetBgRaid(team, group);
1195 group->Create(plr_guid, plr->GetName());
1197 else // raid already exist
1199 if (group->IsMember(plr_guid))
1201 uint8 subgroup = group->GetMemberGroup(plr_guid);
1202 plr->SetBattleGroundRaid(group, subgroup);
1204 else
1206 group->AddMember(plr_guid, plr->GetName());
1207 if (Group* originalGroup = plr->GetOriginalGroup())
1208 if (originalGroup->IsLeader(plr_guid))
1209 group->ChangeLeader(plr_guid);
1214 // This method should be called when player logs into running battleground
1215 void BattleGround::EventPlayerLoggedIn(Player* player, uint64 plr_guid)
1217 // player is correct pointer
1218 for(std::deque<uint64>::iterator itr = m_OfflineQueue.begin(); itr != m_OfflineQueue.end(); ++itr)
1220 if (*itr == plr_guid)
1222 m_OfflineQueue.erase(itr);
1223 break;
1226 m_Players[plr_guid].OfflineRemoveTime = 0;
1227 PlayerAddedToBGCheckIfBGIsRunning(player);
1228 // if battleground is starting, then add preparation aura
1229 // we don't have to do that, because preparation aura isn't removed when player logs out
1232 // This method should be called when player logs out from running battleground
1233 void BattleGround::EventPlayerLoggedOut(Player* player)
1235 // player is correct pointer, it is checked in WorldSession::LogoutPlayer()
1236 m_OfflineQueue.push_back(player->GetGUID());
1237 m_Players[player->GetGUID()].OfflineRemoveTime = sWorld.GetGameTime() + MAX_OFFLINE_TIME;
1238 if (GetStatus() == STATUS_IN_PROGRESS)
1240 if (isBattleGround())
1241 EventPlayerDroppedFlag(player);
1242 else
1244 //1 player is logging out, if it is the last, then end arena!
1245 if (GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam())))
1246 EndBattleGround(GetOtherTeam(player->GetTeam()));
1251 /* This method should be called only once ... it adds pointer to queue */
1252 void BattleGround::AddToBGFreeSlotQueue()
1254 // make sure to add only once
1255 if (!m_InBGFreeSlotQueue && isBattleGround())
1257 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this);
1258 m_InBGFreeSlotQueue = true;
1262 /* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/
1263 void BattleGround::RemoveFromBGFreeSlotQueue()
1265 // set to be able to re-add if needed
1266 m_InBGFreeSlotQueue = false;
1267 // uncomment this code when battlegrounds will work like instances
1268 for (BGFreeSlotQueueType::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr)
1270 if ((*itr)->GetInstanceID() == m_InstanceID)
1272 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr);
1273 return;
1278 // get the number of free slots for team
1279 // returns the number how many players can join battleground to MaxPlayersPerTeam
1280 uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const
1282 //return free slot count to MaxPlayerPerTeam
1283 if (GetStatus() == STATUS_WAIT_JOIN || GetStatus() == STATUS_IN_PROGRESS)
1284 return (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0;
1286 return 0;
1289 bool BattleGround::HasFreeSlots() const
1291 return GetPlayersSize() < GetMaxPlayers();
1294 void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value)
1296 //this procedure is called from virtual function implemented in bg subclass
1297 BattleGroundScoreMap::const_iterator itr = m_PlayerScores.find(Source->GetGUID());
1299 if(itr == m_PlayerScores.end()) // player not found...
1300 return;
1302 switch(type)
1304 case SCORE_KILLING_BLOWS: // Killing blows
1305 itr->second->KillingBlows += value;
1306 break;
1307 case SCORE_DEATHS: // Deaths
1308 itr->second->Deaths += value;
1309 break;
1310 case SCORE_HONORABLE_KILLS: // Honorable kills
1311 itr->second->HonorableKills += value;
1312 break;
1313 case SCORE_BONUS_HONOR: // Honor bonus
1314 // do not add honor in arenas
1315 if (isBattleGround())
1317 // reward honor instantly
1318 if (Source->RewardHonor(NULL, 1, value))
1319 itr->second->BonusHonor += value;
1321 break;
1322 //used only in EY, but in MSG_PVP_LOG_DATA opcode
1323 case SCORE_DAMAGE_DONE: // Damage Done
1324 itr->second->DamageDone += value;
1325 break;
1326 case SCORE_HEALING_DONE: // Healing Done
1327 itr->second->HealingDone += value;
1328 break;
1329 default:
1330 sLog.outError("BattleGround: Unknown player score type %u", type);
1331 break;
1335 void BattleGround::AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid)
1337 m_ReviveQueue[npc_guid].push_back(player_guid);
1339 Player *plr = objmgr.GetPlayer(player_guid);
1340 if (!plr)
1341 return;
1343 plr->CastSpell(plr, SPELL_WAITING_FOR_RESURRECT, true);
1346 void BattleGround::RemovePlayerFromResurrectQueue(uint64 player_guid)
1348 for(std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
1350 for(std::vector<uint64>::iterator itr2 =(itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
1352 if (*itr2 == player_guid)
1354 (itr->second).erase(itr2);
1356 Player *plr = objmgr.GetPlayer(player_guid);
1357 if (!plr)
1358 return;
1360 plr->RemoveAurasDueToSpell(SPELL_WAITING_FOR_RESURRECT);
1362 return;
1368 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)
1370 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
1371 if (!map)
1372 return false;
1374 // must be created this way, adding to godatamap would add it to the base map of the instance
1375 // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
1376 // so we must create it specific for this instance
1377 GameObject * go = new GameObject;
1378 if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry, map,
1379 PHASEMASK_NORMAL, x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,GO_STATE_READY))
1381 sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry);
1382 sLog.outError("Cannot create gameobject template %u! BattleGround not created!", entry);
1383 delete go;
1384 return false;
1387 uint32 guid = go->GetGUIDLow();
1389 // without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata
1390 // 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
1391 GameObjectData& data = objmgr.NewGOData(guid);
1393 data.id = entry;
1394 data.mapid = GetMapId();
1395 data.posX = x;
1396 data.posY = y;
1397 data.posZ = z;
1398 data.orientation = o;
1399 data.rotation0 = rotation0;
1400 data.rotation1 = rotation1;
1401 data.rotation2 = rotation2;
1402 data.rotation3 = rotation3;
1403 data.spawntimesecs = respawnTime;
1404 data.spawnMask = 1;
1405 data.animprogress = 100;
1406 data.go_state = 1;
1408 // add to world, so it can be later looked up from HashMapHolder
1409 go->AddToWorld();
1410 m_BgObjects[type] = go->GetGUID();
1411 return true;
1414 //some doors aren't despawned so we cannot handle their closing in gameobject::update()
1415 //it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
1416 void BattleGround::DoorClose(uint32 type)
1418 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1419 if (obj)
1421 //if doors are open, close it
1422 if (obj->getLootState() == GO_ACTIVATED && obj->GetGoState() != GO_STATE_READY)
1424 //change state to allow door to be closed
1425 obj->SetLootState(GO_READY);
1426 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1429 else
1431 sLog.outError("BattleGround: Door object not found (cannot close doors)");
1435 void BattleGround::DoorOpen(uint32 type)
1437 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1438 if (obj)
1440 //change state to be sure they will be opened
1441 obj->SetLootState(GO_READY);
1442 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1444 else
1446 sLog.outError("BattleGround: Door object not found! - doors will be closed.");
1450 void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime)
1452 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
1453 if (!map)
1454 return;
1455 if (respawntime == 0)
1457 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1458 if (obj)
1460 //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again
1461 if (obj->getLootState() == GO_JUST_DEACTIVATED)
1462 obj->SetLootState(GO_READY);
1463 obj->SetRespawnTime(0);
1464 map->Add(obj);
1467 else
1469 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1470 if (obj)
1472 map->Add(obj);
1473 obj->SetRespawnTime(respawntime);
1474 obj->SetLootState(GO_JUST_DEACTIVATED);
1479 Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o, uint32 respawntime)
1481 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
1482 if (!map)
1483 return NULL;
1485 Creature* pCreature = new Creature;
1486 if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, PHASEMASK_NORMAL, entry, teamval))
1488 sLog.outError("Can't create creature entry: %u",entry);
1489 delete pCreature;
1490 return NULL;
1493 pCreature->Relocate(x, y, z, o);
1495 if (!pCreature->IsPositionValid())
1497 sLog.outError("Creature (guidlow %d, entry %d) not added to battleground. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
1498 delete pCreature;
1499 return NULL;
1502 pCreature->AIM_Initialize();
1504 //pCreature->SetDungeonDifficulty(0);
1506 map->Add(pCreature);
1507 m_BgCreatures[type] = pCreature->GetGUID();
1509 return pCreature;
1512 void BattleGround::SpawnBGCreature(uint32 type, uint32 respawntime)
1514 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceId());
1515 if (!map)
1516 return false;
1518 if (respawntime == 0)
1520 Creature *obj = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1521 if (obj)
1523 //obj->Respawn(); // bugged
1524 obj->SetRespawnTime(0);
1525 objmgr.SaveCreatureRespawnTime(obj->GetGUIDLow(), GetInstanceID(), 0);
1526 map->Add(obj);
1529 else
1531 Creature *obj = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1532 if (obj)
1534 obj->setDeathState(DEAD);
1535 obj->SetRespawnTime(respawntime);
1536 map->Add(obj);
1541 bool BattleGround::DelCreature(uint32 type)
1543 if (!m_BgCreatures[type])
1544 return true;
1546 Creature *cr = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1547 if (!cr)
1549 sLog.outError("Can't find creature guid: %u",GUID_LOPART(m_BgCreatures[type]));
1550 return false;
1552 cr->AddObjectToRemoveList();
1553 m_BgCreatures[type] = 0;
1554 return true;
1557 bool BattleGround::DelObject(uint32 type)
1559 if (!m_BgObjects[type])
1560 return true;
1562 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1563 if (!obj)
1565 sLog.outError("Can't find gobject guid: %u",GUID_LOPART(m_BgObjects[type]));
1566 return false;
1568 obj->SetRespawnTime(0); // not save respawn time
1569 obj->Delete();
1570 m_BgObjects[type] = 0;
1571 return true;
1574 bool BattleGround::AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team)
1576 uint32 entry = 0;
1578 if (team == ALLIANCE)
1579 entry = 13116;
1580 else
1581 entry = 13117;
1583 Creature* pCreature = AddCreature(entry,type,team,x,y,z,o);
1584 if (!pCreature)
1586 sLog.outError("Can't create Spirit guide. BattleGround not created!");
1587 EndNow();
1588 return false;
1591 pCreature->setDeathState(DEAD);
1593 pCreature->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, pCreature->GetGUID());
1594 // aura
1595 pCreature->SetVisibleAura(0, SPELL_SPIRIT_HEAL_CHANNEL);
1596 //pCreature->SetUInt32Value(UNIT_FIELD_AURAFLAGS, 0x00000009);
1597 //pCreature->SetUInt32Value(UNIT_FIELD_AURALEVELS, 0x0000003C);
1598 //pCreature->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS, 0x000000FF);
1599 // casting visual effect
1600 pCreature->SetUInt32Value(UNIT_CHANNEL_SPELL, SPELL_SPIRIT_HEAL_CHANNEL);
1601 // correct cast speed
1602 pCreature->SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
1604 //pCreature->CastSpell(pCreature, SPELL_SPIRIT_HEAL_CHANNEL, true);
1606 return true;
1609 void BattleGround::SendMessageToAll(int32 entry, ChatMsg type, Player const* source)
1611 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source);
1612 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1613 BroadcastWorker(bg_do);
1616 void BattleGround::PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...)
1618 va_list ap;
1619 va_start(ap, source);
1621 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source, &ap);
1622 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1623 BroadcastWorker(bg_do);
1625 va_end(ap);
1628 void BattleGround::SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 arg1, int32 arg2)
1630 MaNGOS::BattleGround2ChatBuilder bg_builder(type, entry, source, arg1, arg2);
1631 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGround2ChatBuilder> bg_do(bg_builder);
1632 BroadcastWorker(bg_do);
1635 void BattleGround::EndNow()
1637 RemoveFromBGFreeSlotQueue();
1638 SetStatus(STATUS_WAIT_LEAVE);
1639 SetEndTime(0);
1643 important notice:
1644 buffs aren't spawned/despawned when players captures anything
1645 buffs are in their positions when battleground starts
1647 void BattleGround::HandleTriggerBuff(uint64 const& go_guid)
1649 GameObject *obj = HashMapHolder<GameObject>::Find(go_guid);
1650 if (!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned())
1651 return;
1653 //change buff type, when buff is used:
1654 int32 index = m_BgObjects.size() - 1;
1655 while (index >= 0 && m_BgObjects[index] != go_guid)
1656 index--;
1657 if (index < 0)
1659 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());
1660 return;
1663 //randomly select new buff
1664 uint8 buff = urand(0, 2);
1665 uint32 entry = obj->GetEntry();
1666 if (m_BuffChange && entry != Buff_Entries[buff])
1668 //despawn current buff
1669 SpawnBGObject(index, RESPAWN_ONE_DAY);
1670 //set index for new one
1671 for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex)
1672 if (entry == Buff_Entries[currBuffTypeIndex])
1674 index -= currBuffTypeIndex;
1675 index += buff;
1679 SpawnBGObject(index, BUFF_RESPAWN_TIME);
1682 void BattleGround::HandleKillPlayer( Player *player, Player *killer )
1684 //keep in mind that for arena this will have to be changed a bit
1686 // add +1 deaths
1687 UpdatePlayerScore(player, SCORE_DEATHS, 1);
1689 // add +1 kills to group and +1 killing_blows to killer
1690 if (killer)
1692 UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1);
1693 UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1);
1695 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1697 Player *plr = objmgr.GetPlayer(itr->first);
1699 if (!plr || plr == killer)
1700 continue;
1702 if (plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player))
1703 UpdatePlayerScore(plr, SCORE_HONORABLE_KILLS, 1);
1707 // to be able to remove insignia -- ONLY IN BattleGrounds
1708 if (!isArena())
1709 player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE );
1712 // return the player's team based on battlegroundplayer info
1713 // used in same faction arena matches mainly
1714 uint32 BattleGround::GetPlayerTeam(uint64 guid)
1716 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1717 if (itr!=m_Players.end())
1718 return itr->second.Team;
1719 return 0;
1722 uint32 BattleGround::GetOtherTeam(uint32 teamId)
1724 return (teamId) ? ((teamId == ALLIANCE) ? HORDE : ALLIANCE) : 0;
1727 bool BattleGround::IsPlayerInBattleGround(uint64 guid)
1729 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1730 if (itr != m_Players.end())
1731 return true;
1732 return false;
1735 void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player* plr)
1737 if (GetStatus() != STATUS_WAIT_LEAVE)
1738 return;
1740 WorldPacket data;
1741 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1743 BlockMovement(plr);
1745 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
1746 plr->GetSession()->SendPacket(&data);
1748 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType());
1749 plr->GetSession()->SendPacket(&data);
1752 uint32 BattleGround::GetAlivePlayersCountByTeam(uint32 Team) const
1754 int count = 0;
1755 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1757 if (itr->second.Team == Team)
1759 Player * pl = objmgr.GetPlayer(itr->first);
1760 if (pl && pl->isAlive())
1761 ++count;
1764 return count;
1767 void BattleGround::CheckArenaWinConditions()
1769 if (!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE))
1770 EndBattleGround(HORDE);
1771 else if (GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE))
1772 EndBattleGround(ALLIANCE);
1775 void BattleGround::SetBgRaid( uint32 TeamID, Group *bg_raid )
1777 Group* &old_raid = TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE];
1778 if(old_raid) old_raid->SetBattlegroundGroup(NULL);
1779 if(bg_raid) bg_raid->SetBattlegroundGroup(this);
1780 old_raid = bg_raid;
1783 WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard( Player* player )
1785 return objmgr.GetClosestGraveYard( player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam() );
1788 bool BattleGround::IsTeamScoreInRange(uint32 team, uint32 minScore, uint32 maxScore) const
1790 BattleGroundTeamId team_idx = GetTeamIndexByTeamId(team);
1791 uint32 score = (m_TeamScores[team_idx] < 0) ? 0 : uint32(m_TeamScores[team_idx]);
1792 return score >= minScore && score <= maxScore;