Last active
August 16, 2020 14:13
-
-
Save SymbolixDEV/9411639 to your computer and use it in GitHub Desktop.
Arena Spectate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
=================================================================================================== | |
From: Walkline | |
Fixed by : SymbolixDEV | |
Complete : 91% | |
Fully working Arena Spectator is for sale ! | |
=================================================================================================== | |
diff --git a/sql/arena-spectator/spectator.sql b/sql/arena-spectator/spectator.sql | |
new file mode 100644 | |
index 0000000..39cff15 | |
--- /dev/null | |
+++ b/sql/arena-spectator/spectator.sql | |
@@ -0,0 +1,12 @@ | |
+-- Implement Arena Spectator | |
+DELETE FROM `command` WHERE `name` = 'spectate'; | |
+INSERT INTO `command` (`name`, `security`, `help`) VALUES ('spectate', 0, 'Syntax: .spectate $subcommand.\nUse .help sppectate'); | |
+DELETE FROM `command` WHERE `name` = 'spectate view'; | |
+INSERT INTO `command` (`name`, `security`, `help`) VALUES ('spectate view', 0, 'Syntax: .spectate view #player\nAllow player to spectate arena from anotherplayer.'); | |
+DELETE FROM `command` WHERE `name` = 'spectate leave'; | |
+INSERT INTO `command` (`name`, `security`, `help`) VALUES ('spectate leave', 0, 'Syntax: .spectate leave\nDisable spectator mode.'); | |
+DELETE FROM `command` WHERE `name` = 'spectate player'; | |
+INSERT INTO `command` (`name`, `security`, `help`) VALUES ('spectate player', 0, 'Syntax: .spectate player #player\nAllow to spectate player.'); | |
+DELETE FROM `command` WHERE `name` = 'spectate reset'; | |
+INSERT INTO `command` (`name`, `security`, `help`) VALUES ('spectate reset', 0, 'Syntax: .spectate reset\nSend addon data.'); | |
+UPDATE `gameobject_template` SET `flags` = 36 WHERE entry IN (185918, 185917, 183970, 183971, 183972, 183973, 183977, 183979, 183978, 183980, 192642, 192643); | |
\ No newline at end of file | |
diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp | |
index 21c054c..bda05b4 100644 | |
--- a/src/server/game/Battlegrounds/Battleground.cpp | |
+++ b/src/server/game/Battlegrounds/Battleground.cpp | |
@@ -909,7 +909,12 @@ void Battleground::EndBattleground(uint32 winner) | |
if (IsRandom() || BattlegroundMgr::IsBGWeekend(GetTypeID())) | |
UpdatePlayerScore(player, SCORE_BONUS_HONOR, GetBonusHonorFromKill(loser_kills)); | |
} | |
- | |
+ | |
+ if (isArena()) | |
+ { | |
+ player->SetGMVisible(true); | |
+ player->SetGameMaster(false); | |
+ } | |
player->ResetAllPowers(); | |
player->CombatStopWithPets(true); | |
@@ -1069,6 +1074,8 @@ void Battleground::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac | |
player->SetBattlegroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG. | |
// reset destination bg team | |
player->SetBGTeam(0); | |
if (Transport) | |
player->TeleportToBGEntryPoint(); | |
@@ -1261,13 +1268,24 @@ void Battleground::EventPlayerLoggedOut(Player* player) | |
m_Players[guid].OfflineRemoveTime = sWorld->GetGameTime() + MAX_OFFLINE_TIME; | |
if (GetStatus() == STATUS_IN_PROGRESS) | |
{ | |
- // drop flag and handle other cleanups | |
- RemovePlayer(player, guid, GetPlayerTeam(guid)); | |
+ if (!player->IsSpectator()) | |
+ { | |
+ // drop flag and handle other cleanups | |
+ RemovePlayer(player, guid, GetPlayerTeam(guid)); | |
- // 1 player is logging out, if it is the last, then end arena! | |
- if (isArena()) | |
- if (GetAlivePlayersCountByTeam(player->GetBGTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetBGTeam()))) | |
- EndBattleground(GetOtherTeam(player->GetBGTeam())); | |
+ // 1 player is logging out, if it is the last, then end arena! | |
+ if (isArena()) | |
+ if (GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam()))) | |
+ EndBattleground(GetOtherTeam(player->GetTeam())); | |
+ } | |
+ } | |
+ | |
+ if (!player->IsSpectator()) | |
+ player->LeaveBattleground(); | |
+ else | |
+ { | |
+ player->TeleportToBGEntryPoint(); | |
+ RemoveSpectator(player->GetGUID()); | |
} | |
} | |
@@ -1966,6 +1984,15 @@ void Battleground::HandleAreaTrigger(Player* player, uint32 trigger) | |
trigger, player->GetMapId(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); | |
} | |
+void Battleground::SendSpectateAddonsMsg(SpectatorAddonMsg msg) | |
+{ | |
+ if (!HaveSpectators()) | |
+ return; | |
+ | |
+ for (SpectatorList::iterator itr = m_Spectators.begin(); itr != m_Spectators.end(); ++itr) | |
+ msg.SendPacket(*itr); | |
+} | |
+ | |
bool Battleground::CheckAchievementCriteriaMeet(uint32 criteriaId, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/) | |
{ | |
TC_LOG_ERROR(LOG_FILTER_BATTLEGROUND, "Battleground::CheckAchievementCriteriaMeet: No implementation for criteria %u", criteriaId); | |
diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h | |
index 1f18054..0c6b058 100644 | |
--- a/src/server/game/Battlegrounds/Battleground.h | |
+++ b/src/server/game/Battlegrounds/Battleground.h | |
@@ -22,6 +22,7 @@ | |
#include "Common.h" | |
#include "SharedDefines.h" | |
#include "DBCEnums.h" | |
+#include "SpectatorAddon.h" | |
class Creature; | |
class GameObject; | |
@@ -368,7 +369,13 @@ class Battleground | |
uint32 GetInvitedCount(uint32 team) const { return (team == ALLIANCE) ? m_InvitedAlliance : m_InvitedHorde; } | |
bool HasFreeSlots() const; | |
uint32 GetFreeSlotsForTeam(uint32 Team) const; | |
- | |
+ | |
+ typedef std::set<uint32> SpectatorList; | |
+ void AddSpectator(uint32 playerId) { m_Spectators.insert(playerId); } | |
+ void RemoveSpectator(uint32 playerId) { m_Spectators.erase(playerId); } | |
+ bool HaveSpectators() { return (m_Spectators.size() > 0); } | |
+ void SendSpectateAddonsMsg(SpectatorAddonMsg msg); | |
+ | |
bool isArena() const { return m_IsArena; } | |
bool isBattleground() const { return !m_IsArena; } | |
bool isRated() const { return m_IsRated; } | |
@@ -654,7 +661,8 @@ class Battleground | |
// Raid Group | |
Group* m_BgRaids[BG_TEAMS_COUNT]; // 0 - alliance, 1 - horde | |
- | |
+ | |
+ SpectatorList m_Spectators; | |
// Players count by team | |
uint32 m_PlayersCount[BG_TEAMS_COUNT]; | |
diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.h b/src/server/game/Battlegrounds/BattlegroundMgr.h | |
index c66d9d6..a74be6e 100644 | |
--- a/src/server/game/Battlegrounds/BattlegroundMgr.h | |
+++ b/src/server/game/Battlegrounds/BattlegroundMgr.h | |
@@ -89,7 +89,8 @@ class BattlegroundMgr | |
Battleground* GetBattleground(uint32 InstanceID, BattlegroundTypeId bgTypeId); | |
Battleground* GetBattlegroundTemplate(BattlegroundTypeId bgTypeId); | |
Battleground* CreateNewBattleground(BattlegroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated); | |
- | |
+ BattlegroundContainer GetBattlegroundsByType(BattlegroundTypeId bgTypeId) { return m_Battlegrounds[bgTypeId]; } | |
+ | |
void AddBattleground(Battleground* bg); | |
void RemoveBattleground(BattlegroundTypeId bgTypeId, uint32 instanceId); | |
void AddToBGFreeSlotQueue(BattlegroundTypeId bgTypeId, Battleground* bg); | |
@@ -113,6 +114,7 @@ class BattlegroundMgr | |
bool isArenaTesting() const { return m_ArenaTesting; } | |
bool isTesting() const { return m_Testing; } | |
+ bool IsArenaType(BattlegroundTypeId bgTypeId); | |
static BattlegroundQueueTypeId BGQueueTypeId(BattlegroundTypeId bgTypeId, uint8 arenaType); | |
static BattlegroundTypeId BGTemplateId(BattlegroundQueueTypeId bgQueueTypeId); | |
@@ -137,11 +139,11 @@ class BattlegroundMgr | |
private: | |
bool CreateBattleground(CreateBattlegroundData& data); | |
uint32 CreateClientVisibleInstanceId(BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id); | |
- static bool isArenaType(BattlegroundTypeId bgTypeId); | |
BattlegroundTypeId GetRandomBG(BattlegroundTypeId id); | |
typedef std::map<BattlegroundTypeId, BattlegroundData> BattlegroundDataContainer; | |
BattlegroundDataContainer bgDataStore; | |
+ BattlegroundContainer m_Battlegrounds[MAX_BATTLEGROUND_TYPE_ID]; | |
BattlegroundQueue m_BattlegroundQueues[MAX_BATTLEGROUND_QUEUE_TYPES]; | |
diff --git a/src/server/game/Battlegrounds/SpectatorAddon.cpp b/src/server/game/Battlegrounds/SpectatorAddon.cpp | |
new file mode 100644 | |
index 0000000..f174c1e | |
--- /dev/null | |
+++ b/src/server/game/Battlegrounds/SpectatorAddon.cpp | |
@@ -0,0 +1,203 @@ | |
+#include "Player.h" | |
+#include "Item.h" | |
+#include "SpellInfo.h" | |
+ | |
+SpectatorAddonMsg::SpectatorAddonMsg() | |
+{ | |
+ for (uint8 i = 0; i < SPECTATOR_PREFIX_COUNT; ++i) | |
+ prefixFlags[i] = false; | |
+ | |
+ player = ""; | |
+ target = ""; | |
+ isAlive = false; | |
+ pClass = CLASS_WARRIOR; | |
+ maxHP = 0; | |
+ maxPower = 0; | |
+ currHP = 0; | |
+ currPower = 0; | |
+ powerType = POWER_MANA; | |
+ spellId = 0; | |
+ castTime = 0; | |
+ team = ALLIANCE; | |
+} | |
+ | |
+bool SpectatorAddonMsg::CanSandAura(uint32 auraID) | |
+{ | |
+ const SpellInfo *spell = sSpellMgr->GetSpellInfo(auraID); | |
+ | |
+ if (!spell) | |
+ return false; | |
+ | |
+ if (spell->SpellIconID == 1) | |
+ return false; | |
+ | |
+ return true; | |
+} | |
+ | |
+void SpectatorAddonMsg::CreateAura(uint32 _caster, uint32 _spellId, bool _isDebuff, uint8 _type, int32 _duration, int32 _expire, uint16 _stack, bool _isRemove) | |
+{ | |
+ if (!CanSandAura(_spellId)) | |
+ return; | |
+ | |
+ aCaster = _caster; | |
+ aSpellId = _spellId; | |
+ aIsDebuff = _isDebuff; | |
+ aType = _type; | |
+ aDuration = _duration; | |
+ aExpire = _expire; | |
+ aStack = _stack; | |
+ aRemove = _isRemove; | |
+ EnableFlag(SPECTATOR_PREFIX_AURA); | |
+} | |
+ | |
+std::string SpectatorAddonMsg::GetMsgData() | |
+{ | |
+ std::string addonData = ""; | |
+ | |
+ if (!isFilledIn(SPECTATOR_PREFIX_PLAYER)) | |
+ { | |
+ TC_LOG_INFO(bg.battleground, "SPECTATOR ADDON: player is not filled in."); | |
+ return addonData; | |
+ } | |
+ | |
+ std::string msg = ""; | |
+ for (uint8 i = 0; i < SPECTATOR_PREFIX_COUNT; ++i) | |
+ if (isFilledIn(i)) | |
+ { | |
+ switch (i) | |
+ { | |
+ case SPECTATOR_PREFIX_PLAYER: | |
+ msg += player + ";"; | |
+ break; | |
+ case SPECTATOR_PREFIX_TARGET: | |
+ msg += "TRG=" + target + ";"; | |
+ break; | |
+ case SPECTATOR_PREFIX_TEAM: | |
+ { | |
+ char buffer[20]; | |
+ sprintf(buffer, "TEM=%i;", (uint16)team); | |
+ msg += buffer; | |
+ break; | |
+ } | |
+ case SPECTATOR_PREFIX_STATUS: | |
+ { | |
+ char buffer[20]; | |
+ sprintf(buffer, "STA=%d;", isAlive); | |
+ msg += buffer; | |
+ break; | |
+ } | |
+ case SPECTATOR_PREFIX_CLASS: | |
+ { | |
+ char buffer[20]; | |
+ sprintf(buffer, "CLA=%i;", (int)pClass); | |
+ msg += buffer; | |
+ break; | |
+ } | |
+ case SPECTATOR_PREFIX_MAXHP: | |
+ { | |
+ char buffer[30]; | |
+ sprintf(buffer, "MHP=%i;", maxHP); | |
+ msg += buffer; | |
+ break; | |
+ } | |
+ case SPECTATOR_PREFIX_CURHP: | |
+ { | |
+ char buffer[30]; | |
+ sprintf(buffer, "CHP=%i;", currHP); | |
+ msg += buffer; | |
+ break; | |
+ } | |
+ case SPECTATOR_PREFIX_MAXPOWER: | |
+ { | |
+ char buffer[30]; | |
+ sprintf(buffer, "MPW=%i;", maxPower); | |
+ msg += buffer; | |
+ break; | |
+ } | |
+ case SPECTATOR_PREFIX_CURPOWER: | |
+ { | |
+ char buffer[30]; | |
+ sprintf(buffer, "CPW=%i;", currPower); | |
+ msg += buffer; | |
+ break; | |
+ } | |
+ case SPECTATOR_PREFIX_POWERTYPE: | |
+ { | |
+ char buffer[20]; | |
+ sprintf(buffer, "PWT=%i;", (uint8)powerType); | |
+ msg += buffer; | |
+ break; | |
+ } | |
+ case SPECTATOR_PREFIX_SPELL: | |
+ { | |
+ char buffer[80]; | |
+ sprintf(buffer, "SPE=%i,%i;", spellId, castTime); | |
+ msg += buffer; | |
+ break; | |
+ } | |
+ case SPECTATOR_PREFIX_AURA: | |
+ { | |
+ char buffer[300]; | |
+ sprintf(buffer, "AUR=%i,%i,%i,%i,%i,%i,%i,0x%X;", aRemove, aStack, | |
+ aExpire, aDuration, | |
+ aSpellId, aType, | |
+ aIsDebuff, aCaster); | |
+ msg += buffer; | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ | |
+ if (msg != "") | |
+ addonData = "ARENASPEC " + msg; | |
+ | |
+ return addonData; | |
+} | |
+ | |
+bool SpectatorAddonMsg::SendPacket(uint32 receiver) | |
+{ | |
+ std::string addonData = GetMsgData(); | |
+ if (addonData == "") | |
+ return false; | |
+ | |
+ Player* rPlayer = ObjectAccessor::FindPlayer(receiver); | |
+ if (!rPlayer) | |
+ return false; | |
+ | |
+ WorldPacket data(SMSG_MESSAGECHAT, 200); | |
+ data << uint8(CHAT_MSG_WHISPER); | |
+ data << uint32(LANG_ADDON); | |
+ data << uint64(0); | |
+ data << uint32(LANG_ADDON); //language 2.1.0 ? | |
+ data << uint64(0); | |
+ data << uint32(addonData.length() + 1); | |
+ data << addonData; | |
+ data << uint8(CHAT_TAG_NONE); | |
+ rPlayer->GetSession()->SendPacket(&data); | |
+ | |
+ return true; | |
+} | |
+ | |
+bool SpectatorAddonMsg::SendPacket(SpectatorAddonMsg msg, uint32 receiver) | |
+{ | |
+ std::string addonData = msg.GetMsgData(); | |
+ if (addonData == "") | |
+ return false; | |
+ | |
+ Player* rPlayer = ObjectAccessor::FindPlayer(receiver); | |
+ if (!rPlayer) | |
+ return false; | |
+ | |
+ WorldPacket data(SMSG_MESSAGECHAT, 200); | |
+ data << uint8(CHAT_MSG_WHISPER); | |
+ data << uint32(LANG_ADDON); | |
+ data << uint64(0); | |
+ data << uint32(LANG_ADDON); //language 2.1.0 ? | |
+ data << uint64(0); | |
+ data << uint32(addonData.length() + 1); | |
+ data << addonData; | |
+ data << uint8(CHAT_TAG_NONE); | |
+ rPlayer->GetSession()->SendPacket(&data); | |
+ | |
+ return true; | |
+} | |
\ No newline at end of file | |
diff --git a/src/server/game/Battlegrounds/SpectatorAddon.h b/src/server/game/Battlegrounds/SpectatorAddon.h | |
new file mode 100644 | |
index 0000000..76e1580 | |
--- /dev/null | |
+++ b/src/server/game/Battlegrounds/SpectatorAddon.h | |
@@ -0,0 +1,77 @@ | |
+#define SPECTATOR_ADDON_SPELL_INTERUPTED 99999 // specific addons | |
+#define SPECTATOR_ADDON_SPELL_CANCELED 99998 // numbers =\ | |
+ | |
+enum SpectatorPrefix { | |
+ SPECTATOR_PREFIX_PLAYER, | |
+ SPECTATOR_PREFIX_STATUS, | |
+ SPECTATOR_PREFIX_MAXHP, | |
+ SPECTATOR_PREFIX_CURHP, | |
+ SPECTATOR_PREFIX_MAXPOWER, | |
+ SPECTATOR_PREFIX_CURPOWER, | |
+ SPECTATOR_PREFIX_POWERTYPE, | |
+ SPECTATOR_PREFIX_TARGET, | |
+ SPECTATOR_PREFIX_CLASS, | |
+ SPECTATOR_PREFIX_TEAM, | |
+ SPECTATOR_PREFIX_SPELL, | |
+ SPECTATOR_PREFIX_AURA, | |
+ SPECTATOR_PREFIX_COUNT // must be at the end of list | |
+}; | |
+ | |
+class SpectatorAddonMsg { | |
+ public: | |
+ SpectatorAddonMsg(); | |
+ | |
+ void SetPlayer(std::string _player) { player = _player; EnableFlag(SPECTATOR_PREFIX_PLAYER); } | |
+ void SetStatus(bool _isAlive) { isAlive = _isAlive; EnableFlag(SPECTATOR_PREFIX_STATUS); } | |
+ void SetClass(uint8 _class) { pClass = _class; EnableFlag(SPECTATOR_PREFIX_CLASS); } | |
+ void SetTarget(std::string _target) { target = _target; EnableFlag(SPECTATOR_PREFIX_TARGET); } | |
+ void SetTeam(uint32 _team) { team = _team; EnableFlag(SPECTATOR_PREFIX_TEAM); } | |
+ | |
+ void SetMaxHP(uint16 hp) { maxHP = hp; EnableFlag(SPECTATOR_PREFIX_MAXHP); } | |
+ void SetCurrentHP(uint16 hp) { currHP = hp; EnableFlag(SPECTATOR_PREFIX_CURHP); } | |
+ void SetMaxPower(uint16 power) { maxPower = power; EnableFlag(SPECTATOR_PREFIX_MAXPOWER); } | |
+ void SetCurrentPower(uint16 power) { currPower = power; EnableFlag(SPECTATOR_PREFIX_CURPOWER); } | |
+ void SetPowerType(Powers power) { powerType = power; EnableFlag(SPECTATOR_PREFIX_POWERTYPE); } | |
+ | |
+ void CastSpell(uint32 _spellId, uint32 _castTime) { spellId = _spellId; castTime = _castTime; EnableFlag(SPECTATOR_PREFIX_SPELL); } | |
+ void CreateAura(uint32 _caster, uint32 _spellId, bool _isDebuff, uint8 _type, int32 _duration, int32 _expire, uint16 _stack, bool _isRemove); | |
+ | |
+ static bool SendPacket(SpectatorAddonMsg msg, uint32 receiver); | |
+ bool SendPacket(uint32 receiver); | |
+ | |
+ std::string GetMsgData(); | |
+ | |
+ bool isFilledIn(uint8 prefix) { return prefixFlags[prefix]; } | |
+ | |
+ static bool CanSandAura(uint32 auraID); | |
+ private: | |
+ | |
+ void EnableFlag(uint8 prefix) { prefixFlags[prefix] = true; } | |
+ std::string player; | |
+ bool isAlive; | |
+ std::string target; | |
+ uint8 pClass; | |
+ | |
+ uint16 maxHP; | |
+ uint16 maxPower; | |
+ uint16 currHP; | |
+ uint16 currPower; | |
+ Powers powerType; | |
+ | |
+ uint32 spellId; | |
+ uint32 castTime; | |
+ | |
+ uint32 team; | |
+ | |
+ // aura data | |
+ uint32 aCaster; | |
+ uint32 aSpellId; | |
+ bool aIsDebuff; | |
+ uint8 aType; | |
+ int32 aDuration; | |
+ int32 aExpire; | |
+ uint16 aStack; | |
+ bool aRemove; | |
+ | |
+ bool prefixFlags[SPECTATOR_PREFIX_COUNT]; | |
+}; | |
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp | |
index 15e7eb4..a28e862 100644 | |
--- a/src/server/game/Entities/GameObject/GameObject.cpp | |
+++ b/src/server/game/Entities/GameObject/GameObject.cpp | |
@@ -472,6 +472,9 @@ void GameObject::Update(uint32 diff) | |
if (ok) | |
{ | |
+ if (Player *tmpPlayer = ok->ToPlayer()) | |
+ if (tmpPlayer->IsSpectator()) | |
+ return; | |
// some traps do not have spell but should be triggered | |
if (goInfo->trap.spellId) | |
CastSpell(ok, goInfo->trap.spellId); | |
@@ -1669,6 +1672,10 @@ void GameObject::Use(Unit* user) | |
void GameObject::CastSpell(Unit* target, uint32 spellId) | |
{ | |
+if (target) | |
+ if (Player *tmpPlayer = target->ToPlayer()) | |
+ if (tmpPlayer->IsSpectator()) | |
+ return; | |
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); | |
if (!spellInfo) | |
return; | |
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp | |
index b1ee724..c6dd28d 100644 | |
--- a/src/server/game/Entities/Player/Player.cpp | |
+++ b/src/server/game/Entities/Player/Player.cpp | |
@@ -1885,6 +1885,20 @@ void Player::setDeathState(DeathState s) | |
SetUInt32Value(PLAYER_SELF_RES_SPELL, 0); | |
} | |
+void Player::SetSelection(uint64 guid) | |
+{ | |
+ m_curSelection = guid; | |
+ SetUInt64Value(UNIT_FIELD_TARGET, guid); | |
+ if (Player *target = ObjectAccessor::FindPlayer(guid)) | |
+ if (HaveSpectators()) | |
+ { | |
+ SpectatorAddonMsg msg; | |
+ msg.SetPlayer(GetName()); | |
+ msg.SetTarget(target->GetName()); | |
+ SendSpectatorAddonMsgToBG(msg); | |
+ } | |
+} | |
+ | |
void Player::InnEnter(time_t time, uint32 mapid, float x, float y, float z) | |
{ | |
inn_pos_mapid = mapid; | |
@@ -2342,7 +2356,17 @@ bool Player::TeleportToBGEntryPoint() | |
ScheduleDelayedOperation(DELAYED_BG_MOUNT_RESTORE); | |
ScheduleDelayedOperation(DELAYED_BG_TAXI_RESTORE); | |
ScheduleDelayedOperation(DELAYED_BG_GROUP_RESTORE); | |
- return TeleportTo(m_bgData.joinPos); | |
+ Battleground *oldBg = GetBattleground(); | |
+ bool result = TeleportTo(m_bgData.joinPos); | |
+ | |
+ if (isSpectator() && result) | |
+ { | |
+ SetSpectate(false); | |
+ if (oldBg) | |
+ oldBg->RemoveSpectator(GetGUID()); | |
+ } | |
+ | |
+ return result; | |
} | |
void Player::ProcessDelayedOperations() | |
@@ -2806,6 +2830,98 @@ void Player::SetInWater(bool apply) | |
getHostileRefManager().updateThreatTables(); | |
} | |
+void Player::SetSpectate(bool on) | |
+{ | |
+ if (on) | |
+ { | |
+ SetSpeed(MOVE_RUN, 3.0); | |
+ spectatorFlag = true; | |
+ | |
+ m_ExtraFlags |= PLAYER_EXTRA_GM_ON; | |
+ setFaction(35); | |
+ SetGMVisible(false); | |
+ SetGameMaster(true); | |
+ | |
+ if (Pet* pet = GetPet()) | |
+ RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true); | |
+ | |
+ UnsummonPetTemporaryIfAny(); | |
+ | |
+ RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); | |
+ ResetContestedPvP(); | |
+ | |
+ getHostileRefManager().setOnlineOfflineState(false); | |
+ CombatStopWithPets(); | |
+ /* comment here to remove morphs */ | |
+ uint32 morphs[8] = {25900, 18718, 29348, 22235, 30414, 736, 20582, 28213}; | |
+ SetDisplayId(morphs[urand(0, 7)]); | |
+ /* comment here to remove morphs */ | |
+ SetDisplayId(20582); | |
+ | |
+ m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_ADMINISTRATOR); | |
+ } | |
+ else | |
+ { | |
+ uint32 newPhase = 0; | |
+ AuraEffectList const& phases = GetAuraEffectsByType(SPELL_AURA_PHASE); | |
+ if (!phases.empty()) | |
+ for (AuraEffectList::const_iterator itr = phases.begin(); itr != phases.end(); ++itr) | |
+ newPhase |= (*itr)->GetMiscValue(); | |
+ | |
+ if (!newPhase) | |
+ newPhase = PHASEMASK_NORMAL; | |
+ | |
+ SetPhaseMask(newPhase, false); | |
+ | |
+ m_ExtraFlags &= ~ PLAYER_EXTRA_GM_ON; | |
+ setFactionForRace(getRace()); | |
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM); | |
+ RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_CHEAT_SPELLS); | |
+ SetGMVisible(true); | |
+ SetGameMaster(false); | |
+ | |
+ if (spectateFrom) | |
+ SetViewpoint(spectateFrom, false); | |
+ | |
+ // restore FFA PvP Server state | |
+ if (sWorld->IsFFAPvPRealm()) | |
+ SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); | |
+ | |
+ // restore FFA PvP area state, remove not allowed for GM mounts | |
+ UpdateArea(m_areaUpdateId); | |
+ getHostileRefManager().setOnlineOfflineState(true); | |
+ m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER); | |
+ spectateCanceled = false; | |
+ spectatorFlag = false; | |
+ RestoreDisplayId(); | |
+ UpdateSpeed(MOVE_RUN, true); | |
+ } | |
+ UpdateObjectVisibility(); | |
+} | |
+ | |
+bool Player::HaveSpectators() | |
+{ | |
+ if (IsSpectator()) | |
+ return false; | |
+ | |
+ if (Battleground *bg = GetBattleground()) | |
+ if (bg->isArena()) | |
+ { | |
+ if (bg->GetStatus() != STATUS_IN_PROGRESS) | |
+ return false; | |
+ | |
+ return bg->HaveSpectators(); | |
+ } | |
+ return false; | |
+} | |
+ | |
+void Player::SendSpectatorAddonMsgToBG(SpectatorAddonMsg msg) | |
+{ | |
+ if (!HaveSpectators()) | |
+ return; | |
+ | |
+ GetBattleground()->SendSpectateAddonsMsg(msg); | |
+} | |
void Player::SetGameMaster(bool on) | |
{ | |
@@ -18373,6 +18489,10 @@ void Player::_LoadSeasonalQuestStatus(PreparedQueryResult result) | |
} | |
m_SeasonalQuestChanged = false; | |
+ | |
+ spectatorFlag = false; | |
+ spectateCanceled = false; | |
+ spectateFrom = NULL; | |
} | |
void Player::_LoadMonthlyQuestStatus(PreparedQueryResult result) | |
@@ -22149,7 +22269,9 @@ bool Player::IsVisibleGloballyFor(Player const* u) const | |
// non faction visibility non-breakable for non-GMs | |
if (!IsVisible()) | |
return false; | |
- | |
+ | |
+ if (IsSpectator()) | |
+ return false; | |
// non-gm stealth/invisibility not hide from global player lists | |
return true; | |
} | |
@@ -22930,6 +23052,32 @@ void Player::SendAurasForTarget(Unit* target) | |
auraApp->BuildUpdatePacket(data, false); | |
} | |
+ if (Player *stream = target->ToPlayer()) | |
+ if (stream->HaveSpectators() && IsSpectator()) | |
+ { | |
+ for (Unit::VisibleAuraMap::const_iterator itr = visibleAuras->begin(); itr != visibleAuras->end(); ++itr) | |
+ { | |
+ AuraApplication * auraApp = itr->second; | |
+ auraApp->BuildUpdatePacket(data, false); | |
+ if (Aura* aura = auraApp->GetBase()) | |
+ { | |
+ SpectatorAddonMsg msg; | |
+ uint64 casterID = 0; | |
+ if (aura->GetCaster()) | |
+ casterID = (aura->GetCaster()->ToPlayer()) ? aura->GetCaster()->GetGUID() : 0; | |
+ msg.SetPlayer(stream->GetName()); | |
+ msg.CreateAura(casterID, aura->GetSpellInfo()->Id, | |
+ aura->GetSpellInfo()->IsPositive(), aura->GetSpellInfo()->Dispel, | |
+ aura->GetDuration(), aura->GetMaxDuration(), | |
+ aura->GetStackAmount(), false); | |
+ msg.SendPacket(GetGUID()); | |
+ } | |
+ | |
+ } | |
+ | |
+ } | |
+ | |
+ | |
GetSession()->SendPacket(&data); | |
} | |
@@ -24057,6 +24205,15 @@ void Player::SetViewpoint(WorldObject* target, bool apply) | |
{ | |
if (apply) | |
{ | |
+ if (target->ToPlayer() == this) | |
+ return; | |
+ | |
+ //remove Viewpoint if already have | |
+ if (isSpectator() && spectateFrom) | |
+ { | |
+ SetViewpoint(spectateFrom, false); | |
+ spectateFrom = NULL; | |
+ } | |
TC_LOG_DEBUG(LOG_FILTER_MAPS, "Player::CreateViewpoint: Player %s create seer %u (TypeId: %u).", GetName().c_str(), target->GetEntry(), target->GetTypeId()); | |
if (!AddUInt64Value(PLAYER_FARSIGHT, target->GetGUID())) | |
@@ -24069,10 +24226,17 @@ void Player::SetViewpoint(WorldObject* target, bool apply) | |
UpdateVisibilityOf(target); | |
if (target->isType(TYPEMASK_UNIT) && !GetVehicle()) | |
- ((Unit*)target)->AddPlayerToVision(this); | |
+ { | |
+ if (IsSpectator()) | |
+ spectateFrom = (Unit*)target; | |
+ | |
+ ((Unit*)target)->AddPlayerToVision(this); | |
+ } | |
} | |
else | |
{ | |
+ if (IsSpectator() && !spectateFrom) | |
+ return; | |
TC_LOG_DEBUG(LOG_FILTER_MAPS, "Player::CreateViewpoint: Player %s remove seer", GetName().c_str()); | |
if (!RemoveUInt64Value(PLAYER_FARSIGHT, target->GetGUID())) | |
@@ -24084,6 +24248,9 @@ void Player::SetViewpoint(WorldObject* target, bool apply) | |
if (target->isType(TYPEMASK_UNIT) && !GetVehicle()) | |
((Unit*)target)->RemovePlayerFromVision(this); | |
+ if (IsSpectator()) | |
+ spectateFrom = NULL; | |
+ | |
//must immediately set seer back otherwise may crash | |
m_seer = this; | |
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h | |
index bac7944..d43e83d 100644 | |
--- a/src/server/game/Entities/Player/Player.h | |
+++ b/src/server/game/Entities/Player/Player.h | |
@@ -28,6 +28,7 @@ | |
#include "QuestDef.h" | |
#include "SpellMgr.h" | |
#include "Unit.h" | |
+#include "Battleground.h" | |
#include <string> | |
#include <vector> | |
@@ -1138,7 +1139,14 @@ class Player : public Unit, public GridObject<Player> | |
bool Has310Flyer(bool checkAllSpells, uint32 excludeSpellId = 0); | |
void SetHas310Flyer(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_HAS_310_FLYER; else m_ExtraFlags &= ~PLAYER_EXTRA_HAS_310_FLYER; } | |
void SetPvPDeath(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_PVP_DEATH; else m_ExtraFlags &= ~PLAYER_EXTRA_PVP_DEATH; } | |
- | |
+ bool HaveSpectators(); | |
+ void SendSpectatorAddonMsgToBG(SpectatorAddonMsg msg); | |
+ bool isSpectateCanceled() { return spectateCanceled; } | |
+ void CancelSpectate() { spectateCanceled = true; } | |
+ Unit* getSpectateFrom() { return spectateFrom; } | |
+ bool isSpectator() const { return spectatorFlag; } | |
+ void SetSpectate(bool on); | |
+ | |
void GiveXP(uint32 xp, Unit* victim, float group_rate=1.0f); | |
void GiveLevel(uint8 level); | |
@@ -1509,7 +1517,7 @@ class Player : public Unit, public GridObject<Player> | |
uint64 GetSelection() const { return m_curSelection; } | |
Unit* GetSelectedUnit() const; | |
Player* GetSelectedPlayer() const; | |
- void SetSelection(uint64 guid) { m_curSelection = guid; SetUInt64Value(UNIT_FIELD_TARGET, guid); } | |
+ void SetSelection(uint64 guid); | |
uint8 GetComboPoints() const { return m_comboPoints; } | |
uint64 GetComboTarget() const { return m_comboTarget; } | |
@@ -2620,6 +2628,9 @@ class Player : public Unit, public GridObject<Player> | |
InstanceTimeMap _instanceResetTimes; | |
uint32 _pendingBindId; | |
uint32 _pendingBindTimer; | |
+ bool spectatorFlag; | |
+ bool spectateCanceled; | |
+ Unit *spectateFrom; | |
uint32 _activeCheats; | |
}; | |
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp | |
index ef684fc..5fe0827 100644 | |
--- a/src/server/game/Entities/Unit/Unit.cpp | |
+++ b/src/server/game/Entities/Unit/Unit.cpp | |
@@ -293,7 +293,19 @@ Unit::~Unit() | |
m_currentSpells[i]->SetReferencedFromCurrent(false); | |
m_currentSpells[i] = NULL; | |
} | |
- | |
+ | |
+ // remove veiw point for spectator | |
+ if (!m_sharedVision.empty()) | |
+ { | |
+ for (SharedVisionList::iterator itr = m_sharedVision.begin(); itr != m_sharedVision.end(); ++itr) | |
+ if ((*itr)->isSpectator() && (*itr)->getSpectateFrom()) | |
+ { | |
+ (*itr)->SetViewpoint((*itr)->getSpectateFrom(), false); | |
+ if (m_sharedVision.empty()) | |
+ break; | |
+ --itr; | |
+ } | |
+ } | |
_DeleteRemovedAuras(); | |
delete m_charmInfo; | |
@@ -2888,6 +2900,7 @@ void Unit::_UpdateSpells(uint32 time) | |
m_currentSpells[i]->SetReferencedFromCurrent(false); | |
m_currentSpells[i] = NULL; // remove pointer | |
} | |
+ | |
} | |
// m_auraUpdateIterator can be updated in indirect called code at aura remove to skip next planned to update but removed auras | |
@@ -13296,6 +13309,13 @@ void Unit::SetHealth(uint32 val) | |
// group update | |
if (Player* player = ToPlayer()) | |
{ | |
+ if (player->HaveSpectators()) | |
+ { | |
+ SpectatorAddonMsg msg; | |
+ msg.SetPlayer(player->GetName()); | |
+ msg.SetCurrentHP(val); | |
+ player->SendSpectatorAddonMsgToBG(msg); | |
+ } | |
if (player->GetGroup()) | |
player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_HP); | |
} | |
@@ -13321,6 +13341,13 @@ void Unit::SetMaxHealth(uint32 val) | |
// group update | |
if (GetTypeId() == TYPEID_PLAYER) | |
{ | |
+ if (ToPlayer()->HaveSpectators()) | |
+ { | |
+ SpectatorAddonMsg msg; | |
+ msg.SetPlayer(ToPlayer()->GetName()); | |
+ msg.SetMaxHP(val); | |
+ ToPlayer()->SendSpectatorAddonMsgToBG(msg); | |
+ } | |
if (ToPlayer()->GetGroup()) | |
ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_HP); | |
} | |
@@ -13358,6 +13385,14 @@ void Unit::SetPower(Powers power, uint32 val) | |
// group update | |
if (Player* player = ToPlayer()) | |
{ | |
+ if (player->HaveSpectators()) | |
+ { | |
+ SpectatorAddonMsg msg; | |
+ msg.SetPlayer(player->GetName()); | |
+ msg.SetCurrentPower(val); | |
+ msg.SetPowerType(power); | |
+ player->SendSpectatorAddonMsgToBG(msg); | |
+ } | |
if (player->GetGroup()) | |
player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_POWER); | |
} | |
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp | |
index 04c2c22..f472848 100644 | |
--- a/src/server/game/Maps/Map.cpp | |
+++ b/src/server/game/Maps/Map.cpp | |
@@ -2744,6 +2744,12 @@ bool BattlegroundMap::AddPlayerToMap(Player* player) | |
void BattlegroundMap::RemovePlayerFromMap(Player* player, bool remove) | |
{ | |
+ if (player && player->IsSpectator() && !player->isSpectateCanceled()) | |
+ { | |
+ if (GetBG()) | |
+ GetBG()->RemoveSpectator(player->GetGUID()); | |
+ player->SetSpectate(false); | |
+ } | |
TC_LOG_INFO("maps", "MAP: Removing player '%s' from bg '%u' of map '%s' before relocating to another map", player->GetName().c_str(), GetInstanceId(), GetMapName()); | |
Map::RemovePlayerFromMap(player, remove); | |
} | |
diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp | |
index 3456041..ddc2b51 100644 | |
--- a/src/server/game/Scripting/ScriptLoader.cpp | |
+++ b/src/server/game/Scripting/ScriptLoader.cpp | |
@@ -24,6 +24,9 @@ void AddSC_example_gossip_codebox(); | |
void AddSC_example_misc(); | |
void AddSC_example_commandscript(); | |
+//Custom | |
+void AddSC_arena_spectator_script(); | |
+ | |
// spells | |
void AddSC_deathknight_spell_scripts(); | |
void AddSC_druid_spell_scripts(); | |
@@ -1396,5 +1399,9 @@ void AddCustomScripts() | |
#ifdef SCRIPTS | |
/* This is where custom scripts should be added. */ | |
+//custom | |
+AddSC_arena_spectator_script(); | |
+ | |
+ | |
#endif | |
} | |
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp | |
index 866cd88..71eb932 100644 | |
--- a/src/server/game/Spells/Spell.cpp | |
+++ b/src/server/game/Spells/Spell.cpp | |
@@ -3068,7 +3068,15 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered | |
ReSetTimer(); | |
TC_LOG_DEBUG("spells", "Spell::prepare: spell id %u source %u caster %d customCastFlags %u mask %u", m_spellInfo->Id, m_caster->GetEntry(), m_originalCaster ? m_originalCaster->GetEntry() : -1, _triggeredCastFlags, m_targets.GetTargetMask()); | |
- | |
+ if (GetCaster() && GetSpellInfo()) | |
+ if (Player *tmpPlayer = GetCaster()->ToPlayer()) | |
+ if (tmpPlayer->HaveSpectators()) | |
+ { | |
+ SpectatorAddonMsg msg; | |
+ msg.SetPlayer(tmpPlayer->GetName()); | |
+ msg.CastSpell(GetSpellInfo()->Id, GetSpellInfo()->CastTimeEntry->CastTime); | |
+ tmpPlayer->SendSpectatorAddonMsgToBG(msg); | |
+ } | |
//Containers for channeled spells have to be set | |
/// @todoApply this to all casted spells if needed | |
// Why check duration? 29350: channelled triggers channelled | |
@@ -4773,6 +4781,10 @@ SpellCastResult Spell::CheckCast(bool strict) | |
m_caster->GetMap()->IsOutdoors(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ())) | |
return SPELL_FAILED_ONLY_INDOORS; | |
} | |
+ | |
+ if (Player *tmpPlayer = m_caster->ToPlayer()) | |
+ if (tmpPlayer->IsSpectator()) | |
+ return SPELL_FAILED_SPELL_UNAVAILABLE; | |
// only check at first call, Stealth auras are already removed at second call | |
// for now, ignore triggered spells | |
diff --git a/src/server/scripts/Custom/ArenaSpectator.cpp b/src/server/scripts/Custom/ArenaSpectator.cpp | |
new file mode 100644 | |
index 0000000..b449406 | |
--- /dev/null | |
+++ b/src/server/scripts/Custom/ArenaSpectator.cpp | |
@@ -0,0 +1,479 @@ | |
+#include "ScriptPCH.h" | |
+#include "Chat.h" | |
+#include "ArenaTeamMgr.h" | |
+#include "BattlegroundMgr.h" | |
+#include "WorldSession.h" | |
+#include "Player.h" | |
+#include "ArenaTeam.h" | |
+#include "Battleground.h" | |
+#include "BattlegroundMgr.h" | |
+#include "CreatureTextMgr.h" | |
+#include "Config. | |
+ | |
+class arena_spectator_commands : public CommandScript | |
+{ | |
+ public: | |
+ arena_spectator_commands() : CommandScript("arena_spectator_commands") { } | |
+ | |
+ static bool HandleSpectateCommand(ChatHandler* handler, char const* args) | |
+ { | |
+ Player* target; | |
+ uint64 target_guid; | |
+ std::string target_name; | |
+ if (!handler->extractPlayerTarget((char*)args, &target, &target_guid, &target_name)) | |
+ return false; | |
+ | |
+ Player* player = handler->GetSession()->GetPlayer(); | |
+ if (target == player || target_guid == player->GetGUID()) | |
+ { | |
+ handler->PSendSysMessage("Can´t Spectate self."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ if (player->IsInCombat()) | |
+ { | |
+ handler->PSendSysMessage("You are in Combat."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ if (!target) | |
+ { | |
+ handler->PSendSysMessage("Target Is not Exist."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ if (player->IsMounted()) | |
+ { | |
+ handler->PSendSysMessage("Cannot Spectate while mounted."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ if (target && (target->HasAura(32728) || target->HasAura(32727))) // Check Arena Preparation thx XXsupr | |
+ { | |
+ handler->PSendSysMessage("Cant do that. Arena didn`t started."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ if (player->GetPet()) | |
+ { | |
+ handler->PSendSysMessage("You must hide your pet."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ if (player->GetMap()->IsBattlegroundOrArena() && !player->IsSpectator()) | |
+ { | |
+ handler->PSendSysMessage("You are already on battleground or arena."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ Map* cMap = target->GetMap(); | |
+ if (!cMap->IsBattleArena()) | |
+ { | |
+ handler->PSendSysMessage("Player didnt found in arena."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ if (player->GetMap()->IsBattleground()) | |
+ { | |
+ handler->PSendSysMessage("Cant do that while you are on battleground."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ // all's well, set bg id | |
+ // when porting out from the bg, it will be reset to 0 | |
+ player->SetBattlegroundId(target->GetBattlegroundId(), target->GetBattlegroundTypeId()); | |
+ // remember current position as entry point for return at bg end teleportation | |
+ if (!player->GetMap()->IsBattlegroundOrArena()) | |
+ player->SetBattlegroundEntryPoint(); | |
+ | |
+ if (target->IsSpectator()) | |
+ { | |
+ handler->PSendSysMessage("Can`t do that. Your target is spectator."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ // stop flight if need | |
+ if (player->IsInFlight()) | |
+ { | |
+ player->GetMotionMaster()->MovementExpired(); | |
+ player->CleanupAfterTaxiFlight(); | |
+ } | |
+ // save only in non-flight case | |
+ else | |
+ player->SaveRecallPosition(); | |
+ | |
+ // search for two teams | |
+ Battleground *bGround = target->GetBattleground(); | |
+ if (bGround->isRated()) | |
+ { | |
+ uint32 slot = bGround->GetArenaType() - 2; | |
+ if (bGround->GetArenaType() > 3) | |
+ slot = 2; | |
+ uint32 firstTeamID = target->GetArenaTeamId(slot); | |
+ uint32 secondTeamID = 0; | |
+ Player *firstTeamMember = target; | |
+ Player *secondTeamMember = NULL; | |
+ for (Battleground::BattlegroundPlayerMap::const_iterator itr = bGround->GetPlayers().begin(); itr != bGround->GetPlayers().end(); ++itr) | |
+ if (Player* tmpPlayer = ObjectAccessor::FindPlayer(itr->first)) | |
+ { | |
+ if (tmpPlayer->IsSpectator()) | |
+ continue; | |
+ | |
+ uint32 tmpID = tmpPlayer->GetArenaTeamId(slot); | |
+ if (tmpID != firstTeamID && tmpID > 0) | |
+ { | |
+ secondTeamID = tmpID; | |
+ secondTeamMember = tmpPlayer; | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if (firstTeamID > 0 && secondTeamID > 0 && secondTeamMember) | |
+ { | |
+ ArenaTeam *firstTeam = sArenaTeamMgr->GetArenaTeamById(firstTeamID); | |
+ ArenaTeam *secondTeam = sArenaTeamMgr->GetArenaTeamById(secondTeamID); | |
+ if (firstTeam && secondTeam) | |
+ { | |
+ handler->PSendSysMessage("You entered to rated arena."); | |
+ handler->PSendSysMessage("Teams:"); | |
+ handler->PSendSysMessage("%s - %s", firstTeam->GetName().c_str(), secondTeam->GetName().c_str()); | |
+ handler->PSendSysMessage("%u(%u) - %u(%u)", firstTeam->GetRating(), firstTeam->GetAverageMMR(firstTeamMember->GetGroup()), | |
+ secondTeam->GetRating(), secondTeam->GetAverageMMR(secondTeamMember->GetGroup())); | |
+ } | |
+ } | |
+ } | |
+ | |
+ // to point to see at target with same orientation | |
+ float x, y, z; | |
+ target->GetContactPoint(player, x, y, z); | |
+ | |
+ player->TeleportTo(target->GetMapId(), x, y, z, player->GetAngle(target), TELE_TO_GM_MODE); | |
+ player->SetPhaseMask(target->GetPhaseMask(), true); | |
+ player->SetSpectate(true); | |
+ target->GetBattleground()->AddSpectator(player->GetGUID()); | |
+ | |
+ return true; | |
+ } | |
+ static bool HandleSpectateCancelCommand(ChatHandler* handler, const char* /*args*/) | |
+ { | |
+ Player* player = handler->GetSession()->GetPlayer(); | |
+ | |
+ if (!player->IsSpectator()) | |
+ { | |
+ handler->PSendSysMessage("You are not spectator."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ player->GetBattleground()->RemoveSpectator(player->GetGUID()); | |
+ player->CancelSpectate(); | |
+ player->TeleportToBGEntryPoint(); | |
+ | |
+ return true; | |
+ } | |
+ | |
+ static bool HandleSpectateFromCommand(ChatHandler* handler, const char *args) | |
+ { | |
+ Player* target; | |
+ uint64 target_guid; | |
+ std::string target_name; | |
+ if (!handler->extractPlayerTarget((char*)args, &target, &target_guid, &target_name)) | |
+ return false; | |
+ | |
+ Player* player = handler->GetSession()->GetPlayer(); | |
+ | |
+ if (!target) | |
+ { | |
+ handler->PSendSysMessage("Cant find player."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ if (!player->IsSpectator()) | |
+ { | |
+ handler->PSendSysMessage("You are not spectator, spectate someone first."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ if (target->IsSpectator() && target != player) | |
+ { | |
+ handler->PSendSysMessage("Can`t do that. Your target is spectator."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ if (player->GetMap() != target->GetMap()) | |
+ { | |
+ handler->PSendSysMessage("Cant do that. Different arenas?"); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ // check for arena preperation | |
+ // if exists than battle didn`t begin | |
+ if (target->HasAura(32728) || target->HasAura(32727)) | |
+ { | |
+ handler->PSendSysMessage("Cant do that. Arena didn`t started."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ (target == player && player->getSpectateFrom()) ? player->SetViewpoint(player->getSpectateFrom(), false) : | |
+ player->SetViewpoint(target, true); | |
+ return true; | |
+ } | |
+ | |
+ static bool HandleSpectateResetCommand(ChatHandler* handler, const char *args) | |
+ { | |
+ Player* player = handler->GetSession()->GetPlayer(); | |
+ | |
+ if (!player) | |
+ { | |
+ handler->PSendSysMessage("Cant find player."); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ if (!player->IsSpectator()) | |
+ { | |
+ handler->PSendSysMessage("You are not spectator!"); | |
+ handler->SetSentErrorMessage(true); | |
+ return false; | |
+ } | |
+ | |
+ Battleground *bGround = player->GetBattleground(); | |
+ if (!bGround) | |
+ return false; | |
+ | |
+ if (bGround->GetStatus() != STATUS_IN_PROGRESS) | |
+ return true; | |
+ | |
+ for (Battleground::BattlegroundPlayerMap::const_iterator itr = bGround->GetPlayers().begin(); itr != bGround->GetPlayers().end(); ++itr) | |
+ if (Player* tmpPlayer = ObjectAccessor::FindPlayer(itr->first)) | |
+ { | |
+ if (tmpPlayer->IsSpectator()) | |
+ continue; | |
+ | |
+ uint32 tmpID = bGround->GetPlayerTeam(tmpPlayer->GetGUID()); | |
+ | |
+ // generate addon massage | |
+ std::string pName = tmpPlayer->GetName(); | |
+ std::string tName = ""; | |
+ | |
+ if (Player *target = tmpPlayer->GetSelectedPlayer()) | |
+ tName = target->GetName(); | |
+ | |
+ SpectatorAddonMsg msg; | |
+ msg.SetPlayer(pName); | |
+ if (tName != "") | |
+ msg.SetTarget(tName); | |
+ msg.SetStatus(tmpPlayer->IsAlive()); | |
+ msg.SetClass(tmpPlayer->getClass()); | |
+ msg.SetCurrentHP(tmpPlayer->GetHealth()); | |
+ msg.SetMaxHP(tmpPlayer->GetMaxHealth()); | |
+ Powers powerType = tmpPlayer->getPowerType(); | |
+ msg.SetMaxPower(tmpPlayer->GetMaxPower(powerType)); | |
+ msg.SetCurrentPower(tmpPlayer->GetPower(powerType)); | |
+ msg.SetPowerType(powerType); | |
+ msg.SetTeam(tmpID); | |
+ msg.SendPacket(player->GetGUID()); | |
+ } | |
+ | |
+ return true; | |
+ } | |
+ | |
+ ChatCommand* GetCommands() const | |
+ { | |
+ static ChatCommand spectateCommandTable[] = | |
+ { | |
+ { "player", SEC_PLAYER, true, &HandleSpectateCommand, "", NULL }, | |
+ { "view", SEC_PLAYER, true, &HandleSpectateFromCommand, "", NULL }, | |
+ { "reset", SEC_PLAYER, true, &HandleSpectateResetCommand, "", NULL }, | |
+ { "leave", SEC_PLAYER, true, &HandleSpectateCancelCommand, "", NULL }, | |
+ { NULL, 0, false, NULL, "", NULL } | |
+ }; | |
+ | |
+ static ChatCommand commandTable[] = | |
+ { | |
+ { "spectate", SEC_PLAYER, false, NULL, "", spectateCommandTable }, | |
+ { NULL, 0, false, NULL, "", NULL } | |
+ }; | |
+ return commandTable; | |
+ } | |
+}; | |
+ | |
+ | |
+enum NpcSpectatorAtions { | |
+ // will be used for scrolling | |
+ NPC_SPECTATOR_ACTION_2V2_GAMES = 1000, | |
+ NPC_SPECTATOR_ACTION_3V3_GAMES = 2000, | |
+ NPC_SPECTATOR_ACTION_5V5_GAMES = 3000, | |
+ // NPC_SPECTATOR_ACTION_SELECTED_PLAYER + player.Guid() | |
+ NPC_SPECTATOR_ACTION_SELECTED_PLAYER = 3000 | |
+}; | |
+ | |
+const uint16 TopGamesRating = 1800; | |
+const uint8 GamesOnPage = 10; | |
+ | |
+class npc_arena_spectator : public CreatureScript | |
+{ | |
+ public: | |
+ npc_arena_spectator() : CreatureScript("npc_arena_spectator") { } | |
+ | |
+ bool OnGossipHello(Player* pPlayer, Creature* pCreature) | |
+ { | |
+ pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Spectate Rated: 2v2 Games", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_2V2_GAMES); | |
+ pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Spectate Rated: 3v3 Games", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_3V3_GAMES); | |
+ pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Spectate Rated: 5v5 Games", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_5V5_GAMES); | |
+ pPlayer->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, pCreature->GetGUID()); | |
+ return true; | |
+ } | |
+ | |
+ bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) | |
+ { | |
+ player->PlayerTalkClass->ClearMenus(); | |
+ if (action >= NPC_SPECTATOR_ACTION_LIST_GAMES && action < NPC_SPECTATOR_ACTION_LIST_TOP_GAMES) | |
+ { | |
+ ShowPage(player, action - NPC_SPECTATOR_ACTION_LIST_GAMES, false); | |
+ player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID()); | |
+ } | |
+ else if (action >= NPC_SPECTATOR_ACTION_LIST_TOP_GAMES && action < NPC_SPECTATOR_ACTION_LIST_TOP_GAMES) | |
+ { | |
+ ShowPage(player, action - NPC_SPECTATOR_ACTION_LIST_TOP_GAMES, true); | |
+ player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID()); | |
+ } | |
+ else | |
+ { | |
+ | |
+ uint64 guid = action - NPC_SPECTATOR_ACTION_SELECTED_PLAYER; | |
+ if (Player* target = ObjectAccessor::FindPlayer(guid)) | |
+ { | |
+ ChatHandler handler(player->GetSession()); | |
+ std::string str = target->GetName(); | |
+ char* pTarget; | |
+ std::strcpy (pTarget, str.c_str()); | |
+ arena_spectator_commands::HandleSpectateCommand(&handler, pTarget); | |
+ } | |
+ } | |
+ return true; | |
+ } | |
+ | |
+ std::string GetClassNameById(uint8 id) | |
+ { | |
+ std::string sClass = ""; | |
+ switch (id) | |
+ { | |
+ case CLASS_WARRIOR: sClass = "Warr "; break; | |
+ case CLASS_PALADIN: sClass = "Pala "; break; | |
+ case CLASS_HUNTER: sClass = "Hunt "; break; | |
+ case CLASS_ROGUE: sClass = "Rogue "; break; | |
+ case CLASS_PRIEST: sClass = "Priest "; break; | |
+ case CLASS_DEATH_KNIGHT: sClass = "D.K. "; break; | |
+ case CLASS_SHAMAN: sClass = "Shaman "; break; | |
+ case CLASS_MAGE: sClass = "Mage "; break; | |
+ case CLASS_WARLOCK: sClass = "Warlock "; break; | |
+ case CLASS_DRUID: sClass = "Druid "; break; | |
+ } | |
+ return sClass; | |
+ } | |
+ | |
+ std::string GetGamesStringData(Battleground* team, uint16 mmr) | |
+ { | |
+ std::string teamsMember[BG_TEAMS_COUNT]; | |
+ uint32 firstTeamId = 0; | |
+ for (Battleground::BattlegroundPlayerMap::const_iterator itr = team->GetPlayers().begin(); itr != team->GetPlayers().end(); ++itr) | |
+ if (Player* player = ObjectAccessor::FindPlayer(itr->first)) | |
+ { | |
+ if (player->IsSpectator()) | |
+ continue; | |
+ | |
+ uint32 team = itr->second.Team; | |
+ if (!firstTeamId) | |
+ firstTeamId = team; | |
+ | |
+ teamsMember[firstTeamId == team] += GetClassNameById(player->getClass()); | |
+ } | |
+ | |
+ std::string data = teamsMember[0] + "("; | |
+ std::stringstream ss; | |
+ ss << mmr; | |
+ data += ss.str(); | |
+ data += ") - " + teamsMember[1]; | |
+ return data; | |
+ } | |
+ | |
+ uint64 GetFirstPlayerGuid(Battleground* team) | |
+ { | |
+ for (Battleground::BattlegroundPlayerMap::const_iterator itr = team->GetPlayers().begin(); itr != team->GetPlayers().end(); ++itr) | |
+ if (Player* player = ObjectAccessor::FindPlayer(itr->first)) | |
+ return itr->first; | |
+ return 0; | |
+ } | |
+ | |
+ void ShowPage(Player* player, uint16 page, bool isTop) | |
+ { | |
+ uint16 highGames = 0; | |
+ uint16 lowGames = 0; | |
+ bool haveNextPage = false; | |
+ for (uint8 i = BATTLEGROUND_NA; i <= BATTLEGROUND_RL; ++i) | |
+ { | |
+ if (!sBattlegroundMgr->IsArenaType((BattlegroundTypeId)i)) | |
+ continue; | |
+ | |
+ BattlegroundContainer arenas = sBattlegroundMgr->GetBattlegroundsByType((BattlegroundTypeId)i); | |
+ for (BattlegroundContainer::const_iterator itr = arenas.begin(); itr != arenas.end(); ++itr) | |
+ { | |
+ Battleground* arena = itr->second; | |
+ | |
+ if (!arena->GetPlayersSize()) | |
+ continue; | |
+ | |
+ uint16 mmr = arena->GetArenaMatchmakerRatingByIndex(0) + arena->GetArenaMatchmakerRatingByIndex(1); | |
+ mmr /= 2; | |
+ | |
+ if (IsTop && arena->GetArenaType() == ARENA_TYPE_3v3) | |
+ { | |
+ TypeThree++; | |
+ if (TypeThree > (page + 1) * GamesOnPage) | |
+ { | |
+ haveNextPage = true; | |
+ break; | |
+ } | |
+ { | |
+ else if (arena->GetArenaType() == ARENA_TYPE_5v5) { | |
+ mmr = arena->GetArenaMatchmakerRatingByIndex(2); | |
+ firstTeamId = target->GetArenaTeamId(2); | |
+ Battleground::BattlegroundPlayerMap::const_iterator citr = arena->GetPlayers().begin(); | |
+ for (; citr != arena->GetPlayers().end(); ++citr) | |
+ if (Player* plrs = sObjectAccessor->FindPlayer(citr->first)) | |
+ if (plrs->GetArenaTeamId(2) != firstTeamId) | |
+ mmrTwo = arena->GetArenaMatchmakerRating(citr->second.Team); | |
+ } | |
+ | |
+ if (TypeThree >= page * GamesOnPage) | |
+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_BATTLE, GetGamesStringData(arena, mmr), GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_SELECTED_PLAYER + GetFirstPlayerGuid(arena)); | |
+ } | |
+ else if (!IsTop && arena->GetArenaType() == ARENA_TYPE_2v2) | |
+ { | |
+ TypeTwo++; | |
+ if (TypeTwo > (page + 1) * GamesOnPage) | |
+ { | |
+ haveNextPage = true; | |
+ break; | |
+ } | |
+ | |
+ if (TypeTwo >= page * GamesOnPage) | |
+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_BATTLE, GetGamesStringData(arena, mmr), GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_SELECTED_PLAYER + GetFirstPlayerGuid(arena)); | |
+ } | |
+ } | |
+ } | |
+ | |
+ if (page > 0) | |
+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_DOT, "Prev..", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_LIST_GAMES + page - 1); | |
+ | |
+ if (haveNextPage) | |
+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_DOT, "Next..", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_LIST_GAMES + page + 1); | |
+ } | |
+}; | |
+ | |
+ | |
+void AddSC_arena_spectator_script() | |
+{ | |
+ new arena_spectator_commands(); | |
+ new npc_arena_spectator(); | |
+} | |
\ No newline at end of file | |
diff --git a/src/server/scripts/Custom/CMakeLists.txt b/src/server/scripts/Custom/CMakeLists.txt | |
index 99cf026..2c91fe8 100644 | |
--- a/src/server/scripts/Custom/CMakeLists.txt | |
+++ b/src/server/scripts/Custom/CMakeLists.txt | |
@@ -10,6 +10,7 @@ | |
set(scripts_STAT_SRCS | |
${scripts_STAT_SRCS} | |
+ Custom/ArenaSpectator.cpp | |
) | |
message(" -> Prepared: Custom") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment