diff --git a/addons/sourcemod/scripting/weapons.sp b/addons/sourcemod/scripting/weapons.sp index 0697c0a..7ab1812 100644 --- a/addons/sourcemod/scripting/weapons.sp +++ b/addons/sourcemod/scripting/weapons.sp @@ -23,6 +23,7 @@ #include #undef REQUIRE_PLUGIN #include +#include #pragma semicolon 1 #pragma newdecls required @@ -88,6 +89,7 @@ public void OnPluginStart() g_Cvar_EnableNameTag = CreateConVar("sm_weapons_enable_nametag", "1", "Enable/Disable name tag options"); g_Cvar_EnableStatTrak = CreateConVar("sm_weapons_enable_stattrak", "1", "Enable/Disable StatTrak options"); g_Cvar_EnableSeed = CreateConVar("sm_weapons_enable_seed", "1", "Enable/Disable Seed options"); + g_Cvar_EnableSearch = CreateConVar("sm_weapons_enable_search", "1", "Enable/Disable Search Function"); g_Cvar_FloatIncrementSize = CreateConVar("sm_weapons_float_increment_size", "0.05", "Increase/Decrease by value for weapon float"); g_Cvar_EnableWeaponOverwrite = CreateConVar("sm_weapons_enable_overwrite", "1", "Enable/Disable players overwriting other players' weapons (picked up from the ground) by using !ws command"); g_Cvar_GracePeriod = CreateConVar("sm_weapons_grace_period", "0", "Grace period in terms of seconds counted after round start for allowing the use of !ws command. 0 means no restrictions"); @@ -202,6 +204,18 @@ public Action CommandWeaponSkins(int client, int args) int menuTime; if((menuTime = GetRemainingGracePeriodSeconds(client)) >= 0) { + if (args > 0) { + + char searchSkinName[32]; + GetCmdArgString(searchSkinName, sizeof(searchSkinName)); + + Menu resultMenu = SearchSkins(client, searchSkinName); + menuPlayerSearchTemp[client] = resultMenu; + resultMenu.Display(client, menuTime); + + return Plugin_Handled; + } + CreateMainMenu(client).Display(client, menuTime); } else @@ -209,6 +223,7 @@ public Action CommandWeaponSkins(int client, int args) PrintToChat(client, " %s \x02%t", g_ChatPrefix, "GracePeriod", g_iGracePeriod); } } + return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/weapons/config.sp b/addons/sourcemod/scripting/weapons/config.sp index 790fc0a..442156b 100644 --- a/addons/sourcemod/scripting/weapons/config.sp +++ b/addons/sourcemod/scripting/weapons/config.sp @@ -55,6 +55,9 @@ public void ReadConfig() CloseHandle(kv); } + delete g_smSkinMenuMap[langCounter]; + g_smSkinMenuMap[langCounter] = new StringMap(); + for (int k = 0; k < sizeof(g_WeaponClasses); k++) { if(menuWeapons[langCounter][k] != null) @@ -70,6 +73,7 @@ public void ReadConfig() int counter = 0; char weaponTemp[20]; + do { char name[64]; char index[5]; @@ -79,14 +83,67 @@ public void ReadConfig() KvGetString(kv, "classes", classes, sizeof(classes)); KvGetString(kv, "index", index, sizeof(index)); + Menu menuSkins[MAX_SKIN]; + for (int k = 0; k < sizeof(g_WeaponClasses); k++) { Format(weaponTemp, sizeof(weaponTemp), "%s;", g_WeaponClasses[k]); if(StrContains(classes, weaponTemp) > -1) { menuWeapons[langCounter][k].AddItem(index, name); + + if (g_bEnableSearch) + { + if (menuSkins[counter] == null) + { + menuSkins[counter] = new Menu(SkinsMenuHandler, MENU_ACTIONS_DEFAULT | MenuAction_DisplayItem); + menuSkins[counter].SetTitle(name); + menuSkins[counter].AddItem("-1", "Apply all"); + menuSkins[counter].AddItem("-2", "Apply current"); + menuSkins[counter].ExitBackButton = true; + } + + char weaponName[32]; + Format(weaponName, sizeof(weaponName), "%T (%s)", g_WeaponClasses[k], LANG_SERVER, index); + char weaponIndexStr[32]; + Format(weaponIndexStr, sizeof(weaponIndexStr), "%d", k); + menuSkins[counter].AddItem(weaponIndexStr, weaponName); + } + } } + + if (g_bEnableSearch) + { + for (int j = 0; j < MAX_SKIN; j++) + { + Menu currentMenu = menuSkins[j]; + if (currentMenu == null)continue; + + char currentMenuName[32]; + currentMenu.GetTitle(currentMenuName, sizeof(currentMenuName)); + if (g_smSkinMenuMap[langCounter].ContainsKey(name)) + { + Menu fatherMenu; + g_smSkinMenuMap[langCounter].GetValue(name, fatherMenu); + for (int l = 2; l < currentMenu.ItemCount; l++) + { + char info[32]; + char display[64]; + int a; + currentMenu.GetItem(l, info, sizeof(info), a, display, sizeof(display)); + fatherMenu.AddItem(info, display); + } + + delete currentMenu; + } + else + { + g_smSkinMenuMap[langCounter].SetValue(name, currentMenu); + } + } + } + counter++; } while (KvGotoNextKey(kv)); diff --git a/addons/sourcemod/scripting/weapons/forwards.sp b/addons/sourcemod/scripting/weapons/forwards.sp index e390240..aebc068 100644 --- a/addons/sourcemod/scripting/weapons/forwards.sp +++ b/addons/sourcemod/scripting/weapons/forwards.sp @@ -44,6 +44,7 @@ public void OnConfigsExecuted() g_bEnableNameTag = g_Cvar_EnableNameTag.BoolValue; g_bEnableStatTrak = g_Cvar_EnableStatTrak.BoolValue; g_bEnableSeed = g_Cvar_EnableSeed.BoolValue; + g_bEnableSearch = g_Cvar_EnableSearch.BoolValue; g_fFloatIncrementSize = g_Cvar_FloatIncrementSize.FloatValue; g_iFloatIncrementPercentage = RoundFloat(g_fFloatIncrementSize * 100.0); g_bOverwriteEnabled = g_Cvar_EnableWeaponOverwrite.BoolValue; diff --git a/addons/sourcemod/scripting/weapons/globals.sp b/addons/sourcemod/scripting/weapons/globals.sp index c8b300c..b8deacb 100644 --- a/addons/sourcemod/scripting/weapons/globals.sp +++ b/addons/sourcemod/scripting/weapons/globals.sp @@ -40,6 +40,7 @@ int g_iKnifeIndices[] = { }; const int MAX_LANG = 40; +const int MAX_SKIN = 1255; Database db = null; @@ -74,6 +75,9 @@ bool g_bEnableStatTrak; ConVar g_Cvar_EnableSeed; bool g_bEnableSeed; +ConVar g_Cvar_EnableSearch; +bool g_bEnableSearch; + ConVar g_Cvar_EnableWeaponOverwrite; bool g_bOverwriteEnabled; @@ -120,10 +124,12 @@ char g_MigrationWeapons[][] = { char g_Language[MAX_LANG][32]; int g_iClientLanguage[MAXPLAYERS+1]; Menu menuWeapons[MAX_LANG][sizeof(g_WeaponClasses)]; +Menu menuPlayerSearchTemp[MAXPLAYERS+1]; StringMap g_smWeaponIndex; StringMap g_smWeaponDefIndex; StringMap g_smLanguageIndex; +StringMap g_smSkinMenuMap[MAX_LANG]; GlobalForward g_hOnKnifeSelect_Pre; GlobalForward g_hOnKnifeSelect_Post; diff --git a/addons/sourcemod/scripting/weapons/helpers.sp b/addons/sourcemod/scripting/weapons/helpers.sp index ce937ba..f263049 100644 --- a/addons/sourcemod/scripting/weapons/helpers.sp +++ b/addons/sourcemod/scripting/weapons/helpers.sp @@ -262,3 +262,34 @@ bool IsWarmUpPeriod() { return view_as(GameRules_GetProp("m_bWarmupPeriod")); } + +int GetSkinIdFromSkinMenuDisplay(char display[32]) +{ + Regex regex = CompileRegex(".+ \\((.+)\\)"); + regex.Match(display); + + char skinIdStr[32]; + regex.GetSubString(1, skinIdStr, sizeof(skinIdStr)); + return StringToInt(skinIdStr); +} + +Menu SearchSkins(int client, char skinName[32]) +{ + StringMapSnapshot snapshot = g_smSkinMenuMap[g_iClientLanguage[client]].Snapshot(); + menuPlayerSearchTemp[client] = null; + Menu result = new Menu(SearchMenuHandler, MENU_ACTIONS_DEFAULT); + + for (int i = 0; i < snapshot.Length; i++) + { + char name[32] + snapshot.GetKey(i, name, sizeof(name)); + + // if current menu is the menu we searched for + if (StrContains(skinName, name, false) > -1 || StrContains(name, skinName, false) > -1) + { + result.AddItem(name, name); + } + } + + return result; +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/weapons/menus.sp b/addons/sourcemod/scripting/weapons/menus.sp index f790855..4b33783 100644 --- a/addons/sourcemod/scripting/weapons/menus.sp +++ b/addons/sourcemod/scripting/weapons/menus.sp @@ -1017,3 +1017,216 @@ public int LanguageMenuHandler(Menu menu, MenuAction action, int client, int sel } } } + +public int SkinsMenuHandler(Menu menu, MenuAction action, int client, int selection) +{ + switch(action) + { + case MenuAction_Select: + { + if(IsClientInGame(client)) + { + if (selection == 0) + { + // Apply to all + for (int i = 2; i < menu.ItemCount; i++) { + char weaponTempIndexStr[32]; + char display[32]; + int a; + menu.GetItem(i, weaponTempIndexStr, sizeof(weaponTempIndexStr), a, display, sizeof(display)); + int tempIndex = StringToInt(weaponTempIndexStr); + int skinId = GetSkinIdFromSkinMenuDisplay(display); + + g_iSkins[client][tempIndex] = skinId; + + char updateFields[256]; + char weaponName[32]; + RemoveWeaponPrefix(g_WeaponClasses[tempIndex], weaponName, sizeof(weaponName)); + Format(updateFields, sizeof(updateFields), "%s = %d", weaponName, skinId); + UpdatePlayerData(client, updateFields); + + RefreshWeapon(client, tempIndex); + } + + DataPack pack; + CreateDataTimer(0.5, SkinsMenuTimer, pack); + pack.WriteCell(menu); + pack.WriteCell(GetClientUserId(client)); + pack.WriteCell(GetMenuSelectionPosition()); + + return 0; + } + else if (selection == 1) + { + // Apply to current + + int weaponEntity = GetEntPropEnt(client, Prop_Data, "m_hActiveWeapon", 0); + char weaponClass[32]; + if (weaponEntity != -1 && GetWeaponClass(weaponEntity, weaponClass, sizeof(weaponClass))) + { + int index; + g_smWeaponIndex.GetValue(weaponClass, index); + + for (int i = 2; i < menu.ItemCount; i++) { + + char weaponTempIndexStr[32]; + char display[32]; + int a; + menu.GetItem(i, weaponTempIndexStr, sizeof(weaponTempIndexStr), a, display, sizeof(display)); + + int tempIndex = StringToInt(weaponTempIndexStr); + + if (tempIndex == index) + { + int skinId = GetSkinIdFromSkinMenuDisplay(display); + + g_iSkins[client][tempIndex] = skinId; + + char updateFields[256]; + char weaponName[32]; + RemoveWeaponPrefix(g_WeaponClasses[tempIndex], weaponName, sizeof(weaponName)); + Format(updateFields, sizeof(updateFields), "%s = %d", weaponName, skinId); + UpdatePlayerData(client, updateFields); + + RefreshWeapon(client, tempIndex); + + DataPack pack; + CreateDataTimer(0.5, SkinsMenuTimer, pack); + pack.WriteCell(menu); + pack.WriteCell(GetClientUserId(client)); + pack.WriteCell(GetMenuSelectionPosition()); + + return 0; + } + } + + // if didn't find + PrintToChat(client, " %s \x02%t", g_ChatPrefix, "SearchApplyCurrentFailed"); + + } + } + else { + /* + TODO: + this enable player to apply selected skin to the weapon in player hand + the weapon may not originally have this skin, + since PR #271 added a cvar "sm_weapons_enable_all_skins", + so its necessary to add a check after #271 is merged + */ + char weaponIndexStr[32]; + char display[32]; + int a; + menu.GetItem(selection, weaponIndexStr, sizeof(weaponIndexStr), a, display, sizeof(display)); + + int weaponEntity = GetEntPropEnt(client, Prop_Data, "m_hActiveWeapon", 0); + char weaponClass[32]; + if (weaponEntity != -1 && GetWeaponClass(weaponEntity, weaponClass, sizeof(weaponClass))) + { + int index; + g_smWeaponIndex.GetValue(weaponClass, index); + int skinId = GetSkinIdFromSkinMenuDisplay(display); + + g_iSkins[client][index] = skinId; + + char updateFields[256]; + char weaponName[32]; + RemoveWeaponPrefix(g_WeaponClasses[index], weaponName, sizeof(weaponName)); + Format(updateFields, sizeof(updateFields), "%s = %d", weaponName, skinId); + UpdatePlayerData(client, updateFields); + + RefreshWeapon(client, index); + } + } + + DataPack pack; + CreateDataTimer(0.5, SkinsMenuTimer, pack); + pack.WriteCell(menu); + pack.WriteCell(GetClientUserId(client)); + pack.WriteCell(GetMenuSelectionPosition()); + + return 0; + } + } + case MenuAction_DisplayItem: + { + if(IsClientInGame(client)) + { + char info[32]; + char display[32]; + int a; + menu.GetItem(selection, info, sizeof(info), a, display, sizeof(display)); + + if (StrEqual(info, "-1")) + { + Format(display, sizeof(display), "%T", "SearchApplyAll", client); + return RedrawMenuItem(display); + } + else if (StrEqual(info, "-2")) + { + Format(display, sizeof(display), "%T", "SearchApplyCurrent", client); + return RedrawMenuItem(display); + } + else + { + // translate weapon name + int skinId = GetSkinIdFromSkinMenuDisplay(display); + Format(display, sizeof(display), "%T", g_WeaponClasses[StringToInt(info)], client); + Format(display, sizeof(display), "%s (%d)", display, skinId); + return RedrawMenuItem(display); + } + } + } + case MenuAction_Cancel: + { + int menuTime; + if((menuTime = GetRemainingGracePeriodSeconds(client)) >= 0) + { + menuPlayerSearchTemp[client].Display(client, menuTime); + } + } + } + + return; +} + +public Action SkinsMenuTimer(Handle timer, DataPack pack) +{ + ResetPack(pack); + Menu menu = pack.ReadCell(); + int clientIndex = GetClientOfUserId(pack.ReadCell()); + int menuSelectionPosition = pack.ReadCell(); + + if(IsValidClient(clientIndex)) + { + int menuTime; + if((menuTime = GetRemainingGracePeriodSeconds(clientIndex)) >= 0) + { + menu.DisplayAt(clientIndex, menuSelectionPosition, menuTime); + } + } +} + +public int SearchMenuHandler(Menu menu, MenuAction menuaction, int client, int selection) +{ + switch (menuaction) + { + case MenuAction_Select: + { + if(IsClientInGame(client)) + { + char subMenuName[32]; + menu.GetItem(selection, subMenuName, sizeof(subMenuName)); + + Menu subMenu; + g_smSkinMenuMap[g_iClientLanguage[client]].GetValue(subMenuName, subMenu); + + int menuTime; + if((menuTime = GetRemainingGracePeriodSeconds(client)) >= 0) + { + subMenu.Display(client, menuTime); + } + } + } + } + +} \ No newline at end of file diff --git a/addons/sourcemod/translations/chi/weapons.phrases.txt b/addons/sourcemod/translations/chi/weapons.phrases.txt index d5b54f4..2fa8da2 100644 --- a/addons/sourcemod/translations/chi/weapons.phrases.txt +++ b/addons/sourcemod/translations/chi/weapons.phrases.txt @@ -56,6 +56,18 @@ { "chi" "名称标签已禁用" } + "SearchApplyAll" + { + "chi" "应用所有皮肤" + } + "SearchApplyCurrent" + { + "chi" "应用到当前武器" + } + "SearchApplyCurrentFailed" + { + "chi" "您的武器没有该皮肤。" + } "ChooseLanguage" { "chi" "请选择皮肤名称显示语言:" diff --git a/addons/sourcemod/translations/weapons.phrases.txt b/addons/sourcemod/translations/weapons.phrases.txt index db22bf9..fcf9b7d 100644 --- a/addons/sourcemod/translations/weapons.phrases.txt +++ b/addons/sourcemod/translations/weapons.phrases.txt @@ -68,6 +68,18 @@ { "en" "Use: !nametag " } + "SearchApplyAll" + { + "en" "Apply to all" + } + "SearchApplyCurrent" + { + "en" "Apply to current" + } + "SearchApplyCurrentFailed" + { + "en" "Your weapon doesn't have this skin." + } "ChooseLanguage" { "en" "Select language for skin names:"