├── WASDSharedAPI ├── .gitignore ├── WASDSharedAPI.dll ├── IWasdMenuOption.cs ├── WASDSharedAPI.csproj ├── IWasdMenu.cs └── IWasdMenuManager.cs ├── WASDMenuAPI ├── Classes │ ├── WasdMenuOption.cs │ ├── WasdMenu.cs │ ├── WasdManager.cs │ └── WasdMenuPlayer.cs ├── WASDMenuAPI.csproj ├── lang │ ├── en.json │ ├── ru.json │ └── pt-BR.json └── WASDMenuAPI.cs ├── README.md ├── .github └── FUNDING.yml ├── LICENSE └── WasdSharedAPITest └── WasdTest.cs /WASDSharedAPI/.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | /packages/ 4 | riderModule.iml 5 | /_ReSharper.Caches/ -------------------------------------------------------------------------------- /WASDSharedAPI/WASDSharedAPI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Interesting-exe/WASDMenuAPI/HEAD/WASDSharedAPI/WASDSharedAPI.dll -------------------------------------------------------------------------------- /WASDSharedAPI/IWasdMenuOption.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | 3 | namespace WASDSharedAPI; 4 | 5 | public interface IWasdMenuOption 6 | { 7 | public IWasdMenu? Parent { get; set; } 8 | public string? OptionDisplay { get; set; } 9 | public Action? OnChoose { get; set; } 10 | public int Index { get; set; } 11 | } -------------------------------------------------------------------------------- /WASDSharedAPI/WASDSharedAPI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /WASDMenuAPI/Classes/WasdMenuOption.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using WASDMenuAPI.Classes; 3 | using WASDSharedAPI; 4 | 5 | namespace WASDMenuAPI; 6 | 7 | public class WasdMenuOption : IWasdMenuOption 8 | { 9 | public IWasdMenu? Parent { get; set; } 10 | public string? OptionDisplay { get; set; } 11 | public Action? OnChoose { get; set; } 12 | public int Index { get; set; } 13 | } -------------------------------------------------------------------------------- /WASDSharedAPI/IWasdMenu.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | 3 | namespace WASDSharedAPI; 4 | 5 | public interface IWasdMenu 6 | { 7 | public string? Title { get; set; } 8 | public LinkedList? Options { get; set; } 9 | // previous option node 10 | public LinkedListNode? Prev { get; set; } 11 | public LinkedListNode Add(string display, Action onChoice); 12 | } -------------------------------------------------------------------------------- /WASDMenuAPI/WASDMenuAPI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ..\WASDSharedAPI\WASDSharedAPI.dll 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WASD Menu API 2 | API to easily create WASD menus. 3 | 4 | ![W8bIt](https://github.com/Interesting-exe/WASDSharedAPI/assets/52731127/6a3ffbee-0912-4815-88d1-d89f5ff05b37) 5 | 6 | ## Credits 7 | Idea and gifs from [oqyh's Kill-Sound-GoldKingZ](https://github.com/oqyh/cs2-Kill-Sound-GoldKingZ). 8 | 9 | ## TODO: 10 | - create proper docs for the API. 11 | - Take a look at [IWasdMenuManager.cs](https://github.com/Interesting-exe/WASDMenuAPI/blob/master/WASDSharedAPI/IWasdMenuManager.cs) and [WasdTest.cs](https://github.com/Interesting-exe/WASDMenuAPI/blob/master/WasdSharedAPITest/WasdTest.cs) for available methods and usage while the docs aren't available. 12 | -------------------------------------------------------------------------------- /WASDSharedAPI/IWasdMenuManager.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | 3 | namespace WASDSharedAPI; 4 | 5 | public interface IWasdMenuManager 6 | { 7 | //open the menu as main menu 8 | public void OpenMainMenu(CCSPlayerController? player, IWasdMenu? menu); 9 | //close all menus 10 | public void CloseMenu(CCSPlayerController? player); 11 | //close current submenu and go to previous menu/submenu 12 | public void CloseSubMenu(CCSPlayerController? player); 13 | //close all submenus and go to main menu 14 | public void CloseAllSubMenus(CCSPlayerController? player); 15 | //open menu as submenu 16 | public void OpenSubMenu(CCSPlayerController? player, IWasdMenu? menu); 17 | //create new menu object 18 | public IWasdMenu CreateMenu(string title = ""); 19 | } -------------------------------------------------------------------------------- /WASDMenuAPI/Classes/WasdMenu.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using WASDSharedAPI; 3 | 4 | namespace WASDMenuAPI.Classes; 5 | 6 | public class WasdMenu : IWasdMenu 7 | { 8 | public string Title { get; set; } = ""; 9 | public LinkedList? Options { get; set; } = new(); 10 | public LinkedListNode? Prev { get; set; } = null; 11 | public LinkedListNode Add(string display, Action onChoice) 12 | { 13 | if (Options == null) 14 | Options = new(); 15 | WasdMenuOption newOption = new WasdMenuOption 16 | { 17 | OptionDisplay = display, 18 | OnChoose = onChoice, 19 | Index = Options.Count, 20 | Parent = this 21 | }; 22 | return Options.AddLast(newOption); 23 | } 24 | } -------------------------------------------------------------------------------- /WASDMenuAPI/lang/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "menu.selection.left": "", 3 | "menu.selection.right": "", 4 | "menu.title.prefix": "", 5 | "menu.more.options.below": " ", 6 | "menu.bottom.text": " [ WASD - To Move ]
[ - To Exit ]" 7 | } -------------------------------------------------------------------------------- /WASDMenuAPI/lang/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "menu.selection.left": "", 3 | "menu.selection.right": "", 4 | "menu.title.prefix": "", 5 | "menu.more.options.below": " ", 6 | "menu.bottom.text": " [ WASD - To Move ]
[ - To Exit ]" 7 | } -------------------------------------------------------------------------------- /WASDMenuAPI/lang/pt-BR.json: -------------------------------------------------------------------------------- 1 | { 2 | "menu.selection.left": "", 3 | "menu.selection.right": "", 4 | "menu.title.prefix": "", 5 | "menu.more.options.below": " ", 6 | "menu.bottom.text": " [ WASD - Para Mover ]
[ - Para Sair ]" 7 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: Interesting-exe # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: Interesting # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Interesting-exe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /WASDMenuAPI/Classes/WasdManager.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using WASDSharedAPI; 3 | 4 | namespace WASDMenuAPI.Classes; 5 | 6 | public class WasdManager : IWasdMenuManager 7 | { 8 | public void OpenMainMenu(CCSPlayerController? player, IWasdMenu? menu) 9 | { 10 | if(player == null) 11 | return; 12 | WASDMenuAPI.Players[player.Slot].OpenMainMenu((WasdMenu?)menu); 13 | } 14 | 15 | public void CloseMenu(CCSPlayerController? player) 16 | { 17 | if(player == null) 18 | return; 19 | WASDMenuAPI.Players[player.Slot].OpenMainMenu(null); 20 | } 21 | 22 | public void CloseSubMenu(CCSPlayerController? player) 23 | { 24 | if(player == null) 25 | return; 26 | WASDMenuAPI.Players[player.Slot].CloseSubMenu(); 27 | } 28 | 29 | public void CloseAllSubMenus(CCSPlayerController? player) 30 | { 31 | if(player == null) 32 | return; 33 | WASDMenuAPI.Players[player.Slot].CloseAllSubMenus(); 34 | } 35 | 36 | public void OpenSubMenu(CCSPlayerController? player, IWasdMenu? menu) 37 | { 38 | if (player == null) 39 | return; 40 | WASDMenuAPI.Players[player.Slot].OpenSubMenu(menu); 41 | } 42 | 43 | public IWasdMenu CreateMenu(string title = "") 44 | { 45 | WasdMenu menu = new WasdMenu 46 | { 47 | Title = title 48 | }; 49 | return menu; 50 | } 51 | } -------------------------------------------------------------------------------- /WasdSharedAPITest/WasdTest.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API.Core; 2 | using CounterStrikeSharp.API.Core.Attributes.Registration; 3 | using CounterStrikeSharp.API.Core.Capabilities; 4 | using CounterStrikeSharp.API.Modules.Commands; 5 | using WASDSharedAPI; 6 | 7 | namespace WasdSharedAPITest; 8 | 9 | public class WasdTest : BasePlugin 10 | { 11 | public override string ModuleName => "WasdMenuTest"; 12 | public override string ModuleVersion => "1.0.0"; 13 | public override string ModuleAuthor => "Interesting"; 14 | 15 | public static IWasdMenuManager? MenuManager; 16 | 17 | public override void Load(bool hotReload) 18 | { 19 | 20 | } 21 | 22 | public IWasdMenuManager? GetMenuManager() 23 | { 24 | if (MenuManager == null) 25 | MenuManager = new PluginCapability("wasdmenu:manager").Get(); 26 | 27 | return MenuManager; 28 | } 29 | 30 | [ConsoleCommand("css_test_menu")] 31 | public void TestMenu(CCSPlayerController? player, CommandInfo? info) 32 | { 33 | var manager = GetMenuManager(); 34 | if(manager == null) 35 | return; 36 | IWasdMenu menu = manager.CreateMenu("Test menu"); 37 | menu.Add("Option 1", (p, option) => 38 | { 39 | p.PrintToChat("You chose option 1!"); 40 | manager.CloseMenu(p); 41 | }); 42 | menu.Add("Option 2", (p, option) => 43 | { 44 | p.PrintToChat("You chose option 2!"); 45 | manager.CloseMenu(p); 46 | }); 47 | IWasdMenu? subMenu = CreateTestSubMenu(); 48 | if (subMenu != null) 49 | { 50 | subMenu.Prev = menu.Add("Sub menu 1", (p, option) => 51 | { 52 | manager.OpenSubMenu(p, subMenu); 53 | }); 54 | } 55 | 56 | menu.Add("sub menu 2", (p, option) => 57 | { 58 | IWasdMenu? subMenu2 = CreateTestSubMenu(); 59 | if(subMenu2 == null) 60 | manager.CloseMenu(p); 61 | else 62 | { 63 | subMenu2.Prev = option.Parent?.Options?.Find(option); 64 | manager.OpenSubMenu(p, subMenu2); 65 | } 66 | }); 67 | 68 | manager.OpenMainMenu(player, menu); 69 | } 70 | 71 | public IWasdMenu? CreateTestSubMenu() 72 | { 73 | var manager = GetMenuManager(); 74 | if(manager == null) 75 | return null; 76 | IWasdMenu sub = manager.CreateMenu("sub menu"); 77 | sub.Add("sub option1", (player, option) => 78 | { 79 | player.PrintToChat("you chose sub option 1"); 80 | manager.CloseSubMenu(player); 81 | }); 82 | sub.Add("sub option2", (player, option) => 83 | { 84 | player.PrintToChat("you chose sub option 2"); 85 | manager.CloseSubMenu(player); 86 | }); 87 | return sub; 88 | } 89 | } -------------------------------------------------------------------------------- /WASDMenuAPI/WASDMenuAPI.cs: -------------------------------------------------------------------------------- 1 | using CounterStrikeSharp.API; 2 | using CounterStrikeSharp.API.Core; 3 | using CounterStrikeSharp.API.Core.Attributes.Registration; 4 | using CounterStrikeSharp.API.Core.Capabilities; 5 | using CounterStrikeSharp.API.Modules.Commands; 6 | using CounterStrikeSharp.API.Modules.Menu; 7 | using WASDMenuAPI.Classes; 8 | using WASDSharedAPI; 9 | 10 | namespace WASDMenuAPI; 11 | 12 | public class WASDMenuAPI : BasePlugin 13 | { 14 | public override string ModuleName => "WASDMenuAPI"; 15 | public override string ModuleVersion => "1.0.2"; 16 | public override string ModuleAuthor => "Interesting"; 17 | 18 | public static readonly Dictionary Players = new(); 19 | 20 | public static PluginCapability WasdMenuManagerCapability = 21 | new ("wasdmenu:manager"); 22 | 23 | public override void Load(bool hotReload) 24 | { 25 | var wasdMenuManager = new WasdManager(); 26 | WasdMenuPlayer.Localizer = Localizer; 27 | Capabilities.RegisterPluginCapability(WasdMenuManagerCapability, () => wasdMenuManager); 28 | RegisterEventHandler((@event, info) => 29 | { 30 | if (@event.Userid != null) 31 | Players[@event.Userid.Slot] = new WasdMenuPlayer 32 | { 33 | player = @event.Userid, 34 | Buttons = 0 35 | }; 36 | return HookResult.Continue; 37 | }); 38 | RegisterEventHandler((@event, info) => 39 | { 40 | if (@event.Userid != null) Players.Remove(@event.Userid.Slot); 41 | return HookResult.Continue; 42 | }); 43 | 44 | RegisterListener(OnTick); 45 | 46 | if(hotReload) 47 | foreach (var pl in Utilities.GetPlayers()) 48 | { 49 | Players[pl.Slot] = new WasdMenuPlayer 50 | { 51 | player = pl, 52 | Buttons = pl.Buttons 53 | }; 54 | } 55 | } 56 | 57 | public void OnTick() 58 | { 59 | foreach (var player in Players.Values.Where(p => p.MainMenu != null)) 60 | { 61 | if ((player.Buttons & PlayerButtons.Forward) == 0 && (player.player.Buttons & PlayerButtons.Forward) != 0) 62 | { 63 | player.ScrollUp(); 64 | } 65 | else if((player.Buttons & PlayerButtons.Back) == 0 && (player.player.Buttons & PlayerButtons.Back) != 0) 66 | { 67 | player.ScrollDown(); 68 | } 69 | else if((player.Buttons & PlayerButtons.Moveright) == 0 &&(player.player.Buttons & PlayerButtons.Moveright) != 0) 70 | { 71 | player.Choose(); 72 | } else if ((player.Buttons & PlayerButtons.Moveleft) == 0 && (player.player.Buttons & PlayerButtons.Moveleft) != 0) 73 | { 74 | player.CloseSubMenu(); 75 | } 76 | 77 | if (((long)player.player.Buttons & 8589934592) == 8589934592) 78 | { 79 | player.OpenMainMenu(null); 80 | } 81 | 82 | player.Buttons = player.player.Buttons; 83 | if(player.CenterHtml != "") 84 | Server.NextFrame(() => 85 | player.player.PrintToCenterHtml(player.CenterHtml) 86 | ); 87 | } 88 | } 89 | 90 | 91 | } 92 | -------------------------------------------------------------------------------- /WASDMenuAPI/Classes/WasdMenuPlayer.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System.Text.Json.Nodes; 3 | using CounterStrikeSharp.API; 4 | using CounterStrikeSharp.API.Core; 5 | using Microsoft.Extensions.Localization; 6 | using WASDMenuAPI.Classes; 7 | using WASDSharedAPI; 8 | 9 | namespace WASDMenuAPI; 10 | 11 | public class WasdMenuPlayer 12 | { 13 | public CCSPlayerController player { get; set; } 14 | public WasdMenu? MainMenu = null; 15 | public LinkedListNode? CurrentChoice = null; 16 | public LinkedListNode? MenuStart = null; 17 | public string CenterHtml = ""; 18 | public int VisibleOptions = 5; 19 | public static IStringLocalizer? Localizer = null; 20 | public PlayerButtons Buttons { get; set; } 21 | 22 | public void OpenMainMenu(WasdMenu? menu) 23 | { 24 | if (menu == null) 25 | { 26 | MainMenu = null; 27 | CurrentChoice = null; 28 | CenterHtml = ""; 29 | return; 30 | } 31 | MainMenu = menu; 32 | VisibleOptions = menu.Title != "" ? 4 : 5; 33 | CurrentChoice = MainMenu.Options?.First; 34 | MenuStart = CurrentChoice; 35 | UpdateCenterHtml(); 36 | } 37 | 38 | public void OpenSubMenu(IWasdMenu? menu) 39 | { 40 | if (menu == null) 41 | { 42 | CurrentChoice = MainMenu?.Options?.First; 43 | MenuStart = CurrentChoice; 44 | UpdateCenterHtml(); 45 | return; 46 | } 47 | 48 | VisibleOptions = menu.Title != "" ? 4 : 5; 49 | CurrentChoice = menu.Options?.First; 50 | MenuStart = CurrentChoice; 51 | UpdateCenterHtml(); 52 | } 53 | public void GoBackToPrev(LinkedListNode? menu) 54 | { 55 | if (menu == null) 56 | { 57 | CurrentChoice = MainMenu?.Options?.First; 58 | MenuStart = CurrentChoice; 59 | UpdateCenterHtml(); 60 | return; 61 | } 62 | 63 | VisibleOptions = menu.Value.Parent?.Title != "" ? 4 : 5; 64 | CurrentChoice = menu; 65 | if (CurrentChoice.Value.Index >= 5 ) 66 | { 67 | MenuStart = CurrentChoice; 68 | for (int i = 0; i < 4; i++) 69 | { 70 | MenuStart = MenuStart?.Previous; 71 | } 72 | } 73 | else 74 | MenuStart = CurrentChoice.List?.First; 75 | UpdateCenterHtml(); 76 | } 77 | 78 | public void CloseSubMenu() 79 | { 80 | if(CurrentChoice?.Value.Parent?.Prev == null) 81 | return; 82 | GoBackToPrev(CurrentChoice?.Value.Parent.Prev); 83 | } 84 | 85 | public void CloseAllSubMenus() 86 | { 87 | OpenSubMenu(null); 88 | } 89 | 90 | public void Choose() 91 | { 92 | CurrentChoice?.Value.OnChoose?.Invoke(player, CurrentChoice.Value); 93 | } 94 | 95 | public void ScrollDown() 96 | { 97 | if(CurrentChoice == null || MainMenu == null) 98 | return; 99 | CurrentChoice = CurrentChoice.Next ?? CurrentChoice.List?.First; 100 | MenuStart = CurrentChoice!.Value.Index >= VisibleOptions ? MenuStart!.Next : CurrentChoice.List?.First; 101 | UpdateCenterHtml(); 102 | } 103 | 104 | public void ScrollUp() 105 | { 106 | if(CurrentChoice == null || MainMenu == null) 107 | return; 108 | CurrentChoice = CurrentChoice.Previous ?? CurrentChoice.List?.Last; 109 | if (CurrentChoice == CurrentChoice?.List?.Last && CurrentChoice?.Value.Index >= VisibleOptions) 110 | { 111 | MenuStart = CurrentChoice; 112 | for (int i = 0; i < VisibleOptions-1; i++) 113 | MenuStart = MenuStart?.Previous; 114 | } 115 | else 116 | MenuStart = CurrentChoice!.Value.Index >= VisibleOptions ? MenuStart!.Previous : CurrentChoice.List?.First; 117 | UpdateCenterHtml(); 118 | } 119 | 120 | private void UpdateCenterHtml() 121 | { 122 | if (CurrentChoice == null || MainMenu == null) 123 | return; 124 | 125 | StringBuilder builder = new StringBuilder(); 126 | int i = 0; 127 | LinkedListNode? option = MenuStart!; 128 | if (option.Value.Parent?.Title != "") 129 | { 130 | builder.AppendLine($"{Localizer?["menu.title.prefix"]}{option.Value.Parent?.Title}
"); 131 | } 132 | 133 | while (i < VisibleOptions && option != null ) 134 | { 135 | if (option == CurrentChoice) 136 | builder.AppendLine($"{Localizer?["menu.selection.left"]} {option.Value.OptionDisplay} {Localizer?["menu.selection.right"]}
"); 137 | else 138 | builder.AppendLine($"{option.Value.OptionDisplay}
"); 139 | option = option.Next; 140 | i++; 141 | } 142 | 143 | if (option != null) { // more options 144 | builder.AppendLine( 145 | $"{Localizer?["menu.more.options.below"]}"); 146 | } 147 | 148 | builder.AppendLine("
" + 149 | $"{Localizer?["menu.bottom.text"]}
"); 150 | builder.AppendLine(""); 151 | CenterHtml = builder.ToString(); 152 | } 153 | } --------------------------------------------------------------------------------