[8449] Deprecate healing/damage item mods and merge internal data in to spell power.
[getmangos.git] / src / game / BattleGround.cpp
blobd40078c5a6335d1733056abfda773a0d80d00040
1 /*
2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "Object.h"
20 #include "Player.h"
21 #include "BattleGround.h"
22 #include "BattleGroundMgr.h"
23 #include "Creature.h"
24 #include "MapManager.h"
25 #include "Language.h"
26 #include "SpellAuras.h"
27 #include "ArenaTeam.h"
28 #include "World.h"
29 #include "Group.h"
30 #include "ObjectMgr.h"
31 #include "WorldPacket.h"
32 #include "Util.h"
33 #include "Formulas.h"
34 #include "GridNotifiersImpl.h"
36 namespace MaNGOS
38 class BattleGroundChatBuilder
40 public:
41 BattleGroundChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, va_list* args = NULL)
42 : i_msgtype(msgtype), i_textId(textId), i_source(source), i_args(args) {}
43 void operator()(WorldPacket& data, int32 loc_idx)
45 char const* text = objmgr.GetMangosString(i_textId,loc_idx);
47 if (i_args)
49 // we need copy va_list before use or original va_list will corrupted
50 va_list ap;
51 va_copy(ap,*i_args);
53 char str [2048];
54 vsnprintf(str,2048,text, ap );
55 va_end(ap);
57 do_helper(data,&str[0]);
59 else
60 do_helper(data,text);
62 private:
63 void do_helper(WorldPacket& data, char const* text)
65 uint64 target_guid = i_source ? i_source ->GetGUID() : 0;
67 data << uint8(i_msgtype);
68 data << uint32(LANG_UNIVERSAL);
69 data << uint64(target_guid); // there 0 for BG messages
70 data << uint32(0); // can be chat msg group or something
71 data << uint64(target_guid);
72 data << uint32(strlen(text)+1);
73 data << text;
74 data << uint8(i_source ? i_source->chatTag() : uint8(0));
77 ChatMsg i_msgtype;
78 int32 i_textId;
79 Player const* i_source;
80 va_list* i_args;
83 class BattleGround2ChatBuilder
85 public:
86 BattleGround2ChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, int32 arg1, int32 arg2)
87 : i_msgtype(msgtype), i_textId(textId), i_source(source), i_arg1(arg1), i_arg2(arg2) {}
88 void operator()(WorldPacket& data, int32 loc_idx)
90 char const* text = objmgr.GetMangosString(i_textId,loc_idx);
91 char const* arg1str = i_arg1 ? objmgr.GetMangosString(i_arg1,loc_idx) : "";
92 char const* arg2str = i_arg2 ? objmgr.GetMangosString(i_arg2,loc_idx) : "";
94 char str [2048];
95 snprintf(str,2048,text, arg1str, arg2str );
97 uint64 target_guid = i_source ? i_source ->GetGUID() : 0;
99 data << uint8(i_msgtype);
100 data << uint32(LANG_UNIVERSAL);
101 data << uint64(target_guid); // there 0 for BG messages
102 data << uint32(0); // can be chat msg group or something
103 data << uint64(target_guid);
104 data << uint32(strlen(str)+1);
105 data << str;
106 data << uint8(i_source ? i_source->chatTag() : uint8(0));
108 private:
110 ChatMsg i_msgtype;
111 int32 i_textId;
112 Player const* i_source;
113 int32 i_arg1;
114 int32 i_arg2;
116 } // namespace MaNGOS
118 template<class Do>
119 void BattleGround::BroadcastWorker(Do& _do)
121 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
122 if (Player *plr = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER)))
123 _do(plr);
126 BattleGround::BattleGround()
128 m_TypeID = BattleGroundTypeId(0);
129 m_InstanceID = 0;
130 m_Status = STATUS_NONE;
131 m_ClientInstanceID = 0;
132 m_EndTime = 0;
133 m_LastResurrectTime = 0;
134 m_QueueId = QUEUE_ID_MAX_LEVEL_19;
135 m_InvitedAlliance = 0;
136 m_InvitedHorde = 0;
137 m_ArenaType = 0;
138 m_IsArena = false;
139 m_Winner = 2;
140 m_StartTime = 0;
141 m_Events = 0;
142 m_IsRated = false;
143 m_BuffChange = false;
144 m_Name = "";
145 m_LevelMin = 0;
146 m_LevelMax = 0;
147 m_InBGFreeSlotQueue = false;
148 m_SetDeleteThis = false;
150 m_MaxPlayersPerTeam = 0;
151 m_MaxPlayers = 0;
152 m_MinPlayersPerTeam = 0;
153 m_MinPlayers = 0;
155 m_MapId = 0;
157 m_TeamStartLocX[BG_TEAM_ALLIANCE] = 0;
158 m_TeamStartLocX[BG_TEAM_HORDE] = 0;
160 m_TeamStartLocY[BG_TEAM_ALLIANCE] = 0;
161 m_TeamStartLocY[BG_TEAM_HORDE] = 0;
163 m_TeamStartLocZ[BG_TEAM_ALLIANCE] = 0;
164 m_TeamStartLocZ[BG_TEAM_HORDE] = 0;
166 m_TeamStartLocO[BG_TEAM_ALLIANCE] = 0;
167 m_TeamStartLocO[BG_TEAM_HORDE] = 0;
169 m_ArenaTeamIds[BG_TEAM_ALLIANCE] = 0;
170 m_ArenaTeamIds[BG_TEAM_HORDE] = 0;
172 m_ArenaTeamRatingChanges[BG_TEAM_ALLIANCE] = 0;
173 m_ArenaTeamRatingChanges[BG_TEAM_HORDE] = 0;
175 m_BgRaids[BG_TEAM_ALLIANCE] = NULL;
176 m_BgRaids[BG_TEAM_HORDE] = NULL;
178 m_PlayersCount[BG_TEAM_ALLIANCE] = 0;
179 m_PlayersCount[BG_TEAM_HORDE] = 0;
181 m_TeamScores[BG_TEAM_ALLIANCE] = 0;
182 m_TeamScores[BG_TEAM_HORDE] = 0;
184 m_PrematureCountDown = false;
185 m_PrematureCountDown = 0;
187 m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_2M;
188 m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_1M;
189 m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_30S;
190 m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE;
191 //we must set to some default existing values
192 m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_WS_START_TWO_MINUTES;
193 m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE;
194 m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE;
195 m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN;
198 BattleGround::~BattleGround()
200 // remove objects and creatures
201 // (this is done automatically in mapmanager update, when the instance is reset after the reset time)
202 int size = m_BgCreatures.size();
203 for(int i = 0; i < size; ++i)
204 DelCreature(i);
206 size = m_BgObjects.size();
207 for(int i = 0; i < size; ++i)
208 DelObject(i);
210 if (GetInstanceID()) // not spam by useless queries in case BG templates
212 // delete creature and go respawn times
213 WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'",GetInstanceID());
214 WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'",GetInstanceID());
215 // delete instance from db
216 CharacterDatabase.PExecute("DELETE FROM instance WHERE id = '%u'",GetInstanceID());
217 // remove from battlegrounds
220 sBattleGroundMgr.RemoveBattleGround(GetInstanceID(), GetTypeID());
221 // unload map
222 if (Map * map = MapManager::Instance().FindMap(GetMapId(), GetInstanceID()))
223 if (map->IsBattleGroundOrArena())
224 ((BattleGroundMap*)map)->SetUnload();
225 // remove from bg free slot queue
226 this->RemoveFromBGFreeSlotQueue();
228 for(BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr)
229 delete itr->second;
232 void BattleGround::Update(uint32 diff)
234 if (!GetPlayersSize() && !GetReviveQueueSize())
235 //BG is empty
236 return;
238 // remove offline players from bg after 5 minutes
239 if (!m_OfflineQueue.empty())
241 BattleGroundPlayerMap::iterator itr = m_Players.find(*(m_OfflineQueue.begin()));
242 if (itr != m_Players.end())
244 if (itr->second.OfflineRemoveTime <= sWorld.GetGameTime())
246 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
247 m_OfflineQueue.pop_front(); // remove from offline queue
248 //do not use itr for anything, because it is erased in RemovePlayerAtLeave()
253 /*********************************************************/
254 /*** BATTLEGROUND RESSURECTION SYSTEM ***/
255 /*********************************************************/
257 //this should be handled by spell system
258 m_LastResurrectTime += diff;
259 if (m_LastResurrectTime >= RESURRECTION_INTERVAL)
261 if (GetReviveQueueSize())
263 for(std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
265 Creature *sh = NULL;
266 for(std::vector<uint64>::const_iterator itr2 = (itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
268 Player *plr = objmgr.GetPlayer(*itr2);
269 if (!plr)
270 continue;
272 if (!sh && plr->IsInWorld())
274 sh = plr->GetMap()->GetCreature(itr->first);
275 // only for visual effect
276 if (sh)
277 // Spirit Heal, effect 117
278 sh->CastSpell(sh, SPELL_SPIRIT_HEAL, true);
281 // Resurrection visual
282 plr->CastSpell(plr, SPELL_RESURRECTION_VISUAL, true);
283 m_ResurrectQueue.push_back(*itr2);
285 (itr->second).clear();
288 m_ReviveQueue.clear();
289 m_LastResurrectTime = 0;
291 else
292 // queue is clear and time passed, just update last resurrection time
293 m_LastResurrectTime = 0;
295 else if (m_LastResurrectTime > 500) // Resurrect players only half a second later, to see spirit heal effect on NPC
297 for(std::vector<uint64>::const_iterator itr = m_ResurrectQueue.begin(); itr != m_ResurrectQueue.end(); ++itr)
299 Player *plr = objmgr.GetPlayer(*itr);
300 if (!plr)
301 continue;
302 plr->ResurrectPlayer(1.0f);
303 plr->CastSpell(plr, SPELL_SPIRIT_HEAL_MANA, true);
304 ObjectAccessor::Instance().ConvertCorpseForPlayer(*itr);
306 m_ResurrectQueue.clear();
309 /*********************************************************/
310 /*** BATTLEGROUND BALLANCE SYSTEM ***/
311 /*********************************************************/
313 // if less then minimum players are in on one side, then start premature finish timer
314 if (GetStatus() == STATUS_IN_PROGRESS && !isArena() && sBattleGroundMgr.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE) < GetMinPlayersPerTeam()))
316 if (!m_PrematureCountDown)
318 m_PrematureCountDown = true;
319 m_PrematureCountDownTimer = sBattleGroundMgr.GetPrematureFinishTime();
321 else if (m_PrematureCountDownTimer < diff)
323 // time's up!
324 uint32 winner = 0;
325 if (GetPlayersCountByTeam(ALLIANCE) >= GetMinPlayersPerTeam())
326 winner = ALLIANCE;
327 else if (GetPlayersCountByTeam(HORDE) >= GetMinPlayersPerTeam())
328 winner = HORDE;
330 EndBattleGround(winner);
331 m_PrematureCountDown = false;
333 else
335 uint32 newtime = m_PrematureCountDownTimer - diff;
336 // announce every minute
337 if (newtime > (MINUTE * IN_MILISECONDS))
339 if (newtime / (MINUTE * IN_MILISECONDS) != m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS))
340 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS)));
342 else
344 //announce every 15 seconds
345 if (newtime / (15 * IN_MILISECONDS) != m_PrematureCountDownTimer / (15 * IN_MILISECONDS))
346 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / IN_MILISECONDS));
348 m_PrematureCountDownTimer = newtime;
351 else if (m_PrematureCountDown)
352 m_PrematureCountDown = false;
354 /*********************************************************/
355 /*** BATTLEGROUND STARTING SYSTEM ***/
356 /*********************************************************/
358 if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize())
360 ModifyStartDelayTime(diff);
362 if (!(m_Events & BG_STARTING_EVENT_1))
364 m_Events |= BG_STARTING_EVENT_1;
366 // setup here, only when at least one player has ported to the map
367 if (!SetupBattleGround())
369 EndNow();
370 return;
373 StartingEventCloseDoors();
374 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FIRST]);
375 //first start warning - 2 or 1 minute
376 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FIRST], CHAT_MSG_BG_SYSTEM_NEUTRAL);
378 // After 1 minute or 30 seconds, warning is signalled
379 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_SECOND] && !(m_Events & BG_STARTING_EVENT_2))
381 m_Events |= BG_STARTING_EVENT_2;
382 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_SECOND], CHAT_MSG_BG_SYSTEM_NEUTRAL);
384 // After 30 or 15 seconds, warning is signalled
385 else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_THIRD] && !(m_Events & BG_STARTING_EVENT_3))
387 m_Events |= BG_STARTING_EVENT_3;
388 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_THIRD], CHAT_MSG_BG_SYSTEM_NEUTRAL);
390 // delay expired (atfer 2 or 1 minute)
391 else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4))
393 m_Events |= BG_STARTING_EVENT_4;
395 StartingEventOpenDoors();
397 SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FOURTH], CHAT_MSG_BG_SYSTEM_NEUTRAL);
398 SetStatus(STATUS_IN_PROGRESS);
399 SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FOURTH]);
401 //remove preparation
402 if (isArena())
404 //TODO : add arena sound PlaySoundToAll(SOUND_ARENA_START);
406 for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
407 if (Player *plr = objmgr.GetPlayer(itr->first))
408 plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION);
410 CheckArenaWinConditions();
412 else
415 PlaySoundToAll(SOUND_BG_START);
417 for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
418 if (Player* plr = objmgr.GetPlayer(itr->first))
419 plr->RemoveAurasDueToSpell(SPELL_PREPARATION);
420 //Announce BG starting
421 if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE))
423 sWorld.SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD, GetName(), GetMinLevel(), GetMaxLevel());
429 /*********************************************************/
430 /*** BATTLEGROUND ENDING SYSTEM ***/
431 /*********************************************************/
433 if (GetStatus() == STATUS_WAIT_LEAVE)
435 // remove all players from battleground after 2 minutes
436 m_EndTime -= diff;
437 if (m_EndTime <= 0)
439 m_EndTime = 0;
440 BattleGroundPlayerMap::iterator itr, next;
441 for(itr = m_Players.begin(); itr != m_Players.end(); itr = next)
443 next = itr;
444 ++next;
445 //itr is erased here!
446 RemovePlayerAtLeave(itr->first, true, true);// remove player from BG
447 // do not change any battleground's private variables
452 //update start time
453 m_StartTime += diff;
456 void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O)
458 BattleGroundTeamId idx = GetTeamIndexByTeamId(TeamID);
459 m_TeamStartLocX[idx] = X;
460 m_TeamStartLocY[idx] = Y;
461 m_TeamStartLocZ[idx] = Z;
462 m_TeamStartLocO[idx] = O;
465 void BattleGround::SendPacketToAll(WorldPacket *packet)
467 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
469 Player *plr = objmgr.GetPlayer(itr->first);
470 if (plr)
471 plr->GetSession()->SendPacket(packet);
472 else
473 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
477 void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender, bool self)
479 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
481 Player *plr = objmgr.GetPlayer(itr->first);
483 if (!plr)
485 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
486 continue;
489 if (!self && sender == plr)
490 continue;
492 uint32 team = itr->second.Team;
493 if(!team) team = plr->GetTeam();
495 if (team == TeamID)
496 plr->GetSession()->SendPacket(packet);
500 void BattleGround::PlaySoundToAll(uint32 SoundID)
502 WorldPacket data;
503 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
504 SendPacketToAll(&data);
507 void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID)
509 WorldPacket data;
511 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
513 Player *plr = objmgr.GetPlayer(itr->first);
515 if (!plr)
517 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
518 continue;
521 uint32 team = itr->second.Team;
522 if(!team) team = plr->GetTeam();
524 if (team == TeamID)
526 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
527 plr->GetSession()->SendPacket(&data);
532 void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID)
534 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
536 Player *plr = objmgr.GetPlayer(itr->first);
538 if (!plr)
540 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
541 continue;
544 uint32 team = itr->second.Team;
545 if(!team) team = plr->GetTeam();
547 if (team == TeamID)
548 plr->CastSpell(plr, SpellID, true);
552 void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID)
554 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
556 Player *plr = objmgr.GetPlayer(itr->first);
558 if (!plr)
560 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
561 continue;
564 uint32 team = itr->second.Team;
565 if(!team) team = plr->GetTeam();
567 if (team == TeamID)
568 UpdatePlayerScore(plr, SCORE_BONUS_HONOR, Honor);
572 void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID)
574 FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
576 if (!factionEntry)
577 return;
579 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
581 Player *plr = objmgr.GetPlayer(itr->first);
583 if (!plr)
585 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
586 continue;
589 uint32 team = itr->second.Team;
590 if(!team) team = plr->GetTeam();
592 if (team == TeamID)
593 plr->GetReputationMgr().ModifyReputation(factionEntry, Reputation);
597 void BattleGround::UpdateWorldState(uint32 Field, uint32 Value)
599 WorldPacket data;
600 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
601 SendPacketToAll(&data);
604 void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source)
606 WorldPacket data;
607 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
608 Source->GetSession()->SendPacket(&data);
611 void BattleGround::EndBattleGround(uint32 winner)
613 this->RemoveFromBGFreeSlotQueue();
615 ArenaTeam * winner_arena_team = NULL;
616 ArenaTeam * loser_arena_team = NULL;
617 uint32 loser_rating = 0;
618 uint32 winner_rating = 0;
619 WorldPacket data;
620 int32 winmsg_id = 0;
622 if (winner == ALLIANCE)
624 winmsg_id = isBattleGround() ? LANG_BG_A_WINS : LANG_ARENA_GOLD_WINS;
626 PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound
628 SetWinner(WINNER_ALLIANCE);
630 else if (winner == HORDE)
632 winmsg_id = isBattleGround() ? LANG_BG_H_WINS : LANG_ARENA_GREEN_WINS;
634 PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound
636 SetWinner(WINNER_HORDE);
638 else
640 SetWinner(3);
643 SetStatus(STATUS_WAIT_LEAVE);
644 //we must set it this way, because end time is sent in packet!
645 m_EndTime = TIME_TO_AUTOREMOVE;
647 // arena rating calculation
648 if (isArena() && isRated())
650 winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(winner));
651 loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(winner)));
652 if (winner_arena_team && loser_arena_team)
654 loser_rating = loser_arena_team->GetStats().rating;
655 winner_rating = winner_arena_team->GetStats().rating;
656 int32 winner_change = winner_arena_team->WonAgainst(loser_rating);
657 int32 loser_change = loser_arena_team->LostAgainst(winner_rating);
658 sLog.outDebug("--- Winner rating: %u, Loser rating: %u, Winner change: %u, Losser change: %u ---", winner_rating, loser_rating, winner_change, loser_change);
659 SetArenaTeamRatingChangeForTeam(winner, winner_change);
660 SetArenaTeamRatingChangeForTeam(GetOtherTeam(winner), loser_change);
662 else
664 SetArenaTeamRatingChangeForTeam(ALLIANCE, 0);
665 SetArenaTeamRatingChangeForTeam(HORDE, 0);
669 for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
671 Player *plr = objmgr.GetPlayer(itr->first);
672 uint32 team = itr->second.Team;
674 if (!plr)
676 //if rated arena match - make member lost!
677 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
679 if (team == winner)
680 winner_arena_team->OfflineMemberLost(itr->first, loser_rating);
681 else
682 loser_arena_team->OfflineMemberLost(itr->first, winner_rating);
684 sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
685 continue;
688 // should remove spirit of redemption
689 if (plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
690 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
692 if (!plr->isAlive())
694 plr->ResurrectPlayer(1.0f);
695 plr->SpawnCorpseBones();
698 //this line is obsolete - team is set ALWAYS
699 //if(!team) team = plr->GetTeam();
701 // per player calculation
702 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
704 if (team == winner)
706 // update achievement BEFORE personal rating update
707 ArenaTeamMember* member = winner_arena_team->GetMember(plr->GetGUID());
708 if (member)
709 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, member->personal_rating);
711 winner_arena_team->MemberWon(plr,loser_rating);
713 else
715 loser_arena_team->MemberLost(plr,winner_rating);
717 // Arena lost => reset the win_rated_arena having the "no_loose" condition
718 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE);
722 if (team == winner)
724 RewardMark(plr,ITEM_WINNER_COUNT);
725 RewardQuestComplete(plr);
727 else
728 RewardMark(plr,ITEM_LOSER_COUNT);
730 plr->CombatStopWithPets(true);
732 BlockMovement(plr);
734 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
735 plr->GetSession()->SendPacket(&data);
737 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
738 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType());
739 plr->GetSession()->SendPacket(&data);
740 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1);
743 if (isArena() && isRated() && winner_arena_team && loser_arena_team)
745 // update arena points only after increasing the player's match count!
746 //obsolete: winner_arena_team->UpdateArenaPointsHelper();
747 //obsolete: loser_arena_team->UpdateArenaPointsHelper();
748 // save the stat changes
749 winner_arena_team->SaveToDB();
750 loser_arena_team->SaveToDB();
751 // send updated arena team stats to players
752 // this way all arena team members will get notified, not only the ones who participated in this match
753 winner_arena_team->NotifyStatsChanged();
754 loser_arena_team->NotifyStatsChanged();
757 if (winmsg_id)
758 SendMessageToAll(winmsg_id, CHAT_MSG_BG_SYSTEM_NEUTRAL);
761 uint32 BattleGround::GetBonusHonorFromKill(uint32 kills) const
763 //variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill)
764 return MaNGOS::Honor::hk_honor_at_level(GetMaxLevel(), kills);
767 uint32 BattleGround::GetBattlemasterEntry() const
769 switch(GetTypeID())
771 case BATTLEGROUND_AV: return 15972;
772 case BATTLEGROUND_WS: return 14623;
773 case BATTLEGROUND_AB: return 14879;
774 case BATTLEGROUND_EY: return 22516;
775 case BATTLEGROUND_NA: return 20200;
776 default: return 0;
780 void BattleGround::RewardMark(Player *plr,uint32 count)
782 BattleGroundMarks mark;
783 bool IsSpell;
784 switch(GetTypeID())
786 case BATTLEGROUND_AV:
787 IsSpell = true;
788 if (count == ITEM_WINNER_COUNT)
789 mark = SPELL_AV_MARK_WINNER;
790 else
791 mark = SPELL_AV_MARK_LOSER;
792 break;
793 case BATTLEGROUND_WS:
794 IsSpell = true;
795 if (count == ITEM_WINNER_COUNT)
796 mark = SPELL_WS_MARK_WINNER;
797 else
798 mark = SPELL_WS_MARK_LOSER;
799 break;
800 case BATTLEGROUND_AB:
801 IsSpell = true;
802 if (count == ITEM_WINNER_COUNT)
803 mark = SPELL_AB_MARK_WINNER;
804 else
805 mark = SPELL_AB_MARK_LOSER;
806 break;
807 case BATTLEGROUND_EY:
808 IsSpell = false;
809 mark = ITEM_EY_MARK_OF_HONOR;
810 break;
811 default:
812 return;
815 if (IsSpell)
816 RewardSpellCast(plr,mark);
817 else
818 RewardItem(plr,mark,count);
821 void BattleGround::RewardSpellCast(Player *plr, uint32 spell_id)
823 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
824 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
825 return;
827 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
828 if(!spellInfo)
830 sLog.outError("Battleground reward casting spell %u not exist.",spell_id);
831 return;
834 plr->CastSpell(plr, spellInfo, true);
837 void BattleGround::RewardItem(Player *plr, uint32 item_id, uint32 count)
839 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
840 if (plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
841 return;
843 ItemPosCountVec dest;
844 uint32 no_space_count = 0;
845 uint8 msg = plr->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item_id, count, &no_space_count );
847 if( msg == EQUIP_ERR_ITEM_NOT_FOUND)
849 sLog.outErrorDb("Battleground reward item (Entry %u) not exist in `item_template`.",item_id);
850 return;
853 if( msg != EQUIP_ERR_OK ) // convert to possible store amount
854 count -= no_space_count;
856 if( count != 0 && !dest.empty()) // can add some
857 if (Item* item = plr->StoreNewItem( dest, item_id, true, 0))
858 plr->SendNewItem(item,count,true,false);
860 if (no_space_count > 0)
861 SendRewardMarkByMail(plr,item_id,no_space_count);
864 void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count)
866 uint32 bmEntry = GetBattlemasterEntry();
867 if (!bmEntry)
868 return;
870 ItemPrototype const* markProto = objmgr.GetItemPrototype(mark);
871 if (!markProto)
872 return;
874 if (Item* markItem = Item::CreateItem(mark,count,plr))
876 // save new item before send
877 markItem->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
879 // item
880 MailItemsInfo mi;
881 mi.AddItem(markItem->GetGUIDLow(), markItem->GetEntry(), markItem);
883 // subject: item name
884 std::string subject = markProto->Name1;
885 int loc_idx = plr->GetSession()->GetSessionDbLocaleIndex();
886 if (loc_idx >= 0 )
887 if (ItemLocale const *il = objmgr.GetItemLocale(markProto->ItemId))
888 if (il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty())
889 subject = il->Name[loc_idx];
891 // text
892 std::string textFormat = plr->GetSession()->GetMangosString(LANG_BG_MARK_BY_MAIL);
893 char textBuf[300];
894 snprintf(textBuf,300,textFormat.c_str(),GetName(),GetName());
895 uint32 itemTextId = objmgr.CreateItemText( textBuf );
897 WorldSession::SendMailTo(plr, MAIL_CREATURE, MAIL_STATIONERY_NORMAL, bmEntry, plr->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE);
901 void BattleGround::RewardQuestComplete(Player *plr)
903 uint32 quest;
904 switch(GetTypeID())
906 case BATTLEGROUND_AV:
907 quest = SPELL_AV_QUEST_REWARD;
908 break;
909 case BATTLEGROUND_WS:
910 quest = SPELL_WS_QUEST_REWARD;
911 break;
912 case BATTLEGROUND_AB:
913 quest = SPELL_AB_QUEST_REWARD;
914 break;
915 case BATTLEGROUND_EY:
916 quest = SPELL_EY_QUEST_REWARD;
917 break;
918 default:
919 return;
922 RewardSpellCast(plr, quest);
925 void BattleGround::BlockMovement(Player *plr)
927 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()
930 void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket)
932 uint32 team = GetPlayerTeam(guid);
933 bool participant = false;
934 // Remove from lists/maps
935 BattleGroundPlayerMap::iterator itr = m_Players.find(guid);
936 if (itr != m_Players.end())
938 UpdatePlayersCountByTeam(team, true); // -1 player
939 m_Players.erase(itr);
940 // check if the player was a participant of the match, or only entered through gm command (goname)
941 participant = true;
944 BattleGroundScoreMap::iterator itr2 = m_PlayerScores.find(guid);
945 if (itr2 != m_PlayerScores.end())
947 delete itr2->second; // delete player's score
948 m_PlayerScores.erase(itr2);
951 RemovePlayerFromResurrectQueue(guid);
953 Player *plr = objmgr.GetPlayer(guid);
955 // should remove spirit of redemption
956 if (plr && plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
957 plr->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
959 if(plr && !plr->isAlive()) // resurrect on exit
961 plr->ResurrectPlayer(1.0f);
962 plr->SpawnCorpseBones();
965 RemovePlayer(plr, guid); // BG subclass specific code
967 if(participant) // if the player was a match participant, remove auras, calc rating, update queue
969 BattleGroundTypeId bgTypeId = GetTypeID();
970 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
971 if (plr)
973 plr->ClearAfkReports();
975 if(!team) team = plr->GetTeam();
977 // if arena, remove the specific arena auras
978 if (isArena())
980 plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out
981 bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing)
983 // unsummon current and summon old pet if there was one and there isn't a current pet
984 plr->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT);
985 plr->ResummonPetTemporaryUnSummonedIfAny();
987 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
989 //left a rated match while the encounter was in progress, consider as loser
990 ArenaTeam * winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
991 ArenaTeam * loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
992 if (winner_arena_team && loser_arena_team)
993 loser_arena_team->MemberLost(plr,winner_arena_team->GetRating());
996 if (SendPacket)
998 WorldPacket data;
999 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0, 0);
1000 plr->GetSession()->SendPacket(&data);
1003 // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg
1004 plr->RemoveBattleGroundQueueId(bgQueueTypeId);
1006 else
1007 // removing offline participant
1009 if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
1011 //left a rated match while the encounter was in progress, consider as loser
1012 ArenaTeam * others_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team)));
1013 ArenaTeam * players_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team));
1014 if (others_arena_team && players_arena_team)
1015 players_arena_team->OfflineMemberLost(guid, others_arena_team->GetRating());
1019 // remove from raid group if player is member
1020 if (Group *group = GetBgRaid(team))
1022 if( !group->RemoveMember(guid, 0) ) // group was disbanded
1024 SetBgRaid(team, NULL);
1025 delete group;
1028 DecreaseInvitedCount(team);
1029 //we should update battleground queue, but only if bg isn't ending
1030 if (isBattleGround() && GetStatus() < STATUS_WAIT_LEAVE)
1031 sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, GetQueueId());
1032 // Let others know
1033 WorldPacket data;
1034 sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, guid);
1035 SendPacketToTeam(team, &data, plr, false);
1038 if (plr)
1040 // Do next only if found in battleground
1041 plr->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG.
1042 // reset destination bg team
1043 plr->SetBGTeam(0);
1045 if (Transport)
1046 plr->TeleportToBGEntryPoint();
1048 sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName());
1051 if (!GetPlayersSize() && !GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
1053 // if no players left AND no invitees left, set this bg to delete in next update
1054 // direct deletion could cause crashes
1055 m_SetDeleteThis = true;
1056 // return to prevent addition to freeslotqueue
1057 return;
1060 // a player exited the battleground, so there are free slots. add to queue
1061 this->AddToBGFreeSlotQueue();
1064 // this method is called when no players remains in battleground
1065 void BattleGround::Reset()
1067 SetQueueId(QUEUE_ID_MAX_LEVEL_19);
1068 SetWinner(WINNER_NONE);
1069 SetStatus(STATUS_WAIT_QUEUE);
1070 SetStartTime(0);
1071 SetEndTime(0);
1072 SetLastResurrectTime(0);
1073 SetArenaType(0);
1074 SetRated(false);
1076 m_Events = 0;
1078 if (m_InvitedAlliance > 0 || m_InvitedHorde > 0)
1079 sLog.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde);
1081 m_InvitedAlliance = 0;
1082 m_InvitedHorde = 0;
1083 m_InBGFreeSlotQueue = false;
1085 m_Players.clear();
1087 for(BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr)
1088 delete itr->second;
1089 m_PlayerScores.clear();
1092 void BattleGround::StartBattleGround()
1094 ///this method should spawn spirit guides and so on
1095 SetStartTime(0);
1097 SetLastResurrectTime(0);
1100 void BattleGround::AddPlayer(Player *plr)
1102 // remove afk from player
1103 if (plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK))
1104 plr->ToggleAFK();
1106 // score struct must be created in inherited class
1108 uint64 guid = plr->GetGUID();
1109 uint32 team = plr->GetBGTeam();
1111 BattleGroundPlayer bp;
1112 bp.OfflineRemoveTime = 0;
1113 bp.Team = team;
1115 // Add to list/maps
1116 m_Players[guid] = bp;
1118 UpdatePlayersCountByTeam(team, false); // +1 player
1120 WorldPacket data;
1121 sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr);
1122 SendPacketToTeam(team, &data, plr, false);
1124 // add arena specific auras
1125 if (isArena())
1127 plr->RemoveArenaSpellCooldowns();
1128 plr->RemoveArenaAuras();
1129 plr->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT);
1130 if(team == ALLIANCE) // gold
1132 if (plr->GetTeam() == HORDE)
1133 plr->CastSpell(plr, SPELL_HORDE_GOLD_FLAG,true);
1134 else
1135 plr->CastSpell(plr, SPELL_ALLIANCE_GOLD_FLAG,true);
1137 else // green
1139 if (plr->GetTeam() == HORDE)
1140 plr->CastSpell(plr, SPELL_HORDE_GREEN_FLAG,true);
1141 else
1142 plr->CastSpell(plr, SPELL_ALLIANCE_GREEN_FLAG,true);
1145 plr->DestroyConjuredItems(true);
1146 plr->UnsummonPetTemporaryIfAny();
1148 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1150 plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true);
1152 plr->SetHealth(plr->GetMaxHealth());
1153 plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA));
1156 else
1158 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
1159 plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells.
1162 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1163 plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
1165 // setup BG group membership
1166 PlayerAddedToBGCheckIfBGIsRunning(plr);
1167 AddOrSetPlayerToCorrectBgGroup(plr, guid, team);
1169 // Log
1170 sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName());
1173 /* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */
1174 void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team)
1176 Group* group = GetBgRaid(team);
1177 if(!group) // first player joined
1179 group = new Group;
1180 SetBgRaid(team, group);
1181 group->Create(plr_guid, plr->GetName());
1183 else // raid already exist
1185 if (group->IsMember(plr_guid))
1187 uint8 subgroup = group->GetMemberGroup(plr_guid);
1188 plr->SetBattleGroundRaid(group, subgroup);
1190 else
1192 group->AddMember(plr_guid, plr->GetName());
1193 if (Group* originalGroup = plr->GetOriginalGroup())
1194 if (originalGroup->IsLeader(plr_guid))
1195 group->ChangeLeader(plr_guid);
1200 // This method should be called when player logs into running battleground
1201 void BattleGround::EventPlayerLoggedIn(Player* player, uint64 plr_guid)
1203 // player is correct pointer
1204 for(std::deque<uint64>::iterator itr = m_OfflineQueue.begin(); itr != m_OfflineQueue.end(); ++itr)
1206 if (*itr == plr_guid)
1208 m_OfflineQueue.erase(itr);
1209 break;
1212 m_Players[plr_guid].OfflineRemoveTime = 0;
1213 PlayerAddedToBGCheckIfBGIsRunning(player);
1214 // if battleground is starting, then add preparation aura
1215 // we don't have to do that, because preparation aura isn't removed when player logs out
1218 // This method should be called when player logs out from running battleground
1219 void BattleGround::EventPlayerLoggedOut(Player* player)
1221 // player is correct pointer, it is checked in WorldSession::LogoutPlayer()
1222 m_OfflineQueue.push_back(player->GetGUID());
1223 m_Players[player->GetGUID()].OfflineRemoveTime = sWorld.GetGameTime() + MAX_OFFLINE_TIME;
1224 if (GetStatus() == STATUS_IN_PROGRESS)
1226 if (isBattleGround())
1227 EventPlayerDroppedFlag(player);
1228 else
1230 //1 player is logging out, if it is the last, then end arena!
1231 if (GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam())))
1232 EndBattleGround(GetOtherTeam(player->GetTeam()));
1237 /* This method should be called only once ... it adds pointer to queue */
1238 void BattleGround::AddToBGFreeSlotQueue()
1240 // make sure to add only once
1241 if (!m_InBGFreeSlotQueue && isBattleGround())
1243 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this);
1244 m_InBGFreeSlotQueue = true;
1248 /* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/
1249 void BattleGround::RemoveFromBGFreeSlotQueue()
1251 // set to be able to re-add if needed
1252 m_InBGFreeSlotQueue = false;
1253 // uncomment this code when battlegrounds will work like instances
1254 for (BGFreeSlotQueueType::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr)
1256 if ((*itr)->GetInstanceID() == m_InstanceID)
1258 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr);
1259 return;
1264 // get the number of free slots for team
1265 // returns the number how many players can join battleground to MaxPlayersPerTeam
1266 uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const
1268 //return free slot count to MaxPlayerPerTeam
1269 if (GetStatus() == STATUS_WAIT_JOIN || GetStatus() == STATUS_IN_PROGRESS)
1270 return (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0;
1272 return 0;
1275 bool BattleGround::HasFreeSlots() const
1277 return GetPlayersSize() < GetMaxPlayers();
1280 void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value)
1282 //this procedure is called from virtual function implemented in bg subclass
1283 BattleGroundScoreMap::const_iterator itr = m_PlayerScores.find(Source->GetGUID());
1285 if(itr == m_PlayerScores.end()) // player not found...
1286 return;
1288 switch(type)
1290 case SCORE_KILLING_BLOWS: // Killing blows
1291 itr->second->KillingBlows += value;
1292 break;
1293 case SCORE_DEATHS: // Deaths
1294 itr->second->Deaths += value;
1295 break;
1296 case SCORE_HONORABLE_KILLS: // Honorable kills
1297 itr->second->HonorableKills += value;
1298 break;
1299 case SCORE_BONUS_HONOR: // Honor bonus
1300 // do not add honor in arenas
1301 if (isBattleGround())
1303 // reward honor instantly
1304 if (Source->RewardHonor(NULL, 1, value))
1305 itr->second->BonusHonor += value;
1307 break;
1308 //used only in EY, but in MSG_PVP_LOG_DATA opcode
1309 case SCORE_DAMAGE_DONE: // Damage Done
1310 itr->second->DamageDone += value;
1311 break;
1312 case SCORE_HEALING_DONE: // Healing Done
1313 itr->second->HealingDone += value;
1314 break;
1315 default:
1316 sLog.outError("BattleGround: Unknown player score type %u", type);
1317 break;
1321 void BattleGround::AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid)
1323 m_ReviveQueue[npc_guid].push_back(player_guid);
1325 Player *plr = objmgr.GetPlayer(player_guid);
1326 if (!plr)
1327 return;
1329 plr->CastSpell(plr, SPELL_WAITING_FOR_RESURRECT, true);
1332 void BattleGround::RemovePlayerFromResurrectQueue(uint64 player_guid)
1334 for(std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
1336 for(std::vector<uint64>::iterator itr2 =(itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
1338 if (*itr2 == player_guid)
1340 (itr->second).erase(itr2);
1342 Player *plr = objmgr.GetPlayer(player_guid);
1343 if (!plr)
1344 return;
1346 plr->RemoveAurasDueToSpell(SPELL_WAITING_FOR_RESURRECT);
1348 return;
1354 bool BattleGround::AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime)
1356 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
1357 if (!map)
1358 return false;
1360 // must be created this way, adding to godatamap would add it to the base map of the instance
1361 // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
1362 // so we must create it specific for this instance
1363 GameObject * go = new GameObject;
1364 if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry, map,
1365 PHASEMASK_NORMAL, x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,GO_STATE_READY))
1367 sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry);
1368 sLog.outError("Cannot create gameobject template %u! BattleGround not created!", entry);
1369 delete go;
1370 return false;
1373 uint32 guid = go->GetGUIDLow();
1375 // without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata
1376 // iirc that was changed, so adding to go data map is no longer required if that was the only function using godata from GameObject without checking if it existed
1377 GameObjectData& data = objmgr.NewGOData(guid);
1379 data.id = entry;
1380 data.mapid = GetMapId();
1381 data.posX = x;
1382 data.posY = y;
1383 data.posZ = z;
1384 data.orientation = o;
1385 data.rotation0 = rotation0;
1386 data.rotation1 = rotation1;
1387 data.rotation2 = rotation2;
1388 data.rotation3 = rotation3;
1389 data.spawntimesecs = respawnTime;
1390 data.spawnMask = 1;
1391 data.animprogress = 100;
1392 data.go_state = 1;
1394 // add to world, so it can be later looked up from HashMapHolder
1395 go->AddToWorld();
1396 m_BgObjects[type] = go->GetGUID();
1397 return true;
1400 //some doors aren't despawned so we cannot handle their closing in gameobject::update()
1401 //it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
1402 void BattleGround::DoorClose(uint32 type)
1404 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1405 if (obj)
1407 //if doors are open, close it
1408 if (obj->getLootState() == GO_ACTIVATED && obj->GetGoState() != GO_STATE_READY)
1410 //change state to allow door to be closed
1411 obj->SetLootState(GO_READY);
1412 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1415 else
1417 sLog.outError("BattleGround: Door object not found (cannot close doors)");
1421 void BattleGround::DoorOpen(uint32 type)
1423 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1424 if (obj)
1426 //change state to be sure they will be opened
1427 obj->SetLootState(GO_READY);
1428 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
1430 else
1432 sLog.outError("BattleGround: Door object not found! - doors will be closed.");
1436 void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime)
1438 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
1439 if (!map)
1440 return;
1441 if (respawntime == 0)
1443 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1444 if (obj)
1446 //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again
1447 if (obj->getLootState() == GO_JUST_DEACTIVATED)
1448 obj->SetLootState(GO_READY);
1449 obj->SetRespawnTime(0);
1450 map->Add(obj);
1453 else
1455 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1456 if (obj)
1458 map->Add(obj);
1459 obj->SetRespawnTime(respawntime);
1460 obj->SetLootState(GO_JUST_DEACTIVATED);
1465 Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o, uint32 respawntime)
1467 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
1468 if (!map)
1469 return NULL;
1471 Creature* pCreature = new Creature;
1472 if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, PHASEMASK_NORMAL, entry, teamval))
1474 sLog.outError("Can't create creature entry: %u",entry);
1475 delete pCreature;
1476 return NULL;
1479 pCreature->Relocate(x, y, z, o);
1481 if (!pCreature->IsPositionValid())
1483 sLog.outError("Creature (guidlow %d, entry %d) not added to battleground. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
1484 delete pCreature;
1485 return NULL;
1488 pCreature->AIM_Initialize();
1490 //pCreature->SetDungeonDifficulty(0);
1492 map->Add(pCreature);
1493 m_BgCreatures[type] = pCreature->GetGUID();
1495 return pCreature;
1498 void BattleGround::SpawnBGCreature(uint32 type, uint32 respawntime)
1500 Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceId());
1501 if (!map)
1502 return false;
1504 if (respawntime == 0)
1506 Creature *obj = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1507 if (obj)
1509 //obj->Respawn(); // bugged
1510 obj->SetRespawnTime(0);
1511 objmgr.SaveCreatureRespawnTime(obj->GetGUIDLow(), GetInstanceID(), 0);
1512 map->Add(obj);
1515 else
1517 Creature *obj = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1518 if (obj)
1520 obj->setDeathState(DEAD);
1521 obj->SetRespawnTime(respawntime);
1522 map->Add(obj);
1527 bool BattleGround::DelCreature(uint32 type)
1529 if (!m_BgCreatures[type])
1530 return true;
1532 Creature *cr = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1533 if (!cr)
1535 sLog.outError("Can't find creature guid: %u",GUID_LOPART(m_BgCreatures[type]));
1536 return false;
1538 cr->AddObjectToRemoveList();
1539 m_BgCreatures[type] = 0;
1540 return true;
1543 bool BattleGround::DelObject(uint32 type)
1545 if (!m_BgObjects[type])
1546 return true;
1548 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1549 if (!obj)
1551 sLog.outError("Can't find gobject guid: %u",GUID_LOPART(m_BgObjects[type]));
1552 return false;
1554 obj->SetRespawnTime(0); // not save respawn time
1555 obj->Delete();
1556 m_BgObjects[type] = 0;
1557 return true;
1560 bool BattleGround::AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team)
1562 uint32 entry = 0;
1564 if (team == ALLIANCE)
1565 entry = 13116;
1566 else
1567 entry = 13117;
1569 Creature* pCreature = AddCreature(entry,type,team,x,y,z,o);
1570 if (!pCreature)
1572 sLog.outError("Can't create Spirit guide. BattleGround not created!");
1573 EndNow();
1574 return false;
1577 pCreature->setDeathState(DEAD);
1579 pCreature->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, pCreature->GetGUID());
1580 // aura
1581 pCreature->SetVisibleAura(0, SPELL_SPIRIT_HEAL_CHANNEL);
1582 //pCreature->SetUInt32Value(UNIT_FIELD_AURAFLAGS, 0x00000009);
1583 //pCreature->SetUInt32Value(UNIT_FIELD_AURALEVELS, 0x0000003C);
1584 //pCreature->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS, 0x000000FF);
1585 // casting visual effect
1586 pCreature->SetUInt32Value(UNIT_CHANNEL_SPELL, SPELL_SPIRIT_HEAL_CHANNEL);
1587 // correct cast speed
1588 pCreature->SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
1590 //pCreature->CastSpell(pCreature, SPELL_SPIRIT_HEAL_CHANNEL, true);
1592 return true;
1595 void BattleGround::SendMessageToAll(int32 entry, ChatMsg type, Player const* source)
1597 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source);
1598 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1599 BroadcastWorker(bg_do);
1602 void BattleGround::PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...)
1604 va_list ap;
1605 va_start(ap, source);
1607 MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source, &ap);
1608 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
1609 BroadcastWorker(bg_do);
1611 va_end(ap);
1614 void BattleGround::SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 arg1, int32 arg2)
1616 MaNGOS::BattleGround2ChatBuilder bg_builder(type, entry, source, arg1, arg2);
1617 MaNGOS::LocalizedPacketDo<MaNGOS::BattleGround2ChatBuilder> bg_do(bg_builder);
1618 BroadcastWorker(bg_do);
1621 void BattleGround::EndNow()
1623 RemoveFromBGFreeSlotQueue();
1624 SetStatus(STATUS_WAIT_LEAVE);
1625 SetEndTime(0);
1629 important notice:
1630 buffs aren't spawned/despawned when players captures anything
1631 buffs are in their positions when battleground starts
1633 void BattleGround::HandleTriggerBuff(uint64 const& go_guid)
1635 GameObject *obj = HashMapHolder<GameObject>::Find(go_guid);
1636 if (!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned())
1637 return;
1639 //change buff type, when buff is used:
1640 int32 index = m_BgObjects.size() - 1;
1641 while (index >= 0 && m_BgObjects[index] != go_guid)
1642 index--;
1643 if (index < 0)
1645 sLog.outError("BattleGround (Type: %u) has buff gameobject (Guid: %u Entry: %u Type:%u) but it hasn't that object in its internal data",GetTypeID(),GUID_LOPART(go_guid),obj->GetEntry(),obj->GetGoType());
1646 return;
1649 //randomly select new buff
1650 uint8 buff = urand(0, 2);
1651 uint32 entry = obj->GetEntry();
1652 if (m_BuffChange && entry != Buff_Entries[buff])
1654 //despawn current buff
1655 SpawnBGObject(index, RESPAWN_ONE_DAY);
1656 //set index for new one
1657 for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex)
1658 if (entry == Buff_Entries[currBuffTypeIndex])
1660 index -= currBuffTypeIndex;
1661 index += buff;
1665 SpawnBGObject(index, BUFF_RESPAWN_TIME);
1668 void BattleGround::HandleKillPlayer( Player *player, Player *killer )
1670 //keep in mind that for arena this will have to be changed a bit
1672 // add +1 deaths
1673 UpdatePlayerScore(player, SCORE_DEATHS, 1);
1675 // add +1 kills to group and +1 killing_blows to killer
1676 if (killer)
1678 UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1);
1679 UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1);
1681 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1683 Player *plr = objmgr.GetPlayer(itr->first);
1685 if (!plr || plr == killer)
1686 continue;
1688 if (plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player))
1689 UpdatePlayerScore(plr, SCORE_HONORABLE_KILLS, 1);
1693 // to be able to remove insignia -- ONLY IN BattleGrounds
1694 if (!isArena())
1695 player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE );
1698 // return the player's team based on battlegroundplayer info
1699 // used in same faction arena matches mainly
1700 uint32 BattleGround::GetPlayerTeam(uint64 guid)
1702 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1703 if (itr!=m_Players.end())
1704 return itr->second.Team;
1705 return 0;
1708 uint32 BattleGround::GetOtherTeam(uint32 teamId)
1710 return (teamId) ? ((teamId == ALLIANCE) ? HORDE : ALLIANCE) : 0;
1713 bool BattleGround::IsPlayerInBattleGround(uint64 guid)
1715 BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
1716 if (itr != m_Players.end())
1717 return true;
1718 return false;
1721 void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player* plr)
1723 if (GetStatus() != STATUS_WAIT_LEAVE)
1724 return;
1726 WorldPacket data;
1727 BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1729 BlockMovement(plr);
1731 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
1732 plr->GetSession()->SendPacket(&data);
1734 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType());
1735 plr->GetSession()->SendPacket(&data);
1738 uint32 BattleGround::GetAlivePlayersCountByTeam(uint32 Team) const
1740 int count = 0;
1741 for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1743 if (itr->second.Team == Team)
1745 Player * pl = objmgr.GetPlayer(itr->first);
1746 if (pl && pl->isAlive())
1747 ++count;
1750 return count;
1753 void BattleGround::CheckArenaWinConditions()
1755 if (!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE))
1756 EndBattleGround(HORDE);
1757 else if (GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE))
1758 EndBattleGround(ALLIANCE);
1761 void BattleGround::SetBgRaid( uint32 TeamID, Group *bg_raid )
1763 Group* &old_raid = TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE];
1764 if(old_raid) old_raid->SetBattlegroundGroup(NULL);
1765 if(bg_raid) bg_raid->SetBattlegroundGroup(this);
1766 old_raid = bg_raid;
1769 WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard( Player* player )
1771 return objmgr.GetClosestGraveYard( player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam() );
1774 bool BattleGround::IsTeamScoreInRange(uint32 team, uint32 minScore, uint32 maxScore) const
1776 BattleGroundTeamId team_idx = GetTeamIndexByTeamId(team);
1777 return m_TeamScores[team_idx] >= minScore && m_TeamScores[team_idx] <= maxScore;