2016-05-10 15:15:16 +02:00
# pragma semicolon 1
# include <sourcemod>
# include <sdktools>
# include <cstrike>
2019-12-05 19:44:21 +01:00
# undef REQUIRE_PLUGIN
# tryinclude <zombiereloaded>
# define REQUIRE_PLUGIN
2018-08-07 22:39:17 +02:00
# include "loghelper.inc"
# pragma newdecls required
2016-05-10 15:15:16 +02:00
2019-12-05 19:44:21 +01:00
bool g_bZRLoaded ;
2018-11-21 00:24:07 +01:00
Handle g_hFwd_SpectateByCommand ;
2019-11-11 00:16:20 +01:00
bool g_bRoundEnd ;
2016-05-10 15:15:16 +02:00
public Plugin myinfo =
{
name = " Spectate " ,
description = " Adds a command to spectate specific players and removes broken spectate mode. " ,
author = " Obus + BotoX " ,
2017-01-22 15:20:08 +01:00
version = " 1.1 " ,
2016-05-10 15:15:16 +02:00
url = " "
}
// Spectator Movement modes (from smlib)
enum Obs_Mode
{
OBS_MODE_NONE = 0 , // not in spectator mode
OBS_MODE_DEATHCAM , // special mode for death cam animation
OBS_MODE_FREEZECAM , // zooms to a target, and freeze-frames on them
OBS_MODE_FIXED , // view from a fixed camera position
OBS_MODE_IN_EYE , // follow a player in first person view
OBS_MODE_CHASE , // follow a player in third person view
OBS_MODE_POI , // PASSTIME point of interest - game objective, big fight, anything interesting; added in the middle of the enum due to tons of hard-coded "<ROAMING" enum compares
OBS_MODE_ROAMING , // free roaming
NUM_OBSERVER_MODES
} ;
public void OnPluginStart ( )
{
LoadTranslations ( " common.phrases " ) ;
RegConsoleCmd ( " sm_spectate " , Command_Spectate , " Spectate a player. " ) ;
RegConsoleCmd ( " sm_spec " , Command_Spectate , " Spectate a player. " ) ;
2017-01-22 15:20:08 +01:00
2019-07-28 11:39:16 +02:00
AddCommandListener ( Command_Suicide , " spectate " ) ;
AddCommandListener ( Command_Suicide , " kill " ) ;
AddCommandListener ( Command_Goto , " spec_goto " ) ;
2018-11-21 00:24:07 +01:00
g_hFwd_SpectateByCommand = CreateGlobalForward ( " OnPlayerSwitchedToSpectateByCommand " , ET_Ignore , Param_Cell ) ;
2019-11-11 00:16:20 +01:00
HookEvent ( " round_start " , OnRoundStart , EventHookMode_Post ) ;
HookEvent ( " round_end " , OnRoundEnd , EventHookMode_Pre ) ;
2016-05-10 15:15:16 +02:00
}
2019-12-05 19:44:21 +01:00
public void OnAllPluginsLoaded ( )
{
g_bZRLoaded = LibraryExists ( " zombiereloaded " ) ;
}
public void OnLibraryAdded ( const char [ ] sName )
{
if ( strcmp ( sName , " zombiereloaded " , false ) = = 0 )
g_bZRLoaded = true ;
}
public void OnLibraryRemoved ( const char [ ] sName )
{
if ( strcmp ( sName , " zombiereloaded " , false ) = = 0 )
g_bZRLoaded = false ;
}
2018-08-07 22:39:17 +02:00
public void OnMapStart ( )
{
GetTeams ( ) ;
}
2019-11-11 00:16:20 +01:00
public void OnRoundStart ( Event hEvent , const char [ ] sName , bool bDontBroadcast )
{
g_bRoundEnd = false ;
}
public void OnRoundEnd ( Event hEvent , const char [ ] sName , bool bDontBroadcast )
{
g_bRoundEnd = true ;
}
2016-05-10 15:15:16 +02:00
public void OnClientSettingsChanged ( int client )
{
static char sSpecMode [ 8 ] ;
GetClientInfo ( client , " cl_spec_mode " , sSpecMode , sizeof ( sSpecMode ) ) ;
Obs_Mode iObserverMode = view_as < Obs_Mode > ( StringToInt ( sSpecMode ) ) ;
// Skip broken OBS_MODE_POI
if ( iObserverMode = = OBS_MODE_POI )
{
ClientCommand ( client , " cl_spec_mode %d " , OBS_MODE_ROAMING ) ;
2016-05-19 16:12:34 +02:00
if ( IsClientInGame ( client ) & & ! IsPlayerAlive ( client ) )
2016-05-10 15:15:16 +02:00
SetEntProp ( client , Prop_Send , " m_iObserverMode " , OBS_MODE_ROAMING ) ;
}
}
public Action Command_Spectate ( int client , int argc )
{
if ( ! client )
{
PrintToServer ( " [SM] Cannot use command from server console. " ) ;
return Plugin_Handled ;
}
2019-12-05 19:44:21 +01:00
# if defined _zr_included
if ( g_bZRLoaded & & IsPlayerAlive ( client ) & & ZR_IsClientZombie ( client ) )
2017-05-20 05:08:31 +02:00
{
bool bOnlyZombie = true ;
for ( int i = 1 ; i < = MaxClients ; i + + )
{
if ( i ! = client & & IsClientInGame ( i ) & & IsPlayerAlive ( i ) & & ZR_IsClientZombie ( i ) )
{
bOnlyZombie = false ;
break ;
}
}
if ( bOnlyZombie )
{
PrintToChat ( client , " [SM] Can not switch to spectate as the last zombie! " ) ;
return Plugin_Handled ;
}
}
2019-12-05 19:44:21 +01:00
# endif
2017-05-20 05:08:31 +02:00
2016-05-10 15:15:16 +02:00
if ( ! argc )
{
if ( GetClientTeam ( client ) ! = CS_TEAM_SPECTATOR )
{
2019-12-05 19:44:21 +01:00
# if defined _zr_included
if ( g_bZRLoaded & & IsPlayerAlive ( client ) & & ZR_IsClientHuman ( client ) & & GetTeamAliveClientCount ( CS_TEAM_T ) > 0 & & ! g_bRoundEnd )
2018-08-07 22:39:17 +02:00
LogPlayerEvent ( client , " triggered " , " switch_to_spec " ) ;
2019-12-05 19:44:21 +01:00
# endif
2018-08-07 22:39:17 +02:00
2018-11-21 00:24:07 +01:00
Call_StartForward ( g_hFwd_SpectateByCommand ) ;
Call_PushCell ( client ) ;
Call_Finish ( ) ;
2019-07-28 11:39:16 +02:00
2019-07-28 11:59:57 +02:00
StripPlayerKnifes ( client ) ;
2016-05-10 15:15:16 +02:00
ForcePlayerSuicide ( client ) ;
ChangeClientTeam ( client , CS_TEAM_SPECTATOR ) ;
}
return Plugin_Handled ;
}
char sTarget [ MAX_TARGET_LENGTH ] ;
GetCmdArg ( 1 , sTarget , sizeof ( sTarget ) ) ;
int iTarget ;
2016-05-10 23:05:52 +02:00
if ( ( iTarget = FindTarget ( client , sTarget , false , false ) ) < = 0 )
2016-05-10 15:15:16 +02:00
return Plugin_Handled ;
if ( ! IsPlayerAlive ( iTarget ) )
{
ReplyToCommand ( client , " [SM] %t " , " Target must be alive " ) ;
return Plugin_Handled ;
}
if ( GetClientTeam ( client ) ! = CS_TEAM_SPECTATOR )
{
2019-12-05 19:44:21 +01:00
# if defined _zr_included
if ( g_bZRLoaded & & IsPlayerAlive ( client ) & & ZR_IsClientHuman ( client ) & & GetTeamAliveClientCount ( CS_TEAM_T ) > 0 & & ! g_bRoundEnd )
2018-08-07 22:39:17 +02:00
LogPlayerEvent ( client , " triggered " , " switch_to_spec " ) ;
2019-12-05 19:44:21 +01:00
# endif
2018-08-07 22:39:17 +02:00
2018-11-21 00:24:07 +01:00
Call_StartForward ( g_hFwd_SpectateByCommand ) ;
Call_PushCell ( client ) ;
Call_Finish ( ) ;
2019-07-28 11:39:16 +02:00
2019-07-28 11:59:57 +02:00
StripPlayerKnifes ( client ) ;
2016-05-10 15:15:16 +02:00
ForcePlayerSuicide ( client ) ;
ChangeClientTeam ( client , CS_TEAM_SPECTATOR ) ;
}
SetEntPropEnt ( client , Prop_Send , " m_hObserverTarget " , iTarget ) ;
Obs_Mode iObserverMode = view_as < Obs_Mode > ( GetEntProp ( client , Prop_Send , " m_iObserverMode " ) ) ;
2019-07-28 11:39:16 +02:00
2016-05-10 15:15:16 +02:00
// If the client is currently in free roaming then switch them to first person view
if ( iObserverMode = = OBS_MODE_ROAMING )
{
2019-07-28 11:39:16 +02:00
ClientCommand ( client , " cl_spec_mode %d " , OBS_MODE_IN_EYE ) ;
2016-05-10 15:15:16 +02:00
SetEntProp ( client , Prop_Send , " m_iObserverMode " , OBS_MODE_IN_EYE ) ;
}
PrintToChat ( client , " \x01 [SM] Spectating \x04 %N \x01 . " , iTarget ) ;
return Plugin_Handled ;
}
2017-01-22 15:20:08 +01:00
2019-07-28 11:39:16 +02:00
public Action Command_Suicide ( int client , char [ ] command , int args )
2018-11-21 00:24:07 +01:00
{
2019-12-05 19:44:21 +01:00
# if defined _zr_included
if ( g_bZRLoaded & & IsPlayerAlive ( client ) & & ZR_IsClientHuman ( client ) & & GetTeamAliveClientCount ( CS_TEAM_T ) > 0 & & ! g_bRoundEnd )
2018-11-21 00:24:07 +01:00
LogPlayerEvent ( client , " triggered " , " switch_to_spec " ) ;
2019-12-05 19:44:21 +01:00
# endif
2018-11-21 00:24:07 +01:00
return Plugin_Continue ;
}
2017-01-22 15:20:08 +01:00
// Fix spec_goto crash exploit
2019-07-28 11:39:16 +02:00
public Action Command_Goto ( int client , const char [ ] command , int argc )
2017-01-22 15:20:08 +01:00
{
if ( argc = = 5 )
{
for ( int i = 1 ; i < = 3 ; i + + )
{
char sArg [ 64 ] ;
GetCmdArg ( i , sArg , 64 ) ;
float fArg = StringToFloat ( sArg ) ;
if ( FloatAbs ( fArg ) > 5000000000.0 )
{
PrintToServer ( " %d -> %f > %f " , i , FloatAbs ( fArg ) , 5000000000.0 ) ;
return Plugin_Handled ;
}
}
}
return Plugin_Continue ;
}
2018-08-07 22:39:17 +02:00
stock int GetTeamAliveClientCount ( int iTeam )
{
int ret = 0 ;
for ( int i = 1 ; i < = MaxClients ; i + + )
{
if ( ! IsClientInGame ( i ) | | GetClientTeam ( i ) ! = iTeam )
continue ;
if ( ! IsPlayerAlive ( i ) )
continue ;
ret + + ;
}
return ret ;
2019-07-28 11:39:16 +02:00
}
2019-07-28 11:59:57 +02:00
stock void StripPlayerKnifes ( int client )
2019-07-28 11:39:16 +02:00
{
2019-07-28 11:59:57 +02:00
int w = - 1 ;
2019-07-28 11:39:16 +02:00
2019-07-28 11:59:57 +02:00
while ( ( w = GetPlayerWeaponSlot ( client , CS_SLOT_KNIFE ) ) ! = - 1 )
{
if ( IsValidEntity ( w ) & & IsValidEdict ( w ) )
2019-07-28 11:39:16 +02:00
{
2019-07-28 11:59:57 +02:00
RemovePlayerItem ( client , w ) ;
AcceptEntityInput ( w , " Kill " ) ;
2019-07-28 11:39:16 +02:00
}
}
2019-12-05 19:44:21 +01:00
}