sm-plugins/GrenadeSkins/scripting/GrenadeSkins_remake.sp

417 lines
11 KiB
SourcePawn

#pragma semicolon 1
#include <sourcemod>
#include <clientprefs>
#include <sdktools>
#include <sdkhooks>
#include <unloze_playtime>
#pragma newdecls required
#define MAX_SKINS 64
enum struct GrenadeSkin
{
char ModelPath[128];
char Name[128];
int VIP;
int Tier;
float Scale;
int Skin;
}
GrenadeSkin g_aGrenadeSkins[MAX_SKINS];
int g_iSkinCount = 0;
int g_iGrenadeSkin[MAXPLAYERS + 1] = { -1, ... };
bool g_bAdminChecked[MAXPLAYERS + 1];
Handle g_hCookie_GrenadeSkin;
public Plugin myinfo =
{
name = "Grenade Skins",
description = "grenade skins but source code actually provided",
author = "redone by claude ai mostly, slight human adjustments",
version = "1.0",
url = ""
};
public void OnPluginStart()
{
RegConsoleCmd("sm_grenadeskin", Cmd_GrenadeSkinSettings);
RegConsoleCmd("sm_grenadeskins", Cmd_GrenadeSkinSettings);
g_hCookie_GrenadeSkin = RegClientCookie("grenade_skin", "Selected grenade skin", CookieAccess_Protected);
SetCookieMenuItem(MenuHandler_CookieMenu, 0, "Grenade Skin");
for (int i = 1; i <= MaxClients; i++)
{
if (IsValidClient(i, true) && AreClientCookiesCached(i))
{
OnClientCookiesCached(i);
}
}
}
public void OnMapStart()
{
LoadDownloads();
LoadSkinsConfig();
}
void LoadDownloads()
{
char sDownloadsFile[PLATFORM_MAX_PATH];
BuildPath(Path_SM, sDownloadsFile, sizeof(sDownloadsFile), "configs/grenade_skins/downloads.txt");
File hFile = OpenFile(sDownloadsFile, "r");
if (!hFile)
{
SetFailState("Could not open file: configs/grenade_skins/downloads.txt");
}
char sLine[1024];
while (hFile.ReadLine(sLine, sizeof(sLine)))
{
TrimString(sLine);
// Skip empty lines and comment lines (lines starting with "//" with no file extension).
if (sLine[0] == '\0')
continue;
if (StrContains(sLine, ".", false) == -1)
continue;
if (FileExists(sLine, false, "GAME"))
{
AddFileToDownloadsTable(sLine);
}
}
delete hFile;
}
void LoadSkinsConfig()
{
// Clear existing skins.
for (int i = 0; i < MAX_SKINS; i++)
{
g_aGrenadeSkins[i].ModelPath[0] = '\0';
g_aGrenadeSkins[i].Name[0] = '\0';
g_aGrenadeSkins[i].VIP = 0;
g_aGrenadeSkins[i].Tier = 0;
g_aGrenadeSkins[i].Scale = 1.0;
g_aGrenadeSkins[i].Skin = 0;
}
g_iSkinCount = 0;
char sConfigFile[PLATFORM_MAX_PATH];
BuildPath(Path_SM, sConfigFile, sizeof(sConfigFile), "configs/grenade_skins/skins.cfg");
if (!FileExists(sConfigFile, false, "GAME"))
{
SetFailState("Could not find: \"%s\"", sConfigFile);
}
KeyValues kv = new KeyValues("Skins");
if (!kv.ImportFromFile(sConfigFile))
{
delete kv;
SetFailState("ImportFromFile() failed!");
}
if (!kv.GotoFirstSubKey(true))
{
delete kv;
SetFailState("Unable to goto first sub key in: \"%s\"", sConfigFile);
}
int i = 0;
do
{
if (i >= MAX_SKINS)
{
LogError("grenade_skins: skins.cfg has more than %d entries, ignoring the rest.", MAX_SKINS);
break;
}
kv.GetString("ModelPath", g_aGrenadeSkins[i].ModelPath, sizeof(g_aGrenadeSkins[].ModelPath), "error");
if (StrEqual(g_aGrenadeSkins[i].ModelPath, "error", true))
{
SetFailState("Unable to read Path");
}
kv.GetString("Name", g_aGrenadeSkins[i].Name, sizeof(g_aGrenadeSkins[].Name), "error");
if (StrEqual(g_aGrenadeSkins[i].Name, "error", true))
{
SetFailState("Unable to read Name");
}
g_aGrenadeSkins[i].VIP = kv.GetNum("VIP", 0);
g_aGrenadeSkins[i].Tier = kv.GetNum("tier", 0);
g_aGrenadeSkins[i].Scale = kv.GetFloat("Scale", 1.0);
g_aGrenadeSkins[i].Skin = kv.GetNum("Skin", 0);
PrecacheModel(g_aGrenadeSkins[i].ModelPath, false);
i++;
}
while (kv.GotoNextKey(true));
g_iSkinCount = i;
delete kv;
}
public void OnClientCookiesCached(int client)
{
char sBuffer[256];
GetClientCookie(client, g_hCookie_GrenadeSkin, sBuffer, sizeof(sBuffer));
if (sBuffer[0])
{
g_iGrenadeSkin[client] = -1;
for (int i = 0; i < g_iSkinCount; i++)
{
if (StrEqual(g_aGrenadeSkins[i].ModelPath, sBuffer, true))
{
g_iGrenadeSkin[client] = i;
if (g_bAdminChecked[client] && !ClientHasSkinAccess(client, i))
{
g_iGrenadeSkin[client] = -1;
}
break;
}
}
}
else
{
g_iGrenadeSkin[client] = -1;
}
}
public void OnClientDisconnect(int client)
{
g_iGrenadeSkin[client] = -1;
g_bAdminChecked[client] = false;
}
public void OnClientPostAdminCheck(int client)
{
g_bAdminChecked[client] = true;
if (AreClientCookiesCached(client) && g_iGrenadeSkin[client] != -1)
{
if (!ClientHasSkinAccess(client, g_iGrenadeSkin[client]))
{
g_iGrenadeSkin[client] = -1;
}
}
}
/**
* Checks whether a client has access to a given skin index,
* taking both the legacy VIP flag and the new tier requirement into account.
*/
bool ClientHasSkinAccess(int client, int skinIndex)
{
if (g_aGrenadeSkins[skinIndex].VIP && !CheckCommandAccess(client, "", ADMFLAG_RESERVATION, false))
{
return false;
}
if (g_aGrenadeSkins[skinIndex].Tier > 0 && GetPlayerTier_native(client) < g_aGrenadeSkins[skinIndex].Tier)
{
return false;
}
return true;
}
public void OnEntitySpawned(int entity, const char[] sClassname)
{
if (StrContains(sClassname, "_projectile", false) == -1)
{
return;
}
int iOwner = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity");
if (!IsValidClient(iOwner, true) || g_iGrenadeSkin[iOwner] == -1)
{
return;
}
int skinIndex = g_iGrenadeSkin[iOwner];
SetEntityRenderMode(entity, RENDER_TRANSALPHA);
float fNadeOrigin[3];
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", fNadeOrigin);
int iNadeProp = CreateEntityAtOrigin("prop_dynamic_override", fNadeOrigin);
DispatchKeyValue(iNadeProp, "disableshadows", "1");
DispatchKeyValue(iNadeProp, "disablereceiveshadows", "1");
DispatchKeyValue(iNadeProp, "DisableBoneFollowers", "1");
DispatchKeyValue(iNadeProp, "solid", "0");
DispatchKeyValue(iNadeProp, "model", g_aGrenadeSkins[skinIndex].ModelPath);
char sScale[32];
FloatToString(g_aGrenadeSkins[skinIndex].Scale, sScale, sizeof(sScale));
DispatchKeyValue(iNadeProp, "modelscale", sScale);
SpawnAndActivate(iNadeProp);
ParentToEntity(iNadeProp, entity);
if (g_aGrenadeSkins[skinIndex].Skin > 0)
{
char sSkinBuf[16];
IntToString(g_aGrenadeSkins[skinIndex].Skin, sSkinBuf, sizeof(sSkinBuf));
SetVariantString(sSkinBuf);
AcceptEntityInput(iNadeProp, "Skin");
}
}
public Action Cmd_GrenadeSkinSettings(int client, int args)
{
ShowSettingsMenu(client);
return Plugin_Handled;
}
public void MenuHandler_CookieMenu(int client, CookieMenuAction action, any info, char[] buffer, int maxlen)
{
switch (action)
{
case CookieMenuAction_DisplayOption:
{
Format(buffer, maxlen, "Grenade Skin");
}
case CookieMenuAction_SelectOption:
{
ShowSettingsMenu(client);
}
}
}
void ShowSettingsMenu(int client)
{
Menu menu = new Menu(MenuHandler_MainMenu);
menu.SetTitle("Grenade Skin");
char sBuffer[128];
Format(sBuffer, sizeof(sBuffer), "No Skin%s", (g_iGrenadeSkin[client] == -1) ? " (Selected)" : "");
menu.AddItem("-1", sBuffer, (g_iGrenadeSkin[client] == -1) ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT);
for (int i = 0; i < g_iSkinCount; i++)
{
bool bSelected = (i == g_iGrenadeSkin[client]);
bool bHasAccess = ClientHasSkinAccess(client, i);
bool bUnavailable = bSelected || !bHasAccess;
char sSuffix[32];
sSuffix[0] = '\0';
if (!bHasAccess)
{
if (g_aGrenadeSkins[i].VIP && !CheckCommandAccess(client, "", ADMFLAG_RESERVATION, false))
{
Format(sSuffix, sizeof(sSuffix), " [VIP]");
}
else if (g_aGrenadeSkins[i].Tier > 0 && GetPlayerTier_native(client) < g_aGrenadeSkins[i].Tier)
{
Format(sSuffix, sizeof(sSuffix), " [Tier %i]", g_aGrenadeSkins[i].Tier);
}
}
else if (bSelected)
{
Format(sSuffix, sizeof(sSuffix), " (Selected)");
}
Format(sBuffer, sizeof(sBuffer), "%s%s", g_aGrenadeSkins[i].Name, sSuffix);
char sInfo[8];
IntToString(i, sInfo, sizeof(sInfo));
menu.AddItem(sInfo, sBuffer, bUnavailable ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT);
}
menu.ExitBackButton = true;
menu.Display(client, MENU_TIME_FOREVER);
}
public int MenuHandler_MainMenu(Menu menu, MenuAction action, int client, int selection)
{
switch (action)
{
case MenuAction_Select:
{
char sInfo[8];
menu.GetItem(selection, sInfo, sizeof(sInfo));
int chosen = StringToInt(sInfo);
g_iGrenadeSkin[client] = chosen;
if (chosen == -1)
{
SetClientCookie(client, g_hCookie_GrenadeSkin, "");
}
else
{
SetClientCookie(client, g_hCookie_GrenadeSkin, g_aGrenadeSkins[chosen].ModelPath);
}
ShowSettingsMenu(client);
}
case MenuAction_Cancel:
{
if (selection == MenuCancel_ExitBack)
{
// Re-open the cookie/settings menu if desired; original plugin
// routed back into the cookie menu here.
}
}
case MenuAction_End:
{
delete menu;
}
}
return 0;
}
// ---------------------------------------------------------------------
// Helper stocks (ported from decompiled output)
// ---------------------------------------------------------------------
stock bool IsValidClient(int client, bool noBots)
{
if (client <= 0 || client > MaxClients || !IsClientConnected(client) || (noBots && IsFakeClient(client)))
{
return false;
}
return IsClientInGame(client);
}
stock int CreateEntityAtOrigin(const char[] classname, const float origin[3])
{
int entity = CreateEntityByName(classname);
TeleportEntity(entity, origin, NULL_VECTOR, NULL_VECTOR);
return entity;
}
stock void SpawnAndActivate(int entity)
{
DispatchSpawn(entity);
ActivateEntity(entity);
}
stock void ParentToEntity(int entity, int parent)
{
SetVariantString("!activator");
AcceptEntityInput(entity, "SetParent", parent, parent);
}