Skip to content

Instantly share code, notes, and snippets.

@irancore
Last active May 3, 2020 19:04
Show Gist options
  • Save irancore/6473293 to your computer and use it in GitHub Desktop.
Save irancore/6473293 to your computer and use it in GitHub Desktop.
For Last Version TrinityCore
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);
+ player->SetGMVisible(true);
+ player->SetGameMaster(false);
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))
+ {
+ sLog->outInfo(LOG_FILTER_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(LOG_FILTER_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(LOG_FILTER_SPELLS_AURAS, "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.h"
+
+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_LIST_GAMES = 1000,
+ NPC_SPECTATOR_ACTION_LIST_TOP_GAMES = 2000,
+
+ // NPC_SPECTATOR_ACTION_SELECTED_PLAYER + player.Guid()
+ NPC_SPECTATOR_ACTION_SELECTED_PLAYER = 3000
+};
+
+const uint16 TopGamesRating = 1800;
+const uint8 GamesOnPage = 20;
+
+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, "Games 3v3", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_LIST_TOP_GAMES);
+ pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Games 2v2", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_LIST_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 = "Warrior "; 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 = "Shama "; 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 && mmr >= TopGamesRating)
+ {
+ highGames++;
+ if (highGames > (page + 1) * GamesOnPage)
+ {
+ haveNextPage = true;
+ break;
+ }
+
+ if (highGames >= 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 && mmr < TopGamesRating)
+ {
+ lowGames++;
+ if (lowGames > (page + 1) * GamesOnPage)
+ {
+ haveNextPage = true;
+ break;
+ }
+
+ if (lowGames >= 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