mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-06 10:03:20 +00:00
update plugins
This commit is contained in:
parent
42497b76f2
commit
509c6a1b21
18 changed files with 1043 additions and 316 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
plugins/l4d2_detections.smx
Normal file
BIN
plugins/l4d2_detections.smx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -62,7 +62,7 @@ public void OnClientAuthorized(int client, const char[] auth) {
|
|||
if(!StrEqual(auth, "BOT", true)) {
|
||||
static char query[256], ip[32];
|
||||
GetClientIP(client, ip, sizeof(ip));
|
||||
Format(query, sizeof(query), "SELECT `reason`, `steamid`, `expired` FROM `bans` WHERE `steamid` = '%s' OR ip = '?'", auth, ip);
|
||||
Format(query, sizeof(query), "SELECT `reason`, `steamid`, `expired` FROM `bans` WHERE `steamid` = 'STEAM_%:%:%s' OR ip = '?'", auth[10], ip);
|
||||
g_db.Query(DB_OnConnectCheck, query, GetClientUserId(client), DBPrio_High);
|
||||
}
|
||||
}
|
||||
|
@ -174,6 +174,7 @@ public void DB_OnConnectCheck(Database db, DBResultSet results, const char[] err
|
|||
KickClient(client, "You have been banned from this server.");
|
||||
} 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);
|
||||
|
|
|
@ -274,8 +274,15 @@ stock bool SpawnMinigun(const float vPos[3], const float vAng[3]) {
|
|||
}
|
||||
}
|
||||
|
||||
stock int GiveClientWeaponLasers(int client, const char[] wpnName) {
|
||||
int entity = GiveClientWeapon(client, wpnName);
|
||||
if(entity != -1) {
|
||||
SetEntProp(entity, Prop_Send, "m_upgradeBitVec", 4);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
stock bool GiveClientWeapon(int client, const char[] wpnName, bool lasers) {
|
||||
stock int GiveClientWeapon(int client, const char[] wpnName) {
|
||||
static char sTemp[64];
|
||||
float pos[3];
|
||||
GetClientAbsOrigin(client, pos);
|
||||
|
@ -286,12 +293,10 @@ stock bool GiveClientWeapon(int client, const char[] wpnName, bool lasers) {
|
|||
DispatchSpawn(entity);
|
||||
TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR);
|
||||
|
||||
if(lasers) SetEntProp(entity, Prop_Send, "m_upgradeBitVec", 4);
|
||||
|
||||
EquipPlayerWeapon(client, entity);
|
||||
return true;
|
||||
return entity;
|
||||
}else{
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
stock int GetNearestEntity(int client, char[] classname)
|
||||
|
@ -589,6 +594,12 @@ stock void StringToLower(char[] str) {
|
|||
str[i] = CharToLower(str[i]);
|
||||
}
|
||||
}
|
||||
stock void StringToUpper(char[] str) {
|
||||
int len = strlen(str);
|
||||
for(int i = 0; i < len; i++) {
|
||||
str[i] = CharToUpper(str[i]);
|
||||
}
|
||||
}
|
||||
|
||||
stock int GetRealClient(int client) {
|
||||
if(IsFakeClient(client)) {
|
||||
|
|
|
@ -19,7 +19,9 @@ int iJoinTime[MAXPLAYERS+1], iIdleStartTime[MAXPLAYERS+1], iJumpAttempts[MAXPLAY
|
|||
float playerTotalDamageFF[MAXPLAYERS+1];
|
||||
int lastFF[MAXPLAYERS+1];
|
||||
|
||||
ConVar hForgivenessTime, hBanTime, hThreshold, hJoinTime, hTKAction, hSuicideAction, hSuicideLimit;
|
||||
float autoFFScaleFactor[MAXPLAYERS+1];
|
||||
|
||||
ConVar hForgivenessTime, hBanTime, hThreshold, hJoinTime, hTKAction, hSuicideAction, hSuicideLimit, hFFAutoScaleAmount, hFFAutoScaleForgivenessAmount, hFFAutoScaleMaxRatio, hFFAutoScaleIgnoreAdmins;
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
|
@ -51,7 +53,11 @@ public void OnPluginStart()
|
|||
hTKAction = CreateConVar("l4d2_tk_action", "3", "How should the TK be punished?\n0 = No action (No message), 1 = Kick, 2 = Instant Ban, 3 = Ban on disconnect", FCVAR_NONE, true, 0.0, true, 3.0);
|
||||
hSuicideAction = CreateConVar("l4d2_suicide_action", "3", "How should a suicider be punished?\n0 = No action (No message), 1 = Kick, 2 = Instant Ban, 3 = Ban on disconnect", FCVAR_NONE, true, 0.0, true, 3.0);
|
||||
hSuicideLimit = CreateConVar("l4d2_suicide_limit", "1", "How many attempts does a new joined player have until action is taken for suiciding?", FCVAR_NONE, true, 0.0);
|
||||
|
||||
|
||||
hFFAutoScaleAmount = CreateConVar("l4d2_tk_auto_ff_rate", "0.04", "The rate at which auto reverse-ff is scaled by.", FCVAR_NONE, true, 0.0);
|
||||
hFFAutoScaleMaxRatio = CreateConVar("l4d2_tk_auto_ff_max_ratio", "5.0", "The maximum amount that the reverse ff can go. 0.0 for unlimited", FCVAR_NONE, true, 0.0);
|
||||
hFFAutoScaleForgivenessAmount = CreateConVar("l4d2_tk_auto_ff_forgive_rate", "0.008", "This amount times amount of minutes since last ff is removed from ff rate", FCVAR_NONE, true, 0.0);
|
||||
hFFAutoScaleIgnoreAdmins = CreateConVar("l4d2_tk_auto_ff_ignore_admins", "1", "Should automatic reverse ff ignore admins? 0 = Admins are subjected\n1 = Admins are excempt", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
|
||||
//AutoExecConfig(true, "l4d2_tkstopper");
|
||||
|
||||
|
@ -78,6 +84,8 @@ public void OnPluginStart()
|
|||
|
||||
RegAdminCmd("sm_ignore", Command_IgnorePlayer, ADMFLAG_KICK, "Makes a player immune for any anti trolling detection for a session");
|
||||
|
||||
RegAdminCmd("sm_tkinfo", Command_TKInfo, ADMFLAG_KICK, "Debug info for TKSTopper");
|
||||
|
||||
if(lateLoaded) {
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
|
||||
|
@ -85,8 +93,18 @@ public void OnPluginStart()
|
|||
}
|
||||
}
|
||||
}
|
||||
// CreateTimer(60.0, Timer_Forgive, _, TIMER_REPEAT);
|
||||
}
|
||||
|
||||
/*public Action Timer_Forgive(Handle h) {
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && ) {
|
||||
|
||||
}
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Special Infected Events
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -173,6 +191,7 @@ public void OnMapEnd() {
|
|||
|
||||
public void OnClientPutInServer(int client) {
|
||||
iJoinTime[client] = GetTime();
|
||||
lastFF[client] = GetTime();
|
||||
SDKHook(client, SDKHook_OnTakeDamage, Event_OnTakeDamage);
|
||||
}
|
||||
|
||||
|
@ -191,21 +210,32 @@ public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroa
|
|||
BanClient(client, hBanTime.IntValue, BANFLAG_AUTO | BANFLAG_AUTHID, "Excessive FF", "Excessive Friendly Fire", "TKStopper");
|
||||
}
|
||||
isPlayerTroll[client] = false;
|
||||
autoFFScaleFactor[client] = 0.0;
|
||||
}
|
||||
|
||||
public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, float& damage, int& damagetype, int& weapon, float damageForce[3], float damagePosition[3]) {
|
||||
if(damage > 0.0 && victim <= MaxClients && attacker <= MaxClients && attacker > 0 && victim > 0) {
|
||||
if(GetUserAdmin(attacker) != INVALID_ADMIN_ID || isImmune[attacker] || IsFakeClient(attacker)) return Plugin_Continue;
|
||||
if(damagetype & DMG_BURN && IsFakeClient(attacker)) {
|
||||
damage = 0.0;
|
||||
return Plugin_Changed;
|
||||
}
|
||||
bool isAdmin = GetUserAdmin(attacker) != INVALID_ADMIN_ID;
|
||||
bool ignore = hFFAutoScaleIgnoreAdmins.BoolValue && isAdmin;
|
||||
if(isImmune[attacker] || IsFakeClient(attacker)) return Plugin_Continue;
|
||||
if(GetClientTeam(victim) != 2 || GetClientTeam(attacker) != 2 || attacker == victim) return Plugin_Continue;
|
||||
//Allow friendly firing BOTS that aren't idle players:
|
||||
//if(IsFakeClient(victim) && !HasEntProp(attacker, Prop_Send, "m_humanSpectatorUserID") || GetEntProp(attacker, Prop_Send, "m_humanSpectatorUserID") == 0) return Plugin_Continue;
|
||||
|
||||
// Stop all damage early if already marked as troll
|
||||
if(isPlayerTroll[attacker]) return Plugin_Stop;
|
||||
// Allow vanilla-damage if being attacked by special (example, charger carry)
|
||||
if(isUnderAttack[victim]) return Plugin_Continue;
|
||||
|
||||
// Is damage not caused by fire or pipebombs?
|
||||
bool isDamageDirect = damagetype & (DMG_BLAST|DMG_BURN|DMG_BLAST_SURFACE) == 0;
|
||||
int time = GetTime();
|
||||
// If is a fall within first 2 minutes, do appropiate action
|
||||
if(damagetype & DMG_FALL && attacker == victim && damage > 0.0 && time- iJoinTime[victim] <= hJoinTime.IntValue * 60000) {
|
||||
if(!isAdmin && damagetype & DMG_FALL && attacker == victim && damage > 0.0 && time - iJoinTime[victim] <= hJoinTime.IntValue * 60) {
|
||||
iJumpAttempts[victim]++;
|
||||
float pos[3];
|
||||
GetNearestPlayerPosition(victim, pos);
|
||||
|
@ -232,10 +262,28 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo
|
|||
playerTotalDamageFF[attacker] = 0.0;
|
||||
}
|
||||
playerTotalDamageFF[attacker] += damage;
|
||||
|
||||
// Auto reverse ff logic
|
||||
lastFF[attacker] = time;
|
||||
if(isDamageDirect && !ignore) {
|
||||
// Decrement any recovered FF
|
||||
float minutesSinceLastFF = (time - lastFF[attacker]) / 60.0;
|
||||
autoFFScaleFactor[attacker] -= minutesSinceLastFF * hFFAutoScaleForgivenessAmount.FloatValue;
|
||||
if(autoFFScaleFactor[attacker] < 0.0) {
|
||||
autoFFScaleFactor[attacker] = 0.0;
|
||||
}
|
||||
// Then increment
|
||||
autoFFScaleFactor[attacker] += hFFAutoScaleAmount.FloatValue * damage;
|
||||
if(hFFAutoScaleMaxRatio.FloatValue > 0.0 && autoFFScaleFactor[attacker] > hFFAutoScaleMaxRatio.FloatValue) {
|
||||
autoFFScaleFactor[attacker] = hFFAutoScaleMaxRatio.FloatValue;
|
||||
}
|
||||
if(minutesSinceLastFF > 3.0) {
|
||||
PrintToConsoleAdmins("%N new reverse ratio: %f", attacker, autoFFScaleFactor[attacker]);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for friendly fire damage
|
||||
if(playerTotalDamageFF[attacker] > hThreshold.IntValue && !IsFinaleEnding && isDamageDirect) {
|
||||
// Check for excessive friendly fire damage in short timespan
|
||||
if(!isAdmin && playerTotalDamageFF[attacker] > hThreshold.IntValue && !IsFinaleEnding && isDamageDirect) {
|
||||
LogAction(-1, attacker, "Excessive FF (%.2f HP)", playerTotalDamageFF[attacker]);
|
||||
if(hTKAction.IntValue == 1) {
|
||||
LogMessage("[NOTICE] Kicking %N for excessive FF (%.2f HP) for %d minutes.", attacker, playerTotalDamageFF[attacker], hBanTime.IntValue);
|
||||
|
@ -255,19 +303,19 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo
|
|||
}
|
||||
|
||||
// Modify damages based on criteria
|
||||
if(iJumpAttempts[victim] > 0 || L4D_IsInFirstCheckpoint(victim) || L4D_IsInLastCheckpoint(victim) || time - iJoinTime[attacker] <= hJoinTime.IntValue * 60000) {
|
||||
if(iJumpAttempts[victim] > 0 || L4D_IsInFirstCheckpoint(victim) || L4D_IsInLastCheckpoint(victim) || time - iJoinTime[attacker] <= hJoinTime.IntValue * 60) {
|
||||
// If the amount of MS is <= join time threshold * 60000 ms then cancel
|
||||
// Or if the player is in a saferoom
|
||||
// Or if the player tried to suicide jump
|
||||
damage = 0.0;
|
||||
return Plugin_Handled;
|
||||
}else if(IsFinaleEnding) {
|
||||
if(isAdmin) return Plugin_Continue;
|
||||
SDKHooks_TakeDamage(attacker, attacker, attacker, damage * 2.0);
|
||||
damage = 0.0;
|
||||
return Plugin_Changed;
|
||||
}else if(!isDamageDirect) { // Ignore fire and propane damage, mistakes can happen
|
||||
// Make the victim take slightly less, attacker more, to in event of 1-1 victim wins
|
||||
SDKHooks_TakeDamage(attacker, attacker, attacker, damage / 1.9);
|
||||
SDKHooks_TakeDamage(attacker, attacker, attacker, float(RoundToCeil(autoFFScaleFactor[attacker] * damage)));
|
||||
damage /= 2.1;
|
||||
return Plugin_Changed;
|
||||
}
|
||||
|
@ -275,6 +323,20 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo
|
|||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action Command_TKInfo(int client, int args) {
|
||||
int time = GetTime();
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) {
|
||||
float minutesSinceLastFF = (time - lastFF[i]) / 60.0;
|
||||
float activeRate = autoFFScaleFactor[i] - (minutesSinceLastFF * hFFAutoScaleForgivenessAmount.FloatValue);
|
||||
if(activeRate < 0.0) {
|
||||
activeRate = 0.0;
|
||||
}
|
||||
ReplyToCommand(client, "%N: %f TK-FF buffer | %f (active: %f), reverse FF rate | %f ff min ago | %d suicide jumps", i, playerTotalDamageFF[i], autoFFScaleFactor[i], activeRate, minutesSinceLastFF, iJumpAttempts[i]);
|
||||
}
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
public Action Command_IgnorePlayer(int client, int args) {
|
||||
char arg1[32];
|
||||
GetCmdArg(1, arg1, sizeof(arg1));
|
||||
|
@ -284,15 +346,15 @@ public Action Command_IgnorePlayer(int client, int args) {
|
|||
bool tn_is_ml;
|
||||
|
||||
if ((target_count = ProcessTargetString(
|
||||
arg1,
|
||||
client,
|
||||
target_list,
|
||||
MaxClients,
|
||||
COMMAND_FILTER_ALIVE,
|
||||
target_name,
|
||||
sizeof(target_name),
|
||||
tn_is_ml)) <= 0)
|
||||
{
|
||||
arg1,
|
||||
client,
|
||||
target_list,
|
||||
MaxClients,
|
||||
COMMAND_FILTER_ALIVE,
|
||||
target_name,
|
||||
sizeof(target_name),
|
||||
tn_is_ml)) <= 0
|
||||
) {
|
||||
ReplyToTargetError(client, target_count);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
@ -338,7 +400,7 @@ stock bool GetNearestPlayerPosition(int client, float pos[3]) {
|
|||
stock void PrintChatToAdmins(const char[] format, any ...) {
|
||||
char buffer[254];
|
||||
VFormat(buffer, sizeof(buffer), format, 2);
|
||||
for(int i = 1; i < MaxClients; i++) {
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i)) {
|
||||
AdminId admin = GetUserAdmin(i);
|
||||
if(admin != INVALID_ADMIN_ID) {
|
||||
|
@ -352,7 +414,7 @@ stock void PrintChatToAdmins(const char[] format, any ...) {
|
|||
stock void PrintToConsoleAdmins(const char[] format, any ...) {
|
||||
char buffer[254];
|
||||
VFormat(buffer, sizeof(buffer), format, 2);
|
||||
for(int i = 1; i < MaxClients; i++) {
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i)) {
|
||||
AdminId admin = GetUserAdmin(i);
|
||||
if(admin != INVALID_ADMIN_ID) {
|
||||
|
|
|
@ -54,7 +54,7 @@ public Action Timer_Check(Handle h) {
|
|||
} else if(GetTime() - startupTime > MAX_TIME_ONLINE_MS) {
|
||||
LogAction(0, -1, "Server has passed max online time threshold, will restart if remains empty");
|
||||
if(IsServerEmpty()) {
|
||||
if(++triesEmpty > 3) {
|
||||
if(++triesEmpty > 4) {
|
||||
LogAction(0, -1, "Server has passed max online time threshold and is empty after %d tries, restarting now", triesEmpty);
|
||||
ServerCommand("quit");
|
||||
}
|
||||
|
@ -86,10 +86,8 @@ bool IsServerEmptyWithOnlyBots() {
|
|||
//Returns true if there is a bot connected and there is no real players
|
||||
bool IsServerEmpty() {
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i)) {
|
||||
if(!IsFakeClient(i))
|
||||
return false;
|
||||
|
||||
if(IsClientConnected(i) && !IsFakeClient(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
218
scripting/l4d2_detections.sp
Normal file
218
scripting/l4d2_detections.sp
Normal file
|
@ -0,0 +1,218 @@
|
|||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#define PLUGIN_VERSION "1.0"
|
||||
#define BILE_NO_HORDE_THRESHOLD 20
|
||||
#define DOOR_CLOSE_THRESHOLD 5000.0
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <left4dhooks>
|
||||
#include <jutils>
|
||||
//#include <sdkhooks>
|
||||
|
||||
enum struct PlayerDetections {
|
||||
int kitPickupsSaferoom;
|
||||
int saferoomLastOpen;
|
||||
int saferoomOpenCount;
|
||||
|
||||
void Reset() {
|
||||
this.kitPickupsSaferoom = 0;
|
||||
this.saferoomLastOpen = 0;
|
||||
this.saferoomOpenCount = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Bile Detections:
|
||||
1. No commons around entowner or bile
|
||||
2. Bile already exists
|
||||
3. Player is currently vomitted on (check time?)
|
||||
4. Bile on tank (common count near tank)
|
||||
*/
|
||||
|
||||
stock bool IsPlayerBoomed(int client) {
|
||||
return GetEntPropFloat(%0, Prop_Send, "m_vomitStart") + 20.1 > GetGameTime();
|
||||
}
|
||||
stock bool IsAnyPlayerBoomed() {
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerBoomed(i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
stock bool AnyRecentBileInPlay(int ignore) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stock int GetEntityCountNear(const float[3] srcPos, float radius = 50000.0) {
|
||||
float pos[3];
|
||||
int count;
|
||||
int entity = -1;
|
||||
while( (entity = FindEntityByClassname(entity, "infected")) != INVALID_ENT_REFERENCE ) {
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", pos);
|
||||
if(GetEntProp(entity, Prop_Send, "m_clientLookatTarget") != -1 && GetVectorDistance(pos, srcPos) <= radius) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
stock int L4D_SpawnCommonInfected2(const float vPos[3], const float vAng[3] = { 0.0, 0.0, 0.0 })
|
||||
{
|
||||
int entity = CreateEntityByName("infected");
|
||||
if( entity != -1 )
|
||||
{
|
||||
DispatchSpawn(entity);
|
||||
TeleportEntity(entity, vPos, vAng, NULL_VECTOR);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
PlayerDetections[MAXPLAYERS+1] detections;
|
||||
|
||||
GlobalForward fwd_PlayerDoubleKit, fwd_NoHordeBileWaste, fwd_DoorFaceCloser, fwd_CheckpointDoorFaceCloser;
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "L4D2 Detections",
|
||||
author = "jackzmc",
|
||||
description = "",
|
||||
version = PLUGIN_VERSION,
|
||||
url = ""
|
||||
};
|
||||
|
||||
public void OnPluginStart() {
|
||||
EngineVersion g_Game = GetEngineVersion();
|
||||
if(g_Game != Engine_Left4Dead && g_Game != Engine_Left4Dead2) {
|
||||
SetFailState("This plugin is for L4D/L4D2 only.");
|
||||
}
|
||||
|
||||
fwd_PlayerDoubleKit = new GlobalForward("OnDoubleKit", ET_Hook, Param_Cell);
|
||||
fwd_NoHordeBileWaste = new GlobalForward("OnNoHordeBileWaste", ET_Event, Param_Cell, Param_Cell);
|
||||
fwd_DoorFaceCloser = new GlobalForward("OnDoorCloseInFace", ET_Hook, Param_Cell);
|
||||
fwd_CheckpointDoorFaceCloser = new GlobalForward("OnDoorCloseInFaceSaferoom", ET_Hook, Param_Cell, Param_Cell);
|
||||
|
||||
HookEvent("item_pickup", Event_ItemPickup);
|
||||
HookEvent("door_close", Event_DoorClose);
|
||||
}
|
||||
|
||||
// Called on map changes too, we want this:
|
||||
public void OnClientDisconnect(int client) {
|
||||
detections[client].Reset();
|
||||
}
|
||||
|
||||
public Action Event_ItemPickup(Event event, const char[] name, bool dontBroadcast) {
|
||||
int client = GetClientOfUserId(event.GetInt("userid"));
|
||||
if(client && L4D_IsInLastCheckpoint(client)) {
|
||||
static char itmName[32];
|
||||
event.GetString("item", itmName, sizeof(itmName));
|
||||
if(StrEqual(itmName, "first_aid_kit")) {
|
||||
if(++detections[client].kitPickupsSaferoom == 2) {
|
||||
InternalDebugLog("DOUBLE_KIT", client);
|
||||
Call_StartForward(fwd_PlayerDoubleKit);
|
||||
Call_PushCell(client);
|
||||
Call_Finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action Event_DoorClose(Event event, const char[] name, bool dontBroadcast) {
|
||||
int client = GetClientOfUserId(event.GetInt("userid"));
|
||||
if(fwd_DoorFaceCloser.FunctionCount > 0 && client) {
|
||||
bool isCheckpoint = event.GetBool("checkpoint");
|
||||
DataPack pack = GetNearestClient(client);
|
||||
pack.Reset();
|
||||
int victim = pack.ReadCell();
|
||||
float dist = pack.ReadFloat();
|
||||
if(victim) {
|
||||
if(dist < DOOR_CLOSE_THRESHOLD) {
|
||||
if(isCheckpoint) {
|
||||
if(detections[client].saferoomLastOpen > 0 && GetTime() - detections[client].saferoomLastOpen > 30000) {
|
||||
detections[client].saferoomLastOpen = 0;
|
||||
detections[client].saferoomOpenCount = 0;
|
||||
}
|
||||
Call_StartForward(fwd_CheckpointDoorFaceCloser);
|
||||
Call_PushCell(client);
|
||||
Call_PushCell(victim);
|
||||
Call_PushCell(++detections[client].saferoomOpenCount);
|
||||
Call_Finish();
|
||||
detections[client].saferoomLastOpen = GetTime();
|
||||
PrintToServer("[Detections] DOOR_SAFEROOM: %N victim -> %N %d times", client, victim, detections[client].saferoomOpenCount);
|
||||
//TODO: Find way to reset, timer?
|
||||
} else {
|
||||
Call_StartForward(fwd_DoorFaceCloser);
|
||||
Call_PushCell(client);
|
||||
Call_PushCell(victim);
|
||||
Call_Finish();
|
||||
PrintToServer("[Detections] DOOR=: %N victim -> %N", client, victim);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEntityDestroyed(int entity) {
|
||||
static char classname[16];
|
||||
if(IsValidEntity(entity) && entity <= 4096) {
|
||||
GetEntityClassname(entity, classname, sizeof(classname));
|
||||
if(StrEqual(classname, "vomitjar_projec")) {
|
||||
int thrower = GetEntPropEnt(entity, Prop_Send, "m_hThrower");
|
||||
if(thrower > 0 && thrower <= MaxClients && IsClientConnected(thrower) && IsClientInGame(thrower)) {
|
||||
static float src[3];
|
||||
float tmp[3];
|
||||
GetClientAbsOrigin(thrower, tmp);
|
||||
// TODO: Get source when lands
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", src);
|
||||
|
||||
int commons = GetEntityCountNear(src, 50000.0);
|
||||
PrintToConsoleAll("[Debug] Bile Thrown By %N, Commons: %d", thrower, commons);
|
||||
if(commons < BILE_NO_HORDE_THRESHOLD) {
|
||||
InternalDebugLog("BILE_NO_HORDE", thrower);
|
||||
Action result;
|
||||
Call_StartForward(fwd_NoHordeBileWaste);
|
||||
Call_PushCell(thrower);
|
||||
Call_PushCell(commons);
|
||||
Call_Finish(result);
|
||||
|
||||
if(result == Plugin_Stop) {
|
||||
AcceptEntityInput(entity, "kill");
|
||||
GiveClientWeapon(thrower, "vomitjar");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Door close
|
||||
|
||||
void InternalDebugLog(const char[] name, int client) {
|
||||
PrintToConsoleAll("[Detection] %s: Client %N", name, client);
|
||||
}
|
||||
|
||||
stock DataPack GetNearestClient(int client) {
|
||||
int victim;
|
||||
float pos[3], pos2[3], distance;
|
||||
GetClientAbsOrigin(client, pos);
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(i != client && IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
|
||||
GetClientAbsOrigin(i, pos2);
|
||||
float dist = GetVectorDistance(pos, pos2, false);
|
||||
if(victim == 0 || dist < distance) {
|
||||
distance = dist;
|
||||
victim = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
DataPack pack = new DataPack();
|
||||
pack.WriteCell(victim);
|
||||
pack.WriteFloat(distance);
|
||||
return pack;
|
||||
}
|
|
@ -27,7 +27,7 @@
|
|||
#define DEBUG_LEVEL DEBUG_GENERIC
|
||||
#define EXTRA_PLAYER_HUD_UPDATE_INTERVAL 0.8
|
||||
//Sets abmExtraCount to this value if set
|
||||
//#define DEBUG_FORCE_PLAYERS 5
|
||||
// #define DEBUG_FORCE_PLAYERS 5
|
||||
|
||||
#define PLUGIN_VERSION "1.0"
|
||||
|
||||
|
@ -269,19 +269,25 @@ public Action Command_RunExtraItems(int client, int args) {
|
|||
/// EVENTS
|
||||
////////////////////////////////////
|
||||
|
||||
// 0 = inactive | 1 = started | 2 = first tank round started | 3 = waiting for tank spawn | # > 3: Health for next tank
|
||||
#define FINALE_TANK 8
|
||||
#define FINALE_STARTED 1
|
||||
#define FINALE_RESCUE_READY 6
|
||||
#define FINALE_HORDE 7
|
||||
#define FINALE_WAIT 10
|
||||
|
||||
public Action L4D2_OnChangeFinaleStage(int &finaleType, const char[] arg) {
|
||||
if(finaleType == 1 && abmExtraCount > 4 && hExtraFinaleTank.BoolValue) {
|
||||
if(finaleType == FINALE_STARTED && abmExtraCount > 4 && hExtraFinaleTank.BoolValue) {
|
||||
finaleStage = 1;
|
||||
PrintToConsoleAll("[EPI] Finale started and over threshold");
|
||||
} else if(finaleType == 8) {
|
||||
} else if(finaleType == FINALE_TANK) {
|
||||
if(finaleStage == 1) {
|
||||
finaleStage = 2;
|
||||
PrintToConsoleAll("[EPI] First tank has spawned");
|
||||
} else {
|
||||
PrintToConsoleAll("[EPI] First tank stage has started");
|
||||
} else if(finaleStage == 2) {
|
||||
finaleStage = 3;
|
||||
PrintToConsoleAll("[EPI] Waiting for second tank to spawn");
|
||||
PrintToConsoleAll("[EPI] Second stage started, waiting for tank");
|
||||
} else {
|
||||
PrintToConsoleAll("invalid");
|
||||
}
|
||||
}
|
||||
return Plugin_Continue;
|
||||
|
@ -291,23 +297,25 @@ public void Event_TankSpawn(Event event, const char[] name, bool dontBroadcast)
|
|||
int user = GetEventInt(event, "userid");
|
||||
int tank = GetClientOfUserId(user);
|
||||
if(finaleStage == 3) {
|
||||
PrintToConsoleAll("[EPI] Third tank spawned, setting health.");
|
||||
PrintToConsoleAll("[EPI] Second tank spawned, setting health.");
|
||||
if(tank > 0 && IsFakeClient(tank)) {
|
||||
RequestFrame(Frame_ExtraTankWait, user);
|
||||
// Sets health in half, sets finaleStage to health
|
||||
CreateTimer(5.0, Timer_SplitTank, user);
|
||||
}
|
||||
finaleStage = 0; //Only set for a frame
|
||||
} else if(finaleStage > 3) {
|
||||
PrintToConsoleAll("[EPI] Third & final tank spawned, setting health.");
|
||||
RequestFrame(Frame_SetExtraTankHealth, user);
|
||||
}
|
||||
}
|
||||
|
||||
public void Frame_ExtraTankWait(int user) {
|
||||
public Action Timer_SplitTank(Handle t, int user) {
|
||||
int tank = GetClientOfUserId(user);
|
||||
if(tank > 0) {
|
||||
// Half their HP, assign half to self and for next tank
|
||||
int hp = GetEntProp(tank, Prop_Send, "m_iHealth") / 2;
|
||||
SetEntProp(tank, Prop_Send, "m_iHealth", hp);
|
||||
finaleStage = hp;
|
||||
// Then, summon the next tank
|
||||
ServerCommand("sm_forcespecial tank");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -362,7 +370,13 @@ public Action Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBr
|
|||
} else {
|
||||
// New client has connected, not on first map.
|
||||
// TODO: Check if Timer_UpdateMinPlayers is needed, or if this works:
|
||||
if(++abmExtraCount > 4) {
|
||||
// Never decrease abmExtraCount
|
||||
int newCount = GetRealSurvivorsCount();
|
||||
if(newCount > abmExtraCount) {
|
||||
abmExtraCount = newCount;
|
||||
}
|
||||
// If 5 survivors, then set them up, TP them.
|
||||
if(abmExtraCount > 4) {
|
||||
RequestFrame(Frame_SetupNewClient, client);
|
||||
}
|
||||
}
|
||||
|
@ -427,8 +441,9 @@ public void Frame_SetupNewClient(int client) {
|
|||
EquipPlayerWeapon(client, item);
|
||||
}
|
||||
static float spawnPos[3];
|
||||
GetCenterPositionInSurvivorFlow(client, spawnPos);
|
||||
TeleportEntity(client, spawnPos, NULL_VECTOR, NULL_VECTOR);
|
||||
// TODO: Fix null
|
||||
if(GetCenterPositionInSurvivorFlow(client, spawnPos))
|
||||
TeleportEntity(client, spawnPos, NULL_VECTOR, NULL_VECTOR);
|
||||
}
|
||||
public Action Timer_GiveClientKit(Handle hdl, int user) {
|
||||
int client = GetClientOfUserId(user);
|
||||
|
@ -479,6 +494,7 @@ public void OnMapStart() {
|
|||
extraKitsStarted = extraKitsAmount;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isLateLoaded) {
|
||||
isLateLoaded = false;
|
||||
}
|
||||
|
@ -505,6 +521,7 @@ public void OnMapStart() {
|
|||
HookEntityOutput("trigger_changelevel", "OnStartTouch", EntityOutput_OnStartTouchSaferoom);
|
||||
|
||||
playersLoadedIn = 0;
|
||||
finaleStage = 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -521,7 +538,6 @@ public void OnMapEnd() {
|
|||
}
|
||||
ammoPacks.Clear();
|
||||
playersLoadedIn = 0;
|
||||
L4D2_RunScript("ExtraPlayerHUD <- { Fields = { } }; HUDSetLayout(ExtraPlayerHud); HUDPlace( g_ModeScript.HUD_RIGHT_BOT, 0.72, 0.79, 0.25, 0.2 ); g_ModeScript");
|
||||
}
|
||||
|
||||
public void Event_RoundFreezeEnd(Event event, const char[] name, bool dontBroadcast) {
|
||||
|
@ -577,7 +593,7 @@ public Action Event_MapTransition(Event event, const char[] name, bool dontBroad
|
|||
}
|
||||
//TODO: Possibly hacky logic of on third different ent id picked up, in short timespan, detect as set of 4 (pills, kits) & give extra
|
||||
public Action Event_Pickup(int client, int weapon) {
|
||||
char name[32];
|
||||
static char name[32];
|
||||
GetEntityClassname(weapon, name, sizeof(name));
|
||||
if(StrEqual(name, "weapon_first_aid_kit", true)) {
|
||||
if(isBeingGivenKit[client]) return Plugin_Continue;
|
||||
|
@ -1108,25 +1124,45 @@ stock void RunVScriptLong(const char[] sCode, any ...) {
|
|||
}
|
||||
|
||||
// Gets a position (from a nav area)
|
||||
stock void GetCenterPositionInSurvivorFlow(int target, float pos[3]) {
|
||||
stock bool GetCenterPositionInSurvivorFlow(int target, float pos[3]) {
|
||||
static float ang[3];
|
||||
int client = GetHighestFlowSurvivor(target);
|
||||
GetClientAbsOrigin(client, pos);
|
||||
int nav = L4D_GetNearestNavArea(pos);
|
||||
L4D_FindRandomSpot(nav, pos);
|
||||
if(client > 0) {
|
||||
GetClientAbsOrigin(client, pos);
|
||||
GetClientAbsAngles(client, ang);
|
||||
pos[2] = -pos[2];
|
||||
TR_TraceRayFilter(pos, ang, MASK_SHOT, RayType_Infinite, Filter_GroundOnly);
|
||||
if(TR_DidHit()) {
|
||||
TR_GetEndPosition(pos);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Filter_GroundOnly(int entity, int mask) {
|
||||
return entity == 0;
|
||||
}
|
||||
|
||||
stock int GetLowestFlowSurvivor(int ignoreTarget = 0) {
|
||||
int client = L4D_GetHighestFlowSurvivor();
|
||||
float lowestFlow = L4D2Direct_GetFlowDistance(client);
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(ignoreTarget != i && IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) {
|
||||
if(L4D2Direct_GetFlowDistance(i) < lowestFlow) {
|
||||
client = i;
|
||||
lowestFlow = L4D2Direct_GetFlowDistance(i);
|
||||
if(client != ignoreTarget) {
|
||||
return client;
|
||||
} else {
|
||||
client = -1;
|
||||
float lowestFlow = L4D2Direct_GetFlowDistance(client);
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(ignoreTarget != i && IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) {
|
||||
if(L4D2Direct_GetFlowDistance(i) < lowestFlow) {
|
||||
client = i;
|
||||
lowestFlow = L4D2Direct_GetFlowDistance(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return client;
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
stock int GetHighestFlowSurvivor(int ignoreTarget = 0) {
|
||||
|
@ -1134,6 +1170,7 @@ stock int GetHighestFlowSurvivor(int ignoreTarget = 0) {
|
|||
if(client != ignoreTarget) {
|
||||
return client;
|
||||
} else {
|
||||
client = -1;
|
||||
float highestFlow = L4D2Direct_GetFlowDistance(client);
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(ignoreTarget != i && IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
107
scripting/l4d2_tank_priority.sp
Normal file
107
scripting/l4d2_tank_priority.sp
Normal file
|
@ -0,0 +1,107 @@
|
|||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#define PLUGIN_VERSION "1.0"
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
//#include <sdkhooks>
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "L4D2 Tank Priority",
|
||||
author = "jackzmc",
|
||||
description = "",
|
||||
version = PLUGIN_VERSION,
|
||||
url = ""
|
||||
};
|
||||
|
||||
#define TANK_CLASS_ID 8
|
||||
|
||||
static int tankChooseVictimTicks[MAXPLAYERS+1]; //Per tank
|
||||
static int totalTankDamage[MAXPLAYERS+1]; //Per survivor
|
||||
static ArrayList clients;
|
||||
|
||||
public void OnPluginStart() {
|
||||
EngineVersion g_Game = GetEngineVersion();
|
||||
if(g_Game != Engine_Left4Dead && g_Game != Engine_Left4Dead2) {
|
||||
SetFailState("This plugin is for L4D/L4D2 only.");
|
||||
}
|
||||
|
||||
clients = new ArrayList(2);
|
||||
|
||||
HookEvent("player_hurt", Event_PlayerHurt);
|
||||
HookEvent("tank_spawn", Event_TankSpawn);
|
||||
}
|
||||
|
||||
|
||||
public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
|
||||
int class = GetEntProp(attacker, Prop_Send, "m_zombieClass");
|
||||
if(class != TANK_CLASS_ID) return Plugin_Continue;
|
||||
|
||||
//Find a new victim
|
||||
if(++tankChooseVictimTicks[attacker] >= 200) {
|
||||
tankChooseVictimTicks[attacker] = 0;
|
||||
clients.Clear();
|
||||
float tankPos[3], clientPos[3];
|
||||
GetClientAbsOrigin(attacker, tankPos);
|
||||
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) {
|
||||
//If a player does less than 50 damage, and has green health add them to list
|
||||
if(totalTankDamage[i] < 100 && GetClientHealth(i) > 40) {
|
||||
GetClientAbsOrigin(i, clientPos);
|
||||
float dist = GetVectorDistance(clientPos, tankPos);
|
||||
// Only add targets who are far enough away from tank
|
||||
if(dist > 5000.0) {
|
||||
PrintToConsoleAll("Adding player %N to possible victim list. Dist=%f, Dmg=%d", i, dist, totalTankDamage[i]);
|
||||
int index = clients.Push(i);
|
||||
clients.Set(index, dist, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(clients.Length == 0) return Plugin_Continue;
|
||||
|
||||
clients.SortCustom(Sort_TankTargetter);
|
||||
/*curTarget = clients.Get(0);*/
|
||||
PrintToConsoleAll("[TankPriority] Player Selected to target: %N", curTarget);
|
||||
//TODO: Possibly clear totalTankDamage
|
||||
//return Plugin_Changed;
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
int Sort_TankTargetter(int index1, int index2, Handle array, Handle hndl) {
|
||||
int client1 = GetArrayCell(array, index1);
|
||||
int client2 = GetArrayCell(array, index2);
|
||||
float distance1 = GetArrayCell(array, index2, 0);
|
||||
float distance2 = GetArrayCell(array, index2, 1);
|
||||
/*500 units away, 0 damage vs 600 units away, 0 damage
|
||||
-> target closest 500
|
||||
500 units away, 10 damage, vs 600 units away 0 damage
|
||||
500 - 10 = 450 vs 600
|
||||
*/
|
||||
return (totalTankDamage[client1] + RoundFloat(distance1)) - (totalTankDamage[client2] + RoundFloat(distance2));
|
||||
}
|
||||
|
||||
public void Event_PlayerHurt(Event event, const char[] name, bool dontBroadcast) {
|
||||
int victim = GetClientOfUserId(event.GetInt("userid"));
|
||||
int attacker = GetClientOfUserId(event.GetInt("attacker"));
|
||||
int dmg = event.GetInt("dmg_health");
|
||||
if(dmg > 0 && attacker > 0 && victim > 0 && IsFakeClient(victim) && GetEntProp(victim, Prop_Send, "m_zombieClass") == TANK_CLASS_ID) {
|
||||
if(GetClientTeam(victim) == 3 && GetClientTeam(attacker) == 2) {
|
||||
totalTankDamage[victim] += dmg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Event_TankSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||
int tank = GetClientOfUserId(GetEventInt(event, "userid"));
|
||||
if(tank > 0 && IsFakeClient(tank)) {
|
||||
tankChooseVictimTicks[tank] = -20;
|
||||
}
|
||||
}
|
291
scripting/sm_player_notes.sp
Normal file
291
scripting/sm_player_notes.sp
Normal file
|
@ -0,0 +1,291 @@
|
|||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#define PLUGIN_VERSION "1.0"
|
||||
#define MAX_PLAYER_HISTORY 25
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
//#include <sdkhooks>
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "Noob DB",
|
||||
author = "jackzmc",
|
||||
description = "",
|
||||
version = PLUGIN_VERSION,
|
||||
url = ""
|
||||
};
|
||||
|
||||
static Database DB;
|
||||
static char query[1024];
|
||||
static char reason[256];
|
||||
static int WaitingForNotePlayer;
|
||||
static char menuNoteTarget[32];
|
||||
|
||||
enum struct PlayerData {
|
||||
char id[32];
|
||||
char name[32];
|
||||
}
|
||||
|
||||
static ArrayList lastPlayers;
|
||||
|
||||
public void OnPluginStart() {
|
||||
if(!SQL_CheckConfig("stats")) {
|
||||
SetFailState("No database entry for 'stats'; no database to connect to.");
|
||||
}
|
||||
if(!ConnectDB()) {
|
||||
SetFailState("Failed to connect to database.");
|
||||
}
|
||||
|
||||
lastPlayers = new ArrayList(sizeof(PlayerData));
|
||||
|
||||
HookEvent("player_disconnect", Event_PlayerDisconnect);
|
||||
HookEvent("player_first_spawn", Event_FirstSpawn);
|
||||
|
||||
RegAdminCmd("sm_note", Command_AddNote, ADMFLAG_KICK, "Add a note to a player");
|
||||
RegAdminCmd("sm_notes", Command_ListNotes, ADMFLAG_KICK, "List notes for a player");
|
||||
RegAdminCmd("sm_notedisconnected", Command_AddNoteDisconnected, ADMFLAG_KICK, "Add a note to any disconnected players");
|
||||
}
|
||||
|
||||
public Action Command_AddNoteDisconnected(int client, int args) {
|
||||
if(lastPlayers.Length == 0) {
|
||||
ReplyToCommand(client, "No disconnected players recorded.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
Menu menu = new Menu(Menu_Disconnected);
|
||||
menu.SetTitle("Add Note For Disconnected");
|
||||
for(int i = lastPlayers.Length + 1; i >= 0; i--) {
|
||||
PlayerData data;
|
||||
lastPlayers.GetArray(i, data, sizeof(data));
|
||||
menu.AddItem(data.id, data.name);
|
||||
}
|
||||
menu.Display(client, 0);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public int Menu_Disconnected(Menu menu, MenuAction action, int client, int item) {
|
||||
if (action == MenuAction_Select) {
|
||||
menu.GetItem(item, menuNoteTarget, sizeof(menuNoteTarget));
|
||||
PrintToChat(client, "Enter a note for %s:", menuNoteTarget);
|
||||
WaitingForNotePlayer = client;
|
||||
} else if (action == MenuAction_End)
|
||||
delete menu;
|
||||
}
|
||||
|
||||
public Action OnClientSayCommand(int client, const char[] command, const char[] sArgs) {
|
||||
if(WaitingForNotePlayer == client) {
|
||||
WaitingForNotePlayer = 0;
|
||||
static char buffer[32];
|
||||
GetClientAuthId(client, AuthId_Steam2, buffer, sizeof(buffer));
|
||||
DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", menuNoteTarget, buffer, sArgs);
|
||||
DB.Query(DB_AddNote, query);
|
||||
LogAction(client, -1, "\"%L\" added note for \"%s\": \"%s\"", client, menuNoteTarget, sArgs);
|
||||
Format(buffer, sizeof(buffer), "%N: ", client);
|
||||
ShowActivity2(client, buffer, "added a note for %s: \"%s\"", menuNoteTarget, sArgs);
|
||||
return Plugin_Stop;
|
||||
}
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action Command_AddNote(int client, int args) {
|
||||
if(args < 2) {
|
||||
ReplyToCommand(client, "Syntax: sm_note <player> <note>");
|
||||
} else {
|
||||
static char target_name[MAX_TARGET_LENGTH];
|
||||
GetCmdArg(1, target_name, sizeof(target_name));
|
||||
GetCmdArg(2, reason, sizeof(reason));
|
||||
|
||||
int target_list[MAXPLAYERS], target_count;
|
||||
bool tn_is_ml;
|
||||
if ((target_count = ProcessTargetString(
|
||||
target_name,
|
||||
client,
|
||||
target_list,
|
||||
1,
|
||||
COMMAND_FILTER_NO_MULTI,
|
||||
target_name,
|
||||
sizeof(target_name),
|
||||
tn_is_ml)) <= 0
|
||||
) {
|
||||
ReplyToTargetError(client, target_count);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
static char auth[32];
|
||||
GetClientAuthId(target_list[0], AuthId_Steam2, auth, sizeof(auth));
|
||||
static char authMarker[32];
|
||||
if(client > 0)
|
||||
GetClientAuthId(client, AuthId_Steam2, authMarker, sizeof(authMarker));
|
||||
DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", auth, authMarker, reason);
|
||||
DB.Query(DB_AddNote, query);
|
||||
LogAction(client, target_list[0], "\"%L\" added note for \"%L\": \"%s\"", client, target_list[0], reason);
|
||||
ShowActivity(client, "added a note for \"%N\": \"%s\"", target_list[0], reason);
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_ListNotes(int client, int args) {
|
||||
if(args < 1) {
|
||||
ReplyToCommand(client, "Syntax: sm_notes <player>");
|
||||
} else {
|
||||
static char target_name[MAX_TARGET_LENGTH];
|
||||
GetCmdArg(1, target_name, sizeof(target_name));
|
||||
GetCmdArg(2, reason, sizeof(reason));
|
||||
|
||||
int target_list[MAXPLAYERS], target_count;
|
||||
bool tn_is_ml;
|
||||
if ((target_count = ProcessTargetString(
|
||||
target_name,
|
||||
client,
|
||||
target_list,
|
||||
1,
|
||||
COMMAND_FILTER_NO_MULTI,
|
||||
target_name,
|
||||
sizeof(target_name),
|
||||
tn_is_ml)) <= 0
|
||||
) {
|
||||
ReplyToTargetError(client, target_count);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
static char auth[32];
|
||||
GetClientAuthId(target_list[0], AuthId_Steam2, auth, sizeof(auth));
|
||||
|
||||
DB.Format(query, sizeof(query), "SELECT notes.content, stats_users.last_alias FROM `notes` JOIN stats_users ON markedBy = stats_users.steamid WHERE notes.`steamid` = '%s'", auth);
|
||||
ReplyToCommand(client, "Fetching notes...");
|
||||
DataPack pack = new DataPack();
|
||||
pack.WriteCell(GetClientUserId(client));
|
||||
pack.WriteCell(GetClientUserId(target_list[0]));
|
||||
pack.WriteString(auth);
|
||||
DB.Query(DB_ListNotesForPlayer, query, pack);
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
bool ConnectDB() {
|
||||
static char error[255];
|
||||
DB = SQL_Connect("stats", true, error, sizeof(error));
|
||||
if (DB== null) {
|
||||
LogError("Database error %s", error);
|
||||
delete DB;
|
||||
return false;
|
||||
} else {
|
||||
PrintToServer("Connected to database stats");
|
||||
SQL_LockDatabase(DB);
|
||||
SQL_FastQuery(DB, "SET NAMES \"UTF8mb4\"");
|
||||
SQL_UnlockDatabase(DB);
|
||||
DB.SetCharset("utf8mb4");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public Action Event_FirstSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||
int client = GetClientUserId(event.GetInt("userid"));
|
||||
if(client > 0 && client <= MaxClients && !IsFakeClient(client)) {
|
||||
static char auth[32];
|
||||
GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth));
|
||||
DB.Format(query, sizeof(query), "SELECT notes.content, stats_users.last_alias FROM `notes` JOIN stats_users ON markedBy = stats_users.steamid WHERE notes.`steamid` = '%s'", auth);
|
||||
DB.Query(DB_FindNotes, query, GetClientUserId(client));
|
||||
}
|
||||
}
|
||||
|
||||
public Action Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast) {
|
||||
if(!event.GetBool("bot")) {
|
||||
PlayerData data;
|
||||
event.GetString("networkid", data.id, sizeof(data.id));
|
||||
if(!StrEqual(data.id, "BOT")) {
|
||||
if(!IsPlayerInHistory(data.id)) {
|
||||
event.GetString("name", data.name, sizeof(data.name));
|
||||
lastPlayers.PushArray(data);
|
||||
if(lastPlayers.Length > MAX_PLAYER_HISTORY) {
|
||||
lastPlayers.Erase(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsPlayerInHistory(const char[] id) {
|
||||
static PlayerData data;
|
||||
for(int i = 0; i < lastPlayers.Length; i++) {
|
||||
lastPlayers.GetArray(i, data, sizeof(data));
|
||||
if(StrEqual(data.id, id))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void DB_FindNotes(Database db, DBResultSet results, const char[] error, any data) {
|
||||
if(db == null || results == null) {
|
||||
LogError("DB_FindNotes returned error: %s", error);
|
||||
return;
|
||||
}
|
||||
//initialize variables
|
||||
int client = GetClientOfUserId(data);
|
||||
if(client && results.RowCount > 0) {
|
||||
static char noteCreator[32];
|
||||
PrintChatToAdmins("Notes for %s", client);
|
||||
while(results.FetchRow()) {
|
||||
results.FetchString(0, reason, sizeof(reason));
|
||||
results.FetchString(1, noteCreator, sizeof(noteCreator));
|
||||
PrintChatToAdmins("%s: %s", noteCreator, reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void DB_ListNotesForPlayer(Database db, DBResultSet results, const char[] error, DataPack pack) {
|
||||
if(db == null || results == null) {
|
||||
LogError("DB_ListNotesForPlayer returned error: %s", error);
|
||||
return;
|
||||
}
|
||||
//initialize variables
|
||||
static char auth[32];
|
||||
pack.Reset();
|
||||
int client = GetClientOfUserId(pack.ReadCell());
|
||||
int target = GetClientOfUserId(pack.ReadCell());
|
||||
pack.ReadString(auth, sizeof(auth));
|
||||
delete pack;
|
||||
if(client > 0) {
|
||||
if(results.RowCount > 0) {
|
||||
if(target > 0) {
|
||||
PrintToChat(client, "Notes for %N:", target);
|
||||
} else {
|
||||
PrintToChat(client, "Notes for %s:", auth);
|
||||
}
|
||||
char noteCreator[32];
|
||||
while(results.FetchRow()) {
|
||||
results.FetchString(0, reason, sizeof(reason));
|
||||
results.FetchString(1, noteCreator, sizeof(noteCreator));
|
||||
PrintToChat(client, "%s: %s", noteCreator, reason);
|
||||
}
|
||||
} else {
|
||||
if(target > 0)
|
||||
PrintToChat(client, "No notes found for %N", target);
|
||||
else
|
||||
PrintToChat(client, "No notes found for %s", auth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DB_AddNote(Database db, DBResultSet results, const char[] error, any data) {
|
||||
if(db == null || results == null) {
|
||||
LogError("DB_AddNote returned error: %s", error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue