diff --git a/addons/sourcemod/configs/vsh/vsh.cfg b/addons/sourcemod/configs/vsh/vsh.cfg index 25117ac8..6162b679 100644 --- a/addons/sourcemod/configs/vsh/vsh.cfg +++ b/addons/sourcemod/configs/vsh/vsh.cfg @@ -2423,6 +2423,8 @@ "vsh_cookies_preferences" "1" //Should preferences use cookies to store? (Disable if you want to store preferences somewhere else) "vsh_cookies_queue" "1" //Should queue use cookies to store? (Disable if you want to store queue somewhere else) + "vsh_blacklist_amount" "2" //How many bosses can a player blacklist? (0 disables this feature) + "vsh_class_limit" "1" //Enable/Disable entire class limit "vsh_class_limit_scout" "6" //Max on how many players can play as that class, use -1 for no limit "vsh_class_limit_soldier" "6" diff --git a/addons/sourcemod/scripting/saxtonhale.sp b/addons/sourcemod/scripting/saxtonhale.sp index 822fc5fb..82b21577 100644 --- a/addons/sourcemod/scripting/saxtonhale.sp +++ b/addons/sourcemod/scripting/saxtonhale.sp @@ -449,6 +449,7 @@ ConVar tf_arena_preround_time; #include "vsh/config.sp" #include "vsh/menu/menu_admin.sp" +#include "vsh/menu/menu_blacklist.sp" #include "vsh/menu/menu_boss.sp" #include "vsh/menu/menu_weapon.sp" #include "vsh/menu.sp" @@ -460,6 +461,7 @@ ConVar tf_arena_preround_time; #include "vsh/function/func_hook.sp" #include "vsh/function/func_native.sp" +#include "vsh/blacklist.sp" #include "vsh/classlimit.sp" #include "vsh/command.sp" #include "vsh/console.sp" @@ -533,6 +535,7 @@ public void OnPluginStart() Config_Init(); + Blacklist_Init(); ClassLimit_Init(); Command_Init(); Console_Init(); @@ -758,6 +761,7 @@ public void OnPluginStart() g_ConfigConvar.Create("vsh_telefrag_damage", "9001.0", "Damage amount to boss from telefrag", _, true, 0.0); g_ConfigConvar.Create("vsh_music_enable", "1", "Enable boss music?", _, true, 0.0, true, 1.0); g_ConfigConvar.Create("vsh_rps_enable", "1", "Allow everyone use Rock Paper Scissors Taunt?", _, true, 0.0, true, 1.0); + g_ConfigConvar.Create("vsh_blacklist_amount", "2", "Maximum amount of bosses a player can blacklist for themselves (0 disables the feature)", _, true, 0.0); //Incase of lateload, call client join functions for (int iClient = 1; iClient <= MaxClients; iClient++) @@ -1324,6 +1328,7 @@ public void OnClientPutInServer(int iClient) SDKHook(iClient, SDKHook_StartTouch, Client_OnStartTouch); SDKHook(iClient, SDKHook_WeaponSwitchPost, Client_OnWeaponSwitchPost); + Blacklist_Load(iClient); Cookies_OnClientJoin(iClient); } diff --git a/addons/sourcemod/scripting/vsh/blacklist.sp b/addons/sourcemod/scripting/vsh/blacklist.sp new file mode 100644 index 00000000..9af1a96f --- /dev/null +++ b/addons/sourcemod/scripting/vsh/blacklist.sp @@ -0,0 +1,141 @@ +#pragma semicolon 1 +#pragma newdecls required + +static ArrayList g_aBlacklistedBosses[MAXPLAYERS]; +static Cookie g_hCookiesBlacklist; + +enum BlacklistResult +{ + BLACKLIST_ADDED, + BLACKLIST_REMOVED, + BLACKLIST_FAILED_TO_CHANGE +} + +void Blacklist_Init() +{ + for (int i = 1; i <= MaxClients; i++) + { + g_aBlacklistedBosses[i] = new ArrayList(32); + } + + g_hCookiesBlacklist = new Cookie("vsh_blacklist", "Which bosses this player has blacklisted", CookieAccess_Protected); +} + +BlacklistResult Blacklist_Toggle(int iClient, const char[] sBoss) +{ + BlacklistResult result; + int iIndex = g_aBlacklistedBosses[iClient].FindString(sBoss); + if (iIndex == -1) + { + int iMax = g_ConfigConvar.LookupInt("vsh_blacklist_amount"); + if (Blacklist_GetAmount(iClient) >= iMax) + return BLACKLIST_FAILED_TO_CHANGE; + + g_aBlacklistedBosses[iClient].PushString(sBoss); + result = BLACKLIST_ADDED; + } + else + { + g_aBlacklistedBosses[iClient].Erase(iIndex); + result = BLACKLIST_REMOVED; + } + + Blacklist_Save(iClient); + return result; +} + +void Blacklist_Load(int iClient) +{ + g_aBlacklistedBosses[iClient].Clear(); + + int iMax = g_ConfigConvar.LookupInt("vsh_blacklist_amount"); + if (iMax <= 0) + return; + + if (IsFakeClient(iClient)) + return; + + // Get list of all bosses + ArrayList aBosses = SaxtonHale_GetAllClassType(VSHClassType_Boss); + + // Cookies can only be 99 chars long + char sValue[100]; + g_hCookiesBlacklist.Get(iClient, sValue, sizeof(sValue)); + + char sExplode[100][16]; + int iCookieAmount = ExplodeString(sValue, ";", sExplode, sizeof(sExplode[]), sizeof(sExplode)); + int iPluginAmount; + + for (int i = 0; i < iMax; i++) + { + if (sExplode[i][0] == '\0') + { + if (i == 0) + iCookieAmount = 0; + + break; + } + + // This boss doesn't exist. Maybe it was recently removed? + if (aBosses.FindString(sExplode[i]) == -1) + continue; + + // This boss is hidden. Maybe it was set this way recently? + if (SaxtonHale_CallFunction(sExplode[i], "IsBossHidden")) + continue; + + g_aBlacklistedBosses[iClient].PushString(sExplode[i]); + iPluginAmount++; + } + + // Something changed since last time? Save the new data + if (iCookieAmount != iPluginAmount) + Blacklist_Save(iClient); + + delete aBosses; +} + +void Blacklist_Save(int iClient) +{ + // Cookies can only be 99 chars long + char sValue[100]; + for (int i = 0; i < Blacklist_GetAmount(iClient); i++) + { + if (i > 0) + StrCat(sValue, sizeof(sValue), ";"); + + char sBuffer[100]; + g_aBlacklistedBosses[iClient].GetString(i, sBuffer, sizeof(sBuffer)); + StrCat(sValue, sizeof(sValue), sBuffer); + } + + g_hCookiesBlacklist.Set(iClient, sValue); +} + +ArrayList Blacklist_Get(int iClient) +{ + // If we somehow have more blacklisted bosses than allowed (ie convar recently changed), only use as many as we can + int iLength = g_aBlacklistedBosses[iClient].Length; + int iMax = g_ConfigConvar.LookupInt("vsh_blacklist_amount"); + + for (int i = iLength - 1; i >= iMax; i--) + g_aBlacklistedBosses[iClient].Erase(i); + + return g_aBlacklistedBosses[iClient].Clone(); +} + +int Blacklist_GetAmount(int iClient) +{ + return g_aBlacklistedBosses[iClient].Length; +} + +bool Blacklist_IsBossBlacklisted(int iClient, const char[] sBoss) +{ + return g_aBlacklistedBosses[iClient].FindString(sBoss) != -1; +} + +void Blacklist_Clear(int iClient) +{ + g_aBlacklistedBosses[iClient].Clear(); + Blacklist_Save(iClient); +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/vsh/command.sp b/addons/sourcemod/scripting/vsh/command.sp index afae0cf7..1c98fdfb 100644 --- a/addons/sourcemod/scripting/vsh/command.sp +++ b/addons/sourcemod/scripting/vsh/command.sp @@ -19,6 +19,7 @@ public void Command_Init() Command_Create("modifiers", Command_Modifiers); Command_Create("next", Command_HaleNext); Command_Create("credits", Command_Credits); + Command_Create("blacklist", Command_Blacklist); Command_Create("settings", Command_Preferences); Command_Create("preferences", Command_Preferences); @@ -120,6 +121,20 @@ public Action Command_Boss(int iClient, int iArgs) return Plugin_Handled; } +public Action Command_Blacklist(int iClient, int iArgs) +{ + if (!g_bEnabled) return Plugin_Continue; + + if (iClient == 0) + { + ReplyToCommand(iClient, "This command can only be used in-game."); + return Plugin_Handled; + } + + MenuBlacklist_DisplayMain(iClient); + return Plugin_Handled; +} + public Action Command_MultiBoss(int iClient, int iArgs) { if (!g_bEnabled) return Plugin_Continue; @@ -194,11 +209,11 @@ public Action Command_Preferences(int iClient, int iArgs) char buffer[512]; if (bValue) - Format(buffer, sizeof(buffer), "Enable"); + Format(buffer, sizeof(buffer), "Enabled"); else - Format(buffer, sizeof(buffer), "Disable"); + Format(buffer, sizeof(buffer), "Disabled"); - PrintToChat(iClient, "%s%s %s %s", TEXT_TAG, TEXT_COLOR, buffer, g_strPreferencesName[nPreferences]); + PrintToChat(iClient, "%s%s %s %s.", TEXT_TAG, TEXT_COLOR, buffer, g_strPreferencesName[nPreferences]); return Plugin_Handled; } else diff --git a/addons/sourcemod/scripting/vsh/menu.sp b/addons/sourcemod/scripting/vsh/menu.sp index 46dd399a..18d90cb6 100644 --- a/addons/sourcemod/scripting/vsh/menu.sp +++ b/addons/sourcemod/scripting/vsh/menu.sp @@ -23,6 +23,7 @@ void Menu_Init() g_hMenuMain.AddItem("modifiers", "Modifiers Info (!vshmodifiers)"); g_hMenuMain.AddItem("queue", "Queue List (!vshnext)"); g_hMenuMain.AddItem("preference", "Settings (!vshsettings)"); + g_hMenuMain.AddItem("blacklist", "Boss Blacklist (!vshblacklist)"); g_hMenuMain.AddItem("credit", "Credits (!vshcredits)"); // Credits @@ -85,6 +86,8 @@ public int Menu_SelectMain(Menu hMenu, MenuAction action, int iClient, int iSele Menu_DisplayQueue(iClient); else if (StrEqual(sSelect, "preference")) Menu_DisplayPreferences(iClient); + else if (StrEqual(sSelect, "blacklist")) + MenuBlacklist_DisplayMain(iClient); else if (StrEqual(sSelect, "credit")) Menu_DisplayCredits(iClient); else diff --git a/addons/sourcemod/scripting/vsh/menu/menu_blacklist.sp b/addons/sourcemod/scripting/vsh/menu/menu_blacklist.sp new file mode 100644 index 00000000..1a3aa25b --- /dev/null +++ b/addons/sourcemod/scripting/vsh/menu/menu_blacklist.sp @@ -0,0 +1,97 @@ +#pragma semicolon 1 +#pragma newdecls required + +void MenuBlacklist_DisplayMain(int iClient) +{ + int iMax = g_ConfigConvar.LookupInt("vsh_blacklist_amount"); + if (iMax <= 0) + { + PrintToChat(iClient, "%s%s The boss blacklist is currently disabled.", TEXT_TAG, TEXT_COLOR); + return; + } + + Menu hMenu = new Menu(MenuBlacklist_SelectMain); + ArrayList aBlacklist = Blacklist_Get(iClient); + + char sTitle[512]; + + if (iMax == 1) + FormatEx(sTitle, sizeof(sTitle), "Boss Blacklist Menu\n \nYou can blacklist a boss to avoid being selected as them.\n "); + else + FormatEx(sTitle, sizeof(sTitle), "Boss Blacklist Menu\n \nYou can blacklist up to %d bosses to avoid being selected as them.\n ", iMax); + + int iLength = aBlacklist.Length; + if (iLength) + { + StrCat(sTitle, sizeof(sTitle), "\nCurrent blacklist:"); + for (int i = 0; i < iLength; i++) + { + char sType[64], sName[64]; + aBlacklist.GetString(i, sType, sizeof(sType)); + SaxtonHale_CallFunction(sType, "GetBossName", sName, sizeof(sName)); + + Format(sTitle, sizeof(sTitle), "%s\n- %s", sTitle, sName); + } + + StrCat(sTitle, sizeof(sTitle), "\n "); + } + + hMenu.SetTitle(sTitle); + hMenu.AddItem("back", "<- Back"); + hMenu.AddItem("list", "Select which bosses to blacklist"); + hMenu.AddItem("clear", "Clear blacklist"); + hMenu.Display(iClient, MENU_TIME_FOREVER); +} + +public int MenuBlacklist_SelectMain(Menu hMenu, MenuAction action, int iClient, int iSelect) +{ + if (action != MenuAction_Select) return 0; + + char sSelect[32]; + hMenu.GetItem(iSelect, sSelect, sizeof(sSelect)); + + if (StrEqual(sSelect, "back")) + { + Menu_DisplayMain(iClient); + } + else if (StrEqual(sSelect, "list")) + { + // Just offload this shit to the vshboss menu man + PrintToChat(iClient, "%s%s You can blacklist bosses in their respective pages.", TEXT_TAG, TEXT_COLOR); + MenuBoss_DisplayList(iClient, VSHClassType_Boss, MenuBoss_CallbackInfo); + } + else if (StrEqual(sSelect, "clear")) + { + MenuBlacklist_DisplayClear(iClient); + } + + return 0; +} + +void MenuBlacklist_DisplayClear(int iClient) +{ + Menu hMenu = new Menu(MenuBlacklist_SelectClear); + + hMenu.SetTitle("Blacklist Menu\n \nAre you sure you want to clear your blacklist?\n "); + hMenu.AddItem("yes", "Yes"); + hMenu.AddItem("no", "No"); + + hMenu.Display(iClient, MENU_TIME_FOREVER); +} + +public int MenuBlacklist_SelectClear(Menu hMenu, MenuAction action, int iClient, int iSelect) +{ + if (action != MenuAction_Select) return 0; + + char sSelect[32]; + hMenu.GetItem(iSelect, sSelect, sizeof(sSelect)); + + if (StrEqual(sSelect, "yes")) + { + Blacklist_Clear(iClient); + PrintToChat(iClient, "%s%s Your blacklist has been cleared.", TEXT_TAG, TEXT_COLOR); + } + + MenuBlacklist_DisplayMain(iClient); + return 0; +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/vsh/menu/menu_boss.sp b/addons/sourcemod/scripting/vsh/menu/menu_boss.sp index ddd8c055..9fdd7dac 100644 --- a/addons/sourcemod/scripting/vsh/menu/menu_boss.sp +++ b/addons/sourcemod/scripting/vsh/menu/menu_boss.sp @@ -158,7 +158,7 @@ public void MenuBoss_CallbackInfo(int iClient, MenuBossOption nOption, const cha MenuBoss_DisplayInfo(iClient, g_nMenuBossClassType[iClient], sType); } -void MenuBoss_DisplayInfo(int iClient, SaxtonHaleClassType nClassType, const char[] sType, int iTime = MENU_TIME_FOREVER) +void MenuBoss_DisplayInfo(int iClient, SaxtonHaleClassType nClassType, const char[] sType, int iTime = MENU_TIME_FOREVER, bool bFromMenu = true) { g_nMenuBossClassType[iClient] = nClassType; @@ -179,17 +179,87 @@ void MenuBoss_DisplayInfo(int iClient, SaxtonHaleClassType nClassType, const cha Format(sInfo, sizeof(sInfo), "%s\n \n%s", sName, sInfo); hMenuBossInfo.SetTitle(sInfo); - hMenuBossInfo.AddItem("back", "<- Back"); + + if (bFromMenu) + { + hMenuBossInfo.AddItem("back", "<- Back"); + + int iMaxBlacklistAmount = g_ConfigConvar.LookupInt("vsh_blacklist_amount"); + if (nClassType == VSHClassType_Boss && iMaxBlacklistAmount) + { + char sDisplay[64]; + int iStyle = ITEMDRAW_DEFAULT; + + if (Blacklist_IsBossBlacklisted(iClient, sType)) + { + sDisplay = "Remove from blacklist"; + } + else + { + sDisplay = "Add to blacklist"; + if (Blacklist_GetAmount(iClient) >= iMaxBlacklistAmount) + { + iStyle = ITEMDRAW_DISABLED; + StrCat(sDisplay, sizeof(sDisplay), " (maximum amount reached)"); + } + } + + FormatEx(sInfo, sizeof(sInfo), "blacklist%s", sType); + hMenuBossInfo.AddItem(sInfo, sDisplay, iStyle); + } + } + else + { + hMenuBossInfo.AddItem("dismiss", "Dismiss"); + } + hMenuBossInfo.Display(iClient, iTime); } public int MenuBoss_SelectInfo(Menu hMenu, MenuAction action, int iClient, int iSelect) { - //Only back button in menu - if (action == MenuAction_Select) - MenuBoss_DisplayList(iClient, g_nMenuBossClassType[iClient], MenuBoss_CallbackInfo); - else if (action == MenuAction_End) + if (action == MenuAction_End) + { delete hMenu; + return 0; + } + + if (action != MenuAction_Select) return 0; + + char sSelect[64]; + hMenu.GetItem(iSelect, sSelect, sizeof(sSelect)); + + if (StrEqual(sSelect, "back")) + { + MenuBoss_DisplayList(iClient, g_nMenuBossClassType[iClient], MenuBoss_CallbackInfo); + } + else if (StrContains(sSelect, "blacklist", true) == 0) + { + // Remove "blacklist" and keep the boss type + char sBuffer[64]; + strcopy(sBuffer, sizeof(sBuffer), sSelect[9]); + + BlacklistResult result = Blacklist_Toggle(iClient, sBuffer); + MenuBoss_DisplayInfo(iClient, g_nMenuBossClassType[iClient], sBuffer); + + SaxtonHale_CallFunction(sBuffer, "GetBossName", sBuffer, sizeof(sBuffer)); + + switch (result) + { + case BLACKLIST_ADDED: + PrintToChat(iClient, "%s%s Added %s%s %sto the boss blacklist.", TEXT_TAG, TEXT_COLOR, TEXT_DARK, sBuffer, TEXT_COLOR); + + case BLACKLIST_REMOVED: + PrintToChat(iClient, "%s%s Removed %s%s %sfrom the boss blacklist.", TEXT_TAG, TEXT_COLOR, TEXT_DARK, sBuffer, TEXT_COLOR); + + case BLACKLIST_FAILED_TO_CHANGE: + PrintToChat(iClient, "%s%s Could not change %s%s %sblacklist status.", TEXT_TAG, TEXT_COLOR, TEXT_DARK, sBuffer, TEXT_COLOR); + } + } + else if (StrEqual(sSelect, "dismiss")) + { + return 0; + } return 0; } diff --git a/addons/sourcemod/scripting/vsh/nextboss.sp b/addons/sourcemod/scripting/vsh/nextboss.sp index 5bd76175..0f93dda4 100644 --- a/addons/sourcemod/scripting/vsh/nextboss.sp +++ b/addons/sourcemod/scripting/vsh/nextboss.sp @@ -282,7 +282,7 @@ void NextBoss_SetBoss(SaxtonHaleNextBoss nextBoss, ArrayList aNonBosses) bool bModifierSet = nextBoss.GetModifier(sModifierType, sizeof(sModifierType)); if (StrEmpty(sBossType)) - NextBoss_GetRandomNormal(sBossType, sizeof(sBossType)); + NextBoss_GetRandomNormal(sBossType, sizeof(sBossType), nextBoss.iClient); if (!bModifierSet) NextBoss_GetRandomModifiers(sModifierType, sizeof(sModifierType)); @@ -306,7 +306,7 @@ void NextBoss_SetBoss(SaxtonHaleNextBoss nextBoss, ArrayList aNonBosses) TF2_ForceTeamJoin(nextBoss.iClient, TFTeam_Boss); //Display to client what boss you are for 10 seconds - MenuBoss_DisplayInfo(nextBoss.iClient, VSHClassType_Boss, sBossType, 10); + MenuBoss_DisplayInfo(nextBoss.iClient, VSHClassType_Boss, sBossType, 10, false); //Enable special round if triggered if (nextBoss.bSpecialClassRound) @@ -362,10 +362,12 @@ stock void NextBoss_RemoveMulti(const char[] sBoss) } } -stock void NextBoss_GetRandomNormal(char[] sBoss, int iLength) +stock void NextBoss_GetRandomNormal(char[] sBoss, int iLength, int iClient) { + ArrayList aBlacklist = Blacklist_Get(iClient); + //Saxton Hale get higher chance to appear - if (GetRandomFloat(0.0, 1.0) <= g_ConfigConvar.LookupFloat("vsh_boss_chance_saxton")) + if (GetRandomFloat(0.0, 1.0) <= g_ConfigConvar.LookupFloat("vsh_boss_chance_saxton") && aBlacklist.FindString("SaxtonHale") == -1) { Format(sBoss, iLength, "SaxtonHale"); return; @@ -379,16 +381,26 @@ stock void NextBoss_GetRandomNormal(char[] sBoss, int iLength) if (iIndex >= 0) aBosses.Erase(iIndex); - //Delet hidden bosses int iBossLength = aBosses.Length; for (int i = iBossLength-1; i >= 0; i--) { + //Delet hidden bosses char sBuffer[MAX_TYPE_CHAR]; aBosses.GetString(i, sBuffer, sizeof(sBuffer)); if (SaxtonHale_CallFunction(sBuffer, "IsBossHidden")) + { + aBosses.Erase(i); + continue; + } + + //Delet blacklisted bosses + iIndex = aBlacklist.FindString(sBuffer); + if (iIndex != -1) aBosses.Erase(i); } + delete aBlacklist; + iBossLength = aBosses.Length; if (iBossLength == 0) {