[7934] Implement ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
[getmangos.git] / src / game / BattleGround.cpp
blobfe7a00a5d80197601844f15a915fade2acd5b51b
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_PrematureCountDown = false;
182 m_PrematureCountDown = 0;
184 m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_2M;
185 m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_1M;
186 m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_30S;
187 m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE;
188 //we must set to some default existing values
189 m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_WS_START_TWO_MINUTES;
190 m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE;
191 m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE;
192 m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN;
195 BattleGround::~BattleGround()
197 // remove objects and creatures
198 // (this is done automatically in mapmanager update, when the instance is reset after the reset time)
199 int size = m_BgCreatures.size();
200 for(int i = 0; i < size; ++i)
202 DelCreature(i);
204 size = m_BgObjects.size();
205 for(int i = 0; i < size; ++i)
207 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();
229 void BattleGround::Update(uint32 diff)
231 if (!GetPlayersSize() && !GetReviveQueueSize())
232 //BG is empty
233 return;
235 // remove offline players from bg after 5 minutes
236 if (!m_OfflineQueue.empty())
238 BattleGroundPlayerMap::iterator itr = m_Players.find(*(m_OfflineQueue.begin()));
239 if (itr != m_Players.end())
241 if (itr->second.OfflineRemoveTime <= sWorld.GetGameTime())
243 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
244 m_OfflineQueue.pop_front(); // remove from offline queue
245 //do not use itr for anything, because it is erased in RemovePlayerAtLeave()
250 /*********************************************************/
251 /*** BATTLEGROUND RESSURECTION SYSTEM ***/
252 /*********************************************************/
254 //this should be handled by spell system
255 m_LastResurrectTime += diff;
256 if (m_LastResurrectTime >= RESURRECTION_INTERVAL)
258 if (GetReviveQueueSize())
260 for(std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
262 Creature *sh = NULL;
263 for(std::vector<uint64>::const_iterator itr2 = (itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
265 Player *plr = objmgr.GetPlayer(*itr2);
266 if (!plr)
267 continue;
269 if (!sh && plr->IsInWorld())
271 sh = plr->GetMap()->GetCreature(itr->first);
272 // only for visual effect
273 if (sh)
274 sh->CastSpell(sh, SPELL_SPIRIT_HEAL, true); // Spirit Heal, effect 117
277 plr->CastSpell(plr, SPELL_RESURRECTION_VISUAL, true); // Resurrection visual
278 m_ResurrectQueue.push_back(*itr2);
280 (itr->second).clear();
283 m_ReviveQueue.clear();
284 m_LastResurrectTime = 0;
286 else
287 // queue is clear and time passed, just update last resurrection time
288 m_LastResurrectTime = 0;
290 else if (m_LastResurrectTime > 500) // Resurrect players only half a second later, to see spirit heal effect on NPC
292 for(std::vector<uint64>::const_iterator itr = m_ResurrectQueue.begin(); itr != m_ResurrectQueue.end(); ++itr)
294 Player *plr = objmgr.GetPlayer(*itr);
295 if (!plr)
296 continue;
297 plr->ResurrectPlayer(1.0f);
298 plr->CastSpell(plr, SPELL_SPIRIT_HEAL_MANA, true);
299 ObjectAccessor::Instance().ConvertCorpseForPlayer(*itr);
301 m_ResurrectQueue.clear();
304 /*********************************************************/
305 /*** BATTLEGROUND BALLANCE SYSTEM ***/
306 /*********************************************************/
308 // if less then minimum players are in on one side, then start premature finish timer
309 if (GetStatus() == STATUS_IN_PROGRESS && !isArena() && sBattleGroundMgr.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE) < GetMinPlayersPerTeam()))
311 if (!m_PrematureCountDown)
313 m_PrematureCountDown = true;
314 m_PrematureCountDownTimer = sBattleGroundMgr.GetPrematureFinishTime();
316 else if (m_PrematureCountDownTimer < diff)
318 // time's up!
319 uint32 winner = 0;
320 if (GetPlayersCountByTeam(ALLIANCE) >= GetMinPlayersPerTeam())
321 winner = ALLIANCE;
322 else if (GetPlayersCountByTeam(HORDE) >= GetMinPlayersPerTeam())
323 winner = HORDE;
325 EndBattleGround(winner);
326 m_PrematureCountDown = false;
328 else
330 uint32 newtime = m_PrematureCountDownTimer - diff;
331 // announce every minute
332 if (newtime > (MINUTE * IN_MILISECONDS))
334 if (newtime / (MINUTE * IN_MILISECONDS) != m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS))
335 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS)));
337 else
339 //announce every 15 seconds
340 if (newtime / (15 * IN_MILISECONDS) != m_PrematureCountDownTimer / (15 * IN_MILISECONDS))
341 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / IN_MILISECONDS));
343 m_PrematureCountDownTimer = newtime;
346 else if (m_PrematureCountDown)
347 m_PrematureCountDown = false;
349 /*********************************************************/
350 /*** BATTLEGROUND STARTING SYSTEM ***/
351 /*********************************************************/
353 if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize())
355 ModifyStartDelayTime(diff);
357 if (!(m_Events & BG_STARTING_EVENT_1))
359 m_Events |= BG_STARTING_EVENT_1;
361 // setup here, only when at least one player has ported to the map
362 if (!SetupBattleGround())
364 EndNow();
365 return;
368 StartingEventCloseDoors();
369 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FIRST]);
370 //first start warning - 2 or 1 minute
371 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FIRST], CHAT_MSG_BG_SYSTEM_NEUTRAL);
373 // After 1 minute or 30 seconds, warning is signalled
374 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_SECOND] && !(m_Events & BG_STARTING_EVENT_2))
376 m_Events |= BG_STARTING_EVENT_2;
377 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_SECOND], CHAT_MSG_BG_SYSTEM_NEUTRAL);
379 // After 30 or 15 seconds, warning is signalled
380 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_THIRD] && !(m_Events & BG_STARTING_EVENT_3))
382 m_Events |= BG_STARTING_EVENT_3;
383 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_THIRD], CHAT_MSG_BG_SYSTEM_NEUTRAL);
385 // delay expired (atfer 2 or 1 minute)
386 else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4))
388 m_Events |= BG_STARTING_EVENT_4;
390 StartingEventOpenDoors();
392 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FOURTH], CHAT_MSG_BG_SYSTEM_NEUTRAL);
393 SetStatus(STATUS_IN_PROGRESS);
394 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FOURTH]);
396 //remove preparation
397 if (isArena())
399 //TODO : add arena sound PlaySoundToAll(SOUND_ARENA_START);
401 for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
402 if (Player *plr = objmgr.GetPlayer(itr->first))
403 plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION);
405 CheckArenaWinConditions();
407 else
410 PlaySoundToAll(SOUND_BG_START);
412 for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
413 if (Player* plr = objmgr.GetPlayer(itr->first))
414 plr->RemoveAurasDueToSpell(SPELL_PREPARATION);
415 //Announce BG starting
416 if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE))
418 sWorld.SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD, GetName(), GetMinLevel(), GetMaxLevel());
424 /*********************************************************/
425 /*** BATTLEGROUND ENDING SYSTEM ***/
426 /*********************************************************/
428 if (GetStatus() == STATUS_WAIT_LEAVE)
430 // remove all players from battleground after 2 minutes
431 m_EndTime -= diff;
432 if (m_EndTime <= 0)
434 m_EndTime = 0;
435 BattleGroundPlayerMap::iterator itr, next;
436 for(itr = m_Players.begin(); itr != m_Players.end(); itr = next)
438 next = itr;
439 ++next;
440 //itr is erased here!
441 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
442 // do not change any battleground's private variables
447 //update start time
448 m_StartTime += diff;
451 void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O)
453 uint8 idx = GetTeamIndexByTeamId(TeamID);
454 m_TeamStartLocX[idx] = X;
455 m_TeamStartLocY[idx] = Y;
456 m_TeamStartLocZ[idx] = Z;
457 m_TeamStartLocO[idx] = O;
460 void BattleGround::SendPacketToAll(WorldPacket *packet)
462 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
464 Player *plr = objmgr.GetPlayer(itr->first);
465 if (plr)
466 plr->GetSession()->SendPacket(packet);
467 else
468 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
472 void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender, bool self)
474 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
476 Player *plr = objmgr.GetPlayer(itr->first);
478 if (!plr)
480 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
481 continue;
484 if (!self && sender == plr)
485 continue;
487 uint32 team = itr->second.Team;
488 if(!team) team = plr->GetTeam();
490 if (team == TeamID)
491 plr->GetSession()->SendPacket(packet);
495 void BattleGround::PlaySoundToAll(uint32 SoundID)
497 WorldPacket data;
498 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
499 SendPacketToAll(&data);
502 void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID)
504 WorldPacket data;
506 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
508 Player *plr = objmgr.GetPlayer(itr->first);
510 if (!plr)
512 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
513 continue;
516 uint32 team = itr->second.Team;
517 if(!team) team = plr->GetTeam();
519 if (team == TeamID)
521 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
522 plr->GetSession()->SendPacket(&data);
527 void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID)
529 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
531 Player *plr = objmgr.GetPlayer(itr->first);
533 if (!plr)
535 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
536 continue;
539 uint32 team = itr->second.Team;
540 if(!team) team = plr->GetTeam();
542 if (team == TeamID)
543 plr->CastSpell(plr, SpellID, true);
547 void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID)
549 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
551 Player *plr = objmgr.GetPlayer(itr->first);
553 if (!plr)
555 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
556 continue;
559 uint32 team = itr->second.Team;
560 if(!team) team = plr->GetTeam();
562 if (team == TeamID)
563 UpdatePlayerScore(plr, SCORE_BONUS_HONOR, Honor);
567 void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID)
569 FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
571 if (!factionEntry)
572 return;
574 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
576 Player *plr = objmgr.GetPlayer(itr->first);
578 if (!plr)
580 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
581 continue;
584 uint32 team = itr->second.Team;
585 if(!team) team = plr->GetTeam();
587 if (team == TeamID)
588 plr->GetReputationMgr().ModifyReputation(factionEntry, Reputation);
592 void BattleGround::UpdateWorldState(uint32 Field, uint32 Value)
594 WorldPacket data;
595 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
596 SendPacketToAll(&data);
599 void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source)
601 WorldPacket data;
602 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
603 Source->GetSession()->SendPacket(&data);
606 void BattleGround::EndBattleGround(uint32 winner)
608 this->RemoveFromBGFreeSlotQueue();
610 ArenaTeam * winner_arena_team = NULL;
611 ArenaTeam * loser_arena_team = NULL;
612 uint32 loser_rating = 0;
613 uint32 winner_rating = 0;
614 WorldPacket data;
615 int32 winmsg_id = 0;
617 if (winner == ALLIANCE)
619 winmsg_id = isBattleGround() ? LANG_BG_A_WINS : LANG_ARENA_GOLD_WINS;
621 PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound
623 SetWinner(WINNER_ALLIANCE);
625 else if (winner == HORDE)
627 winmsg_id = isBattleGround() ? LANG_BG_H_WINS : LANG_ARENA_GREEN_WINS;
629 PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound
631 SetWinner(WINNER_HORDE);
633 else
635 SetWinner(3);
638 SetStatus(STATUS_WAIT_LEAVE);
639 //we must set it this way, because end time is sent in packet!
640 m_EndTime = TIME_TO_AUTOREMOVE;
642 // arena rating calculation
643 if (isArena() && isRated())
645 winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(winner));
646 loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(winner)));
647 if (winner_arena_team && loser_arena_team)
649 loser_rating = loser_arena_team->GetStats().rating;
650 winner_rating = winner_arena_team->GetStats().rating;
651 int32 winner_change = winner_arena_team->WonAgainst(loser_rating);
652 int32 loser_change = loser_arena_team->LostAgainst(winner_rating);
653 sLog.outDebug("--- Winner rating: %u, Loser rating: %u, Winner change: %u, Losser change: %u ---", winner_rating, loser_rating, winner_change, loser_change);
654 SetArenaTeamRatingChangeForTeam(winner, winner_change);
655 SetArenaTeamRatingChangeForTeam(GetOtherTeam(winner), loser_change);
657 else
659 SetArenaTeamRatingChangeForTeam(ALLIANCE, 0);
660 SetArenaTeamRatingChangeForTeam(HORDE, 0);
664 for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
666 Player *plr = objmgr.GetPlayer(itr->first);
667 uint32 team = itr->second.Team;
669 if (!plr)
671 //if rated arena match - make member lost!
672 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
674 if (team == winner)
675 winner_arena_team->OfflineMemberLost(itr->first, loser_rating);
676 else
677 loser_arena_team->OfflineMemberLost(itr->first, winner_rating);
679 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
680 continue;
683 // should remove spirit of redemption
684 if (plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
685 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
687 if (!plr->isAlive())
689 plr->ResurrectPlayer(1.0f);
690 plr->SpawnCorpseBones();
693 //this line is obsolete - team is set ALWAYS
694 //if(!team) team = plr->GetTeam();
696 // per player calculation
697 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
699 if (team == winner)
701 // update achievement BEFORE personal rating update
702 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, winner_arena_team->GetMember(plr->GetGUID())->personal_rating);
704 winner_arena_team->MemberWon(plr,loser_rating);
706 else
708 loser_arena_team->MemberLost(plr,winner_rating);
710 // Arena lost => reset the win_rated_arena having the "no_loose" condition
711 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE);
715 if (team == winner)
717 RewardMark(plr,ITEM_WINNER_COUNT);
718 RewardQuestComplete(plr);
720 else
721 RewardMark(plr,ITEM_LOSER_COUNT);
723 plr->CombatStopWithPets(true);
725 BlockMovement(plr);
727 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
728 plr->GetSession()->SendPacket(&data);
730 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
731 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType());
732 plr->GetSession()->SendPacket(&data);
733 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1);
736 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
738 // update arena points only after increasing the player's match count!
739 //obsolete: winner_arena_team->UpdateArenaPointsHelper();
740 //obsolete: loser_arena_team->UpdateArenaPointsHelper();
741 // save the stat changes
742 winner_arena_team->SaveToDB();
743 loser_arena_team->SaveToDB();
744 // send updated arena team stats to players
745 // this way all arena team members will get notified, not only the ones who participated in this match
746 winner_arena_team->NotifyStatsChanged();
747 loser_arena_team->NotifyStatsChanged();
750 if (winmsg_id)
751 SendMessageToAll(winmsg_id, CHAT_MSG_BG_SYSTEM_NEUTRAL);
754 uint32 BattleGround::GetBonusHonorFromKill(uint32 kills) const
756 //variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill)
757 return MaNGOS::Honor::hk_honor_at_level(GetMaxLevel(), kills);
760 uint32 BattleGround::GetBattlemasterEntry() const
762 switch(GetTypeID())
764 case BATTLEGROUND_AV: return 15972;
765 case BATTLEGROUND_WS: return 14623;
766 case BATTLEGROUND_AB: return 14879;
767 case BATTLEGROUND_EY: return 22516;
768 case BATTLEGROUND_NA: return 20200;
769 default: return 0;
773 void BattleGround::RewardMark(Player *plr,uint32 count)
775 BattleGroundMarks mark;
776 bool IsSpell;
777 switch(GetTypeID())
779 case BATTLEGROUND_AV:
780 IsSpell = true;
781 if (count == ITEM_WINNER_COUNT)
782 mark = SPELL_AV_MARK_WINNER;
783 else
784 mark = SPELL_AV_MARK_LOSER;
785 break;
786 case BATTLEGROUND_WS:
787 IsSpell = true;
788 if (count == ITEM_WINNER_COUNT)
789 mark = SPELL_WS_MARK_WINNER;
790 else
791 mark = SPELL_WS_MARK_LOSER;
792 break;
793 case BATTLEGROUND_AB:
794 IsSpell = true;
795 if (count == ITEM_WINNER_COUNT)
796 mark = SPELL_AB_MARK_WINNER;
797 else
798 mark = SPELL_AB_MARK_LOSER;
799 break;
800 case BATTLEGROUND_EY:
801 IsSpell = false;
802 mark = ITEM_EY_MARK_OF_HONOR;
803 break;
804 default:
805 return;
808 if (IsSpell)
809 RewardSpellCast(plr,mark);
810 else
811 RewardItem(plr,mark,count);
814 void BattleGround::RewardSpellCast(Player *plr, uint32 spell_id)
816 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
817 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
818 return;
820 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
821 if(!spellInfo)
823 sLog.outError("Battleground reward casting spell %u not exist.",spell_id);
824 return;
827 plr->CastSpell(plr, spellInfo, true);
830 void BattleGround::RewardItem(Player *plr, uint32 item_id, uint32 count)
832 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
833 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
834 return;
836 ItemPosCountVec dest;
837 uint32 no_space_count = 0;
838 uint8 msg = plr->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item_id, count, &no_space_count );
840 if( msg == EQUIP_ERR_ITEM_NOT_FOUND)
842 sLog.outErrorDb("Battleground reward item (Entry %u) not exist in `item_template`.",item_id);
843 return;
846 if( msg != EQUIP_ERR_OK ) // convert to possible store amount
847 count -= no_space_count;
849 if( count != 0 && !dest.empty()) // can add some
850 if (Item* item = plr->StoreNewItem( dest, item_id, true, 0))
851 plr->SendNewItem(item,count,false,true);
853 if (no_space_count > 0)
854 SendRewardMarkByMail(plr,item_id,no_space_count);
857 void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count)
859 uint32 bmEntry = GetBattlemasterEntry();
860 if (!bmEntry)
861 return;
863 ItemPrototype const* markProto = objmgr.GetItemPrototype(mark);
864 if (!markProto)
865 return;
867 if (Item* markItem = Item::CreateItem(mark,count,plr))
869 // save new item before send
870 markItem->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
872 // item
873 MailItemsInfo mi;
874 mi.AddItem(markItem->GetGUIDLow(), markItem->GetEntry(), markItem);
876 // subject: item name
877 std::string subject = markProto->Name1;
878 int loc_idx = plr->GetSession()->GetSessionDbLocaleIndex();
879 if (loc_idx >= 0 )
880 if (ItemLocale const *il = objmgr.GetItemLocale(markProto->ItemId))
881 if (il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty())
882 subject = il->Name[loc_idx];
884 // text
885 std::string textFormat = plr->GetSession()->GetMangosString(LANG_BG_MARK_BY_MAIL);
886 char textBuf[300];
887 snprintf(textBuf,300,textFormat.c_str(),GetName(),GetName());
888 uint32 itemTextId = objmgr.CreateItemText( textBuf );
890 WorldSession::SendMailTo(plr, MAIL_CREATURE, MAIL_STATIONERY_NORMAL, bmEntry, plr->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE);
894 void BattleGround::RewardQuestComplete(Player *plr)
896 uint32 quest;
897 switch(GetTypeID())
899 case BATTLEGROUND_AV:
900 quest = SPELL_AV_QUEST_REWARD;
901 break;
902 case BATTLEGROUND_WS:
903 quest = SPELL_WS_QUEST_REWARD;
904 break;
905 case BATTLEGROUND_AB:
906 quest = SPELL_AB_QUEST_REWARD;
907 break;
908 case BATTLEGROUND_EY:
909 quest = SPELL_EY_QUEST_REWARD;
910 break;
911 default:
912 return;
915 RewardSpellCast(plr, quest);
918 void BattleGround::BlockMovement(Player *plr)
920 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()
923 void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket)
925 uint32 team = GetPlayerTeam(guid);
926 bool participant = false;
927 // Remove from lists/maps
928 BattleGroundPlayerMap::iterator itr = m_Players.find(guid);
929 if (itr != m_Players.end())
931 UpdatePlayersCountByTeam(team, true); // -1 player
932 m_Players.erase(itr);
933 // check if the player was a participant of the match, or only entered through gm command (goname)
934 participant = true;
937 std::map<uint64, BattleGroundScore*>::iterator itr2 = m_PlayerScores.find(guid);
938 if (itr2 != m_PlayerScores.end())
940 delete itr2->second; // delete player's score
941 m_PlayerScores.erase(itr2);
944 RemovePlayerFromResurrectQueue(guid);
946 Player *plr = objmgr.GetPlayer(guid);
948 // should remove spirit of redemption
949 if (plr && plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
950 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
952 if(plr && !plr->isAlive()) // resurrect on exit
954 plr->ResurrectPlayer(1.0f);
955 plr->SpawnCorpseBones();
958 RemovePlayer(plr, guid); // BG subclass specific code
960 if(participant) // if the player was a match participant, remove auras, calc rating, update queue
962 BattleGroundTypeId bgTypeId = GetTypeID();
963 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
964 if (plr)
966 plr->ClearAfkReports();
968 if(!team) team = plr->GetTeam();
970 // if arena, remove the specific arena auras
971 if (isArena())
973 plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out
974 bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing)
976 // unsummon current and summon old pet if there was one and there isn't a current pet
977 plr->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT);
978 plr->ResummonPetTemporaryUnSummonedIfAny();
980 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
982 //left a rated match while the encounter was in progress, consider as loser
983 ArenaTeam * winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
984 ArenaTeam * loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
985 if (winner_arena_team && loser_arena_team)
986 loser_arena_team->MemberLost(plr,winner_arena_team->GetRating());
989 if (SendPacket)
991 WorldPacket data;
992 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0, 0);
993 plr->GetSession()->SendPacket(&data);
996 // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg
997 plr->RemoveBattleGroundQueueId(bgQueueTypeId);
999 else
1000 // removing offline participant
1002 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
1004 //left a rated match while the encounter was in progress, consider as loser
1005 ArenaTeam * others_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
1006 ArenaTeam * players_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
1007 if (others_arena_team && players_arena_team)
1008 players_arena_team->OfflineMemberLost(guid, others_arena_team->GetRating());
1012 // remove from raid group if player is member
1013 if (Group *group = GetBgRaid(team))
1015 if( !group->RemoveMember(guid, 0) ) // group was disbanded
1017 SetBgRaid(team, NULL);
1018 delete group;
1021 DecreaseInvitedCount(team);
1022 //we should update battleground queue, but only if bg isn't ending
1023 if (isBattleGround() && GetStatus() < STATUS_WAIT_LEAVE)
1024 sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, GetQueueId());
1025 // Let others know
1026 WorldPacket data;
1027 sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, guid);
1028 SendPacketToTeam(team, &data, plr, false);
1031 if (plr)
1033 // Do next only if found in battleground
1034 plr->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG.
1035 // reset destination bg team
1036 plr->SetBGTeam(0);
1038 if (Transport)
1039 plr->TeleportTo(plr->GetBattleGroundEntryPoint());
1041 sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName());
1044 if (!GetPlayersSize() && !GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
1046 // if no players left AND no invitees left, set this bg to delete in next update
1047 // direct deletion could cause crashes
1048 m_SetDeleteThis = true;
1049 // return to prevent addition to freeslotqueue
1050 return;
1053 // a player exited the battleground, so there are free slots. add to queue
1054 this->AddToBGFreeSlotQueue();
1057 // this method is called when no players remains in battleground
1058 void BattleGround::Reset()
1060 SetQueueId(QUEUE_ID_MAX_LEVEL_19);
1061 SetWinner(WINNER_NONE);
1062 SetStatus(STATUS_WAIT_QUEUE);
1063 SetStartTime(0);
1064 SetEndTime(0);
1065 SetLastResurrectTime(0);
1066 SetArenaType(0);
1067 SetRated(false);
1069 m_Events = 0;
1071 if (m_InvitedAlliance > 0 || m_InvitedHorde > 0)
1072 sLog.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde);
1074 m_InvitedAlliance = 0;
1075 m_InvitedHorde = 0;
1076 m_InBGFreeSlotQueue = false;
1078 m_Players.clear();
1079 m_PlayerScores.clear();
1082 void BattleGround::StartBattleGround()
1084 ///this method should spawn spirit guides and so on
1085 SetStartTime(0);
1087 SetLastResurrectTime(0);
1090 void BattleGround::AddPlayer(Player *plr)
1092 // score struct must be created in inherited class
1094 uint64 guid = plr->GetGUID();
1095 uint32 team = plr->GetBGTeam();
1097 BattleGroundPlayer bp;
1098 bp.OfflineRemoveTime = 0;
1099 bp.Team = team;
1101 // Add to list/maps
1102 m_Players[guid] = bp;
1104 UpdatePlayersCountByTeam(team, false); // +1 player
1106 WorldPacket data;
1107 sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr);
1108 SendPacketToTeam(team, &data, plr, false);
1110 // add arena specific auras
1111 if (isArena())
1113 plr->RemoveArenaSpellCooldowns();
1114 plr->RemoveArenaAuras();
1115 plr->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT);
1116 if(team == ALLIANCE) // gold
1118 if (plr->GetTeam() == HORDE)
1119 plr->CastSpell(plr, SPELL_HORDE_GOLD_FLAG,true);
1120 else
1121 plr->CastSpell(plr, SPELL_ALLIANCE_GOLD_FLAG,true);
1123 else // green
1125 if (plr->GetTeam() == HORDE)
1126 plr->CastSpell(plr, SPELL_HORDE_GREEN_FLAG,true);
1127 else
1128 plr->CastSpell(plr, SPELL_ALLIANCE_GREEN_FLAG,true);
1131 plr->DestroyConjuredItems(true);
1132 plr->UnsummonPetTemporaryIfAny();
1134 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1136 plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true);
1138 plr->SetHealth(plr->GetMaxHealth());
1139 plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA));
1142 else
1144 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1145 plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells.
1148 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1149 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1151 // setup BG group membership
1152 PlayerAddedToBGCheckIfBGIsRunning(plr);
1153 AddOrSetPlayerToCorrectBgGroup(plr, guid, team);
1155 // Log
1156 sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName());
1159 /* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */
1160 void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team)
1162 Group* group = GetBgRaid(team);
1163 if(!group) // first player joined
1165 group = new Group;
1166 SetBgRaid(team, group);
1167 group->Create(plr_guid, plr->GetName());
1169 else // raid already exist
1171 if (group->IsMember(plr_guid))
1173 uint8 subgroup = group->GetMemberGroup(plr_guid);
1174 plr->SetBattleGroundRaid(group, subgroup);
1176 else
1178 group->AddMember(plr_guid, plr->GetName());
1179 if (Group* originalGroup = plr->GetOriginalGroup())
1180 if (originalGroup->IsLeader(plr_guid))
1181 group->ChangeLeader(plr_guid);
1186 // This method should be called when player logs into running battleground
1187 void BattleGround::EventPlayerLoggedIn(Player* player, uint64 plr_guid)
1189 // player is correct pointer
1190 for(std::deque<uint64>::iterator itr = m_OfflineQueue.begin(); itr != m_OfflineQueue.end(); ++itr)
1192 if (*itr == plr_guid)
1194 m_OfflineQueue.erase(itr);
1195 break;
1198 m_Players[plr_guid].OfflineRemoveTime = 0;
1199 PlayerAddedToBGCheckIfBGIsRunning(player);
1200 // if battleground is starting, then add preparation aura
1201 // we don't have to do that, because preparation aura isn't removed when player logs out
1204 // This method should be called when player logs out from running battleground
1205 void BattleGround::EventPlayerLoggedOut(Player* player)
1207 // player is correct pointer, it is checked in WorldSession::LogoutPlayer()
1208 m_OfflineQueue.push_back(player->GetGUID());
1209 m_Players[player->GetGUID()].OfflineRemoveTime = sWorld.GetGameTime() + MAX_OFFLINE_TIME;
1210 if (GetStatus() == STATUS_IN_PROGRESS)
1212 if (isBattleGround())
1213 EventPlayerDroppedFlag(player);
1214 else
1216 //1 player is logging out, if it is the last, then end arena!
1217 if (GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam())))
1218 EndBattleGround(GetOtherTeam(player->GetTeam()));
1223 /* This method should be called only once ... it adds pointer to queue */
1224 void BattleGround::AddToBGFreeSlotQueue()
1226 // make sure to add only once
1227 if (!m_InBGFreeSlotQueue && isBattleGround())
1229 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this);
1230 m_InBGFreeSlotQueue = true;
1234 /* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/
1235 void BattleGround::RemoveFromBGFreeSlotQueue()
1237 // set to be able to re-add if needed
1238 m_InBGFreeSlotQueue = false;
1239 // uncomment this code when battlegrounds will work like instances
1240 for (BGFreeSlotQueueType::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr)
1242 if ((*itr)->GetInstanceID() == m_InstanceID)
1244 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr);
1245 return;
1250 // get the number of free slots for team
1251 // returns the number how many players can join battleground to MaxPlayersPerTeam
1252 uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const
1254 //return free slot count to MaxPlayerPerTeam
1255 if (GetStatus() == STATUS_WAIT_JOIN || GetStatus() == STATUS_IN_PROGRESS)
1256 return (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0;
1258 return 0;
1261 bool BattleGround::HasFreeSlots() const
1263 return GetPlayersSize() < GetMaxPlayers();
1266 void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value)
1268 //this procedure is called from virtual function implemented in bg subclass
1269 std::map<uint64, BattleGroundScore*>::const_iterator itr = m_PlayerScores.find(Source->GetGUID());
1271 if(itr == m_PlayerScores.end()) // player not found...
1272 return;
1274 switch(type)
1276 case SCORE_KILLING_BLOWS: // Killing blows
1277 itr->second->KillingBlows += value;
1278 break;
1279 case SCORE_DEATHS: // Deaths
1280 itr->second->Deaths += value;
1281 break;
1282 case SCORE_HONORABLE_KILLS: // Honorable kills
1283 itr->second->HonorableKills += value;
1284 break;
1285 case SCORE_BONUS_HONOR: // Honor bonus
1286 // do not add honor in arenas
1287 if (isBattleGround())
1289 // reward honor instantly
1290 if (Source->RewardHonor(NULL, 1, value))
1291 itr->second->BonusHonor += value;
1293 break;
1294 //used only in EY, but in MSG_PVP_LOG_DATA opcode
1295 case SCORE_DAMAGE_DONE: // Damage Done
1296 itr->second->DamageDone += value;
1297 break;
1298 case SCORE_HEALING_DONE: // Healing Done
1299 itr->second->HealingDone += value;
1300 break;
1301 default:
1302 sLog.outError("BattleGround: Unknown player score type %u", type);
1303 break;
1307 void BattleGround::AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid)
1309 m_ReviveQueue[npc_guid].push_back(player_guid);
1311 Player *plr = objmgr.GetPlayer(player_guid);
1312 if (!plr)
1313 return;
1315 plr->CastSpell(plr, SPELL_WAITING_FOR_RESURRECT, true);
1316 SpellEntry const *spellInfo = sSpellStore.LookupEntry( SPELL_WAITING_FOR_RESURRECT );
1317 if (spellInfo)
1319 Aura *Aur = CreateAura(spellInfo, 0, NULL, plr);
1320 plr->AddAura(Aur);
1324 void BattleGround::RemovePlayerFromResurrectQueue(uint64 player_guid)
1326 for(std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
1328 for(std::vector<uint64>::iterator itr2 =(itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
1330 if (*itr2 == player_guid)
1332 (itr->second).erase(itr2);
1334 Player *plr = objmgr.GetPlayer(player_guid);
1335 if (!plr)
1336 return;
1338 plr->RemoveAurasDueToSpell(SPELL_WAITING_FOR_RESURRECT);
1340 return;
1346 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)
1348 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
1349 if (!map)
1350 return false;
1352 // must be created this way, adding to godatamap would add it to the base map of the instance
1353 // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
1354 // so we must create it specific for this instance
1355 GameObject * go = new GameObject;
1356 if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry, map,
1357 PHASEMASK_NORMAL, x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,GO_STATE_READY))
1359 sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry);
1360 sLog.outError("Cannot create gameobject template %u! BattleGround not created!", entry);
1361 delete go;
1362 return false;
1365 uint32 guid = go->GetGUIDLow();
1367 // without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata
1368 // 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
1369 GameObjectData& data = objmgr.NewGOData(guid);
1371 data.id = entry;
1372 data.mapid = GetMapId();
1373 data.posX = x;
1374 data.posY = y;
1375 data.posZ = z;
1376 data.orientation = o;
1377 data.rotation0 = rotation0;
1378 data.rotation1 = rotation1;
1379 data.rotation2 = rotation2;
1380 data.rotation3 = rotation3;
1381 data.spawntimesecs = respawnTime;
1382 data.spawnMask = 1;
1383 data.animprogress = 100;
1384 data.go_state = 1;
1386 // add to world, so it can be later looked up from HashMapHolder
1387 go->AddToWorld();
1388 m_BgObjects[type] = go->GetGUID();
1389 return true;
1392 //some doors aren't despawned so we cannot handle their closing in gameobject::update()
1393 //it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
1394 void BattleGround::DoorClose(uint32 type)
1396 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1397 if (obj)
1399 //if doors are open, close it
1400 if (obj->getLootState() == GO_ACTIVATED && obj->GetGoState() != GO_STATE_READY)
1402 //change state to allow door to be closed
1403 obj->SetLootState(GO_READY);
1404 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1407 else
1409 sLog.outError("BattleGround: Door object not found (cannot close doors)");
1413 void BattleGround::DoorOpen(uint32 type)
1415 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1416 if (obj)
1418 //change state to be sure they will be opened
1419 obj->SetLootState(GO_READY);
1420 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1422 else
1424 sLog.outError("BattleGround: Door object not found! - doors will be closed.");
1428 void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime)
1430 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
1431 if (!map)
1432 return;
1433 if (respawntime == 0)
1435 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1436 if (obj)
1438 //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again
1439 if (obj->getLootState() == GO_JUST_DEACTIVATED)
1440 obj->SetLootState(GO_READY);
1441 obj->SetRespawnTime(0);
1442 map->Add(obj);
1445 else
1447 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1448 if (obj)
1450 map->Add(obj);
1451 obj->SetRespawnTime(respawntime);
1452 obj->SetLootState(GO_JUST_DEACTIVATED);
1457 Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o, uint32 respawntime)
1459 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
1460 if (!map)
1461 return NULL;
1463 Creature* pCreature = new Creature;
1464 if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, PHASEMASK_NORMAL, entry, teamval))
1466 sLog.outError("Can't create creature entry: %u",entry);
1467 delete pCreature;
1468 return NULL;
1471 pCreature->Relocate(x, y, z, o);
1473 if (!pCreature->IsPositionValid())
1475 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());
1476 delete pCreature;
1477 return NULL;
1480 pCreature->AIM_Initialize();
1482 //pCreature->SetDungeonDifficulty(0);
1484 map->Add(pCreature);
1485 m_BgCreatures[type] = pCreature->GetGUID();
1487 return pCreature;
1490 void BattleGround::SpawnBGCreature(uint32 type, uint32 respawntime)
1492 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceId());
1493 if (!map)
1494 return false;
1496 if (respawntime == 0)
1498 Creature *obj = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1499 if (obj)
1501 //obj->Respawn(); // bugged
1502 obj->SetRespawnTime(0);
1503 objmgr.SaveCreatureRespawnTime(obj->GetGUIDLow(), GetInstanceID(), 0);
1504 map->Add(obj);
1507 else
1509 Creature *obj = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1510 if (obj)
1512 obj->setDeathState(DEAD);
1513 obj->SetRespawnTime(respawntime);
1514 map->Add(obj);
1519 bool BattleGround::DelCreature(uint32 type)
1521 if (!m_BgCreatures[type])
1522 return true;
1524 Creature *cr = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1525 if (!cr)
1527 sLog.outError("Can't find creature guid: %u",GUID_LOPART(m_BgCreatures[type]));
1528 return false;
1530 cr->CleanupsBeforeDelete();
1531 cr->AddObjectToRemoveList();
1532 m_BgCreatures[type] = 0;
1533 return true;
1536 bool BattleGround::DelObject(uint32 type)
1538 if (!m_BgObjects[type])
1539 return true;
1541 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1542 if (!obj)
1544 sLog.outError("Can't find gobject guid: %u",GUID_LOPART(m_BgObjects[type]));
1545 return false;
1547 obj->SetRespawnTime(0); // not save respawn time
1548 obj->Delete();
1549 m_BgObjects[type] = 0;
1550 return true;
1553 bool BattleGround::AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team)
1555 uint32 entry = 0;
1557 if (team == ALLIANCE)
1558 entry = 13116;
1559 else
1560 entry = 13117;
1562 Creature* pCreature = AddCreature(entry,type,team,x,y,z,o);
1563 if (!pCreature)
1565 sLog.outError("Can't create Spirit guide. BattleGround not created!");
1566 EndNow();
1567 return false;
1570 pCreature->setDeathState(DEAD);
1572 pCreature->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, pCreature->GetGUID());
1573 // aura
1574 pCreature->SetVisibleAura(0, SPELL_SPIRIT_HEAL_CHANNEL);
1575 //pCreature->SetUInt32Value(UNIT_FIELD_AURAFLAGS, 0x00000009);
1576 //pCreature->SetUInt32Value(UNIT_FIELD_AURALEVELS, 0x0000003C);
1577 //pCreature->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS, 0x000000FF);
1578 // casting visual effect
1579 pCreature->SetUInt32Value(UNIT_CHANNEL_SPELL, SPELL_SPIRIT_HEAL_CHANNEL);
1580 // correct cast speed
1581 pCreature->SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
1583 //pCreature->CastSpell(pCreature, SPELL_SPIRIT_HEAL_CHANNEL, true);
1585 return true;
1588 void BattleGround::SendMessageToAll(int32 entry, ChatMsg type, Player const* source)
1590 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source);
1591 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1592 BroadcastWorker(bg_do);
1595 void BattleGround::PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...)
1597 va_list ap;
1598 va_start(ap, source);
1600 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source, &ap);
1601 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1602 BroadcastWorker(bg_do);
1604 va_end(ap);
1607 void BattleGround::SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 arg1, int32 arg2)
1609 MaNGOS::BattleGround2ChatBuilder bg_builder(type, entry, source, arg1, arg2);
1610 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGround2ChatBuilder> bg_do(bg_builder);
1611 BroadcastWorker(bg_do);
1614 void BattleGround::EndNow()
1616 RemoveFromBGFreeSlotQueue();
1617 SetStatus(STATUS_WAIT_LEAVE);
1618 SetEndTime(0);
1622 important notice:
1623 buffs aren't spawned/despawned when players captures anything
1624 buffs are in their positions when battleground starts
1626 void BattleGround::HandleTriggerBuff(uint64 const& go_guid)
1628 GameObject *obj = HashMapHolder<GameObject>::Find(go_guid);
1629 if (!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned())
1630 return;
1632 //change buff type, when buff is used:
1633 int32 index = m_BgObjects.size() - 1;
1634 while (index >= 0 && m_BgObjects[index] != go_guid)
1635 index--;
1636 if (index < 0)
1638 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());
1639 return;
1642 //randomly select new buff
1643 uint8 buff = urand(0, 2);
1644 uint32 entry = obj->GetEntry();
1645 if (m_BuffChange && entry != Buff_Entries[buff])
1647 //despawn current buff
1648 SpawnBGObject(index, RESPAWN_ONE_DAY);
1649 //set index for new one
1650 for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex)
1651 if (entry == Buff_Entries[currBuffTypeIndex])
1653 index -= currBuffTypeIndex;
1654 index += buff;
1658 SpawnBGObject(index, BUFF_RESPAWN_TIME);
1661 void BattleGround::HandleKillPlayer( Player *player, Player *killer )
1663 //keep in mind that for arena this will have to be changed a bit
1665 // add +1 deaths
1666 UpdatePlayerScore(player, SCORE_DEATHS, 1);
1668 // add +1 kills to group and +1 killing_blows to killer
1669 if (killer)
1671 UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1);
1672 UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1);
1674 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1676 Player *plr = objmgr.GetPlayer(itr->first);
1678 if (!plr || plr == killer)
1679 continue;
1681 if (plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player))
1682 UpdatePlayerScore(plr, SCORE_HONORABLE_KILLS, 1);
1686 // to be able to remove insignia -- ONLY IN BattleGrounds
1687 if (!isArena())
1688 player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE );
1691 // return the player's team based on battlegroundplayer info
1692 // used in same faction arena matches mainly
1693 uint32 BattleGround::GetPlayerTeam(uint64 guid)
1695 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1696 if (itr!=m_Players.end())
1697 return itr->second.Team;
1698 return 0;
1701 uint32 BattleGround::GetOtherTeam(uint32 teamId)
1703 return (teamId) ? ((teamId == ALLIANCE) ? HORDE : ALLIANCE) : 0;
1706 bool BattleGround::IsPlayerInBattleGround(uint64 guid)
1708 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1709 if (itr != m_Players.end())
1710 return true;
1711 return false;
1714 void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player* plr)
1716 if (GetStatus() != STATUS_WAIT_LEAVE)
1717 return;
1719 WorldPacket data;
1720 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1722 BlockMovement(plr);
1724 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
1725 plr->GetSession()->SendPacket(&data);
1727 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType());
1728 plr->GetSession()->SendPacket(&data);
1731 uint32 BattleGround::GetAlivePlayersCountByTeam(uint32 Team) const
1733 int count = 0;
1734 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1736 if (itr->second.Team == Team)
1738 Player * pl = objmgr.GetPlayer(itr->first);
1739 if (pl && pl->isAlive())
1740 ++count;
1743 return count;
1746 void BattleGround::CheckArenaWinConditions()
1748 if (!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE))
1749 EndBattleGround(HORDE);
1750 else if (GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE))
1751 EndBattleGround(ALLIANCE);
1754 void BattleGround::SetBgRaid( uint32 TeamID, Group *bg_raid )
1756 Group* &old_raid = TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE];
1757 if(old_raid) old_raid->SetBattlegroundGroup(NULL);
1758 if(bg_raid) bg_raid->SetBattlegroundGroup(this);
1759 old_raid = bg_raid;
1762 WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard( Player* player )
1764 return objmgr.GetClosestGraveYard( player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam() );