Skip to content

Instantly share code, notes, and snippets.

@walkline
Created June 3, 2012 12:52
Show Gist options
  • Save walkline/2863343 to your computer and use it in GitHub Desktop.
Save walkline/2863343 to your computer and use it in GitHub Desktop.
arena spectator system
From b86040a5b96460748af8c3949ffd1ad18a9a6bc2 Mon Sep 17 00:00:00 2001
From: walkline <walkline.ua@gmail.com>
Date: Sun, 3 Jun 2012 15:29:04 +0300
Subject: [PATCH] Implemented Arena Spectator.
diff --git a/sql/updates/fc_updates_world/arena_spectator.sql b/sql/updates/fc_updates_world/arena_spectator.sql
new file mode 100644
index 0000000..280de81
--- /dev/null
+++ b/sql/updates/fc_updates_world/arena_spectator.sql
@@ -0,0 +1,16 @@
+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` = 'spectatefrom';
+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 leav';
+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);
+
+DELETE FROM creature_template WHERE entry = '190000';
+INSERT INTO creature_template (entry, modelid1, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, Health_mod, Mana_mod, Armor_mod, faction_A, faction_H, npcflag, speed_walk, speed_run, scale, rank, dmg_multiplier, unit_class, unit_flags, type, type_flags, InhabitType, RegenHealth, flags_extra, ScriptName) VALUES
+('190000', '29348', "Arena Spectator", "Use addon!", 'Speak', '50000', 71, 71, 1.56, 1.56, 1.56, 35, 35, 3, 1, 1.14286, 1.25, 1, 1, 1, 2, 7, 138936390, 3, 1, 2, 'npc_arena_spectator');
diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp
index a9c7c79..dd4a32b 100755
--- a/src/server/game/Battlegrounds/Battleground.cpp
+++ b/src/server/game/Battlegrounds/Battleground.cpp
@@ -127,6 +127,7 @@ void Battleground::BroadcastWorker(Do& _do)
_do(player);
}
+
Battleground::Battleground()
{
m_TypeID = BATTLEGROUND_TYPE_NONE;
@@ -1304,13 +1305,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->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam())))
- EndBattleground(GetOtherTeam(player->GetTeam()));
+ // 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());
}
}
@@ -1465,6 +1477,15 @@ void Battleground::AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid
player->CastSpell(player, SPELL_WAITING_FOR_RESURRECT, true);
}
+void Battleground::SendSpectateAddonsMsg(SpectatorAddonMsg msg)
+{
+ if (!HaveSpectators())
+ return;
+
+ for (SpectatorList::iterator itr = m_Spectators.begin(); itr != m_Spectators.end(); ++itr)
+ msg.SendPacket(*itr);
+}
+
void Battleground::RemovePlayerFromResurrectQueue(uint64 player_guid)
{
for (std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h
index cda80fe..6117757 100755
--- 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;
@@ -410,6 +411,12 @@ class Battleground
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; }
@@ -685,6 +692,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 7afb83d..db78c14 100755
--- a/src/server/game/Battlegrounds/BattlegroundMgr.h
+++ b/src/server/game/Battlegrounds/BattlegroundMgr.h
@@ -80,6 +80,8 @@ class BattlegroundMgr
Battleground* GetBattlegroundThroughClientInstance(uint32 instanceId, BattlegroundTypeId bgTypeId);
Battleground* GetBattleground(uint32 InstanceID, BattlegroundTypeId bgTypeId); //there must be uint32 because MAX_BATTLEGROUND_TYPE_ID means unknown
+ BattlegroundSet GetBattlegroundsByType(BattlegroundTypeId bgTypeId) { return m_Battlegrounds[bgTypeId]; }
+
Battleground* GetBattlegroundTemplate(BattlegroundTypeId bgTypeId);
Battleground* CreateNewBattleground(BattlegroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated);
diff --git a/src/server/game/Battlegrounds/SpectatorAddon.cpp b/src/server/game/Battlegrounds/SpectatorAddon.cpp
new file mode 100755
index 0000000..04044f4
--- /dev/null
+++ b/src/server/game/Battlegrounds/SpectatorAddon.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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/>.
+ */
+
+#include "Player.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->outString("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;
+}
diff --git a/src/server/game/Battlegrounds/SpectatorAddon.h b/src/server/game/Battlegrounds/SpectatorAddon.h
new file mode 100755
index 0000000..1003486
--- /dev/null
+++ b/src/server/game/Battlegrounds/SpectatorAddon.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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/>.
+ */
+
+#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/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index 6b647c8..a4fb290 100755
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -358,7 +358,7 @@ ChatCommand* ChatHandler::getCommandTable()
{ "channel", SEC_ADMINISTRATOR, true, NULL, "", channelCommandTable },
{ "pet", SEC_GAMEMASTER, false, NULL, "", petCommandTable },
- { "ticket", SEC_MODERATOR, false, NULL, "", ticketCommandTable },
+ { "ticket", SEC_MODERATOR, false, NULL, "", ticketCommandTable },
{ "aura", SEC_ADMINISTRATOR, false, OldHandler<&ChatHandler::HandleAuraCommand>, "", NULL },
{ "unaura", SEC_ADMINISTRATOR, false, OldHandler<&ChatHandler::HandleUnAuraCommand>, "", NULL },
diff --git a/src/server/game/Chat/Commands/Level0.cpp b/src/server/game/Chat/Commands/Level0.cpp
index b2ac090..71dd763 100755
--- a/src/server/game/Chat/Commands/Level0.cpp
+++ b/src/server/game/Chat/Commands/Level0.cpp
@@ -26,6 +26,7 @@
#include "SystemConfig.h"
#include "revision.h"
#include "Util.h"
+#include "ArenaTeamMgr.h"
bool ChatHandler::HandleHelpCommand(const char* args)
{
@@ -156,4 +157,3 @@ bool ChatHandler::HandleServerMotdCommand(const char* /*args*/)
PSendSysMessage(LANG_MOTD_CURRENT, sWorld->GetMotd());
return true;
}
-
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index 74e5082..ab1c718 100755
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -458,6 +458,10 @@ 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);
@@ -1643,6 +1647,11 @@ 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 45b9aab..dd35dea 100755
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -862,6 +862,10 @@ Player::Player(WorldSession* session): Unit(true), m_achievementMgr(this), m_rep
m_SeasonalQuestChanged = false;
+ spectatorFlag = false;
+ spectateCanceled = false;
+ spectateFrom = NULL;
+
SetPendingBind(0, 0);
}
@@ -1828,6 +1832,15 @@ void Player::setDeathState(DeathState s)
return;
}
+ // send spectate addon message
+ if (HaveSpectators())
+ {
+ SpectatorAddonMsg msg;
+ msg.SetPlayer(GetName());
+ msg.SetStatus(false);
+ SendSpectatorAddonMsgToBG(msg);
+ }
+
// drunken state is cleared on death
SetDrunkValue(0);
// lost combo points at any target (targeted combo points clear in Unit::setDeathState)
@@ -1863,6 +1876,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);
+ }
+}
+
bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data)
{
// 0 1 2 3 4 5 6 7
@@ -2329,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()
@@ -2791,6 +2828,97 @@ void Player::SetInWater(bool apply)
getHostileRefManager().updateThreatTables();
}
+void Player::SetSpectate(bool on)
+{
+ if (on)
+ {
+ SetSpeed(MOVE_RUN, 2.5);
+ 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();
+
+ // random dispay id`s
+ uint32 morphs[8] = {25900, 18718, 29348, 22235, 30414, 736, 20582, 28213};
+ SetDisplayId(morphs[urand(0, 7)]);
+
+ 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::SendSpectatorAddonMsgToBG(SpectatorAddonMsg msg)
+{
+ if (!HaveSpectators())
+ return;
+
+ GetBattleground()->SendSpectateAddonsMsg(msg);
+}
+
void Player::SetGameMaster(bool on)
{
if (on)
@@ -22694,6 +22822,31 @@ 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);
}
@@ -23681,6 +23834,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;
+ }
+
sLog->outDebug(LOG_FILTER_MAPS, "Player::CreateViewpoint: Player %s create seer %u (TypeId: %u).", GetName(), target->GetEntry(), target->GetTypeId());
if (!AddUInt64Value(PLAYER_FARSIGHT, target->GetGUID()))
@@ -23693,10 +23856,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;
+
sLog->outDebug(LOG_FILTER_MAPS, "Player::CreateViewpoint: Player %s remove seer", GetName());
if (!RemoveUInt64Value(PLAYER_FARSIGHT, target->GetGUID()))
@@ -23708,6 +23879,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 26f64b9..66598f3 100755
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1180,6 +1180,13 @@ 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);
@@ -1608,7 +1615,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; }
@@ -2903,6 +2910,11 @@ class Player : public Unit, public GridObject<Player>
InstanceTimeMap _instanceResetTimes;
uint32 _pendingBindId;
uint32 _pendingBindTimer;
+
+ // spectator system
+ bool spectatorFlag;
+ bool spectateCanceled;
+ Unit *spectateFrom;
};
void AddItemsSetItem(Player*player, Item* item);
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 54ad565..db8f844 100755
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -286,6 +286,19 @@ Unit::~Unit()
_DeleteRemovedAuras();
+ // 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;
+ }
+ }
+
delete m_charmInfo;
delete movespline;
@@ -517,6 +530,54 @@ void Unit::GetRandomContactPoint(const Unit* obj, float &x, float &y, float &z,
, GetAngle(obj) + (attacker_number ? (static_cast<float>(M_PI/2) - static_cast<float>(M_PI) * (float)rand_norm()) * float(attacker_number) / combat_reach * 0.3f : 0));
}
+void Unit::SetVisibleAura(uint8 slot, AuraApplication * aur)
+{
+ if (Aura* aura = aur->GetBase())
+ if (Player *player = ToPlayer())
+ if (player->HaveSpectators() && slot < MAX_AURAS)
+ {
+ SpectatorAddonMsg msg;
+ uint64 casterID = 0;
+ if (aura->GetCaster())
+ casterID = (aura->GetCaster()->ToPlayer()) ? aura->GetCaster()->GetGUID() : 0;
+ msg.SetPlayer(player->GetName());
+ msg.CreateAura(casterID, aura->GetSpellInfo()->Id,
+ aura->GetSpellInfo()->IsPositive(), aura->GetSpellInfo()->Dispel,
+ aura->GetDuration(), aura->GetMaxDuration(),
+ aura->GetStackAmount(), false);
+ player->SendSpectatorAddonMsgToBG(msg);
+ }
+
+ m_visibleAuras[slot] = aur;
+ UpdateAuraForGroup(slot);
+}
+
+void Unit::RemoveVisibleAura(uint8 slot)
+{
+ AuraApplication *aurApp = GetVisibleAura(slot);
+ if (aurApp && slot < MAX_AURAS)
+ {
+ if (Aura* aura = aurApp->GetBase())
+ if (Player *player = ToPlayer())
+ if (player->HaveSpectators())
+ {
+ SpectatorAddonMsg msg;
+ uint64 casterID = 0;
+ if (aura->GetCaster())
+ casterID = (aura->GetCaster()->ToPlayer()) ? aura->GetCaster()->GetGUID() : 0;
+ msg.SetPlayer(player->GetName());
+ msg.CreateAura(casterID, aura->GetSpellInfo()->Id,
+ aurApp->IsPositive(), aura->GetSpellInfo()->Dispel,
+ aura->GetDuration(), aura->GetMaxDuration(),
+ aura->GetStackAmount(), true);
+ player->SendSpectatorAddonMsgToBG(msg);
+ }
+ }
+
+ m_visibleAuras.erase(slot);
+ UpdateAuraForGroup(slot);
+}
+
void Unit::UpdateInterruptMask()
{
m_interruptMask = 0;
@@ -13814,9 +13875,17 @@ void Unit::SetHealth(uint32 val)
SetUInt32Value(UNIT_FIELD_HEALTH, val);
- // group update
+ // group and spectator 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);
}
@@ -13839,9 +13908,17 @@ void Unit::SetMaxHealth(uint32 val)
uint32 health = GetHealth();
SetUInt32Value(UNIT_FIELD_MAXHEALTH, val);
- // group update
+ // group and spectators 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);
}
@@ -13876,9 +13953,18 @@ void Unit::SetPower(Powers power, uint32 val)
data << uint32(val);
SendMessageToSet(&data, GetTypeId() == TYPEID_PLAYER ? true : false);
- // group update
+ // group and spectators 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);
}
@@ -13902,9 +13988,18 @@ void Unit::SetMaxPower(Powers power, uint32 val)
uint32 cur_power = GetPower(power);
SetStatInt32Value(UNIT_FIELD_MAXPOWER1 + power, val);
- // group update
+ // group and spectators update
if (GetTypeId() == TYPEID_PLAYER)
{
+ if (ToPlayer()->HaveSpectators())
+ {
+ SpectatorAddonMsg msg;
+ msg.SetPlayer(ToPlayer()->GetName());
+ msg.SetMaxPower(val);
+ msg.SetPowerType(power);
+ ToPlayer()->SendSpectatorAddonMsgToBG(msg);
+ }
+
if (ToPlayer()->GetGroup())
ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_POWER);
}
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 1d63fc7..12285dc 100755
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -1999,8 +1999,8 @@ class Unit : public WorldObject
return itr->second;
return 0;
}
- void SetVisibleAura(uint8 slot, AuraApplication * aur){ m_visibleAuras[slot]=aur; UpdateAuraForGroup(slot);}
- void RemoveVisibleAura(uint8 slot){ m_visibleAuras.erase(slot); UpdateAuraForGroup(slot);}
+ void SetVisibleAura(uint8 slot, AuraApplication * aur);
+ void RemoveVisibleAura(uint8 slot);
uint32 GetInterruptMask() const { return m_interruptMask; }
void AddInterruptMask(uint32 mask) { m_interruptMask |= mask; }
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index b196151..e32bb56 100755
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -2744,6 +2744,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);
+ }
+
sLog->outDetail("MAP: Removing player '%s' from bg '%u' of map '%s' before relocating to another map", player->GetName(), GetInstanceId(), GetMapName());
Map::RemovePlayerFromMap(player, remove);
}
diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp
index 2fa6ae8..e5833c5 100755
--- a/src/server/game/Scripting/ScriptLoader.cpp
+++ b/src/server/game/Scripting/ScriptLoader.cpp
@@ -36,6 +36,8 @@ void AddSC_npc_mount();
void AddSC_ArgentTournament();
//Revenge od Dalaran squidrrel
void AddSC_dalaran_squirrel();
+// Arena Spectator
+void AddSC_arena_spectator_script();
// spells
void AddSC_deathknight_spell_scripts();
@@ -1268,5 +1270,6 @@ void AddCustomScripts()
AddSC_npc_mount();
AddSC_ArgentTournament();
AddSC_dalaran_squirrel();
+ AddSC_arena_spectator_script();
#endif
}
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index f29dd8f..2e77b04 100755
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -3004,6 +3004,16 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered
sLog->outDebug(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
//TODO:Apply this to all casted spells if needed
// Why check duration? 29350: channelled triggers channelled
@@ -4689,6 +4699,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 2f0d2a0..eaa62cc 100644
--- a/src/server/scripts/Commands/cs_gm.cpp
+++ b/src/server/scripts/Commands/cs_gm.cpp
@@ -135,6 +135,8 @@ public:
}
char const* name = itr->second->GetName();
uint8 security = itrSec;
+ if (security == 0)
+ continue;
uint8 max = ((16 - strlen(name)) / 2);
uint8 max2 = max;
if ((max + max2 + strlen(name)) == 16)
diff --git a/src/server/scripts/Custom/CMakeLists.txt b/src/server/scripts/Custom/CMakeLists.txt
index ffb2046..24477b0 100644
--- a/src/server/scripts/Custom/CMakeLists.txt
+++ b/src/server/scripts/Custom/CMakeLists.txt
@@ -14,6 +14,7 @@ set(scripts_STAT_SRCS
Custom/npc_mount.cpp
Custom/argent_tournament.cpp
Custom/dalaran_squirrel.cpp
+ Custom/arena_spectator.cpp
)
message(" -> Prepared: Custom")
diff --git a/src/server/scripts/Custom/arena_spectator.cpp b/src/server/scripts/Custom/arena_spectator.cpp
new file mode 100644
index 0000000..2e66ba3
--- /dev/null
+++ b/src/server/scripts/Custom/arena_spectator.cpp
@@ -0,0 +1,485 @@
+/*
+ * 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"
+
+class arena_spectator_commands : public CommandScript
+{
+ public:
+ arena_spectator_commands() : CommandScript("arena_spectator_commands") { }
+
+ static bool HandleSpectateCommand(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 == player || target_guid == player->GetGUID())
+ {
+ handler->SendSysMessage(LANG_CANT_TELEPORT_SELF);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (player->isInCombat())
+ {
+ handler->SendSysMessage(LANG_YOU_IN_COMBAT);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (!target)
+ {
+ handler->SendSysMessage(LANG_PLAYER_NOT_EXIST_OR_OFFLINE);
+ 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, "View games with high rating...", GOSSIP_SENDER_MAIN, NPC_SPECTATOR_ACTION_LIST_TOP_GAMES);
+ pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "View games with low rating...", 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);
+ arena_spectator_commands::HandleSpectateCommand(&handler, target->GetName());
+ }
+ }
+ 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 = "DK "; 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 *arena, uint16 mmr)
+ {
+ std::string teamsMember[BG_TEAMS_COUNT];
+ uint32 firstTeamId = 0;
+ for (Battleground::BattlegroundPlayerMap::const_iterator itr = arena->GetPlayers().begin(); itr != arena->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 *arena)
+ {
+ for (Battleground::BattlegroundPlayerMap::const_iterator itr = arena->GetPlayers().begin(); itr != arena->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_RV; ++i)
+ {
+ if (!sBattlegroundMgr->IsArenaType((BattlegroundTypeId)i))
+ continue;
+
+ BattlegroundSet bgs = sBattlegroundMgr->GetBattlegroundsByType((BattlegroundTypeId)i);
+ for (BattlegroundSet::iterator itr = bgs.begin(); itr != bgs.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();
+}
@brolaf
Copy link

brolaf commented Apr 3, 2013

Need update because of new trinity security system

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment