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
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_QueueId
= QUEUE_ID_MAX_LEVEL_19
;
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
, GetQueueId());
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 SetQueueId(QUEUE_ID_MAX_LEVEL_19
);
1133 SetWinner(WINNER_NONE
);
1134 SetStatus(STATUS_WAIT_QUEUE
);
1142 // door-event2 is always 0
1143 m_ActiveEvents
[BG_EVENT_DOOR
] = 0;
1146 m_ActiveEvents
[ARENA_BUFF_EVENT
] = BG_EVENT_NONE
;
1147 m_ArenaBuffSpawned
= false;
1150 if (m_InvitedAlliance
> 0 || m_InvitedHorde
> 0)
1151 sLog
.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance
, m_InvitedHorde
);
1153 m_InvitedAlliance
= 0;
1155 m_InBGFreeSlotQueue
= false;
1159 for(BattleGroundScoreMap::const_iterator itr
= m_PlayerScores
.begin(); itr
!= m_PlayerScores
.end(); ++itr
)
1161 m_PlayerScores
.clear();
1164 void BattleGround::StartBattleGround()
1168 // add BG to free slot queue
1169 AddToBGFreeSlotQueue();
1171 // add bg to update list
1172 // This must be done here, because we need to have already invited some players when first BG::Update() method is executed
1173 // and it doesn't matter if we call StartBattleGround() more times, because m_BattleGrounds is a map and instance id never changes
1174 sBattleGroundMgr
.AddBattleGround(GetInstanceID(), GetTypeID(), this);
1177 void BattleGround::AddPlayer(Player
*plr
)
1179 // remove afk from player
1180 if (plr
->HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_AFK
))
1183 // score struct must be created in inherited class
1185 uint64 guid
= plr
->GetGUID();
1186 uint32 team
= plr
->GetBGTeam();
1188 BattleGroundPlayer bp
;
1189 bp
.OfflineRemoveTime
= 0;
1193 m_Players
[guid
] = bp
;
1195 UpdatePlayersCountByTeam(team
, false); // +1 player
1198 sBattleGroundMgr
.BuildPlayerJoinedBattleGroundPacket(&data
, plr
);
1199 SendPacketToTeam(team
, &data
, plr
, false);
1201 // add arena specific auras
1204 plr
->RemoveArenaSpellCooldowns();
1205 plr
->RemoveArenaAuras();
1206 plr
->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT
);
1207 if(team
== ALLIANCE
) // gold
1209 if (plr
->GetTeam() == HORDE
)
1210 plr
->CastSpell(plr
, SPELL_HORDE_GOLD_FLAG
,true);
1212 plr
->CastSpell(plr
, SPELL_ALLIANCE_GOLD_FLAG
,true);
1216 if (plr
->GetTeam() == HORDE
)
1217 plr
->CastSpell(plr
, SPELL_HORDE_GREEN_FLAG
,true);
1219 plr
->CastSpell(plr
, SPELL_ALLIANCE_GREEN_FLAG
,true);
1222 plr
->DestroyConjuredItems(true);
1223 plr
->UnsummonPetTemporaryIfAny();
1225 if(GetStatus() == STATUS_WAIT_JOIN
) // not started yet
1227 plr
->CastSpell(plr
, SPELL_ARENA_PREPARATION
, true);
1229 plr
->SetHealth(plr
->GetMaxHealth());
1230 plr
->SetPower(POWER_MANA
, plr
->GetMaxPower(POWER_MANA
));
1235 if(GetStatus() == STATUS_WAIT_JOIN
) // not started yet
1236 plr
->CastSpell(plr
, SPELL_PREPARATION
, true); // reduces all mana cost of spells.
1239 plr
->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE
, ACHIEVEMENT_CRITERIA_CONDITION_MAP
, GetMapId());
1240 plr
->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE
, ACHIEVEMENT_CRITERIA_CONDITION_MAP
, GetMapId());
1242 // setup BG group membership
1243 PlayerAddedToBGCheckIfBGIsRunning(plr
);
1244 AddOrSetPlayerToCorrectBgGroup(plr
, guid
, team
);
1247 sLog
.outDetail("BATTLEGROUND: Player %s joined the battle.", plr
->GetName());
1250 /* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */
1251 void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player
*plr
, uint64 plr_guid
, uint32 team
)
1253 Group
* group
= GetBgRaid(team
);
1254 if(!group
) // first player joined
1257 SetBgRaid(team
, group
);
1258 group
->Create(plr_guid
, plr
->GetName());
1260 else // raid already exist
1262 if (group
->IsMember(plr_guid
))
1264 uint8 subgroup
= group
->GetMemberGroup(plr_guid
);
1265 plr
->SetBattleGroundRaid(group
, subgroup
);
1269 group
->AddMember(plr_guid
, plr
->GetName());
1270 if (Group
* originalGroup
= plr
->GetOriginalGroup())
1271 if (originalGroup
->IsLeader(plr_guid
))
1272 group
->ChangeLeader(plr_guid
);
1277 // This method should be called when player logs into running battleground
1278 void BattleGround::EventPlayerLoggedIn(Player
* player
, uint64 plr_guid
)
1280 // player is correct pointer
1281 for(std::deque
<uint64
>::iterator itr
= m_OfflineQueue
.begin(); itr
!= m_OfflineQueue
.end(); ++itr
)
1283 if (*itr
== plr_guid
)
1285 m_OfflineQueue
.erase(itr
);
1289 m_Players
[plr_guid
].OfflineRemoveTime
= 0;
1290 PlayerAddedToBGCheckIfBGIsRunning(player
);
1291 // if battleground is starting, then add preparation aura
1292 // we don't have to do that, because preparation aura isn't removed when player logs out
1295 // This method should be called when player logs out from running battleground
1296 void BattleGround::EventPlayerLoggedOut(Player
* player
)
1298 // player is correct pointer, it is checked in WorldSession::LogoutPlayer()
1299 m_OfflineQueue
.push_back(player
->GetGUID());
1300 m_Players
[player
->GetGUID()].OfflineRemoveTime
= sWorld
.GetGameTime() + MAX_OFFLINE_TIME
;
1301 if (GetStatus() == STATUS_IN_PROGRESS
)
1303 // drop flag and handle other cleanups
1304 RemovePlayer(player
, player
->GetGUID());
1306 // 1 player is logging out, if it is the last, then end arena!
1308 if (GetAlivePlayersCountByTeam(player
->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player
->GetTeam())))
1309 EndBattleGround(GetOtherTeam(player
->GetTeam()));
1313 /* This method should be called only once ... it adds pointer to queue */
1314 void BattleGround::AddToBGFreeSlotQueue()
1316 // make sure to add only once
1317 if (!m_InBGFreeSlotQueue
&& isBattleGround())
1319 sBattleGroundMgr
.BGFreeSlotQueue
[m_TypeID
].push_front(this);
1320 m_InBGFreeSlotQueue
= true;
1324 /* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/
1325 void BattleGround::RemoveFromBGFreeSlotQueue()
1327 // set to be able to re-add if needed
1328 m_InBGFreeSlotQueue
= false;
1329 // uncomment this code when battlegrounds will work like instances
1330 for (BGFreeSlotQueueType::iterator itr
= sBattleGroundMgr
.BGFreeSlotQueue
[m_TypeID
].begin(); itr
!= sBattleGroundMgr
.BGFreeSlotQueue
[m_TypeID
].end(); ++itr
)
1332 if ((*itr
)->GetInstanceID() == m_InstanceID
)
1334 sBattleGroundMgr
.BGFreeSlotQueue
[m_TypeID
].erase(itr
);
1340 // get the number of free slots for team
1341 // returns the number how many players can join battleground to MaxPlayersPerTeam
1342 uint32
BattleGround::GetFreeSlotsForTeam(uint32 Team
) const
1344 //return free slot count to MaxPlayerPerTeam
1345 if (GetStatus() == STATUS_WAIT_JOIN
|| GetStatus() == STATUS_IN_PROGRESS
)
1346 return (GetInvitedCount(Team
) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team
) : 0;
1351 bool BattleGround::HasFreeSlots() const
1353 return GetPlayersSize() < GetMaxPlayers();
1356 void BattleGround::UpdatePlayerScore(Player
*Source
, uint32 type
, uint32 value
)
1358 //this procedure is called from virtual function implemented in bg subclass
1359 BattleGroundScoreMap::const_iterator itr
= m_PlayerScores
.find(Source
->GetGUID());
1361 if(itr
== m_PlayerScores
.end()) // player not found...
1366 case SCORE_KILLING_BLOWS
: // Killing blows
1367 itr
->second
->KillingBlows
+= value
;
1369 case SCORE_DEATHS
: // Deaths
1370 itr
->second
->Deaths
+= value
;
1372 case SCORE_HONORABLE_KILLS
: // Honorable kills
1373 itr
->second
->HonorableKills
+= value
;
1375 case SCORE_BONUS_HONOR
: // Honor bonus
1376 // do not add honor in arenas
1377 if (isBattleGround())
1379 // reward honor instantly
1380 if (Source
->RewardHonor(NULL
, 1, value
))
1381 itr
->second
->BonusHonor
+= value
;
1384 //used only in EY, but in MSG_PVP_LOG_DATA opcode
1385 case SCORE_DAMAGE_DONE
: // Damage Done
1386 itr
->second
->DamageDone
+= value
;
1388 case SCORE_HEALING_DONE
: // Healing Done
1389 itr
->second
->HealingDone
+= value
;
1392 sLog
.outError("BattleGround: Unknown player score type %u", type
);
1397 bool BattleGround::AddObject(uint32 type
, uint32 entry
, float x
, float y
, float z
, float o
, float rotation0
, float rotation1
, float rotation2
, float rotation3
, uint32 respawnTime
)
1399 // must be created this way, adding to godatamap would add it to the base map of the instance
1400 // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
1401 // so we must create it specific for this instance
1402 GameObject
* go
= new GameObject
;
1403 if(!go
->Create(sObjectMgr
.GenerateLowGuid(HIGHGUID_GAMEOBJECT
),entry
, GetBgMap(),
1404 PHASEMASK_NORMAL
, x
,y
,z
,o
,rotation0
,rotation1
,rotation2
,rotation3
,100,GO_STATE_READY
))
1406 sLog
.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry
);
1407 sLog
.outError("Cannot create gameobject template %u! BattleGround not created!", entry
);
1412 uint32 guid = go->GetGUIDLow();
1414 // without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata
1415 // iirc that was changed, so adding to go data map is no longer required if that was the only function using godata from GameObject without checking if it existed
1416 GameObjectData& data = sObjectMgr.NewGOData(guid);
1419 data.mapid = GetMapId();
1423 data.orientation = o;
1424 data.rotation0 = rotation0;
1425 data.rotation1 = rotation1;
1426 data.rotation2 = rotation2;
1427 data.rotation3 = rotation3;
1428 data.spawntimesecs = respawnTime;
1430 data.animprogress = 100;
1433 // add to world, so it can be later looked up from HashMapHolder
1435 m_BgObjects
[type
] = go
->GetGUID();
1439 //some doors aren't despawned so we cannot handle their closing in gameobject::update()
1440 //it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
1441 void BattleGround::DoorClose(uint64
const& guid
)
1443 GameObject
*obj
= GetBgMap()->GetGameObject(guid
);
1446 //if doors are open, close it
1447 if (obj
->getLootState() == GO_ACTIVATED
&& obj
->GetGoState() != GO_STATE_READY
)
1449 //change state to allow door to be closed
1450 obj
->SetLootState(GO_READY
);
1451 obj
->UseDoorOrButton(RESPAWN_ONE_DAY
);
1456 sLog
.outError("BattleGround: Door object not found (cannot close doors)");
1460 void BattleGround::DoorOpen(uint64
const& guid
)
1462 GameObject
*obj
= GetBgMap()->GetGameObject(guid
);
1465 //change state to be sure they will be opened
1466 obj
->SetLootState(GO_READY
);
1467 obj
->UseDoorOrButton(RESPAWN_ONE_DAY
);
1471 sLog
.outError("BattleGround: Door object not found! - doors will be closed.");
1475 void BattleGround::OnObjectDBLoad(Creature
* creature
)
1477 const BattleGroundEventIdx eventId
= sBattleGroundMgr
.GetCreatureEventIndex(creature
->GetDBTableGUIDLow());
1478 if (eventId
.event1
== BG_EVENT_NONE
)
1480 m_EventObjects
[MAKE_PAIR32(eventId
.event1
, eventId
.event2
)].creatures
.push_back(creature
->GetGUID());
1481 if (!IsActiveEvent(eventId
.event1
, eventId
.event2
))
1482 SpawnBGCreature(creature
->GetGUID(), RESPAWN_ONE_DAY
);
1485 uint64
BattleGround::GetSingleCreatureGuid(uint8 event1
, uint8 event2
)
1487 BGCreatures::const_iterator itr
= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].creatures
.begin();
1488 if (itr
!= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].creatures
.end())
1493 void BattleGround::OnObjectDBLoad(GameObject
* obj
)
1495 const BattleGroundEventIdx eventId
= sBattleGroundMgr
.GetGameObjectEventIndex(obj
->GetDBTableGUIDLow());
1496 if (eventId
.event1
== BG_EVENT_NONE
)
1498 m_EventObjects
[MAKE_PAIR32(eventId
.event1
, eventId
.event2
)].gameobjects
.push_back(obj
->GetGUID());
1499 if (!IsActiveEvent(eventId
.event1
, eventId
.event2
))
1501 SpawnBGObject(obj
->GetGUID(), RESPAWN_ONE_DAY
);
1505 // it's possible, that doors aren't spawned anymore (wsg)
1506 if (GetStatus() >= STATUS_IN_PROGRESS
&& IsDoor(eventId
.event1
, eventId
.event2
))
1507 DoorOpen(obj
->GetGUID());
1511 bool BattleGround::IsDoor(uint8 event1
, uint8 event2
)
1513 if (event1
== BG_EVENT_DOOR
)
1517 sLog
.outError("BattleGround too high event2 for event1:%i", event1
);
1525 void BattleGround::OpenDoorEvent(uint8 event1
, uint8 event2
/*=0*/)
1527 if (!IsDoor(event1
, event2
))
1529 sLog
.outError("BattleGround:OpenDoorEvent this is no door event1:%u event2:%u", event1
, event2
);
1532 if (!IsActiveEvent(event1
, event2
)) // maybe already despawned (eye)
1534 sLog
.outError("BattleGround:OpenDoorEvent this event isn't active event1:%u event2:%u", event1
, event2
);
1537 BGObjects::const_iterator itr
= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].gameobjects
.begin();
1538 for(; itr
!= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].gameobjects
.end(); ++itr
)
1542 void BattleGround::SpawnEvent(uint8 event1
, uint8 event2
, bool spawn
)
1544 // stop if we want to spawn something which was already spawned
1545 // or despawn something which was already despawned
1546 if (event2
== BG_EVENT_NONE
|| (spawn
&& m_ActiveEvents
[event1
] == event2
)
1547 || (!spawn
&& m_ActiveEvents
[event1
] != event2
))
1552 // if event gets spawned, the current active event mus get despawned
1553 SpawnEvent(event1
, m_ActiveEvents
[event1
], false);
1554 m_ActiveEvents
[event1
] = event2
; // set this event to active
1557 m_ActiveEvents
[event1
] = BG_EVENT_NONE
; // no event active if event2 gets despawned
1559 BGCreatures::const_iterator itr
= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].creatures
.begin();
1560 for(; itr
!= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].creatures
.end(); ++itr
)
1561 SpawnBGCreature(*itr
, (spawn
) ? RESPAWN_IMMEDIATELY
: RESPAWN_ONE_DAY
);
1562 BGObjects::const_iterator itr2
= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].gameobjects
.begin();
1563 for(; itr2
!= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].gameobjects
.end(); ++itr2
)
1564 SpawnBGObject(*itr2
, (spawn
) ? RESPAWN_IMMEDIATELY
: RESPAWN_ONE_DAY
);
1567 void BattleGround::SpawnBGObject(uint64
const& guid
, uint32 respawntime
)
1569 Map
* map
= GetBgMap();
1571 GameObject
*obj
= map
->GetGameObject(guid
);
1574 if (respawntime
== 0)
1576 //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again
1577 if (obj
->getLootState() == GO_JUST_DEACTIVATED
)
1578 obj
->SetLootState(GO_READY
);
1579 obj
->SetRespawnTime(0);
1585 obj
->SetRespawnTime(respawntime
);
1586 obj
->SetLootState(GO_JUST_DEACTIVATED
);
1590 void BattleGround::SpawnBGCreature(uint64
const& guid
, uint32 respawntime
)
1592 Map
* map
= GetBgMap();
1594 Creature
* obj
= map
->GetCreature(guid
);
1597 if (respawntime
== 0)
1605 obj
->setDeathState(JUST_DIED
);
1606 obj
->SetRespawnDelay(respawntime
);
1607 obj
->RemoveCorpse();
1611 bool BattleGround::DelObject(uint32 type
)
1613 if (!m_BgObjects
[type
])
1616 GameObject
*obj
= GetBgMap()->GetGameObject(m_BgObjects
[type
]);
1619 sLog
.outError("Can't find gobject guid: %u",GUID_LOPART(m_BgObjects
[type
]));
1623 obj
->SetRespawnTime(0); // not save respawn time
1625 m_BgObjects
[type
] = 0;
1629 void BattleGround::SendMessageToAll(int32 entry
, ChatMsg type
, Player
const* source
)
1631 MaNGOS::BattleGroundChatBuilder
bg_builder(type
, entry
, source
);
1632 MaNGOS::LocalizedPacketDo
<MaNGOS::BattleGroundChatBuilder
> bg_do(bg_builder
);
1633 BroadcastWorker(bg_do
);
1636 void BattleGround::SendYellToAll(int32 entry
, uint32 language
, uint64
const& guid
)
1638 Creature
* source
= GetBgMap()->GetCreature(guid
);
1641 MaNGOS::BattleGroundYellBuilder
bg_builder(language
, entry
, source
);
1642 MaNGOS::LocalizedPacketDo
<MaNGOS::BattleGroundYellBuilder
> bg_do(bg_builder
);
1643 BroadcastWorker(bg_do
);
1646 void BattleGround::PSendMessageToAll(int32 entry
, ChatMsg type
, Player
const* source
, ...)
1649 va_start(ap
, source
);
1651 MaNGOS::BattleGroundChatBuilder
bg_builder(type
, entry
, source
, &ap
);
1652 MaNGOS::LocalizedPacketDo
<MaNGOS::BattleGroundChatBuilder
> bg_do(bg_builder
);
1653 BroadcastWorker(bg_do
);
1658 void BattleGround::SendMessage2ToAll(int32 entry
, ChatMsg type
, Player
const* source
, int32 arg1
, int32 arg2
)
1660 MaNGOS::BattleGround2ChatBuilder
bg_builder(type
, entry
, source
, arg1
, arg2
);
1661 MaNGOS::LocalizedPacketDo
<MaNGOS::BattleGround2ChatBuilder
> bg_do(bg_builder
);
1662 BroadcastWorker(bg_do
);
1665 void BattleGround::SendYell2ToAll(int32 entry
, uint32 language
, uint64
const& guid
, int32 arg1
, int32 arg2
)
1667 Creature
* source
= GetBgMap()->GetCreature(guid
);
1670 MaNGOS::BattleGround2YellBuilder
bg_builder(language
, entry
, source
, arg1
, arg2
);
1671 MaNGOS::LocalizedPacketDo
<MaNGOS::BattleGround2YellBuilder
> bg_do(bg_builder
);
1672 BroadcastWorker(bg_do
);
1675 void BattleGround::EndNow()
1677 RemoveFromBGFreeSlotQueue();
1678 SetStatus(STATUS_WAIT_LEAVE
);
1684 buffs aren't spawned/despawned when players captures anything
1685 buffs are in their positions when battleground starts
1687 void BattleGround::HandleTriggerBuff(uint64
const& go_guid
)
1689 GameObject
*obj
= GetBgMap()->GetGameObject(go_guid
);
1690 if (!obj
|| obj
->GetGoType() != GAMEOBJECT_TYPE_TRAP
|| !obj
->isSpawned())
1693 // static buffs are already handled just by database and don't need
1694 // battleground code
1697 obj
->SetLootState(GO_JUST_DEACTIVATED
); // can be despawned or destroyed
1701 // change buff type, when buff is used:
1702 // TODO this can be done when poolsystem works for instances
1703 int32 index
= m_BgObjects
.size() - 1;
1704 while (index
>= 0 && m_BgObjects
[index
] != go_guid
)
1708 sLog
.outError("BattleGround (Type: %u) has buff gameobject (Guid: %u Entry: %u Type:%u) but it hasn't that object in its internal data",GetTypeID(),GUID_LOPART(go_guid
),obj
->GetEntry(),obj
->GetGoType());
1712 //randomly select new buff
1713 uint8 buff
= urand(0, 2);
1714 uint32 entry
= obj
->GetEntry();
1715 if (m_BuffChange
&& entry
!= Buff_Entries
[buff
])
1717 //despawn current buff
1718 SpawnBGObject(m_BgObjects
[index
], RESPAWN_ONE_DAY
);
1719 //set index for new one
1720 for (uint8 currBuffTypeIndex
= 0; currBuffTypeIndex
< 3; ++currBuffTypeIndex
)
1722 if (entry
== Buff_Entries
[currBuffTypeIndex
])
1724 index
-= currBuffTypeIndex
;
1730 SpawnBGObject(m_BgObjects
[index
], BUFF_RESPAWN_TIME
);
1733 void BattleGround::HandleKillPlayer( Player
*player
, Player
*killer
)
1736 UpdatePlayerScore(player
, SCORE_DEATHS
, 1);
1738 // add +1 kills to group and +1 killing_blows to killer
1741 UpdatePlayerScore(killer
, SCORE_HONORABLE_KILLS
, 1);
1742 UpdatePlayerScore(killer
, SCORE_KILLING_BLOWS
, 1);
1744 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
1746 Player
*plr
= sObjectMgr
.GetPlayer(itr
->first
);
1748 if (!plr
|| plr
== killer
)
1751 if (plr
->GetTeam() == killer
->GetTeam() && plr
->IsAtGroupRewardDistance(player
))
1752 UpdatePlayerScore(plr
, SCORE_HONORABLE_KILLS
, 1);
1756 // to be able to remove insignia -- ONLY IN BattleGrounds
1758 player
->SetFlag( UNIT_FIELD_FLAGS
, UNIT_FLAG_SKINNABLE
);
1761 // return the player's team based on battlegroundplayer info
1762 // used in same faction arena matches mainly
1763 uint32
BattleGround::GetPlayerTeam(uint64 guid
)
1765 BattleGroundPlayerMap::const_iterator itr
= m_Players
.find(guid
);
1766 if (itr
!=m_Players
.end())
1767 return itr
->second
.Team
;
1771 bool BattleGround::IsPlayerInBattleGround(uint64 guid
)
1773 BattleGroundPlayerMap::const_iterator itr
= m_Players
.find(guid
);
1774 if (itr
!= m_Players
.end())
1779 void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player
* plr
)
1781 if (GetStatus() != STATUS_WAIT_LEAVE
)
1785 BattleGroundQueueTypeId bgQueueTypeId
= BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1789 sBattleGroundMgr
.BuildPvpLogDataPacket(&data
, this);
1790 plr
->GetSession()->SendPacket(&data
);
1792 sBattleGroundMgr
.BuildBattleGroundStatusPacket(&data
, this, plr
->GetBattleGroundQueueIndex(bgQueueTypeId
), STATUS_IN_PROGRESS
, GetEndTime(), GetStartTime(), GetArenaType());
1793 plr
->GetSession()->SendPacket(&data
);
1796 uint32
BattleGround::GetAlivePlayersCountByTeam(uint32 Team
) const
1799 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
1801 if (itr
->second
.Team
== Team
)
1803 Player
* pl
= sObjectMgr
.GetPlayer(itr
->first
);
1804 if (pl
&& pl
->isAlive())
1811 void BattleGround::CheckArenaWinConditions()
1813 if (!GetAlivePlayersCountByTeam(ALLIANCE
) && GetPlayersCountByTeam(HORDE
))
1814 EndBattleGround(HORDE
);
1815 else if (GetPlayersCountByTeam(ALLIANCE
) && !GetAlivePlayersCountByTeam(HORDE
))
1816 EndBattleGround(ALLIANCE
);
1819 void BattleGround::SetBgRaid( uint32 TeamID
, Group
*bg_raid
)
1821 Group
* &old_raid
= TeamID
== ALLIANCE
? m_BgRaids
[BG_TEAM_ALLIANCE
] : m_BgRaids
[BG_TEAM_HORDE
];
1822 if(old_raid
) old_raid
->SetBattlegroundGroup(NULL
);
1823 if(bg_raid
) bg_raid
->SetBattlegroundGroup(this);
1827 WorldSafeLocsEntry
const* BattleGround::GetClosestGraveYard( Player
* player
)
1829 return sObjectMgr
.GetClosestGraveYard( player
->GetPositionX(), player
->GetPositionY(), player
->GetPositionZ(), player
->GetMapId(), player
->GetTeam() );
1832 bool BattleGround::IsTeamScoreInRange(uint32 team
, uint32 minScore
, uint32 maxScore
) const
1834 BattleGroundTeamId team_idx
= GetTeamIndexByTeamId(team
);
1835 uint32 score
= (m_TeamScores
[team_idx
] < 0) ? 0 : uint32(m_TeamScores
[team_idx
]);
1836 return score
>= minScore
&& score
<= maxScore
;