├── .gitattributes ├── .gitignore ├── MenuAPI.sln ├── MenuAPI ├── Menu.cs ├── MenuAPI.csproj ├── MenuAPI.nuspec ├── MenuController.cs └── items │ ├── MenuCheckboxItem.cs │ ├── MenuDynamicListItem.cs │ ├── MenuItem.cs │ ├── MenuListItem.cs │ └── MenuSliderItem.cs ├── README.md ├── TestMenu ├── ExampleMenu.cs └── TestMenu.csproj ├── appveyor.yml ├── appveyor ├── after_deploy.cmd ├── on_failure.cmd └── on_success.cmd ├── dependencies └── RedM │ ├── CitizenFX.Core.dll │ └── CitizenFX.Core.xml └── shared └── RedM └── Controls.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | dependencies/MenuAPI\.net\.dll 263 | 264 | postbuild\.cmd 265 | 266 | postbuild-fivem.cmd 267 | 268 | postbuild-redm.cmd 269 | -------------------------------------------------------------------------------- /MenuAPI.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29319.158 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MenuAPI", "MenuAPI\MenuAPI.csproj", "{9C746601-9F3D-4B0D-877C-1C7BC493C5BC}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestMenu", "TestMenu\TestMenu.csproj", "{A91669DA-E3C1-4DC3-A3A6-97476B17214A}" 9 | ProjectSection(ProjectDependencies) = postProject 10 | {9C746601-9F3D-4B0D-877C-1C7BC493C5BC} = {9C746601-9F3D-4B0D-877C-1C7BC493C5BC} 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug FiveM|Any CPU = Debug FiveM|Any CPU 16 | Debug RedM|Any CPU = Debug RedM|Any CPU 17 | Release FiveM|Any CPU = Release FiveM|Any CPU 18 | Release RedM|Any CPU = Release RedM|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {9C746601-9F3D-4B0D-877C-1C7BC493C5BC}.Debug FiveM|Any CPU.ActiveCfg = Debug FiveM|Any CPU 22 | {9C746601-9F3D-4B0D-877C-1C7BC493C5BC}.Debug FiveM|Any CPU.Build.0 = Debug FiveM|Any CPU 23 | {9C746601-9F3D-4B0D-877C-1C7BC493C5BC}.Debug RedM|Any CPU.ActiveCfg = Debug RedM|Any CPU 24 | {9C746601-9F3D-4B0D-877C-1C7BC493C5BC}.Debug RedM|Any CPU.Build.0 = Debug RedM|Any CPU 25 | {9C746601-9F3D-4B0D-877C-1C7BC493C5BC}.Release FiveM|Any CPU.ActiveCfg = Release FiveM|Any CPU 26 | {9C746601-9F3D-4B0D-877C-1C7BC493C5BC}.Release FiveM|Any CPU.Build.0 = Release FiveM|Any CPU 27 | {9C746601-9F3D-4B0D-877C-1C7BC493C5BC}.Release RedM|Any CPU.ActiveCfg = Release RedM|Any CPU 28 | {9C746601-9F3D-4B0D-877C-1C7BC493C5BC}.Release RedM|Any CPU.Build.0 = Release RedM|Any CPU 29 | {A91669DA-E3C1-4DC3-A3A6-97476B17214A}.Debug FiveM|Any CPU.ActiveCfg = Debug FiveM|Any CPU 30 | {A91669DA-E3C1-4DC3-A3A6-97476B17214A}.Debug FiveM|Any CPU.Build.0 = Debug FiveM|Any CPU 31 | {A91669DA-E3C1-4DC3-A3A6-97476B17214A}.Debug RedM|Any CPU.ActiveCfg = Debug RedM|Any CPU 32 | {A91669DA-E3C1-4DC3-A3A6-97476B17214A}.Debug RedM|Any CPU.Build.0 = Debug RedM|Any CPU 33 | {A91669DA-E3C1-4DC3-A3A6-97476B17214A}.Release FiveM|Any CPU.ActiveCfg = Release FiveM|Any CPU 34 | {A91669DA-E3C1-4DC3-A3A6-97476B17214A}.Release FiveM|Any CPU.Build.0 = Release FiveM|Any CPU 35 | {A91669DA-E3C1-4DC3-A3A6-97476B17214A}.Release RedM|Any CPU.ActiveCfg = Release RedM|Any CPU 36 | {A91669DA-E3C1-4DC3-A3A6-97476B17214A}.Release RedM|Any CPU.Build.0 = Release RedM|Any CPU 37 | EndGlobalSection 38 | GlobalSection(SolutionProperties) = preSolution 39 | HideSolutionNode = FALSE 40 | EndGlobalSection 41 | GlobalSection(ExtensibilityGlobals) = postSolution 42 | SolutionGuid = {EE5A945A-4A5A-42DE-89AB-7AE2C7645EE3} 43 | EndGlobalSection 44 | EndGlobal 45 | -------------------------------------------------------------------------------- /MenuAPI/MenuAPI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net452 4 | embedded 5 | MenuAPI 6 | Release RedM;Release FiveM;Debug RedM;Debug FiveM 7 | 8 | MenuAPI FiveM 9 | MenuAPI RedM 10 | false 11 | Tom Grobbe 12 | An advanced Menu API for FiveM C# resources. 13 | An advanced Menu API for RedM C# resources. 14 | Copyright Tom Grobbe 2018-2020 15 | 16 | https://github.com/tomgrobbe/menuapi/ 17 | https://github.com/tomgrobbe/menuapi/ 18 | FiveM MenuAPI 19 | RedM MenuAPI 20 | license.txt 21 | 22 | 23 | 24 | FIVEM 25 | 26 | 27 | 28 | FIVEM;DEBUG 29 | 30 | 31 | 32 | REDM 33 | 34 | 35 | 36 | REDM;DEBUG 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | runtime 48 | 49 | 50 | 51 | 52 | ..\dependencies\RedM\CitizenFX.Core.dll 53 | false 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /MenuAPI/MenuAPI.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $id$ 5 | $title$ 6 | 1.0.0 7 | Tom Grobbe 8 | Tom Grobbe 9 | https://github.com/tomgrobbe/menuapi 10 | $description$ 11 | Copyright Tom Grobbe 2018-2020. 12 | $title$ 13 | 14 | 15 | -------------------------------------------------------------------------------- /MenuAPI/MenuController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using CitizenFX.Core; 6 | using static CitizenFX.Core.Native.API; 7 | 8 | namespace MenuAPI 9 | { 10 | public class MenuController : BaseScript 11 | { 12 | public static List Menus { get; protected set; } = new List(); 13 | internal static HashSet VisibleMenus { get; } = new HashSet(); 14 | #if FIVEM 15 | public const string _texture_dict = "commonmenu"; 16 | public const string _header_texture = "interaction_bgd"; 17 | #endif 18 | #if REDM 19 | public const string _texture_dict = "menu_textures"; 20 | public const string _header_texture = "translate_bg_1a"; 21 | #endif 22 | private static List menuTextureAssets = new List() 23 | { 24 | #if FIVEM 25 | "commonmenu", 26 | "commonmenutu", 27 | "mpleaderboard", 28 | "mphud", 29 | "mpshopsale", 30 | "mpinventory", 31 | "mprankbadge", 32 | "mpcarhud", 33 | "mpcarhud2", 34 | "shared" 35 | #endif 36 | #if REDM 37 | "menu_textures", 38 | "boot_flow", 39 | "generic_textures", 40 | #endif 41 | }; 42 | 43 | #if FIVEM 44 | private static float AspectRatio => GetScreenAspectRatio(false); 45 | #endif 46 | #if REDM 47 | private static float AspectRatio => 16 / 9; 48 | #endif 49 | public static float ScreenWidth => 1080 * AspectRatio; 50 | public static float ScreenHeight => 1080; 51 | public static bool DisableMenuButtons { get; set; } = false; 52 | #if FIVEM 53 | public static bool AreMenuButtonsEnabled => IsAnyMenuOpen() && !Game.IsPaused && CitizenFX.Core.UI.Screen.Fading.IsFadedIn && !IsPlayerSwitchInProgress() && !DisableMenuButtons && !Game.Player.IsDead; 54 | #endif 55 | #if REDM 56 | public static bool AreMenuButtonsEnabled => 57 | IsAnyMenuOpen() && 58 | IsScreenFadedIn() && 59 | !IsPauseMenuActive() && 60 | !DisableMenuButtons && 61 | !IsEntityDead(PlayerPedId()); 62 | #endif 63 | 64 | public static bool NavigateMenuUsingArrows { get; set; } = true; 65 | public static bool EnableManualGCs { get; set; } = true; 66 | public static bool DontOpenAnyMenu { get; set; } = false; 67 | public static bool PreventExitingMenu { get; set; } = false; 68 | public static bool DisableBackButton { get; set; } = false; 69 | public static bool SetDrawOrder { get; set; } = true; 70 | public static Control MenuToggleKey { get; set; } 71 | #if FIVEM 72 | = Control.InteractionMenu; 73 | #endif 74 | #if REDM 75 | = Control.PlayerMenu; 76 | #endif 77 | 78 | public static bool EnableMenuToggleKeyOnController { get; set; } = true; 79 | 80 | internal static Dictionary MenuButtons { get; private set; } = new Dictionary(); 81 | 82 | public static Menu MainMenu { get; set; } = null; 83 | 84 | #if FIVEM 85 | internal static int _scale = RequestScaleformMovie("INSTRUCTIONAL_BUTTONS"); 86 | #endif 87 | 88 | private static int ManualTimerForGC = GetGameTimer(); 89 | 90 | #if FIVEM 91 | private static MenuAlignmentOption _alignment = MenuAlignmentOption.Left; 92 | public static MenuAlignmentOption MenuAlignment 93 | { 94 | get 95 | { 96 | return _alignment; 97 | } 98 | set 99 | { 100 | if (AspectRatio < 1.888888888888889f) 101 | { 102 | // alignment can be whatever the resource wants it to be because this aspect ratio is supported. 103 | _alignment = value; 104 | } 105 | // right aligned menus are not supported for aspect ratios 17:9 or 21:9. 106 | else 107 | { 108 | // no matter what the new value would've been, the aspect ratio does not support right aligned menus, 109 | // so (re)set it to be left aligned. 110 | _alignment = MenuAlignmentOption.Left; 111 | 112 | // In case the value was being changed to be right aligned, notify the user properly. 113 | if (value == MenuAlignmentOption.Right) 114 | Debug.WriteLine($"[MenuAPI ({GetCurrentResourceName()})] Warning: Right aligned menus are not supported for aspect ratios 17:9 or 21:9, left aligned will be used instead."); 115 | } 116 | } 117 | } 118 | 119 | public enum MenuAlignmentOption 120 | { 121 | Left, 122 | Right 123 | } 124 | #endif 125 | 126 | /// 127 | /// Constructor 128 | /// 129 | public MenuController() 130 | { 131 | Tick += ProcessMenus; 132 | #if FIVEM 133 | Tick += DrawInstructionalButtons; 134 | #endif 135 | Tick += ProcessMainButtons; 136 | Tick += ProcessDirectionalButtons; 137 | Tick += ProcessToggleMenuButton; 138 | Tick += MenuButtonsDisableChecks; 139 | } 140 | 141 | /// 142 | /// This binds the menu to the and sets the menu's parent to . 143 | /// 144 | /// 145 | /// 146 | /// 147 | public static void BindMenuItem(Menu parentMenu, Menu childMenu, MenuItem menuItem) 148 | { 149 | AddSubmenu(parentMenu, childMenu); 150 | if (MenuButtons.ContainsKey(menuItem)) 151 | { 152 | MenuButtons[menuItem] = childMenu; 153 | } 154 | else 155 | { 156 | MenuButtons.Add(menuItem, childMenu); 157 | } 158 | } 159 | 160 | /// 161 | /// This adds the to the list. 162 | /// 163 | /// 164 | public static void AddMenu(Menu menu) 165 | { 166 | if (!Menus.Contains(menu)) 167 | { 168 | Menus.Add(menu); 169 | // automatically set the first menu as the main menu if none is set yet, this can be changed at any time though. 170 | if (MainMenu == null) 171 | { 172 | MainMenu = menu; 173 | } 174 | } 175 | } 176 | 177 | /// 178 | /// Adds the to the menus list and sets the menu's parent to . 179 | /// 180 | /// 181 | /// 182 | public static void AddSubmenu(Menu parent, Menu child) 183 | { 184 | if (!Menus.Contains(child)) 185 | AddMenu(child); 186 | child.ParentMenu = parent; 187 | } 188 | 189 | /// 190 | /// Loads the texture dict for the common menu sprites. 191 | /// 192 | /// 193 | private static async Task LoadAssets() 194 | { 195 | menuTextureAssets.ForEach(asset => 196 | { 197 | if (!HasStreamedTextureDictLoaded(asset)) 198 | { 199 | RequestStreamedTextureDict(asset, false); 200 | } 201 | }); 202 | while (menuTextureAssets.Any(asset => { return !HasStreamedTextureDictLoaded(asset); })) 203 | { 204 | await Delay(0); 205 | } 206 | } 207 | 208 | /// 209 | /// Unloads the texture dict for the common menu sprites. 210 | /// 211 | private static void UnloadAssets() 212 | { 213 | menuTextureAssets.ForEach(asset => 214 | { 215 | if (!string.IsNullOrEmpty(asset)) 216 | { 217 | if (HasStreamedTextureDictLoaded(asset)) 218 | { 219 | SetStreamedTextureDictAsNoLongerNeeded(asset); 220 | } 221 | } 222 | }); 223 | } 224 | 225 | /// 226 | /// Returns the currently opened menu. 227 | /// 228 | /// 229 | public static Menu GetCurrentMenu() 230 | { 231 | if (IsAnyMenuOpen()) 232 | { 233 | return VisibleMenus.FirstOrDefault(); 234 | } 235 | return null; 236 | } 237 | 238 | /// 239 | /// Returns true if any menu is currently open. 240 | /// 241 | /// 242 | public static bool IsAnyMenuOpen() => VisibleMenus.Any(); 243 | 244 | 245 | #region Process Menu Buttons 246 | /// 247 | /// Process the select & go back/cancel buttons. 248 | /// 249 | /// 250 | private async Task ProcessMainButtons() 251 | { 252 | if (!IsAnyMenuOpen()) 253 | { 254 | return; 255 | } 256 | if (IsPauseMenuActive()) 257 | { 258 | return; 259 | } 260 | var currentMenu = GetCurrentMenu(); 261 | if (currentMenu == null || DontOpenAnyMenu) 262 | { 263 | return; 264 | } 265 | #if FIVEM 266 | Game.DisableControlThisFrame(0, Control.MultiplayerInfo); 267 | #endif 268 | HandlePreventExit(); 269 | if (!currentMenu.Visible || !AreMenuButtonsEnabled) 270 | { 271 | return; 272 | } 273 | await HandleMainNavigationButtons(currentMenu); 274 | } 275 | 276 | private async Task HandleMainNavigationButtons(Menu currentMenu) 277 | { 278 | // Select / Enter 279 | if ( 280 | #if FIVEM 281 | Game.IsDisabledControlJustReleased(0, Control.FrontendAccept) || 282 | Game.IsControlJustReleased(0, Control.FrontendAccept) || 283 | Game.IsDisabledControlJustReleased(0, Control.VehicleMouseControlOverride) || 284 | Game.IsControlJustReleased(0, Control.VehicleMouseControlOverride) 285 | #endif 286 | #if REDM 287 | IsDisabledControlJustReleased(0, (uint)Control.FrontendAccept) || 288 | IsControlJustReleased(0, (uint)Control.FrontendAccept) 289 | #endif 290 | ) 291 | { 292 | if (currentMenu.Size > 0) 293 | { 294 | currentMenu.SelectItem(currentMenu.CurrentIndex); 295 | } 296 | } 297 | // Cancel / Go Back 298 | else if ( 299 | !DisableBackButton && 300 | #if FIVEM 301 | Game.IsDisabledControlJustReleased(0, Control.PhoneCancel) 302 | #endif 303 | #if REDM 304 | IsDisabledControlJustReleased(0, (uint)Control.FrontendCancel) 305 | #endif 306 | ) 307 | { 308 | // Wait for the next frame to make sure the "cinematic camera" button doesn't get "re-enabled" before the menu gets closed. 309 | await Delay(0); 310 | currentMenu.GoBack(); 311 | } 312 | else if ( 313 | PreventExitingMenu && !DisableBackButton && 314 | #if FIVEM 315 | Game.IsDisabledControlJustReleased(0, Control.PhoneCancel) 316 | #endif 317 | #if REDM 318 | IsDisabledControlJustReleased(0, (uint)Control.CellphoneCancel) 319 | #endif 320 | ) 321 | { 322 | // if there's a parent menu, allow going back to that, but don't allow a 'top-level' menu to be closed. 323 | if (currentMenu.ParentMenu != null) 324 | { 325 | currentMenu.GoBack(); 326 | } 327 | await Delay(0); 328 | } 329 | } 330 | 331 | private void HandlePreventExit() 332 | { 333 | if (PreventExitingMenu) 334 | { 335 | #if FIVEM 336 | Game.DisableControlThisFrame(0, Control.FrontendPause); 337 | Game.DisableControlThisFrame(0, Control.FrontendPauseAlternate); 338 | #endif 339 | #if REDM 340 | DisableControlAction(0, (uint)Control.FrontendPause, true); 341 | DisableControlAction(0, (uint)Control.FrontendPauseAlternate, true); 342 | #endif 343 | } 344 | } 345 | 346 | /// 347 | /// Returns true when one of the 'up' controls is currently pressed, only if the button can be active according to some conditions. 348 | /// 349 | /// 350 | private bool IsUpPressed() 351 | { 352 | if (!AreMenuButtonsEnabled) 353 | { 354 | return false; 355 | } 356 | #if FIVEM 357 | // when the player is holding TAB, while not in a vehicle, and when the scrollwheel is being used, return false to prevent interferring with weapon selection. 358 | if (!Game.PlayerPed.IsInVehicle()) 359 | { 360 | if (Game.IsControlPressed(0, Control.SelectWeapon)) 361 | { 362 | if (Game.IsControlPressed(0, Control.SelectNextWeapon) || Game.IsControlPressed(0, Control.SelectPrevWeapon)) 363 | { 364 | return false; 365 | } 366 | } 367 | } 368 | 369 | // return true if the scrollwheel up or the arrow up key is being used at this frame. 370 | if (Game.IsControlPressed(0, Control.FrontendUp) || 371 | Game.IsDisabledControlPressed(0, Control.FrontendUp) || 372 | Game.IsControlPressed(0, Control.PhoneScrollBackward) || 373 | Game.IsDisabledControlPressed(0, Control.PhoneScrollBackward)) 374 | { 375 | return true; 376 | } 377 | #endif 378 | #if REDM 379 | if ( 380 | IsControlPressed(0, (uint)Control.FrontendUp) || 381 | IsDisabledControlPressed(0, (uint)Control.FrontendUp) || 382 | IsControlPressed(0, (uint)Control.CellphoneScrollBackward) || 383 | IsDisabledControlPressed(0, (uint)Control.CellphoneScrollBackward) 384 | ) 385 | { 386 | return true; 387 | } 388 | #endif 389 | return false; 390 | } 391 | 392 | /// 393 | /// Returns true when one of the 'down' controls is currently pressed, only if the button can be active according to some conditions. 394 | /// 395 | /// 396 | private bool IsDownPressed() 397 | { 398 | if (!AreMenuButtonsEnabled) 399 | { 400 | return false; 401 | } 402 | #if FIVEM 403 | // when the player is holding TAB, while not in a vehicle, and when the scrollwheel is being used, return false to prevent interferring with weapon selection. 404 | if (!Game.PlayerPed.IsInVehicle()) 405 | { 406 | if (Game.IsControlPressed(0, Control.SelectWeapon)) 407 | { 408 | if (Game.IsControlPressed(0, Control.SelectNextWeapon) || Game.IsControlPressed(0, Control.SelectPrevWeapon)) 409 | { 410 | return false; 411 | } 412 | } 413 | } 414 | 415 | // return true if the scrollwheel down or the arrow down key is being used at this frame. 416 | if (Game.IsControlPressed(0, Control.FrontendDown) || 417 | Game.IsDisabledControlPressed(0, Control.FrontendDown) || 418 | Game.IsControlPressed(0, Control.PhoneScrollForward) || 419 | Game.IsDisabledControlPressed(0, Control.PhoneScrollForward)) 420 | { 421 | return true; 422 | } 423 | #endif 424 | #if REDM 425 | if ( 426 | IsControlPressed(0, (uint)Control.FrontendDown) || 427 | IsDisabledControlPressed(0, (uint)Control.FrontendDown) || 428 | IsControlPressed(0, (uint)Control.CellphoneScrollForward) || 429 | IsDisabledControlPressed(0, (uint)Control.CellphoneScrollForward) 430 | ) 431 | { 432 | return true; 433 | } 434 | #endif 435 | return false; 436 | } 437 | 438 | /// 439 | /// Processes the menu toggle button to check if the menu should open or close. 440 | /// 441 | /// 442 | private async Task ProcessToggleMenuButton() 443 | { 444 | #if FIVEM 445 | await ProcessToggleMenuButtonFiveM(); 446 | #endif 447 | #if REDM 448 | ProcessToggleMenuButtonRedM(); 449 | await Task.FromResult(0); 450 | #endif 451 | } 452 | #if REDM 453 | private void ProcessToggleMenuButtonRedM() 454 | { 455 | DisableControlAction(0, (uint)MenuToggleKey, true); 456 | if ( 457 | !IsPauseMenuActive() && 458 | IsScreenFadedIn() && 459 | !IsAnyMenuOpen() && 460 | !DisableMenuButtons && 461 | !IsEntityDead(PlayerPedId()) && 462 | IsDisabledControlJustReleased(0, (uint)MenuToggleKey) 463 | ) 464 | { 465 | if (MainMenu != null) 466 | { 467 | MainMenu.OpenMenu(); 468 | } 469 | else 470 | { 471 | Debug.WriteLine($"[ERROR] [{GetCurrentResourceName()}] [MenuAPI] MainMenu is null, so we can't open it! Make sure that MenuController.MainMenu is set to a valid Menu which is not null!"); 472 | } 473 | } 474 | } 475 | #endif 476 | #if FIVEM 477 | private async Task ProcessToggleMenuButtonFiveM() 478 | { 479 | if (!Game.IsPaused && !IsPauseMenuRestarting() && IsScreenFadedIn() && !IsPlayerSwitchInProgress() && !Game.Player.IsDead && !DisableMenuButtons) 480 | { 481 | if (IsAnyMenuOpen()) 482 | { 483 | DisableMenuKeyThisFrame(); 484 | } 485 | else 486 | { 487 | if (Game.CurrentInputMode == InputMode.GamePad) 488 | { 489 | if (!EnableMenuToggleKeyOnController) 490 | return; 491 | 492 | await HandleMenuToggleKeyForController(); 493 | } 494 | else 495 | { 496 | HandleMenuToggleKeyForKeyboard(); 497 | } 498 | } 499 | } 500 | } 501 | #endif 502 | 503 | /// 504 | /// Process left/right/up/down buttons (also holding down buttons will speed up after 3 iterations) 505 | /// 506 | /// 507 | private async Task ProcessDirectionalButtons() 508 | { 509 | // Return if the buttons are not currently enabled. 510 | if (!AreMenuButtonsEnabled) 511 | { 512 | return; 513 | } 514 | 515 | // Get the currently open menu. 516 | var currentMenu = GetCurrentMenu(); 517 | // If it exists. 518 | if (currentMenu == null || DontOpenAnyMenu || currentMenu.Size < 1 || !currentMenu.Visible) 519 | { 520 | return; 521 | } 522 | if (IsUpPressed()) 523 | { 524 | await HandleUpNavigation(currentMenu); 525 | } 526 | else if (IsDownPressed()) 527 | { 528 | await HandleDownNavigation(currentMenu); 529 | } 530 | 531 | // Check if the Go Left controls are pressed. 532 | else if ( 533 | AreMenuButtonsEnabled && ( 534 | #if FIVEM 535 | Game.IsDisabledControlJustPressed(0, Control.PhoneLeft) || 536 | Game.IsControlJustPressed(0, Control.PhoneLeft) 537 | #endif 538 | #if REDM 539 | IsDisabledControlJustPressed(0, (uint)Control.FrontendLeft) || 540 | IsControlJustPressed(0, (uint)Control.FrontendLeft) 541 | #endif 542 | ) 543 | ) 544 | { 545 | await HandleLeftNavigation(currentMenu); 546 | } 547 | 548 | // Check if the Go Right controls are pressed. 549 | else if ( 550 | AreMenuButtonsEnabled && ( 551 | #if FIVEM 552 | Game.IsDisabledControlJustPressed(0, Control.PhoneRight) || 553 | Game.IsControlJustPressed(0, Control.PhoneRight) 554 | #endif 555 | #if REDM 556 | IsDisabledControlJustPressed(0, (uint)Control.FrontendRight) || 557 | IsControlJustPressed(0, (uint)Control.FrontendRight) 558 | #endif 559 | ) 560 | ) 561 | { 562 | await HandleRightNavigation(currentMenu); 563 | } 564 | } 565 | 566 | private async Task HandleRightNavigation(Menu currentMenu) 567 | { 568 | var item = currentMenu.GetMenuItems()[currentMenu.CurrentIndex]; 569 | if (item.Enabled) 570 | { 571 | currentMenu.GoRight(); 572 | var time = GetGameTimer(); 573 | var times = 0; 574 | var delay = 200; 575 | #if FIVEM 576 | while ((Game.IsDisabledControlPressed(0, Control.PhoneRight) || Game.IsControlPressed(0, Control.PhoneRight)) && GetCurrentMenu() != null && AreMenuButtonsEnabled) 577 | #endif 578 | #if REDM 579 | while ( 580 | GetCurrentMenu() != null && 581 | AreMenuButtonsEnabled && ( 582 | IsDisabledControlPressed(0, (uint)Control.FrontendRight) || 583 | IsControlPressed(0, (uint)Control.FrontendRight) 584 | ) 585 | ) 586 | #endif 587 | { 588 | currentMenu = GetCurrentMenu(); 589 | if (GetGameTimer() - time > delay) 590 | { 591 | times++; 592 | if (times > 2) 593 | { 594 | delay = 150; 595 | } 596 | if (times > 5) 597 | { 598 | delay = 100; 599 | } 600 | if (times > 25) 601 | { 602 | delay = 50; 603 | } 604 | if (times > 60) 605 | { 606 | delay = 25; 607 | } 608 | currentMenu.GoRight(); 609 | time = GetGameTimer(); 610 | } 611 | await Delay(0); 612 | } 613 | } 614 | } 615 | 616 | private async Task HandleLeftNavigation(Menu currentMenu) 617 | { 618 | if (currentMenu.GetCurrentMenuItem() is MenuItem item && item.Enabled) 619 | { 620 | currentMenu.GoLeft(); 621 | var time = GetGameTimer(); 622 | var times = 0; 623 | var delay = 200; 624 | while ( 625 | GetCurrentMenu() != null && 626 | AreMenuButtonsEnabled && ( 627 | #if FIVEM 628 | Game.IsDisabledControlPressed(0, Control.PhoneLeft) || 629 | Game.IsControlPressed(0, Control.PhoneLeft) 630 | #endif 631 | #if REDM 632 | IsDisabledControlPressed(0, (uint)Control.FrontendLeft) || 633 | IsControlPressed(0, (uint)Control.FrontendLeft) 634 | #endif 635 | ) 636 | ) 637 | { 638 | currentMenu = GetCurrentMenu(); 639 | if (GetGameTimer() - time > delay) 640 | { 641 | times++; 642 | if (times > 2) 643 | { 644 | delay = 150; 645 | } 646 | if (times > 5) 647 | { 648 | delay = 100; 649 | } 650 | if (times > 25) 651 | { 652 | delay = 50; 653 | } 654 | if (times > 60) 655 | { 656 | delay = 25; 657 | } 658 | currentMenu.GoLeft(); 659 | time = GetGameTimer(); 660 | } 661 | await Delay(0); 662 | } 663 | } 664 | } 665 | 666 | private async Task HandleDownNavigation(Menu currentMenu) 667 | { 668 | currentMenu.GoDown(); 669 | 670 | var time = GetGameTimer(); 671 | var times = 0; 672 | var delay = 200; 673 | while (IsDownPressed() && GetCurrentMenu() != null) 674 | { 675 | currentMenu = GetCurrentMenu(); 676 | if (GetGameTimer() - time > delay) 677 | { 678 | times++; 679 | if (times > 2) 680 | { 681 | delay = 150; 682 | } 683 | if (times > 5) 684 | { 685 | delay = 100; 686 | } 687 | if (times > 25) 688 | { 689 | delay = 50; 690 | } 691 | if (times > 60) 692 | { 693 | delay = 25; 694 | } 695 | 696 | currentMenu.GoDown(); 697 | 698 | time = GetGameTimer(); 699 | } 700 | await Delay(0); 701 | } 702 | } 703 | 704 | #if FIVEM 705 | private void HandleMenuToggleKeyForKeyboard() 706 | { 707 | if ( 708 | (Game.IsControlJustPressed(0, MenuToggleKey) || Game.IsDisabledControlJustPressed(0, MenuToggleKey)) && 709 | !Game.IsPaused && 710 | !Game.Player.IsDead && 711 | !IsPlayerSwitchInProgress() && 712 | !DontOpenAnyMenu && 713 | IsScreenFadedIn() 714 | ) 715 | { 716 | if (!Menus.Any()) 717 | { 718 | return; 719 | } 720 | if (MainMenu != null) 721 | { 722 | MainMenu.OpenMenu(); 723 | } 724 | else 725 | { 726 | Menus.First().OpenMenu(); 727 | } 728 | } 729 | } 730 | 731 | private async Task HandleMenuToggleKeyForController() 732 | { 733 | int tmpTimer = GetGameTimer(); 734 | while ((Game.IsControlPressed(0, Control.InteractionMenu) || Game.IsDisabledControlPressed(0, Control.InteractionMenu)) && !Game.IsPaused && IsScreenFadedIn() && !Game.Player.IsDead && !IsPlayerSwitchInProgress() && !DontOpenAnyMenu) 735 | { 736 | if (GetGameTimer() - tmpTimer > 400) 737 | { 738 | if (MainMenu != null) 739 | { 740 | MainMenu.OpenMenu(); 741 | } 742 | else 743 | { 744 | if (Menus.Count > 0) 745 | { 746 | Menus[0].OpenMenu(); 747 | } 748 | } 749 | break; 750 | } 751 | await Delay(0); 752 | } 753 | } 754 | 755 | private void DisableMenuKeyThisFrame() 756 | { 757 | Game.DisableControlThisFrame(0, MenuToggleKey); 758 | if (Game.CurrentInputMode == InputMode.MouseAndKeyboard) 759 | { 760 | if ((Game.IsControlJustPressed(0, MenuToggleKey) || Game.IsDisabledControlJustPressed(0, MenuToggleKey)) && !PreventExitingMenu) 761 | { 762 | var menu = GetCurrentMenu(); 763 | if (menu != null) 764 | { 765 | menu.CloseMenu(); 766 | } 767 | } 768 | } 769 | } 770 | #endif 771 | 772 | private async Task HandleUpNavigation(Menu currentMenu) 773 | { 774 | // Update the currently selected item to the new one. 775 | currentMenu.GoUp(); 776 | 777 | // Get the current game time. 778 | var time = GetGameTimer(); 779 | var times = 0; 780 | var delay = 200; 781 | 782 | // Do the following as long as the controls are being pressed. 783 | while (IsUpPressed() && IsAnyMenuOpen() && GetCurrentMenu() != null) 784 | { 785 | // Update the current menu. 786 | currentMenu = GetCurrentMenu(); 787 | 788 | // Check if the game time has changed by "delay" amount. 789 | if (GetGameTimer() - time > delay) 790 | { 791 | // Increment the "changed indexes" counter 792 | times++; 793 | 794 | // If the controls are still being held down after moving 3 indexes, reduce the delay between index changes. 795 | if (times > 2) 796 | { 797 | delay = 150; 798 | } 799 | if (times > 5) 800 | { 801 | delay = 100; 802 | } 803 | if (times > 25) 804 | { 805 | delay = 50; 806 | } 807 | if (times > 60) 808 | { 809 | delay = 25; 810 | } 811 | 812 | // Update the currently selected item to the new one. 813 | currentMenu.GoUp(); 814 | 815 | // Reset the time to the current game timer. 816 | time = GetGameTimer(); 817 | } 818 | 819 | // Wait for the next game tick. 820 | await Delay(0); 821 | } 822 | } 823 | 824 | private async Task MenuButtonsDisableChecks() 825 | { 826 | bool isInputVisible() => UpdateOnscreenKeyboard() == 0; 827 | if (isInputVisible()) 828 | { 829 | bool buttonsState = DisableMenuButtons; 830 | while (isInputVisible()) 831 | { 832 | await Delay(0); 833 | DisableMenuButtons = true; 834 | } 835 | int timer = GetGameTimer(); 836 | while (GetGameTimer() - timer < 300) 837 | { 838 | await Delay(0); 839 | DisableMenuButtons = true; 840 | } 841 | DisableMenuButtons = buttonsState; 842 | } 843 | } 844 | #endregion 845 | 846 | /// 847 | /// Closes all menus. 848 | /// 849 | public static void CloseAllMenus() 850 | { 851 | Menus.ForEach((m) => { if (m.Visible) { m.CloseMenu(); } }); 852 | } 853 | 854 | /// 855 | /// Disables the most important controls for when a menu is open. 856 | /// 857 | private static void DisableControls() 858 | { 859 | if (!IsAnyMenuOpen()) 860 | return; 861 | var currMenu = GetCurrentMenu(); 862 | if (currMenu == null) 863 | return; 864 | if ( 865 | #if FIVEM 866 | Game.PlayerPed.IsDead 867 | #endif 868 | #if REDM 869 | IsEntityDead(PlayerPedId()) 870 | #endif 871 | ) 872 | { 873 | // Close all menus when the player dies. 874 | CloseAllMenus(); 875 | } 876 | #if FIVEM 877 | DisableGenericControls(currMenu); 878 | DisableRadioInputs(); 879 | DisablePhoneAndArrowKeysInputs(); 880 | DisableAttackControls(); 881 | 882 | // When in a vehicle 883 | if (Game.PlayerPed.IsInVehicle()) 884 | { 885 | Game.DisableControlThisFrame(0, Control.VehicleSelectNextWeapon); 886 | Game.DisableControlThisFrame(0, Control.VehicleSelectPrevWeapon); 887 | Game.DisableControlThisFrame(0, Control.VehicleCinCam); 888 | } 889 | #endif 890 | #if REDM 891 | DisableControlsRedM(); 892 | #endif 893 | } 894 | 895 | #if REDM 896 | private static void DisableControlsRedM() 897 | { 898 | DisableControlAction(0, (uint)Control.Attack, true); 899 | DisableControlAction(0, (uint)Control.Attack2, true); 900 | DisableControlAction(0, (uint)Control.HorseAim, true); 901 | DisableControlAction(0, (uint)Control.HorseAttack, true); 902 | DisableControlAction(0, (uint)Control.HorseAttack2, true); 903 | DisableControlAction(0, (uint)Control.HorseMelee, true); 904 | DisableControlAction(0, (uint)Control.MeleeAttack, true); 905 | DisableControlAction(0, (uint)Control.MeleeBlock, true); 906 | DisableControlAction(0, (uint)Control.MeleeGrapple, true); 907 | DisableControlAction(0, (uint)Control.MeleeGrappleAttack, true); 908 | DisableControlAction(0, (uint)Control.MeleeGrappleBreakout, true); 909 | DisableControlAction(0, (uint)Control.MeleeGrappleChoke, true); 910 | DisableControlAction(0, (uint)Control.MeleeGrappleMountSwitch, true); 911 | DisableControlAction(0, (uint)Control.MeleeGrappleReversal, true); 912 | DisableControlAction(0, (uint)Control.MeleeGrappleStandSwitch, true); 913 | DisableControlAction(0, (uint)Control.MeleeHorseAttackPrimary, true); 914 | DisableControlAction(0, (uint)Control.MeleeHorseAttackSecondary, true); 915 | DisableControlAction(0, (uint)Control.MeleeModifier, true); 916 | DisableControlAction(0, (uint)Control.VehAttack, true); 917 | DisableControlAction(0, (uint)Control.VehAttack2, true); 918 | DisableControlAction(0, (uint)Control.VehBoatAttack, true); 919 | DisableControlAction(0, (uint)Control.VehBoatAttack2, true); 920 | DisableControlAction(0, (uint)Control.VehCarAttack, true); 921 | DisableControlAction(0, (uint)Control.VehCarAttack2, true); 922 | DisableControlAction(0, (uint)Control.VehDraftAttack, true); 923 | DisableControlAction(0, (uint)Control.VehDraftAttack2, true); 924 | DisableControlAction(0, (uint)Control.VehFlyAttack, true); 925 | DisableControlAction(0, (uint)Control.VehFlyAttack2, true); 926 | DisableControlAction(0, (uint)Control.VehPassengerAttack, true); 927 | if (IsInputDisabled(2)) 928 | { 929 | DisableControlAction(0, (uint)Control.FrontendPauseAlternate, true); 930 | } 931 | } 932 | #endif 933 | #if FIVEM 934 | /// 935 | /// Disable required game controls when the menu is open. 936 | /// 937 | /// 938 | private static void DisableGenericControls(Menu currMenu) 939 | { 940 | // Disable Gamepad/Controller Specific controls: 941 | if (Game.CurrentInputMode == InputMode.GamePad) 942 | { 943 | Game.DisableControlThisFrame(0, Control.MultiplayerInfo); 944 | // when in a vehicle. 945 | if (Game.PlayerPed.IsInVehicle()) 946 | { 947 | Game.DisableControlThisFrame(0, Control.VehicleHeadlight); 948 | Game.DisableControlThisFrame(0, Control.VehicleDuck); 949 | 950 | // toggles boost in some dlc vehicles, hence it's disabled for controllers only (pressing select in the menu would trigger this). 951 | Game.DisableControlThisFrame(0, Control.VehicleFlyTransform); 952 | } 953 | } 954 | else // when not using a controller. 955 | { 956 | Game.DisableControlThisFrame(0, Control.FrontendPauseAlternate); // disable the escape key opening the pause menu, pressing P still works. 957 | 958 | // Disable the scrollwheel button changing weapons while the menu is open. 959 | // Only if you press TAB (to show the weapon wheel) then it will allow you to change weapons. 960 | if (!Game.IsControlPressed(0, Control.SelectWeapon)) 961 | { 962 | Game.DisableControlThisFrame(24, Control.SelectNextWeapon); 963 | Game.DisableControlThisFrame(24, Control.SelectPrevWeapon); 964 | } 965 | } 966 | var currentItem = currMenu.GetCurrentMenuItem(); 967 | if (currentItem != null) 968 | { 969 | if (currentItem is MenuSliderItem || currentItem is MenuListItem || currentItem is MenuDynamicListItem) 970 | { 971 | if (Game.CurrentInputMode == InputMode.GamePad) 972 | { 973 | Game.DisableControlThisFrame(0, Control.SelectWeapon); 974 | } 975 | } 976 | } 977 | } 978 | 979 | /// 980 | /// Disable conflicting Attack related game controls when the menu is open. 981 | /// 982 | private static void DisableAttackControls() 983 | { 984 | Game.DisableControlThisFrame(0, Control.Attack); 985 | Game.DisableControlThisFrame(0, Control.Attack2); 986 | Game.DisableControlThisFrame(0, Control.MeleeAttack1); 987 | Game.DisableControlThisFrame(0, Control.MeleeAttack2); 988 | Game.DisableControlThisFrame(0, Control.MeleeAttackAlternate); 989 | Game.DisableControlThisFrame(0, Control.MeleeAttackHeavy); 990 | Game.DisableControlThisFrame(0, Control.MeleeAttackLight); 991 | Game.DisableControlThisFrame(0, Control.VehicleAttack); 992 | Game.DisableControlThisFrame(0, Control.VehicleAttack2); 993 | Game.DisableControlThisFrame(0, Control.VehicleFlyAttack); 994 | Game.DisableControlThisFrame(0, Control.VehiclePassengerAttack); 995 | Game.DisableControlThisFrame(0, Control.Aim); 996 | // fires vehicle specific weapons when using right click on the mouse sometimes. 997 | Game.DisableControlThisFrame(0, Control.VehicleAim); 998 | } 999 | 1000 | /// 1001 | /// Disable conflicting Phone/Navigation related game controls when the menu is open. 1002 | /// 1003 | private static void DisablePhoneAndArrowKeysInputs() 1004 | { 1005 | Game.DisableControlThisFrame(0, Control.Phone); 1006 | Game.DisableControlThisFrame(0, Control.PhoneCancel); 1007 | Game.DisableControlThisFrame(0, Control.PhoneDown); 1008 | Game.DisableControlThisFrame(0, Control.PhoneLeft); 1009 | Game.DisableControlThisFrame(0, Control.PhoneRight); 1010 | } 1011 | 1012 | /// 1013 | /// Disable conflicting Radio related game controls when the menu is open. 1014 | /// 1015 | private static void DisableRadioInputs() 1016 | { 1017 | Game.DisableControlThisFrame(0, Control.RadioWheelLeftRight); 1018 | Game.DisableControlThisFrame(0, Control.RadioWheelUpDown); 1019 | Game.DisableControlThisFrame(0, Control.VehicleNextRadio); 1020 | Game.DisableControlThisFrame(0, Control.VehicleRadioWheel); 1021 | Game.DisableControlThisFrame(0, Control.VehiclePrevRadio); 1022 | } 1023 | #endif 1024 | 1025 | /// 1026 | /// Draws all the menus that are visible on the screen. 1027 | /// 1028 | /// 1029 | private static async Task ProcessMenus() 1030 | { 1031 | if (!( 1032 | Menus.Any() && 1033 | IsAnyMenuOpen() && 1034 | IsScreenFadedIn() && 1035 | !IsPauseMenuActive() && 1036 | !IsEntityDead(PlayerPedId()) 1037 | #if FIVEM 1038 | && !IsPlayerSwitchInProgress() 1039 | #endif 1040 | ) 1041 | ) 1042 | { 1043 | UnloadAssets(); 1044 | return; 1045 | } 1046 | await LoadAssets(); 1047 | DisableControls(); 1048 | await DrawMenus(); 1049 | PerformGC(); 1050 | } 1051 | 1052 | private static void PerformGC() 1053 | { 1054 | if (EnableManualGCs) 1055 | { 1056 | // once a minute 1057 | if (GetGameTimer() - ManualTimerForGC > 60000) 1058 | { 1059 | GC.Collect(); 1060 | ManualTimerForGC = GetGameTimer(); 1061 | } 1062 | } 1063 | } 1064 | 1065 | private static async Task DrawMenus() 1066 | { 1067 | Menu menu = GetCurrentMenu(); 1068 | if (menu == null) 1069 | { 1070 | return; 1071 | } 1072 | if (DontOpenAnyMenu) 1073 | { 1074 | if (menu.Visible && !menu.IgnoreDontOpenMenus) 1075 | { 1076 | menu.CloseMenu(); 1077 | } 1078 | } 1079 | else if (menu.Visible) 1080 | { 1081 | await menu.Draw(); 1082 | } 1083 | } 1084 | 1085 | #if FIVEM 1086 | internal static async Task DrawInstructionalButtons() 1087 | { 1088 | if ( 1089 | Game.IsPaused || 1090 | Game.Player.IsDead || 1091 | !IsScreenFadedIn() || 1092 | IsPlayerSwitchInProgress() || 1093 | IsWarningMessageActive() || 1094 | UpdateOnscreenKeyboard() == 0 1095 | ) 1096 | { 1097 | DisposeInstructionalButtonsScaleform(); 1098 | return; 1099 | } 1100 | Menu menu = GetCurrentMenu(); 1101 | if (menu == null || !menu.Visible || !menu.EnableInstructionalButtons) 1102 | { 1103 | DisposeInstructionalButtonsScaleform(); 1104 | return; 1105 | } 1106 | if (!HasScaleformMovieLoaded(_scale)) 1107 | { 1108 | _scale = RequestScaleformMovie("INSTRUCTIONAL_BUTTONS"); 1109 | } 1110 | while (!HasScaleformMovieLoaded(_scale)) 1111 | { 1112 | await Delay(0); 1113 | } 1114 | 1115 | DrawScaleformMovieFullscreen(_scale, 255, 255, 255, 0, 0); 1116 | 1117 | BeginScaleformMovieMethod(_scale, "CLEAR_ALL"); 1118 | EndScaleformMovieMethod(); 1119 | 1120 | 1121 | for (int i = 0; i < menu.InstructionalButtons.Count; i++) 1122 | { 1123 | string text = menu.InstructionalButtons.ElementAt(i).Value; 1124 | Control control = menu.InstructionalButtons.ElementAt(i).Key; 1125 | 1126 | BeginScaleformMovieMethod(_scale, "SET_DATA_SLOT"); 1127 | ScaleformMovieMethodAddParamInt(i); 1128 | string buttonName = GetControlInstructionalButton(0, (int)control, 1); 1129 | PushScaleformMovieMethodParameterString(buttonName); 1130 | PushScaleformMovieMethodParameterString(text); 1131 | EndScaleformMovieMethod(); 1132 | } 1133 | 1134 | // Use custom instructional buttons FIRST if they're present. 1135 | if (menu.CustomInstructionalButtons.Count > 0) 1136 | { 1137 | for (int i = 0; i < menu.CustomInstructionalButtons.Count; i++) 1138 | { 1139 | Menu.InstructionalButton button = menu.CustomInstructionalButtons[i]; 1140 | BeginScaleformMovieMethod(_scale, "SET_DATA_SLOT"); 1141 | ScaleformMovieMethodAddParamInt(i + menu.InstructionalButtons.Count); 1142 | PushScaleformMovieMethodParameterString(button.controlString); 1143 | PushScaleformMovieMethodParameterString(button.instructionText); 1144 | EndScaleformMovieMethod(); 1145 | } 1146 | } 1147 | 1148 | BeginScaleformMovieMethod(_scale, "DRAW_INSTRUCTIONAL_BUTTONS"); 1149 | ScaleformMovieMethodAddParamInt(0); 1150 | EndScaleformMovieMethod(); 1151 | 1152 | DrawScaleformMovieFullscreen(_scale, 255, 255, 255, 255, 0); 1153 | } 1154 | 1155 | private static void DisposeInstructionalButtonsScaleform() 1156 | { 1157 | if (HasScaleformMovieLoaded(_scale)) 1158 | { 1159 | SetScaleformMovieAsNoLongerNeeded(ref _scale); 1160 | } 1161 | } 1162 | #endif 1163 | #if REDM 1164 | /// 1165 | /// Prevent the UI prompts getting stuck on screen if this resource is ever to be restarted while someone has any menu's open. 1166 | /// 1167 | /// 1168 | [EventHandler("onResourceStop")] 1169 | internal static void OnResourceStop(string name) 1170 | { 1171 | if (name == GetCurrentResourceName()) 1172 | { 1173 | Debug.WriteLine($"^3[WARNING] [{name}] Closing all menus because this resource is being stopped, to prevent the UI Prompts getting stuck.^7"); 1174 | CloseAllMenus(); 1175 | } 1176 | } 1177 | #endif 1178 | } 1179 | } 1180 | -------------------------------------------------------------------------------- /MenuAPI/items/MenuCheckboxItem.cs: -------------------------------------------------------------------------------- 1 | using CitizenFX.Core; 2 | using static CitizenFX.Core.Native.API; 3 | 4 | namespace MenuAPI 5 | { 6 | public class MenuCheckboxItem : MenuItem 7 | { 8 | public bool Checked { get; set; } = false; 9 | public CheckboxStyle Style { get; set; } = CheckboxStyle.Tick; 10 | public enum CheckboxStyle 11 | { 12 | #if FIVEM 13 | Cross, 14 | #endif 15 | Tick 16 | } 17 | 18 | /// 19 | /// Creates a basic . 20 | /// 21 | /// 22 | public MenuCheckboxItem(string text) : this(text, null) { } 23 | /// 24 | /// Creates a basic and sets the checked state to 's state. 25 | /// 26 | /// 27 | /// 28 | public MenuCheckboxItem(string text, bool _checked) : this(text, null, _checked) { } 29 | /// 30 | /// Creates a basic and adds an item description. 31 | /// 32 | /// 33 | /// 34 | public MenuCheckboxItem(string text, string description) : this(text, description, false) { } 35 | /// 36 | /// Creates a new with all parameters set. 37 | /// 38 | /// 39 | /// 40 | /// 41 | public MenuCheckboxItem(string text, string description, bool _checked) : base(text, description) 42 | { 43 | Checked = _checked; 44 | } 45 | 46 | private int GetSpriteColour() 47 | { 48 | return Enabled ? 255 : 109; 49 | } 50 | 51 | #if FIVEM 52 | private string GetSpriteName() 53 | { 54 | if (Checked) 55 | { 56 | if (Style == CheckboxStyle.Tick) 57 | { 58 | if (Selected) 59 | { 60 | return "shop_box_tickb"; 61 | } 62 | return "shop_box_tick"; 63 | } 64 | else 65 | { 66 | if (Selected) 67 | { 68 | return "shop_box_crossb"; 69 | } 70 | return "shop_box_cross"; 71 | } 72 | } 73 | else 74 | { 75 | if (Selected) 76 | { 77 | return "shop_box_blankb"; 78 | } 79 | return "shop_box_blank"; 80 | } 81 | } 82 | #endif 83 | 84 | private float GetSpriteX() 85 | { 86 | #if FIVEM 87 | bool leftSide = false; 88 | bool leftAligned = ParentMenu.LeftAligned; 89 | if (leftSide) 90 | { 91 | if (leftAligned) 92 | { 93 | return 20f / MenuController.ScreenWidth; 94 | } 95 | else 96 | { 97 | return GetSafeZoneSize() - ((Width - 20f) / MenuController.ScreenWidth); 98 | } 99 | } 100 | else 101 | { 102 | if (leftAligned) 103 | { 104 | return (Width - 20f) / MenuController.ScreenWidth; 105 | } 106 | else 107 | { 108 | return GetSafeZoneSize() - (20f / MenuController.ScreenWidth); 109 | } 110 | } 111 | #endif 112 | #if REDM 113 | return (Width - 30f) / MenuController.ScreenWidth; 114 | #endif 115 | } 116 | 117 | internal override void Draw(int offset) 118 | { 119 | RightIcon = Icon.NONE; 120 | Label = null; 121 | base.Draw(offset); 122 | #if FIVEM 123 | SetScriptGfxAlign(76, 84); 124 | SetScriptGfxAlignParams(0f, 0f, 0f, 0f); 125 | #endif 126 | 127 | float yOffset = ParentMenu.MenuItemsYOffset + 1f - (RowHeight * MathUtil.Clamp(ParentMenu.Size, 0, ParentMenu.MaxItemsOnScreen)); 128 | #if FIVEM 129 | string name = GetSpriteName(); 130 | #endif 131 | 132 | float spriteY = (ParentMenu.Position.Value + ((Index - offset) * RowHeight) + (20f) + yOffset) / MenuController.ScreenHeight; 133 | float spriteX = GetSpriteX(); 134 | #if FIVEM 135 | float spriteHeight = 45f / MenuController.ScreenHeight; 136 | float spriteWidth = 45f / MenuController.ScreenWidth; 137 | #endif 138 | int color = GetSpriteColour(); 139 | #if FIVEM 140 | DrawSprite("commonmenu", name, spriteX, spriteY, spriteWidth, spriteHeight, 0f, color, color, color, 255); 141 | ResetScriptGfxAlign(); 142 | #endif 143 | #if REDM 144 | float spriteHeight = 24f / MenuController.ScreenHeight; 145 | float spriteWidth = 16f / MenuController.ScreenWidth; 146 | DrawSprite("menu_textures", "SELECTION_BOX_SQUARE", spriteX, spriteY, spriteWidth, spriteHeight, 0f, color, color, color, 255, false); 147 | if (Checked) 148 | { 149 | int[] sc = Enabled ? (Selected ? new int[3] { 255, 255, 255 } : new int[3] { 181, 17, 18 }) : (Selected ? new int[3] { 109, 109, 109 } : new int[3] { 110, 10, 10 }); 150 | DrawSprite("generic_textures", "TICK", spriteX, spriteY, spriteWidth, spriteHeight, 0f, sc[0], sc[1], sc[2], 255, false); 151 | } 152 | #endif 153 | } 154 | 155 | internal override void GoRight() 156 | { 157 | ParentMenu.SelectItem(this); 158 | } 159 | 160 | internal override void Select() 161 | { 162 | Checked = !Checked; 163 | ParentMenu.CheckboxChangedEvent(this, Index, Checked); 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /MenuAPI/items/MenuDynamicListItem.cs: -------------------------------------------------------------------------------- 1 | using static CitizenFX.Core.Native.Function; 2 | using static CitizenFX.Core.Native.API; 3 | 4 | namespace MenuAPI 5 | { 6 | public class MenuDynamicListItem : MenuItem 7 | { 8 | public bool HideArrowsWhenNotSelected { get; set; } = false; 9 | public string CurrentItem { get; set; } = null; 10 | 11 | public delegate string ChangeItemCallback(MenuDynamicListItem item, bool left); 12 | 13 | public ChangeItemCallback Callback { get; set; } 14 | 15 | public MenuDynamicListItem(string text, string initialValue, ChangeItemCallback callback) : this(text, initialValue, callback, null) { } 16 | public MenuDynamicListItem(string text, string initialValue, ChangeItemCallback callback, string description) : base(text, description) 17 | { 18 | CurrentItem = initialValue; 19 | Callback = callback; 20 | } 21 | 22 | internal override void Draw(int indexOffset) 23 | { 24 | if (HideArrowsWhenNotSelected && !Selected) 25 | { 26 | Label = CurrentItem ?? "~r~N/A"; 27 | } 28 | else 29 | { 30 | Label = $"~s~← {CurrentItem ?? "~r~N/A~s~"} ~s~→"; 31 | } 32 | base.Draw(indexOffset); 33 | } 34 | 35 | internal override void GoRight() 36 | { 37 | string oldValue = CurrentItem; 38 | string newSelectedItem = Callback(this, false); 39 | CurrentItem = newSelectedItem; 40 | ParentMenu.DynamicListItemCurrentItemChanged(ParentMenu, this, oldValue, newSelectedItem); 41 | #if FIVEM 42 | PlaySoundFrontend(-1, "NAV_LEFT_RIGHT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false); 43 | #endif 44 | #if REDM 45 | // Has invalid parameter types in API. 46 | Call((CitizenFX.Core.Native.Hash)0xCE5D0FFE83939AF1, -1, "NAV_RIGHT", "HUD_SHOP_SOUNDSET", 1); 47 | #endif 48 | } 49 | 50 | internal override void GoLeft() 51 | { 52 | string oldValue = CurrentItem; 53 | string newSelectedItem = Callback(this, true); 54 | CurrentItem = newSelectedItem; 55 | ParentMenu.DynamicListItemCurrentItemChanged(ParentMenu, this, oldValue, newSelectedItem); 56 | #if FIVEM 57 | PlaySoundFrontend(-1, "NAV_LEFT_RIGHT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false); 58 | #endif 59 | #if REDM 60 | // Has invalid parameter types in API. 61 | Call((CitizenFX.Core.Native.Hash)0xCE5D0FFE83939AF1, -1, "NAV_RIGHT", "HUD_SHOP_SOUNDSET", 1); 62 | #endif 63 | } 64 | 65 | internal override void Select() 66 | { 67 | ParentMenu.DynamicListItemSelectEvent(ParentMenu, this, CurrentItem); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /MenuAPI/items/MenuItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using CitizenFX.Core; 4 | using static CitizenFX.Core.Native.API; 5 | using static CitizenFX.Core.Native.Function; 6 | using static CitizenFX.Core.Native.Hash; 7 | 8 | namespace MenuAPI 9 | { 10 | public class MenuItem 11 | { 12 | public enum Icon 13 | { 14 | NONE, 15 | #if FIVEM 16 | LOCK, 17 | STAR, 18 | WARNING, 19 | CROWN, 20 | MEDAL_BRONZE, 21 | MEDAL_GOLD, 22 | MEDAL_SILVER, 23 | CASH, 24 | COKE, 25 | HEROIN, 26 | METH, 27 | WEED, 28 | AMMO, 29 | ARMOR, 30 | BARBER, 31 | CLOTHING, 32 | FRANKLIN, 33 | BIKE, 34 | CAR, 35 | GUN, 36 | HEALTH_HEART, 37 | MAKEUP_BRUSH, 38 | MASK, 39 | MICHAEL, 40 | TATTOO, 41 | TICK, 42 | TREVOR, 43 | FEMALE, 44 | MALE, 45 | LOCK_ARENA, 46 | ADVERSARY, 47 | BASE_JUMPING, 48 | BRIEFCASE, 49 | MISSION_STAR, 50 | DEATHMATCH, 51 | CASTLE, 52 | TROPHY, 53 | RACE_FLAG, 54 | RACE_FLAG_PLANE, 55 | RACE_FLAG_BICYCLE, 56 | RACE_FLAG_PERSON, 57 | RACE_FLAG_CAR, 58 | RACE_FLAG_BOAT_ANCHOR, 59 | ROCKSTAR, 60 | STUNT, 61 | STUNT_PREMIUM, 62 | RACE_FLAG_STUNT_JUMP, 63 | SHIELD, 64 | TEAM_DEATHMATCH, 65 | VEHICLE_DEATHMATCH, 66 | MP_AMMO_PICKUP, 67 | MP_AMMO, 68 | MP_CASH, 69 | MP_RP, 70 | MP_SPECTATING, 71 | SALE, 72 | GLOBE_WHITE, 73 | GLOBE_RED, 74 | GLOBE_BLUE, 75 | GLOBE_YELLOW, 76 | GLOBE_GREEN, 77 | GLOBE_ORANGE, 78 | INV_ARM_WRESTLING, 79 | INV_BASEJUMP, 80 | INV_MISSION, 81 | INV_DARTS, 82 | INV_DEATHMATCH, 83 | INV_DRUG, 84 | INV_CASTLE, 85 | INV_GOLF, 86 | INV_BIKE, 87 | INV_BOAT, 88 | INV_ANCHOR, 89 | INV_CAR, 90 | INV_DOLLAR, 91 | INV_COKE, 92 | INV_KEY, 93 | INV_DATA, 94 | INV_HELI, 95 | INV_HEORIN, 96 | INV_KEYCARD, 97 | INV_METH, 98 | INV_BRIEFCASE, 99 | INV_LINK, 100 | INV_PERSON, 101 | INV_PLANE, 102 | INV_PLANE2, 103 | INV_QUESTIONMARK, 104 | INV_REMOTE, 105 | INV_SAFE, 106 | INV_STEER_WHEEL, 107 | INV_WEAPON, 108 | INV_WEED, 109 | INV_RACE_FLAG_PLANE, 110 | INV_RACE_FLAG_BICYCLE, 111 | INV_RACE_FLAG_BOAT_ANCHOR, 112 | INV_RACE_FLAG_PERSON, 113 | INV_RACE_FLAG_CAR, 114 | INV_RACE_FLAG_HELMET, 115 | INV_SHOOTING_RANGE, 116 | INV_SURVIVAL, 117 | INV_TEAM_DEATHMATCH, 118 | INV_TENNIS, 119 | INV_VEHICLE_DEATHMATCH, 120 | AUDIO_MUTE, 121 | AUDIO_INACTIVE, 122 | AUDIO_VOL1, 123 | AUDIO_VOL2, 124 | AUDIO_VOL3, 125 | COUNTRY_USA, 126 | COUNTRY_UK, 127 | COUNTRY_SWEDEN, 128 | COUNTRY_KOREA, 129 | COUNTRY_JAPAN, 130 | COUNTRY_ITALY, 131 | COUNTRY_GERMANY, 132 | COUNTRY_FRANCE, 133 | BRAND_ALBANY, 134 | BRAND_ANNIS, 135 | BRAND_BANSHEE, 136 | BRAND_BENEFACTOR, 137 | BRAND_BF, 138 | BRAND_BOLLOKAN, 139 | BRAND_BRAVADO, 140 | BRAND_BRUTE, 141 | BRAND_BUCKINGHAM, 142 | BRAND_CANIS, 143 | BRAND_CHARIOT, 144 | BRAND_CHEVAL, 145 | BRAND_CLASSIQUE, 146 | BRAND_COIL, 147 | BRAND_DECLASSE, 148 | BRAND_DEWBAUCHEE, 149 | BRAND_DILETTANTE, 150 | BRAND_DINKA, 151 | BRAND_DUNDREARY, 152 | BRAND_EMPORER, 153 | BRAND_ENUS, 154 | BRAND_FATHOM, 155 | BRAND_GALIVANTER, 156 | BRAND_GROTTI, 157 | BRAND_GROTTI2, 158 | BRAND_HIJAK, 159 | BRAND_HVY, 160 | BRAND_IMPONTE, 161 | BRAND_INVETERO, 162 | BRAND_JACKSHEEPE, 163 | BRAND_LCC, 164 | BRAND_JOBUILT, 165 | BRAND_KARIN, 166 | BRAND_LAMPADATI, 167 | BRAND_MAIBATSU, 168 | BRAND_MAMMOTH, 169 | BRAND_MTL, 170 | BRAND_NAGASAKI, 171 | BRAND_OBEY, 172 | BRAND_OCELOT, 173 | BRAND_OVERFLOD, 174 | BRAND_PED, 175 | BRAND_PEGASSI, 176 | BRAND_PFISTER, 177 | BRAND_PRINCIPE, 178 | BRAND_PROGEN, 179 | BRAND_PROGEN2, 180 | BRAND_RUNE, 181 | BRAND_SCHYSTER, 182 | BRAND_SHITZU, 183 | BRAND_SPEEDOPHILE, 184 | BRAND_STANLEY, 185 | BRAND_TRUFFADE, 186 | BRAND_UBERMACHT, 187 | BRAND_VAPID, 188 | BRAND_VULCAR, 189 | BRAND_WEENY, 190 | BRAND_WESTERN, 191 | BRAND_WESTERNMOTORCYCLE, 192 | BRAND_WILLARD, 193 | BRAND_ZIRCONIUM, 194 | INFO 195 | #endif 196 | #if REDM 197 | LOCK, 198 | TICK, 199 | CIRCLE, 200 | SADDLE, 201 | STAR, 202 | ARROW_LEFT, 203 | ARROW_RIGHT, 204 | INVITE_SENT, 205 | SELECTION_BOX 206 | #endif 207 | } 208 | public string Text { get; set; } 209 | public string Label { get; set; } 210 | public Icon LeftIcon { get; set; } 211 | public Icon RightIcon { get; set; } 212 | public bool Enabled { get; set; } = true; 213 | public string Description 214 | { 215 | get 216 | { 217 | return _description; 218 | } 219 | set 220 | { 221 | #if FIVEM 222 | _description = value; 223 | #endif 224 | #if REDM 225 | if (value != null) 226 | { 227 | string text = value; 228 | int maxLength = 50; 229 | List lines = new List(); 230 | while (text.Length > maxLength) 231 | { 232 | var substr = text.Substring(0, Math.Min(text.Length - 1, maxLength)); 233 | var lastIndex = substr.LastIndexOf(" "); 234 | if (lastIndex == -1) 235 | { 236 | lastIndex = Math.Min(text.Length - 1, maxLength); 237 | } 238 | lines.Add(text.Substring(0, lastIndex)); 239 | text = text.Substring(lastIndex); 240 | } 241 | lines.Add(text); 242 | text = ""; 243 | foreach (var str in lines) 244 | { 245 | text += str + "\n"; 246 | } 247 | _description = text; 248 | } 249 | #endif 250 | } 251 | } 252 | private string _description; 253 | public int Index { get { if (ParentMenu != null) return ParentMenu.GetMenuItems().IndexOf(this); return -1; } } //{ get; internal set; } 254 | public bool Selected { get { if (ParentMenu != null) { return ParentMenu.CurrentIndex == Index; } return false; } } 255 | public Menu ParentMenu { get; set; } 256 | public int PositionOnScreen { get; internal set; } 257 | protected const float Width = Menu.Width; 258 | protected const float RowHeight = 38f; 259 | 260 | // Allows you to attach data to a menu item if you want to identify the menu item without having to put identification info in the visible text or description. 261 | public dynamic ItemData { get; set; } 262 | 263 | /// 264 | /// Creates a new . 265 | /// 266 | /// 267 | public MenuItem(string text) : this(text, null) { } 268 | 269 | /// 270 | /// Creates a new . 271 | /// 272 | /// 273 | /// 274 | public MenuItem(string text, string description) 275 | { 276 | Text = text; 277 | Description = description; 278 | } 279 | 280 | /// 281 | /// Get the sprite dictionary name for the given icon. 282 | /// 283 | /// 284 | /// 285 | protected string GetSpriteDictionary(Icon icon) 286 | { 287 | switch (icon) 288 | { 289 | #if FIVEM 290 | case Icon.MALE: 291 | case Icon.FEMALE: 292 | case Icon.AUDIO_MUTE: 293 | case Icon.AUDIO_INACTIVE: 294 | case Icon.AUDIO_VOL1: 295 | case Icon.AUDIO_VOL2: 296 | case Icon.AUDIO_VOL3: 297 | return "mpleaderboard"; 298 | case Icon.INV_ARM_WRESTLING: 299 | case Icon.INV_BASEJUMP: 300 | case Icon.INV_MISSION: 301 | case Icon.INV_DARTS: 302 | case Icon.INV_DEATHMATCH: 303 | case Icon.INV_DRUG: 304 | case Icon.INV_CASTLE: 305 | case Icon.INV_GOLF: 306 | case Icon.INV_BIKE: 307 | case Icon.INV_BOAT: 308 | case Icon.INV_ANCHOR: 309 | case Icon.INV_CAR: 310 | case Icon.INV_DOLLAR: 311 | case Icon.INV_COKE: 312 | case Icon.INV_KEY: 313 | case Icon.INV_DATA: 314 | case Icon.INV_HELI: 315 | case Icon.INV_HEORIN: 316 | case Icon.INV_KEYCARD: 317 | case Icon.INV_METH: 318 | case Icon.INV_BRIEFCASE: 319 | case Icon.INV_LINK: 320 | case Icon.INV_PERSON: 321 | case Icon.INV_PLANE: 322 | case Icon.INV_PLANE2: 323 | case Icon.INV_QUESTIONMARK: 324 | case Icon.INV_REMOTE: 325 | case Icon.INV_SAFE: 326 | case Icon.INV_STEER_WHEEL: 327 | case Icon.INV_WEAPON: 328 | case Icon.INV_WEED: 329 | case Icon.INV_RACE_FLAG_PLANE: 330 | case Icon.INV_RACE_FLAG_BICYCLE: 331 | case Icon.INV_RACE_FLAG_BOAT_ANCHOR: 332 | case Icon.INV_RACE_FLAG_PERSON: 333 | case Icon.INV_RACE_FLAG_CAR: 334 | case Icon.INV_RACE_FLAG_HELMET: 335 | case Icon.INV_SHOOTING_RANGE: 336 | case Icon.INV_SURVIVAL: 337 | case Icon.INV_TEAM_DEATHMATCH: 338 | case Icon.INV_TENNIS: 339 | case Icon.INV_VEHICLE_DEATHMATCH: 340 | return "mpinventory"; 341 | case Icon.ADVERSARY: 342 | case Icon.BASE_JUMPING: 343 | case Icon.BRIEFCASE: 344 | case Icon.MISSION_STAR: 345 | case Icon.DEATHMATCH: 346 | case Icon.CASTLE: 347 | case Icon.TROPHY: 348 | case Icon.RACE_FLAG: 349 | case Icon.RACE_FLAG_PLANE: 350 | case Icon.RACE_FLAG_BICYCLE: 351 | case Icon.RACE_FLAG_PERSON: 352 | case Icon.RACE_FLAG_CAR: 353 | case Icon.RACE_FLAG_BOAT_ANCHOR: 354 | case Icon.ROCKSTAR: 355 | case Icon.STUNT: 356 | case Icon.STUNT_PREMIUM: 357 | case Icon.RACE_FLAG_STUNT_JUMP: 358 | case Icon.SHIELD: 359 | case Icon.TEAM_DEATHMATCH: 360 | case Icon.VEHICLE_DEATHMATCH: 361 | return "commonmenutu"; 362 | case Icon.MP_AMMO_PICKUP: 363 | case Icon.MP_AMMO: 364 | case Icon.MP_CASH: 365 | case Icon.MP_RP: 366 | case Icon.MP_SPECTATING: 367 | return "mphud"; 368 | case Icon.SALE: 369 | return "mpshopsale"; 370 | case Icon.GLOBE_WHITE: 371 | case Icon.GLOBE_RED: 372 | case Icon.GLOBE_BLUE: 373 | case Icon.GLOBE_YELLOW: 374 | case Icon.GLOBE_GREEN: 375 | case Icon.GLOBE_ORANGE: 376 | return "mprankbadge"; 377 | case Icon.COUNTRY_USA: 378 | case Icon.COUNTRY_UK: 379 | case Icon.COUNTRY_SWEDEN: 380 | case Icon.COUNTRY_KOREA: 381 | case Icon.COUNTRY_JAPAN: 382 | case Icon.COUNTRY_ITALY: 383 | case Icon.COUNTRY_GERMANY: 384 | case Icon.COUNTRY_FRANCE: 385 | case Icon.BRAND_ALBANY: 386 | case Icon.BRAND_ANNIS: 387 | case Icon.BRAND_BANSHEE: 388 | case Icon.BRAND_BENEFACTOR: 389 | case Icon.BRAND_BF: 390 | case Icon.BRAND_BOLLOKAN: 391 | case Icon.BRAND_BRAVADO: 392 | case Icon.BRAND_BRUTE: 393 | case Icon.BRAND_BUCKINGHAM: 394 | case Icon.BRAND_CANIS: 395 | case Icon.BRAND_CHARIOT: 396 | case Icon.BRAND_CHEVAL: 397 | case Icon.BRAND_CLASSIQUE: 398 | case Icon.BRAND_COIL: 399 | case Icon.BRAND_DECLASSE: 400 | case Icon.BRAND_DEWBAUCHEE: 401 | case Icon.BRAND_DILETTANTE: 402 | case Icon.BRAND_DINKA: 403 | case Icon.BRAND_DUNDREARY: 404 | case Icon.BRAND_EMPORER: 405 | case Icon.BRAND_ENUS: 406 | case Icon.BRAND_FATHOM: 407 | case Icon.BRAND_GALIVANTER: 408 | case Icon.BRAND_GROTTI: 409 | case Icon.BRAND_HIJAK: 410 | case Icon.BRAND_HVY: 411 | case Icon.BRAND_IMPONTE: 412 | case Icon.BRAND_INVETERO: 413 | case Icon.BRAND_JACKSHEEPE: 414 | case Icon.BRAND_JOBUILT: 415 | case Icon.BRAND_KARIN: 416 | case Icon.BRAND_LAMPADATI: 417 | case Icon.BRAND_MAIBATSU: 418 | case Icon.BRAND_MAMMOTH: 419 | case Icon.BRAND_MTL: 420 | case Icon.BRAND_NAGASAKI: 421 | case Icon.BRAND_OBEY: 422 | case Icon.BRAND_OCELOT: 423 | case Icon.BRAND_OVERFLOD: 424 | case Icon.BRAND_PED: 425 | case Icon.BRAND_PEGASSI: 426 | case Icon.BRAND_PFISTER: 427 | case Icon.BRAND_PRINCIPE: 428 | case Icon.BRAND_PROGEN: 429 | case Icon.BRAND_SCHYSTER: 430 | case Icon.BRAND_SHITZU: 431 | case Icon.BRAND_SPEEDOPHILE: 432 | case Icon.BRAND_STANLEY: 433 | case Icon.BRAND_TRUFFADE: 434 | case Icon.BRAND_UBERMACHT: 435 | case Icon.BRAND_VAPID: 436 | case Icon.BRAND_VULCAR: 437 | case Icon.BRAND_WEENY: 438 | case Icon.BRAND_WESTERN: 439 | case Icon.BRAND_WESTERNMOTORCYCLE: 440 | case Icon.BRAND_WILLARD: 441 | case Icon.BRAND_ZIRCONIUM: 442 | return "mpcarhud"; 443 | case Icon.BRAND_GROTTI2: 444 | case Icon.BRAND_LCC: 445 | case Icon.BRAND_PROGEN2: 446 | case Icon.BRAND_RUNE: 447 | return "mpcarhud2"; 448 | case Icon.INFO: 449 | return "shared"; 450 | default: 451 | return "commonmenu"; 452 | #endif 453 | #if REDM 454 | case Icon.LOCK: 455 | case Icon.TICK: 456 | case Icon.CIRCLE: 457 | case Icon.SADDLE: 458 | case Icon.STAR: 459 | case Icon.ARROW_LEFT: 460 | case Icon.ARROW_RIGHT: 461 | case Icon.INVITE_SENT: 462 | case Icon.SELECTION_BOX: 463 | return "menu_textures"; 464 | default: 465 | return ""; 466 | #endif 467 | } 468 | } 469 | 470 | /// 471 | /// Get the sprite name for the given icon depending on the selected state of the item. 472 | /// 473 | /// 474 | /// 475 | /// 476 | protected string GetSpriteName(Icon icon, bool selected) 477 | { 478 | switch (icon) 479 | { 480 | #if FIVEM 481 | case Icon.AMMO: return selected ? "shop_ammo_icon_b" : "shop_ammo_icon_a"; 482 | case Icon.ARMOR: return selected ? "shop_armour_icon_b" : "shop_armour_icon_a"; 483 | case Icon.BARBER: return selected ? "shop_barber_icon_b" : "shop_barber_icon_a"; 484 | case Icon.BIKE: return selected ? "shop_garage_bike_icon_b" : "shop_garage_bike_icon_a"; 485 | case Icon.CAR: return selected ? "shop_garage_icon_b" : "shop_garage_icon_a"; 486 | case Icon.CASH: return "mp_specitem_cash"; 487 | case Icon.CLOTHING: return selected ? "shop_clothing_icon_b" : "shop_clothing_icon_a"; 488 | case Icon.COKE: return "mp_specitem_coke"; 489 | case Icon.CROWN: return "mp_hostcrown"; 490 | case Icon.FRANKLIN: return selected ? "shop_franklin_icon_b" : "shop_franklin_icon_a"; 491 | case Icon.GUN: return selected ? "shop_gunclub_icon_b" : "shop_gunclub_icon_a"; 492 | case Icon.HEALTH_HEART: return selected ? "shop_health_icon_b" : "shop_health_icon_a"; 493 | case Icon.HEROIN: return "mp_specitem_heroin"; 494 | case Icon.LOCK: return "shop_lock"; 495 | case Icon.MAKEUP_BRUSH: return selected ? "shop_makeup_icon_b" : "shop_makeup_icon_a"; 496 | case Icon.MASK: return selected ? "shop_mask_icon_b" : "shop_mask_icon_a"; 497 | case Icon.MEDAL_BRONZE: return "mp_medal_bronze"; 498 | case Icon.MEDAL_GOLD: return "mp_medal_gold"; 499 | case Icon.MEDAL_SILVER: return "mp_medal_silver"; 500 | case Icon.METH: return "mp_specitem_meth"; 501 | case Icon.MICHAEL: return selected ? "shop_michael_icon_b" : "shop_michael_icon_a"; 502 | case Icon.STAR: return "shop_new_star"; 503 | case Icon.TATTOO: return selected ? "shop_tattoos_icon_b" : "shop_tattoos_icon_a"; 504 | case Icon.TICK: return "shop_tick_icon"; 505 | case Icon.TREVOR: return selected ? "shop_trevor_icon_b" : "shop_trevor_icon_a"; 506 | case Icon.WARNING: return "mp_alerttriangle"; 507 | case Icon.WEED: return "mp_specitem_weed"; 508 | case Icon.MALE: return "leaderboard_male_icon"; 509 | case Icon.FEMALE: return "leaderboard_female_icon"; 510 | case Icon.LOCK_ARENA: return "shop_lock_arena"; 511 | case Icon.ADVERSARY: return "adversary"; 512 | case Icon.BASE_JUMPING: return "base_jumping"; 513 | case Icon.BRIEFCASE: return "capture_the_flag"; 514 | case Icon.MISSION_STAR: return "custom_mission"; 515 | case Icon.DEATHMATCH: return "deathmatch"; 516 | case Icon.CASTLE: return "gang_attack"; 517 | case Icon.TROPHY: return "last_team_standing"; 518 | case Icon.RACE_FLAG: return "race"; 519 | case Icon.RACE_FLAG_PLANE: return "race_air"; 520 | case Icon.RACE_FLAG_BICYCLE: return "race_bicycle"; 521 | case Icon.RACE_FLAG_PERSON: return "race_foot"; 522 | case Icon.RACE_FLAG_CAR: return "race_land"; 523 | case Icon.RACE_FLAG_BOAT_ANCHOR: return "race_water"; 524 | case Icon.ROCKSTAR: return "rockstar"; 525 | case Icon.STUNT: return "stunt"; 526 | case Icon.STUNT_PREMIUM: return "stunt_premium"; 527 | case Icon.RACE_FLAG_STUNT_JUMP: return "stunt_race"; 528 | case Icon.SHIELD: return "survival"; 529 | case Icon.TEAM_DEATHMATCH: return "team_deathmatch"; 530 | case Icon.VEHICLE_DEATHMATCH: return "vehicle_deathmatch"; 531 | case Icon.MP_AMMO_PICKUP: return "ammo_pickup"; 532 | case Icon.MP_AMMO: return "mp_anim_ammo"; 533 | case Icon.MP_CASH: return "mp_anim_cash"; 534 | case Icon.MP_RP: return "mp_anim_rp"; 535 | case Icon.MP_SPECTATING: return "spectating"; 536 | case Icon.SALE: return "saleicon"; 537 | case Icon.GLOBE_WHITE: 538 | case Icon.GLOBE_RED: 539 | case Icon.GLOBE_BLUE: 540 | case Icon.GLOBE_YELLOW: 541 | case Icon.GLOBE_GREEN: 542 | case Icon.GLOBE_ORANGE: 543 | return "globe"; 544 | case Icon.INV_ARM_WRESTLING: return "arm_wrestling"; 545 | case Icon.INV_BASEJUMP: return "basejump"; 546 | case Icon.INV_MISSION: return "custom_mission"; 547 | case Icon.INV_DARTS: return "darts"; 548 | case Icon.INV_DEATHMATCH: return "deathmatch"; 549 | case Icon.INV_DRUG: return "drug_trafficking"; 550 | case Icon.INV_CASTLE: return "gang_attack"; 551 | case Icon.INV_GOLF: return "golf"; 552 | case Icon.INV_BIKE: return "mp_specitem_bike"; 553 | case Icon.INV_BOAT: return "mp_specitem_boat"; 554 | case Icon.INV_ANCHOR: return "mp_specitem_boatpickup"; 555 | case Icon.INV_CAR: return "mp_specitem_car"; 556 | case Icon.INV_DOLLAR: return "mp_specitem_cash"; 557 | case Icon.INV_COKE: return "mp_specitem_coke"; 558 | case Icon.INV_KEY: return "mp_specitem_cuffkeys"; 559 | case Icon.INV_DATA: return "mp_specitem_data"; 560 | case Icon.INV_HELI: return "mp_specitem_heli"; 561 | case Icon.INV_HEORIN: return "mp_specitem_heroin"; 562 | case Icon.INV_KEYCARD: return "mp_specitem_keycard"; 563 | case Icon.INV_METH: return "mp_specitem_meth"; 564 | case Icon.INV_BRIEFCASE: return "mp_specitem_package"; 565 | case Icon.INV_LINK: return "mp_specitem_partnericon"; 566 | case Icon.INV_PERSON: return "mp_specitem_ped"; 567 | case Icon.INV_PLANE: return "mp_specitem_plane"; 568 | case Icon.INV_PLANE2: return "mp_specitem_plane2"; 569 | case Icon.INV_QUESTIONMARK: return "mp_specitem_randomobject"; 570 | case Icon.INV_REMOTE: return "mp_specitem_remote"; 571 | case Icon.INV_SAFE: return "mp_specitem_safe"; 572 | case Icon.INV_STEER_WHEEL: return "mp_specitem_steer_wheel"; 573 | case Icon.INV_WEAPON: return "mp_specitem_weapons"; 574 | case Icon.INV_WEED: return "mp_specitem_weed"; 575 | case Icon.INV_RACE_FLAG_PLANE: return "race_air"; 576 | case Icon.INV_RACE_FLAG_BICYCLE: return "race_bike"; 577 | case Icon.INV_RACE_FLAG_BOAT_ANCHOR: return "race_boat"; 578 | case Icon.INV_RACE_FLAG_PERSON: return "race_foot"; 579 | case Icon.INV_RACE_FLAG_CAR: return "race_land"; 580 | case Icon.INV_RACE_FLAG_HELMET: return "race_offroad"; 581 | case Icon.INV_SHOOTING_RANGE: return "shooting_range"; 582 | case Icon.INV_SURVIVAL: return "survival"; 583 | case Icon.INV_TEAM_DEATHMATCH: return "team_deathmatch"; 584 | case Icon.INV_TENNIS: return "tennis"; 585 | case Icon.INV_VEHICLE_DEATHMATCH: return "vehicle_deathmatch"; 586 | case Icon.AUDIO_MUTE: return "leaderboard_audio_mute"; 587 | case Icon.AUDIO_INACTIVE: return "leaderboard_audio_inactive"; 588 | case Icon.AUDIO_VOL1: return "leaderboard_audio_1"; 589 | case Icon.AUDIO_VOL2: return "leaderboard_audio_2"; 590 | case Icon.AUDIO_VOL3: return "leaderboard_audio_3"; 591 | case Icon.COUNTRY_USA: return "vehicle_card_icons_flag_usa"; 592 | case Icon.COUNTRY_UK: return "vehicle_card_icons_flag_uk"; 593 | case Icon.COUNTRY_SWEDEN: return "vehicle_card_icons_flag_sweden"; 594 | case Icon.COUNTRY_KOREA: return "vehicle_card_icons_flag_korea"; 595 | case Icon.COUNTRY_JAPAN: return "vehicle_card_icons_flag_japan"; 596 | case Icon.COUNTRY_ITALY: return "vehicle_card_icons_flag_italy"; 597 | case Icon.COUNTRY_GERMANY: return "vehicle_card_icons_flag_germany"; 598 | case Icon.COUNTRY_FRANCE: return "vehicle_card_icons_flag_france"; 599 | case Icon.BRAND_ALBANY: return "albany"; 600 | case Icon.BRAND_ANNIS: return "annis"; 601 | case Icon.BRAND_BANSHEE: return "banshee"; 602 | case Icon.BRAND_BENEFACTOR: return "benefactor"; 603 | case Icon.BRAND_BF: return "bf"; 604 | case Icon.BRAND_BOLLOKAN: return "bollokan"; 605 | case Icon.BRAND_BRAVADO: return "bravado"; 606 | case Icon.BRAND_BRUTE: return "brute"; 607 | case Icon.BRAND_BUCKINGHAM: return "buckingham"; 608 | case Icon.BRAND_CANIS: return "canis"; 609 | case Icon.BRAND_CHARIOT: return "chariot"; 610 | case Icon.BRAND_CHEVAL: return "cheval"; 611 | case Icon.BRAND_CLASSIQUE: return "classique"; 612 | case Icon.BRAND_COIL: return "coil"; 613 | case Icon.BRAND_DECLASSE: return "declasse"; 614 | case Icon.BRAND_DEWBAUCHEE: return "dewbauchee"; 615 | case Icon.BRAND_DILETTANTE: return "dilettante"; 616 | case Icon.BRAND_DINKA: return "dinka"; 617 | case Icon.BRAND_DUNDREARY: return "dundreary"; 618 | case Icon.BRAND_EMPORER: return "emporer"; 619 | case Icon.BRAND_ENUS: return "enus"; 620 | case Icon.BRAND_FATHOM: return "fathom"; 621 | case Icon.BRAND_GALIVANTER: return "galivanter"; 622 | case Icon.BRAND_GROTTI: return "grotti"; 623 | case Icon.BRAND_HIJAK: return "hijak"; 624 | case Icon.BRAND_HVY: return "hvy"; 625 | case Icon.BRAND_IMPONTE: return "imponte"; 626 | case Icon.BRAND_INVETERO: return "invetero"; 627 | case Icon.BRAND_JACKSHEEPE: return "jacksheepe"; 628 | case Icon.BRAND_JOBUILT: return "jobuilt"; 629 | case Icon.BRAND_KARIN: return "karin"; 630 | case Icon.BRAND_LAMPADATI: return "lampadati"; 631 | case Icon.BRAND_MAIBATSU: return "maibatsu"; 632 | case Icon.BRAND_MAMMOTH: return "mammoth"; 633 | case Icon.BRAND_MTL: return "mtl"; 634 | case Icon.BRAND_NAGASAKI: return "nagasaki"; 635 | case Icon.BRAND_OBEY: return "obey"; 636 | case Icon.BRAND_OCELOT: return "ocelot"; 637 | case Icon.BRAND_OVERFLOD: return "overflod"; 638 | case Icon.BRAND_PED: return "ped"; 639 | case Icon.BRAND_PEGASSI: return "pegassi"; 640 | case Icon.BRAND_PFISTER: return "pfister"; 641 | case Icon.BRAND_PRINCIPE: return "principe"; 642 | case Icon.BRAND_PROGEN: return "progen"; 643 | case Icon.BRAND_SCHYSTER: return "schyster"; 644 | case Icon.BRAND_SHITZU: return "shitzu"; 645 | case Icon.BRAND_SPEEDOPHILE: return "speedophile"; 646 | case Icon.BRAND_STANLEY: return "stanley"; 647 | case Icon.BRAND_TRUFFADE: return "truffade"; 648 | case Icon.BRAND_UBERMACHT: return "ubermacht"; 649 | case Icon.BRAND_VAPID: return "vapid"; 650 | case Icon.BRAND_VULCAR: return "vulcar"; 651 | case Icon.BRAND_WEENY: return "weeny"; 652 | case Icon.BRAND_WESTERN: return "western"; 653 | case Icon.BRAND_WESTERNMOTORCYCLE: return "westernmotorcycle"; 654 | case Icon.BRAND_WILLARD: return "willard"; 655 | case Icon.BRAND_ZIRCONIUM: return "zirconium"; 656 | case Icon.BRAND_GROTTI2: return "grotti_2"; 657 | case Icon.BRAND_LCC: return "lcc"; 658 | case Icon.BRAND_PROGEN2: return "progen"; 659 | case Icon.BRAND_RUNE: return "rune"; 660 | case Icon.INFO: return "info_icon_32"; 661 | default: 662 | break; 663 | #endif 664 | #if REDM 665 | case Icon.LOCK: 666 | return "MENU_ICON_LOCK"; 667 | case Icon.TICK: 668 | return "MENU_ICON_TICK"; 669 | case Icon.CIRCLE: 670 | return "MENU_ICON_CIRCLE"; 671 | case Icon.SADDLE: 672 | return "MENU_ICON_ON_HORSE"; 673 | case Icon.STAR: 674 | return "MENU_ICON_INFO_NEW"; 675 | case Icon.ARROW_LEFT: 676 | return "SELECTION_ARROW_LEFT"; 677 | case Icon.ARROW_RIGHT: 678 | return "SELECTION_ARROW_RIGHT"; 679 | case Icon.INVITE_SENT: 680 | return "MENU_ICON_INVITE_SENT"; 681 | case Icon.SELECTION_BOX: 682 | return "SELECTION_BOX_SQUARE"; 683 | #endif 684 | } 685 | return ""; 686 | } 687 | 688 | /// 689 | /// Get the sprite size for the given icon. 690 | /// 691 | /// 692 | /// 693 | /// 694 | protected float GetSpriteSize(Icon icon, bool width) 695 | { 696 | switch (icon) 697 | { 698 | #if FIVEM 699 | case Icon.CASH: 700 | case Icon.COKE: 701 | case Icon.CROWN: 702 | case Icon.HEROIN: 703 | case Icon.METH: 704 | case Icon.WEED: 705 | case Icon.ADVERSARY: 706 | case Icon.BASE_JUMPING: 707 | case Icon.BRIEFCASE: 708 | case Icon.MISSION_STAR: 709 | case Icon.DEATHMATCH: 710 | case Icon.CASTLE: 711 | case Icon.TROPHY: 712 | case Icon.RACE_FLAG: 713 | case Icon.RACE_FLAG_PLANE: 714 | case Icon.RACE_FLAG_BICYCLE: 715 | case Icon.RACE_FLAG_PERSON: 716 | case Icon.RACE_FLAG_CAR: 717 | case Icon.RACE_FLAG_BOAT_ANCHOR: 718 | case Icon.ROCKSTAR: 719 | case Icon.STUNT: 720 | case Icon.STUNT_PREMIUM: 721 | case Icon.RACE_FLAG_STUNT_JUMP: 722 | case Icon.SHIELD: 723 | case Icon.TEAM_DEATHMATCH: 724 | case Icon.VEHICLE_DEATHMATCH: 725 | case Icon.AUDIO_MUTE: 726 | case Icon.AUDIO_INACTIVE: 727 | case Icon.AUDIO_VOL1: 728 | case Icon.AUDIO_VOL2: 729 | case Icon.AUDIO_VOL3: 730 | case Icon.BRAND_ALBANY: 731 | case Icon.BRAND_ANNIS: 732 | case Icon.BRAND_BANSHEE: 733 | case Icon.BRAND_BENEFACTOR: 734 | case Icon.BRAND_BF: 735 | case Icon.BRAND_BOLLOKAN: 736 | case Icon.BRAND_BRAVADO: 737 | case Icon.BRAND_BRUTE: 738 | case Icon.BRAND_BUCKINGHAM: 739 | case Icon.BRAND_CANIS: 740 | case Icon.BRAND_CHARIOT: 741 | case Icon.BRAND_CHEVAL: 742 | case Icon.BRAND_CLASSIQUE: 743 | case Icon.BRAND_COIL: 744 | case Icon.BRAND_DECLASSE: 745 | case Icon.BRAND_DEWBAUCHEE: 746 | case Icon.BRAND_DILETTANTE: 747 | case Icon.BRAND_DINKA: 748 | case Icon.BRAND_DUNDREARY: 749 | case Icon.BRAND_EMPORER: 750 | case Icon.BRAND_ENUS: 751 | case Icon.BRAND_FATHOM: 752 | case Icon.BRAND_GALIVANTER: 753 | case Icon.BRAND_GROTTI: 754 | case Icon.BRAND_HIJAK: 755 | case Icon.BRAND_HVY: 756 | case Icon.BRAND_IMPONTE: 757 | case Icon.BRAND_INVETERO: 758 | case Icon.BRAND_JACKSHEEPE: 759 | case Icon.BRAND_JOBUILT: 760 | case Icon.BRAND_KARIN: 761 | case Icon.BRAND_LAMPADATI: 762 | case Icon.BRAND_MAIBATSU: 763 | case Icon.BRAND_MAMMOTH: 764 | case Icon.BRAND_MTL: 765 | case Icon.BRAND_NAGASAKI: 766 | case Icon.BRAND_OBEY: 767 | case Icon.BRAND_OCELOT: 768 | case Icon.BRAND_OVERFLOD: 769 | case Icon.BRAND_PED: 770 | case Icon.BRAND_PEGASSI: 771 | case Icon.BRAND_PFISTER: 772 | case Icon.BRAND_PRINCIPE: 773 | case Icon.BRAND_PROGEN: 774 | case Icon.BRAND_SCHYSTER: 775 | case Icon.BRAND_SHITZU: 776 | case Icon.BRAND_SPEEDOPHILE: 777 | case Icon.BRAND_STANLEY: 778 | case Icon.BRAND_TRUFFADE: 779 | case Icon.BRAND_UBERMACHT: 780 | case Icon.BRAND_VAPID: 781 | case Icon.BRAND_VULCAR: 782 | case Icon.BRAND_WEENY: 783 | case Icon.BRAND_WESTERN: 784 | case Icon.BRAND_WESTERNMOTORCYCLE: 785 | case Icon.BRAND_WILLARD: 786 | case Icon.BRAND_ZIRCONIUM: 787 | case Icon.BRAND_GROTTI2: 788 | case Icon.BRAND_LCC: 789 | case Icon.BRAND_PROGEN2: 790 | case Icon.BRAND_RUNE: 791 | case Icon.COUNTRY_USA: 792 | case Icon.COUNTRY_UK: 793 | case Icon.COUNTRY_SWEDEN: 794 | case Icon.COUNTRY_KOREA: 795 | case Icon.COUNTRY_JAPAN: 796 | case Icon.COUNTRY_ITALY: 797 | case Icon.COUNTRY_GERMANY: 798 | case Icon.COUNTRY_FRANCE: 799 | return 30f / (width ? MenuController.ScreenWidth : MenuController.ScreenHeight); 800 | case Icon.STAR: 801 | case Icon.LOCK_ARENA: 802 | return 52f / (width ? MenuController.ScreenWidth : MenuController.ScreenHeight); 803 | case Icon.MEDAL_SILVER: 804 | case Icon.MP_AMMO_PICKUP: 805 | case Icon.MP_AMMO: 806 | case Icon.MP_CASH: 807 | case Icon.MP_RP: 808 | case Icon.GLOBE_WHITE: 809 | case Icon.GLOBE_BLUE: 810 | case Icon.GLOBE_GREEN: 811 | case Icon.GLOBE_ORANGE: 812 | case Icon.GLOBE_RED: 813 | case Icon.GLOBE_YELLOW: 814 | case Icon.INV_ARM_WRESTLING: 815 | case Icon.INV_BASEJUMP: 816 | case Icon.INV_MISSION: 817 | case Icon.INV_DARTS: 818 | case Icon.INV_DEATHMATCH: 819 | case Icon.INV_DRUG: 820 | case Icon.INV_CASTLE: 821 | case Icon.INV_GOLF: 822 | case Icon.INV_BIKE: 823 | case Icon.INV_BOAT: 824 | case Icon.INV_ANCHOR: 825 | case Icon.INV_CAR: 826 | case Icon.INV_DOLLAR: 827 | case Icon.INV_COKE: 828 | case Icon.INV_KEY: 829 | case Icon.INV_DATA: 830 | case Icon.INV_HELI: 831 | case Icon.INV_HEORIN: 832 | case Icon.INV_KEYCARD: 833 | case Icon.INV_METH: 834 | case Icon.INV_BRIEFCASE: 835 | case Icon.INV_LINK: 836 | case Icon.INV_PERSON: 837 | case Icon.INV_PLANE: 838 | case Icon.INV_PLANE2: 839 | case Icon.INV_QUESTIONMARK: 840 | case Icon.INV_REMOTE: 841 | case Icon.INV_SAFE: 842 | case Icon.INV_STEER_WHEEL: 843 | case Icon.INV_WEAPON: 844 | case Icon.INV_WEED: 845 | case Icon.INV_RACE_FLAG_PLANE: 846 | case Icon.INV_RACE_FLAG_BICYCLE: 847 | case Icon.INV_RACE_FLAG_BOAT_ANCHOR: 848 | case Icon.INV_RACE_FLAG_PERSON: 849 | case Icon.INV_RACE_FLAG_CAR: 850 | case Icon.INV_RACE_FLAG_HELMET: 851 | case Icon.INV_SHOOTING_RANGE: 852 | case Icon.INV_SURVIVAL: 853 | case Icon.INV_TEAM_DEATHMATCH: 854 | case Icon.INV_TENNIS: 855 | case Icon.INV_VEHICLE_DEATHMATCH: 856 | return 22f / (width ? MenuController.ScreenWidth : MenuController.ScreenHeight); 857 | default: 858 | return 38f / (width ? MenuController.ScreenWidth : MenuController.ScreenHeight); 859 | #endif 860 | #if REDM 861 | case Icon.TICK: 862 | return width ? (16f / MenuController.ScreenWidth) : (24f / MenuController.ScreenHeight); 863 | case Icon.CIRCLE: 864 | return width ? (16f / MenuController.ScreenWidth) : (26f / MenuController.ScreenHeight); 865 | case Icon.SADDLE: 866 | return width ? (16f / MenuController.ScreenWidth) : (24f / MenuController.ScreenHeight); 867 | case Icon.STAR: 868 | case Icon.ARROW_LEFT: 869 | case Icon.ARROW_RIGHT: 870 | return width ? (14f / MenuController.ScreenWidth) : (22f / MenuController.ScreenHeight); 871 | case Icon.SELECTION_BOX: 872 | case Icon.INVITE_SENT: 873 | return width ? (16f / MenuController.ScreenWidth) : (24f / MenuController.ScreenHeight); 874 | case Icon.LOCK: 875 | default: 876 | return width ? (24f / MenuController.ScreenWidth) : (30f / MenuController.ScreenHeight); 877 | #endif 878 | } 879 | } 880 | 881 | /// 882 | /// Get the sprite color for the provided icon depending on the current state of the item (Enabled & selected values). 883 | /// 884 | /// 885 | /// 886 | /// 887 | protected int[] GetSpriteColour(Icon icon, bool selected) 888 | { 889 | switch (icon) 890 | { 891 | #if FIVEM 892 | case Icon.CROWN: 893 | case Icon.TICK: 894 | case Icon.MALE: 895 | case Icon.FEMALE: 896 | case Icon.LOCK: 897 | case Icon.LOCK_ARENA: 898 | case Icon.ADVERSARY: 899 | case Icon.BASE_JUMPING: 900 | case Icon.BRIEFCASE: 901 | case Icon.MISSION_STAR: 902 | case Icon.DEATHMATCH: 903 | case Icon.CASTLE: 904 | case Icon.TROPHY: 905 | case Icon.RACE_FLAG: 906 | case Icon.RACE_FLAG_PLANE: 907 | case Icon.RACE_FLAG_BICYCLE: 908 | case Icon.RACE_FLAG_PERSON: 909 | case Icon.RACE_FLAG_CAR: 910 | case Icon.RACE_FLAG_BOAT_ANCHOR: 911 | case Icon.ROCKSTAR: 912 | case Icon.STUNT: 913 | case Icon.STUNT_PREMIUM: 914 | case Icon.RACE_FLAG_STUNT_JUMP: 915 | case Icon.SHIELD: 916 | case Icon.TEAM_DEATHMATCH: 917 | case Icon.VEHICLE_DEATHMATCH: 918 | case Icon.MP_SPECTATING: 919 | case Icon.GLOBE_WHITE: 920 | case Icon.AUDIO_MUTE: 921 | case Icon.AUDIO_INACTIVE: 922 | case Icon.AUDIO_VOL1: 923 | case Icon.AUDIO_VOL2: 924 | case Icon.AUDIO_VOL3: 925 | case Icon.BRAND_ALBANY: 926 | case Icon.BRAND_ANNIS: 927 | case Icon.BRAND_BANSHEE: 928 | case Icon.BRAND_BENEFACTOR: 929 | case Icon.BRAND_BF: 930 | case Icon.BRAND_BOLLOKAN: 931 | case Icon.BRAND_BRAVADO: 932 | case Icon.BRAND_BRUTE: 933 | case Icon.BRAND_BUCKINGHAM: 934 | case Icon.BRAND_CANIS: 935 | case Icon.BRAND_CHARIOT: 936 | case Icon.BRAND_CHEVAL: 937 | case Icon.BRAND_CLASSIQUE: 938 | case Icon.BRAND_COIL: 939 | case Icon.BRAND_DECLASSE: 940 | case Icon.BRAND_DEWBAUCHEE: 941 | case Icon.BRAND_DILETTANTE: 942 | case Icon.BRAND_DINKA: 943 | case Icon.BRAND_DUNDREARY: 944 | case Icon.BRAND_EMPORER: 945 | case Icon.BRAND_ENUS: 946 | case Icon.BRAND_FATHOM: 947 | case Icon.BRAND_GALIVANTER: 948 | case Icon.BRAND_GROTTI: 949 | case Icon.BRAND_HIJAK: 950 | case Icon.BRAND_HVY: 951 | case Icon.BRAND_IMPONTE: 952 | case Icon.BRAND_INVETERO: 953 | case Icon.BRAND_JACKSHEEPE: 954 | case Icon.BRAND_JOBUILT: 955 | case Icon.BRAND_KARIN: 956 | case Icon.BRAND_LAMPADATI: 957 | case Icon.BRAND_MAIBATSU: 958 | case Icon.BRAND_MAMMOTH: 959 | case Icon.BRAND_MTL: 960 | case Icon.BRAND_NAGASAKI: 961 | case Icon.BRAND_OBEY: 962 | case Icon.BRAND_OCELOT: 963 | case Icon.BRAND_OVERFLOD: 964 | case Icon.BRAND_PED: 965 | case Icon.BRAND_PEGASSI: 966 | case Icon.BRAND_PFISTER: 967 | case Icon.BRAND_PRINCIPE: 968 | case Icon.BRAND_PROGEN: 969 | case Icon.BRAND_SCHYSTER: 970 | case Icon.BRAND_SHITZU: 971 | case Icon.BRAND_SPEEDOPHILE: 972 | case Icon.BRAND_STANLEY: 973 | case Icon.BRAND_TRUFFADE: 974 | case Icon.BRAND_UBERMACHT: 975 | case Icon.BRAND_VAPID: 976 | case Icon.BRAND_VULCAR: 977 | case Icon.BRAND_WEENY: 978 | case Icon.BRAND_WESTERN: 979 | case Icon.BRAND_WESTERNMOTORCYCLE: 980 | case Icon.BRAND_WILLARD: 981 | case Icon.BRAND_ZIRCONIUM: 982 | case Icon.BRAND_GROTTI2: 983 | case Icon.BRAND_LCC: 984 | case Icon.BRAND_PROGEN2: 985 | case Icon.BRAND_RUNE: 986 | return selected ? (Enabled ? new int[3] { 0, 0, 0 } : new int[3] { 50, 50, 50 }) : (Enabled ? new int[3] { 255, 255, 255 } : new int[3] { 109, 109, 109 }); 987 | case Icon.GLOBE_BLUE: 988 | return Enabled ? new int[3] { 10, 103, 166 } : new int[3] { 11, 62, 117 }; 989 | case Icon.GLOBE_GREEN: 990 | return Enabled ? new int[3] { 10, 166, 85 } : new int[3] { 5, 71, 22 }; 991 | case Icon.GLOBE_ORANGE: 992 | return Enabled ? new int[3] { 232, 145, 14 } : new int[3] { 133, 77, 12 }; 993 | case Icon.GLOBE_RED: 994 | return Enabled ? new int[3] { 207, 43, 31 } : new int[3] { 110, 7, 7 }; 995 | case Icon.GLOBE_YELLOW: 996 | return Enabled ? new int[3] { 232, 207, 14 } : new int[3] { 131, 133, 12 }; 997 | default: 998 | return Enabled ? new int[3] { 255, 255, 255 } : new int[3] { 109, 109, 109 }; 999 | #endif 1000 | #if REDM 1001 | case Icon.STAR: 1002 | return !Enabled ? new int[3] { 163, 106, 5 } : new int[3] { 237, 154, 9 }; 1003 | default: 1004 | return Enabled ? new int[3] { 255, 255, 255 } : new int[3] { 109, 109, 109 }; 1005 | //return new int[3] { 255, 255, 255 }; 1006 | #endif 1007 | } 1008 | } 1009 | 1010 | /// 1011 | /// Get the sprite x position offset for the provided icon and alignment variables. 1012 | /// 1013 | /// 1014 | /// 1015 | /// 1016 | /// 1017 | protected float GetSpriteX(Icon icon, bool leftAligned, bool leftSide) 1018 | { 1019 | #if FIVEM 1020 | if (icon == Icon.NONE) 1021 | { 1022 | return 0f; 1023 | } 1024 | if (leftSide) 1025 | { 1026 | if (leftAligned) 1027 | { 1028 | return 20f / MenuController.ScreenWidth; 1029 | } 1030 | else 1031 | { 1032 | return GetSafeZoneSize() - ((Width - 20f) / MenuController.ScreenWidth); 1033 | } 1034 | } 1035 | else 1036 | { 1037 | if (leftAligned) 1038 | { 1039 | return (Width - 20f) / MenuController.ScreenWidth; 1040 | } 1041 | else 1042 | { 1043 | return GetSafeZoneSize() - (20f / MenuController.ScreenWidth); 1044 | } 1045 | } 1046 | #endif 1047 | #if REDM 1048 | if (leftSide) 1049 | { 1050 | return 30f / MenuController.ScreenWidth; 1051 | } 1052 | return (Width - 30f) / MenuController.ScreenWidth; 1053 | #endif 1054 | } 1055 | 1056 | /// 1057 | /// Handles menu navigation to the right for items that support it, otherwise the item will be selected. 1058 | /// 1059 | internal virtual void GoRight() 1060 | { 1061 | if (Enabled) 1062 | { 1063 | ParentMenu.SelectItem(this); 1064 | } 1065 | } 1066 | 1067 | /// 1068 | /// Handles menu navigation to the left for items that support it, otherwise the menu will navigate to the parent menu. 1069 | /// 1070 | internal virtual void GoLeft() 1071 | { 1072 | if (MenuController.NavigateMenuUsingArrows && !MenuController.DisableBackButton && !(MenuController.PreventExitingMenu && ParentMenu == null)) 1073 | { 1074 | ParentMenu.GoBack(); 1075 | } 1076 | } 1077 | 1078 | /// 1079 | /// Handles item selection. 1080 | /// 1081 | internal virtual void Select() 1082 | { 1083 | ParentMenu.ItemSelectedEvent(this, Index); 1084 | } 1085 | 1086 | /// 1087 | /// Draws the item on the screen. 1088 | /// 1089 | internal virtual void Draw(int indexOffset) 1090 | { 1091 | if (ParentMenu == null) 1092 | { 1093 | return; 1094 | } 1095 | 1096 | int font = 0; 1097 | float textSize = (14f * 27f) / MenuController.ScreenHeight; 1098 | int textColor = Selected ? (Enabled ? 0 : 50) : (Enabled ? 255 : 109); 1099 | 1100 | float yOffset = ParentMenu.MenuItemsYOffset + 1f - (RowHeight * MathUtil.Clamp(ParentMenu.Size, 0, ParentMenu.MaxItemsOnScreen)); 1101 | float textXOffset = 0f; 1102 | float rightTextIconOffset = 0f; 1103 | 1104 | DrawBackground(indexOffset, yOffset, out float x, out float y); 1105 | 1106 | float textMinX = (textXOffset / MenuController.ScreenWidth) + (10f / MenuController.ScreenWidth); 1107 | float textMaxX = (Width - 10f) / MenuController.ScreenWidth; 1108 | float textY = y - ((30f / 2f) / MenuController.ScreenHeight); 1109 | 1110 | textXOffset = DrawLeftIcon(textXOffset, y); 1111 | rightTextIconOffset = DrawRightIcon(rightTextIconOffset, y); 1112 | #if FIVEM 1113 | DrawLabelText(textXOffset, rightTextIconOffset, y, font, textSize, textColor, textY); 1114 | #endif 1115 | DrawItemText(font, textSize, textColor, textMinX, textMaxX, textY, textXOffset, y); 1116 | } 1117 | 1118 | /// 1119 | /// Drwa the item text 1120 | /// 1121 | /// 1122 | /// 1123 | /// 1124 | /// 1125 | /// 1126 | /// 1127 | /// 1128 | /// 1129 | private void DrawItemText(int font, float textSize, int textColor, float textMinX, float textMaxX, float textY, float textXOffset, float y) 1130 | { 1131 | #if FIVEM 1132 | SetScriptGfxAlign(76, 84); 1133 | SetScriptGfxAlignParams(0f, 0f, 0f, 0f); 1134 | SetTextFont(font); 1135 | SetTextScale(textSize, textSize); 1136 | SetTextJustification(1); 1137 | BeginTextCommandDisplayText("STRING"); 1138 | AddTextComponentSubstringPlayerName(Text ?? "N/A"); 1139 | if (Selected || !Enabled) 1140 | { 1141 | SetTextColour(textColor, textColor, textColor, 255); 1142 | } 1143 | if (ParentMenu.LeftAligned) 1144 | { 1145 | SetTextWrap(textMinX, textMaxX); 1146 | EndTextCommandDisplayText(textMinX, textY); 1147 | } 1148 | else 1149 | { 1150 | textMinX = (textXOffset / MenuController.ScreenWidth) + GetSafeZoneSize() - ((Width - 10f) / MenuController.ScreenWidth); 1151 | textMaxX = GetSafeZoneSize() - (10f / MenuController.ScreenWidth); 1152 | SetTextWrap(textMinX, textMaxX); 1153 | EndTextCommandDisplayText(textMinX, textY); 1154 | } 1155 | ResetScriptGfxAlign(); 1156 | #endif 1157 | #if REDM 1158 | SetTextScale(textSize, textSize); 1159 | textColor = Enabled ? 255 : 109; 1160 | SetTextColor(textColor, textColor, textColor, 255); 1161 | textMinX = ((8f + textXOffset) / MenuController.ScreenWidth) + (10f / MenuController.ScreenWidth); 1162 | textMaxX = (Width - 10f) / MenuController.ScreenWidth; 1163 | textY = y - ((30f / 2f) / MenuController.ScreenHeight); 1164 | font = 23; 1165 | // Cfx native, undocumented. 1166 | Call((CitizenFX.Core.Native.Hash)0xADA9255D, font); 1167 | // API version has incorrect parameter types. 1168 | long _text = Call(_CREATE_VAR_STRING, 10, "LITERAL_STRING", (Text ?? "N/A") + (" " + Label ?? "")); 1169 | DisplayText(_text, textMinX, textY); 1170 | #endif 1171 | } 1172 | 1173 | #if FIVEM 1174 | /// 1175 | /// Draw the item label text if it exists. 1176 | /// 1177 | /// 1178 | /// 1179 | /// 1180 | /// 1181 | /// 1182 | /// 1183 | /// 1184 | private void DrawLabelText(float textXOffset, float rightTextIconOffset, float y, int font, float textSize, int textColor, float textY) 1185 | { 1186 | if (string.IsNullOrEmpty(Label)) 1187 | { 1188 | return; 1189 | } 1190 | SetScriptGfxAlign(76, 84); 1191 | SetScriptGfxAlignParams(0f, 0f, 0f, 0f); 1192 | 1193 | BeginTextCommandDisplayText("STRING"); 1194 | SetTextFont(font); 1195 | SetTextScale(textSize, textSize); 1196 | SetTextJustification(2); 1197 | AddTextComponentSubstringPlayerName(Label); 1198 | if (Selected || !Enabled) 1199 | { 1200 | SetTextColour(textColor, textColor, textColor, 255); 1201 | } 1202 | if (ParentMenu.LeftAligned) 1203 | { 1204 | SetTextWrap(0f, ((490f - rightTextIconOffset) / MenuController.ScreenWidth)); 1205 | EndTextCommandDisplayText((10f + rightTextIconOffset) / MenuController.ScreenWidth, textY); 1206 | } 1207 | else 1208 | { 1209 | SetTextWrap(0f, GetSafeZoneSize() - ((10f + rightTextIconOffset) / MenuController.ScreenWidth)); 1210 | EndTextCommandDisplayText(0f, textY); 1211 | } 1212 | ResetScriptGfxAlign(); 1213 | } 1214 | #endif 1215 | 1216 | /// 1217 | /// Draw the right icon if it exists. 1218 | /// 1219 | /// 1220 | /// 1221 | /// 1222 | private float DrawRightIcon(float rightTextIconOffset, float y) 1223 | { 1224 | if (RightIcon == Icon.NONE) 1225 | { 1226 | return rightTextIconOffset; 1227 | } 1228 | #if FIVEM 1229 | rightTextIconOffset = 25f; 1230 | 1231 | SetScriptGfxAlign(76, 84); 1232 | SetScriptGfxAlignParams(0f, 0f, 0f, 0f); 1233 | string name = GetSpriteName(RightIcon, Selected); 1234 | float spriteY = y; 1235 | float spriteX = GetSpriteX(RightIcon, ParentMenu.LeftAligned, false); 1236 | float spriteHeight = GetSpriteSize(RightIcon, false); 1237 | float spriteWidth = GetSpriteSize(RightIcon, true); 1238 | int[] spriteColor = GetSpriteColour(RightIcon, Selected); 1239 | string textureDictionary = GetSpriteDictionary(RightIcon); 1240 | DrawSprite(textureDictionary, name, spriteX, spriteY, spriteWidth, spriteHeight, 0f, spriteColor[0], spriteColor[1], spriteColor[2], 255); 1241 | ResetScriptGfxAlign(); 1242 | #endif 1243 | #if REDM 1244 | string spriteName = GetSpriteName(RightIcon, Selected); 1245 | string spriteDict = GetSpriteDictionary(RightIcon); 1246 | float spriteX = GetSpriteX(RightIcon, true, false); 1247 | float spriteY = y; 1248 | float spriteHeight = GetSpriteSize(RightIcon, false); 1249 | float spriteWidth = GetSpriteSize(RightIcon, true); 1250 | int[] spriteColor = GetSpriteColour(RightIcon, Selected); 1251 | DrawSprite(spriteDict, spriteName, spriteX, spriteY, spriteWidth, spriteHeight, 0f, spriteColor[0], spriteColor[1], spriteColor[2], 255, false); 1252 | #endif 1253 | return rightTextIconOffset; 1254 | } 1255 | 1256 | /// 1257 | /// Draw the left icon if it exists. 1258 | /// 1259 | /// 1260 | /// 1261 | /// 1262 | private float DrawLeftIcon(float textXOffset, float y) 1263 | { 1264 | if (LeftIcon == Icon.NONE) 1265 | { 1266 | return textXOffset; 1267 | } 1268 | textXOffset = 25f; 1269 | #if FIVEM 1270 | SetScriptGfxAlign(76, 84); 1271 | SetScriptGfxAlignParams(0f, 0f, 0f, 0f); 1272 | 1273 | string name = GetSpriteName(LeftIcon, Selected); 1274 | float spriteY = y; 1275 | float spriteX = GetSpriteX(LeftIcon, ParentMenu.LeftAligned, true); 1276 | float spriteHeight = GetSpriteSize(LeftIcon, false); 1277 | float spriteWidth = GetSpriteSize(LeftIcon, true); 1278 | int[] spriteColor = GetSpriteColour(LeftIcon, Selected); 1279 | string textureDictionary = GetSpriteDictionary(LeftIcon); 1280 | 1281 | DrawSprite(textureDictionary, name, spriteX, spriteY, spriteWidth, spriteHeight, 0f, spriteColor[0], spriteColor[1], spriteColor[2], 255); 1282 | ResetScriptGfxAlign(); 1283 | #endif 1284 | #if REDM 1285 | string spriteName = GetSpriteName(LeftIcon, Selected); 1286 | string spriteDict = GetSpriteDictionary(LeftIcon); 1287 | float spriteX = GetSpriteX(LeftIcon, true, true); 1288 | float spriteY = y; 1289 | float spriteHeight = GetSpriteSize(LeftIcon, false); 1290 | float spriteWidth = GetSpriteSize(LeftIcon, true); 1291 | int[] spriteColor = GetSpriteColour(LeftIcon, Selected); 1292 | DrawSprite(spriteDict, spriteName, spriteX, spriteY, spriteWidth, spriteHeight, 0f, spriteColor[0], spriteColor[1], spriteColor[2], 255, false); 1293 | #endif 1294 | return textXOffset; 1295 | } 1296 | 1297 | /// 1298 | /// Draws the background for the menu item if it is selected and output the x/y values for this item. 1299 | /// 1300 | /// 1301 | /// 1302 | /// 1303 | /// 1304 | private void DrawBackground(int indexOffset, float yOffset, out float x, out float y) 1305 | { 1306 | x = (ParentMenu.Position.Key + (Width / 2f)) / MenuController.ScreenWidth; 1307 | y = (ParentMenu.Position.Value + ((Index - indexOffset) * RowHeight) + (20f) + yOffset) / MenuController.ScreenHeight; 1308 | 1309 | if (Selected) 1310 | { 1311 | float width = Width / MenuController.ScreenWidth; 1312 | float height = (RowHeight) / MenuController.ScreenHeight; 1313 | #if FIVEM 1314 | SetScriptGfxAlign(ParentMenu.LeftAligned ? 76 : 82, 84); 1315 | SetScriptGfxAlignParams(0f, 0f, 0f, 0f); 1316 | DrawRect(x, y, width, height, 255, 255, 255, 225); 1317 | ResetScriptGfxAlign(); 1318 | #endif 1319 | #if REDM 1320 | DrawSprite(MenuController._texture_dict, MenuController._header_texture, x, y, width, height, 0f, 181, 17, 18, 255, false); 1321 | #endif 1322 | } 1323 | #if FIVEM 1324 | #endif 1325 | } 1326 | } 1327 | } 1328 | -------------------------------------------------------------------------------- /MenuAPI/items/MenuListItem.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using static CitizenFX.Core.Native.Function; 3 | using static CitizenFX.Core.Native.API; 4 | 5 | namespace MenuAPI 6 | { 7 | public class MenuListItem : MenuItem 8 | { 9 | public int ListIndex { get; set; } = 0; 10 | public List ListItems { get; set; } = new List(); 11 | public bool HideArrowsWhenNotSelected { get; set; } = false; 12 | public bool ShowOpacityPanel { get; set; } = false; 13 | public bool ShowColorPanel { get; set; } = false; 14 | public ColorPanelType ColorPanelColorType = ColorPanelType.Hair; 15 | public enum ColorPanelType 16 | { 17 | Hair, 18 | Makeup 19 | } 20 | public int ItemsCount => ListItems.Count; 21 | 22 | public string GetCurrentSelection() 23 | { 24 | if (ItemsCount > 0 && ListIndex >= 0 && ListIndex < ItemsCount) 25 | { 26 | return ListItems[ListIndex]; 27 | } 28 | return null; 29 | } 30 | 31 | public MenuListItem(string text, List items, int index) : this(text, items, index, null) { } 32 | public MenuListItem(string text, List items, int index, string description) : base(text, description) 33 | { 34 | ListItems = items; 35 | ListIndex = index; 36 | } 37 | 38 | internal override void Draw(int indexOffset) 39 | { 40 | if (ItemsCount < 1) 41 | { 42 | // Add a dummy item to prevent the other while loops from freezing the game. 43 | ListItems.Add("N/A"); 44 | } 45 | 46 | while (ListIndex < 0) 47 | { 48 | ListIndex += ItemsCount; 49 | } 50 | 51 | while (ListIndex >= ItemsCount) 52 | { 53 | ListIndex -= ItemsCount; 54 | } 55 | 56 | if (HideArrowsWhenNotSelected && !Selected) 57 | { 58 | Label = GetCurrentSelection() ?? "~r~N/A"; 59 | } 60 | else 61 | { 62 | Label = $"~s~← {GetCurrentSelection() ?? "~r~N/A~s~"} ~s~→"; 63 | } 64 | 65 | base.Draw(indexOffset); 66 | } 67 | 68 | internal override void GoRight() 69 | { 70 | if (ItemsCount > 0) 71 | { 72 | int oldIndex = ListIndex; 73 | int newIndex = oldIndex; 74 | if (ListIndex >= ItemsCount - 1) 75 | { 76 | newIndex = 0; 77 | } 78 | else 79 | { 80 | newIndex++; 81 | } 82 | ListIndex = newIndex; 83 | ParentMenu.ListItemIndexChangeEvent(ParentMenu, this, oldIndex, newIndex, Index); 84 | #if FIVEM 85 | PlaySoundFrontend(-1, "NAV_LEFT_RIGHT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false); 86 | #endif 87 | #if REDM 88 | // Has invalid parameter types in API. 89 | Call((CitizenFX.Core.Native.Hash)0xCE5D0FFE83939AF1, -1, "NAV_RIGHT", "HUD_SHOP_SOUNDSET", 1); 90 | #endif 91 | } 92 | } 93 | 94 | internal override void GoLeft() 95 | { 96 | if (ItemsCount > 0) 97 | { 98 | int oldIndex = ListIndex; 99 | int newIndex = oldIndex; 100 | if (ListIndex < 1) 101 | { 102 | newIndex = ItemsCount - 1; 103 | } 104 | else 105 | { 106 | newIndex--; 107 | } 108 | ListIndex = newIndex; 109 | 110 | ParentMenu.ListItemIndexChangeEvent(ParentMenu, this, oldIndex, newIndex, Index); 111 | #if FIVEM 112 | PlaySoundFrontend(-1, "NAV_LEFT_RIGHT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false); 113 | #endif 114 | #if REDM 115 | // Has invalid parameter types in API. 116 | Call((CitizenFX.Core.Native.Hash)0xCE5D0FFE83939AF1, -1, "NAV_LEFT", "HUD_SHOP_SOUNDSET", 1); 117 | #endif 118 | } 119 | } 120 | 121 | internal override void Select() 122 | { 123 | ParentMenu.ListItemSelectEvent(ParentMenu, this, ListIndex, Index); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /MenuAPI/items/MenuSliderItem.cs: -------------------------------------------------------------------------------- 1 | #if FIVEM 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using CitizenFX.Core; 8 | using static CitizenFX.Core.Native.API; 9 | 10 | namespace MenuAPI 11 | { 12 | public class MenuSliderItem : MenuItem 13 | { 14 | public int Min { get; private set; } = 0; 15 | public int Max { get; private set; } = 10; 16 | public bool ShowDivider { get; set; } 17 | public int Position { get; set; } = 0; 18 | 19 | public Icon SliderLeftIcon { get; set; } = Icon.NONE; 20 | public Icon SliderRightIcon { get; set; } = Icon.NONE; 21 | 22 | public System.Drawing.Color BackgroundColor { get; set; } = System.Drawing.Color.FromArgb(255, 24, 93, 151); 23 | public System.Drawing.Color BarColor { get; set; } = System.Drawing.Color.FromArgb(255, 53, 165, 223); 24 | 25 | public MenuSliderItem(string name, int min, int max, int startPosition) : this(name, min, max, startPosition, false) { } 26 | public MenuSliderItem(string name, int min, int max, int startPosition, bool showDivider) : this(name, null, min, max, startPosition, showDivider) { } 27 | public MenuSliderItem(string name, string description, int min, int max, int startPosition) : this(name, description, min, max, startPosition, false) { } 28 | public MenuSliderItem(string name, string description, int min, int max, int startPosition, bool showDivider) : base(name, description) 29 | { 30 | Min = min; 31 | Max = max; 32 | ShowDivider = showDivider; 33 | Position = startPosition; 34 | } 35 | 36 | /// 37 | /// Maps ' ' to be a value between ' ' and ' '. 38 | /// 39 | /// 40 | /// 41 | /// 42 | /// 43 | /// 44 | /// 45 | private float Map(float val, float in_min, float in_max, float out_min, float out_max) 46 | { 47 | return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; 48 | } 49 | 50 | internal override void Draw(int indexOffset) 51 | { 52 | RightIcon = SliderRightIcon; 53 | Label = null; 54 | 55 | base.Draw(indexOffset); 56 | 57 | if (Position > Max || Position < Min) 58 | { 59 | Position = (Max - Min) / 2; 60 | } 61 | 62 | float yOffset = ParentMenu.MenuItemsYOffset + 1f - (RowHeight * MathUtil.Clamp(ParentMenu.Size, 0, ParentMenu.MaxItemsOnScreen)); 63 | 64 | float width = 150f / MenuController.ScreenWidth; 65 | float height = 10f / MenuController.ScreenHeight; 66 | float y = (ParentMenu.Position.Value + ((Index - indexOffset) * RowHeight) + (20f) + yOffset) / MenuController.ScreenHeight; 67 | float x = (ParentMenu.Position.Key + (Width)) / MenuController.ScreenWidth - (width / 2f) - (8f / MenuController.ScreenWidth); 68 | if (!ParentMenu.LeftAligned) 69 | { 70 | x = (width / 2f) - (8f / MenuController.ScreenWidth); 71 | } 72 | 73 | if (SliderLeftIcon != Icon.NONE && SliderRightIcon != Icon.NONE) 74 | { 75 | x -= 40f / MenuController.ScreenWidth; 76 | 77 | var leftColor = GetSpriteColour(SliderLeftIcon, Selected); 78 | 79 | SetScriptGfxAlign(ParentMenu.LeftAligned ? 76 : 82, 84); 80 | SetScriptGfxAlignParams(0f, 0f, 0f, 0f); 81 | 82 | string textureDictionary = GetSpriteDictionary(SliderLeftIcon); 83 | 84 | if (ParentMenu.LeftAligned) 85 | { 86 | // left sprite left aligned. 87 | DrawSprite(textureDictionary, GetSpriteName(SliderLeftIcon, Selected), x - (width / 2f + (4f / MenuController.ScreenWidth)) - (GetSpriteSize(SliderLeftIcon, true) / 2f), y, GetSpriteSize(SliderLeftIcon, true), GetSpriteSize(SliderLeftIcon, false), 0f, leftColor[0], leftColor[1], leftColor[2], 255); 88 | 89 | // right sprite is managed by the regular function in MenuItem that handles the right icon. 90 | } 91 | else 92 | { 93 | // left sprite right aligned. 94 | DrawSprite(textureDictionary, GetSpriteName(SliderLeftIcon, Selected), x - (width + (4f / MenuController.ScreenWidth)) - GetSpriteSize(SliderLeftIcon, true) - (20f / MenuController.ScreenWidth), y, GetSpriteSize(SliderLeftIcon, true), GetSpriteSize(SliderLeftIcon, false), 0f, leftColor[0], leftColor[1], leftColor[2], 255); 95 | 96 | // right sprite is managed by the regular function in MenuItem that handles the right icon. 97 | } 98 | 99 | ResetScriptGfxAlign(); 100 | } 101 | 102 | SetScriptGfxAlign(ParentMenu.LeftAligned ? 76 : 82, 84); 103 | SetScriptGfxAlignParams(0f, 0f, 0f, 0f); 104 | #region drawing background bar and foreground bar 105 | 106 | // background 107 | DrawRect(x, y, width, height, BackgroundColor.R, BackgroundColor.G, BackgroundColor.B, BackgroundColor.A); 108 | 109 | float xOffset = Map( 110 | (float)Position, 111 | (float)Min, 112 | (float)Max, 113 | -((width / 4f) * MenuController.ScreenWidth), 114 | (width / 4f) * MenuController.ScreenWidth 115 | ); 116 | xOffset /= MenuController.ScreenWidth; 117 | 118 | // bar (foreground) 119 | if (!ParentMenu.LeftAligned) 120 | { 121 | DrawRect(x - (width / 2f) + xOffset, y, width / 2f, height, BarColor.R, BarColor.G, BarColor.B, BarColor.A); 122 | } 123 | else 124 | { 125 | DrawRect(x + xOffset, y, width / 2f, height, BarColor.R, BarColor.G, BarColor.B, BarColor.A); 126 | } 127 | 128 | #endregion 129 | 130 | #region drawing divider 131 | if (ShowDivider) 132 | { 133 | if (!ParentMenu.LeftAligned) 134 | { 135 | DrawRect(x - width + (4f / MenuController.ScreenWidth), y, 4f / MenuController.ScreenWidth, RowHeight / MenuController.ScreenHeight / 2f, 255, 255, 255, 255); 136 | } 137 | else 138 | { 139 | DrawRect(x + (2f / MenuController.ScreenWidth), y, 4f / MenuController.ScreenWidth, RowHeight / MenuController.ScreenHeight / 2f, 255, 255, 255, 255); 140 | } 141 | } 142 | #endregion 143 | ResetScriptGfxAlign(); 144 | } 145 | 146 | internal override void GoRight() 147 | { 148 | if (Position < Max) 149 | { 150 | Position++; 151 | ParentMenu.SliderItemChangedEvent(ParentMenu, this, Position - 1, Position, Index); 152 | PlaySoundFrontend(-1, "NAV_LEFT_RIGHT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false); 153 | } 154 | else 155 | { 156 | PlaySoundFrontend(-1, "ERROR", "HUD_FRONTEND_DEFAULT_SOUNDSET", false); 157 | } 158 | } 159 | 160 | internal override void GoLeft() 161 | { 162 | if (Position > Min) 163 | { 164 | Position--; 165 | ParentMenu.SliderItemChangedEvent(ParentMenu, this, Position + 1, Position, Index); 166 | PlaySoundFrontend(-1, "NAV_LEFT_RIGHT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false); 167 | } 168 | else 169 | { 170 | PlaySoundFrontend(-1, "ERROR", "HUD_FRONTEND_DEFAULT_SOUNDSET", false); 171 | } 172 | } 173 | 174 | internal override void Select() 175 | { 176 | ParentMenu.SliderSelectedEvent(ParentMenu, this, Position, Index); 177 | } 178 | } 179 | } 180 | #endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MenuAPI 2 | 3 | **FiveM & RedM C# Menu API** 4 | 5 | [![Discord](https://discordapp.com/api/guilds/285424882534187008/widget.png)](https://vespura.com/discord) [![CodeFactor](https://www.codefactor.io/repository/github/tomgrobbe/menuapi/badge)](https://www.codefactor.io/repository/github/tomgrobbe/menuapi) [![Build status](https://ci.appveyor.com/api/projects/status/8nqoeyg0e9rn10ih/branch/master?svg=true)](https://ci.appveyor.com/project/TomGrobbe/menuapi/branch/master) [![Patreon](https://img.shields.io/badge/donate-Patreon-orange.svg)](https://www.patreon.com/vespura) 6 | 7 | Designed specifically as a replacement of **NativeUI**, MenuAPI features performance improvements, **RedM** _and_ **FiveM** support, improved stability, better features, less bugs, full safezone alignment support for both left and right algined menus (FiveM only) and less (in my opinion) unnecessary features. 8 | 9 | This has been coded from the ground up. Using decompiled scripts from GTA V & RedM to figure out what some undocumented natives were used for. 10 | 11 | ## Installation 12 | 13 | _Note, this is only for resource developers, don't install this on your server manually if you're not making a resource with it._ 14 | 15 | You have 2 options: 16 | 17 | 1. Download the latest release zip and use the correct version (FiveM/RedM) for your resource. Simply include the DLL as a reference in your C# project and add `using MenuAPI;` to each file where you need to use MenuAPI. 18 | 2. Use the NuGet package, which can be found [here](https://www.nuget.org/packages/MenuAPI.FiveM/) for FiveM, and [here](https://www.nuget.org/packages/MenuAPI.RedM/) for RedM. 19 | 20 | After doing either of the above and you're ready to build and publish your resource, add `files {'MenuAPI.dll'}` to your `fxmanifest.lua` file, and make sure that you include the `MenuAPI.dll` file in the folder of your resource. 21 | 22 | ## Documentation 23 | 24 | Limited documentation is available [here](https://docs.vespura.com/mapi). 25 | 26 | Feel like contributing to the documentation? The repository for the documentation site can be found [here](https://github.com/TomGrobbe/MenuAPI-Docs), thanks! 27 | 28 | ## Copyright / License 29 | 30 | Copyright © Tom Grobbe 2018-2021. 31 | 32 | MenuAPI is a free resource, using a custom license. 33 | Conditions are listed below. 34 | 35 | ### You are allowed to 36 | 37 | - Include the pre-built files in your projects, for both commercial and non-commercial use 38 | - Modify this code, feel free to create PR's :) 39 | 40 | ### You are NOT allowed to 41 | 42 | - Sell this code or a modified version of it. 43 | - If you release a paid resource that uses MenuAPI, you are not allowed to include MenuAPI in the resource. You will need to provide a free way for anyone to download the MenuAPI version of your resource. 44 | - Re-release this code without using the Fork feature. 45 | 46 | ### You must 47 | 48 | - Provide appropritate credits when including this in your project. 49 | - State any changes you made if you want to re-release this code. 50 | - Keep this license when editing the source code or using this code in your own projects. 51 | 52 | ### In short 53 | 54 | It's very simple, don't steal my stuff, don't try to take credit for code that isn't yours and don't try to make money using my work. That's all I ask. 55 | 56 | If you'd like to do something that's not allowed per this license, contact me and we might be able to figure something out. 57 | 58 | Nothing is guaranteed to work, I do not take responsibility for any bugs or damages caused by this code. Use at your own risk. 59 | -------------------------------------------------------------------------------- /TestMenu/ExampleMenu.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using MenuAPI; 4 | using CitizenFX.Core; 5 | 6 | namespace TestMenu 7 | { 8 | public class ExampleMenu : BaseScript 9 | { 10 | public ExampleMenu() 11 | { 12 | #if FIVEM 13 | // Setting the menu alignment to be right aligned. This can be changed at any time and it'll update instantly. 14 | // To test this, checkout one of the checkbox items in this example menu. Clicking it will toggle the menu alignment. 15 | MenuController.MenuAlignment = MenuController.MenuAlignmentOption.Right; 16 | #endif 17 | // Creating the first menu. 18 | Menu menu = new Menu("Main Menu", "Subtitle"); 19 | MenuController.AddMenu(menu); 20 | 21 | // Adding a new button by directly creating one inline. 22 | // You could also just store it and then add it but we don't need to do that in this example. 23 | menu.AddMenuItem( 24 | new MenuItem( 25 | "Normal Button", 26 | "This is a simple button with a simple description. Scroll down for more button types!" 27 | ) 28 | { 29 | Enabled = false, 30 | LeftIcon = MenuItem.Icon.TICK 31 | } 32 | ); 33 | #if FIVEM 34 | // Creating 3 sliders, showing off the 3 possible variations and custom colors. 35 | MenuSliderItem slider = new MenuSliderItem("Slider", 0, 10, 5, false); 36 | MenuSliderItem slider2 = new MenuSliderItem("Slider + Bar", 0, 10, 5, true) 37 | { 38 | BarColor = System.Drawing.Color.FromArgb(255, 73, 233, 111), 39 | BackgroundColor = System.Drawing.Color.FromArgb(255, 25, 100, 43) 40 | }; 41 | MenuSliderItem slider3 = new MenuSliderItem( 42 | "Slider + Bar + Icons", 43 | "The icons are currently male/female because that's probably the most common use. But any icon can be used!", 44 | 0, 45 | 10, 46 | 5, 47 | true 48 | ) 49 | { 50 | BarColor = System.Drawing.Color.FromArgb(255, 255, 0, 0), 51 | BackgroundColor = System.Drawing.Color.FromArgb(255, 100, 0, 0), 52 | SliderLeftIcon = MenuItem.Icon.MALE, 53 | SliderRightIcon = MenuItem.Icon.FEMALE 54 | }; 55 | 56 | // adding the sliders to the menu. 57 | menu.AddMenuItem(slider); 58 | menu.AddMenuItem(slider2); 59 | menu.AddMenuItem(slider3); 60 | #endif 61 | #if FIVEM 62 | // Creating 3 checkboxs, 2 different styles and one has a locked icon and it's 'not enabled' (not enabled meaning you can't toggle it). 63 | MenuCheckboxItem box = new MenuCheckboxItem( 64 | "Checkbox - Style 1 (click me!)", 65 | "This checkbox can toggle the menu position! Try it out.", 66 | !menu.LeftAligned 67 | ) 68 | { 69 | Style = MenuCheckboxItem.CheckboxStyle.Cross 70 | }; 71 | #endif 72 | MenuCheckboxItem box2 = new MenuCheckboxItem( 73 | "Checkbox - Style 2", 74 | "This checkbox does nothing right now.", 75 | true 76 | ) 77 | { 78 | Style = MenuCheckboxItem.CheckboxStyle.Tick 79 | }; 80 | 81 | MenuCheckboxItem box3 = new MenuCheckboxItem( 82 | "Checkbox (unchecked + locked)", 83 | "Make this menu right aligned. If you set this to false, then the menu will move to the left.", 84 | false 85 | ) 86 | { 87 | Enabled = false, 88 | LeftIcon = MenuItem.Icon.LOCK 89 | }; 90 | 91 | // Adding the checkboxes to the menu. 92 | #if FIVEM 93 | menu.AddMenuItem(box); 94 | #endif 95 | menu.AddMenuItem(box2); 96 | menu.AddMenuItem(box3); 97 | 98 | // Dynamic list item 99 | string ChangeCallback(MenuDynamicListItem item, bool left) 100 | { 101 | if (left) 102 | return (int.Parse(item.CurrentItem) - 1).ToString(); 103 | return (int.Parse(item.CurrentItem) + 1).ToString(); 104 | } 105 | MenuDynamicListItem dynList = new MenuDynamicListItem( 106 | "Dynamic list item.", 107 | "0", 108 | new MenuDynamicListItem.ChangeItemCallback(ChangeCallback), 109 | "Description for this dynamic item. Pressing left will make the value smaller, pressing right will make the value bigger." 110 | ); 111 | menu.AddMenuItem(dynList); 112 | #if FIVEM 113 | // List items (first the 3 special variants, then a normal one) 114 | List colorList = new List(); 115 | for (var i = 0; i < 64; i++) 116 | { 117 | colorList.Add($"Color #{i}"); 118 | } 119 | MenuListItem hairColors = new MenuListItem( 120 | "Hair Color", 121 | colorList, 122 | 0, 123 | "Hair color pallete." 124 | ) 125 | { 126 | ShowColorPanel = true 127 | }; 128 | 129 | // Also special 130 | List makeupColorList = new List(); 131 | for (var i = 0; i < 64; i++) 132 | { 133 | makeupColorList.Add($"Color #{i}"); 134 | } 135 | MenuListItem makeupColors = new MenuListItem("Makeup Color", makeupColorList, 0, "Makeup color pallete.") 136 | { 137 | ShowColorPanel = true, 138 | ColorPanelColorType = MenuListItem.ColorPanelType.Makeup 139 | }; 140 | 141 | // Also special 142 | List opacityList = new List(); 143 | for (var i = 0; i < 11; i++) 144 | { 145 | opacityList.Add($"Opacity {i * 10}%"); 146 | } 147 | MenuListItem opacity = new MenuListItem("Opacity Panel", opacityList, 0, "Set an opacity for something.") 148 | { 149 | ShowOpacityPanel = true 150 | }; 151 | 152 | menu.AddMenuItem(hairColors); 153 | menu.AddMenuItem(makeupColors); 154 | menu.AddMenuItem(opacity); 155 | #endif 156 | // Normal 157 | List normalList = new List() { "Item #1", "Item #2", "Item #3" }; 158 | MenuListItem normalListItem = new MenuListItem( 159 | "Normal List Item", 160 | normalList, 161 | 0, 162 | "And another simple description for yet another simple (list) item. Nothing special about this one." 163 | ); 164 | 165 | // Adding the lists to the menu. 166 | menu.AddMenuItem(normalListItem); 167 | 168 | // Creating a submenu, adding it to the menus list, and creating and binding a button for it. 169 | Menu submenu = new Menu("Submenu", "Secondary Menu"); 170 | MenuController.AddSubmenu(menu, submenu); 171 | 172 | MenuItem menuButton = new MenuItem( 173 | "Submenu", 174 | "This button is bound to a submenu. Clicking it will take you to the submenu." 175 | ) 176 | { 177 | #if FIVEM 178 | Label = "→→→" 179 | #endif 180 | #if REDM 181 | RightIcon = MenuItem.Icon.ARROW_RIGHT 182 | #endif 183 | }; 184 | menu.AddMenuItem(menuButton); 185 | MenuController.BindMenuItem(menu, submenu, menuButton); 186 | 187 | // Adding items with sprites left & right to the submenu. 188 | for (var i = 0; i < Enum.GetValues(typeof(MenuItem.Icon)).Length; i++) 189 | { 190 | var tmpItem = new MenuItem( 191 | $"Icon.{Enum.GetName(typeof(MenuItem.Icon), ((MenuItem.Icon)i))}", 192 | "This menu item has a left and right sprite. Press ~r~HOME~s~ to toggle the 'enabled' state on these items." 193 | ) 194 | { 195 | Label = $"(#{i})", 196 | #if FIVEM 197 | #endif 198 | RightIcon = (MenuItem.Icon)i, 199 | LeftIcon = (MenuItem.Icon)i 200 | }; 201 | 202 | submenu.AddMenuItem(tmpItem); 203 | } 204 | submenu.ButtonPressHandlers.Add( 205 | new Menu.ButtonPressHandler(Control.FrontendSocialClubSecondary, 206 | Menu.ControlPressCheckType.JUST_RELEASED, 207 | new Action((m, c) => 208 | { 209 | m.GetMenuItems().ForEach(a => a.Enabled = !a.Enabled); 210 | }), true) 211 | ); 212 | #if FIVEM 213 | // Instructional buttons setup for the second (submenu) menu. 214 | submenu.InstructionalButtons.Add(Control.CharacterWheel, "Right?!"); 215 | submenu.InstructionalButtons.Add(Control.CursorScrollDown, "Cool"); 216 | submenu.InstructionalButtons.Add(Control.CreatorDelete, "Out!"); 217 | submenu.InstructionalButtons.Add(Control.Cover, "This"); 218 | submenu.InstructionalButtons.Add(Control.Context, "Check"); 219 | #endif 220 | // Create a third menu without a banner. 221 | Menu menu3 = new Menu(null, "Only a subtitle, no banner."); 222 | 223 | // you can use AddSubmenu or AddMenu, both will work but if you want to link this menu from another menu, 224 | // you should use AddSubmenu. 225 | MenuController.AddSubmenu(menu, menu3); 226 | MenuItem thirdSubmenuBtn = new MenuItem( 227 | "Another submenu", 228 | "This is just a submenu without a banner. No big deal. This also has a very long description to test multiple " + 229 | "lines and see if they work properly. Let's find out if it works as intended." 230 | ) 231 | { 232 | #if FIVEM 233 | Label = "→→→" 234 | #endif 235 | #if REDM 236 | RightIcon = MenuItem.Icon.ARROW_RIGHT 237 | #endif 238 | }; 239 | menu.AddMenuItem(thirdSubmenuBtn); 240 | MenuController.BindMenuItem(menu, menu3, thirdSubmenuBtn); 241 | menu3.AddMenuItem(new MenuItem("Nothing here!")); 242 | menu3.AddMenuItem(new MenuItem("Nothing here!")); 243 | menu3.AddMenuItem(new MenuItem("Nothing here!")); 244 | menu3.AddMenuItem(new MenuItem("Nothing here!") { LeftIcon = MenuItem.Icon.TICK }); 245 | 246 | for (var i = 0; i < 10; i++) 247 | { 248 | menu.AddMenuItem(new MenuItem($"Item #{i + 1}.", "With an invisible description.")); 249 | } 250 | 251 | #if FIVEM 252 | // Create menu with weapon stats panel 253 | Menu menu4 = new Menu("Weapon Stats", "Weapon Stats Panel") { ShowWeaponStatsPanel = true }; 254 | menu4.AddMenuItem(new MenuItem("dummy item", "You should add at least one item when using weapon stat panels")); 255 | menu4.SetWeaponStats(0.2f, 0.4f, 0.7f, 0.8f); 256 | menu4.SetWeaponComponentStats(0.4f, 0f, -0.05f, 0.1f); 257 | MenuController.AddSubmenu(menu, menu4); 258 | MenuItem weaponStats = new MenuItem("Weapon stats", "Demo menu for weapon stats components"); 259 | menu.AddMenuItem(weaponStats); 260 | MenuController.BindMenuItem(menu, menu4, weaponStats); 261 | 262 | // Create menu with vehicle stats panel 263 | Menu menu5 = new Menu("Vehicle Stats", "Vehicle Stats Panel") { ShowVehicleStatsPanel = true }; 264 | menu5.AddMenuItem(new MenuItem("dummy item", "You should add at least one item when using vehicle stat panels")); 265 | menu5.SetVehicleStats(0.2f, 0.2f, 0.3f, 0.8f); 266 | menu5.SetVehicleUpgradeStats(0.4f, -0.025f, 0.05f, 0.1f); 267 | MenuController.AddSubmenu(menu, menu5); 268 | MenuItem vehicleStats = new MenuItem("Vehicle stats", "Demo menu for vehicle stats components"); 269 | menu.AddMenuItem(vehicleStats); 270 | MenuController.BindMenuItem(menu, menu5, vehicleStats); 271 | #endif 272 | /*-------------- 273 | Event handlers 274 | --------------*/ 275 | 276 | menu.OnCheckboxChange += (_menu, _item, _index, _checked) => 277 | { 278 | // Code in here gets executed whenever a checkbox is toggled. 279 | Debug.WriteLine($"OnCheckboxChange: [{_menu}, {_item}, {_index}, {_checked}]"); 280 | #if FIVEM 281 | // If the align-menu checkbox is toggled, toggle the menu alignment. 282 | if (_item == box) 283 | { 284 | if (_checked) 285 | { 286 | MenuController.MenuAlignment = MenuController.MenuAlignmentOption.Right; 287 | } 288 | else 289 | { 290 | MenuController.MenuAlignment = MenuController.MenuAlignmentOption.Left; 291 | } 292 | } 293 | #endif 294 | }; 295 | 296 | menu.OnItemSelect += (_menu, _item, _index) => 297 | { 298 | // Code in here would get executed whenever an item is pressed. 299 | Debug.WriteLine($"OnItemSelect: [{_menu}, {_item}, {_index}]"); 300 | }; 301 | 302 | menu.OnIndexChange += (_menu, _oldItem, _newItem, _oldIndex, _newIndex) => 303 | { 304 | // Code in here would get executed whenever the up or down key is pressed and the index of the menu is changed. 305 | Debug.WriteLine($"OnIndexChange: [{_menu}, {_oldItem}, {_newItem}, {_oldIndex}, {_newIndex}]"); 306 | }; 307 | 308 | menu.OnListIndexChange += (_menu, _listItem, _oldIndex, _newIndex, _itemIndex) => 309 | { 310 | // Code in here would get executed whenever the selected value of a list item changes (when left/right key is pressed). 311 | Debug.WriteLine($"OnListIndexChange: [{_menu}, {_listItem}, {_oldIndex}, {_newIndex}, {_itemIndex}]"); 312 | }; 313 | 314 | menu.OnListItemSelect += (_menu, _listItem, _listIndex, _itemIndex) => 315 | { 316 | // Code in here would get executed whenever a list item is pressed. 317 | Debug.WriteLine($"OnListItemSelect: [{_menu}, {_listItem}, {_listIndex}, {_itemIndex}]"); 318 | }; 319 | 320 | #if FIVEM 321 | menu.OnSliderPositionChange += (_menu, _sliderItem, _oldPosition, _newPosition, _itemIndex) => 322 | { 323 | // Code in here would get executed whenever the position of a slider is changed (when left/right key is pressed). 324 | Debug.WriteLine($"OnSliderPositionChange: [{_menu}, {_sliderItem}, {_oldPosition}, {_newPosition}, {_itemIndex}]"); 325 | }; 326 | 327 | menu.OnSliderItemSelect += (_menu, _sliderItem, _sliderPosition, _itemIndex) => 328 | { 329 | // Code in here would get executed whenever a slider item is pressed. 330 | Debug.WriteLine($"OnSliderItemSelect: [{_menu}, {_sliderItem}, {_sliderPosition}, {_itemIndex}]"); 331 | }; 332 | #endif 333 | 334 | menu.OnMenuClose += (_menu) => 335 | { 336 | // Code in here gets triggered whenever the menu is closed. 337 | Debug.WriteLine($"OnMenuClose: [{_menu}]"); 338 | }; 339 | 340 | menu.OnMenuOpen += (_menu) => 341 | { 342 | // Code in here gets triggered whenever the menu is opened. 343 | Debug.WriteLine($"OnMenuOpen: [{_menu}]"); 344 | }; 345 | 346 | menu.OnDynamicListItemCurrentItemChange += (_menu, _dynamicListItem, _oldCurrentItem, _newCurrentItem) => 347 | { 348 | // Code in here would get executed whenever the value of the current item of a dynamic list item changes. 349 | Debug.WriteLine($"OnDynamicListItemCurrentItemChange: [{_menu}, {_dynamicListItem}, {_oldCurrentItem}, {_newCurrentItem}]"); 350 | }; 351 | 352 | menu.OnDynamicListItemSelect += (_menu, _dynamicListItem, _currentItem) => 353 | { 354 | // Code in here would get executed whenever a dynamic list item is pressed. 355 | Debug.WriteLine($"OnDynamicListItemSelect: [{_menu}, {_dynamicListItem}, {_currentItem}]"); 356 | }; 357 | } 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /TestMenu/TestMenu.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net452 5 | embedded 6 | $(AssemblyName).net 7 | Release RedM;Release FiveM;Debug RedM;Debug FiveM 8 | 9 | 10 | 11 | FIVEM 12 | 13 | 14 | 15 | REDM 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | runtime 25 | 26 | 27 | 28 | 29 | ..\dependencies\RedM\CitizenFX.Core.dll 30 | false 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | pull_requests: 2 | do_not_increment_build_number: false 3 | image: Visual Studio 2019 4 | configuration: 5 | - Release RedM 6 | - Release FiveM 7 | before_build: 8 | - nuget restore 9 | - cmd: if %APPVEYOR_REPO_TAG%==true (appveyor SetVariable -Name VERSION_NAME -Value %APPVEYOR_REPO_TAG_NAME%) else (appveyor SetVariable -Name VERSION_NAME -Value dev-%APPVEYOR_BUILD_VERSION%) 10 | - cmd: if "%CONFIGURATION%"=="Release FiveM" (appveyor SetVariable -Name GAME -Value "FiveM") else if "%CONFIGURATION%"=="Release RedM" (appveyor SetVariable -Name GAME -Value "RedM") 11 | build: 12 | parallel: false 13 | project: MenuAPI.sln 14 | include_nuget_references: true 15 | verbosity: minimal 16 | after_build: 17 | - cmd: copy "README.md" ".\MenuAPI\bin\README.md" 18 | - cmd: 7z a MenuAPI-%VERSION_NAME%-%GAME%.zip -r ".\MenuAPI\bin\*" 19 | - cmd: appveyor PushArtifact MenuAPI-%VERSION_NAME%-%GAME%.zip 20 | deploy: 21 | - provider: NuGet 22 | api_key: $(NUGET_API_KEY) 23 | skip_symbols: true 24 | on: 25 | APPVEYOR_REPO_TAG: true 26 | - provider: GitHub 27 | release: "[Release] MenuAPI $(VERSION_NAME)" 28 | tag: $(VERSION_NAME) 29 | artifact: MenuAPI-$(VERSION_NAME)-$(GAME).zip 30 | draft: false 31 | prerelease: false 32 | auth_token: $(github_auth) 33 | on: 34 | APPVEYOR_REPO_TAG: true 35 | description: "MenuAPI release, version $(VERSION_NAME). Download the **FiveM** or **RedM** versions below, by selecting the corresponding zip files.\n\nFor info check the [docs](https://docs.vespura.com/mapi) or checkout the recent commits on GitHub (the latter is your best bet if you want updated info about this version)." 36 | before_deploy: 37 | - ps: $env:NUGET_VERSION=(($env:VERSION_NAME).Substring(1)) 38 | - cmd: if "%CONFIGURATION%"=="Release FiveM" (cd MenuAPI && nuget pack MenuAPI.csproj -Properties Configuration="Release FiveM";Id="MenuAPI.FiveM" -Version "%NUGET_VERSION%" && appveyor PushArtifact MenuAPI.FiveM.%NUGET_VERSION%.nupkg && cd ..) else (cd MenuAPI && nuget pack MenuAPI.csproj -Properties Configuration="Release RedM";Id="MenuAPI.RedM" -Version "%NUGET_VERSION%" && appveyor PushArtifact MenuAPI.RedM.%NUGET_VERSION%.nupkg && cd ..) 39 | after_deploy: 40 | - cmd: appveyor/after_deploy.cmd 41 | on_success: 42 | - cmd: appveyor/on_success.cmd 43 | on_failure: 44 | - cmd: appveyor/on_failure.cmd 45 | -------------------------------------------------------------------------------- /appveyor/after_deploy.cmd: -------------------------------------------------------------------------------- 1 | if not defined WEBHOOK_URL goto end 2 | if not "%CONFIGURATION%"=="Release FiveM" goto end 3 | 4 | curl -H "Content-Type:application/json" -X POST -d "{\"content\":\"^<^@^&540562517806809109^>\",\"embeds\":[{\"title\":\"%APPVEYOR_PROJECT_NAME% (%VERSION_NAME%)\",\"description\":\"New version of **%APPVEYOR_PROJECT_NAME%** (%VERSION_NAME%) is available for both **FiveM** and **RedM** Download it by using the files in this chanel or by going to the GitHub Tag link!\",\"color\":3048181,\"author\":{\"name\":\"Committed by %APPVEYOR_ACCOUNT_NAME%\",\"url\":\"https://github.com/%APPVEYOR_ACCOUNT_NAME%/\"},\"fields\":[{\"name\":\"AppVeyor Build\",\"value\":\"[Here](%APPVEYOR_URL%/project/%APPVEYOR_ACCOUNT_NAME%/%APPVEYOR_PROJECT_SLUG%/builds/%APPVEYOR_BUILD_ID%)\"},{\"name\":\"GitHub Commit (%APPVEYOR_REPO_COMMIT%)\",\"value\":\"[%APPVEYOR_REPO_COMMIT%](https://github.com/%APPVEYOR_ACCOUNT_NAME%/%APPVEYOR_PROJECT_NAME%/commit/%APPVEYOR_REPO_COMMIT%) - %APPVEYOR_REPO_COMMIT_MESSAGE%%APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED%\"},{\"name\":\"GitHub Branch\",\"value\":\"[%APPVEYOR_REPO_BRANCH%](https://github.com/%APPVEYOR_ACCOUNT_NAME%/%APPVEYOR_PROJECT_NAME%/tree/%APPVEYOR_REPO_BRANCH%)\"},{\"name\":\"GitHub Tag\",\"value\":\"[%APPVEYOR_REPO_TAG_NAME%](https://github.com/%APPVEYOR_ACCOUNT_NAME%/%APPVEYOR_PROJECT_NAME%/releases/tag/%APPVEYOR_REPO_TAG_NAME%)\"}]}]}" %WEBHOOK_URL% 5 | 6 | :end -------------------------------------------------------------------------------- /appveyor/on_failure.cmd: -------------------------------------------------------------------------------- 1 | if not defined WEBHOOK_URL goto end 2 | 3 | curl -H "Content-Type:application/json" -X POST -d "{\"embeds\":[{\"title\":\"%APPVEYOR_PROJECT_NAME% (%VERSION_NAME%-%GAME%)\",\"description\":\"Build FAILED! Ouch.\",\"color\":13632027,\"author\":{\"name\":\"Committed by %APPVEYOR_ACCOUNT_NAME%\",\"url\":\"https://github.com/%APPVEYOR_ACCOUNT_NAME%/\"},\"fields\":[{\"name\":\"AppVeyor Build\",\"value\":\"[Here](%APPVEYOR_URL%/project/%APPVEYOR_ACCOUNT_NAME%/%APPVEYOR_PROJECT_SLUG%/builds/%APPVEYOR_BUILD_ID%)\"},{\"name\":\"GitHub Commit (%APPVEYOR_REPO_COMMIT%)\",\"value\":\"[%APPVEYOR_REPO_COMMIT%](https://github.com/%APPVEYOR_ACCOUNT_NAME%/%APPVEYOR_PROJECT_NAME%/commit/%APPVEYOR_REPO_COMMIT%) - %APPVEYOR_REPO_COMMIT_MESSAGE%%APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED%\"},{\"name\":\"GitHub Branch\",\"value\":\"[%APPVEYOR_REPO_BRANCH%](https://github.com/%APPVEYOR_ACCOUNT_NAME%/%APPVEYOR_PROJECT_NAME%/tree/%APPVEYOR_REPO_BRANCH%)\"}]}]}" %WEBHOOK_URL% 4 | 5 | :end -------------------------------------------------------------------------------- /appveyor/on_success.cmd: -------------------------------------------------------------------------------- 1 | if not defined WEBHOOK_URL goto end 2 | curl -s -o nul -F "file=@MenuAPI-%VERSION_NAME%-%GAME%.zip" %WEBHOOK_URL% 3 | 4 | if %APPVEYOR_REPO_TAG%==true goto end 5 | curl -H "Content-Type:application/json" -X POST -d "{\"embeds\":[{\"title\":\"%APPVEYOR_PROJECT_NAME% (%VERSION_NAME%-%GAME%)\",\"description\":\"Build passed!\",\"color\":4502298,\"author\":{\"name\":\"Committed by %APPVEYOR_ACCOUNT_NAME%\",\"url\":\"https://github.com/%APPVEYOR_ACCOUNT_NAME%/\"},\"fields\":[{\"name\":\"AppVeyor Build\",\"value\":\"[Here](%APPVEYOR_URL%/project/%APPVEYOR_ACCOUNT_NAME%/%APPVEYOR_PROJECT_SLUG%/builds/%APPVEYOR_BUILD_ID%)\"},{\"name\":\"GitHub Commit (%APPVEYOR_REPO_COMMIT%)\",\"value\":\"[%APPVEYOR_REPO_COMMIT%](https://github.com/%APPVEYOR_ACCOUNT_NAME%/%APPVEYOR_PROJECT_NAME%/commit/%APPVEYOR_REPO_COMMIT%) - %APPVEYOR_REPO_COMMIT_MESSAGE%%APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED%\"},{\"name\":\"GitHub Branch\",\"value\":\"[%APPVEYOR_REPO_BRANCH%](https://github.com/%APPVEYOR_ACCOUNT_NAME%/%APPVEYOR_PROJECT_NAME%/tree/%APPVEYOR_REPO_BRANCH%)\"}]}]}" %WEBHOOK_URL% 6 | 7 | :end -------------------------------------------------------------------------------- /dependencies/RedM/CitizenFX.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TomGrobbe/MenuAPI/9a7195a5b0658fd48441b3c9a56f264576789f89/dependencies/RedM/CitizenFX.Core.dll -------------------------------------------------------------------------------- /shared/RedM/Controls.cs: -------------------------------------------------------------------------------- 1 | #if REDM 2 | namespace CitizenFX.Core 3 | { 4 | public enum Control : uint 5 | { 6 | NextCamera = 0x7F8D09B8, 7 | LookLr = 0xA987235F, 8 | LookUd = 0xD2047988, 9 | LookUpOnly = 0xC0651D40, 10 | LookDownOnly = 0x8ED92E16, 11 | LookLeftOnly = 0x08F8BC6D, 12 | LookRightOnly = 0xA1EB1353, 13 | CinematicSlowmo = 0x7A9093DE, 14 | RadialMenuNavUd = 0xBA60039F, 15 | RadialMenuNavLr = 0x390948DC, 16 | RadialMenuSlotNavNext = 0xE71F89B8, 17 | RadialMenuSlotNavPrev = 0x93D6723F, 18 | SelectNextWheel = 0x77E56FB3, 19 | SelectItemWheel = 0x1F6D95E5, 20 | QuickSelectInspect = 0xF09866F3, 21 | QuickSelectSetForSwap = 0xD45EC04F, 22 | QuickShortcutAbilitiesMenu = 0x9CC7A1A4, 23 | QuickSelectSecondaryNavNext = 0xF1421CF5, 24 | QuickSelectSecondaryNavPrev = 0xD9F9F017, 25 | QuickSelectToggleShortcutItem = 0xFA0B29CD, 26 | QuickSelectPutAwayRod = 0x253FEC09, 27 | EmotesFavorite = 0xA835261B, 28 | EmotesManage = 0x7E75F4DC, 29 | EmotesSlotNavNext = 0xCBB12F87, 30 | SelectNextWeapon = 0xD0842EDF, 31 | SelectPrevWeapon = 0xF78D7337, 32 | SkipCutscene = 0xCDC4E4E9, 33 | CharacterWheel = 0x972F8D1E, 34 | MultiplayerInfo = 0xE8342FF2, 35 | Sprint = 0x8FFC75D6, 36 | Jump = 0xD9D0E1C0, 37 | Enter = 0xCEFD9220, 38 | Attack = 0x07CE1E61, 39 | Aim = 0xF84FA74F, 40 | LookBehind = 0x9959A6F0, 41 | Phone = 0x4CF871D0, 42 | SpecialAbility = 0xCEE12B50, 43 | SpecialAbilitySecondary = 0x6328239B, 44 | SecondarySpecialAbilitySecondary = 0x811F4A1A, 45 | SpecialAbilityAction = 0x1ECA87D4, 46 | MoveLr = 0x4D8FB4C1, 47 | MoveUd = 0xFDA83190, 48 | MoveUpOnly = 0x8FD015D8, 49 | MoveDownOnly = 0xD27782E3, 50 | MoveLeftOnly = 0x7065027D, 51 | MoveRightOnly = 0xB4E465B4, 52 | Duck = 0xDB096B85, 53 | TwirlPistol = 0x938D4071, 54 | ToggleHolster = 0xB238FE0B, 55 | OpenWheelMenu = 0xAC4BD4F1, 56 | OpenSatchelMenu = 0x4CC0E2FE, 57 | OpenSatchelHorseMenu = 0x5966D52A, 58 | OpenCraftingMenu = 0x734C6E39, 59 | OpenJournal = 0xF3830D8E, 60 | Pickup = 0xE6360A8E, 61 | Ignite = 0xC75C27B0, 62 | SniperZoom = 0x7ABC6A66, 63 | SniperZoomInOnly = 0xA5BDCD3C, 64 | SniperZoomOutOnly = 0x430593AA, 65 | SniperZoomInSecondary = 0x6BE9C207, 66 | SniperZoomOutSecondary = 0x8A7B8833, 67 | ToggleWeaponScope = 0x3076E97C, 68 | Cover = 0xDE794E3E, 69 | CoverTransition = 0x750C8010, 70 | Reload = 0xE30CD707, 71 | Talk = 0x7DCA9C75, 72 | Detonate = 0x73846677, 73 | HudSpecial = 0x580C4473, 74 | Arrest = 0xA4F1006B, 75 | AccurateAim = 0x406ADFAE, 76 | SwitchShoulder = 0x827E9EE8, 77 | IronSight = 0x841240A9, 78 | AimInAir = 0xD8F73058, 79 | SwitchFiringMode = 0xEED15F18, 80 | Context = 0xB73BCA77, 81 | ContextSecondary = 0xF19BE385, 82 | WeaponSpecial = 0x733901F3, 83 | WeaponSpecialTwo = 0x50BA1A77, 84 | Dive = 0x06052D11, 85 | DropWeapon = 0x7DBCD016, 86 | DropAmmo = 0x4E42696E, 87 | ThrowGrenade = 0x0AF99998, 88 | FocusCam = 0xE72B43F4, 89 | Inspect = 0xA61DC630, 90 | InspectZoom = 0x53296B75, 91 | InspectLr = 0x1788C283, 92 | InspectUd = 0xF9781997, 93 | InspectOpenSatchel = 0x9B1CA8DA, 94 | DynamicScenario = 0x2EAB0795, 95 | PlayerMenu = 0x80F28E95, 96 | OpenEmoteWheel = 0xE2B557A3, 97 | OpenEmoteWheelHorse = 0x8B3FA65E, 98 | EmoteGroupLink = 0x1C826362, 99 | EmoteGroupLinkHorse = 0x4FD1C57B, 100 | RevealHud = 0xCF8A4ECA, 101 | SelectRadarMode = 0x0F39B3D4, 102 | SimpleRadar = 0x5FEF1B6D, 103 | ExpandRadar = 0xCF0B11DE, 104 | RegularRadar = 0x51AA7A35, 105 | DisableRadar = 0x70CBD78D, 106 | Surrender = 0xDB8D69B8, 107 | Whistle = 0x24978A28, 108 | WhistleHorseback = 0xE7EB9185, 109 | StopLeadingAnimal = 0x7914A3DD, 110 | CinematicCam = 0x620A6C5E, 111 | CinematicCamHold = 0xD7E7B375, 112 | CinematicCamChangeShot = 0xA6C67243, 113 | CinematicCamUd = 0x84574AE8, 114 | CinematicCamUpOnly = 0xEFCFE6B7, 115 | CinematicCamDownOnly = 0x23AE34A2, 116 | CinematicCamLr = 0x6BC904FC, 117 | ContextA = 0x5181713D, 118 | ContextB = 0x3B24C470, 119 | ContextX = 0xE3BF959B, 120 | ContextY = 0xD51B784F, 121 | ContextLt = 0xC13A6564, 122 | ContextRt = 0x07B8BEAF, 123 | ContextAction = 0xB28318C0, 124 | VehMoveLr = 0xF1E2852C, 125 | VehMoveUd = 0x8A81C00C, 126 | VehMoveUpOnly = 0xDEBD7EF6, 127 | VehMoveDownOnly = 0x16D73E1D, 128 | VehMoveLeftOnly = 0x9DF54706, 129 | VehMoveRightOnly = 0x97A8FD98, 130 | VehSpecial = 0x493919DB, 131 | VehGunLr = 0xB6F3E4FE, 132 | VehGunUd = 0x482560EE, 133 | VehAim = 0xD7CAFCEF, 134 | VehAttack = 0xF4330038, 135 | VehAttack2 = 0xF1C341BA, 136 | VehAccelerate = 0x5B9FD4E2, 137 | VehBrake = 0x6E1F639B, 138 | VehDuck = 0x5B3690F2, 139 | VehHeadlight = 0xF1301666, 140 | VehExit = 0xFEFAB9B4, 141 | VehHandbrake = 0x65D24C98, 142 | VehLookBehind = 0xCAE9B017, 143 | VehNextRadio = 0x22E0F7E7, 144 | VehPrevRadio = 0x9785CE13, 145 | VehNextRadioTrack = 0xF7FA2DDC, 146 | VehPrevRadioTrack = 0x0A94C4FF, 147 | VehRadioWheel = 0x4915AC0A, 148 | VehHorn = 0x63A0D258, 149 | VehFlyThrottleUp = 0x7232BAB3, 150 | VehFlyThrottleDown = 0x084DFF95, 151 | VehFlyYawLeft = 0x31589AD1, 152 | VehFlyYawRight = 0xBD143FC6, 153 | VehPassengerAim = 0xEE2804D0, 154 | VehPassengerAttack = 0x27AD4433, 155 | VehSpecialAbilityFranklin = 0x5EC33578, 156 | VehStuntUd = 0x4AA1560E, 157 | VehSelectNextWeapon = 0x889A626F, 158 | VehSelectPrevWeapon = 0x0C97BAC7, 159 | VehRoof = 0x3E7CF9A4, 160 | VehJump = 0xAA56B926, 161 | VehGrapplingHook = 0xB985AA5E, 162 | VehShuffle = 0xC7083798, 163 | VehTraversal = 0x739D6261, 164 | VehDropProjectile = 0xC61611E6, 165 | VehMouseControlOverride = 0x39CCABD5, 166 | VehFlyRollLr = 0x3C8AB570, 167 | VehFlyRollLeftOnly = 0x56F84EA0, 168 | VehFlyRollRightOnly = 0x876B3361, 169 | VehFlyPitchUd = 0xE67E1E57, 170 | VehFlyPitchUpOnly = 0x6280BA1A, 171 | VehFlyPitchDownOnly = 0x0F4E369F, 172 | VehFlyUndercarriage = 0xFE0FE518, 173 | VehFlyAttack = 0x1D71D7AA, 174 | VehFlySelectNextWeapon = 0x24E94299, 175 | VehFlySelectPrevWeapon = 0xC0D874E5, 176 | VehFlySelectTargetLeft = 0x307FC4C1, 177 | VehFlySelectTargetRight = 0x52F25C96, 178 | VehFlyVerticalFlightMode = 0xE3238029, 179 | VehFlyDuck = 0x378A10F7, 180 | VehFlyAttackCamera = 0x2FBA3F0B, 181 | VehFlyMouseControlOverride = 0x6C9810A5, 182 | VehSubMouseControlOverride = 0x2CAF327E, 183 | VehSubTurnLr = 0x627C4619, 184 | VehSubTurnLeftOnly = 0x44E7E093, 185 | VehSubTurnRightOnly = 0xE78A5A3C, 186 | VehSubPitchUd = 0x469CE271, 187 | VehSubPitchUpOnly = 0xF9EF072A, 188 | VehSubPitchDownOnly = 0xBA2D22AA, 189 | VehSubThrottleUp = 0xD28C446F, 190 | VehSubThrottleDown = 0xF5B2CEFB, 191 | VehSubAscend = 0xD7991F74, 192 | VehSubDescend = 0x7D51DE24, 193 | VehSubTurnHardLeft = 0x64214D49, 194 | VehSubTurnHardRight = 0xA44C0F83, 195 | VehPushbikePedal = 0xFD8D64A7, 196 | VehPushbikeSprint = 0xF03EE151, 197 | VehPushbikeFrontBrake = 0x585E942D, 198 | VehPushbikeRearBrake = 0xF8CBAFB5, 199 | VehDraftMoveUd = 0x23595CEA, 200 | VehDraftTurnLr = 0xA7DFAE8A, 201 | VehDraftMoveUpOnly = 0x29A5E51E, 202 | VehDraftMoveDownOnly = 0x25493EB3, 203 | VehDraftTurnLeftOnly = 0x198AFC64, 204 | VehDraftTurnRightOnly = 0x5E371EA7, 205 | VehDraftAccelerate = 0xE99D2B05, 206 | VehDraftBrake = 0xD648E48D, 207 | VehDraftAim = 0xBDD5830D, 208 | VehDraftAttack = 0xF40AB198, 209 | VehDraftAttack2 = 0x886F12DD, 210 | VehDraftSwitchDrivers = 0x70B87844, 211 | VehBoatTurnLr = 0xD8DFCAB3, 212 | VehBoatTurnLeftOnly = 0x5BED7C91, 213 | VehBoatTurnRightOnly = 0xF9780DFB, 214 | VehBoatAccelerate = 0xB341E812, 215 | VehBoatBrake = 0x428D5F39, 216 | VehBoatAim = 0x92F5F01E, 217 | VehBoatAttack = 0x6866FA3A, 218 | VehBoatAttack2 = 0x876096E9, 219 | VehCarTurnLr = 0x3BD38D43, 220 | VehCarTurnLeftOnly = 0x07D1654C, 221 | VehCarTurnRightOnly = 0x6E3C3649, 222 | VehCarAccelerate = 0xB9F544B0, 223 | VehCarBrake = 0xD1887B3F, 224 | VehCarAim = 0x6777B840, 225 | VehCarAttack = 0x5572F386, 226 | VehCarAttack2 = 0x5B763AD7, 227 | VehHandcartAccelerate = 0xFF3626FC, 228 | VehHandcartBrake = 0x2D79D80A, 229 | HorseMoveLr = 0x126796EB, 230 | HorseMoveUd = 0x3BBDEFEF, 231 | HorseMoveUpOnly = 0x699487BB, 232 | HorseMoveDownOnly = 0x56F82045, 233 | HorseMoveLeftOnly = 0x86D773F6, 234 | HorseMoveRightOnly = 0x7E6B8612, 235 | HorseSpecial = 0x70089459, 236 | HorseGunLr = 0x3D99EEC6, 237 | HorseGunUd = 0xBFF476F9, 238 | HorseAttack = 0x60C81CDE, 239 | HorseAttack2 = 0xC904196D, 240 | HorseSprint = 0x5AA007D7, 241 | HorseStop = 0xE16B9AAD, 242 | HorseExit = 0xCBDB82A8, 243 | HorseLookBehind = 0x81280569, 244 | HorseJump = 0xE4D2CE1D, 245 | HorseAim = 0x61470051, 246 | HorseCollect = 0x7D5B3717, 247 | HitchAnimal = 0xA95E1468, 248 | HorseCommandFlee = 0x4216AF06, 249 | HorseCommandStay = 0xAE5DFDED, 250 | HorseCommandFollow = 0x763E4D27, 251 | HorseMelee = 0x1A3EABBB, 252 | MeleeHorseAttackPrimary = 0x78ED2132, 253 | MeleeHorseAttackSecondary = 0x162AFEB8, 254 | HorseCoverTransition = 0x2996DD15, 255 | MeleeAttack = 0xB2F377E8, 256 | MeleeModifier = 0x1E7D7275, 257 | MeleeBlock = 0xB5EEEFB7, 258 | MeleeGrapple = 0x2277FAE9, 259 | MeleeGrappleAttack = 0xADEAF48C, 260 | MeleeGrappleChoke = 0x018C47CF, 261 | MeleeGrappleReversal = 0x91C9A817, 262 | MeleeGrappleBreakout = 0xD0C1FEFF, 263 | MeleeGrappleStandSwitch = 0xBE1F4699, 264 | MeleeGrappleMountSwitch = 0x67ED272E, 265 | ParachuteDeploy = 0xEBF53058, 266 | ParachuteDetach = 0xFFBFF139, 267 | ParachuteTurnLr = 0x8EC920BF, 268 | ParachuteTurnLeftOnly = 0xC4CF3322, 269 | ParachuteTurnRightOnly = 0x2BDBA378, 270 | ParachutePitchUd = 0xF0526228, 271 | ParachutePitchUpOnly = 0x08BFEA69, 272 | ParachutePitchDownOnly = 0x7C3A4352, 273 | ParachuteBrakeLeft = 0x272BD8BA, 274 | ParachuteBrakeRight = 0x948B3EA7, 275 | ParachuteSmoke = 0x2574FAB0, 276 | ParachutePrecisionLanding = 0xC675B8BD, 277 | Map = 0xE31C6A41, 278 | SelectWeaponUnarmed = 0x1F6EEB0F, 279 | SelectWeaponMelee = 0x109E6852, 280 | SelectWeaponHandgun = 0x184960E3, 281 | SelectWeaponShotgun = 0x76D3EA05, 282 | SelectWeaponSmg = 0xCEF1BB48, 283 | SelectWeaponAutoRifle = 0x05EEA9D0, 284 | SelectWeaponSniper = 0x96C61FDF, 285 | SelectWeaponHeavy = 0x3D1675C3, 286 | SelectWeaponSpecial = 0xC41ECEF8, 287 | SelectCharacterMichael = 0xEA9256B8, 288 | SelectCharacterFranklin = 0x8E8B08CB, 289 | SelectCharacterTrevor = 0xB00CC093, 290 | SelectCharacterMultiplayer = 0xDFB2B3B8, 291 | SaveReplayClip = 0x5B3AF9E3, 292 | SpecialAbilityPc = 0x52E60A8B, 293 | SelectQuickselectSidearmsLeft = 0xE6F612E4, 294 | SelectQuickselectDualwield = 0x1CE6D9EB, 295 | SelectQuickselectSidearmsRight = 0x4F49CC4C, 296 | SelectQuickselectUnarmed = 0x8F9F9E58, 297 | SelectQuickselectMeleeNoUnarmed = 0xAB62E997, 298 | SelectQuickselectSecondaryLongarm = 0xA1FDE2A6, 299 | SelectQuickselectThrown = 0xB03A913B, 300 | SelectQuickselectPrimaryLongarm = 0x42385422, 301 | CellphoneUp = 0xD2EE3B1E, 302 | CellphoneDown = 0x82196002, 303 | CellphoneLeft = 0x3ABBE990, 304 | CellphoneRight = 0xD25EFDCD, 305 | CellphoneSelect = 0xDC264018, 306 | CellphoneCancel = 0xDD833287, 307 | CellphoneOption = 0xD2C28BB4, 308 | CellphoneExtraOption = 0xBE354011, 309 | CellphoneScrollForward = 0xCB4E1798, 310 | CellphoneScrollBackward = 0x47CD0F3B, 311 | CellphoneCameraFocusLock = 0x5AC1805E, 312 | CellphoneCameraGrid = 0xE18CC57A, 313 | CellphoneCameraSelfie = 0x6A440BFE, 314 | CellphoneCameraDof = 0x593DB489, 315 | CellphoneCameraExpression = 0xD7E274E7, 316 | FrontendDown = 0x05CA7C52, 317 | FrontendUp = 0x6319DB71, 318 | FrontendLeft = 0xA65EBAB4, 319 | FrontendRight = 0xDEB34313, 320 | FrontendRdown = 0x5734A944, 321 | FrontendRup = 0xD7DE6B1E, 322 | FrontendRleft = 0x39336A4F, 323 | FrontendRright = 0x5B48F938, 324 | FrontendAxisX = 0xFB56DD5B, 325 | FrontendAxisY = 0x091178D0, 326 | FrontendScrollAxisX = 0x3224BC55, 327 | FrontendScrollAxisY = 0x21651AD6, 328 | FrontendRightAxisX = 0x3D23549A, 329 | FrontendRightAxisY = 0xEB4130DF, 330 | FrontendPause = 0xD82E0BD2, 331 | FrontendPauseAlternate = 0x4A903C11, 332 | FrontendAccept = 0xC7B5340A, 333 | FrontendCancel = 0x156F7119, 334 | FrontendX = 0x6DB8C62F, 335 | FrontendY = 0x7C0162C0, 336 | FrontendLb = 0xE885EF16, 337 | FrontendRb = 0x17BEC168, 338 | FrontendLt = 0x51104035, 339 | FrontendRt = 0x6FED71BC, 340 | FrontendLs = 0x43CDA5B0, 341 | FrontendRs = 0x7DA48D2A, 342 | FrontendLeaderboard = 0x9EDC8D65, 343 | FrontendSocialClub = 0x064D1698, 344 | FrontendSocialClubSecondary = 0xBDB8D6F3, 345 | FrontendDelete = 0x4AF4D473, 346 | FrontendEndscreenAccept = 0x3E32FCEE, 347 | FrontendEndscreenExpand = 0xC79BDE9F, 348 | FrontendSelect = 0x171910DC, 349 | FrontendPhotoMode = 0x44CD301B, 350 | FrontendNavUp = 0x8CFFE0A1, 351 | FrontendNavDown = 0x78114AB3, 352 | FrontendNavLeft = 0x877F1027, 353 | FrontendNavRight = 0x08BD758C, 354 | FrontendMapNavUp = 0x125A70E5, 355 | FrontendMapNavDown = 0xF8480EED, 356 | FrontendMapNavLeft = 0xE0D75B00, 357 | FrontendMapNavRight = 0x28725E5D, 358 | FrontendMapZoom = 0x6B359A27, 359 | GameMenuAccept = 0x43DBF61F, 360 | GameMenuCancel = 0x308588E6, 361 | GameMenuOption = 0xFBD7B3E6, 362 | GameMenuExtraOption = 0xD596CFB0, 363 | GameMenuUp = 0x911CB09E, 364 | GameMenuDown = 0x4403F97F, 365 | GameMenuLeft = 0xAD7FCC5B, 366 | GameMenuRight = 0x65F9EC5B, 367 | GameMenuTabLeft = 0xCBD5B26E, 368 | GameMenuTabRight = 0x110AD1D2, 369 | GameMenuTabLeftSecondary = 0x26E9DC00, 370 | GameMenuTabRightSecondary = 0x8CC9CD42, 371 | GameMenuScrollForward = 0x81457A1A, 372 | GameMenuScrollBackward = 0x9DA42644, 373 | GameMenuStickUp = 0x9CA97399, 374 | GameMenuStickDown = 0x63898D36, 375 | GameMenuStickLeft = 0x06C089D4, 376 | GameMenuStickRight = 0x5BDBE841, 377 | GameMenuRightStickUp = 0xF0232A03, 378 | GameMenuRightStickDown = 0xADB78673, 379 | GameMenuRightStickLeft = 0x71E38966, 380 | GameMenuRightStickRight = 0xE1CECE4B, 381 | GameMenuLs = 0xA8F6DE66, 382 | GameMenuRs = 0x89EA3FA5, 383 | GameMenuRightAxisX = 0x4685AA33, 384 | GameMenuRightAxisY = 0x60C65EB4, 385 | GameMenuLeftAxisX = 0xF431D57A, 386 | GameMenuLeftAxisY = 0x226EB1EF, 387 | Quit = 0x8E90C7BB, 388 | DocumentPageNext = 0xC97792B7, 389 | DocumentPagePrev = 0x20190AB4, 390 | DocumentScroll = 0xAC70F311, 391 | DocumentScrollUpOnly = 0x3D0C19EC, 392 | DocumentScrollDownOnly = 0xD72F3E29, 393 | Attack2 = 0x0283C582, 394 | PrevWeapon = 0xCC1075A7, 395 | NextWeapon = 0xFD0F0C2C, 396 | SniperZoomIn = 0xE4568AA1, 397 | SniperZoomOut = 0xE40CE39E, 398 | SniperZoomInAlternate = 0x3A9897C1, 399 | SniperZoomOutAlternate = 0xBC820489, 400 | ReplayStartStopRecording = 0xDCA6978E, 401 | ReplayStartStopRecordingSecondary = 0x8991A70B, 402 | ReplayMarkerDelete = 0xC7D2C51B, 403 | ReplayClipDelete = 0xF6734E42, 404 | ReplayPause = 0x083137B2, 405 | ReplayRewind = 0xC1339A31, 406 | ReplayFfwd = 0x609A27E8, 407 | ReplayNewmarker = 0xF7C6DA28, 408 | ReplayRecord = 0xAD9A9C7C, 409 | ReplayScreenshot = 0x567FAF34, 410 | ReplayHidehud = 0x7E479C7B, 411 | ReplayStartpoint = 0x5DAFACCF, 412 | ReplayEndpoint = 0x4EF75BBD, 413 | ReplayAdvance = 0x323AA450, 414 | ReplayBack = 0x088C7CD4, 415 | ReplayTools = 0x561A3387, 416 | ReplayRestart = 0x81B8BC9D, 417 | ReplayShowhotkey = 0xEBA2A41E, 418 | ReplayCyclemarkerleft = 0x5C220959, 419 | ReplayCyclemarkerright = 0xC69AE799, 420 | ReplayFovincrease = 0x5925A10D, 421 | ReplayFovdecrease = 0x2B88D701, 422 | ReplayCameraup = 0x749EFF0C, 423 | ReplayCameradown = 0xA1FE9E2A, 424 | ReplaySave = 0xEBC60685, 425 | ReplayToggletime = 0xE3FB91B3, 426 | ReplayToggletips = 0xC8A1DE20, 427 | ReplayPreview = 0x58AC1355, 428 | ReplayToggleTimeline = 0xF8629909, 429 | ReplayTimelinePickupClip = 0xD2454F90, 430 | ReplayTimelineDuplicateClip = 0x4146A033, 431 | ReplayTimelinePlaceClip = 0x60726F50, 432 | ReplayCtrl = 0xD88B47E7, 433 | ReplayTimelineSave = 0x65D70E9D, 434 | ReplayPreviewAudio = 0x79022218, 435 | ReplayActionReplayStart = 0xD9961107, 436 | ReplayActionReplayCancel = 0x93776CAE, 437 | ReplayRecordingStart = 0xFD28D0F4, 438 | ReplayRecordingStop = 0xDB16E702, 439 | ReplaySaveSnapshot = 0xEFEC8FDE, 440 | VehDriveLook = 0xA2117C9A, 441 | VehDriveLook2 = 0x55AC04E5, 442 | VehFlyAttack2 = 0x4D83147C, 443 | RadioWheelUd = 0x14C7291D, 444 | RadioWheelLr = 0xF9FA6BC8, 445 | VehSlowmoUd = 0xF1F9CD26, 446 | VehSlowmoUpOnly = 0x2B981F4F, 447 | VehSlowmoDownOnly = 0x642DE054, 448 | MapPoi = 0x9BEE9213, 449 | InteractLockon = 0xF8982F00, 450 | InteractLockonNeg = 0x26A18F47, 451 | InteractLockonPos = 0xF63A17F9, 452 | InteractLockonRob = 0x9FA5AD07, 453 | InteractLockonY = 0x09A92B8B, 454 | InteractLockonA = 0xD10A3A36, 455 | InteractNeg = 0x424BD2D2, 456 | InteractPos = 0xF6BB7378, 457 | InteractOption1 = 0x760A9C6F, 458 | InteractOption2 = 0x84543902, 459 | InteractAnimal = 0xA1ABB953, 460 | InteractLockonAnimal = 0x5415BE48, 461 | InteractLeadAnimal = 0x17D3BFF5, 462 | InteractLockonDetachHorse = 0xF5C4701B, 463 | InteractHorseCare = 0xB0BCE5D6, 464 | InteractLockonCallAnimal = 0x71F89BBC, 465 | InteractLockonTrackAnimal = 0xE2473BF0, 466 | InteractLockonTargetInfo = 0x31219490, 467 | InteractLockonStudyBinoculars = 0xB3F388BC, 468 | InteractWildAnimal = 0x89F3D2E0, 469 | InteractHorseFeed = 0x0D55A0F0, 470 | InteractHorseBrush = 0x63A38F2C, 471 | EmoteAction = 0x13C42BB2, 472 | EmoteTaunt = 0x470DC190, 473 | EmoteGreet = 0x72BAD5AA, 474 | EmoteComm = 0x661857B3, 475 | EmoteDance = 0xF311100C, 476 | EmoteTwirlGunHold = 0x04FB8191, 477 | EmoteTwirlGunVarA = 0x6990BDDF, 478 | EmoteTwirlGunVarB = 0x52D29063, 479 | EmoteTwirlGunVarC = 0xBC2AE312, 480 | EmoteTwirlGunVarD = 0xAE69478F, 481 | QuickEquipItem = 0x6070D032, 482 | MinigameBuildingCameraNext = 0x16B0EEF8, 483 | MinigameBuildingCameraPrev = 0x5F97B231, 484 | MinigameBuildingHammer = 0xFA91AECD, 485 | CursorAcceptDoubleClick = 0x1C559F2E, 486 | CursorAcceptHold = 0xE474F150, 487 | CursorAccept = 0x9D2AEA88, 488 | CursorCancel = 0x27568539, 489 | CursorCancelDoubleClick = 0x9CB4ECCE, 490 | CursorCancelHold = 0xD7F70F36, 491 | CursorX = 0xD6C4ECDC, 492 | CursorY = 0xE4130778, 493 | CursorScrollUp = 0x62800C92, 494 | CursorScrollDown = 0x8BDE7443, 495 | CursorScrollClick = 0x6AA8A71B, 496 | CursorScrollDoubleClick = 0xE1B6ED6D, 497 | CursorScrollHold = 0x5484DBDD, 498 | CursorForwardClick = 0x11DBBAB9, 499 | CursorForwardDoubleClick = 0x9805D715, 500 | CursorForwardHold = 0x7630C9A1, 501 | CursorBackwardClick = 0x9AF38793, 502 | CursorBackwardDoubleClick = 0xA14BA1FC, 503 | CursorBackwardHold = 0x01AA9FA1, 504 | EnterCheatCode = 0x7BF65AC8, 505 | InteractionMenu = 0xCC510E59, 506 | MpTextChatAll = 0x9720FCEE, 507 | MpTextChatTeam = 0x9098AD9D, 508 | MpTextChatFriends = 0x7098AC73, 509 | MpTextChatCrew = 0x8142FA92, 510 | PushToTalk = 0x4BC9DABB, 511 | CreatorLs = 0x339F3730, 512 | CreatorRs = 0xD8CF0C95, 513 | CreatorLt = 0x446258B6, 514 | CreatorRt = 0x3C3DD371, 515 | CreatorMenuToggle = 0x85D24405, 516 | CreatorAccept = 0x2CD5343E, 517 | CreatorMenuUp = 0xBCD1444B, 518 | CreatorMenuDown = 0x97410755, 519 | CreatorMenuLeft = 0xEC6A30AA, 520 | CreatorMenuRight = 0x19D8334C, 521 | CreatorMenuAccept = 0xFB9C3231, 522 | CreatorMenuCancel = 0xBB3FC460, 523 | CreatorMenuFunction = 0x5A03B3F3, 524 | CreatorMenuExtraFunction = 0xE6B8F103, 525 | CreatorMenuSelect = 0x0984E40A, 526 | CreatorPlace = 0xD74CACAD, 527 | CreatorDelete = 0x3F4DC0EF, 528 | CreatorDrop = 0x414034D5, 529 | CreatorFunction = 0xB05FDA25, 530 | CreatorRotateRight = 0x9D75674E, 531 | CreatorRotateLeft = 0xD41E9C2A, 532 | CreatorGrab = 0x338A0D45, 533 | CreatorSwitchCam = 0x16CCFEC6, 534 | CreatorZoomIn = 0x335D8D76, 535 | CreatorZoomOut = 0x24A42F93, 536 | CreatorRaise = 0x0D0FB9B1, 537 | CreatorLower = 0x1BDE2EB3, 538 | CreatorSearch = 0xF55864CD, 539 | CreatorMoveUd = 0x82428676, 540 | CreatorMoveLr = 0x59753EDC, 541 | CreatorLookUd = 0x55EA24F3, 542 | CreatorLookLr = 0xAEB2A9C7, 543 | CutFree = 0xD2CC4644, 544 | Drop = 0xD2928083, 545 | PickupCarriable = 0xEB2AC491, 546 | PickupCarriable2 = 0xBE8593AF, 547 | PlaceCarriableOntoParent = 0x7D326951, 548 | PickupCarriableFromParent = 0xA1202C7B, 549 | MercyKill = 0x956C2A0E, 550 | Revive = 0x43F2959C, 551 | Hogtie = 0xD9C50532, 552 | CarriableSuicide = 0x6E9734E8, 553 | CarriableBreakFree = 0x295175BF, 554 | InteractHitCarriable = 0x0522B243, 555 | Loot = 0x41AC83D1, 556 | Loot2 = 0x399C6619, 557 | Loot3 = 0x27D1C284, 558 | LootVehicle = 0x14DB6C5E, 559 | LootAmmo = 0xC23D7B9E, 560 | BreakVehicleLock = 0x97C71B28, 561 | LootAliveComponent = 0xFF8109D8, 562 | FeedInteract = 0xA8E3F467, 563 | SaddleTransfer = 0x73A8FD83, 564 | ShopBuy = 0xDFF812F9, 565 | ShopSell = 0x6D1319BE, 566 | ShopSpecial = 0xEA150E72, 567 | ShopBounty = 0xD3ECF82F, 568 | ShopInspect = 0x5E723D8C, 569 | ShopChangeCurrency = 0x90FA19AB, 570 | QuickUseItem = 0xC1989F95, 571 | PromptPageNext = 0x8CF90A9D, 572 | FrontendTouchZoomFactor = 0xE7F89C38, 573 | FrontendTouchZoomX = 0x16661AD0, 574 | FrontendTouchZoomY = 0x253DB87F, 575 | FrontendTouchDragX = 0xEC93548E, 576 | FrontendTouchDragY = 0x9AC130EB, 577 | FrontendTouchTapX = 0xC10E180A, 578 | FrontendTouchTapY = 0xCF4B3484, 579 | FrontendTouchDoubleTapX = 0x1661FAB0, 580 | FrontendTouchDoubleTapY = 0x96E87BBF, 581 | FrontendTouchHoldX = 0x0FF17F1D, 582 | FrontendTouchHoldY = 0x398ED257, 583 | FrontendTouchSwipeUpX = 0x0B71D439, 584 | FrontendTouchSwipeUpY = 0x19CA70EA, 585 | FrontendTouchSwipeDownX = 0xE3B30955, 586 | FrontendTouchSwipeDownY = 0xBDFF3DEA, 587 | FrontendTouchSwipeLeftX = 0x2545B0DE, 588 | FrontendTouchSwipeLeftY = 0xD43D0ECE, 589 | FrontendTouchSwipeRightX = 0xEAB68397, 590 | FrontendTouchSwipeRightY = 0x675B7CE3, 591 | MultiplayerInfoPlayers = 0x9C68CE34, 592 | MultiplayerDeadSwitchRespawn = 0xB4F298BA, 593 | MultiplayerDeadInformLaw = 0x6816A38E, 594 | MultiplayerDeadRespawn = 0x18987353, 595 | MultiplayerDeadDuel = 0xF875FC78, 596 | MultiplayerDeadParley = 0x4D11FE01, 597 | MultiplayerDeadFeud = 0xB4A11066, 598 | MultiplayerDeadLeaderFeud = 0xCC18F960, 599 | MultiplayerDeadPressCharges = 0xE50DCA13, 600 | MultiplayerRaceRespawn = 0x014CA044, 601 | MultiplayerPredatorAbility = 0xC5CF41B2, 602 | MultiplayerSpectatePlayerNext = 0xBA065692, 603 | MultiplayerSpectatePlayerPrev = 0x5092BF47, 604 | MultiplayerSpectateHideHud = 0x7DBA5D49, 605 | MultiplayerSpectatePlayerOptions = 0x4E074EE6, 606 | MultiplayerLeaderboardScrollUd = 0xA917D24B, 607 | MinigameQuit = 0xE9094BA0, 608 | MinigameIncreaseBet = 0xC7CB8D5F, 609 | MinigameDecreaseBet = 0xD3EBF425, 610 | MinigameChangeBetAxisY = 0xBDC733EE, 611 | MinigamePlaceBet = 0x410B0B2E, 612 | MinigameClearBet = 0x4A21C66B, 613 | MinigameHelp = 0x9384E0A8, 614 | MinigameHelpPrev = 0xC5F53156, 615 | MinigameHelpNext = 0x83608AC0, 616 | MinigameReplay = 0x985243B7, 617 | MinigameNewGame = 0x5D1788FF, 618 | MinigamePokerSkip = 0x646A7792, 619 | MinigamePokerCall = 0xDAB9EE72, 620 | MinigamePokerFold = 0x49B4AD1E, 621 | MinigamePokerCheck = 0x206B2087, 622 | MinigamePokerCheckFold = 0x72A9D1F7, 623 | MinigamePokerBet = 0xA9883369, 624 | MinigamePokerHoleCards = 0xC2B1193A, 625 | MinigamePokerBoardCards = 0x03753498, 626 | MinigamePokerSkipTutorial = 0xB568BCD0, 627 | MinigamePokerShowPossibleHands = 0x7765B9D4, 628 | MinigamePokerYourCards = 0xF923B337, 629 | MinigamePokerCommunityCards = 0xE402B898, 630 | MinigamePokerCheatLr = 0x2330F517, 631 | MinigameFishingResetCast = 0xB40A9BDB, 632 | MinigameFishingReleaseFish = 0xF14FD435, 633 | MinigameFishingKeepFish = 0x52C5C34A, 634 | MinigameFishingHook = 0xA1CD103A, 635 | MinigameFishingLeftAxisX = 0x69B10623, 636 | MinigameFishingLeftAxisY = 0x09BF4645, 637 | MinigameFishingRightAxisX = 0x4FD4E558, 638 | MinigameFishingRightAxisY = 0x95F2F193, 639 | MinigameFishingLeanLeft = 0x0D4C3ABA, 640 | MinigameFishingLeanRight = 0x05074A9B, 641 | MinigameFishingQuickEquip = 0x25F525CD, 642 | MinigameFishingReelSpeedUp = 0x2FA915F5, 643 | MinigameFishingReelSpeedDown = 0xD7AF56A0, 644 | MinigameFishingReelSpeedAxis = 0x49C73CB2, 645 | MinigameFishingManualReelIn = 0xA303F462, 646 | MinigameFishingManualReelOutModifier = 0x4556642C, 647 | MinigameCrackpotBoatShowControls = 0x524C3787, 648 | MinigameDominoesViewDominoes = 0x88F8B6B1, 649 | MinigameDominoesViewMoves = 0x7733CF2C, 650 | MinigameDominoesPlayTile = 0x95F5BB7C, 651 | MinigameDominoesSkipDeal = 0xC5E622D7, 652 | MinigameDominoesMoveLeftOnly = 0xFDDD89D4, 653 | MinigameDominoesMoveRightOnly = 0x7D5187C9, 654 | MinigameDominoesMoveUpOnly = 0xC6AB8CB3, 655 | MinigameDominoesMoveDownOnly = 0xFD9FC86D, 656 | MinigameBlackjackHandView = 0x03F1E7CB, 657 | MinigameBlackjackTableView = 0xADE09435, 658 | MinigameBlackjackBetAxisY = 0x3D2EA092, 659 | MinigameBlackjackBet = 0x661D8A31, 660 | MinigameBlackjackDecline = 0xCD7DDF9B, 661 | MinigameBlackjackStand = 0x31260507, 662 | MinigameBlackjackHit = 0xA8142713, 663 | MinigameBlackjackDouble = 0x74486CA4, 664 | MinigameBlackjackSplit = 0x432B111F, 665 | MinigameFffA = 0x0E717DC6, 666 | MinigameFffB = 0x1BC81873, 667 | MinigameFffX = 0x65F0ACDF, 668 | MinigameFffY = 0x73AD4858, 669 | MinigameFffZoom = 0x61E4CACC, 670 | MinigameFffSkipTurn = 0x3073681B, 671 | MinigameFffCycleSequenceLeft = 0x29A3550E, 672 | MinigameFffCycleSequenceRight = 0x7B5B896D, 673 | MinigameFffFlourishContinue = 0x6FC9DE68, 674 | MinigameFffFlourishEnd = 0xF7750B25, 675 | MinigameFffPractice = 0xCA379F82, 676 | MinigameMilkingLeftAction = 0xFF4B2ADA, 677 | MinigameMilkingRightAction = 0x30BE7CF2, 678 | MinigameLeftTrigger = 0x7EC33553, 679 | MinigameRightTrigger = 0xBE78B715, 680 | MinigameActionLeft = 0x0A1EFC09, 681 | MinigameActionRight = 0x16D70379, 682 | MinigameActionUp = 0xF5A13A0D, 683 | MinigameActionDown = 0xF601BCFC, 684 | StickyFeedAccept = 0xF4DD4C67, 685 | StickyFeedCancel = 0x0CFB963F, 686 | StickyFeedX = 0xBD1D94A1, 687 | StickyFeedY = 0xC85BAB1D, 688 | CameraPutAway = 0x5FC770EA, 689 | CameraBack = 0xA4BD74A5, 690 | CameraTakePhoto = 0x44FA14C2, 691 | CameraContextGallery = 0xE8337356, 692 | CameraHandheldUse = 0x776F65E9, 693 | CameraDof = 0x3003F9DC, 694 | CameraSelfie = 0xAC5922EA, 695 | CameraZoom = 0x47EC4C22, 696 | CameraPoseNext = 0xF810FB35, 697 | CameraPosePrev = 0x8D5BE9D1, 698 | CameraExpressionNext = 0xCFA703D3, 699 | CameraExpressionPrev = 0x07B6435D, 700 | TithingIncreaseAmount = 0x24F37AB5, 701 | TithingDecreaseAmount = 0xCEFF5C13, 702 | BreakDoorLock = 0x77110B0A, 703 | InterrogateQuestion = 0xA1AA2D8D, 704 | InterrogateBeat = 0x6E1E0D62, 705 | InterrogateKill = 0x81B2E311, 706 | InterrogateRelease = 0x3C22EF0E, 707 | CampBedInspect = 0xC67E13BB, 708 | PcFreeLook = 0x8AAA0AD4, 709 | MinigameBartenderRaiseGlass = 0xA13460F5, 710 | MinigameBartenderRaiseBottle = 0xF0A25112, 711 | MinigameBartenderPour = 0xCABC2460, 712 | MinigameBartenderServe = 0xDC03B043, 713 | PhotoMode = 0x3C0A40F2, 714 | PhotoModePc = 0x35957F6C, 715 | PhotoModeChangeCamera = 0x9F06B29C, 716 | PhotoModeMoveLr = 0x4F136512, 717 | PhotoModeMoveLeftOnly = 0x311353EB, 718 | PhotoModeMoveRightOnly = 0x5357A7F5, 719 | PhotoModeMoveUd = 0xEC001315, 720 | PhotoModeMoveUpOnly = 0x315D57E6, 721 | PhotoModeMoveDownOnly = 0x4EBCC409, 722 | PhotoModeReset = 0xA209BD57, 723 | PhotoModeLenseNext = 0xB138D899, 724 | PhotoModeLensePrev = 0x06A057F8, 725 | PhotoModeRotateLeft = 0x2EEA1D2A, 726 | PhotoModeRotateRight = 0x96E70854, 727 | PhotoModeToggleHud = 0x7F9055F5, 728 | PhotoModeViewPhotos = 0xDCE96D67, 729 | PhotoModeTakePhoto = 0xA190AAC7, 730 | PhotoModeBack = 0x2F13EC9A, 731 | PhotoModeSwitchMode = 0x8F32E2EB, 732 | PhotoModeFilterIntensity = 0xFE6DD360, 733 | PhotoModeFilterIntensityUp = 0x2286D46B, 734 | PhotoModeFilterIntensityDown = 0xB341F407, 735 | PhotoModeFocalLength = 0x886ABA4E, 736 | PhotoModeFocalLengthUpOnly = 0xFAFBD66A, 737 | PhotoModeFocalLengthDownOnly = 0x01EBFABD, 738 | PhotoModeFilterNext = 0x699F8D08, 739 | PhotoModeFilterPrev = 0x4F640885, 740 | PhotoModeZoomIn = 0x5B843BC9, 741 | PhotoModeZoomOut = 0x2354D2E6, 742 | PhotoModeDof = 0x26B9AE6A, 743 | PhotoModeDofUpOnly = 0x87B07940, 744 | PhotoModeDofDownOnly = 0x047099F1, 745 | PhotoModeExposureUp = 0xC64E2284, 746 | PhotoModeExposureDown = 0xAD07A5A5, 747 | PhotoModeExposureLock = 0x9DE08D71, 748 | PhotoModeContrast = 0x483F707F, 749 | PhotoModeContrastUpOnly = 0x5D2DD717, 750 | PhotoModeContrastDownOnly = 0x30811620, 751 | CraftingEat = 0xB99A9CAD, 752 | CampSetupTent = 0x0B1BE2E8, 753 | MinigameActionX = 0x1D927DF2, 754 | DeprecatedAbove = 0xC1D24F92, 755 | ScriptLeftAxisX = 0x1F8EEF84, 756 | ScriptLeftAxisY = 0x5418D8AB, 757 | ScriptRightAxisX = 0xA6B769E9, 758 | ScriptRightAxisY = 0x27A5EBC0, 759 | ScriptRup = 0x771D6E13, 760 | ScriptRdown = 0x37933367, 761 | ScriptRleft = 0xA4DB0458, 762 | ScriptRright = 0x22A3B800, 763 | ScriptLb = 0xE624C062, 764 | ScriptRb = 0x91E9231C, 765 | ScriptLt = 0x2B314A1E, 766 | ScriptRt = 0x26E9CD17, 767 | ScriptLs = 0xAADDC975, 768 | ScriptRs = 0xD04E9FE2, 769 | ScriptPadUp = 0x0DC15ADD, 770 | ScriptPadDown = 0xB1DA5574, 771 | ScriptPadLeft = 0x1AF81D9E, 772 | ScriptPadRight = 0x82A9B758, 773 | ScriptSelect = 0xC8722109, 774 | ScriptedFlyUd = 0xAEB4B1DE, 775 | ScriptedFlyLr = 0xF1111E4A, 776 | ScriptedFlyZup = 0x639B9FC9, 777 | ScriptedFlyZdown = 0x9C5E030C, 778 | Count = 0x8EDFFB30, 779 | } 780 | } 781 | #endif --------------------------------------------------------------------------------