#pragma semicolon 1 #include #include #include #include #include "loghelper.inc" #pragma newdecls required Handle g_hFwd_SpectateByCommand; public Plugin myinfo = { name = "Spectate", description = "Adds a command to spectate specific players and removes broken spectate mode.", author = "Obus + BotoX", version = "1.1", 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 "(StringToInt(sSpecMode)); // Skip broken OBS_MODE_POI if (iObserverMode == OBS_MODE_POI) { ClientCommand(client, "cl_spec_mode %d", OBS_MODE_ROAMING); if(IsClientInGame(client) && !IsPlayerAlive(client)) 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; } if (IsPlayerAlive(client) && ZR_IsClientZombie(client)) { 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; } } if (!argc) { if (GetClientTeam(client) != CS_TEAM_SPECTATOR) { if ((IsPlayerAlive(client) && ZR_IsClientHuman(client)) && GetTeamAliveClientCount(CS_TEAM_T) > 0) LogPlayerEvent(client, "triggered", "switch_to_spec"); Call_StartForward(g_hFwd_SpectateByCommand); Call_PushCell(client); Call_Finish(); StripPlayerKnifes(client); ForcePlayerSuicide(client); ChangeClientTeam(client, CS_TEAM_SPECTATOR); } return Plugin_Handled; } char sTarget[MAX_TARGET_LENGTH]; GetCmdArg(1, sTarget, sizeof(sTarget)); int iTarget; if ((iTarget = FindTarget(client, sTarget, false, false)) <= 0) return Plugin_Handled; if (!IsPlayerAlive(iTarget)) { ReplyToCommand(client, "[SM] %t", "Target must be alive"); return Plugin_Handled; } if (GetClientTeam(client) != CS_TEAM_SPECTATOR) { if ((IsPlayerAlive(client) && ZR_IsClientHuman(client)) && GetTeamAliveClientCount(CS_TEAM_T) > 0) LogPlayerEvent(client, "triggered", "switch_to_spec"); Call_StartForward(g_hFwd_SpectateByCommand); Call_PushCell(client); Call_Finish(); StripPlayerKnifes(client); ForcePlayerSuicide(client); ChangeClientTeam(client, CS_TEAM_SPECTATOR); } SetEntPropEnt(client, Prop_Send, "m_hObserverTarget", iTarget); Obs_Mode iObserverMode = view_as(GetEntProp(client, Prop_Send, "m_iObserverMode")); // If the client is currently in free roaming then switch them to first person view if (iObserverMode == OBS_MODE_ROAMING) { ClientCommand(client, "cl_spec_mode %d", OBS_MODE_IN_EYE); SetEntProp(client, Prop_Send, "m_iObserverMode", OBS_MODE_IN_EYE); } PrintToChat(client, "\x01[SM] Spectating \x04%N\x01.", iTarget); return Plugin_Handled; } public Action Command_Suicide(int client, char[] command, int args) { if ((IsPlayerAlive(client) && ZR_IsClientHuman(client)) && GetTeamAliveClientCount(CS_TEAM_T) > 0) LogPlayerEvent(client, "triggered", "switch_to_spec"); return Plugin_Continue; } // Fix spec_goto crash exploit public Action Command_Goto(int client, const char[] command, int argc) { 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; } 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; } stock void StripPlayerKnifes(int client) { int w = -1; while ((w = GetPlayerWeaponSlot(client, CS_SLOT_KNIFE)) != -1) { if(IsValidEntity(w) && IsValidEdict(w)) { RemovePlayerItem(client, w); AcceptEntityInput(w, "Kill"); } } }