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 "ObjectMgr.h"
31 #include "WorldPacket.h"
34 #include "GridNotifiersImpl.h"
38 class BattleGroundChatBuilder
41 BattleGroundChatBuilder(ChatMsg msgtype
, int32 textId
, Player
const* source
, va_list* args
= NULL
)
42 : i_msgtype(msgtype
), i_textId(textId
), i_source(source
), i_args(args
) {}
43 void operator()(WorldPacket
& data
, int32 loc_idx
)
45 char const* text
= objmgr
.GetMangosString(i_textId
,loc_idx
);
49 // we need copy va_list before use or original va_list will corrupted
54 vsnprintf(str
,2048,text
, ap
);
57 do_helper(data
,&str
[0]);
63 void do_helper(WorldPacket
& data
, char const* text
)
65 uint64 target_guid
= i_source
? i_source
->GetGUID() : 0;
67 data
<< uint8(i_msgtype
);
68 data
<< uint32(LANG_UNIVERSAL
);
69 data
<< uint64(target_guid
); // there 0 for BG messages
70 data
<< uint32(0); // can be chat msg group or something
71 data
<< uint64(target_guid
);
72 data
<< uint32(strlen(text
)+1);
74 data
<< uint8(i_source
? i_source
->chatTag() : uint8(0));
79 Player
const* i_source
;
83 class BattleGroundYellBuilder
86 BattleGroundYellBuilder(uint32 language
, int32 textId
, Creature
const* source
, va_list* args
= NULL
)
87 : i_language(language
), i_textId(textId
), i_source(source
), i_args(args
) {}
88 void operator()(WorldPacket
& data
, int32 loc_idx
)
90 char const* text
= objmgr
.GetMangosString(i_textId
,loc_idx
);
94 // we need copy va_list before use or original va_list will corrupted
99 vsnprintf(str
,2048,text
, ap
);
102 do_helper(data
,&str
[0]);
105 do_helper(data
,text
);
108 void do_helper(WorldPacket
& data
, char const* text
)
110 //copyied from BuildMonsterChat
111 data
<< (uint8
)CHAT_MSG_MONSTER_YELL
;
112 data
<< (uint32
)i_language
;
113 data
<< (uint64
)i_source
->GetGUID();
114 data
<< (uint32
)0; //2.1.0
115 data
<< (uint32
)(strlen(i_source
->GetName())+1);
116 data
<< i_source
->GetName();
117 data
<< (uint64
)0; //Unit Target - isn't important for bgs
118 data
<< (uint32
)strlen(text
)+1;
120 data
<< (uint8
)0; // ChatTag - for bgs allways 0?
125 Creature
const* i_source
;
130 class BattleGround2ChatBuilder
133 BattleGround2ChatBuilder(ChatMsg msgtype
, int32 textId
, Player
const* source
, int32 arg1
, int32 arg2
)
134 : i_msgtype(msgtype
), i_textId(textId
), i_source(source
), i_arg1(arg1
), i_arg2(arg2
) {}
135 void operator()(WorldPacket
& data
, int32 loc_idx
)
137 char const* text
= objmgr
.GetMangosString(i_textId
,loc_idx
);
138 char const* arg1str
= i_arg1
? objmgr
.GetMangosString(i_arg1
,loc_idx
) : "";
139 char const* arg2str
= i_arg2
? objmgr
.GetMangosString(i_arg2
,loc_idx
) : "";
142 snprintf(str
,2048,text
, arg1str
, arg2str
);
144 uint64 target_guid
= i_source
? i_source
->GetGUID() : 0;
146 data
<< uint8(i_msgtype
);
147 data
<< uint32(LANG_UNIVERSAL
);
148 data
<< uint64(target_guid
); // there 0 for BG messages
149 data
<< uint32(0); // can be chat msg group or something
150 data
<< uint64(target_guid
);
151 data
<< uint32(strlen(str
)+1);
153 data
<< uint8(i_source
? i_source
->chatTag() : uint8(0));
159 Player
const* i_source
;
164 class BattleGround2YellBuilder
167 BattleGround2YellBuilder(uint32 language
, int32 textId
, Creature
const* source
, int32 arg1
, int32 arg2
)
168 : i_language(language
), i_textId(textId
), i_source(source
), i_arg1(arg1
), i_arg2(arg2
) {}
169 void operator()(WorldPacket
& data
, int32 loc_idx
)
171 char const* text
= objmgr
.GetMangosString(i_textId
,loc_idx
);
172 char const* arg1str
= i_arg1
? objmgr
.GetMangosString(i_arg1
,loc_idx
) : "";
173 char const* arg2str
= i_arg2
? objmgr
.GetMangosString(i_arg2
,loc_idx
) : "";
176 snprintf(str
,2048,text
, arg1str
, arg2str
);
177 //copyied from BuildMonsterChat
178 data
<< (uint8
)CHAT_MSG_MONSTER_YELL
;
179 data
<< (uint32
)i_language
;
180 data
<< (uint64
)i_source
->GetGUID();
181 data
<< (uint32
)0; //2.1.0
182 data
<< (uint32
)(strlen(i_source
->GetName())+1);
183 data
<< i_source
->GetName();
184 data
<< (uint64
)0; //Unit Target - isn't important for bgs
185 data
<< (uint32
)strlen(str
)+1;
187 data
<< (uint8
)0; // ChatTag - for bgs allways 0?
193 Creature
const* i_source
;
197 } // namespace MaNGOS
200 void BattleGround::BroadcastWorker(Do
& _do
)
202 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
203 if (Player
*plr
= ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr
->first
, 0, HIGHGUID_PLAYER
)))
207 BattleGround::BattleGround()
209 m_TypeID
= BattleGroundTypeId(0);
211 m_Status
= STATUS_NONE
;
212 m_ClientInstanceID
= 0;
214 m_QueueId
= QUEUE_ID_MAX_LEVEL_19
;
215 m_InvitedAlliance
= 0;
223 m_BuffChange
= false;
227 m_InBGFreeSlotQueue
= false;
228 m_SetDeleteThis
= false;
230 m_MaxPlayersPerTeam
= 0;
232 m_MinPlayersPerTeam
= 0;
238 m_TeamStartLocX
[BG_TEAM_ALLIANCE
] = 0;
239 m_TeamStartLocX
[BG_TEAM_HORDE
] = 0;
241 m_TeamStartLocY
[BG_TEAM_ALLIANCE
] = 0;
242 m_TeamStartLocY
[BG_TEAM_HORDE
] = 0;
244 m_TeamStartLocZ
[BG_TEAM_ALLIANCE
] = 0;
245 m_TeamStartLocZ
[BG_TEAM_HORDE
] = 0;
247 m_TeamStartLocO
[BG_TEAM_ALLIANCE
] = 0;
248 m_TeamStartLocO
[BG_TEAM_HORDE
] = 0;
250 m_ArenaTeamIds
[BG_TEAM_ALLIANCE
] = 0;
251 m_ArenaTeamIds
[BG_TEAM_HORDE
] = 0;
253 m_ArenaTeamRatingChanges
[BG_TEAM_ALLIANCE
] = 0;
254 m_ArenaTeamRatingChanges
[BG_TEAM_HORDE
] = 0;
256 m_BgRaids
[BG_TEAM_ALLIANCE
] = NULL
;
257 m_BgRaids
[BG_TEAM_HORDE
] = NULL
;
259 m_PlayersCount
[BG_TEAM_ALLIANCE
] = 0;
260 m_PlayersCount
[BG_TEAM_HORDE
] = 0;
262 m_TeamScores
[BG_TEAM_ALLIANCE
] = 0;
263 m_TeamScores
[BG_TEAM_HORDE
] = 0;
265 m_PrematureCountDown
= false;
266 m_PrematureCountDown
= 0;
268 m_StartDelayTimes
[BG_STARTING_EVENT_FIRST
] = BG_START_DELAY_2M
;
269 m_StartDelayTimes
[BG_STARTING_EVENT_SECOND
] = BG_START_DELAY_1M
;
270 m_StartDelayTimes
[BG_STARTING_EVENT_THIRD
] = BG_START_DELAY_30S
;
271 m_StartDelayTimes
[BG_STARTING_EVENT_FOURTH
] = BG_START_DELAY_NONE
;
272 //we must set to some default existing values
273 m_StartMessageIds
[BG_STARTING_EVENT_FIRST
] = LANG_BG_WS_START_TWO_MINUTES
;
274 m_StartMessageIds
[BG_STARTING_EVENT_SECOND
] = LANG_BG_WS_START_ONE_MINUTE
;
275 m_StartMessageIds
[BG_STARTING_EVENT_THIRD
] = LANG_BG_WS_START_HALF_MINUTE
;
276 m_StartMessageIds
[BG_STARTING_EVENT_FOURTH
] = LANG_BG_WS_HAS_BEGUN
;
279 BattleGround::~BattleGround()
281 // remove objects and creatures
282 // (this is done automatically in mapmanager update, when the instance is reset after the reset time)
284 int size
= m_BgObjects
.size();
285 for(int i
= 0; i
< size
; ++i
)
288 if (GetInstanceID()) // not spam by useless queries in case BG templates
290 // delete creature and go respawn times
291 WorldDatabase
.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'",GetInstanceID());
292 WorldDatabase
.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'",GetInstanceID());
293 // delete instance from db
294 CharacterDatabase
.PExecute("DELETE FROM instance WHERE id = '%u'",GetInstanceID());
295 // remove from battlegrounds
298 sBattleGroundMgr
.RemoveBattleGround(GetInstanceID(), GetTypeID());
301 // map can be null at bg destruction
305 // remove from bg free slot queue
306 this->RemoveFromBGFreeSlotQueue();
308 for(BattleGroundScoreMap::const_iterator itr
= m_PlayerScores
.begin(); itr
!= m_PlayerScores
.end(); ++itr
)
312 void BattleGround::Update(uint32 diff
)
314 if (!GetPlayersSize())
317 // if there are no players invited, delete BG
318 // this will delete arena or bg object, where any player entered
319 // [[ but if you use battleground object again (more battles possible to be played on 1 instance)
320 // then this condition should be removed and code:
321 // if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE))
322 // this->AddToFreeBGObjectsQueue(); // not yet implemented
323 // should be used instead of current
325 // BattleGround Template instance cannot be updated, because it would be deleted
326 if (!GetInvitedCount(HORDE
) && !GetInvitedCount(ALLIANCE
))
327 m_SetDeleteThis
= true;
331 // remove offline players from bg after 5 minutes
332 if (!m_OfflineQueue
.empty())
334 BattleGroundPlayerMap::iterator itr
= m_Players
.find(*(m_OfflineQueue
.begin()));
335 if (itr
!= m_Players
.end())
337 if (itr
->second
.OfflineRemoveTime
<= sWorld
.GetGameTime())
339 RemovePlayerAtLeave(itr
->first
, true, true);// remove player from BG
340 m_OfflineQueue
.pop_front(); // remove from offline queue
341 //do not use itr for anything, because it is erased in RemovePlayerAtLeave()
346 /*********************************************************/
347 /*** BATTLEGROUND BALLANCE SYSTEM ***/
348 /*********************************************************/
350 // if less then minimum players are in on one side, then start premature finish timer
351 if (GetStatus() == STATUS_IN_PROGRESS
&& !isArena() && sBattleGroundMgr
.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE
) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE
) < GetMinPlayersPerTeam()))
353 if (!m_PrematureCountDown
)
355 m_PrematureCountDown
= true;
356 m_PrematureCountDownTimer
= sBattleGroundMgr
.GetPrematureFinishTime();
358 else if (m_PrematureCountDownTimer
< diff
)
362 if (GetPlayersCountByTeam(ALLIANCE
) >= GetMinPlayersPerTeam())
364 else if (GetPlayersCountByTeam(HORDE
) >= GetMinPlayersPerTeam())
367 EndBattleGround(winner
);
368 m_PrematureCountDown
= false;
372 uint32 newtime
= m_PrematureCountDownTimer
- diff
;
373 // announce every minute
374 if (newtime
> (MINUTE
* IN_MILISECONDS
))
376 if (newtime
/ (MINUTE
* IN_MILISECONDS
) != m_PrematureCountDownTimer
/ (MINUTE
* IN_MILISECONDS
))
377 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING
, CHAT_MSG_SYSTEM
, NULL
, (uint32
)(m_PrematureCountDownTimer
/ (MINUTE
* IN_MILISECONDS
)));
381 //announce every 15 seconds
382 if (newtime
/ (15 * IN_MILISECONDS
) != m_PrematureCountDownTimer
/ (15 * IN_MILISECONDS
))
383 PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS
, CHAT_MSG_SYSTEM
, NULL
, (uint32
)(m_PrematureCountDownTimer
/ IN_MILISECONDS
));
385 m_PrematureCountDownTimer
= newtime
;
389 else if (m_PrematureCountDown
)
390 m_PrematureCountDown
= false;
392 /*********************************************************/
393 /*** ARENA BUFF OBJECT SPAWNING ***/
394 /*********************************************************/
395 if (isArena() && !m_ArenaBuffSpawned
)
397 // 60 seconds after start the buffobjects in arena should get spawned
398 if (m_StartTime
> uint32(m_StartDelayTimes
[BG_STARTING_EVENT_FIRST
] + ARENA_SPAWN_BUFF_OBJECTS
))
400 SpawnEvent(ARENA_BUFF_EVENT
, 0, true);
401 m_ArenaBuffSpawned
= true;
405 /*********************************************************/
406 /*** BATTLEGROUND STARTING SYSTEM ***/
407 /*********************************************************/
409 if (GetStatus() == STATUS_WAIT_JOIN
&& GetPlayersSize())
411 ModifyStartDelayTime(diff
);
413 if (!(m_Events
& BG_STARTING_EVENT_1
))
415 m_Events
|= BG_STARTING_EVENT_1
;
417 // setup here, only when at least one player has ported to the map
418 if (!SetupBattleGround())
424 StartingEventCloseDoors();
425 SetStartDelayTime(m_StartDelayTimes
[BG_STARTING_EVENT_FIRST
]);
426 //first start warning - 2 or 1 minute
427 SendMessageToAll(m_StartMessageIds
[BG_STARTING_EVENT_FIRST
], CHAT_MSG_BG_SYSTEM_NEUTRAL
);
429 // After 1 minute or 30 seconds, warning is signalled
430 else if (GetStartDelayTime() <= m_StartDelayTimes
[BG_STARTING_EVENT_SECOND
] && !(m_Events
& BG_STARTING_EVENT_2
))
432 m_Events
|= BG_STARTING_EVENT_2
;
433 SendMessageToAll(m_StartMessageIds
[BG_STARTING_EVENT_SECOND
], CHAT_MSG_BG_SYSTEM_NEUTRAL
);
435 // After 30 or 15 seconds, warning is signalled
436 else if (GetStartDelayTime() <= m_StartDelayTimes
[BG_STARTING_EVENT_THIRD
] && !(m_Events
& BG_STARTING_EVENT_3
))
438 m_Events
|= BG_STARTING_EVENT_3
;
439 SendMessageToAll(m_StartMessageIds
[BG_STARTING_EVENT_THIRD
], CHAT_MSG_BG_SYSTEM_NEUTRAL
);
441 // delay expired (atfer 2 or 1 minute)
442 else if (GetStartDelayTime() <= 0 && !(m_Events
& BG_STARTING_EVENT_4
))
444 m_Events
|= BG_STARTING_EVENT_4
;
446 StartingEventOpenDoors();
448 SendMessageToAll(m_StartMessageIds
[BG_STARTING_EVENT_FOURTH
], CHAT_MSG_BG_SYSTEM_NEUTRAL
);
449 SetStatus(STATUS_IN_PROGRESS
);
450 SetStartDelayTime(m_StartDelayTimes
[BG_STARTING_EVENT_FOURTH
]);
455 //TODO : add arena sound PlaySoundToAll(SOUND_ARENA_START);
457 for(BattleGroundPlayerMap::const_iterator itr
= GetPlayers().begin(); itr
!= GetPlayers().end(); ++itr
)
458 if (Player
*plr
= objmgr
.GetPlayer(itr
->first
))
459 plr
->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION
);
461 CheckArenaWinConditions();
466 PlaySoundToAll(SOUND_BG_START
);
468 for(BattleGroundPlayerMap::const_iterator itr
= GetPlayers().begin(); itr
!= GetPlayers().end(); ++itr
)
469 if (Player
* plr
= objmgr
.GetPlayer(itr
->first
))
470 plr
->RemoveAurasDueToSpell(SPELL_PREPARATION
);
471 //Announce BG starting
472 if (sWorld
.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE
))
474 sWorld
.SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD
, GetName(), GetMinLevel(), GetMaxLevel());
480 /*********************************************************/
481 /*** BATTLEGROUND ENDING SYSTEM ***/
482 /*********************************************************/
484 if (GetStatus() == STATUS_WAIT_LEAVE
)
486 // remove all players from battleground after 2 minutes
491 BattleGroundPlayerMap::iterator itr
, next
;
492 for(itr
= m_Players
.begin(); itr
!= m_Players
.end(); itr
= next
)
496 //itr is erased here!
497 RemovePlayerAtLeave(itr
->first
, true, true);// remove player from BG
498 // do not change any battleground's private variables
507 void BattleGround::SetTeamStartLoc(uint32 TeamID
, float X
, float Y
, float Z
, float O
)
509 BattleGroundTeamId idx
= GetTeamIndexByTeamId(TeamID
);
510 m_TeamStartLocX
[idx
] = X
;
511 m_TeamStartLocY
[idx
] = Y
;
512 m_TeamStartLocZ
[idx
] = Z
;
513 m_TeamStartLocO
[idx
] = O
;
516 void BattleGround::SendPacketToAll(WorldPacket
*packet
)
518 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
520 Player
*plr
= objmgr
.GetPlayer(itr
->first
);
522 plr
->GetSession()->SendPacket(packet
);
524 sLog
.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr
->first
));
528 void BattleGround::SendPacketToTeam(uint32 TeamID
, WorldPacket
*packet
, Player
*sender
, bool self
)
530 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
532 Player
*plr
= objmgr
.GetPlayer(itr
->first
);
536 sLog
.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr
->first
));
540 if (!self
&& sender
== plr
)
543 uint32 team
= itr
->second
.Team
;
544 if(!team
) team
= plr
->GetTeam();
547 plr
->GetSession()->SendPacket(packet
);
551 void BattleGround::PlaySoundToAll(uint32 SoundID
)
554 sBattleGroundMgr
.BuildPlaySoundPacket(&data
, SoundID
);
555 SendPacketToAll(&data
);
558 void BattleGround::PlaySoundToTeam(uint32 SoundID
, uint32 TeamID
)
562 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
564 Player
*plr
= objmgr
.GetPlayer(itr
->first
);
568 sLog
.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr
->first
));
572 uint32 team
= itr
->second
.Team
;
573 if(!team
) team
= plr
->GetTeam();
577 sBattleGroundMgr
.BuildPlaySoundPacket(&data
, SoundID
);
578 plr
->GetSession()->SendPacket(&data
);
583 void BattleGround::CastSpellOnTeam(uint32 SpellID
, uint32 TeamID
)
585 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
587 Player
*plr
= objmgr
.GetPlayer(itr
->first
);
591 sLog
.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr
->first
));
595 uint32 team
= itr
->second
.Team
;
596 if(!team
) team
= plr
->GetTeam();
599 plr
->CastSpell(plr
, SpellID
, true);
603 void BattleGround::RewardHonorToTeam(uint32 Honor
, uint32 TeamID
)
605 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
607 Player
*plr
= objmgr
.GetPlayer(itr
->first
);
611 sLog
.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr
->first
));
615 uint32 team
= itr
->second
.Team
;
616 if(!team
) team
= plr
->GetTeam();
619 UpdatePlayerScore(plr
, SCORE_BONUS_HONOR
, Honor
);
623 void BattleGround::RewardReputationToTeam(uint32 faction_id
, uint32 Reputation
, uint32 TeamID
)
625 FactionEntry
const* factionEntry
= sFactionStore
.LookupEntry(faction_id
);
630 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
632 Player
*plr
= objmgr
.GetPlayer(itr
->first
);
636 sLog
.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr
->first
));
640 uint32 team
= itr
->second
.Team
;
641 if(!team
) team
= plr
->GetTeam();
644 plr
->GetReputationMgr().ModifyReputation(factionEntry
, Reputation
);
648 void BattleGround::UpdateWorldState(uint32 Field
, uint32 Value
)
651 sBattleGroundMgr
.BuildUpdateWorldStatePacket(&data
, Field
, Value
);
652 SendPacketToAll(&data
);
655 void BattleGround::UpdateWorldStateForPlayer(uint32 Field
, uint32 Value
, Player
*Source
)
658 sBattleGroundMgr
.BuildUpdateWorldStatePacket(&data
, Field
, Value
);
659 Source
->GetSession()->SendPacket(&data
);
662 void BattleGround::EndBattleGround(uint32 winner
)
664 this->RemoveFromBGFreeSlotQueue();
666 ArenaTeam
* winner_arena_team
= NULL
;
667 ArenaTeam
* loser_arena_team
= NULL
;
668 uint32 loser_rating
= 0;
669 uint32 winner_rating
= 0;
673 if (winner
== ALLIANCE
)
675 winmsg_id
= isBattleGround() ? LANG_BG_A_WINS
: LANG_ARENA_GOLD_WINS
;
677 PlaySoundToAll(SOUND_ALLIANCE_WINS
); // alliance wins sound
679 SetWinner(WINNER_ALLIANCE
);
681 else if (winner
== HORDE
)
683 winmsg_id
= isBattleGround() ? LANG_BG_H_WINS
: LANG_ARENA_GREEN_WINS
;
685 PlaySoundToAll(SOUND_HORDE_WINS
); // horde wins sound
687 SetWinner(WINNER_HORDE
);
694 SetStatus(STATUS_WAIT_LEAVE
);
695 //we must set it this way, because end time is sent in packet!
696 m_EndTime
= TIME_TO_AUTOREMOVE
;
698 // arena rating calculation
699 if (isArena() && isRated())
701 winner_arena_team
= objmgr
.GetArenaTeamById(GetArenaTeamIdForTeam(winner
));
702 loser_arena_team
= objmgr
.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(winner
)));
703 if (winner_arena_team
&& loser_arena_team
)
705 loser_rating
= loser_arena_team
->GetStats().rating
;
706 winner_rating
= winner_arena_team
->GetStats().rating
;
707 int32 winner_change
= winner_arena_team
->WonAgainst(loser_rating
);
708 int32 loser_change
= loser_arena_team
->LostAgainst(winner_rating
);
709 sLog
.outDebug("--- Winner rating: %u, Loser rating: %u, Winner change: %u, Losser change: %u ---", winner_rating
, loser_rating
, winner_change
, loser_change
);
710 SetArenaTeamRatingChangeForTeam(winner
, winner_change
);
711 SetArenaTeamRatingChangeForTeam(GetOtherTeam(winner
), loser_change
);
715 SetArenaTeamRatingChangeForTeam(ALLIANCE
, 0);
716 SetArenaTeamRatingChangeForTeam(HORDE
, 0);
720 for(BattleGroundPlayerMap::iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
722 Player
*plr
= objmgr
.GetPlayer(itr
->first
);
723 uint32 team
= itr
->second
.Team
;
727 //if rated arena match - make member lost!
728 if (isArena() && isRated() && winner_arena_team
&& loser_arena_team
)
731 winner_arena_team
->OfflineMemberLost(itr
->first
, loser_rating
);
733 loser_arena_team
->OfflineMemberLost(itr
->first
, winner_rating
);
735 sLog
.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr
->first
));
739 // should remove spirit of redemption
740 if (plr
->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION
))
741 plr
->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT
);
745 plr
->ResurrectPlayer(1.0f
);
746 plr
->SpawnCorpseBones();
750 //needed cause else in av some creatures will kill the players at the end
752 plr
->getHostileRefManager().deleteReferences();
755 //this line is obsolete - team is set ALWAYS
756 //if(!team) team = plr->GetTeam();
758 // per player calculation
759 if (isArena() && isRated() && winner_arena_team
&& loser_arena_team
)
763 // update achievement BEFORE personal rating update
764 ArenaTeamMember
* member
= winner_arena_team
->GetMember(plr
->GetGUID());
766 plr
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
, member
->personal_rating
);
768 winner_arena_team
->MemberWon(plr
,loser_rating
);
772 loser_arena_team
->MemberLost(plr
,winner_rating
);
774 // Arena lost => reset the win_rated_arena having the "no_loose" condition
775 plr
->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA
, ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE
);
781 RewardMark(plr
,ITEM_WINNER_COUNT
);
782 RewardQuestComplete(plr
);
785 RewardMark(plr
,ITEM_LOSER_COUNT
);
787 plr
->CombatStopWithPets(true);
791 sBattleGroundMgr
.BuildPvpLogDataPacket(&data
, this);
792 plr
->GetSession()->SendPacket(&data
);
794 BattleGroundQueueTypeId bgQueueTypeId
= BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
795 sBattleGroundMgr
.BuildBattleGroundStatusPacket(&data
, this, plr
->GetBattleGroundQueueIndex(bgQueueTypeId
), STATUS_IN_PROGRESS
, TIME_TO_AUTOREMOVE
, GetStartTime(), GetArenaType());
796 plr
->GetSession()->SendPacket(&data
);
797 plr
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND
, 1);
800 if (isArena() && isRated() && winner_arena_team
&& loser_arena_team
)
802 // update arena points only after increasing the player's match count!
803 //obsolete: winner_arena_team->UpdateArenaPointsHelper();
804 //obsolete: loser_arena_team->UpdateArenaPointsHelper();
805 // save the stat changes
806 winner_arena_team
->SaveToDB();
807 loser_arena_team
->SaveToDB();
808 // send updated arena team stats to players
809 // this way all arena team members will get notified, not only the ones who participated in this match
810 winner_arena_team
->NotifyStatsChanged();
811 loser_arena_team
->NotifyStatsChanged();
815 SendMessageToAll(winmsg_id
, CHAT_MSG_BG_SYSTEM_NEUTRAL
);
818 uint32
BattleGround::GetBonusHonorFromKill(uint32 kills
) const
820 //variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill)
821 return MaNGOS::Honor::hk_honor_at_level(GetMaxLevel(), kills
);
824 uint32
BattleGround::GetBattlemasterEntry() const
828 case BATTLEGROUND_AV
: return 15972;
829 case BATTLEGROUND_WS
: return 14623;
830 case BATTLEGROUND_AB
: return 14879;
831 case BATTLEGROUND_EY
: return 22516;
832 case BATTLEGROUND_NA
: return 20200;
837 void BattleGround::RewardMark(Player
*plr
,uint32 count
)
839 BattleGroundMarks mark
;
843 case BATTLEGROUND_AV
:
845 if (count
== ITEM_WINNER_COUNT
)
846 mark
= SPELL_AV_MARK_WINNER
;
848 mark
= SPELL_AV_MARK_LOSER
;
850 case BATTLEGROUND_WS
:
852 if (count
== ITEM_WINNER_COUNT
)
853 mark
= SPELL_WS_MARK_WINNER
;
855 mark
= SPELL_WS_MARK_LOSER
;
857 case BATTLEGROUND_AB
:
859 if (count
== ITEM_WINNER_COUNT
)
860 mark
= SPELL_AB_MARK_WINNER
;
862 mark
= SPELL_AB_MARK_LOSER
;
864 case BATTLEGROUND_EY
:
866 mark
= ITEM_EY_MARK_OF_HONOR
;
873 RewardSpellCast(plr
,mark
);
875 RewardItem(plr
,mark
,count
);
878 void BattleGround::RewardSpellCast(Player
*plr
, uint32 spell_id
)
880 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
881 if (plr
->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE
))
884 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spell_id
);
887 sLog
.outError("Battleground reward casting spell %u not exist.",spell_id
);
891 plr
->CastSpell(plr
, spellInfo
, true);
894 void BattleGround::RewardItem(Player
*plr
, uint32 item_id
, uint32 count
)
896 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
897 if (plr
->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE
))
900 ItemPosCountVec dest
;
901 uint32 no_space_count
= 0;
902 uint8 msg
= plr
->CanStoreNewItem( NULL_BAG
, NULL_SLOT
, dest
, item_id
, count
, &no_space_count
);
904 if( msg
== EQUIP_ERR_ITEM_NOT_FOUND
)
906 sLog
.outErrorDb("Battleground reward item (Entry %u) not exist in `item_template`.",item_id
);
910 if( msg
!= EQUIP_ERR_OK
) // convert to possible store amount
911 count
-= no_space_count
;
913 if( count
!= 0 && !dest
.empty()) // can add some
914 if (Item
* item
= plr
->StoreNewItem( dest
, item_id
, true, 0))
915 plr
->SendNewItem(item
,count
,true,false);
917 if (no_space_count
> 0)
918 SendRewardMarkByMail(plr
,item_id
,no_space_count
);
921 void BattleGround::SendRewardMarkByMail(Player
*plr
,uint32 mark
, uint32 count
)
923 uint32 bmEntry
= GetBattlemasterEntry();
927 ItemPrototype
const* markProto
= ObjectMgr::GetItemPrototype(mark
);
931 if (Item
* markItem
= Item::CreateItem(mark
,count
,plr
))
933 // save new item before send
934 markItem
->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
936 // subject: item name
937 std::string subject
= markProto
->Name1
;
938 int loc_idx
= plr
->GetSession()->GetSessionDbLocaleIndex();
940 if (ItemLocale
const *il
= objmgr
.GetItemLocale(markProto
->ItemId
))
941 if (il
->Name
.size() > size_t(loc_idx
) && !il
->Name
[loc_idx
].empty())
942 subject
= il
->Name
[loc_idx
];
945 std::string textFormat
= plr
->GetSession()->GetMangosString(LANG_BG_MARK_BY_MAIL
);
947 snprintf(textBuf
,300,textFormat
.c_str(),GetName(),GetName());
948 uint32 itemTextId
= objmgr
.CreateItemText( textBuf
);
950 MailDraft(subject
, itemTextId
)
952 .SendMailTo(plr
, MailSender(MAIL_CREATURE
, bmEntry
));
956 void BattleGround::RewardQuestComplete(Player
*plr
)
961 case BATTLEGROUND_AV
:
962 quest
= SPELL_AV_QUEST_REWARD
;
964 case BATTLEGROUND_WS
:
965 quest
= SPELL_WS_QUEST_REWARD
;
967 case BATTLEGROUND_AB
:
968 quest
= SPELL_AB_QUEST_REWARD
;
970 case BATTLEGROUND_EY
:
971 quest
= SPELL_EY_QUEST_REWARD
;
977 RewardSpellCast(plr
, quest
);
980 void BattleGround::BlockMovement(Player
*plr
)
982 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()
985 void BattleGround::RemovePlayerAtLeave(uint64 guid
, bool Transport
, bool SendPacket
)
987 uint32 team
= GetPlayerTeam(guid
);
988 bool participant
= false;
989 // Remove from lists/maps
990 BattleGroundPlayerMap::iterator itr
= m_Players
.find(guid
);
991 if (itr
!= m_Players
.end())
993 UpdatePlayersCountByTeam(team
, true); // -1 player
994 m_Players
.erase(itr
);
995 // check if the player was a participant of the match, or only entered through gm command (goname)
999 BattleGroundScoreMap::iterator itr2
= m_PlayerScores
.find(guid
);
1000 if (itr2
!= m_PlayerScores
.end())
1002 delete itr2
->second
; // delete player's score
1003 m_PlayerScores
.erase(itr2
);
1006 Player
*plr
= objmgr
.GetPlayer(guid
);
1008 // should remove spirit of redemption
1009 if (plr
&& plr
->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION
))
1010 plr
->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT
);
1012 if(plr
&& !plr
->isAlive()) // resurrect on exit
1014 plr
->ResurrectPlayer(1.0f
);
1015 plr
->SpawnCorpseBones();
1018 RemovePlayer(plr
, guid
); // BG subclass specific code
1020 if(participant
) // if the player was a match participant, remove auras, calc rating, update queue
1022 BattleGroundTypeId bgTypeId
= GetTypeID();
1023 BattleGroundQueueTypeId bgQueueTypeId
= BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1026 plr
->ClearAfkReports();
1028 if(!team
) team
= plr
->GetTeam();
1030 // if arena, remove the specific arena auras
1033 plr
->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out
1034 bgTypeId
=BATTLEGROUND_AA
; // set the bg type to all arenas (it will be used for queue refreshing)
1036 // unsummon current and summon old pet if there was one and there isn't a current pet
1037 plr
->RemovePet(NULL
, PET_SAVE_NOT_IN_SLOT
);
1038 plr
->ResummonPetTemporaryUnSummonedIfAny();
1040 if (isRated() && GetStatus() == STATUS_IN_PROGRESS
)
1042 //left a rated match while the encounter was in progress, consider as loser
1043 ArenaTeam
* winner_arena_team
= objmgr
.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team
)));
1044 ArenaTeam
* loser_arena_team
= objmgr
.GetArenaTeamById(GetArenaTeamIdForTeam(team
));
1045 if (winner_arena_team
&& loser_arena_team
)
1046 loser_arena_team
->MemberLost(plr
,winner_arena_team
->GetRating());
1052 sBattleGroundMgr
.BuildBattleGroundStatusPacket(&data
, this, plr
->GetBattleGroundQueueIndex(bgQueueTypeId
), STATUS_NONE
, 0, 0, 0);
1053 plr
->GetSession()->SendPacket(&data
);
1056 // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg
1057 plr
->RemoveBattleGroundQueueId(bgQueueTypeId
);
1060 // removing offline participant
1062 if (isRated() && GetStatus() == STATUS_IN_PROGRESS
)
1064 //left a rated match while the encounter was in progress, consider as loser
1065 ArenaTeam
* others_arena_team
= objmgr
.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team
)));
1066 ArenaTeam
* players_arena_team
= objmgr
.GetArenaTeamById(GetArenaTeamIdForTeam(team
));
1067 if (others_arena_team
&& players_arena_team
)
1068 players_arena_team
->OfflineMemberLost(guid
, others_arena_team
->GetRating());
1072 // remove from raid group if player is member
1073 if (Group
*group
= GetBgRaid(team
))
1075 if( !group
->RemoveMember(guid
, 0) ) // group was disbanded
1077 SetBgRaid(team
, NULL
);
1081 DecreaseInvitedCount(team
);
1082 //we should update battleground queue, but only if bg isn't ending
1083 if (isBattleGround() && GetStatus() < STATUS_WAIT_LEAVE
)
1085 // a player has left the battleground, so there are free slots -> add to queue
1086 AddToBGFreeSlotQueue();
1087 sBattleGroundMgr
.m_BattleGroundQueues
[bgQueueTypeId
].Update(bgTypeId
, GetQueueId());
1092 sBattleGroundMgr
.BuildPlayerLeftBattleGroundPacket(&data
, guid
);
1093 SendPacketToTeam(team
, &data
, plr
, false);
1098 // Do next only if found in battleground
1099 plr
->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE
); // We're not in BG.
1100 // reset destination bg team
1104 plr
->TeleportToBGEntryPoint();
1106 sLog
.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr
->GetName());
1109 //battleground object will be deleted next BattleGround::Update() call
1112 // this method is called when no players remains in battleground
1113 void BattleGround::Reset()
1115 SetQueueId(QUEUE_ID_MAX_LEVEL_19
);
1116 SetWinner(WINNER_NONE
);
1117 SetStatus(STATUS_WAIT_QUEUE
);
1125 // door-event2 is always 0
1126 m_ActiveEvents
[BG_EVENT_DOOR
] = 0;
1129 m_ActiveEvents
[ARENA_BUFF_EVENT
] = BG_EVENT_NONE
;
1130 m_ArenaBuffSpawned
= false;
1133 if (m_InvitedAlliance
> 0 || m_InvitedHorde
> 0)
1134 sLog
.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance
, m_InvitedHorde
);
1136 m_InvitedAlliance
= 0;
1138 m_InBGFreeSlotQueue
= false;
1142 for(BattleGroundScoreMap::const_iterator itr
= m_PlayerScores
.begin(); itr
!= m_PlayerScores
.end(); ++itr
)
1144 m_PlayerScores
.clear();
1147 void BattleGround::StartBattleGround()
1151 // add BG to free slot queue
1152 AddToBGFreeSlotQueue();
1154 // add bg to update list
1155 // This must be done here, because we need to have already invited some players when first BG::Update() method is executed
1156 // and it doesn't matter if we call StartBattleGround() more times, because m_BattleGrounds is a map and instance id never changes
1157 sBattleGroundMgr
.AddBattleGround(GetInstanceID(), GetTypeID(), this);
1160 void BattleGround::AddPlayer(Player
*plr
)
1162 // remove afk from player
1163 if (plr
->HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_AFK
))
1166 // score struct must be created in inherited class
1168 uint64 guid
= plr
->GetGUID();
1169 uint32 team
= plr
->GetBGTeam();
1171 BattleGroundPlayer bp
;
1172 bp
.OfflineRemoveTime
= 0;
1176 m_Players
[guid
] = bp
;
1178 UpdatePlayersCountByTeam(team
, false); // +1 player
1181 sBattleGroundMgr
.BuildPlayerJoinedBattleGroundPacket(&data
, plr
);
1182 SendPacketToTeam(team
, &data
, plr
, false);
1184 // add arena specific auras
1187 plr
->RemoveArenaSpellCooldowns();
1188 plr
->RemoveArenaAuras();
1189 plr
->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT
);
1190 if(team
== ALLIANCE
) // gold
1192 if (plr
->GetTeam() == HORDE
)
1193 plr
->CastSpell(plr
, SPELL_HORDE_GOLD_FLAG
,true);
1195 plr
->CastSpell(plr
, SPELL_ALLIANCE_GOLD_FLAG
,true);
1199 if (plr
->GetTeam() == HORDE
)
1200 plr
->CastSpell(plr
, SPELL_HORDE_GREEN_FLAG
,true);
1202 plr
->CastSpell(plr
, SPELL_ALLIANCE_GREEN_FLAG
,true);
1205 plr
->DestroyConjuredItems(true);
1206 plr
->UnsummonPetTemporaryIfAny();
1208 if(GetStatus() == STATUS_WAIT_JOIN
) // not started yet
1210 plr
->CastSpell(plr
, SPELL_ARENA_PREPARATION
, true);
1212 plr
->SetHealth(plr
->GetMaxHealth());
1213 plr
->SetPower(POWER_MANA
, plr
->GetMaxPower(POWER_MANA
));
1218 if(GetStatus() == STATUS_WAIT_JOIN
) // not started yet
1219 plr
->CastSpell(plr
, SPELL_PREPARATION
, true); // reduces all mana cost of spells.
1222 plr
->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE
, ACHIEVEMENT_CRITERIA_CONDITION_MAP
, GetMapId());
1223 plr
->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE
, ACHIEVEMENT_CRITERIA_CONDITION_MAP
, GetMapId());
1225 // setup BG group membership
1226 PlayerAddedToBGCheckIfBGIsRunning(plr
);
1227 AddOrSetPlayerToCorrectBgGroup(plr
, guid
, team
);
1230 sLog
.outDetail("BATTLEGROUND: Player %s joined the battle.", plr
->GetName());
1233 /* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */
1234 void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player
*plr
, uint64 plr_guid
, uint32 team
)
1236 Group
* group
= GetBgRaid(team
);
1237 if(!group
) // first player joined
1240 SetBgRaid(team
, group
);
1241 group
->Create(plr_guid
, plr
->GetName());
1243 else // raid already exist
1245 if (group
->IsMember(plr_guid
))
1247 uint8 subgroup
= group
->GetMemberGroup(plr_guid
);
1248 plr
->SetBattleGroundRaid(group
, subgroup
);
1252 group
->AddMember(plr_guid
, plr
->GetName());
1253 if (Group
* originalGroup
= plr
->GetOriginalGroup())
1254 if (originalGroup
->IsLeader(plr_guid
))
1255 group
->ChangeLeader(plr_guid
);
1260 // This method should be called when player logs into running battleground
1261 void BattleGround::EventPlayerLoggedIn(Player
* player
, uint64 plr_guid
)
1263 // player is correct pointer
1264 for(std::deque
<uint64
>::iterator itr
= m_OfflineQueue
.begin(); itr
!= m_OfflineQueue
.end(); ++itr
)
1266 if (*itr
== plr_guid
)
1268 m_OfflineQueue
.erase(itr
);
1272 m_Players
[plr_guid
].OfflineRemoveTime
= 0;
1273 PlayerAddedToBGCheckIfBGIsRunning(player
);
1274 // if battleground is starting, then add preparation aura
1275 // we don't have to do that, because preparation aura isn't removed when player logs out
1278 // This method should be called when player logs out from running battleground
1279 void BattleGround::EventPlayerLoggedOut(Player
* player
)
1281 // player is correct pointer, it is checked in WorldSession::LogoutPlayer()
1282 m_OfflineQueue
.push_back(player
->GetGUID());
1283 m_Players
[player
->GetGUID()].OfflineRemoveTime
= sWorld
.GetGameTime() + MAX_OFFLINE_TIME
;
1284 if (GetStatus() == STATUS_IN_PROGRESS
)
1286 if (isBattleGround())
1287 EventPlayerDroppedFlag(player
);
1290 //1 player is logging out, if it is the last, then end arena!
1291 if (GetAlivePlayersCountByTeam(player
->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player
->GetTeam())))
1292 EndBattleGround(GetOtherTeam(player
->GetTeam()));
1297 /* This method should be called only once ... it adds pointer to queue */
1298 void BattleGround::AddToBGFreeSlotQueue()
1300 // make sure to add only once
1301 if (!m_InBGFreeSlotQueue
&& isBattleGround())
1303 sBattleGroundMgr
.BGFreeSlotQueue
[m_TypeID
].push_front(this);
1304 m_InBGFreeSlotQueue
= true;
1308 /* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/
1309 void BattleGround::RemoveFromBGFreeSlotQueue()
1311 // set to be able to re-add if needed
1312 m_InBGFreeSlotQueue
= false;
1313 // uncomment this code when battlegrounds will work like instances
1314 for (BGFreeSlotQueueType::iterator itr
= sBattleGroundMgr
.BGFreeSlotQueue
[m_TypeID
].begin(); itr
!= sBattleGroundMgr
.BGFreeSlotQueue
[m_TypeID
].end(); ++itr
)
1316 if ((*itr
)->GetInstanceID() == m_InstanceID
)
1318 sBattleGroundMgr
.BGFreeSlotQueue
[m_TypeID
].erase(itr
);
1324 // get the number of free slots for team
1325 // returns the number how many players can join battleground to MaxPlayersPerTeam
1326 uint32
BattleGround::GetFreeSlotsForTeam(uint32 Team
) const
1328 //return free slot count to MaxPlayerPerTeam
1329 if (GetStatus() == STATUS_WAIT_JOIN
|| GetStatus() == STATUS_IN_PROGRESS
)
1330 return (GetInvitedCount(Team
) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team
) : 0;
1335 bool BattleGround::HasFreeSlots() const
1337 return GetPlayersSize() < GetMaxPlayers();
1340 void BattleGround::UpdatePlayerScore(Player
*Source
, uint32 type
, uint32 value
)
1342 //this procedure is called from virtual function implemented in bg subclass
1343 BattleGroundScoreMap::const_iterator itr
= m_PlayerScores
.find(Source
->GetGUID());
1345 if(itr
== m_PlayerScores
.end()) // player not found...
1350 case SCORE_KILLING_BLOWS
: // Killing blows
1351 itr
->second
->KillingBlows
+= value
;
1353 case SCORE_DEATHS
: // Deaths
1354 itr
->second
->Deaths
+= value
;
1356 case SCORE_HONORABLE_KILLS
: // Honorable kills
1357 itr
->second
->HonorableKills
+= value
;
1359 case SCORE_BONUS_HONOR
: // Honor bonus
1360 // do not add honor in arenas
1361 if (isBattleGround())
1363 // reward honor instantly
1364 if (Source
->RewardHonor(NULL
, 1, value
))
1365 itr
->second
->BonusHonor
+= value
;
1368 //used only in EY, but in MSG_PVP_LOG_DATA opcode
1369 case SCORE_DAMAGE_DONE
: // Damage Done
1370 itr
->second
->DamageDone
+= value
;
1372 case SCORE_HEALING_DONE
: // Healing Done
1373 itr
->second
->HealingDone
+= value
;
1376 sLog
.outError("BattleGround: Unknown player score type %u", type
);
1381 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
)
1383 // must be created this way, adding to godatamap would add it to the base map of the instance
1384 // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
1385 // so we must create it specific for this instance
1386 GameObject
* go
= new GameObject
;
1387 if(!go
->Create(objmgr
.GenerateLowGuid(HIGHGUID_GAMEOBJECT
),entry
, GetBgMap(),
1388 PHASEMASK_NORMAL
, x
,y
,z
,o
,rotation0
,rotation1
,rotation2
,rotation3
,100,GO_STATE_READY
))
1390 sLog
.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry
);
1391 sLog
.outError("Cannot create gameobject template %u! BattleGround not created!", entry
);
1396 uint32 guid = go->GetGUIDLow();
1398 // without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata
1399 // 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
1400 GameObjectData& data = objmgr.NewGOData(guid);
1403 data.mapid = GetMapId();
1407 data.orientation = o;
1408 data.rotation0 = rotation0;
1409 data.rotation1 = rotation1;
1410 data.rotation2 = rotation2;
1411 data.rotation3 = rotation3;
1412 data.spawntimesecs = respawnTime;
1414 data.animprogress = 100;
1417 // add to world, so it can be later looked up from HashMapHolder
1419 m_BgObjects
[type
] = go
->GetGUID();
1423 //some doors aren't despawned so we cannot handle their closing in gameobject::update()
1424 //it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
1425 void BattleGround::DoorClose(uint64
const& guid
)
1427 GameObject
*obj
= GetBgMap()->GetGameObject(guid
);
1430 //if doors are open, close it
1431 if (obj
->getLootState() == GO_ACTIVATED
&& obj
->GetGoState() != GO_STATE_READY
)
1433 //change state to allow door to be closed
1434 obj
->SetLootState(GO_READY
);
1435 obj
->UseDoorOrButton(RESPAWN_ONE_DAY
);
1440 sLog
.outError("BattleGround: Door object not found (cannot close doors)");
1444 void BattleGround::DoorOpen(uint64
const& guid
)
1446 GameObject
*obj
= GetBgMap()->GetGameObject(guid
);
1449 //change state to be sure they will be opened
1450 obj
->SetLootState(GO_READY
);
1451 obj
->UseDoorOrButton(RESPAWN_ONE_DAY
);
1455 sLog
.outError("BattleGround: Door object not found! - doors will be closed.");
1459 void BattleGround::OnObjectDBLoad(Creature
* creature
)
1461 const BattleGroundEventIdx eventId
= sBattleGroundMgr
.GetCreatureEventIndex(creature
->GetDBTableGUIDLow());
1462 if (eventId
.event1
== BG_EVENT_NONE
)
1464 m_EventObjects
[MAKE_PAIR32(eventId
.event1
, eventId
.event2
)].creatures
.push_back(creature
->GetGUID());
1465 if (!IsActiveEvent(eventId
.event1
, eventId
.event2
))
1466 SpawnBGCreature(creature
->GetGUID(), RESPAWN_ONE_DAY
);
1469 uint64
BattleGround::GetSingleCreatureGuid(uint8 event1
, uint8 event2
)
1471 BGCreatures::const_iterator itr
= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].creatures
.begin();
1472 if (itr
!= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].creatures
.end())
1477 void BattleGround::OnObjectDBLoad(GameObject
* obj
)
1479 const BattleGroundEventIdx eventId
= sBattleGroundMgr
.GetGameObjectEventIndex(obj
->GetDBTableGUIDLow());
1480 if (eventId
.event1
== BG_EVENT_NONE
)
1482 m_EventObjects
[MAKE_PAIR32(eventId
.event1
, eventId
.event2
)].gameobjects
.push_back(obj
->GetGUID());
1483 if (!IsActiveEvent(eventId
.event1
, eventId
.event2
))
1485 SpawnBGObject(obj
->GetGUID(), RESPAWN_ONE_DAY
);
1489 // it's possible, that doors aren't spawned anymore (wsg)
1490 if (GetStatus() >= STATUS_IN_PROGRESS
&& IsDoor(eventId
.event1
, eventId
.event2
))
1491 DoorOpen(obj
->GetGUID());
1495 bool BattleGround::IsDoor(uint8 event1
, uint8 event2
)
1497 if (event1
== BG_EVENT_DOOR
)
1501 sLog
.outError("BattleGround too high event2 for event1:%i", event1
);
1509 void BattleGround::OpenDoorEvent(uint8 event1
, uint8 event2
/*=0*/)
1511 if (!IsDoor(event1
, event2
))
1513 sLog
.outError("BattleGround:OpenDoorEvent this is no door event1:%u event2:%u", event1
, event2
);
1516 if (!IsActiveEvent(event1
, event2
)) // maybe already despawned (eye)
1518 sLog
.outError("BattleGround:OpenDoorEvent this event isn't active event1:%u event2:%u", event1
, event2
);
1521 BGObjects::const_iterator itr
= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].gameobjects
.begin();
1522 for(; itr
!= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].gameobjects
.end(); ++itr
)
1526 void BattleGround::SpawnEvent(uint8 event1
, uint8 event2
, bool spawn
)
1528 // stop if we want to spawn something which was already spawned
1529 // or despawn something which was already despawned
1530 if (event2
== BG_EVENT_NONE
|| (spawn
&& m_ActiveEvents
[event1
] == event2
)
1531 || (!spawn
&& m_ActiveEvents
[event1
] != event2
))
1536 // if event gets spawned, the current active event mus get despawned
1537 SpawnEvent(event1
, m_ActiveEvents
[event1
], false);
1538 m_ActiveEvents
[event1
] = event2
; // set this event to active
1541 m_ActiveEvents
[event1
] = BG_EVENT_NONE
; // no event active if event2 gets despawned
1543 BGCreatures::const_iterator itr
= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].creatures
.begin();
1544 for(; itr
!= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].creatures
.end(); ++itr
)
1545 SpawnBGCreature(*itr
, (spawn
) ? RESPAWN_IMMEDIATELY
: RESPAWN_ONE_DAY
);
1546 BGObjects::const_iterator itr2
= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].gameobjects
.begin();
1547 for(; itr2
!= m_EventObjects
[MAKE_PAIR32(event1
, event2
)].gameobjects
.end(); ++itr2
)
1548 SpawnBGObject(*itr2
, (spawn
) ? RESPAWN_IMMEDIATELY
: RESPAWN_ONE_DAY
);
1551 void BattleGround::SpawnBGObject(uint64
const& guid
, uint32 respawntime
)
1553 Map
* map
= GetBgMap();
1555 GameObject
*obj
= map
->GetGameObject(guid
);
1558 if (respawntime
== 0)
1560 //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again
1561 if (obj
->getLootState() == GO_JUST_DEACTIVATED
)
1562 obj
->SetLootState(GO_READY
);
1563 obj
->SetRespawnTime(0);
1569 obj
->SetRespawnTime(respawntime
);
1570 obj
->SetLootState(GO_JUST_DEACTIVATED
);
1574 void BattleGround::SpawnBGCreature(uint64
const& guid
, uint32 respawntime
)
1576 Map
* map
= GetBgMap();
1578 Creature
* obj
= map
->GetCreature(guid
);
1581 if (respawntime
== 0)
1589 obj
->setDeathState(JUST_DIED
);
1590 obj
->SetRespawnDelay(respawntime
);
1591 obj
->RemoveCorpse();
1595 bool BattleGround::DelObject(uint32 type
)
1597 if (!m_BgObjects
[type
])
1600 GameObject
*obj
= GetBgMap()->GetGameObject(m_BgObjects
[type
]);
1603 sLog
.outError("Can't find gobject guid: %u",GUID_LOPART(m_BgObjects
[type
]));
1607 obj
->SetRespawnTime(0); // not save respawn time
1609 m_BgObjects
[type
] = 0;
1613 void BattleGround::SendMessageToAll(int32 entry
, ChatMsg type
, Player
const* source
)
1615 MaNGOS::BattleGroundChatBuilder
bg_builder(type
, entry
, source
);
1616 MaNGOS::LocalizedPacketDo
<MaNGOS::BattleGroundChatBuilder
> bg_do(bg_builder
);
1617 BroadcastWorker(bg_do
);
1620 void BattleGround::SendYellToAll(int32 entry
, uint32 language
, uint64
const& guid
)
1622 Creature
* source
= GetBgMap()->GetCreature(guid
);
1625 MaNGOS::BattleGroundYellBuilder
bg_builder(language
, entry
, source
);
1626 MaNGOS::LocalizedPacketDo
<MaNGOS::BattleGroundYellBuilder
> bg_do(bg_builder
);
1627 BroadcastWorker(bg_do
);
1630 void BattleGround::PSendMessageToAll(int32 entry
, ChatMsg type
, Player
const* source
, ...)
1633 va_start(ap
, source
);
1635 MaNGOS::BattleGroundChatBuilder
bg_builder(type
, entry
, source
, &ap
);
1636 MaNGOS::LocalizedPacketDo
<MaNGOS::BattleGroundChatBuilder
> bg_do(bg_builder
);
1637 BroadcastWorker(bg_do
);
1642 void BattleGround::SendMessage2ToAll(int32 entry
, ChatMsg type
, Player
const* source
, int32 arg1
, int32 arg2
)
1644 MaNGOS::BattleGround2ChatBuilder
bg_builder(type
, entry
, source
, arg1
, arg2
);
1645 MaNGOS::LocalizedPacketDo
<MaNGOS::BattleGround2ChatBuilder
> bg_do(bg_builder
);
1646 BroadcastWorker(bg_do
);
1649 void BattleGround::SendYell2ToAll(int32 entry
, uint32 language
, uint64
const& guid
, int32 arg1
, int32 arg2
)
1651 Creature
* source
= GetBgMap()->GetCreature(guid
);
1654 MaNGOS::BattleGround2YellBuilder
bg_builder(language
, entry
, source
, arg1
, arg2
);
1655 MaNGOS::LocalizedPacketDo
<MaNGOS::BattleGround2YellBuilder
> bg_do(bg_builder
);
1656 BroadcastWorker(bg_do
);
1659 void BattleGround::EndNow()
1661 RemoveFromBGFreeSlotQueue();
1662 SetStatus(STATUS_WAIT_LEAVE
);
1668 buffs aren't spawned/despawned when players captures anything
1669 buffs are in their positions when battleground starts
1671 void BattleGround::HandleTriggerBuff(uint64
const& go_guid
)
1673 GameObject
*obj
= GetBgMap()->GetGameObject(go_guid
);
1674 if (!obj
|| obj
->GetGoType() != GAMEOBJECT_TYPE_TRAP
|| !obj
->isSpawned())
1677 // static buffs are already handled just by database and don't need
1678 // battleground code
1681 obj
->SetLootState(GO_JUST_DEACTIVATED
); // can be despawned or destroyed
1685 // change buff type, when buff is used:
1686 // TODO this can be done when poolsystem works for instances
1687 int32 index
= m_BgObjects
.size() - 1;
1688 while (index
>= 0 && m_BgObjects
[index
] != go_guid
)
1692 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());
1696 //randomly select new buff
1697 uint8 buff
= urand(0, 2);
1698 uint32 entry
= obj
->GetEntry();
1699 if (m_BuffChange
&& entry
!= Buff_Entries
[buff
])
1701 //despawn current buff
1702 SpawnBGObject(m_BgObjects
[index
], RESPAWN_ONE_DAY
);
1703 //set index for new one
1704 for (uint8 currBuffTypeIndex
= 0; currBuffTypeIndex
< 3; ++currBuffTypeIndex
)
1706 if (entry
== Buff_Entries
[currBuffTypeIndex
])
1708 index
-= currBuffTypeIndex
;
1714 SpawnBGObject(m_BgObjects
[index
], BUFF_RESPAWN_TIME
);
1717 void BattleGround::HandleKillPlayer( Player
*player
, Player
*killer
)
1720 UpdatePlayerScore(player
, SCORE_DEATHS
, 1);
1722 // add +1 kills to group and +1 killing_blows to killer
1725 UpdatePlayerScore(killer
, SCORE_HONORABLE_KILLS
, 1);
1726 UpdatePlayerScore(killer
, SCORE_KILLING_BLOWS
, 1);
1728 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
1730 Player
*plr
= objmgr
.GetPlayer(itr
->first
);
1732 if (!plr
|| plr
== killer
)
1735 if (plr
->GetTeam() == killer
->GetTeam() && plr
->IsAtGroupRewardDistance(player
))
1736 UpdatePlayerScore(plr
, SCORE_HONORABLE_KILLS
, 1);
1740 // to be able to remove insignia -- ONLY IN BattleGrounds
1742 player
->SetFlag( UNIT_FIELD_FLAGS
, UNIT_FLAG_SKINNABLE
);
1745 // return the player's team based on battlegroundplayer info
1746 // used in same faction arena matches mainly
1747 uint32
BattleGround::GetPlayerTeam(uint64 guid
)
1749 BattleGroundPlayerMap::const_iterator itr
= m_Players
.find(guid
);
1750 if (itr
!=m_Players
.end())
1751 return itr
->second
.Team
;
1755 bool BattleGround::IsPlayerInBattleGround(uint64 guid
)
1757 BattleGroundPlayerMap::const_iterator itr
= m_Players
.find(guid
);
1758 if (itr
!= m_Players
.end())
1763 void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player
* plr
)
1765 if (GetStatus() != STATUS_WAIT_LEAVE
)
1769 BattleGroundQueueTypeId bgQueueTypeId
= BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
1773 sBattleGroundMgr
.BuildPvpLogDataPacket(&data
, this);
1774 plr
->GetSession()->SendPacket(&data
);
1776 sBattleGroundMgr
.BuildBattleGroundStatusPacket(&data
, this, plr
->GetBattleGroundQueueIndex(bgQueueTypeId
), STATUS_IN_PROGRESS
, GetEndTime(), GetStartTime(), GetArenaType());
1777 plr
->GetSession()->SendPacket(&data
);
1780 uint32
BattleGround::GetAlivePlayersCountByTeam(uint32 Team
) const
1783 for(BattleGroundPlayerMap::const_iterator itr
= m_Players
.begin(); itr
!= m_Players
.end(); ++itr
)
1785 if (itr
->second
.Team
== Team
)
1787 Player
* pl
= objmgr
.GetPlayer(itr
->first
);
1788 if (pl
&& pl
->isAlive())
1795 void BattleGround::CheckArenaWinConditions()
1797 if (!GetAlivePlayersCountByTeam(ALLIANCE
) && GetPlayersCountByTeam(HORDE
))
1798 EndBattleGround(HORDE
);
1799 else if (GetPlayersCountByTeam(ALLIANCE
) && !GetAlivePlayersCountByTeam(HORDE
))
1800 EndBattleGround(ALLIANCE
);
1803 void BattleGround::SetBgRaid( uint32 TeamID
, Group
*bg_raid
)
1805 Group
* &old_raid
= TeamID
== ALLIANCE
? m_BgRaids
[BG_TEAM_ALLIANCE
] : m_BgRaids
[BG_TEAM_HORDE
];
1806 if(old_raid
) old_raid
->SetBattlegroundGroup(NULL
);
1807 if(bg_raid
) bg_raid
->SetBattlegroundGroup(this);
1811 WorldSafeLocsEntry
const* BattleGround::GetClosestGraveYard( Player
* player
)
1813 return objmgr
.GetClosestGraveYard( player
->GetPositionX(), player
->GetPositionY(), player
->GetPositionZ(), player
->GetMapId(), player
->GetTeam() );
1816 bool BattleGround::IsTeamScoreInRange(uint32 team
, uint32 minScore
, uint32 maxScore
) const
1818 BattleGroundTeamId team_idx
= GetTeamIndexByTeamId(team
);
1819 uint32 score
= (m_TeamScores
[team_idx
] < 0) ? 0 : uint32(m_TeamScores
[team_idx
]);
1820 return score
>= minScore
&& score
<= maxScore
;