2 * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "BattleGround.h"
22 #include "BattleGroundMgr.h"
24 #include "MapManager.h"
26 #include "SpellAuras.h"
27 #include "ArenaTeam.h"
30 #include "ObjectDefines.h"
31 #include "ObjectMgr.h"
32 #include "WorldPacket.h"
35 #include "GridNotifiersImpl.h"
39 class BattleGroundChatBuilder
42 BattleGroundChatBuilder(ChatMsg msgtype
, int32 textId
, Player
const* source
, va_list* args
= NULL
)
43 : i_msgtype(msgtype
), i_textId(textId
), i_source(source
), i_args(args
) {}
44 void operator()(WorldPacket
& data
, int32 loc_idx
)
46 char const* text
= sObjectMgr
.GetMangosString(i_textId
,loc_idx
);
50 // we need copy va_list before use or original va_list will corrupted
55 vsnprintf(str
,2048,text
, ap
);
58 do_helper(data
,&str
[0]);
64 void do_helper(WorldPacket
& data
, char const* text
)
66 uint64 target_guid
= i_source
? i_source
->GetGUID() : 0;
68 data
<< uint8(i_msgtype
);
69 data
<< uint32(LANG_UNIVERSAL
);
70 data
<< uint64(target_guid
); // there 0 for BG messages
71 data
<< uint32(0); // can be chat msg group or something
72 data
<< uint64(target_guid
);
73 data
<< uint32(strlen(text
)+1);
75 data
<< uint8(i_source
? i_source
->chatTag() : uint8(0));
80 Player
const* i_source
;
84 class BattleGroundYellBuilder
87 BattleGroundYellBuilder(uint32 language
, int32 textId
, Creature
const* source
, va_list* args
= NULL
)
88 : i_language(language
), i_textId(textId
), i_source(source
), i_args(args
) {}
89 void operator()(WorldPacket
& data
, int32 loc_idx
)
91 char const* text
= sObjectMgr
.GetMangosString(i_textId
,loc_idx
);
95 // we need copy va_list before use or original va_list will corrupted
100 vsnprintf(str
,2048,text
, ap
);
103 do_helper(data
,&str
[0]);
106 do_helper(data
,text
);
109 void do_helper(WorldPacket
& data
, char const* text
)
111 //copyied from BuildMonsterChat
112 data
<< (uint8
)CHAT_MSG_MONSTER_YELL
;
113 data
<< (uint32
)i_language
;
114 data
<< (uint64
)i_source
->GetGUID();
115 data
<< (uint32
)0; //2.1.0
116 data
<< (uint32
)(strlen(i_source
->GetName())+1);
117 data
<< i_source
->GetName();
118 data
<< (uint64
)0; //Unit Target - isn't important for bgs
119 data
<< (uint32
)strlen(text
)+1;
121 data
<< (uint8
)0; // ChatTag - for bgs allways 0?
126 Creature
const* i_source
;
131 class BattleGround2ChatBuilder
134 BattleGround2ChatBuilder(ChatMsg msgtype
, int32 textId
, Player
const* source
, int32 arg1
, int32 arg2
)
135 : i_msgtype(msgtype
), i_textId(textId
), i_source(source
), i_arg1(arg1
), i_arg2(arg2
) {}
136 void operator()(WorldPacket
& data
, int32 loc_idx
)
138 char const* text
= sObjectMgr
.GetMangosString(i_textId
,loc_idx
);
139 char const* arg1str
= i_arg1
? sObjectMgr
.GetMangosString(i_arg1
,loc_idx
) : "";
140 char const* arg2str
= i_arg2
? sObjectMgr
.GetMangosString(i_arg2
,loc_idx
) : "";
143 snprintf(str
,2048,text
, arg1str
, arg2str
);
145 uint64 target_guid
= i_source
? i_source
->GetGUID() : 0;
147 data
<< uint8(i_msgtype
);
148 data
<< uint32(LANG_UNIVERSAL
);
149 data
<< uint64(target_guid
); // there 0 for BG messages
150 data
<< uint32(0); // can be chat msg group or something
151 data
<< uint64(target_guid
);
152 data
<< uint32(strlen(str
)+1);
154 data
<< uint8(i_source
? i_source
->chatTag() : uint8(0));
160 Player
const* i_source
;
165 class BattleGround2YellBuilder
168 BattleGround2YellBuilder(uint32 language
, int32 textId
, Creature
const* source
, int32 arg1
, int32 arg2
)
169 : i_language(language
), i_textId(textId
), i_source(source
), i_arg1(arg1
), i_arg2(arg2
) {}
170 void operator()(WorldPacket
& data
, int32 loc_idx
)
172 char const* text
= sObjectMgr
.GetMangosString(i_textId
,loc_idx
);
173 char const* arg1str
= i_arg1
? sObjectMgr
.GetMangosString(i_arg1
,loc_idx
) : "";
174 char const* arg2str
= i_arg2
? sObjectMgr
.GetMangosString(i_arg2
,loc_idx
) : "";
177 snprintf(str
,2048,text
, arg1str
, arg2str
);
178 //copyied from BuildMonsterChat
179 data
<< (uint8
)CHAT_MSG_MONSTER_YELL
;
180 data
<< (uint32
)i_language
;
181 data
<< (uint64
)i_source
->GetGUID();
182 data
<< (uint32
)0; //2.1.0
183 data
<< (uint32
)(strlen(i_source
->GetName())+1);
184 data
<< i_source
->GetName();
185 data
<< (uint64
)0; //Unit Target - isn't important for bgs
186 data
<< (uint32
)strlen(str
)+1;
188 data
<< (uint8
)0; // ChatTag - for bgs allways 0?
194 Creature
const* i_source
;
198 } // namespace MaNGOS
201 void BattleGround::BroadcastWorker(Do
& _do
)
203 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
204 if (Player
*plr
= ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr
->first
, 0, HIGHGUID_PLAYER
)))
208 BattleGround::BattleGround()
210 m_TypeID
= BattleGroundTypeId(0);
212 m_Status
= STATUS_NONE
;
213 m_ClientInstanceID
= 0;
215 m_BracketId
= BG_BRACKET_ID_FIRST
;
216 m_InvitedAlliance
= 0;
224 m_BuffChange
= false;
228 m_InBGFreeSlotQueue
= false;
229 m_SetDeleteThis
= false;
231 m_MaxPlayersPerTeam
= 0;
233 m_MinPlayersPerTeam
= 0;
239 m_TeamStartLocX
[BG_TEAM_ALLIANCE
] = 0;
240 m_TeamStartLocX
[BG_TEAM_HORDE
] = 0;
242 m_TeamStartLocY
[BG_TEAM_ALLIANCE
] = 0;
243 m_TeamStartLocY
[BG_TEAM_HORDE
] = 0;
245 m_TeamStartLocZ
[BG_TEAM_ALLIANCE
] = 0;
246 m_TeamStartLocZ
[BG_TEAM_HORDE
] = 0;
248 m_TeamStartLocO
[BG_TEAM_ALLIANCE
] = 0;
249 m_TeamStartLocO
[BG_TEAM_HORDE
] = 0;
251 m_ArenaTeamIds
[BG_TEAM_ALLIANCE
] = 0;
252 m_ArenaTeamIds
[BG_TEAM_HORDE
] = 0;
254 m_ArenaTeamRatingChanges
[BG_TEAM_ALLIANCE
] = 0;
255 m_ArenaTeamRatingChanges
[BG_TEAM_HORDE
] = 0;
257 m_BgRaids
[BG_TEAM_ALLIANCE
] = NULL
;
258 m_BgRaids
[BG_TEAM_HORDE
] = NULL
;
260 m_PlayersCount
[BG_TEAM_ALLIANCE
] = 0;
261 m_PlayersCount
[BG_TEAM_HORDE
] = 0;
263 m_TeamScores
[BG_TEAM_ALLIANCE
] = 0;
264 m_TeamScores
[BG_TEAM_HORDE
] = 0;
266 m_PrematureCountDown
= false;
267 m_PrematureCountDown
= 0;
269 m_StartDelayTimes
[BG_STARTING_EVENT_FIRST
] = BG_START_DELAY_2M
;
270 m_StartDelayTimes
[BG_STARTING_EVENT_SECOND
] = BG_START_DELAY_1M
;
271 m_StartDelayTimes
[BG_STARTING_EVENT_THIRD
] = BG_START_DELAY_30S
;
272 m_StartDelayTimes
[BG_STARTING_EVENT_FOURTH
] = BG_START_DELAY_NONE
;
273 //we must set to some default existing values
274 m_StartMessageIds
[BG_STARTING_EVENT_FIRST
] = 0;
275 m_StartMessageIds
[BG_STARTING_EVENT_SECOND
] = LANG_BG_WS_START_ONE_MINUTE
;
276 m_StartMessageIds
[BG_STARTING_EVENT_THIRD
] = LANG_BG_WS_START_HALF_MINUTE
;
277 m_StartMessageIds
[BG_STARTING_EVENT_FOURTH
] = LANG_BG_WS_HAS_BEGUN
;
280 BattleGround::~BattleGround()
282 // remove objects and creatures
283 // (this is done automatically in mapmanager update, when the instance is reset after the reset time)
285 int size
= m_BgObjects
.size();
286 for(int i
= 0; i
< size
; ++i
)
289 if (GetInstanceID()) // not spam by useless queries in case BG templates
291 // delete creature and go respawn times
292 WorldDatabase
.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'",GetInstanceID());
293 WorldDatabase
.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'",GetInstanceID());
294 // delete instance from db
295 CharacterDatabase
.PExecute("DELETE FROM instance WHERE id = '%u'",GetInstanceID());
296 // remove from battlegrounds
299 sBattleGroundMgr
.RemoveBattleGround(GetInstanceID(), GetTypeID());
302 // map can be null at bg destruction
306 // remove from bg free slot queue
307 this->RemoveFromBGFreeSlotQueue();
309 for(BattleGroundScoreMap::const_iterator itr
= m_PlayerScores
.begin(); itr
!= m_PlayerScores
.end(); ++itr
)
313 void BattleGround::Update(uint32 diff
)
315 if (!GetPlayersSize())
318 // if there are no players invited, delete BG
319 // this will delete arena or bg object, where any player entered
320 // [[ but if you use battleground object again (more battles possible to be played on 1 instance)
321 // then this condition should be removed and code:
322 // if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
323 // this->AddToFreeBGObjectsQueue(); // not yet implemented
324 // should be used instead of current
326 // BattleGround Template instance cannot be updated, because it would be deleted
327 if (!GetInvitedCount(HORDE
) && !GetInvitedCount(ALLIANCE
))
328 m_SetDeleteThis
= true;
332 // remove offline players from bg after 5 minutes
333 if (!m_OfflineQueue
.empty())
335 BattleGroundPlayerMap::iterator itr
= m_Players
.find(*(m_OfflineQueue
.begin()));
336 if (itr
!= m_Players
.end())
338 if (itr
->second
.OfflineRemoveTime
<= sWorld
.GetGameTime())
340 RemovePlayerAtLeave(itr
->first
, true, true);// remove player from BG
341 m_OfflineQueue
.pop_front(); // remove from offline queue
342 //do not use itr for anything, because it is erased in RemovePlayerAtLeave()
347 /*********************************************************/
348 /*** BATTLEGROUND BALLANCE SYSTEM ***/
349 /*********************************************************/
351 // if less then minimum players are in on one side, then start premature finish timer
352 if (GetStatus() == STATUS_IN_PROGRESS
&& !isArena() && sBattleGroundMgr
.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE
) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE
) < GetMinPlayersPerTeam()))
354 if (!m_PrematureCountDown
)
356 m_PrematureCountDown
= true;
357 m_PrematureCountDownTimer
= sBattleGroundMgr
.GetPrematureFinishTime();
359 else if (m_PrematureCountDownTimer
< diff
)
363 if (GetPlayersCountByTeam(ALLIANCE
) >= GetMinPlayersPerTeam())
365 else if (GetPlayersCountByTeam(HORDE
) >= GetMinPlayersPerTeam())
368 EndBattleGround(winner
);
369 m_PrematureCountDown
= false;
373 uint32 newtime
= m_PrematureCountDownTimer
- diff
;
374 // announce every minute
375 if (newtime
> (MINUTE
* IN_MILISECONDS
))
377 if (newtime
/ (MINUTE
* IN_MILISECONDS
) != m_PrematureCountDownTimer
/ (MINUTE
* IN_MILISECONDS
))
378 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING
, CHAT_MSG_SYSTEM
, NULL
, (uint32
)(m_PrematureCountDownTimer
/ (MINUTE
* IN_MILISECONDS
)));
382 //announce every 15 seconds
383 if (newtime
/ (15 * IN_MILISECONDS
) != m_PrematureCountDownTimer
/ (15 * IN_MILISECONDS
))
384 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS
, CHAT_MSG_SYSTEM
, NULL
, (uint32
)(m_PrematureCountDownTimer
/ IN_MILISECONDS
));
386 m_PrematureCountDownTimer
= newtime
;
390 else if (m_PrematureCountDown
)
391 m_PrematureCountDown
= false;
393 /*********************************************************/
394 /*** ARENA BUFF OBJECT SPAWNING ***/
395 /*********************************************************/
396 if (isArena() && !m_ArenaBuffSpawned
)
398 // 60 seconds after start the buffobjects in arena should get spawned
399 if (m_StartTime
> uint32(m_StartDelayTimes
[BG_STARTING_EVENT_FIRST
] + ARENA_SPAWN_BUFF_OBJECTS
))
401 SpawnEvent(ARENA_BUFF_EVENT
, 0, true);
402 m_ArenaBuffSpawned
= true;
406 /*********************************************************/
407 /*** BATTLEGROUND STARTING SYSTEM ***/
408 /*********************************************************/
410 if (GetStatus() == STATUS_WAIT_JOIN
&& GetPlayersSize())
412 ModifyStartDelayTime(diff
);
414 if (!(m_Events
& BG_STARTING_EVENT_1
))
416 m_Events
|= BG_STARTING_EVENT_1
;
418 // setup here, only when at least one player has ported to the map
419 if (!SetupBattleGround())
425 StartingEventCloseDoors();
426 SetStartDelayTime(m_StartDelayTimes
[BG_STARTING_EVENT_FIRST
]);
427 //first start warning - 2 or 1 minute, only if defined
428 if (m_StartMessageIds
[BG_STARTING_EVENT_FIRST
])
429 SendMessageToAll(m_StartMessageIds
[BG_STARTING_EVENT_FIRST
], CHAT_MSG_BG_SYSTEM_NEUTRAL
);
431 // After 1 minute or 30 seconds, warning is signalled
432 else if (GetStartDelayTime() <= m_StartDelayTimes
[BG_STARTING_EVENT_SECOND
] && !(m_Events
& BG_STARTING_EVENT_2
))
434 m_Events
|= BG_STARTING_EVENT_2
;
435 SendMessageToAll(m_StartMessageIds
[BG_STARTING_EVENT_SECOND
], CHAT_MSG_BG_SYSTEM_NEUTRAL
);
437 // After 30 or 15 seconds, warning is signalled
438 else if (GetStartDelayTime() <= m_StartDelayTimes
[BG_STARTING_EVENT_THIRD
] && !(m_Events
& BG_STARTING_EVENT_3
))
440 m_Events
|= BG_STARTING_EVENT_3
;
441 SendMessageToAll(m_StartMessageIds
[BG_STARTING_EVENT_THIRD
], CHAT_MSG_BG_SYSTEM_NEUTRAL
);
443 // delay expired (atfer 2 or 1 minute)
444 else if (GetStartDelayTime() <= 0 && !(m_Events
& BG_STARTING_EVENT_4
))
446 m_Events
|= BG_STARTING_EVENT_4
;
448 StartingEventOpenDoors();
450 SendMessageToAll(m_StartMessageIds
[BG_STARTING_EVENT_FOURTH
], CHAT_MSG_BG_SYSTEM_NEUTRAL
);
451 SetStatus(STATUS_IN_PROGRESS
);
452 SetStartDelayTime(m_StartDelayTimes
[BG_STARTING_EVENT_FOURTH
]);
457 //TODO : add arena sound PlaySoundToAll(SOUND_ARENA_START);
459 for(BattleGroundPlayerMap::const_iterator itr
= GetPlayers().begin(); itr
!= GetPlayers().end(); ++itr
)
460 if (Player
*plr
= sObjectMgr
.GetPlayer(itr
->first
))
461 plr
->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION
);
463 CheckArenaWinConditions();
468 PlaySoundToAll(SOUND_BG_START
);
470 for(BattleGroundPlayerMap::const_iterator itr
= GetPlayers().begin(); itr
!= GetPlayers().end(); ++itr
)
471 if (Player
* plr
= sObjectMgr
.GetPlayer(itr
->first
))
472 plr
->RemoveAurasDueToSpell(SPELL_PREPARATION
);
473 //Announce BG starting
474 if (sWorld
.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE
))
476 sWorld
.SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD
, GetName(), GetMinLevel(), GetMaxLevel());
482 /*********************************************************/
483 /*** BATTLEGROUND ENDING SYSTEM ***/
484 /*********************************************************/
486 if (GetStatus() == STATUS_WAIT_LEAVE
)
488 // remove all players from battleground after 2 minutes
493 BattleGroundPlayerMap::iterator itr
, next
;
494 for(itr
= m_Players
.begin(); itr
!= m_Players
.end(); itr
= next
)
498 //itr is erased here!
499 RemovePlayerAtLeave(itr
->first
, true, true);// remove player from BG
500 // do not change any battleground's private variables
509 void BattleGround::SetTeamStartLoc(uint32 TeamID
, float X
, float Y
, float Z
, float O
)
511 BattleGroundTeamId idx
= GetTeamIndexByTeamId(TeamID
);
512 m_TeamStartLocX
[idx
] = X
;
513 m_TeamStartLocY
[idx
] = Y
;
514 m_TeamStartLocZ
[idx
] = Z
;
515 m_TeamStartLocO
[idx
] = O
;
518 void BattleGround::SendPacketToAll(WorldPacket
*packet
)
520 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
522 if (itr
->second
.OfflineRemoveTime
)
524 Player
*plr
= sObjectMgr
.GetPlayer(itr
->first
);
526 plr
->GetSession()->SendPacket(packet
);
528 sLog
.outError("BattleGround:SendPacketToAll: Player (GUID: %u) not found!", GUID_LOPART(itr
->first
));
532 void BattleGround::SendPacketToTeam(uint32 TeamID
, WorldPacket
*packet
, Player
*sender
, bool self
)
534 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
536 if (itr
->second
.OfflineRemoveTime
)
538 Player
*plr
= sObjectMgr
.GetPlayer(itr
->first
);
541 sLog
.outError("BattleGround:SendPacketToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr
->first
));
545 if (!self
&& sender
== plr
)
548 uint32 team
= itr
->second
.Team
;
549 if(!team
) team
= plr
->GetTeam();
552 plr
->GetSession()->SendPacket(packet
);
556 void BattleGround::PlaySoundToAll(uint32 SoundID
)
559 sBattleGroundMgr
.BuildPlaySoundPacket(&data
, SoundID
);
560 SendPacketToAll(&data
);
563 void BattleGround::PlaySoundToTeam(uint32 SoundID
, uint32 TeamID
)
567 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
569 if (itr
->second
.OfflineRemoveTime
)
571 Player
*plr
= sObjectMgr
.GetPlayer(itr
->first
);
575 sLog
.outError("BattleGround:PlaySoundToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr
->first
));
579 uint32 team
= itr
->second
.Team
;
580 if(!team
) team
= plr
->GetTeam();
584 sBattleGroundMgr
.BuildPlaySoundPacket(&data
, SoundID
);
585 plr
->GetSession()->SendPacket(&data
);
590 void BattleGround::CastSpellOnTeam(uint32 SpellID
, uint32 TeamID
)
592 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
594 if (itr
->second
.OfflineRemoveTime
)
596 Player
*plr
= sObjectMgr
.GetPlayer(itr
->first
);
600 sLog
.outError("BattleGround:CastSpellOnTeam: Player (GUID: %u) not found!", GUID_LOPART(itr
->first
));
604 uint32 team
= itr
->second
.Team
;
605 if(!team
) team
= plr
->GetTeam();
608 plr
->CastSpell(plr
, SpellID
, true);
612 void BattleGround::RewardHonorToTeam(uint32 Honor
, uint32 TeamID
)
614 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
616 if (itr
->second
.OfflineRemoveTime
)
618 Player
*plr
= sObjectMgr
.GetPlayer(itr
->first
);
622 sLog
.outError("BattleGround:RewardHonorToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr
->first
));
626 uint32 team
= itr
->second
.Team
;
627 if(!team
) team
= plr
->GetTeam();
630 UpdatePlayerScore(plr
, SCORE_BONUS_HONOR
, Honor
);
634 void BattleGround::RewardReputationToTeam(uint32 faction_id
, uint32 Reputation
, uint32 TeamID
)
636 FactionEntry
const* factionEntry
= sFactionStore
.LookupEntry(faction_id
);
641 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
643 if (itr
->second
.OfflineRemoveTime
)
645 Player
*plr
= sObjectMgr
.GetPlayer(itr
->first
);
649 sLog
.outError("BattleGround:RewardReputationToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr
->first
));
653 uint32 team
= itr
->second
.Team
;
654 if(!team
) team
= plr
->GetTeam();
657 plr
->GetReputationMgr().ModifyReputation(factionEntry
, Reputation
);
661 void BattleGround::UpdateWorldState(uint32 Field
, uint32 Value
)
664 sBattleGroundMgr
.BuildUpdateWorldStatePacket(&data
, Field
, Value
);
665 SendPacketToAll(&data
);
668 void BattleGround::UpdateWorldStateForPlayer(uint32 Field
, uint32 Value
, Player
*Source
)
671 sBattleGroundMgr
.BuildUpdateWorldStatePacket(&data
, Field
, Value
);
672 Source
->GetSession()->SendPacket(&data
);
675 void BattleGround::EndBattleGround(uint32 winner
)
677 this->RemoveFromBGFreeSlotQueue();
679 ArenaTeam
* winner_arena_team
= NULL
;
680 ArenaTeam
* loser_arena_team
= NULL
;
681 uint32 loser_rating
= 0;
682 uint32 winner_rating
= 0;
686 if (winner
== ALLIANCE
)
688 winmsg_id
= isBattleGround() ? LANG_BG_A_WINS
: LANG_ARENA_GOLD_WINS
;
690 PlaySoundToAll(SOUND_ALLIANCE_WINS
); // alliance wins sound
692 SetWinner(WINNER_ALLIANCE
);
694 else if (winner
== HORDE
)
696 winmsg_id
= isBattleGround() ? LANG_BG_H_WINS
: LANG_ARENA_GREEN_WINS
;
698 PlaySoundToAll(SOUND_HORDE_WINS
); // horde wins sound
700 SetWinner(WINNER_HORDE
);
707 SetStatus(STATUS_WAIT_LEAVE
);
708 //we must set it this way, because end time is sent in packet!
709 m_EndTime
= TIME_TO_AUTOREMOVE
;
711 // arena rating calculation
712 if (isArena() && isRated())
714 winner_arena_team
= sObjectMgr
.GetArenaTeamById(GetArenaTeamIdForTeam(winner
));
715 loser_arena_team
= sObjectMgr
.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(winner
)));
716 if (winner_arena_team
&& loser_arena_team
)
718 loser_rating
= loser_arena_team
->GetStats().rating
;
719 winner_rating
= winner_arena_team
->GetStats().rating
;
720 int32 winner_change
= winner_arena_team
->WonAgainst(loser_rating
);
721 int32 loser_change
= loser_arena_team
->LostAgainst(winner_rating
);
722 sLog
.outDebug("--- Winner rating: %u, Loser rating: %u, Winner change: %u, Losser change: %u ---", winner_rating
, loser_rating
, winner_change
, loser_change
);
723 SetArenaTeamRatingChangeForTeam(winner
, winner_change
);
724 SetArenaTeamRatingChangeForTeam(GetOtherTeam(winner
), loser_change
);
728 SetArenaTeamRatingChangeForTeam(ALLIANCE
, 0);
729 SetArenaTeamRatingChangeForTeam(HORDE
, 0);
733 for(BattleGroundPlayerMap::iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
735 uint32 team
= itr
->second
.Team
;
737 if (itr
->second
.OfflineRemoveTime
)
739 //if rated arena match - make member lost!
740 if (isArena() && isRated() && winner_arena_team
&& loser_arena_team
)
743 winner_arena_team
->OfflineMemberLost(itr
->first
, loser_rating
);
745 loser_arena_team
->OfflineMemberLost(itr
->first
, winner_rating
);
749 Player
*plr
= sObjectMgr
.GetPlayer(itr
->first
);
752 sLog
.outError("BattleGround:EndBattleGround Player (GUID: %u) not found!", GUID_LOPART(itr
->first
));
756 // should remove spirit of redemption
757 if (plr
->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION
))
758 plr
->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT
);
762 plr
->ResurrectPlayer(1.0f
);
763 plr
->SpawnCorpseBones();
767 //needed cause else in av some creatures will kill the players at the end
769 plr
->getHostileRefManager().deleteReferences();
772 //this line is obsolete - team is set ALWAYS
773 //if(!team) team = plr->GetTeam();
775 // per player calculation
776 if (isArena() && isRated() && winner_arena_team
&& loser_arena_team
)
780 // update achievement BEFORE personal rating update
781 ArenaTeamMember
* member
= winner_arena_team
->GetMember(plr
->GetGUID());
783 plr
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
, member
->personal_rating
);
785 winner_arena_team
->MemberWon(plr
,loser_rating
);
789 loser_arena_team
->MemberLost(plr
,winner_rating
);
791 // Arena lost => reset the win_rated_arena having the "no_loose" condition
792 plr
->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
, ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE
);
798 RewardMark(plr
,ITEM_WINNER_COUNT
);
799 RewardQuestComplete(plr
);
802 RewardMark(plr
,ITEM_LOSER_COUNT
);
804 plr
->CombatStopWithPets(true);
808 sBattleGroundMgr
.BuildPvpLogDataPacket(&data
, this);
809 plr
->GetSession()->SendPacket(&data
);
811 BattleGroundQueueTypeId bgQueueTypeId
= BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
812 sBattleGroundMgr
.BuildBattleGroundStatusPacket(&data
, this, plr
->GetBattleGroundQueueIndex(bgQueueTypeId
), STATUS_IN_PROGRESS
, TIME_TO_AUTOREMOVE
, GetStartTime(), GetArenaType());
813 plr
->GetSession()->SendPacket(&data
);
814 plr
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND
, 1);
817 if (isArena() && isRated() && winner_arena_team
&& loser_arena_team
)
819 // update arena points only after increasing the player's match count!
820 //obsolete: winner_arena_team->UpdateArenaPointsHelper();
821 //obsolete: loser_arena_team->UpdateArenaPointsHelper();
822 // save the stat changes
823 winner_arena_team
->SaveToDB();
824 loser_arena_team
->SaveToDB();
825 // send updated arena team stats to players
826 // this way all arena team members will get notified, not only the ones who participated in this match
827 winner_arena_team
->NotifyStatsChanged();
828 loser_arena_team
->NotifyStatsChanged();
832 SendMessageToAll(winmsg_id
, CHAT_MSG_BG_SYSTEM_NEUTRAL
);
835 uint32
BattleGround::GetBonusHonorFromKill(uint32 kills
) const
837 //variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill)
838 return MaNGOS::Honor::hk_honor_at_level(GetMaxLevel(), kills
);
841 uint32
BattleGround::GetBattlemasterEntry() const
845 case BATTLEGROUND_AV
: return 15972;
846 case BATTLEGROUND_WS
: return 14623;
847 case BATTLEGROUND_AB
: return 14879;
848 case BATTLEGROUND_EY
: return 22516;
849 case BATTLEGROUND_NA
: return 20200;
854 void BattleGround::RewardMark(Player
*plr
,uint32 count
)
856 BattleGroundMarks mark
;
860 case BATTLEGROUND_AV
:
862 if (count
== ITEM_WINNER_COUNT
)
863 mark
= SPELL_AV_MARK_WINNER
;
865 mark
= SPELL_AV_MARK_LOSER
;
867 case BATTLEGROUND_WS
:
869 if (count
== ITEM_WINNER_COUNT
)
870 mark
= SPELL_WS_MARK_WINNER
;
872 mark
= SPELL_WS_MARK_LOSER
;
874 case BATTLEGROUND_AB
:
876 if (count
== ITEM_WINNER_COUNT
)
877 mark
= SPELL_AB_MARK_WINNER
;
879 mark
= SPELL_AB_MARK_LOSER
;
881 case BATTLEGROUND_EY
:
883 mark
= ITEM_EY_MARK_OF_HONOR
;
890 RewardSpellCast(plr
,mark
);
892 RewardItem(plr
,mark
,count
);
895 void BattleGround::RewardSpellCast(Player
*plr
, uint32 spell_id
)
897 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
898 if (plr
->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE
))
901 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spell_id
);
904 sLog
.outError("Battleground reward casting spell %u not exist.",spell_id
);
908 plr
->CastSpell(plr
, spellInfo
, true);
911 void BattleGround::RewardItem(Player
*plr
, uint32 item_id
, uint32 count
)
913 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
914 if (plr
->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE
))
917 ItemPosCountVec dest
;
918 uint32 no_space_count
= 0;
919 uint8 msg
= plr
->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, item_id
, count
, &no_space_count
);
921 if( msg
== EQUIP_ERR_ITEM_NOT_FOUND
)
923 sLog
.outErrorDb("Battleground reward item (Entry %u) not exist in `item_template`.",item_id
);
927 if( msg
!= EQUIP_ERR_OK
) // convert to possible store amount
928 count
-= no_space_count
;
930 if( count
!= 0 && !dest
.empty()) // can add some
931 if (Item
* item
= plr
->StoreNewItem( dest
, item_id
, true, 0))
932 plr
->SendNewItem(item
,count
,true,false);
934 if (no_space_count
> 0)
935 SendRewardMarkByMail(plr
,item_id
,no_space_count
);
938 void BattleGround::SendRewardMarkByMail(Player
*plr
,uint32 mark
, uint32 count
)
940 uint32 bmEntry
= GetBattlemasterEntry();
944 ItemPrototype
const* markProto
= ObjectMgr::GetItemPrototype(mark
);
948 if (Item
* markItem
= Item::CreateItem(mark
,count
,plr
))
950 // save new item before send
951 markItem
->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
953 // subject: item name
954 std::string subject
= markProto
->Name1
;
955 int loc_idx
= plr
->GetSession()->GetSessionDbLocaleIndex();
957 if (ItemLocale
const *il
= sObjectMgr
.GetItemLocale(markProto
->ItemId
))
958 if (il
->Name
.size() > size_t(loc_idx
) && !il
->Name
[loc_idx
].empty())
959 subject
= il
->Name
[loc_idx
];
962 std::string textFormat
= plr
->GetSession()->GetMangosString(LANG_BG_MARK_BY_MAIL
);
964 snprintf(textBuf
,300,textFormat
.c_str(),GetName(),GetName());
965 uint32 itemTextId
= sObjectMgr
.CreateItemText( textBuf
);
967 MailDraft(subject
, itemTextId
)
969 .SendMailTo(plr
, MailSender(MAIL_CREATURE
, bmEntry
));
973 void BattleGround::RewardQuestComplete(Player
*plr
)
978 case BATTLEGROUND_AV
:
979 quest
= SPELL_AV_QUEST_REWARD
;
981 case BATTLEGROUND_WS
:
982 quest
= SPELL_WS_QUEST_REWARD
;
984 case BATTLEGROUND_AB
:
985 quest
= SPELL_AB_QUEST_REWARD
;
987 case BATTLEGROUND_EY
:
988 quest
= SPELL_EY_QUEST_REWARD
;
994 RewardSpellCast(plr
, quest
);
997 void BattleGround::BlockMovement(Player
*plr
)
999 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()
1002 void BattleGround::RemovePlayerAtLeave(uint64 guid
, bool Transport
, bool SendPacket
)
1004 uint32 team
= GetPlayerTeam(guid
);
1005 bool participant
= false;
1006 // Remove from lists/maps
1007 BattleGroundPlayerMap::iterator itr
= m_Players
.find(guid
);
1008 if (itr
!= m_Players
.end())
1010 UpdatePlayersCountByTeam(team
, true); // -1 player
1011 m_Players
.erase(itr
);
1012 // check if the player was a participant of the match, or only entered through gm command (goname)
1016 BattleGroundScoreMap::iterator itr2
= m_PlayerScores
.find(guid
);
1017 if (itr2
!= m_PlayerScores
.end())
1019 delete itr2
->second
; // delete player's score
1020 m_PlayerScores
.erase(itr2
);
1023 Player
*plr
= sObjectMgr
.GetPlayer(guid
);
1025 // should remove spirit of redemption
1026 if (plr
&& plr
->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION
))
1027 plr
->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT
);
1029 if(plr
&& !plr
->isAlive()) // resurrect on exit
1031 plr
->ResurrectPlayer(1.0f
);
1032 plr
->SpawnCorpseBones();
1035 RemovePlayer(plr
, guid
); // BG subclass specific code
1037 if(participant
) // if the player was a match participant, remove auras, calc rating, update queue
1039 BattleGroundTypeId bgTypeId
= GetTypeID();
1040 BattleGroundQueueTypeId bgQueueTypeId
= BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1043 plr
->ClearAfkReports();
1045 if(!team
) team
= plr
->GetTeam();
1047 // if arena, remove the specific arena auras
1050 plr
->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out
1051 bgTypeId
=BATTLEGROUND_AA
; // set the bg type to all arenas (it will be used for queue refreshing)
1053 // unsummon current and summon old pet if there was one and there isn't a current pet
1054 plr
->RemovePet(NULL
, PET_SAVE_NOT_IN_SLOT
);
1055 plr
->ResummonPetTemporaryUnSummonedIfAny();
1057 if (isRated() && GetStatus() == STATUS_IN_PROGRESS
)
1059 //left a rated match while the encounter was in progress, consider as loser
1060 ArenaTeam
* winner_arena_team
= sObjectMgr
.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team
)));
1061 ArenaTeam
* loser_arena_team
= sObjectMgr
.GetArenaTeamById(GetArenaTeamIdForTeam(team
));
1062 if (winner_arena_team
&& loser_arena_team
)
1063 loser_arena_team
->MemberLost(plr
,winner_arena_team
->GetRating());
1069 sBattleGroundMgr
.BuildBattleGroundStatusPacket(&data
, this, plr
->GetBattleGroundQueueIndex(bgQueueTypeId
), STATUS_NONE
, 0, 0, 0);
1070 plr
->GetSession()->SendPacket(&data
);
1073 // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg
1074 plr
->RemoveBattleGroundQueueId(bgQueueTypeId
);
1077 // removing offline participant
1079 if (isRated() && GetStatus() == STATUS_IN_PROGRESS
)
1081 //left a rated match while the encounter was in progress, consider as loser
1082 ArenaTeam
* others_arena_team
= sObjectMgr
.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team
)));
1083 ArenaTeam
* players_arena_team
= sObjectMgr
.GetArenaTeamById(GetArenaTeamIdForTeam(team
));
1084 if (others_arena_team
&& players_arena_team
)
1085 players_arena_team
->OfflineMemberLost(guid
, others_arena_team
->GetRating());
1089 // remove from raid group if player is member
1090 if (Group
*group
= GetBgRaid(team
))
1092 if( !group
->RemoveMember(guid
, 0) ) // group was disbanded
1094 SetBgRaid(team
, NULL
);
1098 DecreaseInvitedCount(team
);
1099 //we should update battleground queue, but only if bg isn't ending
1100 if (isBattleGround() && GetStatus() < STATUS_WAIT_LEAVE
)
1102 // a player has left the battleground, so there are free slots -> add to queue
1103 AddToBGFreeSlotQueue();
1104 sBattleGroundMgr
.ScheduleQueueUpdate(0, 0, bgQueueTypeId
, bgTypeId
, GetBracketId());
1109 sBattleGroundMgr
.BuildPlayerLeftBattleGroundPacket(&data
, guid
);
1110 SendPacketToTeam(team
, &data
, plr
, false);
1115 // Do next only if found in battleground
1116 plr
->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE
); // We're not in BG.
1117 // reset destination bg team
1121 plr
->TeleportToBGEntryPoint();
1123 sLog
.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr
->GetName());
1126 //battleground object will be deleted next BattleGround::Update() call
1129 // this method is called when no players remains in battleground
1130 void BattleGround::Reset()
1132 SetWinner(WINNER_NONE
);
1133 SetStatus(STATUS_WAIT_QUEUE
);
1141 // door-event2 is always 0
1142 m_ActiveEvents
[BG_EVENT_DOOR
] = 0;
1145 m_ActiveEvents
[ARENA_BUFF_EVENT
] = BG_EVENT_NONE
;
1146 m_ArenaBuffSpawned
= false;
1149 if (m_InvitedAlliance
> 0 || m_InvitedHorde
> 0)
1150 sLog
.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance
, m_InvitedHorde
);
1152 m_InvitedAlliance
= 0;
1154 m_InBGFreeSlotQueue
= false;
1158 for(BattleGroundScoreMap::const_iterator itr
= m_PlayerScores
.begin(); itr
!= m_PlayerScores
.end(); ++itr
)
1160 m_PlayerScores
.clear();
1163 void BattleGround::StartBattleGround()
1167 // add BG to free slot queue
1168 AddToBGFreeSlotQueue();
1170 // add bg to update list
1171 // This must be done here, because we need to have already invited some players when first BG::Update() method is executed
1172 // and it doesn't matter if we call StartBattleGround() more times, because m_BattleGrounds is a map and instance id never changes
1173 sBattleGroundMgr
.AddBattleGround(GetInstanceID(), GetTypeID(), this);
1176 void BattleGround::AddPlayer(Player
*plr
)
1178 // remove afk from player
1179 if (plr
->HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_AFK
))
1182 // score struct must be created in inherited class
1184 uint64 guid
= plr
->GetGUID();
1185 uint32 team
= plr
->GetBGTeam();
1187 BattleGroundPlayer bp
;
1188 bp
.OfflineRemoveTime
= 0;
1192 m_Players
[guid
] = bp
;
1194 UpdatePlayersCountByTeam(team
, false); // +1 player
1197 sBattleGroundMgr
.BuildPlayerJoinedBattleGroundPacket(&data
, plr
);
1198 SendPacketToTeam(team
, &data
, plr
, false);
1200 // add arena specific auras
1203 plr
->RemoveArenaSpellCooldowns();
1204 plr
->RemoveArenaAuras();
1205 plr
->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT
);
1206 if(team
== ALLIANCE
) // gold
1208 if (plr
->GetTeam() == HORDE
)
1209 plr
->CastSpell(plr
, SPELL_HORDE_GOLD_FLAG
,true);
1211 plr
->CastSpell(plr
, SPELL_ALLIANCE_GOLD_FLAG
,true);
1215 if (plr
->GetTeam() == HORDE
)
1216 plr
->CastSpell(plr
, SPELL_HORDE_GREEN_FLAG
,true);
1218 plr
->CastSpell(plr
, SPELL_ALLIANCE_GREEN_FLAG
,true);
1221 plr
->DestroyConjuredItems(true);
1222 plr
->UnsummonPetTemporaryIfAny();
1224 if(GetStatus() == STATUS_WAIT_JOIN
) // not started yet
1226 plr
->CastSpell(plr
, SPELL_ARENA_PREPARATION
, true);
1228 plr
->SetHealth(plr
->GetMaxHealth());
1229 plr
->SetPower(POWER_MANA
, plr
->GetMaxPower(POWER_MANA
));
1234 if(GetStatus() == STATUS_WAIT_JOIN
) // not started yet
1235 plr
->CastSpell(plr
, SPELL_PREPARATION
, true); // reduces all mana cost of spells.
1238 plr
->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE
, ACHIEVEMENT_CRITERIA_CONDITION_MAP
, GetMapId());
1239 plr
->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE
, ACHIEVEMENT_CRITERIA_CONDITION_MAP
, GetMapId());
1241 // setup BG group membership
1242 PlayerAddedToBGCheckIfBGIsRunning(plr
);
1243 AddOrSetPlayerToCorrectBgGroup(plr
, guid
, team
);
1246 sLog
.outDetail("BATTLEGROUND: Player %s joined the battle.", plr
->GetName());
1249 /* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */
1250 void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player
*plr
, uint64 plr_guid
, uint32 team
)
1252 Group
* group
= GetBgRaid(team
);
1253 if(!group
) // first player joined
1256 SetBgRaid(team
, group
);
1257 group
->Create(plr_guid
, plr
->GetName());
1259 else // raid already exist
1261 if (group
->IsMember(plr_guid
))
1263 uint8 subgroup
= group
->GetMemberGroup(plr_guid
);
1264 plr
->SetBattleGroundRaid(group
, subgroup
);
1268 group
->AddMember(plr_guid
, plr
->GetName());
1269 if (Group
* originalGroup
= plr
->GetOriginalGroup())
1270 if (originalGroup
->IsLeader(plr_guid
))
1271 group
->ChangeLeader(plr_guid
);
1276 // This method should be called when player logs into running battleground
1277 void BattleGround::EventPlayerLoggedIn(Player
* player
, uint64 plr_guid
)
1279 // player is correct pointer
1280 for(std::deque
<uint64
>::iterator itr
= m_OfflineQueue
.begin(); itr
!= m_OfflineQueue
.end(); ++itr
)
1282 if (*itr
== plr_guid
)
1284 m_OfflineQueue
.erase(itr
);
1288 m_Players
[plr_guid
].OfflineRemoveTime
= 0;
1289 PlayerAddedToBGCheckIfBGIsRunning(player
);
1290 // if battleground is starting, then add preparation aura
1291 // we don't have to do that, because preparation aura isn't removed when player logs out
1294 // This method should be called when player logs out from running battleground
1295 void BattleGround::EventPlayerLoggedOut(Player
* player
)
1297 // player is correct pointer, it is checked in WorldSession::LogoutPlayer()
1298 m_OfflineQueue
.push_back(player
->GetGUID());
1299 m_Players
[player
->GetGUID()].OfflineRemoveTime
= sWorld
.GetGameTime() + MAX_OFFLINE_TIME
;
1300 if (GetStatus() == STATUS_IN_PROGRESS
)
1302 // drop flag and handle other cleanups
1303 RemovePlayer(player
, player
->GetGUID());
1305 // 1 player is logging out, if it is the last, then end arena!
1307 if (GetAlivePlayersCountByTeam(player
->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player
->GetTeam())))
1308 EndBattleGround(GetOtherTeam(player
->GetTeam()));
1312 /* This method should be called only once ... it adds pointer to queue */
1313 void BattleGround::AddToBGFreeSlotQueue()
1315 // make sure to add only once
1316 if (!m_InBGFreeSlotQueue
&& isBattleGround())
1318 sBattleGroundMgr
.BGFreeSlotQueue
[m_TypeID
].push_front(this);
1319 m_InBGFreeSlotQueue
= true;
1323 /* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/
1324 void BattleGround::RemoveFromBGFreeSlotQueue()
1326 // set to be able to re-add if needed
1327 m_InBGFreeSlotQueue
= false;
1328 // uncomment this code when battlegrounds will work like instances
1329 for (BGFreeSlotQueueType::iterator itr
= sBattleGroundMgr
.BGFreeSlotQueue
[m_TypeID
].begin(); itr
!= sBattleGroundMgr
.BGFreeSlotQueue
[m_TypeID
].end(); ++itr
)
1331 if ((*itr
)->GetInstanceID() == m_InstanceID
)
1333 sBattleGroundMgr
.BGFreeSlotQueue
[m_TypeID
].erase(itr
);
1339 // get the number of free slots for team
1340 // returns the number how many players can join battleground to MaxPlayersPerTeam
1341 uint32
BattleGround::GetFreeSlotsForTeam(uint32 Team
) const
1343 //return free slot count to MaxPlayerPerTeam
1344 if (GetStatus() == STATUS_WAIT_JOIN
|| GetStatus() == STATUS_IN_PROGRESS
)
1345 return (GetInvitedCount(Team
) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team
) : 0;
1350 bool BattleGround::HasFreeSlots() const
1352 return GetPlayersSize() < GetMaxPlayers();
1355 void BattleGround::UpdatePlayerScore(Player
*Source
, uint32 type
, uint32 value
)
1357 //this procedure is called from virtual function implemented in bg subclass
1358 BattleGroundScoreMap::const_iterator itr
= m_PlayerScores
.find(Source
->GetGUID());
1360 if(itr
== m_PlayerScores
.end()) // player not found...
1365 case SCORE_KILLING_BLOWS
: // Killing blows
1366 itr
->second
->KillingBlows
+= value
;
1368 case SCORE_DEATHS
: // Deaths
1369 itr
->second
->Deaths
+= value
;
1371 case SCORE_HONORABLE_KILLS
: // Honorable kills
1372 itr
->second
->HonorableKills
+= value
;
1374 case SCORE_BONUS_HONOR
: // Honor bonus
1375 // do not add honor in arenas
1376 if (isBattleGround())
1378 // reward honor instantly
1379 if (Source
->RewardHonor(NULL
, 1, value
))
1380 itr
->second
->BonusHonor
+= value
;
1383 //used only in EY, but in MSG_PVP_LOG_DATA opcode
1384 case SCORE_DAMAGE_DONE
: // Damage Done
1385 itr
->second
->DamageDone
+= value
;
1387 case SCORE_HEALING_DONE
: // Healing Done
1388 itr
->second
->HealingDone
+= value
;
1391 sLog
.outError("BattleGround: Unknown player score type %u", type
);
1396 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
)
1398 // must be created this way, adding to godatamap would add it to the base map of the instance
1399 // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
1400 // so we must create it specific for this instance
1401 GameObject
* go
= new GameObject
;
1402 if(!go
->Create(sObjectMgr
.GenerateLowGuid(HIGHGUID_GAMEOBJECT
),entry
, GetBgMap(),
1403 PHASEMASK_NORMAL
, x
,y
,z
,o
,rotation0
,rotation1
,rotation2
,rotation3
,100,GO_STATE_READY
))
1405 sLog
.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry
);
1406 sLog
.outError("Cannot create gameobject template %u! BattleGround not created!", entry
);
1411 uint32 guid = go->GetGUIDLow();
1413 // without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata
1414 // 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
1415 GameObjectData& data = sObjectMgr.NewGOData(guid);
1418 data.mapid = GetMapId();
1422 data.orientation = o;
1423 data.rotation0 = rotation0;
1424 data.rotation1 = rotation1;
1425 data.rotation2 = rotation2;
1426 data.rotation3 = rotation3;
1427 data.spawntimesecs = respawnTime;
1429 data.animprogress = 100;
1432 // add to world, so it can be later looked up from HashMapHolder
1434 m_BgObjects
[type
] = go
->GetGUID();
1438 //some doors aren't despawned so we cannot handle their closing in gameobject::update()
1439 //it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
1440 void BattleGround::DoorClose(uint64
const& guid
)
1442 GameObject
*obj
= GetBgMap()->GetGameObject(guid
);
1445 //if doors are open, close it
1446 if (obj
->getLootState() == GO_ACTIVATED
&& obj
->GetGoState() != GO_STATE_READY
)
1448 //change state to allow door to be closed
1449 obj
->SetLootState(GO_READY
);
1450 obj
->UseDoorOrButton(RESPAWN_ONE_DAY
);
1455 sLog
.outError("BattleGround: Door object not found (cannot close doors)");
1459 void BattleGround::DoorOpen(uint64
const& guid
)
1461 GameObject
*obj
= GetBgMap()->GetGameObject(guid
);
1464 //change state to be sure they will be opened
1465 obj
->SetLootState(GO_READY
);
1466 obj
->UseDoorOrButton(RESPAWN_ONE_DAY
);
1470 sLog
.outError("BattleGround: Door object not found! - doors will be closed.");
1474 void BattleGround::OnObjectDBLoad(Creature
* creature
)
1476 const BattleGroundEventIdx eventId
= sBattleGroundMgr
.GetCreatureEventIndex(creature
->GetDBTableGUIDLow());
1477 if (eventId
.event1
== BG_EVENT_NONE
)
1479 m_EventObjects
[MAKE_PAIR32(eventId
.event1
, eventId
.event2
)].creatures
.push_back(creature
->GetGUID());
1480 if (!IsActiveEvent(eventId
.event1
, eventId
.event2
))
1481 SpawnBGCreature(creature
->GetGUID(), RESPAWN_ONE_DAY
);
1484 uint64
BattleGround::GetSingleCreatureGuid(uint8 event1
, uint8 event2
)
1486 BGCreatures::const_iterator itr
= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].creatures
.begin();
1487 if (itr
!= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].creatures
.end())
1492 void BattleGround::OnObjectDBLoad(GameObject
* obj
)
1494 const BattleGroundEventIdx eventId
= sBattleGroundMgr
.GetGameObjectEventIndex(obj
->GetDBTableGUIDLow());
1495 if (eventId
.event1
== BG_EVENT_NONE
)
1497 m_EventObjects
[MAKE_PAIR32(eventId
.event1
, eventId
.event2
)].gameobjects
.push_back(obj
->GetGUID());
1498 if (!IsActiveEvent(eventId
.event1
, eventId
.event2
))
1500 SpawnBGObject(obj
->GetGUID(), RESPAWN_ONE_DAY
);
1504 // it's possible, that doors aren't spawned anymore (wsg)
1505 if (GetStatus() >= STATUS_IN_PROGRESS
&& IsDoor(eventId
.event1
, eventId
.event2
))
1506 DoorOpen(obj
->GetGUID());
1510 bool BattleGround::IsDoor(uint8 event1
, uint8 event2
)
1512 if (event1
== BG_EVENT_DOOR
)
1516 sLog
.outError("BattleGround too high event2 for event1:%i", event1
);
1524 void BattleGround::OpenDoorEvent(uint8 event1
, uint8 event2
/*=0*/)
1526 if (!IsDoor(event1
, event2
))
1528 sLog
.outError("BattleGround:OpenDoorEvent this is no door event1:%u event2:%u", event1
, event2
);
1531 if (!IsActiveEvent(event1
, event2
)) // maybe already despawned (eye)
1533 sLog
.outError("BattleGround:OpenDoorEvent this event isn't active event1:%u event2:%u", event1
, event2
);
1536 BGObjects::const_iterator itr
= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].gameobjects
.begin();
1537 for(; itr
!= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].gameobjects
.end(); ++itr
)
1541 void BattleGround::SpawnEvent(uint8 event1
, uint8 event2
, bool spawn
)
1543 // stop if we want to spawn something which was already spawned
1544 // or despawn something which was already despawned
1545 if (event2
== BG_EVENT_NONE
|| (spawn
&& m_ActiveEvents
[event1
] == event2
)
1546 || (!spawn
&& m_ActiveEvents
[event1
] != event2
))
1551 // if event gets spawned, the current active event mus get despawned
1552 SpawnEvent(event1
, m_ActiveEvents
[event1
], false);
1553 m_ActiveEvents
[event1
] = event2
; // set this event to active
1556 m_ActiveEvents
[event1
] = BG_EVENT_NONE
; // no event active if event2 gets despawned
1558 BGCreatures::const_iterator itr
= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].creatures
.begin();
1559 for(; itr
!= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].creatures
.end(); ++itr
)
1560 SpawnBGCreature(*itr
, (spawn
) ? RESPAWN_IMMEDIATELY
: RESPAWN_ONE_DAY
);
1561 BGObjects::const_iterator itr2
= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].gameobjects
.begin();
1562 for(; itr2
!= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].gameobjects
.end(); ++itr2
)
1563 SpawnBGObject(*itr2
, (spawn
) ? RESPAWN_IMMEDIATELY
: RESPAWN_ONE_DAY
);
1566 void BattleGround::SpawnBGObject(uint64
const& guid
, uint32 respawntime
)
1568 Map
* map
= GetBgMap();
1570 GameObject
*obj
= map
->GetGameObject(guid
);
1573 if (respawntime
== 0)
1575 //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again
1576 if (obj
->getLootState() == GO_JUST_DEACTIVATED
)
1577 obj
->SetLootState(GO_READY
);
1578 obj
->SetRespawnTime(0);
1584 obj
->SetRespawnTime(respawntime
);
1585 obj
->SetLootState(GO_JUST_DEACTIVATED
);
1589 void BattleGround::SpawnBGCreature(uint64
const& guid
, uint32 respawntime
)
1591 Map
* map
= GetBgMap();
1593 Creature
* obj
= map
->GetCreature(guid
);
1596 if (respawntime
== 0)
1604 obj
->setDeathState(JUST_DIED
);
1605 obj
->SetRespawnDelay(respawntime
);
1606 obj
->RemoveCorpse();
1610 bool BattleGround::DelObject(uint32 type
)
1612 if (!m_BgObjects
[type
])
1615 GameObject
*obj
= GetBgMap()->GetGameObject(m_BgObjects
[type
]);
1618 sLog
.outError("Can't find gobject guid: %u",GUID_LOPART(m_BgObjects
[type
]));
1622 obj
->SetRespawnTime(0); // not save respawn time
1624 m_BgObjects
[type
] = 0;
1628 void BattleGround::SendMessageToAll(int32 entry
, ChatMsg type
, Player
const* source
)
1630 MaNGOS::BattleGroundChatBuilder
bg_builder(type
, entry
, source
);
1631 MaNGOS::LocalizedPacketDo
<MaNGOS::BattleGroundChatBuilder
> bg_do(bg_builder
);
1632 BroadcastWorker(bg_do
);
1635 void BattleGround::SendYellToAll(int32 entry
, uint32 language
, uint64
const& guid
)
1637 Creature
* source
= GetBgMap()->GetCreature(guid
);
1640 MaNGOS::BattleGroundYellBuilder
bg_builder(language
, entry
, source
);
1641 MaNGOS::LocalizedPacketDo
<MaNGOS::BattleGroundYellBuilder
> bg_do(bg_builder
);
1642 BroadcastWorker(bg_do
);
1645 void BattleGround::PSendMessageToAll(int32 entry
, ChatMsg type
, Player
const* source
, ...)
1648 va_start(ap
, source
);
1650 MaNGOS::BattleGroundChatBuilder
bg_builder(type
, entry
, source
, &ap
);
1651 MaNGOS::LocalizedPacketDo
<MaNGOS::BattleGroundChatBuilder
> bg_do(bg_builder
);
1652 BroadcastWorker(bg_do
);
1657 void BattleGround::SendMessage2ToAll(int32 entry
, ChatMsg type
, Player
const* source
, int32 arg1
, int32 arg2
)
1659 MaNGOS::BattleGround2ChatBuilder
bg_builder(type
, entry
, source
, arg1
, arg2
);
1660 MaNGOS::LocalizedPacketDo
<MaNGOS::BattleGround2ChatBuilder
> bg_do(bg_builder
);
1661 BroadcastWorker(bg_do
);
1664 void BattleGround::SendYell2ToAll(int32 entry
, uint32 language
, uint64
const& guid
, int32 arg1
, int32 arg2
)
1666 Creature
* source
= GetBgMap()->GetCreature(guid
);
1669 MaNGOS::BattleGround2YellBuilder
bg_builder(language
, entry
, source
, arg1
, arg2
);
1670 MaNGOS::LocalizedPacketDo
<MaNGOS::BattleGround2YellBuilder
> bg_do(bg_builder
);
1671 BroadcastWorker(bg_do
);
1674 void BattleGround::EndNow()
1676 RemoveFromBGFreeSlotQueue();
1677 SetStatus(STATUS_WAIT_LEAVE
);
1683 buffs aren't spawned/despawned when players captures anything
1684 buffs are in their positions when battleground starts
1686 void BattleGround::HandleTriggerBuff(uint64
const& go_guid
)
1688 GameObject
*obj
= GetBgMap()->GetGameObject(go_guid
);
1689 if (!obj
|| obj
->GetGoType() != GAMEOBJECT_TYPE_TRAP
|| !obj
->isSpawned())
1692 // static buffs are already handled just by database and don't need
1693 // battleground code
1696 obj
->SetLootState(GO_JUST_DEACTIVATED
); // can be despawned or destroyed
1700 // change buff type, when buff is used:
1701 // TODO this can be done when poolsystem works for instances
1702 int32 index
= m_BgObjects
.size() - 1;
1703 while (index
>= 0 && m_BgObjects
[index
] != go_guid
)
1707 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());
1711 //randomly select new buff
1712 uint8 buff
= urand(0, 2);
1713 uint32 entry
= obj
->GetEntry();
1714 if (m_BuffChange
&& entry
!= Buff_Entries
[buff
])
1716 //despawn current buff
1717 SpawnBGObject(m_BgObjects
[index
], RESPAWN_ONE_DAY
);
1718 //set index for new one
1719 for (uint8 currBuffTypeIndex
= 0; currBuffTypeIndex
< 3; ++currBuffTypeIndex
)
1721 if (entry
== Buff_Entries
[currBuffTypeIndex
])
1723 index
-= currBuffTypeIndex
;
1729 SpawnBGObject(m_BgObjects
[index
], BUFF_RESPAWN_TIME
);
1732 void BattleGround::HandleKillPlayer( Player
*player
, Player
*killer
)
1735 UpdatePlayerScore(player
, SCORE_DEATHS
, 1);
1737 // add +1 kills to group and +1 killing_blows to killer
1740 UpdatePlayerScore(killer
, SCORE_HONORABLE_KILLS
, 1);
1741 UpdatePlayerScore(killer
, SCORE_KILLING_BLOWS
, 1);
1743 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
1745 Player
*plr
= sObjectMgr
.GetPlayer(itr
->first
);
1747 if (!plr
|| plr
== killer
)
1750 if (plr
->GetTeam() == killer
->GetTeam() && plr
->IsAtGroupRewardDistance(player
))
1751 UpdatePlayerScore(plr
, SCORE_HONORABLE_KILLS
, 1);
1755 // to be able to remove insignia -- ONLY IN BattleGrounds
1757 player
->SetFlag( UNIT_FIELD_FLAGS
, UNIT_FLAG_SKINNABLE
);
1760 // return the player's team based on battlegroundplayer info
1761 // used in same faction arena matches mainly
1762 uint32
BattleGround::GetPlayerTeam(uint64 guid
)
1764 BattleGroundPlayerMap::const_iterator itr
= m_Players
.find(guid
);
1765 if (itr
!=m_Players
.end())
1766 return itr
->second
.Team
;
1770 bool BattleGround::IsPlayerInBattleGround(uint64 guid
)
1772 BattleGroundPlayerMap::const_iterator itr
= m_Players
.find(guid
);
1773 if (itr
!= m_Players
.end())
1778 void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player
* plr
)
1780 if (GetStatus() != STATUS_WAIT_LEAVE
)
1784 BattleGroundQueueTypeId bgQueueTypeId
= BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1788 sBattleGroundMgr
.BuildPvpLogDataPacket(&data
, this);
1789 plr
->GetSession()->SendPacket(&data
);
1791 sBattleGroundMgr
.BuildBattleGroundStatusPacket(&data
, this, plr
->GetBattleGroundQueueIndex(bgQueueTypeId
), STATUS_IN_PROGRESS
, GetEndTime(), GetStartTime(), GetArenaType());
1792 plr
->GetSession()->SendPacket(&data
);
1795 uint32
BattleGround::GetAlivePlayersCountByTeam(uint32 Team
) const
1798 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
1800 if (itr
->second
.Team
== Team
)
1802 Player
* pl
= sObjectMgr
.GetPlayer(itr
->first
);
1803 if (pl
&& pl
->isAlive())
1810 void BattleGround::CheckArenaWinConditions()
1812 if (!GetAlivePlayersCountByTeam(ALLIANCE
) && GetPlayersCountByTeam(HORDE
))
1813 EndBattleGround(HORDE
);
1814 else if (GetPlayersCountByTeam(ALLIANCE
) && !GetAlivePlayersCountByTeam(HORDE
))
1815 EndBattleGround(ALLIANCE
);
1818 void BattleGround::SetBgRaid( uint32 TeamID
, Group
*bg_raid
)
1820 Group
* &old_raid
= TeamID
== ALLIANCE
? m_BgRaids
[BG_TEAM_ALLIANCE
] : m_BgRaids
[BG_TEAM_HORDE
];
1821 if(old_raid
) old_raid
->SetBattlegroundGroup(NULL
);
1822 if(bg_raid
) bg_raid
->SetBattlegroundGroup(this);
1826 WorldSafeLocsEntry
const* BattleGround::GetClosestGraveYard( Player
* player
)
1828 return sObjectMgr
.GetClosestGraveYard( player
->GetPositionX(), player
->GetPositionY(), player
->GetPositionZ(), player
->GetMapId(), player
->GetTeam() );
1831 bool BattleGround::IsTeamScoreInRange(uint32 team
, uint32 minScore
, uint32 maxScore
) const
1833 BattleGroundTeamId team_idx
= GetTeamIndexByTeamId(team
);
1834 uint32 score
= (m_TeamScores
[team_idx
] < 0) ? 0 : uint32(m_TeamScores
[team_idx
]);
1835 return score
>= minScore
&& score
<= maxScore
;
1838 void BattleGround::SetBracket( PvPDifficultyEntry
const* bracketEntry
)
1840 m_BracketId
= bracketEntry
->GetBracketId();
1841 SetLevelRange(bracketEntry
->minLevel
,bracketEntry
->maxLevel
);