Sure, it's a pretty old plugin I haven't used in many many years so why not. If you end up making a release you will want to use SourceMod's HudMsg functions and make the message buffer dynamic. Updating to the new syntax and giving admins the option to hide themselves from the list would be cool too. To optimize more just remove the prethink hook and lower the SPEC_MESSAGE_DELAY time. If you lower SPEC_MESSAGE_DELAY keep in mind it'll send messages over the network more often.
PHP Code:
#include <sourcemod>
#include <sdkhooks>
#pragma semicolon 1
new const String:PLUGIN_VERSION[] = "1.0";
public Plugin:myinfo =
{
name = "Show Spectators",
author = "hlstriker",
description = "Shows who is spectating a specific player.",
version = PLUGIN_VERSION,
url = "www.swoobles.com"
}
const OBS_MODE_IN_EYE = 4;
const OBS_MODE_CHASE = 5;
const Float:SPEC_MESSAGE_DELAY = 1.2;
new Float:g_fNextSpecMessage[MAXPLAYERS+1];
new g_iSpectating[MAXPLAYERS+1];
new UserMsg:g_msgHudMsg;
public OnPluginStart()
{
CreateConVar("hls_showspec_version", PLUGIN_VERSION, "Show Spectators Version", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_NOTIFY|FCVAR_PRINTABLEONLY);
g_msgHudMsg = GetUserMessageId("HudMsg");
for(new iClient=1; iClient<=MaxClients; iClient++)
{
if(IsClientInGame(iClient))
SDKHook(iClient, SDKHook_PreThink, hook_PreThink);
}
CreateTimer(SPEC_MESSAGE_DELAY, TimerSpecMessage, _, TIMER_REPEAT);
}
public Action:TimerSpecMessage(Handle:hTimer)
{
new Float:fCurTime = GetEngineTime();
for(new iClient=1; iClient<=MaxClients; iClient++)
{
if(!IsClientInGame(iClient) || IsPlayerAlive(iClient))
continue;
if(g_fNextSpecMessage[iClient] > fCurTime)
continue;
BuildSpecMessage(iClient);
}
}
public OnClientPutInServer(iClient)
{
SDKHook(iClient, SDKHook_PreThink, hook_PreThink);
}
public hook_PreThink(iClient)
{
if(IsPlayerAlive(iClient))
return;
if(!UpdateSpectatingTarget(iClient))
return;
BuildSpecMessage(iClient);
}
UpdateSpectatingTarget(iClient)
{
// Return true if the client is spectating a different player.
switch(GetEntProp(iClient, Prop_Send, "m_iObserverMode"))
{
case OBS_MODE_IN_EYE, OBS_MODE_CHASE:
{
new iSpectating = GetEntPropEnt(iClient, Prop_Send, "m_hObserverTarget");
if(iSpectating != g_iSpectating[iClient])
{
g_iSpectating[iClient] = iSpectating;
return true;
}
}
default:
{
if(g_iSpectating[iClient])
{
// This client was spectating someone, but isn't anymore so clear their message.
g_iSpectating[iClient] = 0;
ShowSpecMessage(iClient, " ");
}
}
}
return false;
}
BuildSpecMessage(iClient)
{
g_fNextSpecMessage[iClient] = GetEngineTime() + SPEC_MESSAGE_DELAY - 0.2;
if(g_iSpectating[iClient] <= 0 || !IsClientInGame(g_iSpectating[iClient]))
return;
static String:szBuffer[254], iBufLen, String:szName[32];
iBufLen = 0;
GetClientName(g_iSpectating[iClient], szName, sizeof(szName));
iBufLen += FormatEx(szBuffer[iBufLen], sizeof(szBuffer)-iBufLen, "\nSpectating %s:\n", szName);
for(new iSpectator=1; iSpectator<=MaxClients; iSpectator++)
{
if(!IsClientInGame(iSpectator) || IsPlayerAlive(iSpectator))
continue;
if(g_iSpectating[iSpectator] != g_iSpectating[iClient])
continue;
GetClientName(iSpectator, szName, sizeof(szName));
iBufLen += FormatEx(szBuffer[iBufLen], sizeof(szBuffer)-iBufLen, "%s\n", szName);
}
ShowSpecMessage(iClient, szBuffer);
}
ShowSpecMessage(iClient, const String:szMessage[])
{
HudMsg(iClient, 1, Float:{0.8, -1.0}, {255, 0, 0, 255}, {0, 255, 0, 255}, 0, 0.1, 0.1, SPEC_MESSAGE_DELAY + 0.1, 0.0, szMessage);
}
HudMsg(iClient, iChannel, const Float:fPosition[2], const iColor1[4], const iColor2[4], iEffect, Float:fFadeInTime, Float:fFadeOutTime, Float:fHoldTime, Float:fEffectTime, const String:szText[], any:...)
{
if(GetUserMessageType() != UM_Protobuf)
return false;
decl String:szBuffer[256];
VFormat(szBuffer, sizeof(szBuffer), szText, 12);
decl iClients[1];
iClients[0] = iClient;
new Handle:hMessage = StartMessageEx(g_msgHudMsg, iClients, 1);
PbSetInt(hMessage, "channel", iChannel);
PbSetVector2D(hMessage, "pos", fPosition);
PbSetColor(hMessage, "clr1", iColor1);
PbSetColor(hMessage, "clr2", iColor2);
PbSetInt(hMessage, "effect", iEffect);
PbSetFloat(hMessage, "fade_in_time", fFadeInTime);
PbSetFloat(hMessage, "fade_out_time", fFadeOutTime);
PbSetFloat(hMessage, "hold_time", fHoldTime);
PbSetFloat(hMessage, "fx_time", fEffectTime);
PbSetString(hMessage, "text", szBuffer);
EndMessage();
return true;
}