diff --git a/mapchooser_extended/scripting/include/mapchooser_extended.inc b/mapchooser_extended/scripting/include/mapchooser_extended.inc index fe7fdc9..f514762 100755 --- a/mapchooser_extended/scripting/include/mapchooser_extended.inc +++ b/mapchooser_extended/scripting/include/mapchooser_extended.inc @@ -116,6 +116,7 @@ native int GetMapCooldownTime(const char[] map); // in unix time native int GetMapCooldownTime2(const char[] map); // in unix time native int GetMapLowPopHelpCount(const char[] map); native int GetMapLaserFag(const char[] map); +native int GetMapTrails(const char[] map); native int GetMapMinTime(const char[] map); native int GetMapMaxTime(const char[] map); native int GetMapMinPlayers(const char[] map); diff --git a/mapchooser_extended/scripting/mapchooser_extended_unloze.sp b/mapchooser_extended/scripting/mapchooser_extended_unloze.sp index ad9134b..9264dec 100755 --- a/mapchooser_extended/scripting/mapchooser_extended_unloze.sp +++ b/mapchooser_extended/scripting/mapchooser_extended_unloze.sp @@ -477,6 +477,7 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max CreateNative("GetMapCooldownTime2", Native_GetMapCooldownTime2); CreateNative("GetMapLowPopHelpCount", Native_GetMapLowPopHelpCount); CreateNative("GetMapLaserFag", Native_GetMapLaserFag); + CreateNative("GetMapTrails", Native_GetMapTrails); CreateNative("GetMapMinTime", Native_GetMapMinTime); CreateNative("GetMapMaxTime", Native_GetMapMaxTime); CreateNative("GetMapMinPlayers", Native_GetMapMinPlayers); @@ -2676,6 +2677,27 @@ public int Native_GetMapCooldownTime2(Handle plugin, int numParams) return InternalGetMapCooldownTime2(map); } +//GetMapTrails +public int Native_GetMapTrails(Handle plugin, int numParams) +{ + int len; + GetNativeStringLength(1, len); + + if(len <= 0) + return false; + + char[] map = new char[len+1]; + GetNativeString(1, map, len+1); + int count = 0; + + if (g_Config && g_Config.JumpToKey(map)) + { + count = g_Config.GetNum("Trails", count); + g_Config.Rewind(); + } + return count; +} + //GetMapLaserFag public int Native_GetMapLaserFag(Handle plugin, int numParams) { diff --git a/unloze_trails/scripting/unloze_trails.sp b/unloze_trails/scripting/unloze_trails.sp new file mode 100644 index 0000000..ff75feb --- /dev/null +++ b/unloze_trails/scripting/unloze_trails.sp @@ -0,0 +1,439 @@ +#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 + +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)); +} + +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", 2.0); + DispatchKeyValue(g_iSpriteModel[client], "startwidth", "10"); + DispatchKeyValue(g_iSpriteModel[client], "endwidth", "6"); + + 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] = 0.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; +}