[7607] Improvements in support some generic achievement classes
[AHbot.git] / src / game / BattleGround.cpp
blobb62fd78436c4915acaa06152afdd5eedbd029c2d
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::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>::iterator itr2 = (itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
265 Player *plr = objmgr.GetPlayer(*itr2);
266 if(!plr)
267 continue;
269 if (!sh)
271 sh = ObjectAccessor::GetCreature(*plr, 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>::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::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 " I64FMTD " not found!", itr->first);
472 void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender, bool self)
474 for(BattleGroundPlayerMap::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 " I64FMTD " not found!", 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::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 " I64FMTD " not found!", 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::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 " I64FMTD " not found!", 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::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 " I64FMTD " not found!", 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::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 " I64FMTD " not found!", 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 " I64FMTD " not found!", 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)
700 winner_arena_team->MemberWon(plr,loser_rating);
701 else
702 loser_arena_team->MemberLost(plr,winner_rating);
705 if(team == winner)
707 RewardMark(plr,ITEM_WINNER_COUNT);
708 RewardQuest(plr);
710 else
712 RewardMark(plr,ITEM_LOSER_COUNT);
715 plr->CombatStopWithPets(true);
717 BlockMovement(plr);
719 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
720 plr->GetSession()->SendPacket(&data);
722 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
723 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType());
724 plr->GetSession()->SendPacket(&data);
725 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1);
728 if(isArena() && isRated() && winner_arena_team && loser_arena_team)
730 // update arena points only after increasing the player's match count!
731 //obsolete: winner_arena_team->UpdateArenaPointsHelper();
732 //obsolete: loser_arena_team->UpdateArenaPointsHelper();
733 // save the stat changes
734 winner_arena_team->SaveToDB();
735 loser_arena_team->SaveToDB();
736 // send updated arena team stats to players
737 // this way all arena team members will get notified, not only the ones who participated in this match
738 winner_arena_team->NotifyStatsChanged();
739 loser_arena_team->NotifyStatsChanged();
742 // inform invited players about the removal
743 sBattleGroundMgr.m_BattleGroundQueues[BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType())].BGEndedRemoveInvites(this);
745 if(winmsg_id)
746 SendMessageToAll(winmsg_id, CHAT_MSG_BG_SYSTEM_NEUTRAL);
749 uint32 BattleGround::GetBonusHonorFromKill(uint32 kills) const
751 //variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill)
752 return MaNGOS::Honor::hk_honor_at_level(GetMaxLevel(), kills);
755 uint32 BattleGround::GetBattlemasterEntry() const
757 switch(GetTypeID())
759 case BATTLEGROUND_AV: return 15972;
760 case BATTLEGROUND_WS: return 14623;
761 case BATTLEGROUND_AB: return 14879;
762 case BATTLEGROUND_EY: return 22516;
763 case BATTLEGROUND_NA: return 20200;
764 default: return 0;
768 void BattleGround::RewardMark(Player *plr,uint32 count)
770 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
771 if(plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
772 return;
774 BattleGroundMarks mark;
775 bool IsSpell;
776 switch(GetTypeID())
778 case BATTLEGROUND_AV:
779 IsSpell = true;
780 if(count == ITEM_WINNER_COUNT)
781 mark = SPELL_AV_MARK_WINNER;
782 else
783 mark = SPELL_AV_MARK_LOSER;
784 break;
785 case BATTLEGROUND_WS:
786 IsSpell = true;
787 if(count == ITEM_WINNER_COUNT)
788 mark = SPELL_WS_MARK_WINNER;
789 else
790 mark = SPELL_WS_MARK_LOSER;
791 break;
792 case BATTLEGROUND_AB:
793 IsSpell = true;
794 if(count == ITEM_WINNER_COUNT)
795 mark = SPELL_AB_MARK_WINNER;
796 else
797 mark = SPELL_AB_MARK_LOSER;
798 break;
799 case BATTLEGROUND_EY:
800 IsSpell = false;
801 mark = ITEM_EY_MARK_OF_HONOR;
802 break;
803 default:
804 return;
807 if(IsSpell)
808 plr->CastSpell(plr, mark, true);
809 else if ( objmgr.GetItemPrototype( mark ) )
811 ItemPosCountVec dest;
812 uint32 no_space_count = 0;
813 uint8 msg = plr->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, mark, count, &no_space_count );
814 if( msg != EQUIP_ERR_OK ) // convert to possible store amount
815 count -= no_space_count;
817 if( count != 0 && !dest.empty()) // can add some
818 if(Item* item = plr->StoreNewItem( dest, mark, true, 0))
819 plr->SendNewItem(item,count,false,true);
821 if(no_space_count > 0)
822 SendRewardMarkByMail(plr,mark,no_space_count);
826 void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count)
828 uint32 bmEntry = GetBattlemasterEntry();
829 if(!bmEntry)
830 return;
832 ItemPrototype const* markProto = objmgr.GetItemPrototype(mark);
833 if(!markProto)
834 return;
836 if(Item* markItem = Item::CreateItem(mark,count,plr))
838 // save new item before send
839 markItem->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
841 // item
842 MailItemsInfo mi;
843 mi.AddItem(markItem->GetGUIDLow(), markItem->GetEntry(), markItem);
845 // subject: item name
846 std::string subject = markProto->Name1;
847 int loc_idx = plr->GetSession()->GetSessionDbLocaleIndex();
848 if ( loc_idx >= 0 )
849 if(ItemLocale const *il = objmgr.GetItemLocale(markProto->ItemId))
850 if (il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty())
851 subject = il->Name[loc_idx];
853 // text
854 std::string textFormat = plr->GetSession()->GetMangosString(LANG_BG_MARK_BY_MAIL);
855 char textBuf[300];
856 snprintf(textBuf,300,textFormat.c_str(),GetName(),GetName());
857 uint32 itemTextId = objmgr.CreateItemText( textBuf );
859 WorldSession::SendMailTo(plr, MAIL_CREATURE, MAIL_STATIONERY_NORMAL, bmEntry, plr->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE);
863 void BattleGround::RewardQuest(Player *plr)
865 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
866 if(plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
867 return;
869 uint32 quest;
870 switch(GetTypeID())
872 case BATTLEGROUND_AV:
873 quest = SPELL_AV_QUEST_REWARD;
874 break;
875 case BATTLEGROUND_WS:
876 quest = SPELL_WS_QUEST_REWARD;
877 break;
878 case BATTLEGROUND_AB:
879 quest = SPELL_AB_QUEST_REWARD;
880 break;
881 case BATTLEGROUND_EY:
882 quest = SPELL_EY_QUEST_REWARD;
883 break;
884 default:
885 return;
888 plr->CastSpell(plr, quest, true);
891 void BattleGround::BlockMovement(Player *plr)
893 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()
896 void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket)
898 uint32 team = GetPlayerTeam(guid);
899 bool participant = false;
900 // Remove from lists/maps
901 BattleGroundPlayerMap::iterator itr = m_Players.find(guid);
902 if(itr != m_Players.end())
904 UpdatePlayersCountByTeam(team, true); // -1 player
905 m_Players.erase(itr);
906 // check if the player was a participant of the match, or only entered through gm command (goname)
907 participant = true;
910 std::map<uint64, BattleGroundScore*>::iterator itr2 = m_PlayerScores.find(guid);
911 if(itr2 != m_PlayerScores.end())
913 delete itr2->second; // delete player's score
914 m_PlayerScores.erase(itr2);
917 RemovePlayerFromResurrectQueue(guid);
919 Player *plr = objmgr.GetPlayer(guid);
921 // should remove spirit of redemption
922 if(plr && plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
923 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
925 if(plr && !plr->isAlive()) // resurrect on exit
927 plr->ResurrectPlayer(1.0f);
928 plr->SpawnCorpseBones();
931 RemovePlayer(plr, guid); // BG subclass specific code
933 if(participant) // if the player was a match participant, remove auras, calc rating, update queue
935 BattleGroundTypeId bgTypeId = GetTypeID();
936 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
937 if(plr)
939 plr->ClearAfkReports();
941 if(!team) team = plr->GetTeam();
943 // if arena, remove the specific arena auras
944 if(isArena())
946 plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out
947 bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing)
949 // summon old pet if there was one and there isn't a current pet
950 if(!plr->GetPet() && plr->GetTemporaryUnsummonedPetNumber())
952 Pet* NewPet = new Pet;
953 if(!NewPet->LoadPetFromDB(plr, 0, (plr)->GetTemporaryUnsummonedPetNumber(), true))
954 delete NewPet;
956 (plr)->SetTemporaryUnsummonedPetNumber(0);
959 if(isRated() && GetStatus() == STATUS_IN_PROGRESS)
961 //left a rated match while the encounter was in progress, consider as loser
962 ArenaTeam * winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
963 ArenaTeam * loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
964 if(winner_arena_team && loser_arena_team)
965 loser_arena_team->MemberLost(plr,winner_arena_team->GetRating());
968 if(SendPacket)
970 WorldPacket data;
971 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0, 0);
972 plr->GetSession()->SendPacket(&data);
975 // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg
976 plr->RemoveBattleGroundQueueId(bgQueueTypeId);
978 else
979 // removing offline participant
981 if(isRated() && GetStatus() == STATUS_IN_PROGRESS)
983 //left a rated match while the encounter was in progress, consider as loser
984 ArenaTeam * others_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
985 ArenaTeam * players_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
986 if( others_arena_team && players_arena_team )
987 players_arena_team->OfflineMemberLost(guid, others_arena_team->GetRating());
991 // remove from raid group if player is member
992 if(Group *group = GetBgRaid(team))
994 if( !group->RemoveMember(guid, 0) ) // group was disbanded
996 SetBgRaid(team, NULL);
997 delete group;
1000 DecreaseInvitedCount(team);
1001 //we should update battleground queue, but only if bg isn't ending
1002 if( isBattleGround() && GetStatus() < STATUS_WAIT_LEAVE )
1003 sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, GetQueueId());
1004 // Let others know
1005 WorldPacket data;
1006 sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, guid);
1007 SendPacketToTeam(team, &data, plr, false);
1010 if( plr )
1012 // Do next only if found in battleground
1013 plr->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG.
1014 // reset destination bg team
1015 plr->SetBGTeam(0);
1017 if(Transport)
1018 plr->TeleportTo(plr->GetBattleGroundEntryPoint());
1020 sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName());
1023 if(!GetPlayersSize() && !GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
1025 // if no players left AND no invitees left, set this bg to delete in next update
1026 // direct deletion could cause crashes
1027 m_SetDeleteThis = true;
1028 // return to prevent addition to freeslotqueue
1029 return;
1032 // a player exited the battleground, so there are free slots. add to queue
1033 this->AddToBGFreeSlotQueue();
1036 // this method is called when no players remains in battleground
1037 void BattleGround::Reset()
1039 SetQueueId(QUEUE_ID_MAX_LEVEL_19);
1040 SetWinner(WINNER_NONE);
1041 SetStatus(STATUS_WAIT_QUEUE);
1042 SetStartTime(0);
1043 SetEndTime(0);
1044 SetLastResurrectTime(0);
1045 SetArenaType(0);
1046 SetRated(false);
1048 m_Events = 0;
1050 if (m_InvitedAlliance > 0 || m_InvitedHorde > 0)
1051 sLog.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde);
1053 m_InvitedAlliance = 0;
1054 m_InvitedHorde = 0;
1055 m_InBGFreeSlotQueue = false;
1057 m_Players.clear();
1058 m_PlayerScores.clear();
1061 void BattleGround::StartBattleGround()
1063 ///this method should spawn spirit guides and so on
1064 SetStartTime(0);
1066 SetLastResurrectTime(0);
1069 void BattleGround::AddPlayer(Player *plr)
1071 // score struct must be created in inherited class
1073 uint64 guid = plr->GetGUID();
1074 uint32 team = plr->GetBGTeam();
1076 BattleGroundPlayer bp;
1077 bp.OfflineRemoveTime = 0;
1078 bp.Team = team;
1080 // Add to list/maps
1081 m_Players[guid] = bp;
1083 UpdatePlayersCountByTeam(team, false); // +1 player
1085 WorldPacket data;
1086 sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr);
1087 SendPacketToTeam(team, &data, plr, false);
1089 // add arena specific auras
1090 if(isArena())
1092 plr->RemoveArenaSpellCooldowns();
1093 plr->RemoveArenaAuras();
1094 plr->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT);
1095 if(team == ALLIANCE) // gold
1097 if(plr->GetTeam() == HORDE)
1098 plr->CastSpell(plr, SPELL_HORDE_GOLD_FLAG,true);
1099 else
1100 plr->CastSpell(plr, SPELL_ALLIANCE_GOLD_FLAG,true);
1102 else // green
1104 if(plr->GetTeam() == HORDE)
1105 plr->CastSpell(plr, SPELL_HORDE_GREEN_FLAG,true);
1106 else
1107 plr->CastSpell(plr, SPELL_ALLIANCE_GREEN_FLAG,true);
1110 plr->DestroyConjuredItems(true);
1112 Pet* pet = plr->GetPet();
1113 if(pet)
1115 if(pet->getPetType() == SUMMON_PET || pet->getPetType() == HUNTER_PET)
1117 (plr)->SetTemporaryUnsummonedPetNumber(pet->GetCharmInfo()->GetPetNumber());
1118 (plr)->SetOldPetSpell(pet->GetUInt32Value(UNIT_CREATED_BY_SPELL));
1120 (plr)->RemovePet(NULL,PET_SAVE_NOT_IN_SLOT);
1122 else
1123 (plr)->SetTemporaryUnsummonedPetNumber(0);
1125 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1127 plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true);
1129 plr->SetHealth(plr->GetMaxHealth());
1130 plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA));
1133 else
1135 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1136 plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells.
1139 // setup BG group membership
1140 PlayerAddedToBGCheckIfBGIsRunning(plr);
1141 AddOrSetPlayerToCorrectBgGroup(plr, guid, team);
1143 // Log
1144 sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName());
1147 /* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */
1148 void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team)
1150 Group* group = GetBgRaid(team);
1151 if(!group) // first player joined
1153 group = new Group;
1154 SetBgRaid(team, group);
1155 group->Create(plr_guid, plr->GetName());
1157 else // raid already exist
1159 if(group->IsMember(plr_guid))
1161 uint8 subgroup = group->GetMemberGroup(plr_guid);
1162 plr->SetBattleGroundRaid(group, subgroup);
1164 else
1166 group->AddMember(plr_guid, plr->GetName());
1167 if( Group* originalGroup = plr->GetOriginalGroup() )
1168 if( originalGroup->IsLeader(plr_guid) )
1169 group->ChangeLeader(plr_guid);
1174 // This method should be called when player logs into running battleground
1175 void BattleGround::EventPlayerLoggedIn(Player* player, uint64 plr_guid)
1177 // player is correct pointer
1178 for(std::deque<uint64>::iterator itr = m_OfflineQueue.begin(); itr != m_OfflineQueue.end(); ++itr)
1180 if( *itr == plr_guid )
1182 m_OfflineQueue.erase(itr);
1183 break;
1186 m_Players[plr_guid].OfflineRemoveTime = 0;
1187 PlayerAddedToBGCheckIfBGIsRunning(player);
1188 // if battleground is starting, then add preparation aura
1189 // we don't have to do that, because preparation aura isn't removed when player logs out
1192 // This method should be called when player logs out from running battleground
1193 void BattleGround::EventPlayerLoggedOut(Player* player)
1195 // player is correct pointer, it is checked in WorldSession::LogoutPlayer()
1196 m_OfflineQueue.push_back(player->GetGUID());
1197 m_Players[player->GetGUID()].OfflineRemoveTime = sWorld.GetGameTime() + MAX_OFFLINE_TIME;
1198 if( GetStatus() == STATUS_IN_PROGRESS )
1200 if( isBattleGround() )
1201 EventPlayerDroppedFlag(player);
1202 else
1204 //1 player is logging out, if it is the last, then end arena!
1205 if( GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam())) )
1206 EndBattleGround(GetOtherTeam(player->GetTeam()));
1211 /* This method should be called only once ... it adds pointer to queue */
1212 void BattleGround::AddToBGFreeSlotQueue()
1214 // make sure to add only once
1215 if(!m_InBGFreeSlotQueue && isBattleGround())
1217 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this);
1218 m_InBGFreeSlotQueue = true;
1222 /* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/
1223 void BattleGround::RemoveFromBGFreeSlotQueue()
1225 // set to be able to re-add if needed
1226 m_InBGFreeSlotQueue = false;
1227 // uncomment this code when battlegrounds will work like instances
1228 for (BGFreeSlotQueueType::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr)
1230 if ((*itr)->GetInstanceID() == m_InstanceID)
1232 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr);
1233 return;
1238 // get the number of free slots for team
1239 // returns the number how many players can join battleground to MaxPlayersPerTeam
1240 uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const
1242 //return free slot count to MaxPlayerPerTeam
1243 if (GetStatus() == STATUS_WAIT_JOIN || GetStatus() == STATUS_IN_PROGRESS)
1244 return (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0;
1246 return 0;
1249 bool BattleGround::HasFreeSlots() const
1251 return GetPlayersSize() < GetMaxPlayers();
1254 void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value)
1256 //this procedure is called from virtual function implemented in bg subclass
1257 std::map<uint64, BattleGroundScore*>::iterator itr = m_PlayerScores.find(Source->GetGUID());
1259 if(itr == m_PlayerScores.end()) // player not found...
1260 return;
1262 switch(type)
1264 case SCORE_KILLING_BLOWS: // Killing blows
1265 itr->second->KillingBlows += value;
1266 break;
1267 case SCORE_DEATHS: // Deaths
1268 itr->second->Deaths += value;
1269 break;
1270 case SCORE_HONORABLE_KILLS: // Honorable kills
1271 itr->second->HonorableKills += value;
1272 break;
1273 case SCORE_BONUS_HONOR: // Honor bonus
1274 // do not add honor in arenas
1275 if(isBattleGround())
1277 // reward honor instantly
1278 if(Source->RewardHonor(NULL, 1, value))
1279 itr->second->BonusHonor += value;
1281 break;
1282 //used only in EY, but in MSG_PVP_LOG_DATA opcode
1283 case SCORE_DAMAGE_DONE: // Damage Done
1284 itr->second->DamageDone += value;
1285 break;
1286 case SCORE_HEALING_DONE: // Healing Done
1287 itr->second->HealingDone += value;
1288 break;
1289 default:
1290 sLog.outError("BattleGround: Unknown player score type %u", type);
1291 break;
1295 void BattleGround::AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid)
1297 m_ReviveQueue[npc_guid].push_back(player_guid);
1299 Player *plr = objmgr.GetPlayer(player_guid);
1300 if(!plr)
1301 return;
1303 plr->CastSpell(plr, SPELL_WAITING_FOR_RESURRECT, true);
1304 SpellEntry const *spellInfo = sSpellStore.LookupEntry( SPELL_WAITING_FOR_RESURRECT );
1305 if(spellInfo)
1307 Aura *Aur = CreateAura(spellInfo, 0, NULL, plr);
1308 plr->AddAura(Aur);
1312 void BattleGround::RemovePlayerFromResurrectQueue(uint64 player_guid)
1314 for(std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
1316 for(std::vector<uint64>::iterator itr2 =(itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
1318 if(*itr2 == player_guid)
1320 (itr->second).erase(itr2);
1322 Player *plr = objmgr.GetPlayer(player_guid);
1323 if(!plr)
1324 return;
1326 plr->RemoveAurasDueToSpell(SPELL_WAITING_FOR_RESURRECT);
1328 return;
1334 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)
1336 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
1337 if(!map)
1338 return false;
1340 // must be created this way, adding to godatamap would add it to the base map of the instance
1341 // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
1342 // so we must create it specific for this instance
1343 GameObject * go = new GameObject;
1344 if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry, map,
1345 PHASEMASK_NORMAL, x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,1))
1347 sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry);
1348 sLog.outError("Cannot create gameobject template %u! BattleGround not created!", entry);
1349 delete go;
1350 return false;
1353 uint32 guid = go->GetGUIDLow();
1355 // without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata
1356 // 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
1357 GameObjectData& data = objmgr.NewGOData(guid);
1359 data.id = entry;
1360 data.mapid = GetMapId();
1361 data.posX = x;
1362 data.posY = y;
1363 data.posZ = z;
1364 data.orientation = o;
1365 data.rotation0 = rotation0;
1366 data.rotation1 = rotation1;
1367 data.rotation2 = rotation2;
1368 data.rotation3 = rotation3;
1369 data.spawntimesecs = respawnTime;
1370 data.spawnMask = 1;
1371 data.animprogress = 100;
1372 data.go_state = 1;
1374 // add to world, so it can be later looked up from HashMapHolder
1375 go->AddToWorld();
1376 m_BgObjects[type] = go->GetGUID();
1377 return true;
1380 //some doors aren't despawned so we cannot handle their closing in gameobject::update()
1381 //it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
1382 void BattleGround::DoorClose(uint32 type)
1384 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1385 if(obj)
1387 //if doors are open, close it
1388 if( obj->getLootState() == GO_ACTIVATED && !obj->GetGoState() )
1390 //change state to allow door to be closed
1391 obj->SetLootState(GO_READY);
1392 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1395 else
1397 sLog.outError("BattleGround: Door object not found (cannot close doors)");
1401 void BattleGround::DoorOpen(uint32 type)
1403 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1404 if(obj)
1406 //change state to be sure they will be opened
1407 obj->SetLootState(GO_READY);
1408 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1410 else
1412 sLog.outError("BattleGround: Door object not found! - doors will be closed.");
1416 void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime)
1418 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
1419 if(!map)
1420 return;
1421 if( respawntime == 0 )
1423 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1424 if(obj)
1426 //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again
1427 if( obj->getLootState() == GO_JUST_DEACTIVATED )
1428 obj->SetLootState(GO_READY);
1429 obj->SetRespawnTime(0);
1430 map->Add(obj);
1433 else
1435 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1436 if(obj)
1438 map->Add(obj);
1439 obj->SetRespawnTime(respawntime);
1440 obj->SetLootState(GO_JUST_DEACTIVATED);
1445 Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o, uint32 respawntime)
1447 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
1448 if(!map)
1449 return NULL;
1451 Creature* pCreature = new Creature;
1452 if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, PHASEMASK_NORMAL, entry, teamval))
1454 sLog.outError("Can't create creature entry: %u",entry);
1455 delete pCreature;
1456 return NULL;
1459 pCreature->Relocate(x, y, z, o);
1461 if(!pCreature->IsPositionValid())
1463 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());
1464 return NULL;
1467 pCreature->AIM_Initialize();
1469 //pCreature->SetDungeonDifficulty(0);
1471 map->Add(pCreature);
1472 m_BgCreatures[type] = pCreature->GetGUID();
1474 return pCreature;
1477 void BattleGround::SpawnBGCreature(uint32 type, uint32 respawntime)
1479 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceId());
1480 if(!map)
1481 return false;
1483 if(respawntime == 0)
1485 Creature *obj = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1486 if(obj)
1488 //obj->Respawn(); // bugged
1489 obj->SetRespawnTime(0);
1490 objmgr.SaveCreatureRespawnTime(obj->GetGUIDLow(), GetInstanceID(), 0);
1491 map->Add(obj);
1494 else
1496 Creature *obj = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1497 if(obj)
1499 obj->setDeathState(DEAD);
1500 obj->SetRespawnTime(respawntime);
1501 map->Add(obj);
1506 bool BattleGround::DelCreature(uint32 type)
1508 if(!m_BgCreatures[type])
1509 return true;
1511 Creature *cr = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1512 if(!cr)
1514 sLog.outError("Can't find creature guid: %u",GUID_LOPART(m_BgCreatures[type]));
1515 return false;
1517 cr->CleanupsBeforeDelete();
1518 cr->AddObjectToRemoveList();
1519 m_BgCreatures[type] = 0;
1520 return true;
1523 bool BattleGround::DelObject(uint32 type)
1525 if(!m_BgObjects[type])
1526 return true;
1528 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1529 if(!obj)
1531 sLog.outError("Can't find gobject guid: %u",GUID_LOPART(m_BgObjects[type]));
1532 return false;
1534 obj->SetRespawnTime(0); // not save respawn time
1535 obj->Delete();
1536 m_BgObjects[type] = 0;
1537 return true;
1540 bool BattleGround::AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team)
1542 uint32 entry = 0;
1544 if(team == ALLIANCE)
1545 entry = 13116;
1546 else
1547 entry = 13117;
1549 Creature* pCreature = AddCreature(entry,type,team,x,y,z,o);
1550 if(!pCreature)
1552 sLog.outError("Can't create Spirit guide. BattleGround not created!");
1553 EndNow();
1554 return false;
1557 pCreature->setDeathState(DEAD);
1559 pCreature->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, pCreature->GetGUID());
1560 // aura
1561 pCreature->SetVisibleAura(0, SPELL_SPIRIT_HEAL_CHANNEL);
1562 //pCreature->SetUInt32Value(UNIT_FIELD_AURAFLAGS, 0x00000009);
1563 //pCreature->SetUInt32Value(UNIT_FIELD_AURALEVELS, 0x0000003C);
1564 //pCreature->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS, 0x000000FF);
1565 // casting visual effect
1566 pCreature->SetUInt32Value(UNIT_CHANNEL_SPELL, SPELL_SPIRIT_HEAL_CHANNEL);
1567 // correct cast speed
1568 pCreature->SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
1570 //pCreature->CastSpell(pCreature, SPELL_SPIRIT_HEAL_CHANNEL, true);
1572 return true;
1575 void BattleGround::SendMessageToAll(int32 entry, ChatMsg type, Player const* source)
1577 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source);
1578 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1579 BroadcastWorker(bg_do);
1582 void BattleGround::PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...)
1584 va_list ap;
1585 va_start(ap, source);
1587 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source, &ap);
1588 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1589 BroadcastWorker(bg_do);
1591 va_end(ap);
1594 void BattleGround::SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 arg1, int32 arg2)
1596 MaNGOS::BattleGround2ChatBuilder bg_builder(type, entry, source, arg1, arg2);
1597 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGround2ChatBuilder> bg_do(bg_builder);
1598 BroadcastWorker(bg_do);
1601 void BattleGround::EndNow()
1603 RemoveFromBGFreeSlotQueue();
1604 SetStatus(STATUS_WAIT_LEAVE);
1605 SetEndTime(0);
1606 // inform invited players about the removal
1607 sBattleGroundMgr.m_BattleGroundQueues[BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType())].BGEndedRemoveInvites(this);
1611 important notice:
1612 buffs aren't spawned/despawned when players captures anything
1613 buffs are in their positions when battleground starts
1615 void BattleGround::HandleTriggerBuff(uint64 const& go_guid)
1617 GameObject *obj = HashMapHolder<GameObject>::Find(go_guid);
1618 if(!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned())
1619 return;
1621 //change buff type, when buff is used:
1622 int32 index = m_BgObjects.size() - 1;
1623 while (index >= 0 && m_BgObjects[index] != go_guid)
1624 index--;
1625 if (index < 0)
1627 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());
1628 return;
1631 //randomly select new buff
1632 uint8 buff = urand(0, 2);
1633 uint32 entry = obj->GetEntry();
1634 if( m_BuffChange && entry != Buff_Entries[buff] )
1636 //despawn current buff
1637 SpawnBGObject(index, RESPAWN_ONE_DAY);
1638 //set index for new one
1639 for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex)
1640 if( entry == Buff_Entries[currBuffTypeIndex] )
1642 index -= currBuffTypeIndex;
1643 index += buff;
1647 SpawnBGObject(index, BUFF_RESPAWN_TIME);
1650 void BattleGround::HandleKillPlayer( Player *player, Player *killer )
1652 //keep in mind that for arena this will have to be changed a bit
1654 // add +1 deaths
1655 UpdatePlayerScore(player, SCORE_DEATHS, 1);
1657 // add +1 kills to group and +1 killing_blows to killer
1658 if( killer )
1660 UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1);
1661 UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1);
1663 for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1665 Player *plr = objmgr.GetPlayer(itr->first);
1667 if(!plr || plr == killer)
1668 continue;
1670 if( plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player) )
1671 UpdatePlayerScore(plr, SCORE_HONORABLE_KILLS, 1);
1675 // to be able to remove insignia -- ONLY IN BattleGrounds
1676 if( !isArena() )
1677 player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE );
1680 // return the player's team based on battlegroundplayer info
1681 // used in same faction arena matches mainly
1682 uint32 BattleGround::GetPlayerTeam(uint64 guid)
1684 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1685 if(itr!=m_Players.end())
1686 return itr->second.Team;
1687 return 0;
1690 uint32 BattleGround::GetOtherTeam(uint32 teamId)
1692 return (teamId) ? ((teamId == ALLIANCE) ? HORDE : ALLIANCE) : 0;
1695 bool BattleGround::IsPlayerInBattleGround(uint64 guid)
1697 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1698 if(itr != m_Players.end())
1699 return true;
1700 return false;
1703 void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player* plr)
1705 if(GetStatus() != STATUS_WAIT_LEAVE)
1706 return;
1708 WorldPacket data;
1709 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1711 BlockMovement(plr);
1713 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
1714 plr->GetSession()->SendPacket(&data);
1716 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType());
1717 plr->GetSession()->SendPacket(&data);
1720 uint32 BattleGround::GetAlivePlayersCountByTeam(uint32 Team) const
1722 int count = 0;
1723 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1725 if(itr->second.Team == Team)
1727 Player * pl = objmgr.GetPlayer(itr->first);
1728 if(pl && pl->isAlive())
1729 ++count;
1732 return count;
1735 void BattleGround::CheckArenaWinConditions()
1737 if( !GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE) )
1738 EndBattleGround(HORDE);
1739 else if( GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE) )
1740 EndBattleGround(ALLIANCE);
1743 void BattleGround::SetBgRaid( uint32 TeamID, Group *bg_raid )
1745 Group* &old_raid = TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE];
1746 if(old_raid) old_raid->SetBattlegroundGroup(NULL);
1747 if(bg_raid) bg_raid->SetBattlegroundGroup(this);
1748 old_raid = bg_raid;
1751 WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard( Player* player )
1753 return objmgr.GetClosestGraveYard( player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam() );