Merge branch 'master' of github.com:Jackzmc/sourcemod-plugins

This commit is contained in:
Jackz 2023-08-15 18:53:04 -05:00
commit 6ab22e8a60
No known key found for this signature in database
GPG key ID: E0BBD94CF657F603
5 changed files with 106 additions and 121 deletions

Binary file not shown.

View file

@ -2,9 +2,12 @@
#define DEBUG #define DEBUG
#define PLUGIN_VERSION "0.00" // Update intervals (only sends when > 0 players)
#define UPDATE_INTERVAL 6.0 // The update interval when there are active viewers
#define UPDATE_INTERVAL_SLOW 15.0 #define UPDATE_INTERVAL 5.0
// The update interval when there are no viewers on.
// We still need to poll to know how many viewers are watching
#define UPDATE_INTERVAL_SLOW 20.0
#include <sourcemod> #include <sourcemod>
#include <sdktools> #include <sdktools>
@ -18,9 +21,9 @@ public Plugin myinfo =
{ {
name = "Admin Panel", name = "Admin Panel",
author = "Jackz", author = "Jackz",
description = "", description = "Plugin to integrate with admin panel",
version = PLUGIN_VERSION, version = "1.0.0",
url = "" url = "https://github.com/jackzmc/l4d2-admin-dash"
}; };
ConVar cvar_debug; ConVar cvar_debug;
@ -82,10 +85,10 @@ public void OnPluginStart()
#define DATE_FORMAT "%F at %I:%M %p" #define DATE_FORMAT "%F at %I:%M %p"
Action Command_PanelStatus(int client, int args) { Action Command_PanelStatus(int client, int args) {
ReplyToCommand(client, "Active: %b", updateTimer != null); ReplyToCommand(client, "Active: %b", updateTimer != null);
ReplyToCommand(client, "Last Error Code: %d", lastErrorCode);
ReplyToCommand(client, "#Players: %d", numberOfPlayers); ReplyToCommand(client, "#Players: %d", numberOfPlayers);
ReplyToCommand(client, "Update Interval: %0f s", fastUpdateMode ? UPDATE_INTERVAL : UPDATE_INTERVAL_SLOW); ReplyToCommand(client, "Update Interval: %0f s", fastUpdateMode ? UPDATE_INTERVAL : UPDATE_INTERVAL_SLOW);
char buffer[32]; char buffer[32];
ReplyToCommand(client, "Last Error Code: %d", lastErrorCode);
if(lastSuccessTime > 0) if(lastSuccessTime > 0)
FormatTime(buffer, sizeof(buffer), DATE_FORMAT, lastSuccessTime); FormatTime(buffer, sizeof(buffer), DATE_FORMAT, lastSuccessTime);
else else
@ -99,7 +102,7 @@ void TryStartTimer(bool fast = true) {
fastUpdateMode = fast; fastUpdateMode = fast;
float interval = fast ? UPDATE_INTERVAL : UPDATE_INTERVAL_SLOW; float interval = fast ? UPDATE_INTERVAL : UPDATE_INTERVAL_SLOW;
updateTimer = CreateTimer(interval, Timer_PostStatus, _, TIMER_REPEAT); updateTimer = CreateTimer(interval, Timer_PostStatus, _, TIMER_REPEAT);
PrintToServer("[AdminPanel] Timer created, updating every %.1f seconds", interval); PrintToServer("[AdminPanel] Updating every %.1f seconds", interval);
} }
} }
@ -205,7 +208,7 @@ void Callback_PostStatus(HTTPResponse response, any value, const char[] error) {
PrintToServer("[AdminPanel] Response: OK/204"); PrintToServer("[AdminPanel] Response: OK/204");
// We have subscribers, kill timer and recreate it in fast mode (if not already): // We have subscribers, kill timer and recreate it in fast mode (if not already):
if(!fastUpdateMode) { if(!fastUpdateMode) {
PrintToServer("[AdminPanel] We have subscribers, increasing interval"); PrintToServer("[AdminPanel] Switching to fast update interval for active viewers.");
if(updateTimer != null) if(updateTimer != null)
delete updateTimer; delete updateTimer;
TryStartTimer(true); TryStartTimer(true);
@ -215,7 +218,7 @@ void Callback_PostStatus(HTTPResponse response, any value, const char[] error) {
lastErrorCode = 0; lastErrorCode = 0;
// We have no subscribers, kill timer and recreate it in slow mode (if not already): // We have no subscribers, kill timer and recreate it in slow mode (if not already):
if(fastUpdateMode) { if(fastUpdateMode) {
PrintToServer("[AdminPanel] No subscribers, decreasing interval"); PrintToServer("[AdminPanel] Switching to slow update interval, no viewers");
if(updateTimer != null) if(updateTimer != null)
delete updateTimer; delete updateTimer;
TryStartTimer(false); TryStartTimer(false);
@ -366,6 +369,7 @@ JSONObject GetPlayer(int client) {
player.SetInt("joinTime", playerJoinTime[client]); player.SetInt("joinTime", playerJoinTime[client]);
player.SetInt("permHealth", GetEntProp(client, Prop_Send, "m_iHealth")); player.SetInt("permHealth", GetEntProp(client, Prop_Send, "m_iHealth"));
if(team == 2) { if(team == 2) {
// Include idle players (player here is their idle bot)
if(IsFakeClient(client)) { if(IsFakeClient(client)) {
int idlePlayer = L4D_GetIdlePlayerOfBot(client); int idlePlayer = L4D_GetIdlePlayerOfBot(client);
if(idlePlayer > 0) { if(idlePlayer > 0) {

View file

@ -559,6 +559,19 @@ stock void PrintChatToAdmins(const char[] format, any ...) {
} }
PrintToServer("%s", buffer); PrintToServer("%s", buffer);
} }
stock void CPrintChatToAdmin(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) {
CPrintToChat(i, "%s", buffer);
}
}
}
PCrintToServer("%s", buffer);
}
stock bool IsValidAdmin(int client, const char[] flags) { stock bool IsValidAdmin(int client, const char[] flags) {
int ibFlags = ReadFlagString(flags); int ibFlags = ReadFlagString(flags);
if ((GetUserFlagBits(client) & ibFlags) == ibFlags) { if ((GetUserFlagBits(client) & ibFlags) == ibFlags) {
@ -569,20 +582,6 @@ stock bool IsValidAdmin(int client, const char[] flags) {
return false; return false;
} }
stock void NotifyAllAdmins(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 && admin.ImmunityLevel > 0) {
PrintToChat(i, "%s", buffer);
}
}
}
PrintToServer("%s", buffer);
}
stock float GetNearestEntityDistance(int originEntity, char[] classname) { stock float GetNearestEntityDistance(int originEntity, char[] classname) {
float compareVec[3], entityVecOrigin[3]; float compareVec[3], entityVecOrigin[3];
GetEntPropVector(originEntity, Prop_Send, "m_vecOrigin", compareVec); GetEntPropVector(originEntity, Prop_Send, "m_vecOrigin", compareVec);

View file

@ -10,6 +10,7 @@
#include <sdkhooks> #include <sdkhooks>
#include <jutils> #include <jutils>
#include <left4dhooks> #include <left4dhooks>
#include <multicolors>
enum { enum {
Immune_None, Immune_None,
@ -18,15 +19,6 @@ enum {
} }
bool lateLoaded, isFinaleEnding; bool lateLoaded, isFinaleEnding;
// bool isPlayerTroll[MAXPLAYERS+1], isUnderAttack[MAXPLAYERS+1];
// ImmunityFlag immunityFlags[MAXPLAYERS+1];
// int iJoinTime[MAXPLAYERS+1];
// int iIdleStartTime[MAXPLAYERS+1];
// int iLastFFTime[MAXPLAYERS+1];
// int iJumpAttempts[MAXPLAYERS+1];
// float playerTotalDamageFF[MAXPLAYERS+1];
// float autoFFScaleFactor[MAXPLAYERS+1];
enum struct PlayerData { enum struct PlayerData {
int joinTime; int joinTime;
@ -107,6 +99,7 @@ public void OnPluginStart() {
AutoExecConfig(true, "l4d2_tkstopper"); AutoExecConfig(true, "l4d2_tkstopper");
HookEvent("finale_vehicle_ready", Event_FinaleVehicleReady); HookEvent("finale_vehicle_ready", Event_FinaleVehicleReady);
HookEvent("finale_start", Event_FinaleStart);
HookEvent("player_team", Event_PlayerDisconnect); HookEvent("player_team", Event_PlayerDisconnect);
HookEvent("charger_carry_start", Event_ChargerCarry); HookEvent("charger_carry_start", Event_ChargerCarry);
@ -140,7 +133,7 @@ public void OnPluginStart() {
} }
LoadTranslations("common.phrases"); LoadTranslations("common.phrases");
} }
public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[] newValue) { void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[] newValue) {
cvar.GetString(gamemode, sizeof(gamemode)); cvar.GetString(gamemode, sizeof(gamemode));
if(StrEqual(gamemode, "coop")) { if(StrEqual(gamemode, "coop")) {
isEnabled = true; isEnabled = true;
@ -151,55 +144,17 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Special Infected Events // Special Infected Events
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
public Action Event_ChargerCarry(Event event, const char[] name, bool dontBroadcast) { Action Event_ChargerCarry(Event event, const char[] name, bool dontBroadcast) {
int userid = event.GetInt("victim"); return SetUnderAttack("charger_carry_start");
int victim = GetClientOfUserId(userid);
if(victim) {
if(StrEqual(name, "charger_carry_start")) {
pData[victim].underAttack = true;
} else {
CreateTimer(1.0, Timer_StopSpecialAttackImmunity, userid);
}
}
return Plugin_Continue;
} }
Action Event_HunterPounce(Event event, const char[] name, bool dontBroadcast) {
public Action Event_HunterPounce(Event event, const char[] name, bool dontBroadcast) { return SetUnderAttack(event, "lunge_pounce");
int userid = event.GetInt("victim");
int victim = GetClientOfUserId(userid);
if(victim) {
if(StrEqual(name, "lunge_pounce")) {
pData[victim].underAttack = true;
} else {
CreateTimer(1.0, Timer_StopSpecialAttackImmunity, userid);
}
}
return Plugin_Continue;
} }
Action Event_SmokerChoke(Event event, const char[] name, bool dontBroadcast) {
public Action Event_SmokerChoke(Event event, const char[] name, bool dontBroadcast) { return SetUnderAttack("choke_start");
int userid = event.GetInt("victim");
int victim = GetClientOfUserId(userid);
if(victim) {
if(StrEqual(name, "choke_start")) {
pData[victim].underAttack = true;
} else {
CreateTimer(1.0, Timer_StopSpecialAttackImmunity, userid);
}
}
return Plugin_Continue;
} }
public Action Event_JockeyRide(Event event, const char[] name, bool dontBroadcast) { Action Event_JockeyRide(Event event, const char[] name, bool dontBroadcast) {
int userid = event.GetInt("victim"); return SetUnderAttack("jockey_ride");
int victim = GetClientOfUserId(userid);
if(victim) {
if(StrEqual(name, "jockey_ride")) {
pData[victim].underAttack = true;
} else {
CreateTimer(1.0, Timer_StopSpecialAttackImmunity, userid);
}
}
return Plugin_Continue;
} }
Action Timer_StopSpecialAttackImmunity(Handle h, int userid) { Action Timer_StopSpecialAttackImmunity(Handle h, int userid) {
@ -209,6 +164,19 @@ Action Timer_StopSpecialAttackImmunity(Handle h, int userid) {
} }
return Plugin_Continue; return Plugin_Continue;
} }
Action SetUnderAttack(Event event, const char[] checkString) {
int userid = event.GetInt("victim");
int victim = GetClientOfUserId(userid);
if(victim) {
if(StrEqual(name, checkString)) {
pData[victim].underAttack = true;
} else {
CreateTimer(1.0, Timer_StopSpecialAttackImmunity, userid);
}
}
return Plugin_Continue;
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// IDLE // IDLE
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -233,16 +201,14 @@ public Action Event_PlayerToBot(Event event, char[] name, bool dontBroadcast) {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Misc events // Misc events
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
public void Event_FinaleVehicleReady(Event event, const char[] name, bool dontBroadcast) { void Event_FinaleVehicleReady(Event event, const char[] name, bool dontBroadcast) {
isFinaleEnding = true; isFinaleEnding = true;
for(int i = 1; i <= MaxClients; i++) { WarnStillMarked();
if(pData[i].isTroll && IsClientConnected(i) && IsClientInGame(i)) {
PrintChatToAdmins("Note: %N is still marked as troll and will be banned after this game. Use \"/ignore <player> tk\" to ignore them.", i);
}
}
PrintToServer("[TKStopper] Escape vehicle active, 2x rff in effect"); PrintToServer("[TKStopper] Escape vehicle active, 2x rff in effect");
} }
void Event_FinaleStart(Event event, const char[] name, bool dontBroadcast) {
WarnStillMarked();
}
public void OnMapEnd() { public void OnMapEnd() {
isFinaleEnding = false; isFinaleEnding = false;
} }
@ -272,13 +238,17 @@ public void OnClientDisconnect(int client) {
} }
// Only clear things when they fully left on their own accord: // Only clear things when they fully left on their own accord:
public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast) { void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast) {
if(!event.GetBool("disconnect") || !isEnabled) return; if(!event.GetBool("disconnect") || !isEnabled) return;
int client = GetClientOfUserId(event.GetInt("userid")); int client = GetClientOfUserId(event.GetInt("userid"));
if(client > 0 && event.GetInt("team") <= 2) { if(client > 0 && event.GetInt("team") <= 2) {
if (pData[client].isTroll && !IsFakeClient(client)) { if (pData[client].isTroll && !IsFakeClient(client)) {
BanClient(client, hBanTime.IntValue, BANFLAG_AUTO | BANFLAG_AUTHID, "Excessive FF (Auto)", "Excessive Friendly Fire", "TKStopper"); BanClient(client, hBanTime.IntValue, BANFLAG_AUTO | BANFLAG_AUTHID, "Excessive FF (Auto)", "Excessive Friendly Fire", "TKStopper");
if(hBanTime.IntValue > 0)
CPrintChatToAdmins("{olive}%N{default} has been banned for %d minutes (marked as troll). If this was a mistake, you can discard their ban from the admin panel at {yellow}https://admin.jackz.me", client, hBanTime.IntValue);
else
CPrintChatToAdmins("{olive}%N{default} has been permanently banned (marked as troll). If this was a mistake, you can discard their ban from the admin panel at {yellow}https://admin.jackz.me", client);
pData[client].isTroll = false; pData[client].isTroll = false;
} }
@ -286,7 +256,7 @@ public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroa
float minutesSinceiLastFFTime = GetLastFFMinutes(client); float minutesSinceiLastFFTime = GetLastFFMinutes(client);
float activeRate = GetActiveRate(client); float activeRate = GetActiveRate(client);
PrintToConsoleAll("[TKStopper] FF Summary for %N:", client); PrintToConsoleAll("[TKStopper] FF Summary for %N:", client);
PrintToConsoleAll("\t\t%.2f TK-FF buffer (%.2f total ff, %d freq.) | %.3f (buf %f) rFF rate | lastff %.1f min ago | %d suicide jumps", PrintToConsoleAll("\t%.2f TK-FF buffer (%.2f total ff, %d freq.) | %.3f (buf %f) rFF rate | lastff %.1f min ago | %d suicide jumps",
pData[client].TKDamageBuffer, pData[client].TKDamageBuffer,
pData[client].totalDamageFF, pData[client].totalDamageFF,
pData[client].totalFFCount, pData[client].totalFFCount,
@ -306,7 +276,7 @@ public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroa
} }
} }
public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, float& damage, int& damagetype, int& weapon, float damageForce[3], float damagePosition[3]) { Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, float& damage, int& damagetype, int& weapon, float damageForce[3], float damagePosition[3]) {
if(isEnabled && damage > 0.0 && victim <= MaxClients && attacker <= MaxClients && attacker > 0 && victim > 0) { if(isEnabled && damage > 0.0 && victim <= MaxClients && attacker <= MaxClients && attacker > 0 && victim > 0) {
if(GetClientTeam(victim) != GetClientTeam(attacker) || attacker == victim) if(GetClientTeam(victim) != GetClientTeam(attacker) || attacker == victim)
return Plugin_Continue; return Plugin_Continue;
@ -352,15 +322,15 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo
if(pData[victim].jumpAttempts > hSuicideLimit.IntValue) { if(pData[victim].jumpAttempts > hSuicideLimit.IntValue) {
if(hSuicideAction.IntValue == 1) { if(hSuicideAction.IntValue == 1) {
LogMessage("[NOTICE] Kicking %N for suicide attempts", victim, hBanTime.IntValue); LogMessage("[NOTICE] Kicking %N for suicide attempts", victim, hBanTime.IntValue);
NotifyAllAdmins("[Notice] Kicking %N for suicide attempts", victim, hBanTime.IntValue); PrintToChatAdmins("[Notice] Kicking %N for suicide attempts", victim, hBanTime.IntValue);
KickClient(victim, "Troll"); KickClient(victim, "Troll");
} else if(hSuicideAction.IntValue == 2) { } else if(hSuicideAction.IntValue == 2) {
LogMessage("[NOTICE] Banning %N for suicide attempts for %d minutes.", victim, hBanTime.IntValue); LogMessage("[NOTICE] Banning %N for suicide attempts for %d minutes.", victim, hBanTime.IntValue);
NotifyAllAdmins("[Notice] Banning %N for suicide attempts for %d minutes.", victim, hBanTime.IntValue); PrintToChatAdmins("[Notice] Banning %N for suicide attempts for %d minutes.", victim, hBanTime.IntValue);
BanClient(victim, hBanTime.IntValue, BANFLAG_AUTO | BANFLAG_AUTHID, "Suicide fall attempts", "Troll", "TKStopper"); BanClient(victim, hBanTime.IntValue, BANFLAG_AUTO | BANFLAG_AUTHID, "Suicide fall attempts", "Troll", "TKStopper");
} else if(hSuicideAction.IntValue == 3) { } else if(hSuicideAction.IntValue == 3) {
LogMessage("[NOTICE] %N will be banned for suicide attempts for %d minutes. ", victim, hBanTime.IntValue); LogMessage("[NOTICE] %N will be banned for suicide attempts for %d minutes. ", victim, hBanTime.IntValue);
NotifyAllAdmins("[Notice] %N will be banned for suicide attempts for %d minutes. Use \"/ignore <player> tk\" to make them immune.", victim, hBanTime.IntValue); PrintToChatAdmins("[Notice] %N will be banned for suicide attempts for %d minutes. Use \"/ignore <player> tk\" to make them immune.", victim, hBanTime.IntValue);
pData[victim].isTroll = true; pData[victim].isTroll = true;
} }
} }
@ -427,15 +397,15 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo
if(!pData[attacker].pendingAction) { if(!pData[attacker].pendingAction) {
if(hTKAction.IntValue == 1) { if(hTKAction.IntValue == 1) {
LogMessage("[TKStopper] Kicking %N for excessive FF (%.2f HP)", attacker, pData[attacker].TKDamageBuffer); LogMessage("[TKStopper] Kicking %N for excessive FF (%.2f HP)", attacker, pData[attacker].TKDamageBuffer);
NotifyAllAdmins("[Notice] Kicking %N for excessive FF (%.2f HP)", attacker, pData[attacker].TKDamageBuffer); PrintToChatAdmins("[Notice] Kicking %N for excessive FF (%.2f HP)", attacker, pData[attacker].TKDamageBuffer);
KickClient(attacker, "Excessive FF"); KickClient(attacker, "Excessive FF");
} else if(hTKAction.IntValue == 2) { } else if(hTKAction.IntValue == 2) {
LogMessage("[TKStopper] Banning %N for excessive FF (%.2f HP) for %d minutes.", attacker, pData[attacker].TKDamageBuffer, hBanTime.IntValue); LogMessage("[TKStopper] Banning %N for excessive FF (%.2f HP) for %d minutes.", attacker, pData[attacker].TKDamageBuffer, hBanTime.IntValue);
NotifyAllAdmins("[Notice] Banning %N for excessive FF (%.2f HP) for %d minutes.", attacker, pData[attacker].TKDamageBuffer, hBanTime.IntValue); PrintToChatAdmins("[Notice] Banning %N for excessive FF (%.2f HP) for %d minutes.", attacker, pData[attacker].TKDamageBuffer, hBanTime.IntValue);
BanClient(attacker, hBanTime.IntValue, BANFLAG_AUTO | BANFLAG_AUTHID, "Excessive FF (Auto)", "Excessive Friendly Fire (Automatic)", "TKStopper"); BanClient(attacker, hBanTime.IntValue, BANFLAG_AUTO | BANFLAG_AUTHID, "Excessive FF (Auto)", "Excessive Friendly Fire (Automatic)", "TKStopper");
} else if(hTKAction.IntValue == 3) { } else if(hTKAction.IntValue == 3) {
LogMessage("[TKStopper] %N will be banned for FF on disconnect (%.2f HP) for %d minutes. ", attacker, pData[attacker].TKDamageBuffer, hBanTime.IntValue); LogMessage("[TKStopper] %N will be banned for FF on disconnect (%.2f HP) for %d minutes. ", attacker, pData[attacker].TKDamageBuffer, hBanTime.IntValue);
NotifyAllAdmins("[Notice] %N will be banned for FF on disconnect (%.2f HP) for %d minutes. Use \"/ignore <player> tk\" to make them immune.", attacker, pData[attacker].TKDamageBuffer, hBanTime.IntValue); CPrintToChatAdmins("[Notice] %N will be banned for %d minutes for attempted teamkilling (%.2f HP) when they disconnect. Use {olive}/ignore <player> tk{default} to make them immune.", attacker, hBanTime.IntValue, pData[attacker].TKDamageBuffer);
pData[attacker].isTroll = true; pData[attacker].isTroll = true;
} }
pData[attacker].pendingAction = true; pData[attacker].pendingAction = true;
@ -482,7 +452,7 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo
/// COMMANDS /// COMMANDS
public Action Command_TKInfo(int client, int args) { Action Command_TKInfo(int client, int args) {
int time = GetTime(); int time = GetTime();
if(!isEnabled) { if(!isEnabled) {
ReplyToCommand(client, "Warn: Plugin is disabled in current gamemode (%s)", gamemode); ReplyToCommand(client, "Warn: Plugin is disabled in current gamemode (%s)", gamemode);
@ -509,28 +479,28 @@ public Action Command_TKInfo(int client, int args) {
return Plugin_Handled; return Plugin_Handled;
} }
int target = target_list[0]; int target = target_list[0];
ReplyToCommand(client, "FF Review for '%N':", target); CReplyToCommand(client, "FF Review for {yellow}%N", target);
if(pData[target].isTroll) { if(pData[target].isTroll) {
ReplyToCommand(client, "- will be banned on disconnect for TK -", target); CReplyToCommand(client, "{olive}- will be banned on disconnect for TK -", target);
} }
if(pData[target].immunityFlags == Immune_TK) { if(pData[target].immunityFlags == Immune_TK) {
ReplyToCommand(client, "Immunity: Teamkiller Detection", target); CReplyToCommand(client, "Immunity: {yellow}Teamkiller Detection", target);
} else if(pData[target].immunityFlags == Immune_RFF) { } else if(pData[target].immunityFlags == Immune_RFF) {
ReplyToCommand(client, "Immunity: Auto reverse-ff", target); CReplyToCommand(client, "Immunity: {yellow}Auto reverse-ff", target);
} else if(view_as<int>(pData[target].immunityFlags) > 0) { } else if(view_as<int>(pData[target].immunityFlags) > 0) {
ReplyToCommand(client, "Immunity: Teamkiller Detection, Auto reverse-ff", target); CReplyToCommand(client, "Immunity: {yellow}Teamkiller Detection, Auto reverse-ff", target);
} else { } else {
ReplyToCommand(client, "Immunity: (none, use /ignore <player> [immunity] to toggle)", target); CReplyToCommand(client, "Immunity: (none, use {green}/ignore <player> [immunity]{default} to toggle)", target);
} }
float minutesSinceiLastFFTime = GetLastFFMinutes(target); float minutesSinceiLastFFTime = GetLastFFMinutes(target);
float activeRate = GetActiveRate(target); float activeRate = GetActiveRate(target);
ReplyToCommand(client, "FF Frequency: %d (active %d, %d forgotten)", pData[target].totalFFCount, pData[target].ffCount, (pData[target].totalFFCount - pData[target].ffCount)); CReplyToCommand(client, "FF Frequency: {yellow}%d {default}(recent: %d, %d forgiven)", pData[target].totalFFCount, pData[target].ffCount, (pData[target].totalFFCount - pData[target].ffCount));
ReplyToCommand(client, "Total FF Damage: %.1f HP (%.1f min ago last ff)", pData[target].totalDamageFF, minutesSinceiLastFFTime); CReplyToCommand(client, "Total FF Damage: {yellow}%.1f HP{default} (last fff %.1f min ago)", pData[target].totalDamageFF, minutesSinceiLastFFTime);
if(~pData[target].immunityFlags & Immune_TK) if(~pData[target].immunityFlags & Immune_TK)
ReplyToCommand(client, "Recent FF (TKDetectBuff): %.1f", pData[target].TKDamageBuffer); CReplyToCommand(client, "Recent FF (TKDetectBuffer): {yellow}%.1f", pData[target].TKDamageBuffer);
if(~pData[target].immunityFlags & Immune_RFF) if(~pData[target].immunityFlags & Immune_RFF)
ReplyToCommand(client, "Auto Reverse-FF: %.1fx return rate", activeRate); CReplyToCommand(client, "Auto Reverse-FF: {yellow}%.1fx return rate", activeRate);
ReplyToCommand(client, "Attempted suicide jumps: %d", pData[target].jumpAttempts); CReplyToCommand(client, "Attempted suicide jumps: {yellow}%d", pData[target].jumpAttempts);
} else { } else {
for(int i = 1; i <= MaxClients; i++) { for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) { if(IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) {
@ -557,25 +527,25 @@ public Action Command_TKInfo(int client, int args) {
} }
public Action Command_IgnorePlayer(int client, int args) { Action Command_IgnorePlayer(int client, int args) {
char arg1[32], arg2[16]; char arg1[32], arg2[16];
GetCmdArg(1, arg1, sizeof(arg1)); GetCmdArg(1, arg1, sizeof(arg1));
GetCmdArg(2, arg2, sizeof(arg2)); GetCmdArg(2, arg2, sizeof(arg2));
if(args < 2) { if(args < 2) {
ReplyToCommand(client, "Usage: sm_ignore <player> <tk/teamkill/rff/reverseff>"); CReplyToCommand(client, "Usage: {yellow}sm_ignore <player> <tk|teamkill / rff|reverseff>");
return Plugin_Handled; return Plugin_Handled;
} }
int flags = 0; int flags = 0;
if(StrEqual(arg2, "tk") || StrEqual(arg2, "teamkill")) { if(StrEqual(arg2, "tk") || StrEqual(arg2, "teamkill")) {
flags = Immune_TK; flags = Immune_TK;
} else if(StrEqual(arg2, "all") || StrEqual(arg2, "a")) { } else if(arg2[0] == 'a') {
flags = Immune_TK | Immune_RFF; flags = Immune_TK | Immune_RFF;
} else if(StrEqual(arg2, "reverseff") || StrEqual(arg2, "rff")) { } else if(StrEqual(arg2, "reverseff") || StrEqual(arg2, "rff")) {
flags = Immune_RFF; flags = Immune_RFF;
} else { } else {
ReplyToCommand(client, "Usage: sm_ignore <player> <tk/teamkill/rff/reverseff>"); CReplyToCommand(client, "Usage: {yellow}sm_ignore <player> <tk|teamkill / rff|reverseff>");
return Plugin_Handled; return Plugin_Handled;
} }
@ -607,10 +577,10 @@ public Action Command_IgnorePlayer(int client, int args) {
if (flags & Immune_TK) { if (flags & Immune_TK) {
if (pData[target].immunityFlags & Immune_TK) { if (pData[target].immunityFlags & Immune_TK) {
LogAction(client, target, "\"%L\" re-enabled teamkiller detection for \"%L\"", client, target); LogAction(client, target, "\"%L\" re-enabled teamkiller detection for \"%L\"", client, target);
ShowActivity2(client, "[FTT] ", "%N has re-enabled teamkiller detection for %N", client, target); CShowActivity2(client, "[FTT] ", "{yellow}%N has re-enabled teamkiller detection for {olive}%N", client, target);
} else { } else {
LogAction(client, target, "\"%L\" ignored teamkiller detection for \"%L\"", client, target); LogAction(client, target, "\"%L\" ignored teamkiller detection for \"%L\"", client, target);
ShowActivity2(client, "[FTT] ", "%N has ignored teamkiller detection for %N", client, target); CShowActivity2(client, "[FTT] ", "{yellow}%N has ignored teamkiller detection for {olive}%N", client, target);
} }
pData[target].immunityFlags ^= Immune_TK; pData[target].immunityFlags ^= Immune_TK;
} }
@ -620,7 +590,7 @@ public Action Command_IgnorePlayer(int client, int args) {
LogAction(client, target, "\"%L\" re-enabled auto reverse friendly-fire for \"%L\"", client, target); LogAction(client, target, "\"%L\" re-enabled auto reverse friendly-fire for \"%L\"", client, target);
} else { } else {
LogAction(client, target, "\"%L\" disabled auto reverse friendly-fire for \"%L\"", client, target); LogAction(client, target, "\"%L\" disabled auto reverse friendly-fire for \"%L\"", client, target);
ShowActivity2(client, "[FTT] ", "%N has disabled auto reverse friendly-fire for %N", client, target); CShowActivity2(client, "[FTT] ", "{yellow}%N has disabled auto reverse friendly-fire for {olive}%N", client, target);
pData[target].autoRFFScaleFactor = 0.0; pData[target].autoRFFScaleFactor = 0.0;
} }
pData[target].immunityFlags ^= Immune_RFF; pData[target].immunityFlags ^= Immune_RFF;
@ -654,9 +624,9 @@ void GetImmunityFlagName(int flag, char[] buffer, int bufferLength) {
if(flag == Immune_RFF) { if(flag == Immune_RFF) {
strcopy(buffer, bufferLength, "Reverse Friendly-Fire"); strcopy(buffer, bufferLength, "Reverse Friendly-Fire");
} else if(flag == Immune_TK) { } else if(flag == Immune_TK) {
strcopy(buffer, bufferLength, "Reverse Friendly-Fire"); strcopy(buffer, bufferLength, "Teamkiller Detection");
} else { } else {
strcopy(buffer, bufferLength, "-unknown flag-"); strcopy(buffer, bufferLength, "-error: unknown flag-");
} }
} }
@ -674,6 +644,13 @@ void _CheckNative(int target, int flag) {
ThrowNativeError(SP_ERROR_NATIVE, "Flag is invalid"); ThrowNativeError(SP_ERROR_NATIVE, "Flag is invalid");
} }
} }
void WarnStillMarked() {
for(int i = 1; i <= MaxClients; i++) {
if(pData[i].isTroll && IsClientConnected(i) && IsClientInGame(i)) {
CPrintChatToAdmins("Note: {yellow}%N is still marked as troll and will be banned after this game. Use {olive}/ignore <player> tk{default} to ignore them.", i);
}
}
}
/// STOCKS /// STOCKS
float GetLastFFMinutes(int client) { float GetLastFFMinutes(int client) {

View file

@ -5,7 +5,7 @@
#define PLUGIN_VERSION "1.0" #define PLUGIN_VERSION "1.0"
#define MAX_PLAYER_HISTORY 25 #define MAX_PLAYER_HISTORY 25
#define MAX_NOTES_TO_SHOW 10 #define MAX_NOTES_TO_SHOW 5
#define DATABASE_CONFIG_NAME "stats" #define DATABASE_CONFIG_NAME "stats"
#include <sourcemod> #include <sourcemod>
@ -374,7 +374,7 @@ public void Event_FirstSpawn(Event event, const char[] name, bool dontBroadcast)
if(client > 0 && client <= MaxClients && !IsFakeClient(client)) { if(client > 0 && client <= MaxClients && !IsFakeClient(client)) {
static char auth[32]; static char auth[32];
GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth)); GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth));
DB.Format(query, sizeof(query), "SELECT notes.content, stats_users.last_alias, markedBy FROM `notes` JOIN stats_users ON markedBy = stats_users.steamid WHERE notes.`steamid` = '%s'", auth); DB.Format(query, sizeof(query), "SELECT notes.content, stats_users.last_alias, markedBy FROM `notes` JOIN stats_users ON markedBy = stats_users.steamid WHERE notes.`steamid` = '%s' ORDER BY id ASC", auth);
DB.Query(DB_FindNotes, query, GetClientUserId(client)); DB.Query(DB_FindNotes, query, GetClientUserId(client));
} }
} }
@ -417,7 +417,9 @@ public void DB_FindNotes(Database db, DBResultSet results, const char[] error, a
CPrintChatToAdmins("{yellow}> Notes for %N", client); CPrintChatToAdmins("{yellow}> Notes for %N", client);
int actions = 0; int actions = 0;
int repP = 0, repN = 0; int repP = 0, repN = 0;
int count = 0;
while(results.FetchRow()) { while(results.FetchRow()) {
count++;
DBResult result; DBResult result;
results.FetchString(0, reason, sizeof(reason)); results.FetchString(0, reason, sizeof(reason));
results.FetchString(1, noteCreator, sizeof(noteCreator), result); results.FetchString(1, noteCreator, sizeof(noteCreator), result);
@ -432,10 +434,13 @@ public void DB_FindNotes(Database db, DBResultSet results, const char[] error, a
repP++; repP++;
} else if(StrEqual(reason, "-rep")) { } else if(StrEqual(reason, "-rep")) {
repN++; repN++;
} else { } else if(count < MAX_NOTES_TO_SHOW) {
CPrintChatToAdmins(" {olive}%s: {default}%s", noteCreator, reason); CPrintChatToAdmins(" {olive}%s: {default}%s", noteCreator, reason);
} }
} }
if(count >= MAX_NOTES_TO_SHOW) {
CPrintChatToAdmins(" ... and {olive}%d {default}more", MAX_NOTES_COUNT - count);
}
if(actions > 0) { if(actions > 0) {
CPrintChatToAdmins(" > {olive}%d Auto Actions Applied", actions); CPrintChatToAdmins(" > {olive}%d Auto Actions Applied", actions);