Skip to content

Instantly share code, notes, and snippets.

@FALL1N1
Last active August 29, 2015 14:17
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save FALL1N1/29c64c1330a9154cb598 to your computer and use it in GitHub Desktop.
Arena Spectator (all revisions) \\ // All you need to do is to put the both files in the CMakeLists.txt and ScriptLoader.cpp
From 8f0b67ad3fda93e0cb7c2d29a3b4eaa405146aca Mon Sep 17 00:00:00 2001
From: Emiliyan Kurtseliyanski <emiliyan9604bg@gmail.com>
Date: Fri, 13 Mar 2015 17:50:38 +0200
Subject: [PATCH] Properly implement the Arena Spectator
---
sql/custom/spectator.sql | 13 +
src/server/game/Accounts/RBAC.h | 6 +
src/server/game/Battlegrounds/Battleground.cpp | 24 +-
src/server/game/Battlegrounds/Battleground.h | 10 +
src/server/game/Battlegrounds/BattlegroundMgr.h | 12 +-
src/server/game/Battlegrounds/SpectatorAddon.cpp | 0
src/server/game/Battlegrounds/SpectatorAddon.h | 0
src/server/game/Entities/GameObject/GameObject.cpp | 10 +
src/server/game/Entities/Player/Player.cpp | 127 ++++-
src/server/game/Entities/Player/Player.h | 15 +-
src/server/game/Entities/Unit/Unit.cpp | 14 +
src/server/game/Handlers/ChatHandler.cpp | 6 +
src/server/game/Maps/Map.cpp | 7 +
src/server/game/Miscellaneous/Language.h | 3 +
src/server/game/Spells/Spell.cpp | 4 +
src/server/scripts/Commands/cs_gm.cpp | 2 +
src/server/scripts/Custom/arena_spectator.cpp | 611 +++++++++++++++++++++
17 files changed, 848 insertions(+), 16 deletions(-)
create mode 100644 sql/custom/spectator.sql
create mode 100644 src/server/game/Battlegrounds/SpectatorAddon.cpp
create mode 100644 src/server/game/Battlegrounds/SpectatorAddon.h
create mode 100644 src/server/scripts/Custom/arena_spectator.cpp
diff --git a/sql/custom/spectator.sql b/sql/custom/spectator.sql
new file mode 100644
index 0000000..f917883
--- /dev/null
+++ b/sql/custom/spectator.sql
@@ -0,0 +1,13 @@
+insert into `rbac_permissions` (`id`, `name`) values
+('1000', 'spectate'),
+('1001', 'spectate player'),
+('1002', 'spectate view'),
+('1003', 'spectate reset'),
+('1004', 'spectate leave');
+
+insert into `rbac_linked_permissions` (`id`, `linkedId`) values
+('195','1000'),
+('195','1001'),
+('195','1002'),
+('195','1003'),
+('195','1004');
\ No newline at end of file
diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h
index d63ba84..51cdfbd 100644
--- a/src/server/game/Accounts/RBAC.h
+++ b/src/server/game/Accounts/RBAC.h
@@ -696,6 +696,12 @@ enum RBACPermissions
RBAC_PERM_COMMAND_MODIFY_XP = 798,
// custom permissions 1000+
+ RBAC_PERM_COMMAND_SPECTATE = 1000,
+ RBAC_PERM_COMMAND_SPECTATE_PLAYER = 1001,
+ RBAC_PERM_COMMAND_SPECTATE_VIEW = 1002,
+ RBAC_PERM_COMMAND_SPECTATE_RESET = 1003,
+ RBAC_PERM_COMMAND_SPECTATE_LEAVE = 1004,
+
RBAC_PERM_MAX
};
diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp
index f428c73..28531ed 100644
--- a/src/server/game/Battlegrounds/Battleground.cpp
+++ b/src/server/game/Battlegrounds/Battleground.cpp
@@ -33,6 +33,7 @@
#include "ReputationMgr.h"
#include "SpellAuraEffects.h"
#include "SpellAuras.h"
+#include "SpectatorAddon.h"
#include "Util.h"
#include "WorldPacket.h"
#include "Transport.h"
@@ -1134,13 +1135,22 @@ 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));
-
- // 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()));
+ 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->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam())))
+ EndBattleground(GetOtherTeam(player->GetTeam()));
+ }
+ }
+ if (!player->IsSpectator())
+ player->LeaveBattleground();
+ else
+ {
+ player->TeleportToBGEntryPoint();
+ RemoveSpectator(player->GetGUID());
}
}
diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h
index deba7aa..a952ec4 100644
--- a/src/server/game/Battlegrounds/Battleground.h
+++ b/src/server/game/Battlegrounds/Battleground.h
@@ -26,6 +26,7 @@
#include "WorldPacket.h"
#include "Object.h"
#include "GameObject.h"
+#include "SpectatorAddon.h"
class Creature;
class GameObject;
@@ -301,6 +302,13 @@ class Battleground
bool HasFreeSlots() const;
uint32 GetFreeSlotsForTeam(uint32 Team) const;
+ /* SPECTATOR */
+ 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); }
+ /* SPECTATOR */
+
bool isArena() const { return m_IsArena; }
bool isBattleground() const { return !m_IsArena; }
bool isRated() const { return m_IsRated; }
@@ -581,6 +589,8 @@ class Battleground
// Raid Group
Group* m_BgRaids[BG_TEAMS_COUNT]; // 0 - alliance, 1 - horde
+
+ SpectatorList m_Spectators; // Spectator
// 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 3b245ca..e84d352 100644
--- a/src/server/game/Battlegrounds/BattlegroundMgr.h
+++ b/src/server/game/Battlegrounds/BattlegroundMgr.h
@@ -85,6 +85,7 @@ 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);
@@ -109,6 +110,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);
@@ -117,6 +119,14 @@ class BattlegroundMgr
static HolidayIds BGTypeToWeekendHolidayId(BattlegroundTypeId bgTypeId);
static BattlegroundTypeId WeekendHolidayIdToBGType(HolidayIds holiday);
static bool IsBGWeekend(BattlegroundTypeId bgTypeId);
+ BattlegroundData* GetAllBattlegroundsWithTypeId(BattlegroundTypeId bgTypeId)
+ {
+ BattlegroundDataContainer::iterator it = bgDataStore.find(bgTypeId);
+ if (it == bgDataStore.end())
+ return NULL;
+
+ return &it->second;
+ }
uint32 GetMaxRatingDifference() const;
uint32 GetRatingDiscardTimer() const;
@@ -134,12 +144,12 @@ class BattlegroundMgr
private:
bool CreateBattleground(BattlegroundTemplate const* bgTemplate);
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];
std::vector<uint64> m_QueueUpdateScheduler;
diff --git a/src/server/game/Battlegrounds/SpectatorAddon.cpp b/src/server/game/Battlegrounds/SpectatorAddon.cpp
new file mode 100644
index 0000000..e69de29
diff --git a/src/server/game/Battlegrounds/SpectatorAddon.h b/src/server/game/Battlegrounds/SpectatorAddon.h
new file mode 100644
index 0000000..e69de29
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index d77aab7..2d97fd8 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -551,6 +551,11 @@ void GameObject::Update(uint32 diff)
}
else if (Unit* target = ObjectAccessor::GetUnit(*this, m_lootStateUnitGUID))
{
+ // If player is spectator do not activate.
+ if (Player *tmpPlayer = target->ToPlayer())
+ if (tmpPlayer->IsSpectator())
+ return;
+
// Some traps do not have a spell but should be triggered
if (goInfo->trap.spellId)
CastSpell(target, goInfo->trap.spellId);
@@ -1752,6 +1757,11 @@ void GameObject::Use(Unit* user)
void GameObject::CastSpell(Unit* target, uint32 spellId, bool triggered /*= true*/)
{
+ 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 b285d8d..70b8516 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -69,6 +69,7 @@
#include "SpellAuraEffects.h"
#include "SpellAuras.h"
#include "SpellMgr.h"
+#include "SpectatorAddon.h"
#include "Transport.h"
#include "UpdateData.h"
#include "UpdateFieldFlags.h"
@@ -907,7 +908,9 @@ Player::Player(WorldSession* session): Unit(true)
m_MonthlyQuestChanged = false;
m_SeasonalQuestChanged = false;
-
+ spectatorFlag = false;
+ spectateCanceled = false;
+ spectateFrom = NULL;
SetPendingBind(0, 0);
_activeCheats = CHEAT_NONE;
@@ -1906,7 +1909,6 @@ void Player::setDeathState(DeathState s)
TC_LOG_ERROR("entities.player", "setDeathState: attempt to kill a dead player %s(%d)", GetName().c_str(), GetGUIDLow());
return;
}
-
// drunken state is cleared on death
SetDrunkValue(0);
// lost combo points at any target (targeted combo points clear in Unit::setDeathState)
@@ -1951,6 +1953,12 @@ void Player::InnEnter(time_t time, uint32 mapid, float x, float y, float z)
time_inn_enter = time;
}
+void Player::SetSelection(ObjectGuid guid)
+{
+ uint32 m_curSelection = guid;
+ SetUInt64Value(UNIT_FIELD_TARGET, guid);
+}
+
bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data)
{
// 0 1 2 3 4 5 6 7
@@ -2380,7 +2388,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()
@@ -2829,6 +2847,85 @@ void Player::SetInWater(bool apply)
getHostileRefManager().updateThreatTables();
}
+void Player::SetSpectate(bool on)
+{
+ if (on)
+ {
+ SetSpeed(MOVE_RUN, 5.0);
+ spectatorFlag = true;
+
+ m_ExtraFlags |= PLAYER_EXTRA_GM_ON;
+ setFaction(35);
+
+ if (Pet* pet = GetPet())
+ {
+ RemovePet(pet, PET_SAVE_AS_CURRENT);
+ }
+ UnsummonPetTemporaryIfAny();
+
+ RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
+ ResetContestedPvP();
+
+ getHostileRefManager().setOnlineOfflineState(false);
+ CombatStopWithPets();
+
+ 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);
+
+ 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::SetGameMaster(bool on)
{
if (on)
@@ -24558,6 +24655,16 @@ 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("maps", "Player::CreateViewpoint: Player %s create seer %u (TypeId: %u).", GetName().c_str(), target->GetEntry(), target->GetTypeId());
if (!AddGuidValue(PLAYER_FARSIGHT, target->GetGUID()))
@@ -24570,10 +24677,18 @@ void Player::SetViewpoint(WorldObject* target, bool apply)
UpdateVisibilityOf(target);
if (target->isType(TYPEMASK_UNIT) && !GetVehicle())
+ {
+ if (IsSpectator())
+ spectateFrom = (Unit*)target;
+
((Unit*)target)->AddPlayerToVision(this);
+ }
}
else
{
+ if (IsSpectator() && !spectateFrom)
+ return;
+
TC_LOG_DEBUG("maps", "Player::CreateViewpoint: Player %s remove seer", GetName().c_str());
if (!RemoveGuidValue(PLAYER_FARSIGHT, target->GetGUID()))
@@ -24584,10 +24699,12 @@ void Player::SetViewpoint(WorldObject* target, bool apply)
if (target->isType(TYPEMASK_UNIT) && !GetVehicle())
((Unit*)target)->RemovePlayerFromVision(this);
-
//must immediately set seer back otherwise may crash
m_seer = this;
-
+
+ if (IsSpectator())
+ spectateFrom = NULL;
+
//WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0);
//GetSession()->SendPacket(&data);
}
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 2b9211e..8de7d6d 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 <limits>
#include <string>
@@ -1176,7 +1177,7 @@ class Player : public Unit, public GridObject<Player>
// mount_id can be used in scripting calls
bool isAcceptWhispers() const { return (m_ExtraFlags & PLAYER_EXTRA_ACCEPT_WHISPERS) != 0; }
void SetAcceptWhispers(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_ACCEPT_WHISPERS; else m_ExtraFlags &= ~PLAYER_EXTRA_ACCEPT_WHISPERS; }
- bool IsGameMaster() const { return (m_ExtraFlags & PLAYER_EXTRA_GM_ON) != 0; }
+ bool IsGameMaster() const { return (m_ExtraFlags & PLAYER_EXTRA_GM_ON); }
void SetGameMaster(bool on);
bool isGMChat() const { return (m_ExtraFlags & PLAYER_EXTRA_GM_CHAT) != 0; }
void SetGMChat(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_GM_CHAT; else m_ExtraFlags &= ~PLAYER_EXTRA_GM_CHAT; }
@@ -1187,6 +1188,12 @@ 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();
+ 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);
@@ -1561,7 +1568,7 @@ class Player : public Unit, public GridObject<Player>
Player* GetSelectedPlayer() const;
void SetTarget(ObjectGuid /*guid*/) override { } /// Used for serverside target changes, does not apply to players
- void SetSelection(ObjectGuid guid) { SetGuidValue(UNIT_FIELD_TARGET, guid); }
+ void SetSelection(ObjectGuid guid);
uint8 GetComboPoints() const { return m_comboPoints; }
ObjectGuid GetComboTarget() const { return m_comboTarget; }
@@ -2693,7 +2700,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 f47cc79..47f2a13 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -54,6 +54,7 @@
#include "Spell.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
+#include "SpectatorAddon.h"
#include "TemporarySummon.h"
#include "Totem.h"
#include "Transport.h"
@@ -288,6 +289,19 @@ Unit::~Unit()
m_currentSpells[i] = NULL;
}
+ // remove view 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 i_motionMaster;
diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp
index 288bb82..b50accb 100644
--- a/src/server/game/Handlers/ChatHandler.cpp
+++ b/src/server/game/Handlers/ChatHandler.cpp
@@ -553,6 +553,12 @@ void WorldSession::HandleTextEmoteOpcode(WorldPacket& recvData)
recvData >> emoteNum;
recvData >> guid;
+ if (GetPlayer()->IsSpectator())
+ {
+ SendNotification(LANG_SPEC_CAN_NOT_CHAT);
+ return;
+ }
+
sScriptMgr->OnPlayerTextEmote(GetPlayer(), text_emote, emoteNum, guid);
EmotesTextEntry const* em = sEmotesTextStore.LookupEntry(text_emote);
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 7f350df..58a8349 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -3253,6 +3253,13 @@ 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/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index 52254eb..517998f 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -853,6 +853,9 @@ enum TrinityStrings
LANG_RBAC_EMAIL_REQUIRED = 881,
// Room for in-game strings 882-999 not used
+ // Arena Spectator (Emote Disable)
+ LANG_SPEC_CAN_NOT_CHAT = 900,
+
// Level 4 (CLI only commands)
LANG_COMMAND_EXIT = 1000,
LANG_ACCOUNT_DELETED = 1001,
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index e8e1bbd..f2b8d56 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -4684,6 +4684,10 @@ SpellCastResult Spell::CheckCast(bool strict)
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
if (strict && !(_triggeredCastFlags & TRIGGERED_IGNORE_SHAPESHIFT))
diff --git a/src/server/scripts/Commands/cs_gm.cpp b/src/server/scripts/Commands/cs_gm.cpp
index ee2bb89..58e3254 100644
--- a/src/server/scripts/Commands/cs_gm.cpp
+++ b/src/server/scripts/Commands/cs_gm.cpp
@@ -133,6 +133,8 @@ public:
itrSec <= AccountTypes(sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_GM_LIST)))) &&
(!handler->GetSession() || itr->second->IsVisibleGloballyFor(handler->GetSession()->GetPlayer())))
{
+ if (itr->second->IsSpectator())
+ continue; // don't show spectators, they're not really gms
if (first)
{
first = false;
diff --git a/src/server/scripts/Custom/arena_spectator.cpp b/src/server/scripts/Custom/arena_spectator.cpp
new file mode 100644
index 0000000..5c028c9
--- /dev/null
+++ b/src/server/scripts/Custom/arena_spectator.cpp
@@ -0,0 +1,611 @@
+/*
+* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
+* Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* ScriptData
+Name: Arena Spectator
+%Complete: 100
+Comment: Script allow spectate arena games
+Category: Custom Script
+EndScriptData */
+
+#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"
+
+int8 UsingGossip;
+
+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("You can't spectate yourself.");
+ 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 online or does not exist.");
+ 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 in a battleground or arena.");
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ Map* cMap = target->GetMap();
+ if (!cMap->IsBattleArena())
+ {
+ handler->PSendSysMessage("Player is not in an Arena match.");
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (player->GetMap()->IsBattleground())
+ {
+ handler->PSendSysMessage("You can't do that while in a battleground.");
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (target->HasAura(32728) || target->HasAura(32727))
+ {
+ handler->PSendSysMessage("You can't do that. The Arena match didn't start yet.");
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (target->IsSpectator())
+ {
+ handler->PSendSysMessage("You can't do that. Your target is a spectator.");
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (player->IsMounted())
+ {
+ handler->PSendSysMessage("Cannot Spectate while mounted.");
+ 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();
+
+ // 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 a Rated Arena.");
+ handler->PSendSysMessage("Teams:");
+ handler->PSendSysMessage("|cFFffffff%s|r vs |cFFffffff%s|r", firstTeam->GetName().c_str(), secondTeam->GetName().c_str());
+ handler->PSendSysMessage("|cFFffffff%u(%u)|r -- |cFFffffff%u(%u)|r", 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() || player->isSpectateCanceled())
+ {
+ handler->PSendSysMessage("You are not a spectator.");
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ // RemoveSpectator shouldn't be needed to be done explicitly as it'll be done in the CancelSpectate func
+ //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("Player is not online or does not exist.");
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (!player->IsSpectator())
+ {
+ handler->PSendSysMessage("You are not a spectator, spectate someone first.");
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (target->IsSpectator() && target != player)
+ {
+ handler->PSendSysMessage("You can't do that. Your target is a spectator.");
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (player->GetMap() != target->GetMap())
+ {
+ handler->PSendSysMessage("You can't do that. Your target might be in a different arena match.");
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (target && target->HasAuraType(SPELL_AURA_MOD_STEALTH))
+ {
+ handler->PSendSysMessage("You can't target stealthed players.");
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ // check for arena preperation
+ // if exists than battle didn`t begin
+ if (target && target->HasAura(32728) || target->HasAura(32727))
+ {
+ handler->PSendSysMessage("You can't do that. The Arena match didn't start yet.");
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (target == player && player->getSpectateFrom())
+ player->SetViewpoint(player->getSpectateFrom(), false);
+ else
+ player->SetViewpoint(target, true);
+ return true;
+ }
+
+ // addon data
+ static bool HandleSpectateResetCommand(ChatHandler* handler, const char *args)
+ {
+ Player* player = handler->GetSession()->GetPlayer();
+
+ if (!player->IsSpectator())
+ {
+ handler->PSendSysMessage("You are not a 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 OVERRIDE
+ {
+ static ChatCommand spectateCommandTable[] =
+ {
+ { "player", rbac::RBAC_PERM_COMMAND_SPECTATE_PLAYER, true, &HandleSpectateCommand, "", NULL },
+ { "view", rbac::RBAC_PERM_COMMAND_SPECTATE_VIEW, true, &HandleSpectateFromCommand, "", NULL },
+ { "reset", rbac::RBAC_PERM_COMMAND_SPECTATE_RESET, true, &HandleSpectateResetCommand, "", NULL },
+ { "leave", rbac::RBAC_PERM_COMMAND_SPECTATE_LEAVE, true, &HandleSpectateCancelCommand, "", NULL },
+ { NULL, 0, false, NULL, "", NULL }
+ };
+
+ static ChatCommand commandTable[] =
+ {
+ { "spectate", rbac::RBAC_PERM_COMMAND_SPECTATE, 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_SPECIFIC = 500,
+
+ // NPC_SPECTATOR_ACTION_SELECTED_PLAYER + player.Guid()
+ NPC_SPECTATOR_ACTION_SELECTED_PLAYER = 3000
+};
+
+const uint8 GamesOnPage = 15;
+
+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, "|TInterface\\icons\\Achievement_Arena_2v2_7:35:35:-30:0|tGames: 2v2", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_2V2_GAMES);
+ pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "|TInterface\\icons\\Achievement_Arena_3v3_7:35:35:-30:0|tGames: 3v3", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_3V3_GAMES);
+ pPlayer->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_CHAT, "|TInterface\\icons\\Spell_Holy_DevineAegis:35:35:-30:0|tSpectate Specific Player.", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_SPECIFIC, "", 0, true);
+ 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_SPECIFIC)
+ {
+
+ }
+ if (action = NPC_SPECTATOR_ACTION_2V2_GAMES)
+ {
+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_DOT, "Refresh", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_2V2_GAMES);
+ ShowPage(player, action - NPC_SPECTATOR_ACTION_2V2_GAMES, false);
+ player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
+ }
+ else if (action = NPC_SPECTATOR_ACTION_3V3_GAMES)
+ {
+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_DOT, "Refresh", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_3V3_GAMES);
+ ShowPage(player, action - NPC_SPECTATOR_ACTION_3V3_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());
+ char const* pTarget = target->GetName().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 = "Paladin "; break;
+ case CLASS_HUNTER: sClass = "Hunter "; break;
+ case CLASS_ROGUE: sClass = "Rogue "; break;
+ case CLASS_PRIEST: sClass = "Priest "; break;
+ case CLASS_DEATH_KNIGHT: sClass = "DKnight "; 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, uint16 mmrTwo)
+ {
+ 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;
+
+ if (player->IsGameMaster())
+ continue;
+
+ uint32 team = itr->second.Team;
+ if (!firstTeamId)
+ firstTeamId = team;
+
+ teamsMember[firstTeamId == team] += GetClassNameById(player->getClass());
+ }
+
+ std::string data = teamsMember[0] + "(";
+ std::stringstream sstwo;
+ std::stringstream ss;
+ ss << mmr;
+ sstwo << mmrTwo;
+ data += ss.str();
+ data += ") - ";
+ data += teamsMember[1] + "(" + sstwo.str();
+ data += ")";
+ 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)
+ {
+ uint32 firstTeamId = 0;
+ uint16 TypeTwo = 0;
+ uint16 TypeThree = 0;
+ uint16 mmr = 0;
+ uint16 mmrTwo = 0;
+ bool haveNextPage = false;
+ for (uint8 i = 0; i <= MAX_BATTLEGROUND_TYPE_ID; ++i)
+ {
+ if (!sBattlegroundMgr->IsArenaType(BattlegroundTypeId(i)))
+ continue;
+
+ BattlegroundData* arenas = sBattlegroundMgr->GetAllBattlegroundsWithTypeId(BattlegroundTypeId(i));
+
+ if (!arenas || arenas->m_Battlegrounds.empty())
+ continue;
+
+ for (BattlegroundContainer::const_iterator itr = arenas->m_Battlegrounds.begin(); itr != arenas->m_Battlegrounds.end(); ++itr)
+ {
+ Battleground* arena = itr->second;
+ Player* target = ObjectAccessor::FindPlayer(GetFirstPlayerGuid(arena));
+ if (!target)
+ continue;
+
+ if (target->HasAura(32728) || target->HasAura(32727))
+ continue;
+
+ if (!arena->GetPlayersSize())
+ continue;
+
+ if (!arena->isRated())
+ continue;
+
+ if (arena->GetArenaType() == ARENA_TYPE_2v2)
+ {
+ mmr = arena->GetArenaMatchmakerRatingByIndex(0);
+ firstTeamId = target->GetArenaTeamId(0);
+ Battleground::BattlegroundPlayerMap::const_iterator citr = arena->GetPlayers().begin();
+ for (; citr != arena->GetPlayers().end(); ++citr)
+ if (Player* plrs = sObjectAccessor->FindPlayer(citr->first))
+ if (plrs->GetArenaTeamId(0) != firstTeamId)
+ mmrTwo = arena->GetArenaMatchmakerRating(citr->second.Team);
+ }
+ else
+ {
+ mmr = arena->GetArenaMatchmakerRatingByIndex(1);
+ firstTeamId = target->GetArenaTeamId(1);
+ Battleground::BattlegroundPlayerMap::const_iterator citr = arena->GetPlayers().begin();
+ for (; citr != arena->GetPlayers().end(); ++citr)
+ if (Player* plrs = sObjectAccessor->FindPlayer(citr->first))
+ if (plrs->GetArenaTeamId(1) != firstTeamId)
+ mmrTwo = arena->GetArenaMatchmakerRating(citr->second.Team);
+ }
+
+ if (IsTop && arena->GetArenaType() == ARENA_TYPE_3v3)
+ {
+ TypeThree++;
+ if (TypeThree > (page + 1) * GamesOnPage)
+ {
+ haveNextPage = true;
+ break;
+ }
+
+ if (TypeThree >= page * GamesOnPage)
+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_BATTLE, GetGamesStringData(arena, mmr, mmrTwo), 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, mmrTwo), 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_2V2_GAMES + page - 1);
+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_DOT, "Prev..", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_3V3_GAMES + page - 1);
+ }
+
+ if (haveNextPage)
+ {
+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_DOT, "Next..", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_2V2_GAMES + page + 1);
+ player->ADD_GOSSIP_ITEM(GOSSIP_ICON_DOT, "Next..", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_3V3_GAMES + page + 1);
+ }
+ }
+
+ bool OnGossipSelectCode(Player* player, Creature* creature, uint32 sender, uint32 action, const char* code)
+ {
+ if (!player)
+ return true;
+
+ player->PlayerTalkClass->ClearMenus();
+ player->CLOSE_GOSSIP_MENU();
+ if (sender == GOSSIP_SENDER_MAIN)
+ {
+ switch (action)
+ {
+ case NPC_SPECTATOR_ACTION_SPECIFIC: // choosing a player
+
+ const char* plrName = code;
+
+ char playerName[50];
+ strcpy(playerName, plrName);
+
+ for (int i = 0; i < 13; i++)
+ {
+ if (playerName[i] == NULL)
+ break;
+ if (i == 0 && playerName[i] > 96)
+ playerName[0] -= 32;
+ else if (playerName[i] < 97)
+ playerName[i] += 32;
+ }
+
+ if (Player* target = sObjectAccessor->FindPlayerByName(playerName))
+ {
+ ChatHandler handler(player->GetSession());
+ char const* pTarget = target->GetName().c_str();
+ arena_spectator_commands::HandleSpectateCommand(&handler, pTarget);
+ }
+ ChatHandler(player->GetSession()).PSendSysMessage("Player is not online or does not exist.");
+ return true;
+ }
+ }
+
+ return false;
+ }
+};
+
+
+
+void AddSC_arena_spectator_script()
+{
+ new arena_spectator_commands();
+ new npc_arena_spectator();
+}
\ No newline at end of file
--
1.8.3.msysgit.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment