[8315] Fixed memory leaks (mostly at server shutdown) and code cleanups.
[getmangos.git] / src / game / BattleGround.cpp
blob9449e4f6e13f9b1b33982e6dc347e12ceab668dd
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() && !GetReviveQueueSize())
235 //BG is empty
236 return;
238 // remove offline players from bg after 5 minutes
239 if (!m_OfflineQueue.empty())
241 BattleGroundPlayerMap::iterator itr = m_Players.find(*(m_OfflineQueue.begin()));
242 if (itr != m_Players.end())
244 if (itr->second.OfflineRemoveTime <= sWorld.GetGameTime())
246 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
247 m_OfflineQueue.pop_front(); // remove from offline queue
248 //do not use itr for anything, because it is erased in RemovePlayerAtLeave()
253 /*********************************************************/
254 /*** BATTLEGROUND RESSURECTION SYSTEM ***/
255 /*********************************************************/
257 //this should be handled by spell system
258 m_LastResurrectTime += diff;
259 if (m_LastResurrectTime >= RESURRECTION_INTERVAL)
261 if (GetReviveQueueSize())
263 for(std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
265 Creature *sh = NULL;
266 for(std::vector<uint64>::const_iterator itr2 = (itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
268 Player *plr = objmgr.GetPlayer(*itr2);
269 if (!plr)
270 continue;
272 if (!sh && plr->IsInWorld())
274 sh = plr->GetMap()->GetCreature(itr->first);
275 // only for visual effect
276 if (sh)
277 sh->CastSpell(sh, SPELL_SPIRIT_HEAL, true); // Spirit Heal, effect 117
280 plr->CastSpell(plr, SPELL_RESURRECTION_VISUAL, true); // Resurrection visual
281 m_ResurrectQueue.push_back(*itr2);
283 (itr->second).clear();
286 m_ReviveQueue.clear();
287 m_LastResurrectTime = 0;
289 else
290 // queue is clear and time passed, just update last resurrection time
291 m_LastResurrectTime = 0;
293 else if (m_LastResurrectTime > 500) // Resurrect players only half a second later, to see spirit heal effect on NPC
295 for(std::vector<uint64>::const_iterator itr = m_ResurrectQueue.begin(); itr != m_ResurrectQueue.end(); ++itr)
297 Player *plr = objmgr.GetPlayer(*itr);
298 if (!plr)
299 continue;
300 plr->ResurrectPlayer(1.0f);
301 plr->CastSpell(plr, SPELL_SPIRIT_HEAL_MANA, true);
302 ObjectAccessor::Instance().ConvertCorpseForPlayer(*itr);
304 m_ResurrectQueue.clear();
307 /*********************************************************/
308 /*** BATTLEGROUND BALLANCE SYSTEM ***/
309 /*********************************************************/
311 // if less then minimum players are in on one side, then start premature finish timer
312 if (GetStatus() == STATUS_IN_PROGRESS && !isArena() && sBattleGroundMgr.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE) < GetMinPlayersPerTeam()))
314 if (!m_PrematureCountDown)
316 m_PrematureCountDown = true;
317 m_PrematureCountDownTimer = sBattleGroundMgr.GetPrematureFinishTime();
319 else if (m_PrematureCountDownTimer < diff)
321 // time's up!
322 uint32 winner = 0;
323 if (GetPlayersCountByTeam(ALLIANCE) >= GetMinPlayersPerTeam())
324 winner = ALLIANCE;
325 else if (GetPlayersCountByTeam(HORDE) >= GetMinPlayersPerTeam())
326 winner = HORDE;
328 EndBattleGround(winner);
329 m_PrematureCountDown = false;
331 else
333 uint32 newtime = m_PrematureCountDownTimer - diff;
334 // announce every minute
335 if (newtime > (MINUTE * IN_MILISECONDS))
337 if (newtime / (MINUTE * IN_MILISECONDS) != m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS))
338 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS)));
340 else
342 //announce every 15 seconds
343 if (newtime / (15 * IN_MILISECONDS) != m_PrematureCountDownTimer / (15 * IN_MILISECONDS))
344 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / IN_MILISECONDS));
346 m_PrematureCountDownTimer = newtime;
349 else if (m_PrematureCountDown)
350 m_PrematureCountDown = false;
352 /*********************************************************/
353 /*** BATTLEGROUND STARTING SYSTEM ***/
354 /*********************************************************/
356 if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize())
358 ModifyStartDelayTime(diff);
360 if (!(m_Events & BG_STARTING_EVENT_1))
362 m_Events |= BG_STARTING_EVENT_1;
364 // setup here, only when at least one player has ported to the map
365 if (!SetupBattleGround())
367 EndNow();
368 return;
371 StartingEventCloseDoors();
372 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FIRST]);
373 //first start warning - 2 or 1 minute
374 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FIRST], CHAT_MSG_BG_SYSTEM_NEUTRAL);
376 // After 1 minute or 30 seconds, warning is signalled
377 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_SECOND] && !(m_Events & BG_STARTING_EVENT_2))
379 m_Events |= BG_STARTING_EVENT_2;
380 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_SECOND], CHAT_MSG_BG_SYSTEM_NEUTRAL);
382 // After 30 or 15 seconds, warning is signalled
383 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_THIRD] && !(m_Events & BG_STARTING_EVENT_3))
385 m_Events |= BG_STARTING_EVENT_3;
386 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_THIRD], CHAT_MSG_BG_SYSTEM_NEUTRAL);
388 // delay expired (atfer 2 or 1 minute)
389 else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4))
391 m_Events |= BG_STARTING_EVENT_4;
393 StartingEventOpenDoors();
395 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FOURTH], CHAT_MSG_BG_SYSTEM_NEUTRAL);
396 SetStatus(STATUS_IN_PROGRESS);
397 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FOURTH]);
399 //remove preparation
400 if (isArena())
402 //TODO : add arena sound PlaySoundToAll(SOUND_ARENA_START);
404 for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
405 if (Player *plr = objmgr.GetPlayer(itr->first))
406 plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION);
408 CheckArenaWinConditions();
410 else
413 PlaySoundToAll(SOUND_BG_START);
415 for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
416 if (Player* plr = objmgr.GetPlayer(itr->first))
417 plr->RemoveAurasDueToSpell(SPELL_PREPARATION);
418 //Announce BG starting
419 if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE))
421 sWorld.SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD, GetName(), GetMinLevel(), GetMaxLevel());
427 /*********************************************************/
428 /*** BATTLEGROUND ENDING SYSTEM ***/
429 /*********************************************************/
431 if (GetStatus() == STATUS_WAIT_LEAVE)
433 // remove all players from battleground after 2 minutes
434 m_EndTime -= diff;
435 if (m_EndTime <= 0)
437 m_EndTime = 0;
438 BattleGroundPlayerMap::iterator itr, next;
439 for(itr = m_Players.begin(); itr != m_Players.end(); itr = next)
441 next = itr;
442 ++next;
443 //itr is erased here!
444 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
445 // do not change any battleground's private variables
450 //update start time
451 m_StartTime += diff;
454 void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O)
456 BattleGroundTeamId idx = GetTeamIndexByTeamId(TeamID);
457 m_TeamStartLocX[idx] = X;
458 m_TeamStartLocY[idx] = Y;
459 m_TeamStartLocZ[idx] = Z;
460 m_TeamStartLocO[idx] = O;
463 void BattleGround::SendPacketToAll(WorldPacket *packet)
465 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
467 Player *plr = objmgr.GetPlayer(itr->first);
468 if (plr)
469 plr->GetSession()->SendPacket(packet);
470 else
471 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
475 void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender, bool self)
477 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
479 Player *plr = objmgr.GetPlayer(itr->first);
481 if (!plr)
483 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
484 continue;
487 if (!self && sender == plr)
488 continue;
490 uint32 team = itr->second.Team;
491 if(!team) team = plr->GetTeam();
493 if (team == TeamID)
494 plr->GetSession()->SendPacket(packet);
498 void BattleGround::PlaySoundToAll(uint32 SoundID)
500 WorldPacket data;
501 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
502 SendPacketToAll(&data);
505 void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID)
507 WorldPacket data;
509 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
511 Player *plr = objmgr.GetPlayer(itr->first);
513 if (!plr)
515 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
516 continue;
519 uint32 team = itr->second.Team;
520 if(!team) team = plr->GetTeam();
522 if (team == TeamID)
524 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
525 plr->GetSession()->SendPacket(&data);
530 void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID)
532 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
534 Player *plr = objmgr.GetPlayer(itr->first);
536 if (!plr)
538 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
539 continue;
542 uint32 team = itr->second.Team;
543 if(!team) team = plr->GetTeam();
545 if (team == TeamID)
546 plr->CastSpell(plr, SpellID, true);
550 void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID)
552 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
554 Player *plr = objmgr.GetPlayer(itr->first);
556 if (!plr)
558 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
559 continue;
562 uint32 team = itr->second.Team;
563 if(!team) team = plr->GetTeam();
565 if (team == TeamID)
566 UpdatePlayerScore(plr, SCORE_BONUS_HONOR, Honor);
570 void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID)
572 FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
574 if (!factionEntry)
575 return;
577 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
579 Player *plr = objmgr.GetPlayer(itr->first);
581 if (!plr)
583 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
584 continue;
587 uint32 team = itr->second.Team;
588 if(!team) team = plr->GetTeam();
590 if (team == TeamID)
591 plr->GetReputationMgr().ModifyReputation(factionEntry, Reputation);
595 void BattleGround::UpdateWorldState(uint32 Field, uint32 Value)
597 WorldPacket data;
598 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
599 SendPacketToAll(&data);
602 void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source)
604 WorldPacket data;
605 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
606 Source->GetSession()->SendPacket(&data);
609 void BattleGround::EndBattleGround(uint32 winner)
611 this->RemoveFromBGFreeSlotQueue();
613 ArenaTeam * winner_arena_team = NULL;
614 ArenaTeam * loser_arena_team = NULL;
615 uint32 loser_rating = 0;
616 uint32 winner_rating = 0;
617 WorldPacket data;
618 int32 winmsg_id = 0;
620 if (winner == ALLIANCE)
622 winmsg_id = isBattleGround() ? LANG_BG_A_WINS : LANG_ARENA_GOLD_WINS;
624 PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound
626 SetWinner(WINNER_ALLIANCE);
628 else if (winner == HORDE)
630 winmsg_id = isBattleGround() ? LANG_BG_H_WINS : LANG_ARENA_GREEN_WINS;
632 PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound
634 SetWinner(WINNER_HORDE);
636 else
638 SetWinner(3);
641 SetStatus(STATUS_WAIT_LEAVE);
642 //we must set it this way, because end time is sent in packet!
643 m_EndTime = TIME_TO_AUTOREMOVE;
645 // arena rating calculation
646 if (isArena() && isRated())
648 winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(winner));
649 loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(winner)));
650 if (winner_arena_team && loser_arena_team)
652 loser_rating = loser_arena_team->GetStats().rating;
653 winner_rating = winner_arena_team->GetStats().rating;
654 int32 winner_change = winner_arena_team->WonAgainst(loser_rating);
655 int32 loser_change = loser_arena_team->LostAgainst(winner_rating);
656 sLog.outDebug("--- Winner rating: %u, Loser rating: %u, Winner change: %u, Losser change: %u ---", winner_rating, loser_rating, winner_change, loser_change);
657 SetArenaTeamRatingChangeForTeam(winner, winner_change);
658 SetArenaTeamRatingChangeForTeam(GetOtherTeam(winner), loser_change);
660 else
662 SetArenaTeamRatingChangeForTeam(ALLIANCE, 0);
663 SetArenaTeamRatingChangeForTeam(HORDE, 0);
667 for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
669 Player *plr = objmgr.GetPlayer(itr->first);
670 uint32 team = itr->second.Team;
672 if (!plr)
674 //if rated arena match - make member lost!
675 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
677 if (team == winner)
678 winner_arena_team->OfflineMemberLost(itr->first, loser_rating);
679 else
680 loser_arena_team->OfflineMemberLost(itr->first, winner_rating);
682 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
683 continue;
686 // should remove spirit of redemption
687 if (plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
688 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
690 if (!plr->isAlive())
692 plr->ResurrectPlayer(1.0f);
693 plr->SpawnCorpseBones();
696 //this line is obsolete - team is set ALWAYS
697 //if(!team) team = plr->GetTeam();
699 // per player calculation
700 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
702 if (team == winner)
704 // update achievement BEFORE personal rating update
705 ArenaTeamMember* member = winner_arena_team->GetMember(plr->GetGUID());
706 if (member)
707 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, member->personal_rating);
709 winner_arena_team->MemberWon(plr,loser_rating);
711 else
713 loser_arena_team->MemberLost(plr,winner_rating);
715 // Arena lost => reset the win_rated_arena having the "no_loose" condition
716 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE);
720 if (team == winner)
722 RewardMark(plr,ITEM_WINNER_COUNT);
723 RewardQuestComplete(plr);
725 else
726 RewardMark(plr,ITEM_LOSER_COUNT);
728 plr->CombatStopWithPets(true);
730 BlockMovement(plr);
732 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
733 plr->GetSession()->SendPacket(&data);
735 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
736 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType());
737 plr->GetSession()->SendPacket(&data);
738 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1);
741 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
743 // update arena points only after increasing the player's match count!
744 //obsolete: winner_arena_team->UpdateArenaPointsHelper();
745 //obsolete: loser_arena_team->UpdateArenaPointsHelper();
746 // save the stat changes
747 winner_arena_team->SaveToDB();
748 loser_arena_team->SaveToDB();
749 // send updated arena team stats to players
750 // this way all arena team members will get notified, not only the ones who participated in this match
751 winner_arena_team->NotifyStatsChanged();
752 loser_arena_team->NotifyStatsChanged();
755 if (winmsg_id)
756 SendMessageToAll(winmsg_id, CHAT_MSG_BG_SYSTEM_NEUTRAL);
759 uint32 BattleGround::GetBonusHonorFromKill(uint32 kills) const
761 //variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill)
762 return MaNGOS::Honor::hk_honor_at_level(GetMaxLevel(), kills);
765 uint32 BattleGround::GetBattlemasterEntry() const
767 switch(GetTypeID())
769 case BATTLEGROUND_AV: return 15972;
770 case BATTLEGROUND_WS: return 14623;
771 case BATTLEGROUND_AB: return 14879;
772 case BATTLEGROUND_EY: return 22516;
773 case BATTLEGROUND_NA: return 20200;
774 default: return 0;
778 void BattleGround::RewardMark(Player *plr,uint32 count)
780 BattleGroundMarks mark;
781 bool IsSpell;
782 switch(GetTypeID())
784 case BATTLEGROUND_AV:
785 IsSpell = true;
786 if (count == ITEM_WINNER_COUNT)
787 mark = SPELL_AV_MARK_WINNER;
788 else
789 mark = SPELL_AV_MARK_LOSER;
790 break;
791 case BATTLEGROUND_WS:
792 IsSpell = true;
793 if (count == ITEM_WINNER_COUNT)
794 mark = SPELL_WS_MARK_WINNER;
795 else
796 mark = SPELL_WS_MARK_LOSER;
797 break;
798 case BATTLEGROUND_AB:
799 IsSpell = true;
800 if (count == ITEM_WINNER_COUNT)
801 mark = SPELL_AB_MARK_WINNER;
802 else
803 mark = SPELL_AB_MARK_LOSER;
804 break;
805 case BATTLEGROUND_EY:
806 IsSpell = false;
807 mark = ITEM_EY_MARK_OF_HONOR;
808 break;
809 default:
810 return;
813 if (IsSpell)
814 RewardSpellCast(plr,mark);
815 else
816 RewardItem(plr,mark,count);
819 void BattleGround::RewardSpellCast(Player *plr, uint32 spell_id)
821 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
822 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
823 return;
825 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
826 if(!spellInfo)
828 sLog.outError("Battleground reward casting spell %u not exist.",spell_id);
829 return;
832 plr->CastSpell(plr, spellInfo, true);
835 void BattleGround::RewardItem(Player *plr, uint32 item_id, uint32 count)
837 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
838 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
839 return;
841 ItemPosCountVec dest;
842 uint32 no_space_count = 0;
843 uint8 msg = plr->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item_id, count, &no_space_count );
845 if( msg == EQUIP_ERR_ITEM_NOT_FOUND)
847 sLog.outErrorDb("Battleground reward item (Entry %u) not exist in `item_template`.",item_id);
848 return;
851 if( msg != EQUIP_ERR_OK ) // convert to possible store amount
852 count -= no_space_count;
854 if( count != 0 && !dest.empty()) // can add some
855 if (Item* item = plr->StoreNewItem( dest, item_id, true, 0))
856 plr->SendNewItem(item,count,false,true);
858 if (no_space_count > 0)
859 SendRewardMarkByMail(plr,item_id,no_space_count);
862 void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count)
864 uint32 bmEntry = GetBattlemasterEntry();
865 if (!bmEntry)
866 return;
868 ItemPrototype const* markProto = objmgr.GetItemPrototype(mark);
869 if (!markProto)
870 return;
872 if (Item* markItem = Item::CreateItem(mark,count,plr))
874 // save new item before send
875 markItem->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
877 // item
878 MailItemsInfo mi;
879 mi.AddItem(markItem->GetGUIDLow(), markItem->GetEntry(), markItem);
881 // subject: item name
882 std::string subject = markProto->Name1;
883 int loc_idx = plr->GetSession()->GetSessionDbLocaleIndex();
884 if (loc_idx >= 0 )
885 if (ItemLocale const *il = objmgr.GetItemLocale(markProto->ItemId))
886 if (il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty())
887 subject = il->Name[loc_idx];
889 // text
890 std::string textFormat = plr->GetSession()->GetMangosString(LANG_BG_MARK_BY_MAIL);
891 char textBuf[300];
892 snprintf(textBuf,300,textFormat.c_str(),GetName(),GetName());
893 uint32 itemTextId = objmgr.CreateItemText( textBuf );
895 WorldSession::SendMailTo(plr, MAIL_CREATURE, MAIL_STATIONERY_NORMAL, bmEntry, plr->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE);
899 void BattleGround::RewardQuestComplete(Player *plr)
901 uint32 quest;
902 switch(GetTypeID())
904 case BATTLEGROUND_AV:
905 quest = SPELL_AV_QUEST_REWARD;
906 break;
907 case BATTLEGROUND_WS:
908 quest = SPELL_WS_QUEST_REWARD;
909 break;
910 case BATTLEGROUND_AB:
911 quest = SPELL_AB_QUEST_REWARD;
912 break;
913 case BATTLEGROUND_EY:
914 quest = SPELL_EY_QUEST_REWARD;
915 break;
916 default:
917 return;
920 RewardSpellCast(plr, quest);
923 void BattleGround::BlockMovement(Player *plr)
925 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()
928 void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket)
930 uint32 team = GetPlayerTeam(guid);
931 bool participant = false;
932 // Remove from lists/maps
933 BattleGroundPlayerMap::iterator itr = m_Players.find(guid);
934 if (itr != m_Players.end())
936 UpdatePlayersCountByTeam(team, true); // -1 player
937 m_Players.erase(itr);
938 // check if the player was a participant of the match, or only entered through gm command (goname)
939 participant = true;
942 BattleGroundScoreMap::iterator itr2 = m_PlayerScores.find(guid);
943 if (itr2 != m_PlayerScores.end())
945 delete itr2->second; // delete player's score
946 m_PlayerScores.erase(itr2);
949 RemovePlayerFromResurrectQueue(guid);
951 Player *plr = objmgr.GetPlayer(guid);
953 // should remove spirit of redemption
954 if (plr && plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
955 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
957 if(plr && !plr->isAlive()) // resurrect on exit
959 plr->ResurrectPlayer(1.0f);
960 plr->SpawnCorpseBones();
963 RemovePlayer(plr, guid); // BG subclass specific code
965 if(participant) // if the player was a match participant, remove auras, calc rating, update queue
967 BattleGroundTypeId bgTypeId = GetTypeID();
968 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
969 if (plr)
971 plr->ClearAfkReports();
973 if(!team) team = plr->GetTeam();
975 // if arena, remove the specific arena auras
976 if (isArena())
978 plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out
979 bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing)
981 // unsummon current and summon old pet if there was one and there isn't a current pet
982 plr->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT);
983 plr->ResummonPetTemporaryUnSummonedIfAny();
985 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
987 //left a rated match while the encounter was in progress, consider as loser
988 ArenaTeam * winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
989 ArenaTeam * loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
990 if (winner_arena_team && loser_arena_team)
991 loser_arena_team->MemberLost(plr,winner_arena_team->GetRating());
994 if (SendPacket)
996 WorldPacket data;
997 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0, 0);
998 plr->GetSession()->SendPacket(&data);
1001 // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg
1002 plr->RemoveBattleGroundQueueId(bgQueueTypeId);
1004 else
1005 // removing offline participant
1007 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
1009 //left a rated match while the encounter was in progress, consider as loser
1010 ArenaTeam * others_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
1011 ArenaTeam * players_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
1012 if (others_arena_team && players_arena_team)
1013 players_arena_team->OfflineMemberLost(guid, others_arena_team->GetRating());
1017 // remove from raid group if player is member
1018 if (Group *group = GetBgRaid(team))
1020 if( !group->RemoveMember(guid, 0) ) // group was disbanded
1022 SetBgRaid(team, NULL);
1023 delete group;
1026 DecreaseInvitedCount(team);
1027 //we should update battleground queue, but only if bg isn't ending
1028 if (isBattleGround() && GetStatus() < STATUS_WAIT_LEAVE)
1029 sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, GetQueueId());
1030 // Let others know
1031 WorldPacket data;
1032 sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, guid);
1033 SendPacketToTeam(team, &data, plr, false);
1036 if (plr)
1038 // Do next only if found in battleground
1039 plr->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG.
1040 // reset destination bg team
1041 plr->SetBGTeam(0);
1043 if (Transport)
1044 plr->TeleportTo(plr->GetBattleGroundEntryPoint());
1046 sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName());
1049 if (!GetPlayersSize() && !GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
1051 // if no players left AND no invitees left, set this bg to delete in next update
1052 // direct deletion could cause crashes
1053 m_SetDeleteThis = true;
1054 // return to prevent addition to freeslotqueue
1055 return;
1058 // a player exited the battleground, so there are free slots. add to queue
1059 this->AddToBGFreeSlotQueue();
1062 // this method is called when no players remains in battleground
1063 void BattleGround::Reset()
1065 SetQueueId(QUEUE_ID_MAX_LEVEL_19);
1066 SetWinner(WINNER_NONE);
1067 SetStatus(STATUS_WAIT_QUEUE);
1068 SetStartTime(0);
1069 SetEndTime(0);
1070 SetLastResurrectTime(0);
1071 SetArenaType(0);
1072 SetRated(false);
1074 m_Events = 0;
1076 if (m_InvitedAlliance > 0 || m_InvitedHorde > 0)
1077 sLog.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde);
1079 m_InvitedAlliance = 0;
1080 m_InvitedHorde = 0;
1081 m_InBGFreeSlotQueue = false;
1083 m_Players.clear();
1085 for(BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr)
1086 delete itr->second;
1087 m_PlayerScores.clear();
1090 void BattleGround::StartBattleGround()
1092 ///this method should spawn spirit guides and so on
1093 SetStartTime(0);
1095 SetLastResurrectTime(0);
1098 void BattleGround::AddPlayer(Player *plr)
1100 // score struct must be created in inherited class
1102 uint64 guid = plr->GetGUID();
1103 uint32 team = plr->GetBGTeam();
1105 BattleGroundPlayer bp;
1106 bp.OfflineRemoveTime = 0;
1107 bp.Team = team;
1109 // Add to list/maps
1110 m_Players[guid] = bp;
1112 UpdatePlayersCountByTeam(team, false); // +1 player
1114 WorldPacket data;
1115 sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr);
1116 SendPacketToTeam(team, &data, plr, false);
1118 // add arena specific auras
1119 if (isArena())
1121 plr->RemoveArenaSpellCooldowns();
1122 plr->RemoveArenaAuras();
1123 plr->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT);
1124 if(team == ALLIANCE) // gold
1126 if (plr->GetTeam() == HORDE)
1127 plr->CastSpell(plr, SPELL_HORDE_GOLD_FLAG,true);
1128 else
1129 plr->CastSpell(plr, SPELL_ALLIANCE_GOLD_FLAG,true);
1131 else // green
1133 if (plr->GetTeam() == HORDE)
1134 plr->CastSpell(plr, SPELL_HORDE_GREEN_FLAG,true);
1135 else
1136 plr->CastSpell(plr, SPELL_ALLIANCE_GREEN_FLAG,true);
1139 plr->DestroyConjuredItems(true);
1140 plr->UnsummonPetTemporaryIfAny();
1142 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1144 plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true);
1146 plr->SetHealth(plr->GetMaxHealth());
1147 plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA));
1150 else
1152 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1153 plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells.
1156 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1157 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1159 // setup BG group membership
1160 PlayerAddedToBGCheckIfBGIsRunning(plr);
1161 AddOrSetPlayerToCorrectBgGroup(plr, guid, team);
1163 // Log
1164 sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName());
1167 /* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */
1168 void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team)
1170 Group* group = GetBgRaid(team);
1171 if(!group) // first player joined
1173 group = new Group;
1174 SetBgRaid(team, group);
1175 group->Create(plr_guid, plr->GetName());
1177 else // raid already exist
1179 if (group->IsMember(plr_guid))
1181 uint8 subgroup = group->GetMemberGroup(plr_guid);
1182 plr->SetBattleGroundRaid(group, subgroup);
1184 else
1186 group->AddMember(plr_guid, plr->GetName());
1187 if (Group* originalGroup = plr->GetOriginalGroup())
1188 if (originalGroup->IsLeader(plr_guid))
1189 group->ChangeLeader(plr_guid);
1194 // This method should be called when player logs into running battleground
1195 void BattleGround::EventPlayerLoggedIn(Player* player, uint64 plr_guid)
1197 // player is correct pointer
1198 for(std::deque<uint64>::iterator itr = m_OfflineQueue.begin(); itr != m_OfflineQueue.end(); ++itr)
1200 if (*itr == plr_guid)
1202 m_OfflineQueue.erase(itr);
1203 break;
1206 m_Players[plr_guid].OfflineRemoveTime = 0;
1207 PlayerAddedToBGCheckIfBGIsRunning(player);
1208 // if battleground is starting, then add preparation aura
1209 // we don't have to do that, because preparation aura isn't removed when player logs out
1212 // This method should be called when player logs out from running battleground
1213 void BattleGround::EventPlayerLoggedOut(Player* player)
1215 // player is correct pointer, it is checked in WorldSession::LogoutPlayer()
1216 m_OfflineQueue.push_back(player->GetGUID());
1217 m_Players[player->GetGUID()].OfflineRemoveTime = sWorld.GetGameTime() + MAX_OFFLINE_TIME;
1218 if (GetStatus() == STATUS_IN_PROGRESS)
1220 if (isBattleGround())
1221 EventPlayerDroppedFlag(player);
1222 else
1224 //1 player is logging out, if it is the last, then end arena!
1225 if (GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam())))
1226 EndBattleGround(GetOtherTeam(player->GetTeam()));
1231 /* This method should be called only once ... it adds pointer to queue */
1232 void BattleGround::AddToBGFreeSlotQueue()
1234 // make sure to add only once
1235 if (!m_InBGFreeSlotQueue && isBattleGround())
1237 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this);
1238 m_InBGFreeSlotQueue = true;
1242 /* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/
1243 void BattleGround::RemoveFromBGFreeSlotQueue()
1245 // set to be able to re-add if needed
1246 m_InBGFreeSlotQueue = false;
1247 // uncomment this code when battlegrounds will work like instances
1248 for (BGFreeSlotQueueType::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr)
1250 if ((*itr)->GetInstanceID() == m_InstanceID)
1252 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr);
1253 return;
1258 // get the number of free slots for team
1259 // returns the number how many players can join battleground to MaxPlayersPerTeam
1260 uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const
1262 //return free slot count to MaxPlayerPerTeam
1263 if (GetStatus() == STATUS_WAIT_JOIN || GetStatus() == STATUS_IN_PROGRESS)
1264 return (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0;
1266 return 0;
1269 bool BattleGround::HasFreeSlots() const
1271 return GetPlayersSize() < GetMaxPlayers();
1274 void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value)
1276 //this procedure is called from virtual function implemented in bg subclass
1277 BattleGroundScoreMap::const_iterator itr = m_PlayerScores.find(Source->GetGUID());
1279 if(itr == m_PlayerScores.end()) // player not found...
1280 return;
1282 switch(type)
1284 case SCORE_KILLING_BLOWS: // Killing blows
1285 itr->second->KillingBlows += value;
1286 break;
1287 case SCORE_DEATHS: // Deaths
1288 itr->second->Deaths += value;
1289 break;
1290 case SCORE_HONORABLE_KILLS: // Honorable kills
1291 itr->second->HonorableKills += value;
1292 break;
1293 case SCORE_BONUS_HONOR: // Honor bonus
1294 // do not add honor in arenas
1295 if (isBattleGround())
1297 // reward honor instantly
1298 if (Source->RewardHonor(NULL, 1, value))
1299 itr->second->BonusHonor += value;
1301 break;
1302 //used only in EY, but in MSG_PVP_LOG_DATA opcode
1303 case SCORE_DAMAGE_DONE: // Damage Done
1304 itr->second->DamageDone += value;
1305 break;
1306 case SCORE_HEALING_DONE: // Healing Done
1307 itr->second->HealingDone += value;
1308 break;
1309 default:
1310 sLog.outError("BattleGround: Unknown player score type %u", type);
1311 break;
1315 void BattleGround::AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid)
1317 m_ReviveQueue[npc_guid].push_back(player_guid);
1319 Player *plr = objmgr.GetPlayer(player_guid);
1320 if (!plr)
1321 return;
1323 plr->CastSpell(plr, SPELL_WAITING_FOR_RESURRECT, true);
1324 SpellEntry const *spellInfo = sSpellStore.LookupEntry( SPELL_WAITING_FOR_RESURRECT );
1325 if (spellInfo)
1327 Aura *Aur = CreateAura(spellInfo, 0, NULL, plr);
1328 plr->AddAura(Aur);
1332 void BattleGround::RemovePlayerFromResurrectQueue(uint64 player_guid)
1334 for(std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
1336 for(std::vector<uint64>::iterator itr2 =(itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
1338 if (*itr2 == player_guid)
1340 (itr->second).erase(itr2);
1342 Player *plr = objmgr.GetPlayer(player_guid);
1343 if (!plr)
1344 return;
1346 plr->RemoveAurasDueToSpell(SPELL_WAITING_FOR_RESURRECT);
1348 return;
1354 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)
1356 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
1357 if (!map)
1358 return false;
1360 // must be created this way, adding to godatamap would add it to the base map of the instance
1361 // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
1362 // so we must create it specific for this instance
1363 GameObject * go = new GameObject;
1364 if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry, map,
1365 PHASEMASK_NORMAL, x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,GO_STATE_READY))
1367 sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry);
1368 sLog.outError("Cannot create gameobject template %u! BattleGround not created!", entry);
1369 delete go;
1370 return false;
1373 uint32 guid = go->GetGUIDLow();
1375 // without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata
1376 // 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
1377 GameObjectData& data = objmgr.NewGOData(guid);
1379 data.id = entry;
1380 data.mapid = GetMapId();
1381 data.posX = x;
1382 data.posY = y;
1383 data.posZ = z;
1384 data.orientation = o;
1385 data.rotation0 = rotation0;
1386 data.rotation1 = rotation1;
1387 data.rotation2 = rotation2;
1388 data.rotation3 = rotation3;
1389 data.spawntimesecs = respawnTime;
1390 data.spawnMask = 1;
1391 data.animprogress = 100;
1392 data.go_state = 1;
1394 // add to world, so it can be later looked up from HashMapHolder
1395 go->AddToWorld();
1396 m_BgObjects[type] = go->GetGUID();
1397 return true;
1400 //some doors aren't despawned so we cannot handle their closing in gameobject::update()
1401 //it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
1402 void BattleGround::DoorClose(uint32 type)
1404 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1405 if (obj)
1407 //if doors are open, close it
1408 if (obj->getLootState() == GO_ACTIVATED && obj->GetGoState() != GO_STATE_READY)
1410 //change state to allow door to be closed
1411 obj->SetLootState(GO_READY);
1412 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1415 else
1417 sLog.outError("BattleGround: Door object not found (cannot close doors)");
1421 void BattleGround::DoorOpen(uint32 type)
1423 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1424 if (obj)
1426 //change state to be sure they will be opened
1427 obj->SetLootState(GO_READY);
1428 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1430 else
1432 sLog.outError("BattleGround: Door object not found! - doors will be closed.");
1436 void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime)
1438 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
1439 if (!map)
1440 return;
1441 if (respawntime == 0)
1443 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1444 if (obj)
1446 //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again
1447 if (obj->getLootState() == GO_JUST_DEACTIVATED)
1448 obj->SetLootState(GO_READY);
1449 obj->SetRespawnTime(0);
1450 map->Add(obj);
1453 else
1455 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1456 if (obj)
1458 map->Add(obj);
1459 obj->SetRespawnTime(respawntime);
1460 obj->SetLootState(GO_JUST_DEACTIVATED);
1465 Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o, uint32 respawntime)
1467 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
1468 if (!map)
1469 return NULL;
1471 Creature* pCreature = new Creature;
1472 if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, PHASEMASK_NORMAL, entry, teamval))
1474 sLog.outError("Can't create creature entry: %u",entry);
1475 delete pCreature;
1476 return NULL;
1479 pCreature->Relocate(x, y, z, o);
1481 if (!pCreature->IsPositionValid())
1483 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());
1484 delete pCreature;
1485 return NULL;
1488 pCreature->AIM_Initialize();
1490 //pCreature->SetDungeonDifficulty(0);
1492 map->Add(pCreature);
1493 m_BgCreatures[type] = pCreature->GetGUID();
1495 return pCreature;
1498 void BattleGround::SpawnBGCreature(uint32 type, uint32 respawntime)
1500 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceId());
1501 if (!map)
1502 return false;
1504 if (respawntime == 0)
1506 Creature *obj = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1507 if (obj)
1509 //obj->Respawn(); // bugged
1510 obj->SetRespawnTime(0);
1511 objmgr.SaveCreatureRespawnTime(obj->GetGUIDLow(), GetInstanceID(), 0);
1512 map->Add(obj);
1515 else
1517 Creature *obj = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1518 if (obj)
1520 obj->setDeathState(DEAD);
1521 obj->SetRespawnTime(respawntime);
1522 map->Add(obj);
1527 bool BattleGround::DelCreature(uint32 type)
1529 if (!m_BgCreatures[type])
1530 return true;
1532 Creature *cr = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1533 if (!cr)
1535 sLog.outError("Can't find creature guid: %u",GUID_LOPART(m_BgCreatures[type]));
1536 return false;
1538 cr->AddObjectToRemoveList();
1539 m_BgCreatures[type] = 0;
1540 return true;
1543 bool BattleGround::DelObject(uint32 type)
1545 if (!m_BgObjects[type])
1546 return true;
1548 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1549 if (!obj)
1551 sLog.outError("Can't find gobject guid: %u",GUID_LOPART(m_BgObjects[type]));
1552 return false;
1554 obj->SetRespawnTime(0); // not save respawn time
1555 obj->Delete();
1556 m_BgObjects[type] = 0;
1557 return true;
1560 bool BattleGround::AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team)
1562 uint32 entry = 0;
1564 if (team == ALLIANCE)
1565 entry = 13116;
1566 else
1567 entry = 13117;
1569 Creature* pCreature = AddCreature(entry,type,team,x,y,z,o);
1570 if (!pCreature)
1572 sLog.outError("Can't create Spirit guide. BattleGround not created!");
1573 EndNow();
1574 return false;
1577 pCreature->setDeathState(DEAD);
1579 pCreature->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, pCreature->GetGUID());
1580 // aura
1581 pCreature->SetVisibleAura(0, SPELL_SPIRIT_HEAL_CHANNEL);
1582 //pCreature->SetUInt32Value(UNIT_FIELD_AURAFLAGS, 0x00000009);
1583 //pCreature->SetUInt32Value(UNIT_FIELD_AURALEVELS, 0x0000003C);
1584 //pCreature->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS, 0x000000FF);
1585 // casting visual effect
1586 pCreature->SetUInt32Value(UNIT_CHANNEL_SPELL, SPELL_SPIRIT_HEAL_CHANNEL);
1587 // correct cast speed
1588 pCreature->SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
1590 //pCreature->CastSpell(pCreature, SPELL_SPIRIT_HEAL_CHANNEL, true);
1592 return true;
1595 void BattleGround::SendMessageToAll(int32 entry, ChatMsg type, Player const* source)
1597 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source);
1598 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1599 BroadcastWorker(bg_do);
1602 void BattleGround::PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...)
1604 va_list ap;
1605 va_start(ap, source);
1607 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source, &ap);
1608 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1609 BroadcastWorker(bg_do);
1611 va_end(ap);
1614 void BattleGround::SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 arg1, int32 arg2)
1616 MaNGOS::BattleGround2ChatBuilder bg_builder(type, entry, source, arg1, arg2);
1617 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGround2ChatBuilder> bg_do(bg_builder);
1618 BroadcastWorker(bg_do);
1621 void BattleGround::EndNow()
1623 RemoveFromBGFreeSlotQueue();
1624 SetStatus(STATUS_WAIT_LEAVE);
1625 SetEndTime(0);
1629 important notice:
1630 buffs aren't spawned/despawned when players captures anything
1631 buffs are in their positions when battleground starts
1633 void BattleGround::HandleTriggerBuff(uint64 const& go_guid)
1635 GameObject *obj = HashMapHolder<GameObject>::Find(go_guid);
1636 if (!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned())
1637 return;
1639 //change buff type, when buff is used:
1640 int32 index = m_BgObjects.size() - 1;
1641 while (index >= 0 && m_BgObjects[index] != go_guid)
1642 index--;
1643 if (index < 0)
1645 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());
1646 return;
1649 //randomly select new buff
1650 uint8 buff = urand(0, 2);
1651 uint32 entry = obj->GetEntry();
1652 if (m_BuffChange && entry != Buff_Entries[buff])
1654 //despawn current buff
1655 SpawnBGObject(index, RESPAWN_ONE_DAY);
1656 //set index for new one
1657 for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex)
1658 if (entry == Buff_Entries[currBuffTypeIndex])
1660 index -= currBuffTypeIndex;
1661 index += buff;
1665 SpawnBGObject(index, BUFF_RESPAWN_TIME);
1668 void BattleGround::HandleKillPlayer( Player *player, Player *killer )
1670 //keep in mind that for arena this will have to be changed a bit
1672 // add +1 deaths
1673 UpdatePlayerScore(player, SCORE_DEATHS, 1);
1675 // add +1 kills to group and +1 killing_blows to killer
1676 if (killer)
1678 UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1);
1679 UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1);
1681 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1683 Player *plr = objmgr.GetPlayer(itr->first);
1685 if (!plr || plr == killer)
1686 continue;
1688 if (plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player))
1689 UpdatePlayerScore(plr, SCORE_HONORABLE_KILLS, 1);
1693 // to be able to remove insignia -- ONLY IN BattleGrounds
1694 if (!isArena())
1695 player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE );
1698 // return the player's team based on battlegroundplayer info
1699 // used in same faction arena matches mainly
1700 uint32 BattleGround::GetPlayerTeam(uint64 guid)
1702 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1703 if (itr!=m_Players.end())
1704 return itr->second.Team;
1705 return 0;
1708 uint32 BattleGround::GetOtherTeam(uint32 teamId)
1710 return (teamId) ? ((teamId == ALLIANCE) ? HORDE : ALLIANCE) : 0;
1713 bool BattleGround::IsPlayerInBattleGround(uint64 guid)
1715 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1716 if (itr != m_Players.end())
1717 return true;
1718 return false;
1721 void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player* plr)
1723 if (GetStatus() != STATUS_WAIT_LEAVE)
1724 return;
1726 WorldPacket data;
1727 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1729 BlockMovement(plr);
1731 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
1732 plr->GetSession()->SendPacket(&data);
1734 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType());
1735 plr->GetSession()->SendPacket(&data);
1738 uint32 BattleGround::GetAlivePlayersCountByTeam(uint32 Team) const
1740 int count = 0;
1741 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1743 if (itr->second.Team == Team)
1745 Player * pl = objmgr.GetPlayer(itr->first);
1746 if (pl && pl->isAlive())
1747 ++count;
1750 return count;
1753 void BattleGround::CheckArenaWinConditions()
1755 if (!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE))
1756 EndBattleGround(HORDE);
1757 else if (GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE))
1758 EndBattleGround(ALLIANCE);
1761 void BattleGround::SetBgRaid( uint32 TeamID, Group *bg_raid )
1763 Group* &old_raid = TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE];
1764 if(old_raid) old_raid->SetBattlegroundGroup(NULL);
1765 if(bg_raid) bg_raid->SetBattlegroundGroup(this);
1766 old_raid = bg_raid;
1769 WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard( Player* player )
1771 return objmgr.GetClosestGraveYard( player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam() );
1774 bool BattleGround::IsTeamScoreInRange(uint32 team, uint32 minScore, uint32 maxScore) const
1776 BattleGroundTeamId team_idx = GetTeamIndexByTeamId(team);
1777 return m_TeamScores[team_idx] >= minScore && m_TeamScores[team_idx] <= maxScore;