mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-05 20:23:20 +00:00
Merge branch 'master' of github.com:Jackzmc/sourcemod-plugins
This commit is contained in:
commit
5b60b8a95f
34 changed files with 2116 additions and 236 deletions
BIN
plugins/Block3Person.smx
Normal file
BIN
plugins/Block3Person.smx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
plugins/l4d2_baseball.smx
Normal file
BIN
plugins/l4d2_baseball.smx
Normal file
Binary file not shown.
BIN
plugins/l4d2_forceset.smx
Normal file
BIN
plugins/l4d2_forceset.smx
Normal file
Binary file not shown.
BIN
plugins/l4d2_witch_force_attack_cmd.smx
Normal file
BIN
plugins/l4d2_witch_force_attack_cmd.smx
Normal file
Binary file not shown.
BIN
plugins/l4d_anti_rush.smx
Normal file
BIN
plugins/l4d_anti_rush.smx
Normal file
Binary file not shown.
BIN
plugins/l4d_sm_respawn.smx
Normal file
BIN
plugins/l4d_sm_respawn.smx
Normal file
Binary file not shown.
BIN
plugins/sm_player_recorder.smx
Normal file
BIN
plugins/sm_player_recorder.smx
Normal file
Binary file not shown.
BIN
plugins/spray_control.smx
Normal file
BIN
plugins/spray_control.smx
Normal file
Binary file not shown.
BIN
pluginsl4d2_extraplayeritems.smx
Normal file
BIN
pluginsl4d2_extraplayeritems.smx
Normal file
Binary file not shown.
123
scripting/Block3Person.sp
Normal file
123
scripting/Block3Person.sp
Normal file
|
@ -0,0 +1,123 @@
|
|||
#define PLUGIN_VERSION "1.1"
|
||||
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
#include <sourcemod>
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "Block3person",
|
||||
author = "Dragokas",
|
||||
description = "Block 3-rd person view by creating blindness",
|
||||
version = PLUGIN_VERSION,
|
||||
url = "https://github.com/dragokas"
|
||||
}
|
||||
|
||||
bool aBlinded[MAXPLAYERS];
|
||||
UserMsg g_FadeUserMsgId;
|
||||
|
||||
static const int BLIND_DURATION = 50;
|
||||
|
||||
|
||||
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
|
||||
{
|
||||
char sGameName[12];
|
||||
GetGameFolderName(sGameName, sizeof(sGameName));
|
||||
if( strcmp(sGameName, "left4dead", false) )
|
||||
{
|
||||
strcopy(error, err_max, "Plugin only supports Left 4 Dead 1.");
|
||||
return APLRes_SilentFailure;
|
||||
}
|
||||
g_FadeUserMsgId = GetUserMessageId("Fade");
|
||||
if (g_FadeUserMsgId == INVALID_MESSAGE_ID) {
|
||||
strcopy(error, err_max, "Cannot find Fade user message ID.");
|
||||
return APLRes_SilentFailure;
|
||||
}
|
||||
return APLRes_Success;
|
||||
}
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
LoadTranslations("Block3Person.phrases");
|
||||
HookEvent("round_start", OnRoundStart, EventHookMode_PostNoCopy);
|
||||
}
|
||||
|
||||
public Action OnRoundStart(Event event, const char[] name, bool dontBroadcast)
|
||||
{
|
||||
CreateTimer(0.9, Timer_CheckClientViewState, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action Timer_CheckClientViewState(Handle timer)
|
||||
{
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
if(IsClientInGame(i) && GetClientTeam(i) == 2 && !IsFakeClient(i) && IsPlayerAlive(i))
|
||||
QueryClientConVar(i, "c_thirdpersonshoulder", QueryClientConVarCallback);
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public void QueryClientConVarCallback(QueryCookie cookie, int client, ConVarQueryResult result, const char[] sCvarName, const char[] bCvarValue)
|
||||
{
|
||||
if (StringToInt(bCvarValue) != 0) {
|
||||
if (!aBlinded[client]) {
|
||||
aBlinded[client] = true;
|
||||
BlindClient(client, true);
|
||||
PrintHintText(client, "%t", "Blind_Warning");
|
||||
}
|
||||
} else {
|
||||
if (aBlinded[client]) {
|
||||
aBlinded[client] = false;
|
||||
PrintHintText(client, "%t", "Unblind_tip");
|
||||
BlindClient(client, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlindClient(int target, bool bDoBlind = true)
|
||||
{
|
||||
int targets[2];
|
||||
targets[0] = target;
|
||||
|
||||
int holdtime;
|
||||
|
||||
int flags;
|
||||
if (!bDoBlind)
|
||||
{
|
||||
flags = (0x0001 | 0x0010);
|
||||
holdtime = 10000;
|
||||
}
|
||||
else
|
||||
{
|
||||
flags = (0x0002 | 0x0008);
|
||||
holdtime = 10;
|
||||
}
|
||||
|
||||
int color[4] = { 0, 0, 0, 0 };
|
||||
color[3] = 255;
|
||||
|
||||
Handle message = StartMessageEx(g_FadeUserMsgId, targets, 1);
|
||||
if (GetUserMessageType() == UM_Protobuf)
|
||||
{
|
||||
Protobuf pb = UserMessageToProtobuf(message);
|
||||
pb.SetInt("duration", BLIND_DURATION);
|
||||
pb.SetInt("hold_time", holdtime);
|
||||
pb.SetInt("flags", flags);
|
||||
pb.SetColor("clr", color);
|
||||
}
|
||||
else
|
||||
{
|
||||
BfWrite bf = UserMessageToBfWrite(message);
|
||||
bf.WriteShort(BLIND_DURATION);
|
||||
bf.WriteShort(holdtime);
|
||||
bf.WriteShort(flags);
|
||||
bf.WriteByte(color[0]);
|
||||
bf.WriteByte(color[1]);
|
||||
bf.WriteByte(color[2]);
|
||||
bf.WriteByte(color[3]);
|
||||
}
|
||||
|
||||
EndMessage();
|
||||
}
|
|
@ -95,7 +95,7 @@ public Action Command_GiveOthersBMP(int client, int args) {
|
|||
client,
|
||||
target_list,
|
||||
MAXPLAYERS,
|
||||
COMMAND_FILTER_ALIVE, /* Only allow alive players */
|
||||
COMMAND_FILTER_ALIVE,
|
||||
target_name,
|
||||
sizeof(target_name),
|
||||
tn_is_ml)) <= 0)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
//#define DEBUG
|
||||
#define DEBUG 1
|
||||
|
||||
#define PLUGIN_VERSION "1.0"
|
||||
|
||||
|
@ -22,7 +22,8 @@ static ConVar hLaserNotice, hFinaleTimer, hFFNotice, hMPGamemode, hPingDropThres
|
|||
static int iFinaleStartTime, botDropMeleeWeapon[MAXPLAYERS+1], iHighPingCount[MAXPLAYERS+1], reserveMode;
|
||||
static bool isHighPingIdle[MAXPLAYERS+1], isL4D1Survivors;
|
||||
static Handle hTakeOverBot, hGoAwayFromKeyboard;
|
||||
static char lastSound[MAXPLAYERS+1][64];
|
||||
static StringMap SteamIDs;
|
||||
static char lastSound[MAXPLAYERS+1][64], gamemode[32];
|
||||
|
||||
static float OUT_OF_BOUNDS[3] = {0.0, -1000.0, 0.0};
|
||||
|
||||
|
@ -66,7 +67,6 @@ public void OnPluginStart() {
|
|||
hFinaleTimer = CreateConVar("sm_time_finale", "0.0", "Record the time it takes to complete finale. 0 -> OFF, 1 -> Gauntlets Only, 2 -> All finales", FCVAR_NONE, true, 0.0, true, 2.0);
|
||||
hFFNotice = CreateConVar("sm_ff_notice", "0.0", "Notify players if a FF occurs. 0 -> Disabled, 1 -> In chat, 2 -> In Hint text", FCVAR_NONE, true, 0.0, true, 2.0);
|
||||
hPingDropThres = CreateConVar("sm_autoidle_ping_max", "0.0", "The highest ping a player can have until they will automatically go idle.\n0=OFF, Min is 30", FCVAR_NONE, true, 0.0, true, 1000.0);
|
||||
hMPGamemode = FindConVar("mp_gamemode");
|
||||
hForceSurvivorSet = FindConVar("l4d_force_survivorset");
|
||||
|
||||
hFFNotice.AddChangeHook(CVC_FFNotice);
|
||||
|
@ -75,6 +75,12 @@ public void OnPluginStart() {
|
|||
}
|
||||
|
||||
LasersUsed = new ArrayList(1, 0);
|
||||
SteamIDs = new StringMap();
|
||||
|
||||
ConVar hGamemode = FindConVar("mp_gamemode");
|
||||
hGamemode.GetString(gamemode, sizeof(gamemode));
|
||||
hGamemode.AddChangeHook(Event_GamemodeChange);
|
||||
Event_GamemodeChange(hGamemode, gamemode, gamemode);
|
||||
|
||||
HookEvent("player_use", Event_PlayerUse);
|
||||
HookEvent("round_end", Event_RoundEnd);
|
||||
|
@ -111,25 +117,48 @@ public void OnPluginStart() {
|
|||
RegAdminCmd("sm_perms", Command_SetServerPermissions, ADMFLAG_KICK, "Sets the server's permissions.");
|
||||
RegAdminCmd("sm_permissions", Command_SetServerPermissions, ADMFLAG_KICK, "Sets the server's permissions.");
|
||||
RegConsoleCmd("sm_pmodels", Command_ListClientModels, "Lists all player's models");
|
||||
RegAdminCmd("sm_skipoutro", Command_SkipOutro, ADMFLAG_KICK, "Skips the outro");
|
||||
|
||||
CreateTimer(8.0, Timer_CheckPlayerPings, _, TIMER_REPEAT);
|
||||
}
|
||||
|
||||
public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[] newValue) {
|
||||
cvar.GetString(gamemode, sizeof(gamemode));
|
||||
}
|
||||
|
||||
public void OnClientConnected(int client) {
|
||||
if(!IsFakeClient(client) && reserveMode == 1) {
|
||||
PrintToChatAll("%N is connecting", client);
|
||||
PrintChatToAdmins("%N is connecting", client);
|
||||
}
|
||||
}
|
||||
|
||||
stock void PrintChatToAdmins(const char[] format, any ...) {
|
||||
char buffer[254];
|
||||
VFormat(buffer, sizeof(buffer), format, 2);
|
||||
for(int i = 1; i < MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i)) {
|
||||
AdminId admin = GetUserAdmin(i);
|
||||
if(admin != INVALID_ADMIN_ID) {
|
||||
PrintToChat(i, "%s", buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
PrintToServer("%s", buffer);
|
||||
}
|
||||
|
||||
|
||||
public void OnClientPostAdminCheck(int client) {
|
||||
if(!IsFakeClient(client)) {
|
||||
if(reserveMode == 2) {
|
||||
if(GetUserAdmin(client) == INVALID_ADMIN_ID) {
|
||||
static char auth[32];
|
||||
GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth));
|
||||
if(reserveMode == 3 || (reserveMode == 2 && GetUserAdmin(client) == INVALID_ADMIN_ID)) {
|
||||
int index;
|
||||
if(!SteamIDs.GetValue(auth, index)) {
|
||||
KickClient(client, "Sorry, server is reserved");
|
||||
return;
|
||||
}
|
||||
}else if(reserveMode == 3) {
|
||||
KickClient(client, "Sorry, server is reserved");
|
||||
}
|
||||
SteamIDs.SetValue(auth, client);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,7 +168,7 @@ public Action Command_SetServerPermissions(int client, int args) {
|
|||
GetCmdArg(1, arg1, sizeof(arg1));
|
||||
if(StrEqual(arg1, "public", false)) {
|
||||
reserveMode = 0;
|
||||
}else if(StrContains(arg1, "notice", false) > -1) {
|
||||
}else if(StrContains(arg1, "noti", false) > -1) {
|
||||
reserveMode = 1;
|
||||
}else if(StrContains(arg1, "admin", false) > -1) {
|
||||
reserveMode = 2;
|
||||
|
@ -158,6 +187,7 @@ public Action Command_SetServerPermissions(int client, int args) {
|
|||
|
||||
|
||||
public Action Timer_CheckPlayerPings(Handle timer) {
|
||||
if(StrEqual(gamemode, "hideandseek")) return Plugin_Continue;
|
||||
if(hPingDropThres.IntValue != 0) {
|
||||
for (int i = 1; i <= MaxClients; i++ ) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i) && IsPlayerAlive(i) && GetClientTeam(i) > 1) {
|
||||
|
@ -256,7 +286,14 @@ public Action Command_SwapPlayer(int client, int args) {
|
|||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
//TODO: Implement idle bot support
|
||||
public Action Command_SkipOutro(int client, int args) {
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i)) {
|
||||
ClientCommand(i, "skipouttro");
|
||||
}
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
public Action Command_ListClientModels(int client, int args) {
|
||||
char model[64];
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
|
@ -359,7 +396,23 @@ public Action Command_SetClientModel(int client, int args) {
|
|||
GetCmdArg(2, arg2, sizeof(arg2));
|
||||
GetCmdArg(3, arg3, sizeof(arg3));
|
||||
|
||||
char modelPath[64];
|
||||
// If args sm_model <survivor> -> sm_model <self> <model>
|
||||
static char modelPath[64];
|
||||
if(args == 1) {
|
||||
int modelID = GetSurvivorId(arg1, false);
|
||||
if(modelID != -1) {
|
||||
int team = GetClientTeam(client);
|
||||
if(team != 2 && team != 4) {
|
||||
ReplyToCommand(client, "You must be a survivor.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
GetSurvivorModel(modelID, modelPath, sizeof(modelPath));
|
||||
if(isL4D1Survivors && hForceSurvivorSet != null && hForceSurvivorSet.IntValue < 2) modelID = GetSurvivorId(arg2, true);
|
||||
SetCharacter(client, modelID, modelPath, false);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
}
|
||||
|
||||
int modelID = GetSurvivorId(arg2, false);
|
||||
if(modelID == -1) {
|
||||
ReplyToCommand(client, "Invalid survivor type entered. Case-sensitive, full name required.");
|
||||
|
@ -386,34 +439,13 @@ public Action Command_SetClientModel(int client, int args) {
|
|||
ReplyToTargetError(client, target_count);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
int target;
|
||||
for (int i = 0; i < target_count; i++) {
|
||||
target = target_list[i];
|
||||
int target = target_list[i];
|
||||
bool keepModel = StrEqual(arg3, "keep", false);
|
||||
if(IsClientConnected(target) && IsClientInGame(target) && IsPlayerAlive(target)) {
|
||||
int team = GetClientTeam(target_list[i]);
|
||||
if(team == 2 || team == 4) {
|
||||
SetEntProp(target, Prop_Send, "m_survivorCharacter", modelID);
|
||||
SetEntityModel(target, modelPath);
|
||||
if (IsFakeClient(target)) {
|
||||
char name[32];
|
||||
GetSurvivorName(target, name, sizeof(name));
|
||||
SetClientInfo(target, "name", name);
|
||||
}
|
||||
UpdatePlayerIdentity(target, view_as<Character>(modelID), keepModel);
|
||||
|
||||
DataPack pack = new DataPack();
|
||||
pack.WriteCell(GetClientUserId(target));
|
||||
for(int slot = 0; slot <= 1; slot++) {
|
||||
int weapon = GetPlayerWeaponSlot(target, slot);
|
||||
if( weapon > 0 ) {
|
||||
SDKHooks_DropWeapon(target, weapon, NULL_VECTOR);
|
||||
pack.WriteCell(EntIndexToEntRef(weapon)); // Save last held weapon to switch back
|
||||
|
||||
}
|
||||
}
|
||||
CreateTimer(0.1, Timer_RequipWeapon, pack);
|
||||
|
||||
SetCharacter(target, modelID, modelPath, keepModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -421,6 +453,29 @@ public Action Command_SetClientModel(int client, int args) {
|
|||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
void SetCharacter(int target, int modelID, char[] modelPath, bool keepModel) {
|
||||
SetEntProp(target, Prop_Send, "m_survivorCharacter", modelID);
|
||||
SetEntityModel(target, modelPath);
|
||||
if (IsFakeClient(target)) {
|
||||
char name[32];
|
||||
GetSurvivorName(target, name, sizeof(name));
|
||||
SetClientInfo(target, "name", name);
|
||||
}
|
||||
UpdatePlayerIdentity(target, view_as<Character>(modelID), keepModel);
|
||||
|
||||
DataPack pack = new DataPack();
|
||||
pack.WriteCell(GetClientUserId(target));
|
||||
for(int slot = 0; slot <= 1; slot++) {
|
||||
int weapon = GetPlayerWeaponSlot(target, slot);
|
||||
if( weapon > 0 ) {
|
||||
SDKHooks_DropWeapon(target, weapon, NULL_VECTOR);
|
||||
pack.WriteCell(EntIndexToEntRef(weapon)); // Save last held weapon to switch back
|
||||
|
||||
}
|
||||
}
|
||||
CreateTimer(0.1, Timer_RequipWeapon, pack);
|
||||
}
|
||||
|
||||
public Action Cmd_SetSurvivor(int client, int args) {
|
||||
if(args < 1) {
|
||||
ReplyToCommand(client, "Usage: sm_surv <player> <survivor>");
|
||||
|
@ -492,6 +547,11 @@ public void OnClientDisconnect(int client) {
|
|||
TeleportEntity(botDropMeleeWeapon[client], pos, NULL_VECTOR, NULL_VECTOR);
|
||||
botDropMeleeWeapon[client] = -1;
|
||||
}
|
||||
if(!IsFakeClient(client)) {
|
||||
static char auth[32];
|
||||
GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth));
|
||||
SteamIDs.Remove(auth);
|
||||
}
|
||||
}
|
||||
int disabledItem[2048];
|
||||
//Can also probably prevent kit drop to pick them up
|
||||
|
@ -515,11 +575,11 @@ public Action Timer_AllowKitPickup(Handle h, int entity) {
|
|||
}
|
||||
public void OnMapStart() {
|
||||
AddFileToDownloadsTable("sound/custom/meow1.mp3");
|
||||
PrecacheSound("sound/custom/meow1.mp3");
|
||||
PrecacheSound("custom/meow1.mp3");
|
||||
AddFileToDownloadsTable("sound/custom/xen_teleport.mp3");
|
||||
PrecacheSound("sound/custom/xen_teleport.mp3");
|
||||
PrecacheSound("custom/xen_teleport.mp3");
|
||||
AddFileToDownloadsTable("sound/custom/mariokartmusic.mp3");
|
||||
PrecacheSound("sound/custom/mariokartmusic.mp3");
|
||||
PrecacheSound("custom/mariokartmusic.mp3");
|
||||
|
||||
HookEntityOutput("info_changelevel", "OnStartTouch", EntityOutput_OnStartTouchSaferoom);
|
||||
HookEntityOutput("trigger_changelevel", "OnStartTouch", EntityOutput_OnStartTouchSaferoom);
|
||||
|
@ -544,7 +604,7 @@ public void OnSceneStageChanged(int scene, SceneStages stage) {
|
|||
public Action Event_BotPlayerSwap(Event event, const char[] name, bool dontBroadcast) {
|
||||
int bot = GetClientOfUserId(event.GetInt("bot"));
|
||||
if(StrEqual(name, "player_bot_replace")) {
|
||||
//Bot replaced player
|
||||
//Bot replaced player, hook any drop events
|
||||
SDKHook(bot, SDKHook_WeaponDrop, Event_OnWeaponDrop);
|
||||
}else{
|
||||
//Player replaced a bot
|
||||
|
@ -562,10 +622,10 @@ public Action Event_BotPlayerSwap(Event event, const char[] name, bool dontBroad
|
|||
}
|
||||
}
|
||||
public Action Event_OnWeaponDrop(int client, int weapon) {
|
||||
if(!IsValidEntity(weapon)) return Plugin_Continue;
|
||||
char wpn[32];
|
||||
if(!IsValidEntity(weapon) || !IsFakeClient(client)) return Plugin_Continue;
|
||||
static char wpn[32];
|
||||
GetEdictClassname(weapon, wpn, sizeof(wpn));
|
||||
if(IsFakeClient(client) && StrEqual(wpn, "weapon_melee") && GetEntProp(client, Prop_Send, "m_humanSpectatorUserID") > 0) {
|
||||
if(StrEqual(wpn, "weapon_melee") && GetEntProp(client, Prop_Send, "m_humanSpectatorUserID") > 0) {
|
||||
#if defined DEBUG 0
|
||||
PrintToServer("Bot %N dropped melee weapon %s", client, wpn);
|
||||
#endif
|
||||
|
@ -607,9 +667,7 @@ public void EntityOutput_OnStartTouchSaferoom(const char[] output, int caller, i
|
|||
EquipPlayerWeapon(client, botDropMeleeWeapon[client]);
|
||||
botDropMeleeWeapon[client] = -1;
|
||||
}
|
||||
char currentGamemode[16];
|
||||
hMPGamemode.GetString(currentGamemode, sizeof(currentGamemode));
|
||||
if(StrEqual(currentGamemode, "tankrun", false)) {
|
||||
if(StrEqual(gamemode, "tankrun", false)) {
|
||||
if(!IsFakeClient(client)) {
|
||||
CreateTimer(1.0, Timer_TPBots, client, TIMER_FLAG_NO_MAPCHANGE);
|
||||
}
|
||||
|
@ -726,15 +784,6 @@ stock int GetAnyValidClient() {
|
|||
return -1;
|
||||
}
|
||||
|
||||
stock int GetRealClient(int client) {
|
||||
if(IsFakeClient(client)) {
|
||||
int realPlayer = GetClientOfUserId(GetEntProp(client, Prop_Send, "m_humanSpectatorUserID"));
|
||||
return realPlayer > 0 ? realPlayer : -1;
|
||||
}else{
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
||||
stock int GetIdleBot(int client) {
|
||||
for(int i = 1; i <= MaxClients; i++ ) {
|
||||
if(IsClientConnected(i) && HasEntProp(i, Prop_Send, "m_humanSpectatorUserID")) {
|
||||
|
|
84
scripting/disable_cameras.sp
Normal file
84
scripting/disable_cameras.sp
Normal file
|
@ -0,0 +1,84 @@
|
|||
#include <sourcemod>
|
||||
#include <sdktools_functions>
|
||||
#include <sdktools_entinput>
|
||||
|
||||
// Fixed issues:
|
||||
// - Server crash when kicking a bot who have been an active target of camera (point_viewcontrol_survivor)
|
||||
// - Multiple visual spectator bugs after team swap in finale
|
||||
|
||||
public void Event_round_start_pre_entity(Event event, const char[] name, bool dontBroadcast)
|
||||
{
|
||||
static char classes[][] = {
|
||||
"point_viewcontrol",
|
||||
"point_viewcontrol_survivor",
|
||||
"point_viewcontrol_multiplayer",
|
||||
};
|
||||
|
||||
for (int i = 0; i < sizeof(classes); i++) {
|
||||
int entity = INVALID_ENT_REFERENCE;
|
||||
while ((entity = FindEntityByClassname(entity, classes[i])) != INVALID_ENT_REFERENCE) {
|
||||
// Invoke a "Disable" input on camera entities to free all players
|
||||
// Doing so on round_start_pre_entity should help to not let map logic kick in too early
|
||||
AcceptEntityInput(entity, "Disable");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
{
|
||||
if (!IsClientInGame(client)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int viewEntity = GetEntPropEnt(client, Prop_Send, "m_hViewEntity");
|
||||
if (!IsValidEdict(viewEntity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char cls[64];
|
||||
GetEdictClassname(viewEntity, cls, sizeof(cls));
|
||||
if (strncmp(cls, "point_viewcontrol", 17) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Matches CSurvivorCamera, CTriggerCamera
|
||||
if (strcmp(cls[17], "_survivor") == 0 || cls[17] == '\0') {
|
||||
// Disable entity to prevent CMoveableCamera::FollowTarget to cause a crash
|
||||
// m_hTargetEnt EHANDLE is not checked for existence and can be NULL
|
||||
// CBaseEntity::GetAbsAngles being called on causing a crash
|
||||
AcceptEntityInput(viewEntity, "Disable");
|
||||
}
|
||||
|
||||
// Matches CTriggerCameraMultiplayer
|
||||
if (strcmp(cls[17], "_multiplayer") == 0) {
|
||||
AcceptEntityInput(viewEntity, "RemovePlayer", client);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
HookEvent("round_start_pre_entity", Event_round_start_pre_entity, EventHookMode_PostNoCopy);
|
||||
}
|
||||
|
||||
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
|
||||
{
|
||||
switch (GetEngineVersion()) {
|
||||
case Engine_Left4Dead2, Engine_Left4Dead:
|
||||
{
|
||||
return APLRes_Success;
|
||||
}
|
||||
}
|
||||
|
||||
strcopy(error, err_max, "Plugin only supports Left 4 Dead and Left 4 Dead 2.");
|
||||
|
||||
return APLRes_SilentFailure;
|
||||
}
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "[L4D/2] Unlink Camera Entities",
|
||||
author = "shqke",
|
||||
description = "Frees cached players from camera entity",
|
||||
version = "1.1",
|
||||
url = "https://github.com/shqke/sp_public"
|
||||
};
|
|
@ -174,12 +174,14 @@ public void DB_OnConnectCheck(Database db, DBResultSet results, const char[] err
|
|||
LogMessage("%N is banned: %s", client, reason);
|
||||
if(hKickType.IntValue > 0) {
|
||||
if(reasonResult == DBVal_Data)
|
||||
KickClient(client, "You have been banned: %s", reason);
|
||||
KickClient(client, "You have been banned:\n%s", reason);
|
||||
else
|
||||
KickClient(client, "You have been banned from this server.");
|
||||
static char query[128];
|
||||
g_db.Format(query, sizeof(query), "UPDATE bans SET times_tried=times_tried+1 WHERE steamid = '%s'", steamid);
|
||||
g_db.Query(DB_OnBanQuery, query);
|
||||
} else {
|
||||
PrintChatToAdmins("%N was banned from this server for: \"%s\"", client, reason);
|
||||
return;
|
||||
}
|
||||
static char query[128];
|
||||
g_db.Format(query, sizeof(query), "UPDATE bans SET times_tried=times_tried+1 WHERE steamid = '%s'", steamid);
|
||||
|
|
|
@ -138,16 +138,30 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
|
|||
L4D2Infected class = view_as<L4D2Infected>(GetEntProp(attacker, Prop_Send, "m_zombieClass"));
|
||||
// Check for any existing victims
|
||||
int existingTarget = GetClientOfUserId(g_iAttackerTarget[attacker]);
|
||||
if(existingTarget > 0 && IsPlayerAlive(existingTarget)) {
|
||||
if(gInstaSpecialMagnet[existingTarget] > 0) {
|
||||
curTarget = existingTarget;
|
||||
return Plugin_Changed;
|
||||
} else if(class == L4D2Infected_Tank && (!IsPlayerIncapped(existingTarget) || hMagnetTargetMode.IntValue & 2) && WillMagnetRun(Trolls[tankMagnetID], existingTarget)) {
|
||||
curTarget = existingTarget;
|
||||
return Plugin_Changed;
|
||||
}else if(class != L4D2Infected_Tank && (!IsPlayerIncapped(existingTarget) || hMagnetTargetMode.IntValue & 1) && WillMagnetRun(Trolls[spMagnetID], existingTarget)) {
|
||||
curTarget = existingTarget;
|
||||
return Plugin_Changed;
|
||||
if(existingTarget > 0) {
|
||||
if(IsPlayerAlive(existingTarget)) {
|
||||
// Insta-specials ALWAYS target
|
||||
if(gInstaSpecialMagnet[existingTarget] > 0) {
|
||||
curTarget = existingTarget;
|
||||
return Plugin_Changed;
|
||||
}
|
||||
// Stop targetting if no longer magnetted:
|
||||
if(class == L4D2Infected_Tank) {
|
||||
if(!Trolls[tankMagnetID].IsActive(existingTarget) || !WillMagnetRun(Trolls[tankMagnetID], existingTarget)) return Plugin_Continue;
|
||||
} else if(class != L4D2Infected_Tank) {
|
||||
if(!Trolls[spMagnetID].IsActive(existingTarget) || !WillMagnetRun(Trolls[spMagnetID], existingTarget)) return Plugin_Continue;
|
||||
}
|
||||
|
||||
// Only set target based on incap rules:
|
||||
if(class == L4D2Infected_Tank && (!IsPlayerIncapped(existingTarget) || hMagnetTargetMode.IntValue & 2) && WillMagnetRun(Trolls[tankMagnetID], existingTarget)) {
|
||||
curTarget = existingTarget;
|
||||
return Plugin_Changed;
|
||||
}else if(class != L4D2Infected_Tank && (!IsPlayerIncapped(existingTarget) || hMagnetTargetMode.IntValue & 1) && WillMagnetRun(Trolls[spMagnetID], existingTarget)) {
|
||||
curTarget = existingTarget;
|
||||
return Plugin_Changed;
|
||||
}
|
||||
} else {
|
||||
g_iAttackerTarget[attacker] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,13 +171,18 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
|
|||
int closestClient = -1;
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) {
|
||||
if(class == L4D2Infected_Tank && Trolls[tankMagnetID].IsActive(i) && !WillMagnetRun(Trolls[tankMagnetID], i)) continue;
|
||||
else if(class != L4D2Infected_Tank && Trolls[spMagnetID].IsActive(i) && !WillMagnetRun(Trolls[spMagnetID], i)) continue;
|
||||
if(class == L4D2Infected_Tank) {
|
||||
if(!Trolls[tankMagnetID].IsActive(i) || !WillMagnetRun(Trolls[tankMagnetID], i)) continue;
|
||||
} else if(class != L4D2Infected_Tank) {
|
||||
if(!Trolls[spMagnetID].IsActive(i) || !WillMagnetRun(Trolls[spMagnetID], i)) continue;
|
||||
}
|
||||
|
||||
if(IsPlayerIncapped(i)) {
|
||||
if((class == L4D2Infected_Tank && hMagnetTargetMode.IntValue & 2 == 0) || (class != L4D2Infected_Tank && hMagnetTargetMode.IntValue & 1 == 0)) continue;
|
||||
}
|
||||
|
||||
PrintToConsoleAll("[FTT/Debug] Adding possible magnet victim %N for %N", i, attacker);
|
||||
|
||||
GetClientAbsOrigin(i, survPos);
|
||||
float dist = GetVectorDistance(survPos, spPos, true);
|
||||
if(closestClient == -1 || dist < closestDistance) {
|
||||
|
@ -176,14 +195,14 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
|
|||
if(closestClient > 0) {
|
||||
g_iAttackerTarget[attacker] = GetClientUserId(closestClient);
|
||||
curTarget = closestClient;
|
||||
PrintToConsoleAll("[FTT] New target for %d: %N", attacker, curTarget);
|
||||
return Plugin_Changed;
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
bool WillMagnetRun(const Troll troll, int i) {
|
||||
if(troll.activeFlagClients[i] == 0) return true;
|
||||
if(troll.activeFlagClients[i] == 0) return false;
|
||||
|
||||
float cChance = 1.0;
|
||||
//Skip first bit as it is ('Always')
|
||||
if(troll.activeFlagClients[i] & 2) // 2nd: 50%
|
||||
|
@ -422,6 +441,7 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
|
|||
if(g_bPendingItemGive[client] && !(buttons & IN_ATTACK2)) {
|
||||
int target = GetClientAimTarget(client, true);
|
||||
if(target > -1) {
|
||||
ClientCommand(client, "slot5");
|
||||
buttons |= IN_ATTACK2;
|
||||
RequestFrame(StopItemGive, client);
|
||||
return Plugin_Changed;
|
||||
|
@ -509,16 +529,17 @@ public Action Event_TakeDamage(int victim, int& attacker, int& inflictor, float&
|
|||
if(hBotReverseFFDefend.IntValue > 0 && IsFakeClient(attacker) && shootAtTarget[attacker] == 0 && GetClientTeam(attacker) == 2 && GetClientTeam(victim) == 2) return Plugin_Stop;
|
||||
if(attacker != victim && hBotReverseFFDefend.IntValue > 0 && hBotReverseFFDefend.IntValue == 2 || GetUserAdmin(attacker) == INVALID_ADMIN_ID) {
|
||||
if(IsFakeClient(victim) && !IsFakeClient(attacker) && GetClientTeam(attacker) == 2 && GetClientTeam(victim) == 2) {
|
||||
|
||||
if(shootAtTarget[victim] == attacker) {
|
||||
shootAtTargetHP[attacker] -= RoundFloat(damage);
|
||||
shootAtTargetLoops[victim] += 4;
|
||||
return Plugin_Continue;
|
||||
} else if(shootAtTarget[victim] > 0) {
|
||||
// Don't switch, wait for timer to stop
|
||||
return Plugin_Continue;
|
||||
if(hBotDefendChance.IntValue >= GetRandomFloat()) {
|
||||
if(shootAtTarget[victim] == attacker) {
|
||||
shootAtTargetHP[attacker] -= RoundFloat(damage);
|
||||
shootAtTargetLoops[victim] += 4;
|
||||
return Plugin_Continue;
|
||||
} else if(shootAtTarget[victim] > 0) {
|
||||
// Don't switch, wait for timer to stop
|
||||
return Plugin_Continue;
|
||||
}
|
||||
SetBotTarget(attacker, victim, GetClientRealHealth(attacker) - RoundFloat(damage));
|
||||
}
|
||||
SetBotTarget(attacker, victim, GetClientRealHealth(attacker) - RoundFloat(damage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -531,7 +552,7 @@ public Action SoundHook(int[] clients, int& numClients, char sample[PLATFORM_MAX
|
|||
if(honkID == 0) honkID = GetTrollID("Honk / Meow");
|
||||
if(vocalGagID == 0) vocalGagID = GetTrollID("Vocalize Gag");
|
||||
|
||||
if(lastButtonUser > -1 && StrEqual(sample, "npc/mega_mob/mega_mob_incoming.wav")) {
|
||||
if(lastButtonUser > -1 && !IsFakeClient(lastButtonUser) && StrEqual(sample, "npc/mega_mob/mega_mob_incoming.wav")) {
|
||||
PrintToConsoleAll("CRESCENDO STARTED BY %N", lastButtonUser);
|
||||
#if defined DEBUG
|
||||
PrintToChatAll("CRESCENDO STARTED BY %N", lastButtonUser);
|
||||
|
@ -570,10 +591,9 @@ public Action Event_WitchVictimSet(Event event, const char[] name, bool dontBroa
|
|||
static int witchTrollID;
|
||||
if(witchTrollID == 0) witchTrollID = GetTrollID("Witch Magnet");
|
||||
|
||||
int witch = event.GetInt("witchid");
|
||||
int witch = event.GetInt("witchid"), closestClient = -1;
|
||||
float closestDistance, survPos[3], witchPos[3];
|
||||
GetEntPropVector(witch, Prop_Send, "m_vecOrigin", witchPos);
|
||||
int closestClient = -1;
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) {
|
||||
|
@ -682,3 +702,16 @@ int FindClosestVisibleClient(int source) {
|
|||
public bool TraceEntityFilterPlayer(int entity, int mask, any data) {
|
||||
return data != entity && entity <= MaxClients && GetClientTeam(entity) == 2 && IsPlayerAlive(entity);
|
||||
}
|
||||
|
||||
float iLastAntiRushEvent[MAXPLAYERS+1];
|
||||
public Action OnAntiRush(int client, int &type, float distance) {
|
||||
PrintToConsoleAll("[FTT] Antirush: %N (dist=%d) (GameTime=%f)", client, distance, GetGameTime());
|
||||
if(type == 3 && IsPlayerAlive(client) && !IsPlayerIncapped(client)) {
|
||||
if(GetGameTime() - iLastAntiRushEvent[client] > 30.0) {
|
||||
int special = GetRandomInt(0,6);
|
||||
iLastAntiRushEvent[client] = GetGameTime();
|
||||
SpawnSpecialNear(client, special);
|
||||
PrintToConsoleAll("[FTT] Spawning anti-rush special on %N (dist=%f) (special=%d)", client, distance, special);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ ConVar hMagnetTargetMode;
|
|||
ConVar hBadThrowHitSelf;
|
||||
ConVar hBotReverseFFDefend;
|
||||
ConVar hSbFriendlyFire;
|
||||
ConVar hBotDefendChance;
|
||||
|
||||
|
||||
bool g_bPendingItemGive[MAXPLAYERS+1];
|
||||
|
|
10
scripting/include/l4d2_detections.inc
Normal file
10
scripting/include/l4d2_detections.inc
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Called when a player takes two kits
|
||||
forward void OnDoubleKit(int client);
|
||||
// Called when a bile is thrown when no zombies around
|
||||
forward void OnNoHordeBileWaste(int client, int commons);
|
||||
|
||||
// Called when a door is closed within range of another player.
|
||||
// Victim will be the closest victim to the door, may be incorrect.
|
||||
forward void OnDoorCloseInFace(int client, int victim);
|
||||
// Called on saferoom doors, with a count. Resets after 20s of no door opening.
|
||||
forward void OnDoorCloseInFaceSaferoom(int client, int victim, int count);
|
|
@ -1,7 +1,7 @@
|
|||
enum PZDamage_Type {
|
||||
PZDamage_Killed,
|
||||
PZDamage_Killed, //0
|
||||
PZDamage_Incapped,
|
||||
PZDamage_Killed2,
|
||||
PZDamage_Killed2, //2
|
||||
PZDamage_Revived,
|
||||
PZDamage_Progress,
|
||||
PZDamage_DestroyedCan,
|
||||
|
|
|
@ -3,12 +3,13 @@
|
|||
#endif
|
||||
#define l4d2_weapons_inc_
|
||||
|
||||
#define GETWEAPONNAME(%0) (IsValidWeaponId(WeaponId:(%0)) ? (WeaponNames[_:(%0)]) : "")
|
||||
#define GETLONGWEAPONNAME(%0) (IsValidWeaponId(WeaponId:(%0)) ? (LongWeaponNames[_:(%0)]) : "")
|
||||
#define GETMELEEWEAPONNAME(%0) (IsValidWeaponId(MeleeWeaponId:(%0)) ? (MeleeWeaponNames[_:(%0)]) : "")
|
||||
#define GETLONGMELEEWEAPONNAME(%0) (IsValidWeaponId(MeleeWeaponId:(%0)) ? (LongMeleeWeaponNames[_:(%0)]) : "")
|
||||
#define GETWEAPONMODEL(%0) (HasValidWeaponModel(WeaponId:(%0)) ? (WeaponModels[_:(%0)]) : "")
|
||||
#define GETMELEEWEAPONMODEL(%0) (HasValidWeaponModel(MeleeWeaponId:(%0)) ? (MeleeWeaponModels[_:(%0)]) : "")
|
||||
#define GETWEAPONNAME(%0) (IsValidWeaponId(WeaponId (%0)) ? (WeaponNames[(%0)]) : "")
|
||||
#define GETLONGWEAPONNAME(%0) (IsValidWeaponId(WeaponId (%0)) ? (LongWeaponNames[(%0)]) : "")
|
||||
#define GETMELEEWEAPONNAME(%0) (IsValidWeaponId(MeleeWeaponId (%0)) ? (MeleeWeaponNames[(%0)]) : "")
|
||||
#define GETLONGMELEEWEAPONNAME(%0) (IsValidWeaponId(MeleeWeaponId (%0)) ? (LongMeleeWeaponNames[(%0)]) : "")
|
||||
#define GETWEAPONMODEL(%0) (HasValidWeaponModel(WeaponId (%0)) ? (WeaponModels[(%0)]) : "")
|
||||
#define GETMELEEWEAPONMODEL(%0) (HasValidWeaponModel(MeleeWeaponId (%0)) ? (MeleeWeaponModels[(%0)]) : "")
|
||||
|
||||
|
||||
// Weapon ID enumerations.
|
||||
// These values are *NOT* arbitrary!
|
||||
|
@ -92,8 +93,7 @@ enum MeleeWeaponId
|
|||
};
|
||||
|
||||
// Weapon names for each of the weapons, used in identification.
|
||||
const char WeaponNames[sizeof(WeaponId)][] =
|
||||
{
|
||||
char WeaponNames[56][] = {
|
||||
"weapon_none", "weapon_pistol", "weapon_smg", // 0
|
||||
"weapon_pumpshotgun", "weapon_autoshotgun", "weapon_rifle", // 3
|
||||
"weapon_hunting_rifle", "weapon_smg_silenced", "weapon_shotgun_chrome", // 6
|
||||
|
@ -116,8 +116,7 @@ const char WeaponNames[sizeof(WeaponId)][] =
|
|||
};
|
||||
|
||||
// Long weapon names
|
||||
const char LongWeaponNames[WeaponId][] =
|
||||
{
|
||||
char LongWeaponNames[56][] = {
|
||||
"None", "Pistol", "Uzi", // 0
|
||||
"Pump", "Autoshotgun", "M-16", // 3
|
||||
"Hunting Rifle", "Mac", "Chrome", // 6
|
||||
|
@ -160,7 +159,7 @@ char MeleeWeaponNames[MeleeWeaponId][] =
|
|||
};
|
||||
|
||||
// Long melee weapon names
|
||||
const char LongMeleeWeaponNames[MeleeWeaponId][] =
|
||||
char LongMeleeWeaponNames[MeleeWeaponId][] =
|
||||
{
|
||||
"None",
|
||||
"Knife",
|
||||
|
@ -181,8 +180,7 @@ const char LongMeleeWeaponNames[MeleeWeaponId][] =
|
|||
|
||||
// World weapon models for each of the weapons. Useful for making new weapon spawns.
|
||||
// Some models are left blank because no single model can be given, the model is known or none exist.
|
||||
const char WeaponModels[WeaponId][] =
|
||||
{
|
||||
char WeaponModels[56][] = {
|
||||
"",
|
||||
"/w_models/weapons/w_pistol_B.mdl",
|
||||
"/w_models/weapons/w_smg_uzi.mdl",
|
||||
|
@ -241,7 +239,7 @@ const char WeaponModels[WeaponId][] =
|
|||
""
|
||||
};
|
||||
|
||||
const char MeleeWeaponModels[MeleeWeaponId][] =
|
||||
char MeleeWeaponModels[15][] =
|
||||
{
|
||||
"",
|
||||
"/w_models/weapons/w_knife_t.mdl",
|
||||
|
@ -260,8 +258,7 @@ const char MeleeWeaponModels[MeleeWeaponId][] =
|
|||
"/weapons/melee/w_tonfa.mdl"
|
||||
};
|
||||
|
||||
const int WeaponSlots[WeaponId] =
|
||||
{
|
||||
int WeaponSlots[56] = {
|
||||
-1, // WEPID_NONE
|
||||
1, // WEPID_PISTOL
|
||||
0, // WEPID_SMG
|
||||
|
@ -335,17 +332,16 @@ static Handle hMeleeWeaponModelsTrie = INVALID_HANDLE;
|
|||
|
||||
stock void InitWeaponNamesTrie() {
|
||||
hWeaponNamesTrie = CreateTrie();
|
||||
for(new i = 0; i < _:WeaponId; i++)
|
||||
{
|
||||
SetTrieValue(hWeaponNamesTrie, WeaponNames[WeaponId:i], i);
|
||||
for(int i = 0; i < view_as<int>(WeaponId); i++) {
|
||||
SetTrieValue(hWeaponNamesTrie, WeaponNames[i], i);
|
||||
}
|
||||
|
||||
hMeleeWeaponNamesTrie = CreateTrie();
|
||||
hMeleeWeaponModelsTrie = CreateTrie();
|
||||
for (new i = 0; i < _:MeleeWeaponId; ++i)
|
||||
for (int i = 0; i < view_as<int>(MeleeWeaponId); ++i)
|
||||
{
|
||||
SetTrieValue(hMeleeWeaponNamesTrie, MeleeWeaponNames[MeleeWeaponId:i], i);
|
||||
SetTrieString(hMeleeWeaponModelsTrie, MeleeWeaponModels[MeleeWeaponId:i], MeleeWeaponNames[MeleeWeaponId:i]);
|
||||
SetTrieValue(hMeleeWeaponNamesTrie, MeleeWeaponNames[i], i);
|
||||
SetTrieString(hMeleeWeaponModelsTrie, MeleeWeaponModels[i], MeleeWeaponNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,13 +352,12 @@ stock void InitWeaponNamesTrie() {
|
|||
* @param wepid WeaponId to check for validity
|
||||
* @return True if wepid is valid, false otherwise.
|
||||
*/
|
||||
stock bool IsValidWeaponId({WeaponId, MeleeWeaponId} wepid, tagType = tagof(wepid))
|
||||
{
|
||||
if (tagType == tagof(MeleeWeaponId))
|
||||
{
|
||||
return MeleeWeaponId:wepid >= WEPID_MELEE_NONE && MeleeWeaponId:wepid < MeleeWeaponId;
|
||||
}
|
||||
return wepid >= WEPID_NONE && wepid < WeaponId;
|
||||
stock bool IsValidWeaponId(WeaponId wepid){
|
||||
return wepid != WEPID_NONE;
|
||||
}
|
||||
|
||||
stock bool IsValidMeleeWeaponId(MeleeWeaponId wepid) {
|
||||
return MeleeWeaponId:wepid >= WEPID_MELEE_NONE && MeleeWeaponId:wepid < MeleeWeaponId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -371,8 +366,7 @@ stock bool IsValidWeaponId({WeaponId, MeleeWeaponId} wepid, tagType = tagof(wepi
|
|||
* @param wepid WeaponId to get the slot for.
|
||||
* @return Slot number (0-4) or -1 for invalid WeaponId or no slot
|
||||
*/
|
||||
stock int GetSlotFromWeaponId(WeaponId wepid)
|
||||
{
|
||||
stock int GetSlotFromWeaponId(WeaponId wepid) {
|
||||
return IsValidWeaponId(wepid) ? WeaponSlots[wepid] : -1;
|
||||
}
|
||||
|
||||
|
@ -383,13 +377,14 @@ stock int GetSlotFromWeaponId(WeaponId wepid)
|
|||
* @param wepid WeaponId to check for a known weapon model for.
|
||||
* @return True if a valid weapon model exists for wepid, false otherwise.
|
||||
*/
|
||||
stock bool:HasValidWeaponModel({WeaponId, MeleeWeaponId}:wepid, tagType = tagof(wepid))
|
||||
{
|
||||
if (tagType == tagof(MeleeWeaponId))
|
||||
{
|
||||
stock bool HasValidWeaponModel(WeaponId wepid) {
|
||||
return IsValidWeaponId(wepid) && WeaponModels[wepid][0] != '\0';
|
||||
}
|
||||
|
||||
stock bool HasValidMeleeWeaponModel(MeleeWeaponId wepid) {
|
||||
if (tagType == tagof(MeleeWeaponId)) {
|
||||
return IsValidWeaponId(MeleeWeaponId:wepid) && MeleeWeaponModels[MeleeWeaponId:wepid][0] != '\0';
|
||||
}
|
||||
return IsValidWeaponId(wepid) && WeaponModels[wepid][0] != '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -398,16 +393,13 @@ stock bool:HasValidWeaponModel({WeaponId, MeleeWeaponId}:wepid, tagType = tagof(
|
|||
* @param weaponName Weapon name string to look up Id from
|
||||
* @return The corresponding WeaponId if found, else WEPID_NONE
|
||||
*/
|
||||
stock WeaponId WeaponNameToId(const char weaponName[])
|
||||
{
|
||||
new WeaponID:id;
|
||||
if(hWeaponNamesTrie == INVALID_HANDLE)
|
||||
{
|
||||
stock WeaponId WeaponNameToId(const char[] weaponName) {
|
||||
int id;
|
||||
if(hWeaponNamesTrie == INVALID_HANDLE) {
|
||||
InitWeaponNamesTrie();
|
||||
}
|
||||
if(GetTrieValue(hWeaponNamesTrie, weaponName, id))
|
||||
{
|
||||
return WeaponId:id;
|
||||
if(GetTrieValue(hWeaponNamesTrie, weaponName, id)) {
|
||||
return view_as<WeaponId>(id);
|
||||
}
|
||||
return WEPID_NONE;
|
||||
}
|
||||
|
@ -420,16 +412,12 @@ stock WeaponId WeaponNameToId(const char weaponName[])
|
|||
* @param length Max length which can be written to the buffer.
|
||||
* @return Number of bytes written to buffer, or 0 for invalid weaponId.
|
||||
*/
|
||||
stock GetWeaponName({WeaponId, MeleeWeaponId}:wepid, String:nameBuffer[], length, tagType = tagof(wepid))
|
||||
{
|
||||
if (tagType == tagof(MeleeWeaponId))
|
||||
{
|
||||
strcopy(nameBuffer, length, GETMELEEWEAPONNAME(wepid));
|
||||
}
|
||||
else
|
||||
{
|
||||
strcopy(nameBuffer, length, GETWEAPONNAME(wepid));
|
||||
}
|
||||
stock int GetWeaponName(WeaponId wepid, char[] nameBuffer, int length) {
|
||||
return IsValidWeaponId(wepid) ? strcopy(nameBuffer, length, WeaponNames[wepid]) : 0;
|
||||
}
|
||||
|
||||
stock int GetMeleeWeaponName(WeaponId wepid, char[] nameBuffer, int length) {
|
||||
return IsValidWeaponId(wepid) ? strcopy(nameBuffer, length, MeleeWeaponNames[wepid]) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -440,16 +428,12 @@ stock GetWeaponName({WeaponId, MeleeWeaponId}:wepid, String:nameBuffer[], length
|
|||
* @param length Max length which can be written to the buffer.
|
||||
* @return Number of bytes written to buffer, or 0 for invalid weaponId.
|
||||
*/
|
||||
stock GetLongWeaponName({WeaponId, MeleeWeaponId}:wepid, String:nameBuffer[], length, tagType = tagof(wepid))
|
||||
{
|
||||
if (tagType == tagof(MeleeWeaponId))
|
||||
{
|
||||
strcopy(nameBuffer, length, GETLONGMELEEWEAPONNAME(wepid));
|
||||
}
|
||||
else
|
||||
{
|
||||
strcopy(nameBuffer, length, GETLONGWEAPONNAME(wepid));
|
||||
}
|
||||
stock int GetLongWeaponName(WeaponId wepid, char[] nameBuffer, int length) {
|
||||
strcopy(nameBuffer, length, GETLONGMELEEWEAPONNAME(wepid));
|
||||
}
|
||||
|
||||
stock int GetLongMeleeWeaponName(WeaponId wepid, char[] nameBuffer, int length) {
|
||||
strcopy(nameBuffer, length, GETLONGWEAPONNAME(wepid));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -461,16 +445,12 @@ stock GetLongWeaponName({WeaponId, MeleeWeaponId}:wepid, String:nameBuffer[], le
|
|||
* @param length Max length which can be written to the buffer.
|
||||
* @return Number of bytes written to buffer, or 0 for invalid weaponid or no weapon model available.
|
||||
*/
|
||||
stock GetWeaponModel({WeaponId, MeleeWeaponId}:wepid, String:modelBuffer[], length, tagType = tagof(wepid))
|
||||
{
|
||||
if (tagType == tagof(MeleeWeaponId))
|
||||
{
|
||||
strcopy(modelBuffer, length, GETMELEEWEAPONMODEL(wepid));
|
||||
}
|
||||
else
|
||||
{
|
||||
strcopy(modelBuffer, length, GETWEAPONMODEL(wepid));
|
||||
}
|
||||
stock int GetWeaponModel(MeleeWeaponId wepid, char[] modelBuffer, int length) {
|
||||
strcopy(modelBuffer, length, GETWEAPONMODEL(wepid));
|
||||
}
|
||||
|
||||
stock int GetMeleeWeaponModel(MeleeWeaponId wepid, char[] modelBuffer, int length) {
|
||||
strcopy(modelBuffer, length, GETMELEEWEAPONMODEL(wepid));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -480,26 +460,21 @@ stock GetWeaponModel({WeaponId, MeleeWeaponId}:wepid, String:modelBuffer[], leng
|
|||
* @param entity Index of entity to identify
|
||||
* @return WeaponID for the entity if it is a weapon, WEPID_NONE otherwise
|
||||
*/
|
||||
stock WeaponId:IdentifyWeapon(entity)
|
||||
{
|
||||
if(!entity || !IsValidEntity(entity) || !IsValidEdict(entity))
|
||||
{
|
||||
stock WeaponId IdentifyWeapon(int entity) {
|
||||
if(!entity || !IsValidEntity(entity) || !IsValidEdict(entity)) {
|
||||
return WEPID_NONE;
|
||||
}
|
||||
decl String:class[64];
|
||||
if(!GetEdictClassname(entity, class, sizeof(class)))
|
||||
{
|
||||
static char class[64];
|
||||
if(!GetEdictClassname(entity, class, sizeof(class))) {
|
||||
return WEPID_NONE;
|
||||
}
|
||||
|
||||
if(StrEqual(class, "weapon_spawn"))
|
||||
{
|
||||
return WeaponId:GetEntProp(entity,Prop_Send,"m_weaponID");
|
||||
if(StrEqual(class, "weapon_spawn")) {
|
||||
return view_as<WeaponId>(GetEntProp(entity ,Prop_Send, "m_weaponID"));
|
||||
}
|
||||
|
||||
new len = strlen(class);
|
||||
if(len-6 > 0 && StrEqual(class[len-6], "_spawn"))
|
||||
{
|
||||
int len = strlen(class);
|
||||
if(len-6 > 0 && StrEqual(class[len-6], "_spawn")) {
|
||||
class[len-6]='\0';
|
||||
return WeaponNameToId(class);
|
||||
}
|
||||
|
@ -508,41 +483,30 @@ stock WeaponId:IdentifyWeapon(entity)
|
|||
}
|
||||
|
||||
// Helper function used for getting an entity's internal melee name
|
||||
stock bool:GetMeleeWeaponNameFromEntity(entity, String:buffer[], length) {
|
||||
decl String:classname[64];
|
||||
if (! GetEdictClassname(entity, classname, sizeof(classname)))
|
||||
{
|
||||
stock bool GetMeleeWeaponNameFromEntity(int entity, char[] buffer, int length) {
|
||||
static char classname[64];
|
||||
if (!GetEdictClassname(entity, classname, sizeof(classname))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StrEqual(classname, "weapon_melee_spawn"))
|
||||
{
|
||||
if (hMeleeWeaponModelsTrie == INVALID_HANDLE)
|
||||
{
|
||||
if (StrEqual(classname, "weapon_melee_spawn")) {
|
||||
if (hMeleeWeaponModelsTrie == INVALID_HANDLE) {
|
||||
InitWeaponNamesTrie();
|
||||
}
|
||||
|
||||
decl String:sModelName[128];
|
||||
static char sModelName[128];
|
||||
GetEntPropString(entity, Prop_Data, "m_ModelName", sModelName, sizeof(sModelName));
|
||||
|
||||
// Strip models directory
|
||||
if (strncmp(sModelName, "models/", 7, false) == 0)
|
||||
{
|
||||
if (strncmp(sModelName, "models/", 7, false) == 0) {
|
||||
strcopy(sModelName, sizeof(sModelName), sModelName[6]);
|
||||
}
|
||||
|
||||
if (GetTrieString(hMeleeWeaponModelsTrie, sModelName, buffer, length))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (StrEqual(classname, "weapon_melee"))
|
||||
{
|
||||
return GetTrieString(hMeleeWeaponModelsTrie, sModelName, buffer, length)
|
||||
} else if (StrEqual(classname, "weapon_melee")) {
|
||||
GetEntPropString(entity, Prop_Data, "m_strMapSetScriptName", buffer, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -553,28 +517,23 @@ stock bool:GetMeleeWeaponNameFromEntity(entity, String:buffer[], length) {
|
|||
* @param entity Index of entity to identify
|
||||
* @return MeleeWeaponId for the entity if it is a weapon, WEPID_MELEE_NONE otherwise
|
||||
*/
|
||||
stock MeleeWeaponId:IdentifyMeleeWeapon(entity)
|
||||
{
|
||||
if (IdentifyWeapon(entity) != WEPID_MELEE)
|
||||
{
|
||||
stock MeleeWeaponId IdentifyMeleeWeapon(int entity) {
|
||||
if (IdentifyWeapon(entity) != WEPID_MELEE) {
|
||||
return WEPID_MELEE_NONE;
|
||||
}
|
||||
|
||||
decl String:sName[128];
|
||||
if (! GetMeleeWeaponNameFromEntity(entity, sName, sizeof(sName)))
|
||||
{
|
||||
static char sName[128];
|
||||
if (! GetMeleeWeaponNameFromEntity(entity, sName, sizeof(sName))) {
|
||||
return WEPID_MELEE_NONE;
|
||||
}
|
||||
|
||||
if (hMeleeWeaponNamesTrie == INVALID_HANDLE)
|
||||
{
|
||||
if (hMeleeWeaponNamesTrie == INVALID_HANDLE) {
|
||||
InitWeaponNamesTrie();
|
||||
}
|
||||
|
||||
new id;
|
||||
if(GetTrieValue(hMeleeWeaponNamesTrie, sName, id))
|
||||
{
|
||||
return MeleeWeaponId:id;
|
||||
int id;
|
||||
if(GetTrieValue(hMeleeWeaponNamesTrie, sName, id)) {
|
||||
return id;
|
||||
}
|
||||
return WEPID_MELEE_NONE;
|
||||
}
|
||||
|
@ -590,7 +549,7 @@ stock MeleeWeaponId:IdentifyMeleeWeapon(entity)
|
|||
* @param model World model to use for the weapon spawn
|
||||
* @return entity of the new weapon spawn, or -1 on errors.
|
||||
*/
|
||||
stock ConvertWeaponSpawn(entity, WeaponId:wepid, count=5, const String:model[] = "")
|
||||
stock int ConvertWeaponSpawn(int entity, WeaponId wepid, int count = 5, const char model[] = "")
|
||||
{
|
||||
if(!IsValidEntity(entity)) return -1;
|
||||
if(!IsValidWeaponId(wepid)) return -1;
|
||||
|
@ -609,12 +568,9 @@ stock ConvertWeaponSpawn(entity, WeaponId:wepid, count=5, const String:model[] =
|
|||
SetEntProp(entity, Prop_Send, "m_weaponID", wepid);
|
||||
|
||||
decl String:buf[64];
|
||||
if(model[0] == '\0')
|
||||
{
|
||||
if(model[0] == '\0') {
|
||||
SetEntityModel(entity, model);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
GetWeaponModel(wepid, buf, sizeof(buf));
|
||||
SetEntityModel(entity, buf);
|
||||
}
|
||||
|
|
1
scripting/include/l4d_anti_rush.inc
Normal file
1
scripting/include/l4d_anti_rush.inc
Normal file
|
@ -0,0 +1 @@
|
|||
forward Action OnAntiRush(int client, int &type, float distance);
|
|
@ -18,7 +18,7 @@
|
|||
* Copyright (C) 2017 "Accelerator74"
|
||||
*
|
||||
* Left 4 DHooks Direct SourceMod plugin
|
||||
* Copyright (C) 2021 "SilverShot" / "Silvers"
|
||||
* Copyright (C) 2022 "SilverShot" / "Silvers"
|
||||
*
|
||||
* =============================================================================
|
||||
*
|
||||
|
@ -59,8 +59,8 @@
|
|||
|
||||
|
||||
// Natives:
|
||||
// L4D1 = 24 [left4downtown] + 47 [l4d_direct] + 15 [l4d2addresses] + 44 [silvers - mine!] + 4 [anim] = 126
|
||||
// L4D2 = 53 [left4downtown] + 61 [l4d_direct] + 26 [l4d2addresses] + 79 [silvers - mine!] + 4 [anim] = 212
|
||||
// L4D1 = 25 [left4downtown] + 47 [l4d_direct] + 15 [l4d2addresses] + 46 [silvers - mine!] + 4 [anim] = 128
|
||||
// L4D2 = 54 [left4downtown] + 61 [l4d_direct] + 26 [l4d2addresses] + 81 [silvers - mine!] + 4 [anim] = 215
|
||||
|
||||
// Forwards:
|
||||
// L4D1 = 61;
|
||||
|
@ -196,6 +196,10 @@ public void __pl_l4dh_SetNTVOptional()
|
|||
MarkNativeAsOptional("L4D_GetMobSpawnTimerDuration");
|
||||
MarkNativeAsOptional("L4D2_ChangeFinaleStage");
|
||||
MarkNativeAsOptional("L4D2_SpawnWitchBride");
|
||||
MarkNativeAsOptional("L4D_LobbyUnreserve");
|
||||
MarkNativeAsOptional("L4D_LobbyIsReserved");
|
||||
MarkNativeAsOptional("L4D_GetLobbyReservation");
|
||||
MarkNativeAsOptional("L4D_SetLobbyReservation");
|
||||
|
||||
// l4d2weapons.inc
|
||||
MarkNativeAsOptional("L4D_GetWeaponID");
|
||||
|
@ -497,7 +501,7 @@ native int AnimGetFromActivity(char[] activity);
|
|||
* @remarks Only used for bot special spawns (not players)
|
||||
* @remarks zombieClass: 1=Smoker, 2=Boomer, 3=Hunter, 4=Spitter, 5=Jockey, 6=Charger
|
||||
*
|
||||
* @param zombieClass Zombie class that will be spawned.
|
||||
* @param zombieClass Zombie class that will be spawned
|
||||
* @param vecPos Vector coordinate where special will be spawned
|
||||
* @param vecAng QAngle where spcial will be facing
|
||||
*
|
||||
|
@ -511,8 +515,8 @@ forward Action L4D_OnSpawnSpecial(int &zombieClass, const float vecPos[3], const
|
|||
* @remarks Only used for bot special spawns (not players)
|
||||
* @remarks zombieClass: 1=Smoker, 2=Boomer, 3=Hunter, 4=Spitter, 5=Jockey, 6=Charger
|
||||
*
|
||||
* @param client The client index who spawned
|
||||
* @param zombieClass Zombie class that will be spawned.
|
||||
* @param client The client index who spawned. Can be 0 if spawning failed
|
||||
* @param zombieClass Zombie class that will be spawned
|
||||
* @param vecPos Vector coordinate where special will be spawned
|
||||
* @param vecAng QAngle where spcial will be facing
|
||||
*
|
||||
|
@ -1351,6 +1355,15 @@ forward Action L4D_OnVomitedUpon(int victim, int &attacker, bool &boomerExplosio
|
|||
*/
|
||||
forward Action L4D2_OnHitByVomitJar(int victim, int &attacker);
|
||||
|
||||
/**
|
||||
* @brief Called when the server changes hibernation status
|
||||
*
|
||||
* @param hibernating true when hibernating, false when waking up
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
forward void L4D_OnServerHibernationUpdate(bool hibernating);
|
||||
|
||||
/**
|
||||
* @brief Called when the client's material system is expecting instructions from the server in regards to addons
|
||||
* @remarks Doesn't fire if l4d2_addons_eclipse is -1 or 0
|
||||
|
@ -2325,11 +2338,39 @@ native int L4D2_SpawnWitchBride(const float vecPos[3], const float vecAng[3]);
|
|||
|
||||
/**
|
||||
* @brief Removes lobby reservation from a server
|
||||
* @remarks Sets the reservation cookie to 0,
|
||||
* it is safe to call this even if it's unreserved.
|
||||
* @remarks Sets the reservation cookie to 0, it is safe to call this even if it's unreserved.
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
native void L4D_LobbyUnreserve();
|
||||
|
||||
/**
|
||||
* @brief Checks if the server is currently reserved for a lobby
|
||||
* @remarks Server is automatically unreserved if it hibernates or if all players leave.
|
||||
*
|
||||
* @return true if reserved, false if not reserved
|
||||
*/
|
||||
native bool L4D_LobbyIsReserved();
|
||||
|
||||
/**
|
||||
* @brief Returns the lobby reservation ID
|
||||
*
|
||||
* @reservation String to store the reservation ID to
|
||||
* @maxlength Maximum length of the string to store to
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
native void L4D_GetLobbyReservation(char [] reservation, int maxlength);
|
||||
|
||||
/**
|
||||
* @brief Sets the lobby reservation ID
|
||||
*
|
||||
* @reservation The reservation ID to set
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
native void L4D_SetLobbyReservation(char reservation[20]);
|
||||
|
||||
/**
|
||||
* @brief Get the current campaign scores stored in the Director
|
||||
* @remarks The campaign scores are updated after L4D_OnSetCampaignScores
|
||||
|
@ -2345,17 +2386,6 @@ native void L4D_LobbyUnreserve();
|
|||
#pragma deprecated Use GetTeamScore and OnClearTeamScores instead
|
||||
native int L4D_GetCampaignScores(int &scoreA, int &scoreB);
|
||||
|
||||
/**
|
||||
* @brief Checks if the server is currently reserved for a lobby
|
||||
* @remarks Server is automatically unreserved if it hibernates or
|
||||
* if all players leave.
|
||||
*
|
||||
* @deprecated This will always return false on L4D2 or on Linux.
|
||||
*
|
||||
* @return true if reserved, false if not reserved
|
||||
*/
|
||||
#pragma deprecated This will always return false on L4D2 or on Linux.
|
||||
native bool L4D_LobbyIsReserved();
|
||||
/**
|
||||
* @brief Get the time remaining before the next director horde.
|
||||
* @remarks This timer is used for scripted event hordes and natural timed hordes
|
||||
|
@ -2704,6 +2734,8 @@ enum L4D2FloatWeaponAttributes
|
|||
L4D2FWA_PelletScatterYaw,
|
||||
L4D2FWA_VerticalPunch,
|
||||
L4D2FWA_HorizontalPunch, // Requires "z_gun_horiz_punch" cvar changed to "1".
|
||||
L4D2FWA_GainRange,
|
||||
L4D2FWA_ReloadDuration,
|
||||
MAX_SIZE_L4D2FloatWeaponAttributes
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Left 4 DHooks Direct
|
||||
* Copyright (C) 2021 Silvers
|
||||
* Copyright (C) 2022 Silvers
|
||||
*
|
||||
* 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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Copyright (C) 2021 LuxLuma
|
||||
* Copyright (C) 2022 LuxLuma
|
||||
*
|
||||
* 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
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Left 4 DHooks Direct - Stock Functions
|
||||
* Copyright (C) 2021 Silvers
|
||||
* Copyright (C) 2022 Silvers
|
||||
*
|
||||
* 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
|
||||
|
@ -827,7 +827,10 @@ stock int GetAnyRandomClient()
|
|||
}
|
||||
|
||||
if( aClients.Length > 0 )
|
||||
{
|
||||
SetRandomSeed(GetGameTickCount());
|
||||
client = aClients.Get(GetRandomInt(0, aClients.Length - 1));
|
||||
}
|
||||
|
||||
delete aClients;
|
||||
|
||||
|
@ -866,7 +869,7 @@ stock int Local_GetRandomClient(int team, int alive = -1, int bots = -1)
|
|||
|
||||
for( int i = 1; i <= MaxClients; i++ )
|
||||
{
|
||||
if( IsClientInGame(i) && GetClientTeam(i) == team && (alive == -1 || IsPlayerAlive(i) == view_as<bool>(alive)) && (bots == -1 || IsFakeClient(i) == view_as<bool>(alive)) )
|
||||
if( IsClientInGame(i) && GetClientTeam(i) == team && (alive == -1 || IsPlayerAlive(i) == view_as<bool>(alive)) && (bots == -1 || IsFakeClient(i) == view_as<bool>(bots)) )
|
||||
{
|
||||
aClients.Push(i);
|
||||
}
|
||||
|
@ -875,7 +878,10 @@ stock int Local_GetRandomClient(int team, int alive = -1, int bots = -1)
|
|||
int client;
|
||||
|
||||
if( aClients.Length > 0 )
|
||||
{
|
||||
SetRandomSeed(GetGameTickCount());
|
||||
client = aClients.Get(GetRandomInt(0, aClients.Length - 1));
|
||||
}
|
||||
|
||||
delete aClients;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* =============================================================================
|
||||
* Left 4 Dead Stocks Library (C)2011-2012 Buster "Mr. Zero" Nielsen
|
||||
* Syntax Update and merge into "Left 4 DHooks Direct" (C) 2021 "SilverShot"
|
||||
* Syntax Update and merge into "Left 4 DHooks Direct" (C) 2022 "SilverShot"
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
|
50
scripting/include/smrcon.inc
Normal file
50
scripting/include/smrcon.inc
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* @brief Called when an RCon session auth is processed
|
||||
*
|
||||
* @param rconId RCon listener ID, unique per session.
|
||||
* @param address Originating IP address.
|
||||
* @param password Password sent by RCon client.
|
||||
* @param allow True to grant auth, false otherwise.
|
||||
* @return Plugin_Changed to use given allow value, Plugin_Continue to let engine process.
|
||||
*/
|
||||
forward Action SMRCon_OnAuth(int rconId, const char[] address, const char[] password, bool &allow);
|
||||
|
||||
/**
|
||||
* @brief Called when an RCon command is processed.
|
||||
*
|
||||
* @note Rejection here does not count as a bad password attempt;
|
||||
* however, the RCon log line will be annotated in the form
|
||||
* of 'command (rejected) "%s"' rather than just 'command "%s"'
|
||||
*
|
||||
* @param rconId RCon listener ID, unique per session.
|
||||
* @param address Originating IP address.
|
||||
* @param command Command sent by RCon client.
|
||||
* @param allow True to allow command to be processed, false otherwise.
|
||||
* @return Plugin_Changed to use given allow value, Plugin_Continue to let engine process.
|
||||
*/
|
||||
forward Action SMRCon_OnCommand(int rconId, const char[] address, const char[] command, bool &allow);
|
||||
|
||||
/**
|
||||
* @brief Called when an RCon session is disconnected.
|
||||
*
|
||||
* @param rconId RCon listener ID, unique per session.
|
||||
*/
|
||||
forward void SMRCon_OnDisconnect(int rconId);
|
||||
|
||||
/**
|
||||
* @brief Called when an RCon log line is written
|
||||
*
|
||||
* @param rconId RCon listener ID, unique per session.
|
||||
* @param address Originating IP address.
|
||||
* @param logdata Log data (usually either "Bad Password" or "command"
|
||||
* followed by the command.
|
||||
* @return Plugin_Continue to log, Plugin_Handled to block.
|
||||
*/
|
||||
forward Action SMRCon_OnLog(int rconId, const char[] address, const char[] logdata);
|
||||
|
||||
/**
|
||||
* @brief Determines whether current server command originated from an RCon session.
|
||||
*
|
||||
* @return True if command originated from RCon session, false if from console or not in server command callback.
|
||||
*/
|
||||
native bool SMRCon_IsCmdFromRCon();
|
171
scripting/l4d2_baseball.sp
Normal file
171
scripting/l4d2_baseball.sp
Normal file
|
@ -0,0 +1,171 @@
|
|||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
|
||||
#define HIT_1 "weapons/golf_club/wpn_golf_club_melee_01.wav"
|
||||
#define HIT_2 "weapons/golf_club/wpn_golf_club_melee_02.wav"
|
||||
|
||||
ConVar sv_melee_force_projectile, sv_melee_radius_projectile, sv_melee_force_boost_projectile_up;
|
||||
int g_iLaser, g_iGlow;
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "[L4D2] Baseball",
|
||||
author = "BHaType",
|
||||
description = "Melee weapons can now deflect projectile",
|
||||
version = "0.0",
|
||||
url = ""
|
||||
}
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
sv_melee_force_projectile = CreateConVar("sv_melee_force_projectile", "0.6");
|
||||
sv_melee_force_boost_projectile_up = CreateConVar("sv_melee_force_boost_projectile_up", "250.0");
|
||||
sv_melee_radius_projectile = CreateConVar("sv_melee_radius_projectile", "75.0");
|
||||
|
||||
AutoExecConfig(true, "l4d2_baseball");
|
||||
|
||||
HookEvent("weapon_fire", weapon_fire);
|
||||
HookEvent("entity_shoved", entity_shoved);
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
PrecacheSound(HIT_1, true);
|
||||
PrecacheSound(HIT_2, true);
|
||||
|
||||
g_iLaser = PrecacheModel("materials/sprites/laserbeam.vmt");
|
||||
g_iGlow = PrecacheModel("materials/sprites/glow.vmt");
|
||||
}
|
||||
|
||||
public void entity_shoved (Event event, const char[] name, bool dontbroadcast)
|
||||
{
|
||||
int entity = event.GetInt("entityid");
|
||||
|
||||
static char szName[36];
|
||||
GetEntityClassname(entity, szName, sizeof szName);
|
||||
|
||||
if ( StrContains(szName, "_projectile") != -1 )
|
||||
{
|
||||
float vVelocity[3];
|
||||
vVelocity = CalculateBaseForce(entity);
|
||||
vVelocity[2] += sv_melee_force_boost_projectile_up.FloatValue;
|
||||
|
||||
TeleportEntity(entity, NULL_VECTOR, NULL_VECTOR, vVelocity);
|
||||
}
|
||||
}
|
||||
|
||||
public void weapon_fire (Event event, const char[] name, bool dontbroadcast)
|
||||
{
|
||||
static char szName[36];
|
||||
event.GetString("weapon", szName, sizeof szName);
|
||||
|
||||
if ( strcmp(szName, "melee") != 0 )
|
||||
return;
|
||||
|
||||
int client = event.GetInt("userid");
|
||||
timer (CreateTimer(0.1, timer, client, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE), client);
|
||||
}
|
||||
|
||||
public Action timer (Handle timer, int client)
|
||||
{
|
||||
client = GetClientOfUserId(client);
|
||||
|
||||
if ( !client )
|
||||
return Plugin_Stop;
|
||||
|
||||
int weapon = GetPlayerWeaponSlot(client, 1);
|
||||
|
||||
if ( weapon == -1 || GetEntPropFloat(weapon, Prop_Send, "m_flNextPrimaryAttack") <= GetGameTime())
|
||||
return Plugin_Stop;
|
||||
|
||||
float vAngles[3], vOrigin[3], vVector[3], vEnd[3];
|
||||
|
||||
GetClientEyePosition(client, vOrigin);
|
||||
|
||||
GetClientEyeAngles(client, vAngles);
|
||||
GetAngleVectors(vAngles, vVector, NULL_VECTOR, NULL_VECTOR);
|
||||
ScaleVector(vVector, sv_melee_radius_projectile.FloatValue);
|
||||
AddVectors(vOrigin, vVector, vEnd);
|
||||
|
||||
GetClientEyePosition(client, vOrigin);
|
||||
|
||||
#define hull 10.0
|
||||
static const float vMaxs[3] = { hull, hull, hull };
|
||||
static const float vMins[3] = { -hull, -hull, -hull };
|
||||
|
||||
TR_TraceHullFilter(vOrigin, vEnd, vMins, vMaxs, MASK_SOLID, TraceFilter, client);
|
||||
float vHit[3];
|
||||
TR_GetEndPosition(vHit);
|
||||
|
||||
if ( TR_DidHit () )
|
||||
{
|
||||
int entity = TR_GetEntityIndex();
|
||||
|
||||
if ( entity != 0 )
|
||||
{
|
||||
static char szName[36];
|
||||
GetEntityClassname(entity, szName, sizeof szName);
|
||||
|
||||
if ( StrContains(szName, "_projectile") != -1 )
|
||||
{
|
||||
float vVelocity[3], vVec[3];
|
||||
|
||||
vVelocity = CalculateBaseForce(entity, client);
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", vOrigin);
|
||||
|
||||
MakeVectorFromPoints(vHit, vOrigin, vVec);
|
||||
ScaleVector(vVec, 1.0 - sv_melee_force_projectile.FloatValue * sv_melee_radius_projectile.FloatValue);
|
||||
AddVectors(vVec, vVector, vVec);
|
||||
AddVectors(vVec, vVelocity, vVelocity);
|
||||
|
||||
TE_SetupSparks(vHit, vVelocity, 1, 1);
|
||||
TE_SendToAll();
|
||||
|
||||
NegateVector(vVelocity);
|
||||
|
||||
vVelocity[2] += sv_melee_force_boost_projectile_up.FloatValue;
|
||||
TeleportEntity(entity, NULL_VECTOR, NULL_VECTOR, vVelocity);
|
||||
|
||||
int color[4] = { 255, ... };
|
||||
for (int i; i <= 2; i++)
|
||||
color[i] = GetRandomInt(0, 255);
|
||||
|
||||
TE_SetupBeamFollow(entity, g_iLaser, g_iGlow, 4.6, 0.8, 0.8, 1, color);
|
||||
TE_SendToAll();
|
||||
|
||||
EmitSoundToAll((GetRandomInt(0, 1) == 0 ? HIT_1 : HIT_2), SOUND_FROM_WORLD, .origin = vHit);
|
||||
|
||||
// PrintToChatAll("\x04%N \x03baseballed projectile for \x04%.2f \x03velocity!", client, GetVectorLength(vVelocity));
|
||||
return Plugin_Stop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
float[] CalculateBaseForce (int victim, int attacker = 0)
|
||||
{
|
||||
float m_vecBaseVelocity[3], m_vecVelocity[3], vAngles[3];
|
||||
|
||||
if ( attacker )
|
||||
{
|
||||
GetEntPropVector(attacker, Prop_Send, "m_vecBaseVelocity", m_vecBaseVelocity);
|
||||
GetClientEyeAngles(attacker, vAngles);
|
||||
}
|
||||
|
||||
GetEntPropVector(victim, Prop_Data, "m_vecVelocity", m_vecVelocity);
|
||||
AddVectors(m_vecBaseVelocity, m_vecVelocity, m_vecVelocity);
|
||||
|
||||
ScaleVector(m_vecVelocity, sv_melee_force_projectile.FloatValue);
|
||||
|
||||
return m_vecVelocity;
|
||||
}
|
||||
|
||||
public bool TraceFilter (int entity, int mask, int data)
|
||||
{
|
||||
return entity != data;
|
||||
}
|
1020
scripting/l4d_anti_rush.sp
Normal file
1020
scripting/l4d_anti_rush.sp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -40,8 +40,9 @@ public void OnPluginStart() {
|
|||
PluginCvarTimeout = CreateConVar("l4d_rts_timeout", "30", "How long will the server stay disconnected from matchmaking? 0 - never restore matchmaking connection", 0, true, 0.0, true, 300.0);
|
||||
PluginCvarImmuneLevel = CreateConVar("l4d_rts_immunelevel", "1", "Any player >= to this level will cancel the lobby vote.", 0);
|
||||
|
||||
RegAdminCmd("sm_rts", Command_MakeReservation, ADMFLAG_BAN, "Free the server from all players, then reserve it.");
|
||||
RegAdminCmd("sm_cr", Command_CancelReservation, ADMFLAG_BAN, "Cancel reservation and make server public again.");
|
||||
RegAdminCmd("sm_rts", Command_MakeReservation, ADMFLAG_KICK, "Free the server from all players, then reserve it.");
|
||||
RegAdminCmd("sm_cr", Command_CancelReservation, ADMFLAG_KICK, "Cancel reservation and make server public again.");
|
||||
RegAdminCmd("sm_forcelobby", Command_ForceLobby, ADMFLAG_BAN, "Force call vote to return to lobby");
|
||||
|
||||
SteamGroupExclusiveCvar = FindConVar("sv_steamgroup_exclusive");
|
||||
SearchKeyCvar = FindConVar("sv_search_key");
|
||||
|
@ -68,6 +69,17 @@ public void OnMapStart() {
|
|||
isMapChange = false;
|
||||
}
|
||||
|
||||
public Action Command_ForceLobby(int client, int args) {
|
||||
Handle bf = StartMessageOne("VoteStart", client, USERMSG_RELIABLE);
|
||||
BfWriteByte(bf, 0);
|
||||
BfWriteByte(bf, client);
|
||||
BfWriteString(bf, "returntolobby");
|
||||
BfWriteString(bf, "");
|
||||
BfWriteString(bf, "");
|
||||
EndMessage();
|
||||
PassVote();
|
||||
}
|
||||
|
||||
public Action Command_MakeReservation(int client, int args) {
|
||||
bool isAdminOnline, isServerEmpty = true;
|
||||
if(client > 0) {
|
||||
|
|
136
scripting/sm_player_recorder.sp
Normal file
136
scripting/sm_player_recorder.sp
Normal file
|
@ -0,0 +1,136 @@
|
|||
#pragma semicolon 1
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#define PLUGIN_AUTHOR "Jackz"
|
||||
#define PLUGIN_VERSION "1.00"
|
||||
|
||||
#define DATABASE_NAME "player-recorder"
|
||||
#define RECORD_INTERVAL 60.0
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
|
||||
#pragma newdecls required
|
||||
|
||||
public Plugin myinfo = {
|
||||
name = "SRCDS Player Count Recorder",
|
||||
author = PLUGIN_AUTHOR,
|
||||
description = "",
|
||||
version = PLUGIN_VERSION,
|
||||
url = ""
|
||||
};
|
||||
|
||||
static Database g_db;
|
||||
static char playerID[32];
|
||||
|
||||
enum struct PlayerData {
|
||||
int timestamp;
|
||||
int playerCount;
|
||||
}
|
||||
|
||||
static ArrayList g_playerData;
|
||||
static int iLastCount;
|
||||
static bool active;
|
||||
|
||||
public void OnPluginStart() {
|
||||
if(!SQL_CheckConfig(DATABASE_NAME)) {
|
||||
SetFailState("No database entry for '" ... DATABASE_NAME ... "'; no database to connect to.");
|
||||
} else if(!ConnectDB()) {
|
||||
SetFailState("Failed to connect to database.");
|
||||
}
|
||||
|
||||
g_playerData = new ArrayList(sizeof(PlayerData));
|
||||
|
||||
|
||||
ConVar hPlayerCountID = CreateConVar("sm_playercount_id", "", "The ID to use for player count recording. Will not record if not set", FCVAR_NONE);
|
||||
hPlayerCountID.GetString(playerID, sizeof(playerID));
|
||||
hPlayerCountID.AddChangeHook(Change_ID);
|
||||
|
||||
if(strlen(playerID) > 0) {
|
||||
Init();
|
||||
}
|
||||
}
|
||||
|
||||
void Init() {
|
||||
HookEvent("player_first_spawn", Event_Connection, EventHookMode_PostNoCopy);
|
||||
HookEvent("player_disconnect", Event_Connection, EventHookMode_PostNoCopy);
|
||||
|
||||
CreateTimer(RECORD_INTERVAL, Timer_PushCounts, _, TIMER_REPEAT);
|
||||
active = true;
|
||||
PlayerData data;
|
||||
data.timestamp = GetTime();
|
||||
data.playerCount = GetPlayerCount();
|
||||
g_playerData.PushArray(data);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void Change_ID(ConVar convar, const char[] oldValue, const char[] newValue) {
|
||||
convar.GetString(playerID, sizeof(playerID));
|
||||
if(!active && strlen(playerID) > 0) {
|
||||
Init();
|
||||
}
|
||||
}
|
||||
|
||||
bool ConnectDB() {
|
||||
char error[255];
|
||||
g_db = SQL_Connect(DATABASE_NAME, true, error, sizeof(error));
|
||||
if (g_db == null) {
|
||||
LogError("Database error %s", error);
|
||||
delete g_db;
|
||||
return false;
|
||||
} else {
|
||||
PrintToServer("[SPR] Connected to database \"" ... DATABASE_NAME ... "\"");
|
||||
SQL_LockDatabase(g_db);
|
||||
SQL_FastQuery(g_db, "SET NAMES \"UTF8mb4\"");
|
||||
SQL_UnlockDatabase(g_db);
|
||||
g_db.SetCharset("utf8mb4");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Event_Connection(Event event, const char[] name, bool dontBroadcast) {
|
||||
int count = GetPlayerCount();
|
||||
if(count != iLastCount) {
|
||||
PlayerData data;
|
||||
data.timestamp = GetTime();
|
||||
data.playerCount = count;
|
||||
g_playerData.PushArray(data);
|
||||
iLastCount = count;
|
||||
}
|
||||
}
|
||||
|
||||
public Action Timer_PushCounts(Handle h) {
|
||||
Transaction transact = new Transaction();
|
||||
static char query[255];
|
||||
static PlayerData data;
|
||||
int length = g_playerData.Length;
|
||||
for(int i = 0; i < length; i++) {
|
||||
g_playerData.GetArray(i, data, sizeof(data));
|
||||
g_db.Format(query, sizeof(query), "INSERT INTO player_count (server_name, timestamp, count) VALUES ('%s', %d, %d)",
|
||||
playerID,
|
||||
data.timestamp,
|
||||
data.playerCount
|
||||
);
|
||||
transact.AddQuery(query);
|
||||
}
|
||||
g_playerData.Resize(g_playerData.Length - length);
|
||||
g_db.Execute(transact, _, SQL_TransactionFailed, length, DBPrio_Low);
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public void SQL_TransactionFailed(Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData) {
|
||||
PrintToServer("[PlayerRecorder] Push failure: %s at query %d/%d", error, failIndex, numQueries);
|
||||
}
|
||||
|
||||
int GetPlayerCount() {
|
||||
int count;
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && !IsFakeClient(i)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
194
scripting/spray_control.sp
Normal file
194
scripting/spray_control.sp
Normal file
|
@ -0,0 +1,194 @@
|
|||
#pragma semicolon 1
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#define PLUGIN_VERSION "0.00"
|
||||
|
||||
#define DB_NAME "sprayfiltercontrol"
|
||||
#define DB_TABLE "spray_results" //not used
|
||||
|
||||
// The minimum value for detection, number is related to value of RESULT_TEXT
|
||||
#define ADULT_THRES 2
|
||||
#define RACY_THRES 2
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <system2>
|
||||
|
||||
#pragma newdecls required
|
||||
|
||||
public Plugin myinfo = {
|
||||
name = "Spay Filter Control",
|
||||
author = "jackzmc",
|
||||
description = "",
|
||||
version = PLUGIN_VERSION,
|
||||
url = ""
|
||||
};
|
||||
|
||||
static Database g_db;
|
||||
static char apikey[64];
|
||||
/* TODO:
|
||||
1. Plugin start, fetch API key from keyvalue
|
||||
2. On client connect, check database for spray result
|
||||
3. Run system2 if no result
|
||||
*/
|
||||
char RESULT_TEXT[6][] = {
|
||||
"UNKNOWN",
|
||||
"VERY UNLIKELY",
|
||||
"UNLIKELY",
|
||||
"POSSIBLE",
|
||||
"LIKELY",
|
||||
"VERY LIKELY"
|
||||
};
|
||||
|
||||
enum Result {
|
||||
UNKNOWN = -1,
|
||||
VERY_UNLIKELY = 0,
|
||||
UNLIKELY,
|
||||
POSSIBLE,
|
||||
LIKELY,
|
||||
VERY_LIKELY
|
||||
}
|
||||
|
||||
enum struct SprayResult {
|
||||
Result adult;
|
||||
Result racy;
|
||||
}
|
||||
|
||||
public void OnPluginStart() {
|
||||
if(!SQL_CheckConfig(DB_NAME)) {
|
||||
SetFailState("No database entry for " ... DB_NAME ... "; no database to connect to.");
|
||||
}
|
||||
if(!ConnectDB()) {
|
||||
SetFailState("Failed to connect to database.");
|
||||
}
|
||||
|
||||
KeyValues kv = new KeyValues("Config");
|
||||
kv.ImportFromFile("spraycontrol.cfg");
|
||||
|
||||
if (!kv.JumpToKey("apikey")) {
|
||||
delete kv;
|
||||
SetFailState("No 'apikey' provided in spraycontrol.cfg");
|
||||
}
|
||||
|
||||
kv.GetString("apikey", apikey, sizeof(apikey));
|
||||
|
||||
RegAdminCmd("sm_checkspray", Command_CheckSpray, ADMFLAG_GENERIC, "Gets the spray results of a user");
|
||||
}
|
||||
|
||||
bool ConnectDB() {
|
||||
char error[255];
|
||||
g_db = SQL_Connect(DB_NAME, true, error, sizeof(error));
|
||||
if (g_db == null) {
|
||||
LogError("[SFC] Database error %s", error);
|
||||
delete g_db;
|
||||
return false;
|
||||
} else {
|
||||
SQL_LockDatabase(g_db);
|
||||
SQL_FastQuery(g_db, "SET NAMES \"UTF8mb4\"");
|
||||
SQL_UnlockDatabase(g_db);
|
||||
g_db.SetCharset("utf8mb4");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Events
|
||||
|
||||
public void OnClientAuthorized(int client, const char[] auth) {
|
||||
if(!StrEqual(auth, "BOT", true)) {
|
||||
char filename[64], query[128];
|
||||
if(!GetPlayerDecalFile(client, filename, sizeof(filename))) {
|
||||
return; //They don't have a spray
|
||||
}
|
||||
Format(query, sizeof(query), "SELECT adult,racy FROM spray_results WHERE steamid = '%s' AND sprayid = '%s'", auth, filename);
|
||||
g_db.Query(DB_OnConnectCheck, query, GetClientUserId(client));
|
||||
}
|
||||
}
|
||||
|
||||
public void DB_OnConnectCheck(Database db, DBResultSet results, const char[] error, int user) {
|
||||
int client = GetClientOfUserId(user);
|
||||
if(db == INVALID_HANDLE || results == null) {
|
||||
LogError("DB_OnConnectCheck returned error: %s", error);
|
||||
}else{
|
||||
if(results.RowCount > 0 && client) {
|
||||
int adult = results.FetchInt(0) + 1;
|
||||
int racy = results.FetchInt(1) + 1;
|
||||
|
||||
CheckUser(client, adult, racy);
|
||||
} else {
|
||||
char filename[64];
|
||||
if(!GetPlayerDecalFile(client, filename, sizeof(filename))) {
|
||||
return; //They don't have a spray
|
||||
}
|
||||
System2_ExecuteFormattedThreaded(ExecuteCallback, GetClientUserId(client), "test-spray %s %s", filename, apikey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecuteCallback(bool success, const char[] command, System2ExecuteOutput output, int user) {
|
||||
int client = GetClientOfUserId(user);
|
||||
if(client <= 0) return; //Client disconnected, void result
|
||||
if (!success || output.ExitStatus != 0) {
|
||||
PrintToServer("[SFC] Could not get the spray result for %N", client);
|
||||
} else {
|
||||
char outputString[128];
|
||||
output.GetOutput(outputString, sizeof(outputString));
|
||||
|
||||
char results[2][64];
|
||||
char bit[3][16];
|
||||
ExplodeString(outputString, "\n", results, 2, 64);
|
||||
|
||||
int adult = -1;
|
||||
int racy = -1;
|
||||
|
||||
ExplodeString(results[0], "=", bit, 3, 16);
|
||||
adult = StringToInt(bit[2]);
|
||||
ExplodeString(results[1], "=", bit, 3, 16);
|
||||
racy = StringToInt(bit[2]);
|
||||
|
||||
PrintToServer("[SFC] %N Spray Results | adult=%s racy=%s", RESULT_TEXT[adult], RESULT_TEXT[racy]);
|
||||
|
||||
CheckUser(client, adult, racy);
|
||||
}
|
||||
}
|
||||
|
||||
public Action Command_CheckSpray(int client, int args) {
|
||||
if(args < 1)
|
||||
ReplyToCommand(client, "Usage: sm_checkspray <client>");
|
||||
else {
|
||||
char arg1[64];
|
||||
GetCmdArg(1, arg1, sizeof(arg1));
|
||||
int target = FindTarget(client, arg1, true, false);
|
||||
if(target > 0) {
|
||||
char filename[64];
|
||||
if(!GetPlayerDecalFile(client, filename, sizeof(filename))) {
|
||||
ReplyToCommand(client, "%N does not have a spray", target);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
System2_ExecuteFormattedThreaded(ExecuteCallback, GetClientUserId(client), "test-spray %s %s", filename, apikey);
|
||||
} else {
|
||||
ReplyToCommand(client, "Could not find target.");
|
||||
}
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
void CheckUser(int client, int adult, int racy) {
|
||||
if(adult > 3 || (adult > ADULT_THRES && racy > RACY_THRES)) {
|
||||
PrintToAdmins("%N 's spray has a questionable spray. Adult=%s Racy=%s",
|
||||
client,
|
||||
RESULT_TEXT[adult],
|
||||
RESULT_TEXT[racy]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
stock void PrintToAdmins(const char[] format, any ...) {
|
||||
char message[100];
|
||||
VFormat(message, sizeof(message), format, 2);
|
||||
for (int x = 1; x <= MaxClients; x++){
|
||||
if (IsClientConnected(x) && IsClientInGame(x) && GetUserAdmin(x) != INVALID_ADMIN_ID) {
|
||||
PrintToChat(x, message);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue