290 lines
6.3 KiB
SourcePawn
290 lines
6.3 KiB
SourcePawn
#include <sourcemod>
|
|
#include <sdkhooks>
|
|
#include <sdktools>
|
|
|
|
#pragma semicolon 1
|
|
#pragma newdecls required
|
|
|
|
#define TIMER_INTERVAL 1.0
|
|
Handle g_hTimer = INVALID_HANDLE;
|
|
|
|
ConVar g_CVar_MaxWeapons;
|
|
ConVar g_CVar_WeaponLifetime;
|
|
|
|
int g_RealRoundStartedTime;
|
|
int g_MaxWeapons;
|
|
int g_MaxWeaponLifetime;
|
|
|
|
#define MAX_WEAPONS MAXPLAYERS
|
|
int G_WeaponArray[MAX_WEAPONS][2];
|
|
|
|
bool g_bEngineVersionIsCSGO = false;
|
|
|
|
public Plugin myinfo =
|
|
{
|
|
name = "WeaponCleaner",
|
|
author = "BotoX",
|
|
description = "Clean unneeded weapons",
|
|
version = "2.2.1",
|
|
url = ""
|
|
};
|
|
|
|
public void OnPluginStart()
|
|
{
|
|
g_CVar_MaxWeapons = CreateConVar("sm_weaponcleaner_max", "5", "The maximum amount of weapons allowed in the game.", 0, true, 0.0, true, MAX_WEAPONS - 1.0);
|
|
g_MaxWeapons = g_CVar_MaxWeapons.IntValue;
|
|
g_CVar_MaxWeapons.AddChangeHook(OnConVarChanged);
|
|
|
|
g_CVar_WeaponLifetime = CreateConVar("sm_weaponcleaner_lifetime", "15", "The maximum number of seconds a weapon is allowed in the game.", 0, true, 0.0);
|
|
g_MaxWeaponLifetime = g_CVar_WeaponLifetime.IntValue;
|
|
g_CVar_WeaponLifetime.AddChangeHook(OnConVarChanged);
|
|
|
|
HookEvent("round_start", Event_RoundStart);
|
|
|
|
AutoExecConfig(true, "plugin.WeaponCleaner");
|
|
|
|
g_bEngineVersionIsCSGO = (GetEngineVersion() == Engine_CSGO);
|
|
|
|
for(int client = 1; client <= MaxClients; client++)
|
|
{
|
|
if(IsClientInGame(client))
|
|
OnClientPutInServer(client);
|
|
}
|
|
}
|
|
|
|
public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
|
{
|
|
if(convar == g_CVar_MaxWeapons)
|
|
{
|
|
if(StringToInt(newValue) < StringToInt(oldValue))
|
|
{
|
|
// Need to shrink list and kill items
|
|
int d = StringToInt(oldValue) - StringToInt(newValue);
|
|
|
|
// Kill items that don't have space anymore
|
|
for(int i = 0; d && i < g_MaxWeapons; i++)
|
|
{
|
|
if(!G_WeaponArray[i][0])
|
|
continue;
|
|
|
|
// Kill it
|
|
if(KillWeapon(G_WeaponArray[i][0]))
|
|
{
|
|
// Move index backwards (since the list was modified by removing it)
|
|
i--;
|
|
d--;
|
|
}
|
|
}
|
|
}
|
|
g_MaxWeapons = StringToInt(newValue);
|
|
}
|
|
else if(convar == g_CVar_WeaponLifetime)
|
|
{
|
|
g_MaxWeaponLifetime = StringToInt(newValue);
|
|
CheckWeapons();
|
|
}
|
|
}
|
|
|
|
public void OnMapStart()
|
|
{
|
|
if(g_hTimer != INVALID_HANDLE && CloseHandle(g_hTimer))
|
|
g_hTimer = INVALID_HANDLE;
|
|
|
|
g_hTimer = CreateTimer(TIMER_INTERVAL, Timer_CleanupWeapons, INVALID_HANDLE, TIMER_REPEAT);
|
|
}
|
|
|
|
public void OnMapEnd()
|
|
{
|
|
if(g_hTimer != INVALID_HANDLE && CloseHandle(g_hTimer))
|
|
g_hTimer = INVALID_HANDLE;
|
|
}
|
|
|
|
public void OnClientPutInServer(int client)
|
|
{
|
|
SDKHook(client, SDKHook_WeaponDropPost, OnWeaponDrop);
|
|
SDKHook(client, SDKHook_WeaponEquipPost, OnWeaponEquip);
|
|
}
|
|
|
|
public void OnClientDisconnect(int client)
|
|
{
|
|
if(!IsClientInGame(client))
|
|
return;
|
|
|
|
// Simulate dropping all equipped weapons
|
|
for(int i = 0; i < 5; i++)
|
|
{
|
|
int weapon = GetPlayerWeaponSlot(client, i);
|
|
if(weapon != -1)
|
|
OnWeaponDrop(client, weapon);
|
|
}
|
|
}
|
|
|
|
public void OnEntityDestroyed(int entity)
|
|
{
|
|
// wtf sourcemod?
|
|
if(entity == -1)
|
|
return;
|
|
|
|
RemoveWeapon(EntIndexToEntRef(EntRefToEntIndex(entity)));
|
|
}
|
|
|
|
public void OnEntitySpawned(int entity, const char[] classname)
|
|
{
|
|
if(strncmp(classname, "weapon_", 7) == 0)
|
|
{
|
|
OnWeaponSpawnedPost(entity);
|
|
}
|
|
}
|
|
|
|
public void OnWeaponSpawnedPost(int entity)
|
|
{
|
|
if(!IsValidEntity(entity))
|
|
return;
|
|
|
|
int HammerID = GetEntProp(entity, Prop_Data, "m_iHammerID");
|
|
// Should not be cleaned since it's a map spawned weapon
|
|
if(HammerID)
|
|
return;
|
|
|
|
// Weapon doesn't belong to any player
|
|
if(GetEntPropEnt(entity, Prop_Data, "m_hOwnerEntity") == -1)
|
|
InsertWeapon(entity);
|
|
}
|
|
|
|
public Action OnWeaponEquip(int client, int entity)
|
|
{
|
|
if(!IsValidEntity(entity))
|
|
return;
|
|
|
|
int HammerID = GetEntProp(entity, Prop_Data, "m_iHammerID");
|
|
// Should not be cleaned since it's a map spawned weapon
|
|
if(HammerID)
|
|
return;
|
|
|
|
// Weapon should not be cleaned anymore
|
|
RemoveWeapon(EntIndexToEntRef(entity));
|
|
}
|
|
|
|
public Action OnWeaponDrop(int client, int entity)
|
|
{
|
|
if(!IsValidEntity(entity))
|
|
return;
|
|
|
|
int HammerID = GetEntProp(entity, Prop_Data, "m_iHammerID");
|
|
// Should not be cleaned since it's a map spawned weapon
|
|
if(HammerID)
|
|
return;
|
|
|
|
// Kill all dropped weapons during mp_freezetime
|
|
// or if no weapons are allowed at all
|
|
if(GetTime() < g_RealRoundStartedTime || !g_MaxWeapons)
|
|
{
|
|
// Kill it
|
|
AcceptEntityInput(entity, "Kill");
|
|
return;
|
|
}
|
|
|
|
// Weapon should be cleaned again
|
|
InsertWeapon(entity);
|
|
}
|
|
|
|
bool InsertWeapon(int entity)
|
|
{
|
|
if(!g_MaxWeapons)
|
|
return false;
|
|
|
|
int entref = EntIndexToEntRef(entity);
|
|
|
|
// Try to find a free slot
|
|
for(int i = 0; i < g_MaxWeapons; i++)
|
|
{
|
|
if(G_WeaponArray[i][0])
|
|
continue;
|
|
|
|
// Found a free slot, add it here
|
|
G_WeaponArray[i][0] = entref;
|
|
G_WeaponArray[i][1] = GetTime();
|
|
return true;
|
|
}
|
|
|
|
// No free slot found
|
|
// Kill the first (oldest) item in the list
|
|
KillWeapon(G_WeaponArray[0][0]);
|
|
|
|
// Add new weapon to the end of the list
|
|
G_WeaponArray[g_MaxWeapons - 1][0] = entref;
|
|
G_WeaponArray[g_MaxWeapons - 1][1] = GetTime();
|
|
return true;
|
|
}
|
|
|
|
bool RemoveWeapon(int entref)
|
|
{
|
|
// Find the Weapon
|
|
for(int i = 0; i < g_MaxWeapons; i++)
|
|
{
|
|
if(G_WeaponArray[i][0] == entref)
|
|
{
|
|
G_WeaponArray[i][0] = 0; G_WeaponArray[i][1] = 0;
|
|
|
|
// Move list items in front of this index back by one
|
|
for(int j = i + 1; j < g_MaxWeapons; j++)
|
|
{
|
|
G_WeaponArray[j - 1][0] = G_WeaponArray[j][0];
|
|
G_WeaponArray[j - 1][1] = G_WeaponArray[j][1];
|
|
}
|
|
|
|
// Reset last list item
|
|
G_WeaponArray[g_MaxWeapons - 1][0] = 0;
|
|
G_WeaponArray[g_MaxWeapons - 1][1] = 0;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CheckWeapons()
|
|
{
|
|
for(int i = 0; i < g_MaxWeapons; i++)
|
|
{
|
|
if(!G_WeaponArray[i][0])
|
|
continue;
|
|
|
|
if(g_MaxWeaponLifetime && GetTime() - G_WeaponArray[i][1] >= g_MaxWeaponLifetime)
|
|
{
|
|
// Kill it
|
|
if(KillWeapon(G_WeaponArray[i][0]))
|
|
{
|
|
// Move index backwards (since the list was modified by removing it)
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool KillWeapon(int entref)
|
|
{
|
|
if(!IsValidEntity(entref))
|
|
return RemoveWeapon(entref);
|
|
|
|
AcceptEntityInput(entref, "Kill");
|
|
|
|
return RemoveWeapon(entref);
|
|
}
|
|
|
|
public Action Event_RoundStart(Event event, const char[] name, bool dontBroadcast)
|
|
{
|
|
for(int i = 0; i < MAX_WEAPONS; i++)
|
|
{
|
|
G_WeaponArray[i][0] = 0;
|
|
G_WeaponArray[i][1] = 0;
|
|
}
|
|
g_RealRoundStartedTime = GetTime() + GetConVarInt(FindConVar("mp_freezetime"));
|
|
}
|
|
|
|
public Action Timer_CleanupWeapons(Handle timer)
|
|
{
|
|
CheckWeapons();
|
|
}
|