[9581] Fixed apply damage reduction to melee/ranged damage.
[getmangos.git] / src / game / BattleGround.cpp
blob7bc403df5d86671d1496af11c8abbc997c41458b
1 /*
2 * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "Object.h"
20 #include "Player.h"
21 #include "BattleGround.h"
22 #include "BattleGroundMgr.h"
23 #include "Creature.h"
24 #include "MapManager.h"
25 #include "Language.h"
26 #include "SpellAuras.h"
27 #include "ArenaTeam.h"
28 #include "World.h"
29 #include "Group.h"
30 #include "ObjectGuid.h"
31 #include "ObjectMgr.h"
32 #include "WorldPacket.h"
33 #include "Util.h"
34 #include "Formulas.h"
35 #include "GridNotifiersImpl.h"
37 namespace MaNGOS
39 class BattleGroundChatBuilder
41 public:
42 BattleGroundChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, va_list* args = NULL)
43 : i_msgtype(msgtype), i_textId(textId), i_source(source), i_args(args) {}
44 void operator()(WorldPacket& data, int32 loc_idx)
46 char const* text = sObjectMgr.GetMangosString(i_textId,loc_idx);
48 if (i_args)
50 // we need copy va_list before use or original va_list will corrupted
51 va_list ap;
52 va_copy(ap,*i_args);
54 char str [2048];
55 vsnprintf(str,2048,text, ap );
56 va_end(ap);
58 do_helper(data,&str[0]);
60 else
61 do_helper(data,text);
63 private:
64 void do_helper(WorldPacket& data, char const* text)
66 uint64 target_guid = i_source ? i_source ->GetGUID() : 0;
68 data << uint8(i_msgtype);
69 data << uint32(LANG_UNIVERSAL);
70 data << uint64(target_guid); // there 0 for BG messages
71 data << uint32(0); // can be chat msg group or something
72 data << uint64(target_guid);
73 data << uint32(strlen(text)+1);
74 data << text;
75 data << uint8(i_source ? i_source->chatTag() : uint8(0));
78 ChatMsg i_msgtype;
79 int32 i_textId;
80 Player const* i_source;
81 va_list* i_args;
84 class BattleGroundYellBuilder
86 public:
87 BattleGroundYellBuilder(uint32 language, int32 textId, Creature const* source, va_list* args = NULL)
88 : i_language(language), i_textId(textId), i_source(source), i_args(args) {}
89 void operator()(WorldPacket& data, int32 loc_idx)
91 char const* text = sObjectMgr.GetMangosString(i_textId,loc_idx);
93 if(i_args)
95 // we need copy va_list before use or original va_list will corrupted
96 va_list ap;
97 va_copy(ap,*i_args);
99 char str [2048];
100 vsnprintf(str,2048,text, ap );
101 va_end(ap);
103 do_helper(data,&str[0]);
105 else
106 do_helper(data,text);
108 private:
109 void do_helper(WorldPacket& data, char const* text)
111 //copyied from BuildMonsterChat
112 data << (uint8)CHAT_MSG_MONSTER_YELL;
113 data << (uint32)i_language;
114 data << (uint64)i_source->GetGUID();
115 data << (uint32)0; //2.1.0
116 data << (uint32)(strlen(i_source->GetName())+1);
117 data << i_source->GetName();
118 data << (uint64)0; //Unit Target - isn't important for bgs
119 data << (uint32)strlen(text)+1;
120 data << text;
121 data << (uint8)0; // ChatTag - for bgs allways 0?
124 uint32 i_language;
125 int32 i_textId;
126 Creature const* i_source;
127 va_list* i_args;
131 class BattleGround2ChatBuilder
133 public:
134 BattleGround2ChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, int32 arg1, int32 arg2)
135 : i_msgtype(msgtype), i_textId(textId), i_source(source), i_arg1(arg1), i_arg2(arg2) {}
136 void operator()(WorldPacket& data, int32 loc_idx)
138 char const* text = sObjectMgr.GetMangosString(i_textId,loc_idx);
139 char const* arg1str = i_arg1 ? sObjectMgr.GetMangosString(i_arg1,loc_idx) : "";
140 char const* arg2str = i_arg2 ? sObjectMgr.GetMangosString(i_arg2,loc_idx) : "";
142 char str [2048];
143 snprintf(str,2048,text, arg1str, arg2str );
145 uint64 target_guid = i_source ? i_source ->GetGUID() : 0;
147 data << uint8(i_msgtype);
148 data << uint32(LANG_UNIVERSAL);
149 data << uint64(target_guid); // there 0 for BG messages
150 data << uint32(0); // can be chat msg group or something
151 data << uint64(target_guid);
152 data << uint32(strlen(str)+1);
153 data << str;
154 data << uint8(i_source ? i_source->chatTag() : uint8(0));
156 private:
158 ChatMsg i_msgtype;
159 int32 i_textId;
160 Player const* i_source;
161 int32 i_arg1;
162 int32 i_arg2;
165 class BattleGround2YellBuilder
167 public:
168 BattleGround2YellBuilder(uint32 language, int32 textId, Creature const* source, int32 arg1, int32 arg2)
169 : i_language(language), i_textId(textId), i_source(source), i_arg1(arg1), i_arg2(arg2) {}
170 void operator()(WorldPacket& data, int32 loc_idx)
172 char const* text = sObjectMgr.GetMangosString(i_textId,loc_idx);
173 char const* arg1str = i_arg1 ? sObjectMgr.GetMangosString(i_arg1,loc_idx) : "";
174 char const* arg2str = i_arg2 ? sObjectMgr.GetMangosString(i_arg2,loc_idx) : "";
176 char str [2048];
177 snprintf(str,2048,text, arg1str, arg2str );
178 //copyied from BuildMonsterChat
179 data << (uint8)CHAT_MSG_MONSTER_YELL;
180 data << (uint32)i_language;
181 data << (uint64)i_source->GetGUID();
182 data << (uint32)0; //2.1.0
183 data << (uint32)(strlen(i_source->GetName())+1);
184 data << i_source->GetName();
185 data << (uint64)0; //Unit Target - isn't important for bgs
186 data << (uint32)strlen(str)+1;
187 data << str;
188 data << (uint8)0; // ChatTag - for bgs allways 0?
190 private:
192 uint32 i_language;
193 int32 i_textId;
194 Creature const* i_source;
195 int32 i_arg1;
196 int32 i_arg2;
198 } // namespace MaNGOS
200 template<class Do>
201 void BattleGround::BroadcastWorker(Do& _do)
203 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
204 if (Player *plr = ObjectAccessor::FindPlayer(ObjectGuid(HIGHGUID_PLAYER, itr->first)))
205 _do(plr);
208 BattleGround::BattleGround()
210 m_TypeID = BattleGroundTypeId(0);
211 m_InstanceID = 0;
212 m_Status = STATUS_NONE;
213 m_ClientInstanceID = 0;
214 m_EndTime = 0;
215 m_BracketId = BG_BRACKET_ID_FIRST;
216 m_InvitedAlliance = 0;
217 m_InvitedHorde = 0;
218 m_ArenaType = 0;
219 m_IsArena = false;
220 m_Winner = 2;
221 m_StartTime = 0;
222 m_Events = 0;
223 m_IsRated = false;
224 m_BuffChange = false;
225 m_Name = "";
226 m_LevelMin = 0;
227 m_LevelMax = 0;
228 m_InBGFreeSlotQueue = false;
229 m_SetDeleteThis = false;
231 m_MaxPlayersPerTeam = 0;
232 m_MaxPlayers = 0;
233 m_MinPlayersPerTeam = 0;
234 m_MinPlayers = 0;
236 m_MapId = 0;
237 m_Map = NULL;
239 m_TeamStartLocX[BG_TEAM_ALLIANCE] = 0;
240 m_TeamStartLocX[BG_TEAM_HORDE] = 0;
242 m_TeamStartLocY[BG_TEAM_ALLIANCE] = 0;
243 m_TeamStartLocY[BG_TEAM_HORDE] = 0;
245 m_TeamStartLocZ[BG_TEAM_ALLIANCE] = 0;
246 m_TeamStartLocZ[BG_TEAM_HORDE] = 0;
248 m_TeamStartLocO[BG_TEAM_ALLIANCE] = 0;
249 m_TeamStartLocO[BG_TEAM_HORDE] = 0;
251 m_ArenaTeamIds[BG_TEAM_ALLIANCE] = 0;
252 m_ArenaTeamIds[BG_TEAM_HORDE] = 0;
254 m_ArenaTeamRatingChanges[BG_TEAM_ALLIANCE] = 0;
255 m_ArenaTeamRatingChanges[BG_TEAM_HORDE] = 0;
257 m_BgRaids[BG_TEAM_ALLIANCE] = NULL;
258 m_BgRaids[BG_TEAM_HORDE] = NULL;
260 m_PlayersCount[BG_TEAM_ALLIANCE] = 0;
261 m_PlayersCount[BG_TEAM_HORDE] = 0;
263 m_TeamScores[BG_TEAM_ALLIANCE] = 0;
264 m_TeamScores[BG_TEAM_HORDE] = 0;
266 m_PrematureCountDown = false;
267 m_PrematureCountDown = 0;
269 m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_2M;
270 m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_1M;
271 m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_30S;
272 m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE;
273 //we must set to some default existing values
274 m_StartMessageIds[BG_STARTING_EVENT_FIRST] = 0;
275 m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE;
276 m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE;
277 m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN;
280 BattleGround::~BattleGround()
282 // remove objects and creatures
283 // (this is done automatically in mapmanager update, when the instance is reset after the reset time)
285 int size = m_BgObjects.size();
286 for(int i = 0; i < size; ++i)
287 DelObject(i);
289 if (GetInstanceID()) // not spam by useless queries in case BG templates
291 // delete creature and go respawn times
292 WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'",GetInstanceID());
293 WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'",GetInstanceID());
294 // delete instance from db
295 CharacterDatabase.PExecute("DELETE FROM instance WHERE id = '%u'",GetInstanceID());
296 // remove from battlegrounds
299 sBattleGroundMgr.RemoveBattleGround(GetInstanceID(), GetTypeID());
301 // unload map
302 // map can be null at bg destruction
303 if (m_Map)
304 m_Map->SetUnload();
306 // remove from bg free slot queue
307 this->RemoveFromBGFreeSlotQueue();
309 for(BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr)
310 delete itr->second;
313 void BattleGround::Update(uint32 diff)
315 if (!GetPlayersSize())
317 // BG is empty
318 // if there are no players invited, delete BG
319 // this will delete arena or bg object, where any player entered
320 // [[ but if you use battleground object again (more battles possible to be played on 1 instance)
321 // then this condition should be removed and code:
322 // if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
323 // this->AddToFreeBGObjectsQueue(); // not yet implemented
324 // should be used instead of current
325 // ]]
326 // BattleGround Template instance cannot be updated, because it would be deleted
327 if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
328 m_SetDeleteThis = true;
329 return;
332 // remove offline players from bg after 5 minutes
333 if (!m_OfflineQueue.empty())
335 BattleGroundPlayerMap::iterator itr = m_Players.find(*(m_OfflineQueue.begin()));
336 if (itr != m_Players.end())
338 if (itr->second.OfflineRemoveTime <= sWorld.GetGameTime())
340 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
341 m_OfflineQueue.pop_front(); // remove from offline queue
342 //do not use itr for anything, because it is erased in RemovePlayerAtLeave()
347 /*********************************************************/
348 /*** BATTLEGROUND BALLANCE SYSTEM ***/
349 /*********************************************************/
351 // if less then minimum players are in on one side, then start premature finish timer
352 if (GetStatus() == STATUS_IN_PROGRESS && !isArena() && sBattleGroundMgr.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE) < GetMinPlayersPerTeam()))
354 if (!m_PrematureCountDown)
356 m_PrematureCountDown = true;
357 m_PrematureCountDownTimer = sBattleGroundMgr.GetPrematureFinishTime();
359 else if (m_PrematureCountDownTimer < diff)
361 // time's up!
362 uint32 winner = 0;
363 if (GetPlayersCountByTeam(ALLIANCE) >= GetMinPlayersPerTeam())
364 winner = ALLIANCE;
365 else if (GetPlayersCountByTeam(HORDE) >= GetMinPlayersPerTeam())
366 winner = HORDE;
368 EndBattleGround(winner);
369 m_PrematureCountDown = false;
371 else
373 uint32 newtime = m_PrematureCountDownTimer - diff;
374 // announce every minute
375 if (newtime > (MINUTE * IN_MILISECONDS))
377 if (newtime / (MINUTE * IN_MILISECONDS) != m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS))
378 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS)));
380 else
382 //announce every 15 seconds
383 if (newtime / (15 * IN_MILISECONDS) != m_PrematureCountDownTimer / (15 * IN_MILISECONDS))
384 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / IN_MILISECONDS));
386 m_PrematureCountDownTimer = newtime;
390 else if (m_PrematureCountDown)
391 m_PrematureCountDown = false;
393 /*********************************************************/
394 /*** ARENA BUFF OBJECT SPAWNING ***/
395 /*********************************************************/
396 if (isArena() && !m_ArenaBuffSpawned)
398 // 60 seconds after start the buffobjects in arena should get spawned
399 if (m_StartTime > uint32(m_StartDelayTimes[BG_STARTING_EVENT_FIRST] + ARENA_SPAWN_BUFF_OBJECTS))
401 SpawnEvent(ARENA_BUFF_EVENT, 0, true);
402 m_ArenaBuffSpawned = true;
406 /*********************************************************/
407 /*** BATTLEGROUND STARTING SYSTEM ***/
408 /*********************************************************/
410 if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize())
412 ModifyStartDelayTime(diff);
414 if (!(m_Events & BG_STARTING_EVENT_1))
416 m_Events |= BG_STARTING_EVENT_1;
418 // setup here, only when at least one player has ported to the map
419 if (!SetupBattleGround())
421 EndNow();
422 return;
425 StartingEventCloseDoors();
426 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FIRST]);
427 //first start warning - 2 or 1 minute, only if defined
428 if (m_StartMessageIds[BG_STARTING_EVENT_FIRST])
429 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FIRST], CHAT_MSG_BG_SYSTEM_NEUTRAL);
431 // After 1 minute or 30 seconds, warning is signalled
432 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_SECOND] && !(m_Events & BG_STARTING_EVENT_2))
434 m_Events |= BG_STARTING_EVENT_2;
435 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_SECOND], CHAT_MSG_BG_SYSTEM_NEUTRAL);
437 // After 30 or 15 seconds, warning is signalled
438 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_THIRD] && !(m_Events & BG_STARTING_EVENT_3))
440 m_Events |= BG_STARTING_EVENT_3;
441 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_THIRD], CHAT_MSG_BG_SYSTEM_NEUTRAL);
443 // delay expired (atfer 2 or 1 minute)
444 else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4))
446 m_Events |= BG_STARTING_EVENT_4;
448 StartingEventOpenDoors();
450 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FOURTH], CHAT_MSG_BG_SYSTEM_NEUTRAL);
451 SetStatus(STATUS_IN_PROGRESS);
452 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FOURTH]);
454 //remove preparation
455 if (isArena())
457 //TODO : add arena sound PlaySoundToAll(SOUND_ARENA_START);
459 for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
460 if (Player *plr = sObjectMgr.GetPlayer(itr->first))
461 plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION);
463 CheckArenaWinConditions();
465 else
468 PlaySoundToAll(SOUND_BG_START);
470 for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
471 if (Player* plr = sObjectMgr.GetPlayer(itr->first))
472 plr->RemoveAurasDueToSpell(SPELL_PREPARATION);
473 //Announce BG starting
474 if (sWorld.getConfig(CONFIG_BOOL_BATTLEGROUND_QUEUE_ANNOUNCER_START))
476 sWorld.SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD, GetName(), GetMinLevel(), GetMaxLevel());
482 /*********************************************************/
483 /*** BATTLEGROUND ENDING SYSTEM ***/
484 /*********************************************************/
486 if (GetStatus() == STATUS_WAIT_LEAVE)
488 // remove all players from battleground after 2 minutes
489 m_EndTime -= diff;
490 if (m_EndTime <= 0)
492 m_EndTime = 0;
493 BattleGroundPlayerMap::iterator itr, next;
494 for(itr = m_Players.begin(); itr != m_Players.end(); itr = next)
496 next = itr;
497 ++next;
498 //itr is erased here!
499 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
500 // do not change any battleground's private variables
505 //update start time
506 m_StartTime += diff;
509 void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O)
511 BattleGroundTeamId idx = GetTeamIndexByTeamId(TeamID);
512 m_TeamStartLocX[idx] = X;
513 m_TeamStartLocY[idx] = Y;
514 m_TeamStartLocZ[idx] = Z;
515 m_TeamStartLocO[idx] = O;
518 void BattleGround::SendPacketToAll(WorldPacket *packet)
520 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
522 if (itr->second.OfflineRemoveTime)
523 continue;
524 Player *plr = sObjectMgr.GetPlayer(itr->first);
525 if (plr)
526 plr->GetSession()->SendPacket(packet);
527 else
528 sLog.outError("BattleGround:SendPacketToAll: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
532 void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender, bool self)
534 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
536 if (itr->second.OfflineRemoveTime)
537 continue;
538 Player *plr = sObjectMgr.GetPlayer(itr->first);
539 if (!plr)
541 sLog.outError("BattleGround:SendPacketToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
542 continue;
545 if (!self && sender == plr)
546 continue;
548 uint32 team = itr->second.Team;
549 if(!team) team = plr->GetTeam();
551 if (team == TeamID)
552 plr->GetSession()->SendPacket(packet);
556 void BattleGround::PlaySoundToAll(uint32 SoundID)
558 WorldPacket data;
559 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
560 SendPacketToAll(&data);
563 void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID)
565 WorldPacket data;
567 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
569 if (itr->second.OfflineRemoveTime)
570 continue;
571 Player *plr = sObjectMgr.GetPlayer(itr->first);
573 if (!plr)
575 sLog.outError("BattleGround:PlaySoundToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
576 continue;
579 uint32 team = itr->second.Team;
580 if(!team) team = plr->GetTeam();
582 if (team == TeamID)
584 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
585 plr->GetSession()->SendPacket(&data);
590 void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID)
592 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
594 if (itr->second.OfflineRemoveTime)
595 continue;
596 Player *plr = sObjectMgr.GetPlayer(itr->first);
598 if (!plr)
600 sLog.outError("BattleGround:CastSpellOnTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
601 continue;
604 uint32 team = itr->second.Team;
605 if(!team) team = plr->GetTeam();
607 if (team == TeamID)
608 plr->CastSpell(plr, SpellID, true);
612 void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID)
614 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
616 if (itr->second.OfflineRemoveTime)
617 continue;
618 Player *plr = sObjectMgr.GetPlayer(itr->first);
620 if (!plr)
622 sLog.outError("BattleGround:RewardHonorToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
623 continue;
626 uint32 team = itr->second.Team;
627 if(!team) team = plr->GetTeam();
629 if (team == TeamID)
630 UpdatePlayerScore(plr, SCORE_BONUS_HONOR, Honor);
634 void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID)
636 FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
638 if (!factionEntry)
639 return;
641 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
643 if (itr->second.OfflineRemoveTime)
644 continue;
645 Player *plr = sObjectMgr.GetPlayer(itr->first);
647 if (!plr)
649 sLog.outError("BattleGround:RewardReputationToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
650 continue;
653 uint32 team = itr->second.Team;
654 if(!team) team = plr->GetTeam();
656 if (team == TeamID)
657 plr->GetReputationMgr().ModifyReputation(factionEntry, Reputation);
661 void BattleGround::UpdateWorldState(uint32 Field, uint32 Value)
663 WorldPacket data;
664 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
665 SendPacketToAll(&data);
668 void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source)
670 WorldPacket data;
671 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
672 Source->GetSession()->SendPacket(&data);
675 void BattleGround::EndBattleGround(uint32 winner)
677 this->RemoveFromBGFreeSlotQueue();
679 ArenaTeam * winner_arena_team = NULL;
680 ArenaTeam * loser_arena_team = NULL;
681 uint32 loser_rating = 0;
682 uint32 winner_rating = 0;
683 WorldPacket data;
684 int32 winmsg_id = 0;
686 if (winner == ALLIANCE)
688 winmsg_id = isBattleGround() ? LANG_BG_A_WINS : LANG_ARENA_GOLD_WINS;
690 PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound
692 SetWinner(WINNER_ALLIANCE);
694 else if (winner == HORDE)
696 winmsg_id = isBattleGround() ? LANG_BG_H_WINS : LANG_ARENA_GREEN_WINS;
698 PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound
700 SetWinner(WINNER_HORDE);
702 else
704 SetWinner(3);
707 SetStatus(STATUS_WAIT_LEAVE);
708 //we must set it this way, because end time is sent in packet!
709 m_EndTime = TIME_TO_AUTOREMOVE;
711 // arena rating calculation
712 if (isArena() && isRated())
714 winner_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(winner));
715 loser_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(winner)));
716 if (winner_arena_team && loser_arena_team)
718 loser_rating = loser_arena_team->GetStats().rating;
719 winner_rating = winner_arena_team->GetStats().rating;
720 int32 winner_change = winner_arena_team->WonAgainst(loser_rating);
721 int32 loser_change = loser_arena_team->LostAgainst(winner_rating);
722 sLog.outDebug("--- Winner rating: %u, Loser rating: %u, Winner change: %u, Losser change: %u ---", winner_rating, loser_rating, winner_change, loser_change);
723 SetArenaTeamRatingChangeForTeam(winner, winner_change);
724 SetArenaTeamRatingChangeForTeam(GetOtherTeam(winner), loser_change);
726 else
728 SetArenaTeamRatingChangeForTeam(ALLIANCE, 0);
729 SetArenaTeamRatingChangeForTeam(HORDE, 0);
733 for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
735 uint32 team = itr->second.Team;
737 if (itr->second.OfflineRemoveTime)
739 //if rated arena match - make member lost!
740 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
742 if (team == winner)
743 winner_arena_team->OfflineMemberLost(itr->first, loser_rating);
744 else
745 loser_arena_team->OfflineMemberLost(itr->first, winner_rating);
747 continue;
749 Player *plr = sObjectMgr.GetPlayer(itr->first);
750 if (!plr)
752 sLog.outError("BattleGround:EndBattleGround Player (GUID: %u) not found!", GUID_LOPART(itr->first));
753 continue;
756 // should remove spirit of redemption
757 if (plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
758 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
760 if (!plr->isAlive())
762 plr->ResurrectPlayer(1.0f);
763 plr->SpawnCorpseBones();
765 else
767 //needed cause else in av some creatures will kill the players at the end
768 plr->CombatStop();
769 plr->getHostileRefManager().deleteReferences();
772 //this line is obsolete - team is set ALWAYS
773 //if(!team) team = plr->GetTeam();
775 // per player calculation
776 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
778 if (team == winner)
780 // update achievement BEFORE personal rating update
781 ArenaTeamMember* member = winner_arena_team->GetMember(plr->GetGUID());
782 if (member)
783 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, member->personal_rating);
785 winner_arena_team->MemberWon(plr,loser_rating);
787 else
789 loser_arena_team->MemberLost(plr,winner_rating);
791 // Arena lost => reset the win_rated_arena having the "no_loose" condition
792 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE);
796 if (team == winner)
798 RewardMark(plr,ITEM_WINNER_COUNT);
799 RewardQuestComplete(plr);
800 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_BG, 1);
802 else
803 RewardMark(plr,ITEM_LOSER_COUNT);
805 plr->CombatStopWithPets(true);
807 BlockMovement(plr);
809 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
810 plr->GetSession()->SendPacket(&data);
812 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
813 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType());
814 plr->GetSession()->SendPacket(&data);
815 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1);
818 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
820 // update arena points only after increasing the player's match count!
821 //obsolete: winner_arena_team->UpdateArenaPointsHelper();
822 //obsolete: loser_arena_team->UpdateArenaPointsHelper();
823 // save the stat changes
824 winner_arena_team->SaveToDB();
825 loser_arena_team->SaveToDB();
826 // send updated arena team stats to players
827 // this way all arena team members will get notified, not only the ones who participated in this match
828 winner_arena_team->NotifyStatsChanged();
829 loser_arena_team->NotifyStatsChanged();
832 if (winmsg_id)
833 SendMessageToAll(winmsg_id, CHAT_MSG_BG_SYSTEM_NEUTRAL);
836 uint32 BattleGround::GetBonusHonorFromKill(uint32 kills) const
838 //variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill)
839 return (uint32)MaNGOS::Honor::hk_honor_at_level(GetMaxLevel(), kills);
842 uint32 BattleGround::GetBattlemasterEntry() const
844 switch(GetTypeID())
846 case BATTLEGROUND_AV: return 15972;
847 case BATTLEGROUND_WS: return 14623;
848 case BATTLEGROUND_AB: return 14879;
849 case BATTLEGROUND_EY: return 22516;
850 case BATTLEGROUND_NA: return 20200;
851 default: return 0;
855 void BattleGround::RewardMark(Player *plr,uint32 count)
857 BattleGroundMarks mark;
858 bool IsSpell;
859 switch(GetTypeID())
861 case BATTLEGROUND_AV:
862 IsSpell = true;
863 if (count == ITEM_WINNER_COUNT)
864 mark = SPELL_AV_MARK_WINNER;
865 else
866 mark = SPELL_AV_MARK_LOSER;
867 break;
868 case BATTLEGROUND_WS:
869 IsSpell = true;
870 if (count == ITEM_WINNER_COUNT)
871 mark = SPELL_WS_MARK_WINNER;
872 else
873 mark = SPELL_WS_MARK_LOSER;
874 break;
875 case BATTLEGROUND_AB:
876 IsSpell = true;
877 if (count == ITEM_WINNER_COUNT)
878 mark = SPELL_AB_MARK_WINNER;
879 else
880 mark = SPELL_AB_MARK_LOSER;
881 break;
882 case BATTLEGROUND_EY:
883 IsSpell = false;
884 mark = ITEM_EY_MARK_OF_HONOR;
885 break;
886 default:
887 return;
890 if (IsSpell)
891 RewardSpellCast(plr,mark);
892 else
893 RewardItem(plr,mark,count);
896 void BattleGround::RewardSpellCast(Player *plr, uint32 spell_id)
898 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
899 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
900 return;
902 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
903 if(!spellInfo)
905 sLog.outError("Battleground reward casting spell %u not exist.",spell_id);
906 return;
909 plr->CastSpell(plr, spellInfo, true);
912 void BattleGround::RewardItem(Player *plr, uint32 item_id, uint32 count)
914 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
915 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
916 return;
918 ItemPosCountVec dest;
919 uint32 no_space_count = 0;
920 uint8 msg = plr->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item_id, count, &no_space_count );
922 if( msg == EQUIP_ERR_ITEM_NOT_FOUND)
924 sLog.outErrorDb("Battleground reward item (Entry %u) not exist in `item_template`.",item_id);
925 return;
928 if( msg != EQUIP_ERR_OK ) // convert to possible store amount
929 count -= no_space_count;
931 if( count != 0 && !dest.empty()) // can add some
932 if (Item* item = plr->StoreNewItem( dest, item_id, true, 0))
933 plr->SendNewItem(item,count,true,false);
935 if (no_space_count > 0)
936 SendRewardMarkByMail(plr,item_id,no_space_count);
939 void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count)
941 uint32 bmEntry = GetBattlemasterEntry();
942 if (!bmEntry)
943 return;
945 ItemPrototype const* markProto = ObjectMgr::GetItemPrototype(mark);
946 if (!markProto)
947 return;
949 if (Item* markItem = Item::CreateItem(mark,count,plr))
951 // save new item before send
952 markItem->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
954 // subject: item name
955 std::string subject = markProto->Name1;
956 int loc_idx = plr->GetSession()->GetSessionDbLocaleIndex();
957 if (loc_idx >= 0 )
958 if (ItemLocale const *il = sObjectMgr.GetItemLocale(markProto->ItemId))
959 if (il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty())
960 subject = il->Name[loc_idx];
962 // text
963 std::string textFormat = plr->GetSession()->GetMangosString(LANG_BG_MARK_BY_MAIL);
964 char textBuf[300];
965 snprintf(textBuf,300,textFormat.c_str(),GetName(),GetName());
966 uint32 itemTextId = sObjectMgr.CreateItemText( textBuf );
968 MailDraft(subject, itemTextId)
969 .AddItem(markItem)
970 .SendMailTo(plr, MailSender(MAIL_CREATURE, bmEntry));
974 void BattleGround::RewardQuestComplete(Player *plr)
976 uint32 quest;
977 switch(GetTypeID())
979 case BATTLEGROUND_AV:
980 quest = SPELL_AV_QUEST_REWARD;
981 break;
982 case BATTLEGROUND_WS:
983 quest = SPELL_WS_QUEST_REWARD;
984 break;
985 case BATTLEGROUND_AB:
986 quest = SPELL_AB_QUEST_REWARD;
987 break;
988 case BATTLEGROUND_EY:
989 quest = SPELL_EY_QUEST_REWARD;
990 break;
991 default:
992 return;
995 RewardSpellCast(plr, quest);
998 void BattleGround::BlockMovement(Player *plr)
1000 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()
1003 void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket)
1005 uint32 team = GetPlayerTeam(guid);
1006 bool participant = false;
1007 // Remove from lists/maps
1008 BattleGroundPlayerMap::iterator itr = m_Players.find(guid);
1009 if (itr != m_Players.end())
1011 UpdatePlayersCountByTeam(team, true); // -1 player
1012 m_Players.erase(itr);
1013 // check if the player was a participant of the match, or only entered through gm command (goname)
1014 participant = true;
1017 BattleGroundScoreMap::iterator itr2 = m_PlayerScores.find(guid);
1018 if (itr2 != m_PlayerScores.end())
1020 delete itr2->second; // delete player's score
1021 m_PlayerScores.erase(itr2);
1024 Player *plr = sObjectMgr.GetPlayer(guid);
1026 // should remove spirit of redemption
1027 if (plr && plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
1028 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
1030 if(plr && !plr->isAlive()) // resurrect on exit
1032 plr->ResurrectPlayer(1.0f);
1033 plr->SpawnCorpseBones();
1036 RemovePlayer(plr, guid); // BG subclass specific code
1038 if(participant) // if the player was a match participant, remove auras, calc rating, update queue
1040 BattleGroundTypeId bgTypeId = GetTypeID();
1041 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1042 if (plr)
1044 plr->ClearAfkReports();
1046 if(!team) team = plr->GetTeam();
1048 // if arena, remove the specific arena auras
1049 if (isArena())
1051 plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out
1052 bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing)
1054 // unsummon current and summon old pet if there was one and there isn't a current pet
1055 plr->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT);
1056 plr->ResummonPetTemporaryUnSummonedIfAny();
1058 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
1060 //left a rated match while the encounter was in progress, consider as loser
1061 ArenaTeam * winner_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
1062 ArenaTeam * loser_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
1063 if (winner_arena_team && loser_arena_team)
1064 loser_arena_team->MemberLost(plr,winner_arena_team->GetRating());
1067 if (SendPacket)
1069 WorldPacket data;
1070 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0, 0);
1071 plr->GetSession()->SendPacket(&data);
1074 // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg
1075 plr->RemoveBattleGroundQueueId(bgQueueTypeId);
1077 else
1078 // removing offline participant
1080 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
1082 //left a rated match while the encounter was in progress, consider as loser
1083 ArenaTeam * others_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
1084 ArenaTeam * players_arena_team = sObjectMgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
1085 if (others_arena_team && players_arena_team)
1086 players_arena_team->OfflineMemberLost(guid, others_arena_team->GetRating());
1090 // remove from raid group if player is member
1091 if (Group *group = GetBgRaid(team))
1093 if( !group->RemoveMember(guid, 0) ) // group was disbanded
1095 SetBgRaid(team, NULL);
1096 delete group;
1099 DecreaseInvitedCount(team);
1100 //we should update battleground queue, but only if bg isn't ending
1101 if (isBattleGround() && GetStatus() < STATUS_WAIT_LEAVE)
1103 // a player has left the battleground, so there are free slots -> add to queue
1104 AddToBGFreeSlotQueue();
1105 sBattleGroundMgr.ScheduleQueueUpdate(0, 0, bgQueueTypeId, bgTypeId, GetBracketId());
1108 // Let others know
1109 WorldPacket data;
1110 sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, guid);
1111 SendPacketToTeam(team, &data, plr, false);
1114 if (plr)
1116 // Do next only if found in battleground
1117 plr->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG.
1118 // reset destination bg team
1119 plr->SetBGTeam(0);
1121 if (Transport)
1122 plr->TeleportToBGEntryPoint();
1124 sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName());
1127 //battleground object will be deleted next BattleGround::Update() call
1130 // this method is called when no players remains in battleground
1131 void BattleGround::Reset()
1133 SetWinner(WINNER_NONE);
1134 SetStatus(STATUS_WAIT_QUEUE);
1135 SetStartTime(0);
1136 SetEndTime(0);
1137 SetArenaType(0);
1138 SetRated(false);
1140 m_Events = 0;
1142 // door-event2 is always 0
1143 m_ActiveEvents[BG_EVENT_DOOR] = 0;
1144 if (isArena())
1146 m_ActiveEvents[ARENA_BUFF_EVENT] = BG_EVENT_NONE;
1147 m_ArenaBuffSpawned = false;
1150 if (m_InvitedAlliance > 0 || m_InvitedHorde > 0)
1151 sLog.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde);
1153 m_InvitedAlliance = 0;
1154 m_InvitedHorde = 0;
1155 m_InBGFreeSlotQueue = false;
1157 m_Players.clear();
1159 for(BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr)
1160 delete itr->second;
1161 m_PlayerScores.clear();
1164 void BattleGround::StartBattleGround()
1166 SetStartTime(0);
1168 // add BG to free slot queue
1169 AddToBGFreeSlotQueue();
1171 // add bg to update list
1172 // This must be done here, because we need to have already invited some players when first BG::Update() method is executed
1173 // and it doesn't matter if we call StartBattleGround() more times, because m_BattleGrounds is a map and instance id never changes
1174 sBattleGroundMgr.AddBattleGround(GetInstanceID(), GetTypeID(), this);
1177 void BattleGround::AddPlayer(Player *plr)
1179 // remove afk from player
1180 if (plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK))
1181 plr->ToggleAFK();
1183 // score struct must be created in inherited class
1185 uint64 guid = plr->GetGUID();
1186 uint32 team = plr->GetBGTeam();
1188 BattleGroundPlayer bp;
1189 bp.OfflineRemoveTime = 0;
1190 bp.Team = team;
1192 // Add to list/maps
1193 m_Players[guid] = bp;
1195 UpdatePlayersCountByTeam(team, false); // +1 player
1197 WorldPacket data;
1198 sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr);
1199 SendPacketToTeam(team, &data, plr, false);
1201 // add arena specific auras
1202 if (isArena())
1204 plr->RemoveArenaSpellCooldowns();
1205 plr->RemoveArenaAuras();
1206 plr->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT);
1207 if(team == ALLIANCE) // gold
1209 if (plr->GetTeam() == HORDE)
1210 plr->CastSpell(plr, SPELL_HORDE_GOLD_FLAG,true);
1211 else
1212 plr->CastSpell(plr, SPELL_ALLIANCE_GOLD_FLAG,true);
1214 else // green
1216 if (plr->GetTeam() == HORDE)
1217 plr->CastSpell(plr, SPELL_HORDE_GREEN_FLAG,true);
1218 else
1219 plr->CastSpell(plr, SPELL_ALLIANCE_GREEN_FLAG,true);
1222 plr->DestroyConjuredItems(true);
1223 plr->UnsummonPetTemporaryIfAny();
1225 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1227 plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true);
1229 plr->SetHealth(plr->GetMaxHealth());
1230 plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA));
1233 else
1235 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1236 plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells.
1239 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1240 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1242 // setup BG group membership
1243 PlayerAddedToBGCheckIfBGIsRunning(plr);
1244 AddOrSetPlayerToCorrectBgGroup(plr, guid, team);
1246 // Log
1247 sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName());
1250 /* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */
1251 void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team)
1253 Group* group = GetBgRaid(team);
1254 if(!group) // first player joined
1256 group = new Group;
1257 SetBgRaid(team, group);
1258 group->Create(plr_guid, plr->GetName());
1260 else // raid already exist
1262 if (group->IsMember(plr_guid))
1264 uint8 subgroup = group->GetMemberGroup(plr_guid);
1265 plr->SetBattleGroundRaid(group, subgroup);
1267 else
1269 group->AddMember(plr_guid, plr->GetName());
1270 if (Group* originalGroup = plr->GetOriginalGroup())
1271 if (originalGroup->IsLeader(plr_guid))
1272 group->ChangeLeader(plr_guid);
1277 // This method should be called when player logs into running battleground
1278 void BattleGround::EventPlayerLoggedIn(Player* player, uint64 plr_guid)
1280 // player is correct pointer
1281 for(std::deque<uint64>::iterator itr = m_OfflineQueue.begin(); itr != m_OfflineQueue.end(); ++itr)
1283 if (*itr == plr_guid)
1285 m_OfflineQueue.erase(itr);
1286 break;
1289 m_Players[plr_guid].OfflineRemoveTime = 0;
1290 PlayerAddedToBGCheckIfBGIsRunning(player);
1291 // if battleground is starting, then add preparation aura
1292 // we don't have to do that, because preparation aura isn't removed when player logs out
1295 // This method should be called when player logs out from running battleground
1296 void BattleGround::EventPlayerLoggedOut(Player* player)
1298 // player is correct pointer, it is checked in WorldSession::LogoutPlayer()
1299 m_OfflineQueue.push_back(player->GetGUID());
1300 m_Players[player->GetGUID()].OfflineRemoveTime = sWorld.GetGameTime() + MAX_OFFLINE_TIME;
1301 if (GetStatus() == STATUS_IN_PROGRESS)
1303 // drop flag and handle other cleanups
1304 RemovePlayer(player, player->GetGUID());
1306 // 1 player is logging out, if it is the last, then end arena!
1307 if (isArena())
1308 if (GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam())))
1309 EndBattleGround(GetOtherTeam(player->GetTeam()));
1313 /* This method should be called only once ... it adds pointer to queue */
1314 void BattleGround::AddToBGFreeSlotQueue()
1316 // make sure to add only once
1317 if (!m_InBGFreeSlotQueue && isBattleGround())
1319 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this);
1320 m_InBGFreeSlotQueue = true;
1324 /* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/
1325 void BattleGround::RemoveFromBGFreeSlotQueue()
1327 // set to be able to re-add if needed
1328 m_InBGFreeSlotQueue = false;
1329 // uncomment this code when battlegrounds will work like instances
1330 for (BGFreeSlotQueueType::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr)
1332 if ((*itr)->GetInstanceID() == m_InstanceID)
1334 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr);
1335 return;
1340 // get the number of free slots for team
1341 // returns the number how many players can join battleground to MaxPlayersPerTeam
1342 uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const
1344 //return free slot count to MaxPlayerPerTeam
1345 if (GetStatus() == STATUS_WAIT_JOIN || GetStatus() == STATUS_IN_PROGRESS)
1346 return (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0;
1348 return 0;
1351 bool BattleGround::HasFreeSlots() const
1353 return GetPlayersSize() < GetMaxPlayers();
1356 void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value)
1358 //this procedure is called from virtual function implemented in bg subclass
1359 BattleGroundScoreMap::const_iterator itr = m_PlayerScores.find(Source->GetGUID());
1361 if(itr == m_PlayerScores.end()) // player not found...
1362 return;
1364 switch(type)
1366 case SCORE_KILLING_BLOWS: // Killing blows
1367 itr->second->KillingBlows += value;
1368 break;
1369 case SCORE_DEATHS: // Deaths
1370 itr->second->Deaths += value;
1371 break;
1372 case SCORE_HONORABLE_KILLS: // Honorable kills
1373 itr->second->HonorableKills += value;
1374 break;
1375 case SCORE_BONUS_HONOR: // Honor bonus
1376 // do not add honor in arenas
1377 if (isBattleGround())
1379 // reward honor instantly
1380 if (Source->RewardHonor(NULL, 1, (float)value))
1381 itr->second->BonusHonor += value;
1383 break;
1384 //used only in EY, but in MSG_PVP_LOG_DATA opcode
1385 case SCORE_DAMAGE_DONE: // Damage Done
1386 itr->second->DamageDone += value;
1387 break;
1388 case SCORE_HEALING_DONE: // Healing Done
1389 itr->second->HealingDone += value;
1390 break;
1391 default:
1392 sLog.outError("BattleGround: Unknown player score type %u", type);
1393 break;
1397 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*/)
1399 // must be created this way, adding to godatamap would add it to the base map of the instance
1400 // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
1401 // so we must create it specific for this instance
1402 GameObject * go = new GameObject;
1403 if(!go->Create(sObjectMgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry, GetBgMap(),
1404 PHASEMASK_NORMAL, x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,GO_STATE_READY))
1406 sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry);
1407 sLog.outError("Cannot create gameobject template %u! BattleGround not created!", entry);
1408 delete go;
1409 return false;
1412 uint32 guid = go->GetGUIDLow();
1414 // without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata
1415 // 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
1416 GameObjectData& data = sObjectMgr.NewGOData(guid);
1418 data.id = entry;
1419 data.mapid = GetMapId();
1420 data.posX = x;
1421 data.posY = y;
1422 data.posZ = z;
1423 data.orientation = o;
1424 data.rotation0 = rotation0;
1425 data.rotation1 = rotation1;
1426 data.rotation2 = rotation2;
1427 data.rotation3 = rotation3;
1428 data.spawntimesecs = respawnTime;
1429 data.spawnMask = 1;
1430 data.animprogress = 100;
1431 data.go_state = 1;
1433 // add to world, so it can be later looked up from HashMapHolder
1434 go->AddToWorld();
1435 m_BgObjects[type] = go->GetGUID();
1436 return true;
1439 //some doors aren't despawned so we cannot handle their closing in gameobject::update()
1440 //it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
1441 void BattleGround::DoorClose(uint64 const& guid)
1443 GameObject *obj = GetBgMap()->GetGameObject(guid);
1444 if (obj)
1446 //if doors are open, close it
1447 if (obj->getLootState() == GO_ACTIVATED && obj->GetGoState() != GO_STATE_READY)
1449 //change state to allow door to be closed
1450 obj->SetLootState(GO_READY);
1451 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1454 else
1456 sLog.outError("BattleGround: Door object not found (cannot close doors)");
1460 void BattleGround::DoorOpen(uint64 const& guid)
1462 GameObject *obj = GetBgMap()->GetGameObject(guid);
1463 if (obj)
1465 //change state to be sure they will be opened
1466 obj->SetLootState(GO_READY);
1467 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1469 else
1471 sLog.outError("BattleGround: Door object not found! - doors will be closed.");
1475 void BattleGround::OnObjectDBLoad(Creature* creature)
1477 const BattleGroundEventIdx eventId = sBattleGroundMgr.GetCreatureEventIndex(creature->GetDBTableGUIDLow());
1478 if (eventId.event1 == BG_EVENT_NONE)
1479 return;
1480 m_EventObjects[MAKE_PAIR32(eventId.event1, eventId.event2)].creatures.push_back(creature->GetGUID());
1481 if (!IsActiveEvent(eventId.event1, eventId.event2))
1482 SpawnBGCreature(creature->GetGUID(), RESPAWN_ONE_DAY);
1485 uint64 BattleGround::GetSingleCreatureGuid(uint8 event1, uint8 event2)
1487 BGCreatures::const_iterator itr = m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.begin();
1488 if (itr != m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.end())
1489 return *itr;
1490 return 0;
1493 void BattleGround::OnObjectDBLoad(GameObject* obj)
1495 const BattleGroundEventIdx eventId = sBattleGroundMgr.GetGameObjectEventIndex(obj->GetDBTableGUIDLow());
1496 if (eventId.event1 == BG_EVENT_NONE)
1497 return;
1498 m_EventObjects[MAKE_PAIR32(eventId.event1, eventId.event2)].gameobjects.push_back(obj->GetGUID());
1499 if (!IsActiveEvent(eventId.event1, eventId.event2))
1501 SpawnBGObject(obj->GetGUID(), RESPAWN_ONE_DAY);
1503 else
1505 // it's possible, that doors aren't spawned anymore (wsg)
1506 if (GetStatus() >= STATUS_IN_PROGRESS && IsDoor(eventId.event1, eventId.event2))
1507 DoorOpen(obj->GetGUID());
1511 bool BattleGround::IsDoor(uint8 event1, uint8 event2)
1513 if (event1 == BG_EVENT_DOOR)
1515 if (event2 > 0)
1517 sLog.outError("BattleGround too high event2 for event1:%i", event1);
1518 return false;
1520 return true;
1522 return false;
1525 void BattleGround::OpenDoorEvent(uint8 event1, uint8 event2 /*=0*/)
1527 if (!IsDoor(event1, event2))
1529 sLog.outError("BattleGround:OpenDoorEvent this is no door event1:%u event2:%u", event1, event2);
1530 return;
1532 if (!IsActiveEvent(event1, event2)) // maybe already despawned (eye)
1534 sLog.outError("BattleGround:OpenDoorEvent this event isn't active event1:%u event2:%u", event1, event2);
1535 return;
1537 BGObjects::const_iterator itr = m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.begin();
1538 for(; itr != m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.end(); ++itr)
1539 DoorOpen(*itr);
1542 void BattleGround::SpawnEvent(uint8 event1, uint8 event2, bool spawn)
1544 // stop if we want to spawn something which was already spawned
1545 // or despawn something which was already despawned
1546 if (event2 == BG_EVENT_NONE || (spawn && m_ActiveEvents[event1] == event2)
1547 || (!spawn && m_ActiveEvents[event1] != event2))
1548 return;
1550 if (spawn)
1552 // if event gets spawned, the current active event mus get despawned
1553 SpawnEvent(event1, m_ActiveEvents[event1], false);
1554 m_ActiveEvents[event1] = event2; // set this event to active
1556 else
1557 m_ActiveEvents[event1] = BG_EVENT_NONE; // no event active if event2 gets despawned
1559 BGCreatures::const_iterator itr = m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.begin();
1560 for(; itr != m_EventObjects[MAKE_PAIR32(event1, event2)].creatures.end(); ++itr)
1561 SpawnBGCreature(*itr, (spawn) ? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY);
1562 BGObjects::const_iterator itr2 = m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.begin();
1563 for(; itr2 != m_EventObjects[MAKE_PAIR32(event1, event2)].gameobjects.end(); ++itr2)
1564 SpawnBGObject(*itr2, (spawn) ? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY);
1567 void BattleGround::SpawnBGObject(uint64 const& guid, uint32 respawntime)
1569 Map* map = GetBgMap();
1571 GameObject *obj = map->GetGameObject(guid);
1572 if(!obj)
1573 return;
1574 if (respawntime == 0)
1576 //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again
1577 if (obj->getLootState() == GO_JUST_DEACTIVATED)
1578 obj->SetLootState(GO_READY);
1579 obj->SetRespawnTime(0);
1580 map->Add(obj);
1582 else
1584 map->Add(obj);
1585 obj->SetRespawnTime(respawntime);
1586 obj->SetLootState(GO_JUST_DEACTIVATED);
1590 void BattleGround::SpawnBGCreature(uint64 const& guid, uint32 respawntime)
1592 Map* map = GetBgMap();
1594 Creature* obj = map->GetCreature(guid);
1595 if (!obj)
1596 return;
1597 if (respawntime == 0)
1599 obj->Respawn();
1600 map->Add(obj);
1602 else
1604 map->Add(obj);
1605 obj->setDeathState(JUST_DIED);
1606 obj->SetRespawnDelay(respawntime);
1607 obj->RemoveCorpse();
1611 bool BattleGround::DelObject(uint32 type)
1613 if (!m_BgObjects[type])
1614 return true;
1616 GameObject *obj = GetBgMap()->GetGameObject(m_BgObjects[type]);
1617 if (!obj)
1619 sLog.outError("Can't find gobject guid: %u",GUID_LOPART(m_BgObjects[type]));
1620 return false;
1623 obj->SetRespawnTime(0); // not save respawn time
1624 obj->Delete();
1625 m_BgObjects[type] = 0;
1626 return true;
1629 void BattleGround::SendMessageToAll(int32 entry, ChatMsg type, Player const* source)
1631 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source);
1632 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1633 BroadcastWorker(bg_do);
1636 void BattleGround::SendYellToAll(int32 entry, uint32 language, uint64 const& guid)
1638 Creature* source = GetBgMap()->GetCreature(guid);
1639 if(!source)
1640 return;
1641 MaNGOS::BattleGroundYellBuilder bg_builder(language, entry, source);
1642 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundYellBuilder> bg_do(bg_builder);
1643 BroadcastWorker(bg_do);
1646 void BattleGround::PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...)
1648 va_list ap;
1649 va_start(ap, source);
1651 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source, &ap);
1652 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1653 BroadcastWorker(bg_do);
1655 va_end(ap);
1658 void BattleGround::SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 arg1, int32 arg2)
1660 MaNGOS::BattleGround2ChatBuilder bg_builder(type, entry, source, arg1, arg2);
1661 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGround2ChatBuilder> bg_do(bg_builder);
1662 BroadcastWorker(bg_do);
1665 void BattleGround::SendYell2ToAll(int32 entry, uint32 language, uint64 const& guid, int32 arg1, int32 arg2)
1667 Creature* source = GetBgMap()->GetCreature(guid);
1668 if(!source)
1669 return;
1670 MaNGOS::BattleGround2YellBuilder bg_builder(language, entry, source, arg1, arg2);
1671 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGround2YellBuilder> bg_do(bg_builder);
1672 BroadcastWorker(bg_do);
1675 void BattleGround::EndNow()
1677 RemoveFromBGFreeSlotQueue();
1678 SetStatus(STATUS_WAIT_LEAVE);
1679 SetEndTime(0);
1683 important notice:
1684 buffs aren't spawned/despawned when players captures anything
1685 buffs are in their positions when battleground starts
1687 void BattleGround::HandleTriggerBuff(uint64 const& go_guid)
1689 GameObject *obj = GetBgMap()->GetGameObject(go_guid);
1690 if (!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned())
1691 return;
1693 // static buffs are already handled just by database and don't need
1694 // battleground code
1695 if (!m_BuffChange)
1697 obj->SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
1698 return;
1701 // change buff type, when buff is used:
1702 // TODO this can be done when poolsystem works for instances
1703 int32 index = m_BgObjects.size() - 1;
1704 while (index >= 0 && m_BgObjects[index] != go_guid)
1705 index--;
1706 if (index < 0)
1708 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());
1709 return;
1712 //randomly select new buff
1713 uint8 buff = urand(0, 2);
1714 uint32 entry = obj->GetEntry();
1715 if (m_BuffChange && entry != Buff_Entries[buff])
1717 //despawn current buff
1718 SpawnBGObject(m_BgObjects[index], RESPAWN_ONE_DAY);
1719 //set index for new one
1720 for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex)
1722 if (entry == Buff_Entries[currBuffTypeIndex])
1724 index -= currBuffTypeIndex;
1725 index += buff;
1730 SpawnBGObject(m_BgObjects[index], BUFF_RESPAWN_TIME);
1733 void BattleGround::HandleKillPlayer( Player *player, Player *killer )
1735 // add +1 deaths
1736 UpdatePlayerScore(player, SCORE_DEATHS, 1);
1738 // add +1 kills to group and +1 killing_blows to killer
1739 if (killer)
1741 UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1);
1742 UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1);
1744 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1746 Player *plr = sObjectMgr.GetPlayer(itr->first);
1748 if (!plr || plr == killer)
1749 continue;
1751 if (plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player))
1752 UpdatePlayerScore(plr, SCORE_HONORABLE_KILLS, 1);
1756 // to be able to remove insignia -- ONLY IN BattleGrounds
1757 if (!isArena())
1758 player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE );
1761 // return the player's team based on battlegroundplayer info
1762 // used in same faction arena matches mainly
1763 uint32 BattleGround::GetPlayerTeam(uint64 guid)
1765 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1766 if (itr!=m_Players.end())
1767 return itr->second.Team;
1768 return 0;
1771 bool BattleGround::IsPlayerInBattleGround(uint64 guid)
1773 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1774 if (itr != m_Players.end())
1775 return true;
1776 return false;
1779 void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player* plr)
1781 if (GetStatus() != STATUS_WAIT_LEAVE)
1782 return;
1784 WorldPacket data;
1785 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1787 BlockMovement(plr);
1789 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
1790 plr->GetSession()->SendPacket(&data);
1792 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType());
1793 plr->GetSession()->SendPacket(&data);
1796 uint32 BattleGround::GetAlivePlayersCountByTeam(uint32 Team) const
1798 int count = 0;
1799 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1801 if (itr->second.Team == Team)
1803 Player * pl = sObjectMgr.GetPlayer(itr->first);
1804 if (pl && pl->isAlive())
1805 ++count;
1808 return count;
1811 void BattleGround::CheckArenaWinConditions()
1813 if (!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE))
1814 EndBattleGround(HORDE);
1815 else if (GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE))
1816 EndBattleGround(ALLIANCE);
1819 void BattleGround::SetBgRaid( uint32 TeamID, Group *bg_raid )
1821 Group* &old_raid = TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE];
1822 if(old_raid) old_raid->SetBattlegroundGroup(NULL);
1823 if(bg_raid) bg_raid->SetBattlegroundGroup(this);
1824 old_raid = bg_raid;
1827 WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard( Player* player )
1829 return sObjectMgr.GetClosestGraveYard( player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam() );
1832 bool BattleGround::IsTeamScoreInRange(uint32 team, uint32 minScore, uint32 maxScore) const
1834 BattleGroundTeamId team_idx = GetTeamIndexByTeamId(team);
1835 uint32 score = (m_TeamScores[team_idx] < 0) ? 0 : uint32(m_TeamScores[team_idx]);
1836 return score >= minScore && score <= maxScore;
1839 void BattleGround::SetBracket( PvPDifficultyEntry const* bracketEntry )
1841 m_BracketId = bracketEntry->GetBracketId();
1842 SetLevelRange(bracketEntry->minLevel,bracketEntry->maxLevel);