Merge branch 'master' into 303
[getmangos.git] / src / game / BattleGround.cpp
blobc51c4e9e378ac481a683cdd8e87b82fa538d0d17
1 /*
2 * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "Object.h"
20 #include "Player.h"
21 #include "BattleGround.h"
22 #include "Creature.h"
23 #include "MapManager.h"
24 #include "Language.h"
25 #include "Chat.h"
26 #include "SpellAuras.h"
27 #include "World.h"
28 #include "Util.h"
30 BattleGround::BattleGround()
32 m_TypeID = 0;
33 m_InstanceID = 0;
34 m_Status = 0;
35 m_EndTime = 0;
36 m_LastResurrectTime = 0;
37 m_Queue_type = MAX_BATTLEGROUND_QUEUES;
38 m_InvitedAlliance = 0;
39 m_InvitedHorde = 0;
40 m_ArenaType = 0;
41 m_IsArena = false;
42 m_Winner = 2;
43 m_StartTime = 0;
44 m_Events = 0;
45 m_IsRated = false;
46 m_BuffChange = false;
47 m_Name = "";
48 m_LevelMin = 0;
49 m_LevelMax = 0;
51 m_MaxPlayersPerTeam = 0;
52 m_MaxPlayers = 0;
53 m_MinPlayersPerTeam = 0;
54 m_MinPlayers = 0;
56 m_MapId = 0;
58 m_TeamStartLocX[BG_TEAM_ALLIANCE] = 0;
59 m_TeamStartLocX[BG_TEAM_HORDE] = 0;
61 m_TeamStartLocY[BG_TEAM_ALLIANCE] = 0;
62 m_TeamStartLocY[BG_TEAM_HORDE] = 0;
64 m_TeamStartLocZ[BG_TEAM_ALLIANCE] = 0;
65 m_TeamStartLocZ[BG_TEAM_HORDE] = 0;
67 m_TeamStartLocO[BG_TEAM_ALLIANCE] = 0;
68 m_TeamStartLocO[BG_TEAM_HORDE] = 0;
70 m_BgRaids[BG_TEAM_ALLIANCE] = NULL;
71 m_BgRaids[BG_TEAM_HORDE] = NULL;
73 m_PlayersCount[BG_TEAM_ALLIANCE] = 0;
74 m_PlayersCount[BG_TEAM_HORDE] = 0;
77 BattleGround::~BattleGround()
82 void BattleGround::Update(time_t diff)
85 if(!GetPlayersSize() && !GetRemovedPlayersSize() && !GetReviveQueueSize())
86 //BG is empty
87 return;
89 WorldPacket data;
91 if(GetRemovedPlayersSize())
93 for(std::map<uint64, uint8>::iterator itr = m_RemovedPlayers.begin(); itr != m_RemovedPlayers.end(); ++itr)
95 Player *plr = objmgr.GetPlayer(itr->first);
96 switch(itr->second)
98 //following code is handled by event:
99 /*case 0:
100 sBattleGroundMgr.m_BattleGroundQueues[GetTypeID()].RemovePlayer(itr->first);
101 //RemovePlayerFromQueue(itr->first);
102 if(plr)
104 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(m_TypeID), STATUS_NONE, 0, 0);
105 plr->GetSession()->SendPacket(&data);
107 break;*/
108 case 1: // currently in bg and was removed from bg
109 if(plr)
110 RemovePlayerAtLeave(itr->first, true, true);
111 else
112 RemovePlayerAtLeave(itr->first, false, false);
113 break;
114 case 2: // revive queue
115 RemovePlayerFromResurrectQueue(itr->first);
116 break;
117 default:
118 sLog.outError("BattleGround: Unknown remove player case!");
121 m_RemovedPlayers.clear();
124 // this code isn't efficient and its idea isn't implemented yet
125 /* offline players are removed from battleground in worldsession::LogoutPlayer()
126 // remove offline players from bg after ~5 minutes
127 if(GetPlayersSize())
129 for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
131 Player *plr = objmgr.GetPlayer(itr->first);
132 itr->second.LastOnlineTime += diff;
134 if(plr)
135 itr->second.LastOnlineTime = 0; // update last online time
136 else
137 if(itr->second.LastOnlineTime >= MAX_OFFLINE_TIME) // 5 minutes
138 m_RemovedPlayers[itr->first] = 1; // add to remove list (BG)
142 m_LastResurrectTime += diff;
143 if (m_LastResurrectTime >= RESURRECTION_INTERVAL)
145 if(GetReviveQueueSize())
147 for(std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
149 Creature *sh = NULL;
150 for(std::vector<uint64>::iterator itr2 = (itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
152 Player *plr = objmgr.GetPlayer(*itr2);
153 if(!plr)
154 continue;
156 if (!sh)
158 sh = ObjectAccessor::GetCreature(*plr, itr->first);
159 // only for visual effect
160 if (sh)
161 sh->CastSpell(sh, SPELL_SPIRIT_HEAL, true); // Spirit Heal, effect 117
164 plr->CastSpell(plr, SPELL_RESURRECTION_VISUAL, true); // Resurrection visual
165 m_ResurrectQueue.push_back(*itr2);
167 (itr->second).clear();
170 m_ReviveQueue.clear();
171 m_LastResurrectTime = 0;
173 else
174 // queue is clear and time passed, just update last resurrection time
175 m_LastResurrectTime = 0;
177 else if (m_LastResurrectTime > 500) // Resurrect players only half a second later, to see spirit heal effect on NPC
179 for(std::vector<uint64>::iterator itr = m_ResurrectQueue.begin(); itr != m_ResurrectQueue.end(); ++itr)
181 Player *plr = objmgr.GetPlayer(*itr);
182 if(!plr)
183 continue;
184 plr->ResurrectPlayer(1.0f);
185 plr->CastSpell(plr, SPELL_SPIRIT_HEAL_MANA, true);
186 ObjectAccessor::Instance().ConvertCorpseForPlayer(*itr);
188 m_ResurrectQueue.clear();
191 if(GetStatus() == STATUS_WAIT_LEAVE)
193 // remove all players from battleground after 2 minutes
194 m_EndTime += diff;
195 if(m_EndTime >= TIME_TO_AUTOREMOVE) // 2 minutes
197 for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
199 m_RemovedPlayers[itr->first] = 1; // add to remove list (BG)
201 // do not change any battleground's private variables
206 void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O)
208 uint8 idx = GetTeamIndexByTeamId(TeamID);
209 m_TeamStartLocX[idx] = X;
210 m_TeamStartLocY[idx] = Y;
211 m_TeamStartLocZ[idx] = Z;
212 m_TeamStartLocO[idx] = O;
215 void BattleGround::SendPacketToAll(WorldPacket *packet)
217 for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
219 Player *plr = objmgr.GetPlayer(itr->first);
220 if(plr)
221 plr->GetSession()->SendPacket(packet);
222 else
223 sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first);
227 void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender, bool self)
229 for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
231 Player *plr = objmgr.GetPlayer(itr->first);
233 if(!plr)
235 sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first);
236 continue;
239 if(!self && sender == plr)
240 continue;
242 if(plr->GetTeam() == TeamID)
243 plr->GetSession()->SendPacket(packet);
247 void BattleGround::PlaySoundToAll(uint32 SoundID)
249 WorldPacket data;
250 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
251 SendPacketToAll(&data);
254 void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID)
256 WorldPacket data;
258 for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
260 Player *plr = objmgr.GetPlayer(itr->first);
262 if(!plr)
264 sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first);
265 continue;
268 if(plr->GetTeam() == TeamID)
270 sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
271 plr->GetSession()->SendPacket(&data);
276 void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID)
278 for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
280 Player *plr = objmgr.GetPlayer(itr->first);
282 if(!plr)
284 sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first);
285 continue;
288 if(plr->GetTeam() == TeamID)
289 plr->CastSpell(plr, SpellID, true);
293 void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID)
295 for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
297 Player *plr = objmgr.GetPlayer(itr->first);
299 if(!plr)
301 sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first);
302 continue;
305 if(plr->GetTeam() == TeamID)
306 UpdatePlayerScore(plr, SCORE_BONUS_HONOR, Honor);
310 void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID)
312 FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
314 if(!factionEntry)
315 return;
317 for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
319 Player *plr = objmgr.GetPlayer(itr->first);
321 if(!plr)
323 sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first);
324 continue;
327 if(plr->GetTeam() == TeamID)
328 plr->ModifyFactionReputation(factionEntry, Reputation);
332 void BattleGround::UpdateWorldState(uint32 Field, uint32 Value)
334 WorldPacket data;
335 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
336 SendPacketToAll(&data);
339 void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source)
341 WorldPacket data;
342 sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value);
343 Source->GetSession()->SendPacket(&data);
346 void BattleGround::EndBattleGround(uint32 winner)
348 WorldPacket data;
349 Player *Source = NULL;
350 const char *winmsg = "";
352 if(winner == ALLIANCE)
354 winmsg = GetMangosString(LANG_BG_A_WINS);
356 PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound
358 SetWinner(WINNER_ALLIANCE);
360 else
362 winmsg = GetMangosString(LANG_BG_H_WINS);
364 PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound
366 SetWinner(WINNER_HORDE);
369 SetStatus(STATUS_WAIT_LEAVE);
370 m_EndTime = 0;
372 for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
374 Player *plr = objmgr.GetPlayer(itr->first);
375 if(!plr)
377 sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first);
378 continue;
381 if(!plr->isAlive())
383 plr->ResurrectPlayer(1.0f);
384 plr->SpawnCorpseBones();
387 if(plr->GetTeam() == winner)
389 if(!Source)
390 Source = plr;
391 RewardMark(plr,ITEM_WINNER_COUNT);
392 UpdatePlayerScore(plr, SCORE_BONUS_HONOR, 20);
393 RewardQuest(plr);
395 else
397 RewardMark(plr,ITEM_LOSER_COUNT);
400 plr->CombatStopWithPets(true);
402 BlockMovement(plr);
404 sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
405 plr->GetSession()->SendPacket(&data);
407 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(m_TypeID), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime());
408 plr->GetSession()->SendPacket(&data);
409 plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1);
412 if(Source)
414 ChatHandler(Source).FillMessageData(&data, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, Source->GetGUID(), winmsg);
415 SendPacketToAll(&data);
419 uint32 BattleGround::GetBattlemasterEntry() const
421 switch(GetTypeID())
423 case BATTLEGROUND_AV: return 15972;
424 case BATTLEGROUND_WS: return 14623;
425 case BATTLEGROUND_AB: return 14879;
426 case BATTLEGROUND_EY: return 22516;
427 case BATTLEGROUND_NA: return 20200;
428 default: return 0;
432 void BattleGround::RewardMark(Player *plr,uint32 count)
434 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
435 if(plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
436 return;
438 BattleGroundMarks mark;
439 bool IsSpell;
440 switch(GetTypeID())
442 case BATTLEGROUND_AV:
443 IsSpell = true;
444 if(count == ITEM_WINNER_COUNT)
445 mark = SPELL_AV_MARK_WINNER;
446 else
447 mark = SPELL_AV_MARK_LOSER;
448 break;
449 case BATTLEGROUND_WS:
450 IsSpell = true;
451 if(count == ITEM_WINNER_COUNT)
452 mark = SPELL_WS_MARK_WINNER;
453 else
454 mark = SPELL_WS_MARK_LOSER;
455 break;
456 case BATTLEGROUND_AB:
457 IsSpell = true;
458 if(count == ITEM_WINNER_COUNT)
459 mark = SPELL_AB_MARK_WINNER;
460 else
461 mark = SPELL_AB_MARK_LOSER;
462 break;
463 case BATTLEGROUND_EY:
464 IsSpell = false;
465 mark = ITEM_EY_MARK_OF_HONOR;
466 break;
467 default:
468 return;
471 if(IsSpell)
472 plr->CastSpell(plr, mark, true);
473 else if ( objmgr.GetItemPrototype( mark ) )
475 ItemPosCountVec dest;
476 uint32 no_space_count = 0;
477 uint8 msg = plr->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, mark, count, &no_space_count );
478 if( msg != EQUIP_ERR_OK ) // convert to possible store amount
479 count -= no_space_count;
481 if( count != 0 && !dest.empty()) // can add some
482 if(Item* item = plr->StoreNewItem( dest, mark, true, 0))
483 plr->SendNewItem(item,count,false,true);
485 if(no_space_count > 0)
486 SendRewardMarkByMail(plr,mark,no_space_count);
490 void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count)
492 uint32 bmEntry = GetBattlemasterEntry();
493 if(!bmEntry)
494 return;
496 ItemPrototype const* markProto = objmgr.GetItemPrototype(mark);
497 if(!markProto)
498 return;
500 if(Item* markItem = Item::CreateItem(mark,count,plr))
502 // save new item before send
503 markItem->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
505 // item
506 MailItemsInfo mi;
507 mi.AddItem(markItem->GetGUIDLow(), markItem->GetEntry(), markItem);
509 // subject: item name
510 std::string subject = markProto->Name1;
511 int loc_idx = plr->GetSession()->GetSessionDbLocaleIndex();
512 if ( loc_idx >= 0 )
513 if(ItemLocale const *il = objmgr.GetItemLocale(markProto->ItemId))
514 if (il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty())
515 subject = il->Name[loc_idx];
517 // text
518 std::string textFormat = plr->GetSession()->GetMangosString(LANG_BG_MARK_BY_MAIL);
519 char textBuf[300];
520 snprintf(textBuf,300,textFormat.c_str(),GetName(),GetName());
521 uint32 itemTextId = objmgr.CreateItemText( textBuf );
523 WorldSession::SendMailTo(plr, MAIL_CREATURE, MAIL_STATIONERY_NORMAL, bmEntry, plr->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE);
527 void BattleGround::RewardQuest(Player *plr)
529 // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
530 if(plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
531 return;
533 uint32 quest;
534 switch(GetTypeID())
536 case BATTLEGROUND_AV:
537 quest = SPELL_AV_QUEST_REWARD;
538 break;
539 case BATTLEGROUND_WS:
540 quest = SPELL_WS_QUEST_REWARD;
541 break;
542 case BATTLEGROUND_AB:
543 quest = SPELL_AB_QUEST_REWARD;
544 break;
545 case BATTLEGROUND_EY:
546 quest = SPELL_EY_QUEST_REWARD;
547 break;
548 default:
549 return;
552 plr->CastSpell(plr, quest, true);
555 void BattleGround::BlockMovement(Player *plr)
557 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()
560 void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket)
562 // Remove from lists/maps
563 std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.find(guid);
564 if(itr != m_Players.end())
566 UpdatePlayersCountByTeam(itr->second.Team, true); // -1 player
567 m_Players.erase(itr);
570 std::map<uint64, BattleGroundScore*>::iterator itr2 = m_PlayerScores.find(guid);
571 if(itr2 != m_PlayerScores.end())
573 delete itr2->second; // delete player's score
574 m_PlayerScores.erase(itr2);
577 RemovePlayerFromResurrectQueue(guid);
579 Player *plr = objmgr.GetPlayer(guid);
581 if(plr && !plr->isAlive()) // resurrect on exit
583 plr->ResurrectPlayer(1.0f);
584 plr->SpawnCorpseBones();
587 RemovePlayer(plr, guid); // BG subclass specific code
589 if(plr)
591 plr->ClearAfkReports();
593 if(isArena())
595 if(!sWorld.IsFFAPvPRealm())
596 plr->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP);
599 WorldPacket data;
600 if(SendPacket)
602 sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(m_TypeID), STATUS_NONE, 0, 0);
603 plr->GetSession()->SendPacket(&data);
606 // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg
607 plr->RemoveBattleGroundQueueId(m_TypeID);
609 DecreaseInvitedCount(plr->GetTeam());
610 //we should update battleground queue, but only if bg isn't ending
611 if (GetQueueType() < MAX_BATTLEGROUND_QUEUES)
612 sBattleGroundMgr.m_BattleGroundQueues[GetTypeID()].Update(GetTypeID(), GetQueueType());
614 if(!plr->GetBattleGroundId())
615 return;
617 Group * group = plr->GetGroup();
619 // remove from raid group if exist
620 if(group && group == GetBgRaid(plr->GetTeam()))
622 if(!group->RemoveMember(guid, 0)) // group was disbanded
624 SetBgRaid(plr->GetTeam(), NULL);
625 delete group;
629 // Do next only if found in battleground
630 plr->SetBattleGroundId(0); // We're not in BG.
632 // Let others know
633 sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, plr);
634 SendPacketToTeam(plr->GetTeam(), &data, plr, false);
636 if(Transport)
638 plr->TeleportTo(plr->GetBattleGroundEntryPointMap(), plr->GetBattleGroundEntryPointX(), plr->GetBattleGroundEntryPointY(), plr->GetBattleGroundEntryPointZ(), plr->GetBattleGroundEntryPointO());
639 //sLog.outDetail("BATTLEGROUND: Sending %s to %f,%f,%f,%f", pl->GetName(), x,y,z,O);
642 // Log
643 sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName());
646 /// there will be code which will add battleground to BGFreeSlotQueue , when battleground instance will exist
647 // we always should check if BG is in that queue before adding..
649 if(!GetPlayersSize())
651 Reset();
655 // this method is called when no players remains in battleground
656 void BattleGround::Reset()
658 SetQueueType(MAX_BATTLEGROUND_QUEUES);
659 SetWinner(WINNER_NONE);
660 SetStatus(STATUS_WAIT_QUEUE);
661 SetStartTime(0);
662 SetEndTime(0);
663 SetLastResurrectTime(0);
665 m_Events = 0;
667 if (m_InvitedAlliance > 0 || m_InvitedHorde > 0)
668 sLog.outError("BattleGround system ERROR: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde);
670 m_InvitedAlliance = 0;
671 m_InvitedHorde = 0;
673 m_Players.clear();
674 m_PlayerScores.clear();
676 // reset BGSubclass
677 ResetBGSubclass();
680 void BattleGround::StartBattleGround()
682 ///this method should spawn spirit guides and so on
683 SetStartTime(0);
685 SetLastResurrectTime(0);
688 void BattleGround::AddPlayer(Player *plr)
690 // score struct must be created in inherited class
692 uint64 guid = plr->GetGUID();
693 uint32 team = plr->GetBGTeam();
695 BattleGroundPlayer bp;
696 bp.LastOnlineTime = 0;
697 bp.Team = team;
699 // Add to list/maps
700 m_Players[guid] = bp;
702 UpdatePlayersCountByTeam(team, false); // +1 player
704 WorldPacket data;
705 sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr);
706 SendPacketToTeam(team, &data, plr, false);
708 if(isArena())
710 plr->RemoveArenaSpellCooldowns();
711 //plr->RemoveArenaAuras();
712 plr->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT);
713 if(team == ALLIANCE) // gold
715 if(plr->GetTeam() == HORDE)
716 plr->CastSpell(plr, SPELL_HORDE_GOLD_FLAG,true);
717 else
718 plr->CastSpell(plr, SPELL_ALLIANCE_GOLD_FLAG,true);
720 else
722 if(plr->GetTeam() == HORDE) // green
723 plr->CastSpell(plr, SPELL_HORDE_GREEN_FLAG,true);
724 else
725 plr->CastSpell(plr, SPELL_ALLIANCE_GREEN_FLAG,true);
728 plr->DestroyConjuredItems(true);
730 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
732 plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true);
734 plr->SetHealth(plr->GetMaxHealth());
735 plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA));
738 else
740 if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
741 plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells.
744 if(isArena())
745 plr->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP);
747 // Log
748 sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName());
751 /* This method should be called only once ... it adds pointer to queue */
752 void BattleGround::AddToBGFreeSlotQueue()
754 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this);
757 /* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/
758 void BattleGround::RemoveFromBGFreeSlotQueue()
760 /* uncomment this code when battlegrounds will work like instances
761 for (std::deque<BattleGround*>::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr)
763 if ((*itr)->GetInstanceID() == m_InstanceID)
765 sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr);
766 return;
772 this method should decide, if we can invite new player of certain team to BG, it is based on BATTLEGROUND_STATUS
774 bool BattleGround::HasFreeSlotsForTeam(uint32 Team) const
776 //if BG is starting ... invite anyone:
777 if (GetStatus() == STATUS_WAIT_JOIN)
778 return GetInvitedCount(Team) < GetMaxPlayersPerTeam();
779 //if BG is already started .. do not allow to join too much players of one faction
780 uint32 otherTeam;
781 if (Team == ALLIANCE)
782 otherTeam = GetInvitedCount(HORDE);
783 else
784 otherTeam = GetInvitedCount(ALLIANCE);
785 if (GetStatus() == STATUS_IN_PROGRESS)
786 return (GetInvitedCount(Team) <= otherTeam && GetInvitedCount(Team) < GetMaxPlayersPerTeam());
788 return false;
791 /* this method isn't called already, it will be useful when more battlegrounds of one type will be available */
792 bool BattleGround::HasFreeSlots() const
794 return GetPlayersSize() < GetMaxPlayers();
797 void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value)
799 //this procedure is called from virtual function implemented in bg subclass
800 std::map<uint64, BattleGroundScore*>::iterator itr = m_PlayerScores.find(Source->GetGUID());
802 if(itr == m_PlayerScores.end()) // player not found...
803 return;
805 switch(type)
807 case SCORE_KILLING_BLOWS: // Killing blows
808 itr->second->KillingBlows += value;
809 break;
810 case SCORE_DEATHS: // Deaths
811 itr->second->Deaths += value;
812 break;
813 case SCORE_HONORABLE_KILLS: // Honorable kills
814 itr->second->HonorableKills += value;
815 break;
816 case SCORE_BONUS_HONOR: // Honor bonus
817 // reward honor instantly
818 if(Source->RewardHonor(NULL, 1, value))
819 itr->second->BonusHonor += value;
820 break;
821 //used only in EY, but in MSG_PVP_LOG_DATA opcode
822 case SCORE_DAMAGE_DONE: // Damage Done
823 itr->second->DamageDone += value;
824 break;
825 case SCORE_HEALING_DONE: // Healing Done
826 itr->second->HealingDone += value;
827 break;
828 default:
829 sLog.outError("BattleGround: Unknown player score type %u", type);
830 break;
834 void BattleGround::AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid)
836 m_ReviveQueue[npc_guid].push_back(player_guid);
838 Player *plr = objmgr.GetPlayer(player_guid);
839 if(!plr)
840 return;
842 plr->CastSpell(plr, SPELL_WAITING_FOR_RESURRECT, true);
843 SpellEntry const *spellInfo = sSpellStore.LookupEntry( SPELL_WAITING_FOR_RESURRECT );
844 if(spellInfo)
846 Aura *Aur = CreateAura(spellInfo, 0, NULL, plr);
847 plr->AddAura(Aur);
851 void BattleGround::RemovePlayerFromResurrectQueue(uint64 player_guid)
853 for(std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
855 for(std::vector<uint64>::iterator itr2 =(itr->second).begin(); itr2 != (itr->second).end(); ++itr2)
857 if(*itr2 == player_guid)
859 (itr->second).erase(itr2);
861 Player *plr = objmgr.GetPlayer(player_guid);
862 if(!plr)
863 return;
865 plr->RemoveAurasDueToSpell(SPELL_WAITING_FOR_RESURRECT);
867 return;
873 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)
875 GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(entry);
876 if(!goinfo)
878 sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry);
879 return false;
882 uint32 guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
884 GameObjectData& data = objmgr.NewGOData(guid);
886 data.id = entry;
887 data.mapid = GetMapId();
888 data.posX = x;
889 data.posY = y;
890 data.posZ = z;
891 data.orientation = o;
892 data.rotation0 = rotation0;
893 data.rotation1 = rotation1;
894 data.rotation2 = rotation2;
895 data.rotation3 = rotation3;
896 data.spawntimesecs = respawnTime;
897 data.animprogress = 100;
898 data.go_state = 1;
899 data.spawnMask = 1;
900 objmgr.AddGameobjectToGrid(guid, &data);
902 m_BgObjects[type] = MAKE_NEW_GUID(guid, entry, HIGHGUID_GAMEOBJECT);
904 return true;
907 //some doors aren't despawned so we cannot handle their closing in gameobject::update()
908 //it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
909 void BattleGround::DoorClose(uint32 type)
911 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
912 if(obj)
914 //if doors are open, close it
915 if( obj->getLootState() == GO_ACTIVATED && !obj->GetGoState() )
917 //change state to allow door to be closed
918 obj->SetLootState(GO_READY);
919 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
922 else
924 sLog.outError("BattleGround: Door object not found (cannot close doors)");
928 void BattleGround::DoorOpen(uint32 type)
930 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
931 if(obj)
933 //change state to be sure they will be opened
934 obj->SetLootState(GO_READY);
935 obj->UseDoorOrButton(RESPAWN_ONE_DAY);
937 else
939 sLog.outError("BattleGround: Door object not found! - doors will be closed.");
943 void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime)
945 if( respawntime == 0 )
947 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
948 if(obj)
950 //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again
951 if( obj->getLootState() == GO_JUST_DEACTIVATED )
952 obj->SetLootState(GO_READY);
953 obj->Respawn();
955 else
956 objmgr.SaveGORespawnTime(GUID_LOPART(m_BgObjects[type]), 0, 0);
958 else
960 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
961 if(obj)
963 obj->SetRespawnTime(respawntime);
964 obj->SetLootState(GO_JUST_DEACTIVATED);
966 else
967 objmgr.SaveGORespawnTime(GUID_LOPART(m_BgObjects[type]), 0, time(NULL) + respawntime);
971 Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o)
973 // note: this should normally be FindMap
974 // but it's a hack to allow the battlegrounds to initialize at server startup
975 Map * map = MapManager::Instance().GetMap(GetMapId(), 0);
976 if(!map) return NULL;
978 Creature* pCreature = new Creature;
979 if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, entry, teamval))
981 sLog.outError("Can't create creature entry: %u",entry);
982 delete pCreature;
983 return NULL;
986 pCreature->Relocate(x, y, z, o);
988 if(!pCreature->IsPositionValid())
990 sLog.outError("ERROR: Creature (guidlow %d, entry %d) not added to battleground. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
991 return NULL;
994 pCreature->AIM_Initialize();
996 //pCreature->SetDungeonDifficulty(0);
998 map->Add(pCreature);
999 m_BgCreatures[type] = pCreature->GetGUID();
1000 return pCreature;
1003 bool BattleGround::DelCreature(uint32 type)
1005 Creature *cr = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
1006 if(!cr)
1008 sLog.outError("Can't find creature guid: %u",GUID_LOPART(m_BgCreatures[type]));
1009 return false;
1011 cr->CleanupsBeforeDelete();
1012 cr->AddObjectToRemoveList();
1013 m_BgCreatures[type] = 0;
1014 return true;
1017 bool BattleGround::DelObject(uint32 type)
1019 GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
1020 if(!obj)
1022 sLog.outError("Can't find gobject guid: %u",GUID_LOPART(m_BgObjects[type]));
1023 return false;
1025 obj->SetRespawnTime(0); // not save respawn time
1026 obj->Delete();
1027 m_BgObjects[type] = 0;
1028 return true;
1031 bool BattleGround::AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team)
1033 uint32 entry = 0;
1035 if(team == ALLIANCE)
1036 entry = 13116;
1037 else
1038 entry = 13117;
1040 Creature* pCreature = AddCreature(entry,type,team,x,y,z,o);
1041 if(!pCreature)
1043 sLog.outError("Can't create Spirit guide. BattleGround not created!");
1044 EndNow();
1045 return false;
1048 pCreature->setDeathState(DEAD);
1050 pCreature->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, pCreature->GetGUID());
1051 // aura
1052 pCreature->SetVisibleAura(0, SPELL_SPIRIT_HEAL_CHANNEL);
1053 //pCreature->SetUInt32Value(UNIT_FIELD_AURAFLAGS, 0x00000009);
1054 //pCreature->SetUInt32Value(UNIT_FIELD_AURALEVELS, 0x0000003C);
1055 //pCreature->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS, 0x000000FF);
1056 // casting visual effect
1057 pCreature->SetUInt32Value(UNIT_CHANNEL_SPELL, SPELL_SPIRIT_HEAL_CHANNEL);
1058 // correct cast speed
1059 pCreature->SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
1061 //pCreature->CastSpell(pCreature, SPELL_SPIRIT_HEAL_CHANNEL, true);
1063 return true;
1066 void BattleGround::SendMessageToAll(char const* text)
1068 WorldPacket data;
1069 ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, text, NULL);
1070 SendPacketToAll(&data);
1073 void BattleGround::SendMessageToAll(int32 entry)
1075 char const* text = GetMangosString(entry);
1076 WorldPacket data;
1077 ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, text, NULL);
1078 SendPacketToAll(&data);
1081 void BattleGround::EndNow()
1083 SetStatus(STATUS_WAIT_LEAVE);
1084 SetEndTime(TIME_TO_AUTOREMOVE);
1087 // Battleground messages are localized using the dbc lang, they are not client language dependent
1088 const char *BattleGround::GetMangosString(int32 entry)
1090 // FIXME: now we have different DBC locales and need localized message for each target client
1091 return objmgr.GetMangosStringForDBCLocale(entry);
1095 important notice:
1096 buffs aren't spawned/despawned when players captures anything
1097 buffs are in their positions when battleground starts
1099 void BattleGround::HandleTriggerBuff(uint64 const& go_guid)
1101 GameObject *obj = HashMapHolder<GameObject>::Find(go_guid);
1102 if(!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned())
1103 return;
1105 //change buff type, when buff is used:
1106 int32 index = m_BgObjects.size() - 1;
1107 while (index >= 0 && m_BgObjects[index] != go_guid)
1108 index--;
1109 if (index < 0)
1111 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());
1112 return;
1115 //randomly select new buff
1116 uint8 buff = urand(0, 2);
1117 uint32 entry = obj->GetEntry();
1118 if( m_BuffChange && entry != Buff_Entries[buff] )
1120 //despawn current buff
1121 SpawnBGObject(index, RESPAWN_ONE_DAY);
1122 //set index for new one
1123 for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex)
1124 if( entry == Buff_Entries[currBuffTypeIndex] )
1126 index -= currBuffTypeIndex;
1127 index += buff;
1131 SpawnBGObject(index, BUFF_RESPAWN_TIME);
1134 void BattleGround::HandleKillPlayer( Player *player, Player *killer )
1136 //keep in mind that for arena this will have to be changed a bit
1138 // add +1 deaths
1139 UpdatePlayerScore(player, SCORE_DEATHS, 1);
1141 // add +1 kills to group and +1 killing_blows to killer
1142 if( killer )
1144 UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1);
1145 UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1);
1147 for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
1149 Player *plr = objmgr.GetPlayer(itr->first);
1151 if(!plr || plr == killer)
1152 continue;
1154 if( plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player) )
1155 UpdatePlayerScore(plr, SCORE_HONORABLE_KILLS, 1);
1159 // to be able to remove insignia
1160 player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE );