479 lines
13 KiB
SourcePawn
479 lines
13 KiB
SourcePawn
#pragma semicolon 1
|
|
#define PLUGIN_AUTHOR "jenz"
|
|
#define PLUGIN_VERSION "1.0"
|
|
|
|
#include <unloze_playtime>
|
|
#include <clientprefs>
|
|
#include <mapchooser_extended>
|
|
#include <zombiereloaded>
|
|
#include <sdkhooks>
|
|
#include <sdktools>
|
|
|
|
bool g_bTrailsEnabled;
|
|
bool g_bRoundStartSpawnTrails;
|
|
int g_iSpriteModel[MAXPLAYERS + 1] = {-1, ...};
|
|
|
|
int g_iClientTrail[MAXPLAYERS + 1] = { -1, ... }; // index into g_hTrails, -1 = none
|
|
|
|
float g_fTrailsLifeTime = 1.0;
|
|
int g_iTrailsStartWidth = 10;
|
|
int g_iTrailsEndWidth = 6;
|
|
|
|
Handle g_hCookie_trailName;
|
|
Handle g_hCookie_trailHide;
|
|
|
|
bool g_bHideTrails[MAXPLAYERS + 1];
|
|
|
|
public Plugin myinfo =
|
|
{
|
|
name = "unloze trails",
|
|
author = PLUGIN_AUTHOR,
|
|
description = "adds trails based on tier, vip and maps",
|
|
version = PLUGIN_VERSION,
|
|
url = "www.unloze.com"
|
|
};
|
|
|
|
enum struct TrailData
|
|
{
|
|
char name[128];
|
|
char material[512];
|
|
int tier;
|
|
int vip;
|
|
}
|
|
|
|
ArrayList g_hTrails;
|
|
|
|
public void OnPluginStart()
|
|
{
|
|
RegConsoleCmd("sm_trails", Command_Trail, "The Trails menu");
|
|
RegConsoleCmd("sm_trail", Command_Trail, "The Trails menu");
|
|
|
|
HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy);
|
|
HookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy);
|
|
HookEvent("player_death", PlayerDeath);
|
|
|
|
g_hCookie_trailName = RegClientCookie("unloze_trail", "Select your trail", CookieAccess_Protected);
|
|
g_hCookie_trailHide = RegClientCookie("unloze_trail_hide", "Hide trails", CookieAccess_Protected);
|
|
SetCookieMenuItem(MenuHandler_CookieMenu, 0, "Unloze Trails");
|
|
|
|
g_hTrails = new ArrayList(sizeof(TrailData));
|
|
|
|
|
|
ConVar cvar;
|
|
HookConVarChange((cvar = CreateConVar("sm_unloze_trails_lifetime", "1.0", "Lifetime of trails")), Cvar_TrailsLifeTime);
|
|
g_fTrailsLifeTime = cvar.FloatValue;
|
|
delete cvar;
|
|
|
|
ConVar cvar1;
|
|
HookConVarChange((cvar1 = CreateConVar("sm_unloze_trails_start_width", "10", "Start width of trails")), Cvar_TrailsStartWidth);
|
|
g_iTrailsStartWidth = cvar1.IntValue;
|
|
delete cvar1;
|
|
|
|
ConVar cvar2;
|
|
HookConVarChange((cvar2 = CreateConVar("sm_unloze_trails_end_width", "6", "End width of trails")), Cvar_TrailsEndWidth);
|
|
g_iTrailsEndWidth = cvar2.IntValue;
|
|
delete cvar2;
|
|
}
|
|
|
|
public void Cvar_TrailsEndWidth(ConVar convar, const char[] oldValue, const char[] newValue)
|
|
{
|
|
g_iTrailsEndWidth = convar.IntValue;
|
|
}
|
|
|
|
public void Cvar_TrailsStartWidth(ConVar convar, const char[] oldValue, const char[] newValue)
|
|
{
|
|
g_iTrailsStartWidth = convar.IntValue;
|
|
}
|
|
|
|
public void Cvar_TrailsLifeTime(ConVar convar, const char[] oldValue, const char[] newValue)
|
|
{
|
|
g_fTrailsLifeTime = convar.FloatValue;
|
|
}
|
|
|
|
public void MenuHandler_CookieMenu(int client, CookieMenuAction action, any info, char[] buffer, int maxlen)
|
|
{
|
|
switch (action)
|
|
{
|
|
case CookieMenuAction_DisplayOption:
|
|
{
|
|
Format(buffer, maxlen, "Trails");
|
|
}
|
|
case CookieMenuAction_SelectOption:
|
|
{
|
|
if (!g_bTrailsEnabled)
|
|
{
|
|
PrintToChat(client, "[Trails] Trails are disabled on this map.");
|
|
return;
|
|
}
|
|
OpenTrailsMenu(client);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void PlayerDeath(Handle:event,const String:name[],bool:dontBroadcast)
|
|
{
|
|
int client = GetClientOfUserId(GetEventInt(event, "userid"));
|
|
KillTrail(client);
|
|
}
|
|
|
|
public void Event_RoundEnd(Handle event, const char[] name, bool dontBroadcast)
|
|
{
|
|
if (!g_bTrailsEnabled)
|
|
{
|
|
return;
|
|
}
|
|
for (int client = 1; client <= MAXPLAYERS; client++)
|
|
{
|
|
KillTrail(client);
|
|
}
|
|
}
|
|
|
|
public void Event_RoundStart(Handle event, const char[] name, bool dontBroadcast)
|
|
{
|
|
if (!g_bTrailsEnabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
g_bRoundStartSpawnTrails = false;
|
|
//From Round start we wait 5 seconds before spawning trails so map has time to load its edicts for the round.
|
|
CreateTimer(5.0, allow_spawning_trails);
|
|
}
|
|
|
|
public Action allow_spawning_trails(Handle hTimer)
|
|
{
|
|
g_bRoundStartSpawnTrails = true;
|
|
for (int client = 1; client <= MAXPLAYERS; client++)
|
|
{
|
|
if (IsValidClient(client) && g_iClientTrail[client] != -1 && IsPlayerAlive(client) && ZR_IsClientHuman(client))
|
|
{
|
|
SpawnTrail(client);
|
|
}
|
|
}
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Action Command_Trail(int client, int args)
|
|
{
|
|
if (!g_bTrailsEnabled)
|
|
{
|
|
PrintToChat(client, "[Trails] Trails are disabled on this map.");
|
|
return Plugin_Handled;
|
|
}
|
|
OpenTrailsMenu(client);
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
void OpenTrailsMenu(int client)
|
|
{
|
|
Menu menu = new Menu(MenuHandler_Trails);
|
|
menu.SetTitle("Trails Menu");
|
|
|
|
if (g_bHideTrails[client])
|
|
{
|
|
menu.AddItem("-1", "Unhide Trails");
|
|
}
|
|
else
|
|
{
|
|
menu.AddItem("-1", "Hide Trails");
|
|
}
|
|
|
|
int count = g_hTrails.Length;
|
|
int clientTier = GetPlayerTier_native(client);
|
|
bool vipAccess = CheckCommandAccess(client, "", ADMFLAG_RESERVATION, false);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
TrailData trail;
|
|
g_hTrails.GetArray(i, trail);
|
|
|
|
char info[128];
|
|
IntToString(i, info, sizeof(info));
|
|
|
|
if (trail.tier > clientTier)
|
|
{
|
|
char displayName[128];
|
|
Format(displayName, sizeof(displayName), "%s (Tier %d)", trail.name, trail.tier);
|
|
menu.AddItem(info, displayName, ITEMDRAW_DISABLED);
|
|
}
|
|
else if (trail.vip && !vipAccess)
|
|
{
|
|
char displayName[128];
|
|
Format(displayName, sizeof(displayName), "%s (VIP)", trail.name);
|
|
menu.AddItem(info, displayName, ITEMDRAW_DISABLED);
|
|
}
|
|
else
|
|
{
|
|
menu.AddItem(info, trail.name);
|
|
}
|
|
}
|
|
|
|
menu.ExitButton = true;
|
|
menu.Display(client, MENU_TIME_FOREVER);
|
|
}
|
|
|
|
public int MenuHandler_Trails(Menu menu, MenuAction action, int param1, int param2)
|
|
{
|
|
switch (action)
|
|
{
|
|
case MenuAction_Select:
|
|
{
|
|
char info[8];
|
|
menu.GetItem(param2, info, sizeof(info));
|
|
int index = StringToInt(info);
|
|
g_iClientTrail[param1] = index;
|
|
|
|
if (index == -1)
|
|
{
|
|
g_bHideTrails[param1] = !g_bHideTrails[param1];
|
|
PrintToChat(param1, "Trails are now %s", g_bHideTrails[param1]? "Hidden": "Unhidden");
|
|
SetClientCookie(param1, g_hCookie_trailName, "");
|
|
KillTrail(param1);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
TrailData trail;
|
|
g_hTrails.GetArray(index, trail);
|
|
PrintToChat(param1, "[Trails] Trail set to: %s", trail.name);
|
|
SetClientCookie(param1, g_hCookie_trailName, trail.name);
|
|
if (IsPlayerAlive(param1) && ZR_IsClientHuman(param1))
|
|
{
|
|
SpawnTrail(param1);
|
|
}
|
|
else
|
|
{
|
|
PrintToChat(param1, "Not Applying trail right now since you are not alive as human.");
|
|
}
|
|
}
|
|
|
|
//OpenTrailsMenu(param1); // reopen
|
|
}
|
|
case MenuAction_End:
|
|
{
|
|
delete menu;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public OnMapEnd()
|
|
{
|
|
for (int client = 1; client <= MAXPLAYERS; client++)
|
|
{
|
|
KillTrail(client);
|
|
}
|
|
|
|
for (int client = 1; client <= MAXPLAYERS; client++)
|
|
{
|
|
g_iSpriteModel[client] = -1;
|
|
}
|
|
}
|
|
|
|
public void OnClientDisconnect(client)
|
|
{
|
|
g_bHideTrails[client] = false;
|
|
KillTrail(client);
|
|
g_iClientTrail[client] = -1;
|
|
}
|
|
|
|
public void OnClientCookiesCached(int client)
|
|
{
|
|
char sBuffer[512];
|
|
GetClientCookie(client, g_hCookie_trailHide, sBuffer, sizeof(sBuffer));
|
|
|
|
if (sBuffer[0])
|
|
{
|
|
g_bHideTrails[client] = StringToInt(sBuffer) != 0 ? true : false;
|
|
}
|
|
else
|
|
{
|
|
g_bHideTrails[client] = false;
|
|
}
|
|
|
|
GetClientCookie(client, g_hCookie_trailName, sBuffer, sizeof(sBuffer));
|
|
if (sBuffer[0])
|
|
{
|
|
g_iClientTrail[client] = -1;
|
|
int count = g_hTrails.Length;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
TrailData trail;
|
|
g_hTrails.GetArray(i, trail);
|
|
if (StrEqual(trail.name, sBuffer, true))
|
|
{
|
|
g_iClientTrail[client] = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_iClientTrail[client] = -1;
|
|
}
|
|
}
|
|
|
|
public void OnClientPostAdminCheck(int client)
|
|
{
|
|
g_iSpriteModel[client] = -1;
|
|
}
|
|
|
|
public void OnMapStart()
|
|
{
|
|
char Mapname[128];
|
|
GetCurrentMap(Mapname, sizeof(Mapname));
|
|
g_bTrailsEnabled = GetMapTrails(Mapname) != 0 ? true : false;
|
|
LoadTrails();
|
|
}
|
|
|
|
public void LoadTrails()
|
|
{
|
|
g_hTrails.Clear();
|
|
char path[PLATFORM_MAX_PATH];
|
|
BuildPath(Path_SM, path, sizeof(path), "configs/unloze_trails.cfg");
|
|
|
|
KeyValues kv = new KeyValues("Trails");
|
|
if (!kv.ImportFromFile(path))
|
|
{
|
|
LogError("[trails] Could not load %s", path);
|
|
delete kv;
|
|
return;
|
|
}
|
|
|
|
// Enter the root "Trails" section, then iterate its children
|
|
if (kv.GotoFirstSubKey())
|
|
{
|
|
do
|
|
{
|
|
TrailData trail;
|
|
kv.GetSectionName(trail.name, sizeof(trail.name));
|
|
kv.GetString("material", trail.material, sizeof(trail.material), "");
|
|
trail.tier = kv.GetNum("tier", 0);
|
|
trail.vip = kv.GetNum("VIP", 0);
|
|
g_hTrails.PushArray(trail);
|
|
|
|
PrecacheModel(trail.material, false);
|
|
AddFileToDownloadsTable(trail.material);
|
|
|
|
char vtf_download[512];
|
|
strcopy(vtf_download, sizeof(vtf_download), trail.material);
|
|
|
|
//modifying last two characters so its .vtf
|
|
int len = strlen(vtf_download);
|
|
vtf_download[len - 2] = 't';
|
|
vtf_download[len - 1] = 'f';
|
|
AddFileToDownloadsTable(vtf_download);
|
|
}
|
|
while (kv.GotoNextKey());
|
|
}
|
|
delete kv;
|
|
}
|
|
|
|
public void ZR_OnClientInfected(int client, int attacker, bool motherInfect, bool respawnOverride, bool respawn)
|
|
{
|
|
KillTrail(client);
|
|
}
|
|
|
|
public void KillTrail(int client)
|
|
{
|
|
if (g_iSpriteModel[client] > MaxClients && IsValidEdict(g_iSpriteModel[client]))
|
|
{
|
|
SDKUnhook(g_iSpriteModel[client], SDKHook_SetTransmit, Hook_TrailShouldHide);
|
|
AcceptEntityInput(g_iSpriteModel[client], "kill");
|
|
}
|
|
g_iSpriteModel[client] = -1;
|
|
}
|
|
|
|
public void SpawnTrail(int client)
|
|
{
|
|
KillTrail(client);
|
|
if (!g_bRoundStartSpawnTrails)
|
|
{
|
|
PrintToChat(client, "Cant spawn trails this early in the round.");
|
|
return;
|
|
}
|
|
|
|
//we got 2048 edicts in total
|
|
int edict_limit = 1700;
|
|
int edict_count = GetEntityCount();
|
|
if (edict_count > edict_limit)
|
|
{
|
|
PrintToChat(client, "Not spawning your trail due to edict limitations");
|
|
return;
|
|
}
|
|
|
|
g_iSpriteModel[client] = CreateEntityByName("env_spritetrail");
|
|
|
|
if (g_iSpriteModel[client] != -1)
|
|
{
|
|
DispatchKeyValueFloat(g_iSpriteModel[client], "lifetime", g_fTrailsLifeTime);
|
|
char startwidth[16];
|
|
Format(startwidth, sizeof(startwidth), "%i", g_iTrailsStartWidth);
|
|
DispatchKeyValue(g_iSpriteModel[client], "startwidth", startwidth);
|
|
|
|
char endwidth[16];
|
|
Format(endwidth, sizeof(endwidth), "%i", g_iTrailsEndWidth);
|
|
DispatchKeyValue(g_iSpriteModel[client], "endwidth", endwidth);
|
|
|
|
TrailData trail;
|
|
g_hTrails.GetArray(g_iClientTrail[client], trail);
|
|
DispatchKeyValue(g_iSpriteModel[client], "spritename", trail.material);
|
|
DispatchKeyValue(g_iSpriteModel[client], "renderamt", "255");
|
|
|
|
DispatchKeyValue(g_iSpriteModel[client], "rendercolor", "255 255 255 255");
|
|
DispatchKeyValue(g_iSpriteModel[client], "rendermode", "1");
|
|
|
|
// We give the name for our entities here
|
|
char buffer[1024];
|
|
Format(buffer, sizeof(buffer), "unloze_trails_%d", g_iSpriteModel[client]);
|
|
DispatchKeyValue(g_iSpriteModel[client], "targetname", buffer);
|
|
|
|
DispatchSpawn(g_iSpriteModel[client]);
|
|
|
|
float dest_vector[3];
|
|
dest_vector[0] = 0.0;
|
|
dest_vector[1] = 0.0;
|
|
dest_vector[2] = 5.0;
|
|
|
|
float or[3];
|
|
float ang[3];
|
|
float fForward[3];
|
|
float fRight[3];
|
|
float fUp[3];
|
|
|
|
GetClientAbsOrigin(client, or);
|
|
GetClientAbsAngles(client, ang);
|
|
|
|
GetAngleVectors(ang, fForward, fRight, fUp);
|
|
|
|
or[0] += fRight[0]*dest_vector[0] + fForward[0]*dest_vector[1] + fUp[0]*dest_vector[2];
|
|
or[1] += fRight[1]*dest_vector[0] + fForward[1]*dest_vector[1] + fUp[1]*dest_vector[2];
|
|
or[2] += fRight[2]*dest_vector[0] + fForward[2]*dest_vector[1] + fUp[2]*dest_vector[2];
|
|
|
|
TeleportEntity(g_iSpriteModel[client], or, NULL_VECTOR, NULL_VECTOR);
|
|
|
|
SetVariantString("!activator");
|
|
AcceptEntityInput(g_iSpriteModel[client], "SetParent", client);
|
|
SetEntPropFloat(g_iSpriteModel[client], Prop_Send, "m_flTextureRes", 0.05);
|
|
SetEntPropEnt(g_iSpriteModel[client], Prop_Send, "m_hOwnerEntity", client);
|
|
|
|
SDKHook(g_iSpriteModel[client], SDKHook_SetTransmit, Hook_TrailShouldHide);
|
|
}
|
|
}
|
|
|
|
public Action Hook_TrailShouldHide(int entity, int client)
|
|
{
|
|
if (g_bHideTrails[client])
|
|
{
|
|
return Plugin_Handled;
|
|
}
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
stock bool IsValidClient(int client)
|
|
{
|
|
if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client))
|
|
return true;
|
|
return false;
|
|
}
|