#pragma semicolon 1 #define PLUGIN_AUTHOR "jenz" #define PLUGIN_VERSION "1.0" #include #include #include #include #include #include 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; }