diff --git a/GrenadeSkins/configs/grenade_skins/downloads.txt b/GrenadeSkins/configs/grenade_skins/downloads.txt new file mode 100644 index 0000000..507685d --- /dev/null +++ b/GrenadeSkins/configs/grenade_skins/downloads.txt @@ -0,0 +1,80 @@ +// Angery nut +materials/models/unloze/william/back.vmt +materials/models/unloze/william/back.vtf +materials/models/unloze/william/front.vmt +materials/models/unloze/william/front.vtf +models/unloze/william/angerynut.dx80.vtx +models/unloze/william/angerynut.dx90.vtx +models/unloze/william/angerynut.mdl +models/unloze/william/angerynut.sw.vtx +models/unloze/william/angerynut.vvd +// +//// Eevee +models\unloze\grenades\eevee.dx80.vtx +models\unloze\grenades\eevee.dx90.vtx +models\unloze\grenades\eevee.mdl +models\unloze\grenades\eevee.sw.vtx +models\unloze\grenades\eevee.vvd +materials\models\unloze\grenades\eevee\eevee_body.vtf +materials\models\unloze\grenades\eevee\eevee_body_shiny.vtf +materials\models\unloze\grenades\eevee\eevee_eye.vtf +materials\models\unloze\grenades\eevee\eevee_eye_shiny.vtf +materials\models\unloze\grenades\eevee\eevee_mouth.vtf +materials\models\unloze\grenades\eevee\eevee_mouth_shiny.vtf +materials\models\unloze\grenades\eevee\eievui.vmt +materials\models\unloze\grenades\eevee\eievuieye.vmt +materials\models\unloze\grenades\eevee\eievuieye_shiny.vmt +materials\models\unloze\grenades\eevee\eievuimouth.vmt +materials\models\unloze\grenades\eevee\eievuimouth_shiny.vmt +materials\models\unloze\grenades\eevee\eievui_shiny.vmt +// +//// Holy Grenade +models\unloze\grenades\holy_grenade.dx80.vtx +models\unloze\grenades\holy_grenade.dx90.vtx +models\unloze\grenades\holy_grenade.mdl +models\unloze\grenades\holy_grenade.phy +models\unloze\grenades\holy_grenade.sw.vtx +models\unloze\grenades\holy_grenade.vvd +materials\models\unloze\grenades\holy_grenade\holy_grenade.vmt +materials\models\unloze\grenades\holy_grenade\holy_grenade.vtf +// +//// Lenny +models\unloze\grenades\lenny.dx80.vtx +models\unloze\grenades\lenny.dx90.vtx +models\unloze\grenades\lenny.mdl +models\unloze\grenades\lenny.sw.vtx +models\unloze\grenades\lenny.vvd +models\unloze\grenades\lennysad.dx80.vtx +models\unloze\grenades\lennysad.dx90.vtx +models\unloze\grenades\lennysad.mdl +models\unloze\grenades\lennysad.sw.vtx +models\unloze\grenades\lennysad.vvd +materials\models\unloze\grenades\lenny\black.vmt +// +//// Pokeball +models\unloze\grenades\pokeball.dx80.vtx +models\unloze\grenades\pokeball.dx90.vtx +models\unloze\grenades\pokeball.mdl +models\unloze\grenades\pokeball.phy +models\unloze\grenades\pokeball.sw.vtx +models\unloze\grenades\pokeball.vvd +materials\models\unloze\grenades\pokeball\pokeball.vmt +materials\models\unloze\grenades\pokeball\pokeball.vtf +materials\models\unloze\grenades\pokeball\pokeball_great.vmt +materials\models\unloze\grenades\pokeball\pokeball_great.vtf +materials\models\unloze\grenades\pokeball\pokeball_gs.vmt +materials\models\unloze\grenades\pokeball\pokeball_gs.vtf +materials\models\unloze\grenades\pokeball\pokeball_master.vmt +materials\models\unloze\grenades\pokeball\pokeball_master.vtf +materials\models\unloze\grenades\pokeball\pokeball_ultra.vmt +materials\models\unloze\grenades\pokeball\pokeball_ultra.vtf +// +//// Soccer Ball +materials/models/unloze/soccer/soccerball.vmt +materials/models/unloze/soccer/soccerball_unloze.vtf +models/unloze/soccer/soccerball.dx80.vtx +models/unloze/soccer/soccerball.dx90.vtx +models/unloze/soccer/soccerball.mdl +models/unloze/soccer/soccerball.phy +models/unloze/soccer/soccerball.sw.vtx +models/unloze/soccer/soccerball.vvd diff --git a/GrenadeSkins/configs/grenade_skins/skins.txt b/GrenadeSkins/configs/grenade_skins/skins.txt new file mode 100644 index 0000000..ca02146 --- /dev/null +++ b/GrenadeSkins/configs/grenade_skins/skins.txt @@ -0,0 +1,193 @@ +"Skins" +{ + "0" + { + "Name" "Hula Doll" + "ModelPath" "models/props_lab/huladoll.mdl" + "Scale" "1.7" + "Tier" "1" + } + "1" + { + "Name" "Banana" + "ModelPath" "models/props/cs_italy/bananna.mdl" + "Tier" "2" + } + "2" + { + "Name" "Melon" + "ModelPath" "models/props_junk/watermelon01.mdl" + "Scale" "0.7" + "Tier" "2" + } + "3" + { + "Name" "Orange" + "ModelPath" "models/props/cs_italy/orange.mdl" + "Scale" "1.5" + "Tier" "3" + } + "4" + { + "Name" "Creepy Doll" + "ModelPath" "models/props_c17/doll01.mdl" + "Scale" "0.8" + "Tier" "3" + } + "5" + { + "Name" "Turtle" + "ModelPath" "models/props/de_tides/vending_turtle.mdl" + "Tier" "4" + } + "6" + { + "Name" "Goldfish" + "ModelPath" "models/props/de_inferno/goldfish.mdl" + "Tier" "5" + } + "7" + { + "Name" "River Fish" + "ModelPath" "models/props/cs_militia/fishriver01.mdl" + "Tier" "5" + } + "8" + { + "Name" "Snowman Head" + "ModelPath" "models/props/cs_office/snowman_face.mdl" + "Scale" "0.7" + "Tier" "6" + } + "9" + { + "Name" "Glass Bottle" + "ModelPath" "models/props/cs_militia/bottle01.mdl" + "Tier" "7" + } + "10" + { + "Name" "Gift" + "ModelPath" "models/items/cs_gift.mdl" + "Scale" "0.6" + "Tier" "8" + } + "11" + { + "Name" "Rollermine" + "ModelPath" "models/roller_spikes.mdl" + "VIP" "1" + "Scale" "0.7" + } + "12" + { + "Name" "Helicopter Bomb" + "ModelPath" "models/combine_helicopter/helicopter_bomb01.mdl" + "VIP" "1" + "Scale" "0.35" + } + "13" + { + "Name" "Explosive Barrel" + "ModelPath" "models/props_c17/oildrum001_explosive.mdl" + "VIP" "1" + "Scale" "0.25" + } + "14" + { + "Name" "Skull" + "ModelPath" "models/gibs/agibs.mdl" + "VIP" "1" + "Scale" "1.3" + } + "15" + { + "Name" "Angery Nut" + "ModelPath" "models/unloze/william/angerynut.mdl" + "VIP" "1" + "Scale" "0.8" + } + "16" + { + "Name" "Holy Grenade" + "ModelPath" "models/unloze/grenades/holy_grenade.mdl" + "Scale" "0.6" + "VIP" "1" + } + "17" + { + "Name" "Lenny Face" + "ModelPath" "models/unloze/grenades/lenny.mdl" + "VIP" "1" + "Scale" "0.8" + } + "18" + { + "Name" "Lenny Sad" + "ModelPath" "models/unloze/grenades/lennysad.mdl" + "VIP" "1" + "Scale" "0.8" + } + "19" + { + "Name" "Soccer Ball" + "ModelPath" "models/unloze/soccer/soccerball.mdl" + "VIP" "1" + "Scale" "0.4" + } + "20" + { + "Name" "Eevee" + "ModelPath" "models/unloze/grenades/eevee.mdl" + "VIP" "1" + "Scale" "0.5" + } + "21" + { + "Name" "Shiny Eevee" + "ModelPath" "models/unloze/grenades/eevee.mdl" + "VIP" "1" + "Skin" "1" + "Scale" "0.5" + } + "22" + { + "Name" "Poke Ball" + "ModelPath" "models/unloze/grenades/pokeball.mdl" + "VIP" "1" + "Scale" "1.5" + } + "23" + { + "Name" "Great Ball" + "ModelPath" "models/unloze/grenades/pokeball.mdl" + "VIP" "1" + "Skin" "1" + "Scale" "1.5" + } + "24" + { + "Name" "Ultra Ball" + "ModelPath" "models/unloze/grenades/pokeball.mdl" + "VIP" "1" + "Skin" "4" + "Scale" "1.5" + } + "25" + { + "Name" "Master Ball" + "ModelPath" "models/unloze/grenades/pokeball.mdl" + "VIP" "1" + "Skin" "3" + "Scale" "1.5" + } + "26" + { + "Name" "GS Ball" + "ModelPath" "models/unloze/grenades/pokeball.mdl" + "VIP" "1" + "Skin" "2" + "Scale" "1.5" + } + +} diff --git a/GrenadeSkins/scripting/GrenadeSkins_remake.sp b/GrenadeSkins/scripting/GrenadeSkins_remake.sp new file mode 100644 index 0000000..bf3c165 --- /dev/null +++ b/GrenadeSkins/scripting/GrenadeSkins_remake.sp @@ -0,0 +1,416 @@ +#pragma semicolon 1 +#include +#include +#include +#include +#include + +#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); +}