├── .github └── FUNDING.yml ├── LICENSE.md ├── README.md ├── client ├── main.lua └── menus │ ├── misc │ ├── connection.lua │ ├── developer.lua │ ├── main.lua │ └── teleport.lua │ ├── online_players.lua │ ├── player │ ├── main.lua │ ├── options.lua │ └── weapons.lua │ ├── recording.lua │ ├── vehicles │ ├── main.lua │ ├── options.lua │ ├── personal.lua │ └── spawner.lua │ └── world.lua ├── config ├── locations.lua └── weapons.lua ├── fxmanifest.lua ├── perms.cfg └── server ├── main.lua ├── misc.lua ├── players.lua ├── teleports.lua ├── time.lua ├── vehicles.lua └── weather.lua /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: BerkieB -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Tom Grobbe - https://www.vespura.com/ 2 | BerkieB - https://github.com/BerkieBb 3 | 4 | Copyright © 2023 5 | 6 | You can use and edit this code to your liking as long as you don't ever claim it to be your own code and always provide proper credit. You're not allowed to sell bMenu or any code you take from it. If you want to release your own version of bMenu, you have to link the original GitHub repository, or release it via a forked repository. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bMenu 2 | 3 | bMenu is a lua version of vMenu, I came up with doing this because vMenu is basically abandoned now but it's still a great resource, I could've kept it in C# but I wanted to do it in lua because more people understand it so people can contribute more easily. This is not finished and is still a work in progress, if you want to contribute, feel free to do so by forking this repository and making a pull request. Please don't use this in a live environment yet as no permissions besides one for the command/keybind are setup and this is still unfinished. 4 | 5 | Another note, please don't change the name of this resource when using this, I will be setting up kvp like done in vMenu soon and that will require the resource name to be the same in every server for the user settings to load. 6 | 7 | Releases will be pushed once version 1.0.0 is ready, I won't be updating the version until I find that the menu is ready-to-use (thus 1.0.0 will be created). 8 | 9 | If you've got any ideas that you'd like implemented or things you'd want changed, feel free to make an issue or a pull request with the changes you want. 10 | 11 | *Yes I took the license from vMenu and edited it a bit to match this name and add myself to it, it's a good license.* 12 | 13 | ## Requirements 14 | 15 | Without any of the following requirements, this resource won't start 16 | 17 | * [ox_lib](https://github.com/overextended/ox_lib/releases) 18 | * OneSync Infinity enabled 19 | * [FXServer](https://runtime.fivem.net/artifacts/fivem/) 5904 or above 20 | 21 | ## Credits 22 | 23 | * [vMenu](https://github.com/TomGrobbe/vMenu) made by [TomGrobbe](https://github.com/TomGrobbe) 24 | 25 | ## Contact 26 | 27 | If you want to contact me, you can do that in either the issues or on Discord by adding me as a friend by my username `berkieb` or by joining my [discord](https://discord.gg/ZpqEZWSvZU) 28 | 29 | ## License 30 | 31 | [Same as vMenu but a little edited but kept TomGrobbe in there](LICENSE.md) -------------------------------------------------------------------------------- /client/main.lua: -------------------------------------------------------------------------------- 1 | --#region Startup 2 | 3 | -- I don't want you renaming this because this uses kvp and that requires the resource name to be the same across servers to work correctly 4 | if GetCurrentResourceName() ~= 'bMenu' then 5 | error('Please don\'t rename this resource, change the folder name (back) to \'bMenu\' (case sensitive) to make sure the saved data can be saved and fetched accordingly from the cache.') 6 | return 7 | end 8 | 9 | --#endregion Startup 10 | 11 | --#region Variables 12 | 13 | MenuOpen = false 14 | MenuPosition = 'top-right' 15 | MenuIndexes = {} 16 | 17 | --#endregion Variables 18 | 19 | --#region Functions 20 | 21 | ---@param v number 22 | ---@return integer 23 | function math.sign(v) 24 | return v >= 0 and 1 or -1 25 | end 26 | 27 | ---@param value any 28 | ---@param array any[] 29 | ---@return boolean 30 | function ArrayIncludes(value, array) 31 | for i = 1, #array do 32 | local arrayVal = array[i] 33 | if value == arrayVal then 34 | return true 35 | end 36 | end 37 | 38 | return false 39 | end 40 | 41 | ---@param str string 42 | function ToProperCase(str) 43 | return string.gsub(str, '(%a)([%w_\']*)', function(first, rest) 44 | return first:upper()..rest:lower() 45 | end) 46 | end 47 | 48 | ---@param isFullMenuClose boolean 49 | ---@param keyPressed? string 50 | ---@param previousMenu? string 51 | function CloseMenu(isFullMenuClose, keyPressed, previousMenu) 52 | if isFullMenuClose or not keyPressed or not previousMenu or keyPressed == 'Escape' then 53 | lib.hideMenu(false) 54 | MenuOpen = false 55 | return 56 | end 57 | 58 | lib.showMenu(previousMenu, MenuIndexes[previousMenu]) 59 | end 60 | 61 | ---@param text string 62 | ---@param x number 63 | ---@param y number 64 | ---@param size? number 65 | ---@param position? 0 | 1 | 2 0: center | 1: left | 2: right 66 | ---@param font? number 67 | ---@param disableTextOutline? boolean 68 | function DrawTextOnScreen(text, x, y, size, position, font, disableTextOutline) 69 | if 70 | not IsHudPreferenceSwitchedOn() 71 | or IsHudHidden() 72 | or IsPlayerSwitchInProgress() 73 | or IsScreenFadedOut() 74 | or IsPauseMenuActive() 75 | or IsFrontendFading() 76 | or IsPauseMenuRestarting() 77 | then 78 | return 79 | end 80 | 81 | size = size or 0.48 82 | position = position or 1 83 | font = font or 6 84 | 85 | SetTextFont(font) 86 | SetTextScale(1.0, size) 87 | if position == 2 then 88 | SetTextWrap(0, x) 89 | end 90 | SetTextJustification(position) 91 | if not disableTextOutline then 92 | SetTextOutline() 93 | end 94 | BeginTextCommandDisplayText('STRING') 95 | AddTextComponentSubstringPlayerName(text) 96 | EndTextCommandDisplayText(x, y) 97 | end 98 | 99 | ---@param pos vector3 100 | ---@param safeModeDisabled? boolean 101 | function TeleportToCoords(pos, safeModeDisabled) 102 | if safeModeDisabled then 103 | RequestCollisionAtCoord(pos.x, pos.y, pos.z) 104 | local entity = cache.seat == -1 and cache.vehicle or cache.ped 105 | SetEntityCoords(entity, pos.x, pos.y, pos.z, false, false, false, true) 106 | return 107 | end 108 | 109 | local vehicleRestoreVisibility = IsInVehicle(true) and IsEntityVisible(cache.vehicle) 110 | local pedRestoreVisibility = IsEntityVisible(cache.ped) 111 | 112 | if IsInVehicle(true) then 113 | FreezeEntityPosition(cache.vehicle, true) 114 | if IsEntityVisible(cache.vehicle) then 115 | NetworkFadeOutEntity(cache.vehicle, true, false) 116 | end 117 | else 118 | ClearPedTasksImmediately(cache.ped) 119 | FreezeEntityPosition(cache.ped, true) 120 | if IsEntityVisible(cache.ped) then 121 | NetworkFadeOutEntity(cache.ped, true, false) 122 | end 123 | end 124 | 125 | DoScreenFadeOut(500) 126 | while not IsScreenFadedOut() do 127 | Wait(0) 128 | end 129 | 130 | local groundZ = 850 131 | local found = false 132 | for zz = 950, 0, -25 do 133 | local z = zz 134 | if zz % 2 ~= 0 then 135 | z = 950 - zz 136 | end 137 | 138 | RequestCollisionAtCoord(pos.x, pos.y, z) 139 | 140 | NewLoadSceneStart(pos.x, pos.y, z, pos.x, pos.y, z, 50, 0) 141 | 142 | local tempTimer = GetGameTimer() 143 | 144 | while IsNetworkLoadingScene() do 145 | if GetGameTimer() - tempTimer > 1000 then 146 | break 147 | end 148 | Wait(0) 149 | end 150 | 151 | SetEntityCoords(IsInVehicle(true) and cache.vehicle or cache.ped, pos.x, pos.y, z, false, false, false, true) 152 | 153 | tempTimer = GetGameTimer() 154 | 155 | while not HasCollisionLoadedAroundEntity(cache.ped) do 156 | if GetGameTimer() - tempTimer > 1000 then 157 | return 158 | end 159 | Wait(0) 160 | end 161 | 162 | found, groundZ = GetGroundZFor_3dCoord(pos.x, pos.y, z, false) 163 | 164 | if found then 165 | if IsInVehicle(true) then 166 | SetEntityCoords(cache.vehicle, pos.x, pos.y, groundZ, false, false, false, true) 167 | FreezeEntityPosition(cache.vehicle, false) 168 | SetVehicleOnGroundProperly(cache.vehicle) 169 | FreezeEntityPosition(cache.vehicle, true) 170 | else 171 | SetEntityCoords(cache.ped, pos.x, pos.y, groundZ, false, false, false, true) 172 | end 173 | break 174 | end 175 | 176 | Wait(10) 177 | end 178 | 179 | if not found then 180 | local result, safePos = GetNthClosestVehicleNode(pos.x, pos.y, pos.z, 0, 0, 0, 0) 181 | if not result or not safePos then 182 | safePos = pos 183 | end 184 | 185 | if IsInVehicle(true) then 186 | SetEntityCoords(cache.vehicle, safePos.x, safePos.y, safePos.z, false, false, false, true) 187 | FreezeEntityPosition(cache.vehicle, false) 188 | SetVehicleOnGroundProperly(cache.vehicle) 189 | FreezeEntityPosition(cache.vehicle, true) 190 | else 191 | SetEntityCoords(cache.ped, safePos.x, safePos.y, safePos.z, false, false, false, true) 192 | end 193 | end 194 | 195 | if IsInVehicle(true) then 196 | if vehicleRestoreVisibility then 197 | NetworkFadeInEntity(cache.vehicle, true) 198 | if not pedRestoreVisibility then 199 | SetEntityVisible(cache.ped, false, false) 200 | end 201 | end 202 | FreezeEntityPosition(cache.vehicle, false) 203 | else 204 | if pedRestoreVisibility then 205 | NetworkFadeInEntity(cache.ped, true) 206 | end 207 | FreezeEntityPosition(cache.ped, false) 208 | end 209 | 210 | DoScreenFadeIn(500) 211 | SetGameplayCamRelativePitch(0.0, 1.0) 212 | end 213 | 214 | function TeleportToWaypoint() 215 | if not IsWaypointActive() then return end 216 | 217 | local waypointBlipInfo = GetFirstBlipInfoId(GetWaypointBlipEnumId()) 218 | local waypointBlipPos = waypointBlipInfo ~= 0 and GetBlipInfoIdType(waypointBlipInfo) == 4 and GetBlipInfoIdCoord(waypointBlipInfo) or vec2(0, 0) 219 | RequestCollisionAtCoord(waypointBlipPos.x, waypointBlipPos.y, 1000) 220 | local result, z = GetGroundZFor_3dCoord(waypointBlipPos.x, waypointBlipPos.y, 1000, false) 221 | if not result then 222 | z = 0 223 | end 224 | 225 | waypointBlipPos = vec3(waypointBlipPos.x, waypointBlipPos.y, z) 226 | 227 | TeleportToCoords(waypointBlipPos) 228 | end 229 | 230 | --#endregion Functions 231 | 232 | --#region Commands 233 | 234 | RegisterCommand('bmenu', function() 235 | local hasPermission = lib.callback.await('bMenu:server:hasCommandPermission', false, 'bmenu') 236 | if not hasPermission then 237 | MenuOpen = false -- Making sure this property is set accordingly 238 | return 239 | end 240 | 241 | MenuOpen = not MenuOpen 242 | 243 | if MenuOpen then 244 | local perms = lib.callback.await('bMenu:server:hasConvarPermission', false, 'Main', {'OnlinePlayers', 'PlayerRelated', 'VehicleRelated', 'WorldRelated', 'Recording', 'Misc'}) 245 | local menuOptions = { 246 | {label = 'You don\'t have access to anything', icon = 'face-sad-tear', args = {'none'}} 247 | } 248 | 249 | local index = 1 250 | 251 | if perms.OnlinePlayers then 252 | menuOptions[index] = {label = 'Online Players', icon = 'user-group', args = {'bMenu_online_players'}} 253 | index += 1 254 | end 255 | 256 | if perms.PlayerRelated then 257 | menuOptions[index] = {label = 'Player Related Options', icon = 'user-gear', args = {'bMenu_player_related_options'}} 258 | index += 1 259 | end 260 | 261 | if perms.VehicleRelated then 262 | menuOptions[index] = {label = 'Vehicle Related Options', icon = 'car', args = {'bMenu_vehicle_related_options'}} 263 | index += 1 264 | end 265 | 266 | if perms.WorldRelated then 267 | menuOptions[index] = {label = 'World Related Options', icon = 'globe', args = {'bMenu_world_related_options'}} 268 | index += 1 269 | end 270 | 271 | if perms.Recording then 272 | menuOptions[index] = {label = 'Recording Options', icon = 'video', args = {'bMenu_recording_options'}} 273 | index += 1 274 | end 275 | 276 | if perms.Misc then 277 | menuOptions[index] = {label = 'Miscellaneous Options', icon = 'gear', description = 'Show all options that don\'t fit in the other categories', args = {'bMenu_misc_options'}} 278 | index += 1 279 | end 280 | 281 | lib.registerMenu({ 282 | id = 'bMenu_main', 283 | title = 'Berkie Menu', 284 | position = MenuPosition, 285 | onClose = function() 286 | CloseMenu(true) 287 | end, 288 | onSelected = function(selected) 289 | MenuIndexes['bMenu_main'] = selected 290 | end, 291 | options = menuOptions 292 | }, function(_, _, args) 293 | if args[1] == 'none' then return end 294 | 295 | if args[1] == 'bMenu_online_players' then 296 | CreatePlayerMenu() 297 | elseif args[1] == 'bMenu_player_related_options' then 298 | CreatePlayerOptionsMenu() 299 | elseif args[1] == 'bMenu_vehicle_related_options' then 300 | CreateVehicleOptionsMenu() 301 | elseif args[1] == 'bMenu_world_related_options' then 302 | CreateWorldMenu() 303 | elseif args[1] == 'bMenu_recording_options' then 304 | CreateRecordingMenu() 305 | elseif args[1] == 'bMenu_misc_options' then 306 | CreateMiscMenu() 307 | end 308 | 309 | lib.showMenu(args[1], MenuIndexes[args[1]]) 310 | end) 311 | 312 | lib.showMenu('bMenu_main', MenuIndexes['bMenu_main']) 313 | return 314 | end 315 | 316 | lib.hideMenu(true) 317 | end, false) 318 | 319 | RegisterKeyMapping('bmenu', 'Open Menu', 'KEYBOARD', 'M') 320 | 321 | AddEventHandler('onResourceStop', function(resource) 322 | if resource ~= GetCurrentResourceName() or not MenuOpen then return end 323 | 324 | CloseMenu(true) 325 | end) 326 | 327 | --#endregion Commands 328 | 329 | --[[ 330 | Things I have to do before the 1.0.0 release 331 | 332 | Finish Misc menu 333 | 334 | Extend private message functionality (under online players) 335 | 336 | Add Player Appearance menu (under player related options) 337 | 338 | Add MP Ped Customization menu (under player related options) 339 | 340 | Add components to Weapon Options menu (under player related options) 341 | 342 | Add Weapon Loadouts menu (under player related options) 343 | 344 | Add Saved Vehicles menu (under vehicle related options) 345 | 346 | Add About menu 347 | 348 | Add updates that vMenu didn't have 349 | ]] 350 | -------------------------------------------------------------------------------- /client/menus/misc/connection.lua: -------------------------------------------------------------------------------- 1 | --#region Functions 2 | 3 | function SetupConnectionOptions() 4 | local perms = lib.callback.await('bMenu:server:hasConvarPermission', false, {'Misc', 'ConnectionOptions'}, {'Quit_Join_Session', 'Quit_Game'}) 5 | local menuOptions = { 6 | {label = 'No access', description = 'You don\'t have access to any options, press enter to return', args = {'bMenu_misc_options'}} 7 | } 8 | local index = 1 9 | 10 | if perms.Quit_Join_Session then 11 | menuOptions[index] = {label = 'Quit Session', description = 'Leaves you connected to the server, but quits the network session. Can not be used when you are the host', args = {'quit_session'}, close = false} 12 | index += 1 13 | 14 | menuOptions[index] = {label = 'Rejoin Session', description = 'This may not work in all cases, but you can try to use this if you want to re-join the previous session after clicking \'Quit Session\'', args = {'rejoin_session'}, close = false} 15 | index += 1 16 | end 17 | 18 | if perms.Quit_Game then 19 | menuOptions[index] = {label = 'Quit Game', description = 'Exits the game after 5 seconds', args = {'quit_game'}} 20 | index += 1 21 | end 22 | 23 | lib.registerMenu({ 24 | id = 'bMenu_misc_options_connection_options', 25 | title = 'Connection Options', 26 | position = MenuPosition, 27 | onClose = function(keyPressed) 28 | CloseMenu(false, keyPressed, 'bMenu_misc_options') 29 | end, 30 | onSelected = function(selected) 31 | MenuIndexes['bMenu_misc_options_connection_options'] = selected 32 | end, 33 | options = menuOptions 34 | }, function(_, _, args) 35 | if args[1] == 'bMenu_misc_options' then 36 | lib.showMenu(args[1], MenuIndexes[args[1]]) 37 | return 38 | end 39 | 40 | if args[1] == 'quit_session' then 41 | if not NetworkIsSessionActive() then 42 | lib.notify({ 43 | description = 'You are currently not in a session', 44 | type = 'error' 45 | }) 46 | return 47 | end 48 | 49 | if NetworkIsHost() then 50 | lib.notify({ 51 | description = 'You cannot leave the session that you\'re the host of', 52 | type = 'error' 53 | }) 54 | return 55 | end 56 | 57 | NetworkSessionEnd(true, true) 58 | elseif args[1] == 'rejoin_session' then 59 | if NetworkIsSessionActive() then 60 | lib.notify({ 61 | description = 'You are already in a session', 62 | type = 'error' 63 | }) 64 | return 65 | end 66 | 67 | NetworkSessionHost(-1, lib.callback.await('bMenu:server:getMaxClients', false), false) 68 | elseif args[1] == 'quit_game' then 69 | lib.notify({ 70 | description = 'The game will exit in 5 seconds', 71 | type = 'inform' 72 | }) 73 | Wait(5000) 74 | RestartGame() 75 | end 76 | end) 77 | end 78 | 79 | --#endregion Functions -------------------------------------------------------------------------------- /client/menus/misc/main.lua: -------------------------------------------------------------------------------- 1 | --#region Variables 2 | 3 | local enablePMs = true 4 | local speedKmh = false 5 | local speedMph = false 6 | local displayLocation = false 7 | local showTime = false 8 | local joinNotifs = GetConvarInt('chat_showJoins', 1) == 1 9 | local quitNotifs = GetConvarInt('chat_showQuits', 1) == 1 10 | local deathNotifs = false 11 | local nightVision = false 12 | local thermalVision = false 13 | local playerNames = false 14 | local driftMode = false 15 | local playerNamesDistance = 500 16 | local safeZoneSizeX = (1 / GetSafeZoneSize() / 3) - 0.358 17 | local gamerTags = {} 18 | local menuPositionIndexes = { 19 | ['top-right'] = 1, 20 | ['top-left'] = 2, 21 | ['bottom-left'] = 3, 22 | ['bottom-right'] = 4 23 | } 24 | 25 | local menuPositionNames = {} 26 | 27 | for k, v in pairs(menuPositionIndexes) do 28 | menuPositionNames[v] = k 29 | end 30 | 31 | --#endregion Variables 32 | 33 | --#region Functions 34 | 35 | function CreateMiscMenu() 36 | local perms = lib.callback.await('bMenu:server:hasConvarPermission', false, 'Misc', {'TeleportOptions', 'DevTools', 'ConnectionOptions', 'Menu_Position', 'Toggle_PM', 'Show_Speed', 'Display_Location', 'Show_Time', 'Toggle_Join_Notifications', 'Toggle_Quit_Notifications', 'Toggle_Death_Notifications', 'Toggle_Night_Vision', 'Toggle_Thermal_Vision', 'Toggle_Player_Names'}) 37 | local menuOptions = { 38 | {label = 'No access', description = 'You don\'t have access to any options, press enter to return', args = {'bMenu_main'}} 39 | } 40 | local index = 1 41 | 42 | if perms.TeleportOptions then 43 | menuOptions[index] = {label = 'Teleport Options', args = {'bMenu_misc_options_teleport_options'}} 44 | index += 1 45 | end 46 | 47 | if perms.DevTools then 48 | menuOptions[index] = {label = 'Development Tools', args = {'bMenu_misc_options_developer_options'}} 49 | index += 1 50 | end 51 | 52 | if perms.ConnectionOptions then 53 | menuOptions[index] = {label = 'Connection Options', args = {'bMenu_misc_options_connection_options'}} 54 | index += 1 55 | end 56 | 57 | if perms.Menu_Position then 58 | menuOptions[index] = {label = 'Menu Position', values = {'Top Left', 'Top Right', 'Bottom Left', 'Bottom Right'}, defaultIndex = menuPositionIndexes[MenuPosition], args = {'menu_position'}, close = false} 59 | index += 1 60 | end 61 | 62 | if perms.Toggle_PM then 63 | menuOptions[index] = {label = 'Enable Private Messages', description = 'If this is enabled, other people can send you a private message via the online players tab', checked = enablePMs, args = {'enable_pms'}, close = false} 64 | index += 1 65 | end 66 | 67 | if perms.Show_Speed then 68 | menuOptions[index] = {label = 'Show Speed KM/h', description = 'Show a speedometer on your screen indicating your speed in KM/h', checked = speedKmh, args = {'speed_kmh'}, close = false} 69 | index += 1 70 | 71 | menuOptions[index] = {label = 'Show Speed MPH', description = 'Show a speedometer on your screen indicating your speed in MPH', checked = speedMph, args = {'speed_mph'}, close = false} 72 | index += 1 73 | end 74 | 75 | if perms.Display_Location then 76 | menuOptions[index] = {label = 'Display Location', description = 'Shows your current location and heading, as well as the nearest cross road. WARNING: you should not keep this on at all times as this is a very heavy action', checked = displayLocation, args = {'display_location'}, close = false} 77 | index += 1 78 | end 79 | 80 | if perms.Show_Time then 81 | menuOptions[index] = {label = 'Show Time', description = 'Shows you the current time on screen', checked = showTime, args = {'show_time'}, close = false} 82 | index += 1 83 | end 84 | 85 | if perms.Toggle_Join_Notifications then 86 | menuOptions[index] = {label = 'Show Join Notifications', description = 'Receive notifications when someone joins the server', checked = joinNotifs, args = {'show_join_notifs'}, close = false} 87 | index += 1 88 | end 89 | 90 | if perms.Toggle_Quit_Notifications then 91 | menuOptions[index] = {label = 'Show Quit Notifications', description = 'Receive notifications when someone leaves the server', checked = quitNotifs, args = {'show_quit_notifs'}, close = false} 92 | index += 1 93 | end 94 | 95 | if perms.Toggle_Death_Notifications then 96 | menuOptions[index] = {label = 'Show Death Notifications', description = 'Receive notifications when someone dies or gets killed', checked = deathNotifs, args = {'show_death_notifs'}, close = false} 97 | index += 1 98 | end 99 | 100 | if perms.Toggle_Night_Vision then 101 | menuOptions[index] = {label = 'Toggle Night Vision', description = 'Enable or disable night vision', checked = nightVision, args = {'night_vision'}, close = false} 102 | index += 1 103 | end 104 | 105 | if perms.Toggle_Thermal_Vision then 106 | menuOptions[index] = {label = 'Toggle Thermal Vision', description = 'Enable or disable thermal vision', checked = thermalVision, args = {'thermal_vision'}, close = false} 107 | index += 1 108 | end 109 | 110 | if perms.Toggle_Player_Names then 111 | menuOptions[index] = {label = 'Show Player Names', description = 'Enables or disables players names over their head', checked = playerNames, args = {'show_player_names'}, close = false} 112 | index += 1 113 | end 114 | 115 | lib.registerMenu({ 116 | id = 'bMenu_misc_options', 117 | title = 'Miscellaneous Options', 118 | position = MenuPosition, 119 | onClose = function(keyPressed) 120 | CloseMenu(false, keyPressed, 'bMenu_main') 121 | end, 122 | onSelected = function(selected) 123 | MenuIndexes['bMenu_misc_options'] = selected 124 | end, 125 | onCheck = function(selected, checked, args) 126 | if args[1] == 'enable_pms' then 127 | enablePMs = checked 128 | lib.setMenuOptions('bMenu_misc_options', {label = 'Enable Private Messages', description = 'If this is enabled, other people can send you a private message via the online players tab', checked = checked, args = {'enable_pms'}, close = false}, selected) 129 | elseif args[1] == 'speed_kmh' then 130 | speedKmh = checked 131 | lib.setMenuOptions('bMenu_misc_options', {label = 'Show Speed KM/H', checked = checked, args = {'speed_kmh'}, close = false}, selected) 132 | elseif args[1] == 'speed_mph' then 133 | speedMph = checked 134 | lib.setMenuOptions('bMenu_misc_options', {label = 'Show Speed MPH', checked = checked, args = {'speed_mph'}, close = false}, selected) 135 | elseif args[1] == 'display_location' then 136 | displayLocation = checked 137 | lib.setMenuOptions('bMenu_misc_options', {label = 'Display Location', description = 'Shows your current location and heading, as well as the nearest cross road', checked = checked, args = {'display_location'}, close = false}, selected) 138 | elseif args[1] == 'show_time' then 139 | showTime = checked 140 | lib.setMenuOptions('bMenu_misc_options', {label = 'Show Time', description = 'Shows you the current time on screen', checked = checked, args = {'show_time'}, close = false}, selected) 141 | elseif args[1] == 'show_join_notifs' then 142 | joinNotifs = checked 143 | lib.callback.await('bMenu:server:setConvar', false, 'chat_showJoins', checked and 1 or 0, true, true, 'bMenu_misc_options', {label = 'Show Join Notifications', description = 'Receive notifications when someone joins the server', checked = checked, args = {'show_join_notifs'}, close = false}, selected) 144 | lib.setMenuOptions('bMenu_misc_options', {label = 'Show Join Notifications', description = 'Receive notifications when someone joins the server', checked = checked, args = {'show_join_notifs'}, close = false}, selected) 145 | elseif args[1] == 'show_quit_notifs' then 146 | quitNotifs = checked 147 | lib.callback.await('bMenu:server:setConvar', false, 'chat_showQuits', checked and 1 or 0, true, true, 'bMenu_misc_options', {label = 'Show Quit Notifications', description = 'Receive notifications when someone leaves the server', checked = checked, args = {'show_quit_notifs'}, close = false}, selected) 148 | lib.setMenuOptions('bMenu_misc_options', {label = 'Show Quit Notifications', description = 'Receive notifications when someone leaves the server', checked = checked, args = {'show_quit_notifs'}, close = false}, selected) 149 | elseif args[1] == 'show_death_notifs' then 150 | deathNotifs = checked 151 | lib.callback.await('bMenu:server:setConvar', false, 'bMenu_showDeaths', checked and 1 or 0) 152 | lib.setMenuOptions('bMenu_misc_options', {label = 'Show Death Notifications', description = 'Receive notifications when someone dies or gets killed', checked = deathNotifs, args = {'show_death_notifs'}, close = false}, selected) 153 | elseif args[1] == 'night_vision' then 154 | nightVision = checked 155 | SetNightvision(checked) 156 | lib.setMenuOptions('bMenu_misc_options', {label = 'Toggle Night Vision', description = 'Enable or disable night vision', checked = checked, args = {'night_vision'}, close = false}, selected) 157 | elseif args[1] == 'thermal_vision' then 158 | thermalVision = checked 159 | SetSeethrough(checked) 160 | lib.setMenuOptions('bMenu_misc_options', {label = 'Toggle Thermal Vision', description = 'Enable or disable thermal vision', checked = checked, args = {'thermal_vision'}, close = false}, selected) 161 | elseif args[1] == 'show_player_names' then 162 | playerNames = checked 163 | lib.setMenuOptions('bMenu_misc_options', {label = 'Show Player Names', description = 'Enables or disables players names over their head', checked = checked, args = {'show_player_names'}, close = false}, selected) 164 | end 165 | end, 166 | onSideScroll = function(selected, scrollIndex, args) 167 | if args[1] == 'menu_position' then 168 | MenuPosition = menuPositionNames[scrollIndex] 169 | lib.setMenuOptions('bMenu_misc_options', {label = 'Menu Position', values = {'Top Left', 'Top Right', 'Bottom Left', 'Bottom Right'}, defaultIndex = menuPositionIndexes[MenuPosition], args = {'menu_position'}, close = false}, selected) 170 | lib.hideMenu(false) 171 | lib.showMenu('bMenu_misc_options', MenuIndexes['bMenu_misc_options']) 172 | end 173 | end, 174 | options = menuOptions 175 | }, function(_, _, args) 176 | if string.match(args[1], 'bMenu') then 177 | if args[1] == 'bMenu_misc_options_teleport_options' then 178 | SetupTeleportOptions() 179 | elseif args[1] == 'bMenu_misc_options_connection_options' then 180 | SetupConnectionOptions() 181 | elseif args[1] == 'bMenu_misc_options_developer_options' then 182 | SetupDeveloperOptions() 183 | end 184 | 185 | lib.showMenu(args[1], MenuIndexes[args[1]]) 186 | end 187 | end) 188 | end 189 | 190 | --#endregion Functions 191 | 192 | --#region Events 193 | 194 | AddEventHandler('gameEventTriggered', function(name, args) 195 | if name ~= 'CEventNetworkEntityDamage' or not deathNotifs then return end 196 | local victim, victimDied = args[1], args[4] 197 | if not victimDied or not IsPedAPlayer(victim) or (not IsPedDeadOrDying(victim, true) and not IsPedFatallyInjured(victim)) then return end 198 | local player = NetworkGetPlayerIndexFromPed(victim) 199 | local killerPed = GetPedSourceOfDeath(victim) 200 | local killerPlayer = NetworkGetPlayerIndexFromPed(killerPed) 201 | local deathCause = GetPedCauseOfDeath(victim) 202 | local description = killerPed ~= cache.ped and NetworkIsPlayerActive(killerPlayer) and ('%s was killed by %s with %s'):format(GetPlayerName(player), GetPlayerName(killerPlayer), deathCause) or ('%s was killed by %s'):format(GetPlayerName(player), deathCause) 203 | lib.notify({title = 'Death Notification', description = description, type = 'inform'}) 204 | end) 205 | 206 | --#endregion Events 207 | 208 | --#region Callbacks 209 | 210 | lib.callback.register('bMenu:client:canSendMessage', function() 211 | return enablePMs 212 | end) 213 | 214 | --#endregion Callbacks 215 | 216 | --#region Commands 217 | 218 | RegisterCommand('bMenu_toggleDriftMode', function() 219 | driftMode = not driftMode 220 | 221 | if not cache.vehicle then return end 222 | 223 | SetVehicleReduceGrip(cache.vehicle, driftMode) 224 | end, true) 225 | 226 | RegisterKeyMapping('bMenu_toggleDriftMode', 'Toggle drift mode for bMenu', 'KEYBOARD', 'LSHIFT') 227 | 228 | --#endregion Commands 229 | 230 | --#region Threads 231 | 232 | lib.onCache('vehicle', function(value, oldValue) 233 | if oldValue then SetVehicleReduceGrip(oldValue, false) end 234 | if value then SetVehicleReduceGrip(value, false) end 235 | end) 236 | 237 | CreateThread(function() 238 | while true do 239 | Wait(0) 240 | 241 | if cache.vehicle then 242 | if speedKmh then 243 | DrawTextOnScreen(('%s KM/h'):format(lib.math.round(GetEntitySpeed(cache.vehicle) * 3.6)), 0.995, 0.955, 0.7, 2, 4) 244 | end 245 | 246 | if speedMph then 247 | DrawTextOnScreen(('%s MPH'):format(lib.math.round(GetEntitySpeed(cache.vehicle) * 2.23694)), 0.995, speedKmh and 0.925 or 0.955, 0.7, 2, 4) 248 | if speedKmh then 249 | HideHudComponentThisFrame(9) 250 | end 251 | end 252 | end 253 | 254 | if displayLocation then 255 | local currentPos = GetEntityCoords(cache.ped, true) 256 | local _, nodePos = GetNthClosestVehicleNode(currentPos.x, currentPos.y, currentPos.z, 0, 0, 0, 0) 257 | nodePos = nodePos or currentPos 258 | local heading = GetEntityHeading(cache.ped) 259 | local safeZoneSize = GetSafeZoneSize() 260 | safeZoneSizeX = (1 / safeZoneSize / 3) - 0.358 261 | local _, crossing = GetStreetNameAtCoord(currentPos.x, currentPos.y, currentPos.z) or 1, 1 262 | local crossingName = GetStreetNameFromHashKey(crossing) 263 | local suffix = (crossingName and crossingName ~= '' and crossingName ~= 'NULL') and ('~t~ / %s'):format(crossingName) or '' 264 | local prefix = #(currentPos - nodePos) > 1400 and '~m~Near ~s~' or '~s~' 265 | local headingCharacter = (heading > 320 or heading < 45) and 'N' or (heading >= 45 and heading <= 135) and 'W' or (heading > 135 and heading < 225) and 'S' or 'E' 266 | 267 | SetScriptGfxAlign(0, 84) 268 | SetScriptGfxAlignParams(0, 0, 0, 0) 269 | 270 | SetTextWrap(0, 1) 271 | DrawTextOnScreen(prefix .. crossingName .. suffix, 0.234 + safeZoneSizeX, safeZoneSize - GetRenderedCharacterHeight(0.48, 6) - GetRenderedCharacterHeight(0.48, 6), 0.48, 1, 6, false) 272 | 273 | SetTextWrap(0, 1) 274 | DrawTextOnScreen(GetLabelText(GetNameOfZone(currentPos.x, currentPos.y, currentPos.z)), 0.234 + safeZoneSizeX, safeZoneSize - GetRenderedCharacterHeight(0.45, 6) - GetRenderedCharacterHeight(0.95, 6), 0.45, 1, 6, false) 275 | 276 | SetTextWrap(0, 1) 277 | DrawTextOnScreen('~t~|', 0.188 + safeZoneSizeX, safeZoneSize - GetRenderedCharacterHeight(1.2, 6) - GetRenderedCharacterHeight(0.4, 6), 1.2, 1, 6, false) 278 | 279 | SetTextWrap(0, 1) 280 | DrawTextOnScreen(headingCharacter, 0.208 + safeZoneSizeX, safeZoneSize - GetRenderedCharacterHeight(1.2, 6) - GetRenderedCharacterHeight(0.4, 6), 1.2, 0, 6, false) 281 | 282 | SetTextWrap(0, 1) 283 | DrawTextOnScreen('~t~|', 0.228 + safeZoneSizeX, safeZoneSize - GetRenderedCharacterHeight(1.2, 6) - GetRenderedCharacterHeight(0.4, 6), 1.2, 2, 6, false) 284 | 285 | ResetScriptGfxAlign() 286 | end 287 | 288 | if showTime then 289 | local hour = GetClockHours() 290 | local minute = GetClockMinutes() 291 | local formattedTime = string.format('%s:%s', hour < 10 and '0'..hour or hour, minute < 10 and '0'..minute or minute) 292 | SetScriptGfxAlign(0, 84) 293 | SetScriptGfxAlignParams(0, 0, 0, 0) 294 | DrawTextOnScreen('~c~'..formattedTime, 0.208 + safeZoneSizeX, GetSafeZoneSize() - GetRenderedCharacterHeight(0.4, 1), 0.4, 0, 6, false) 295 | ResetScriptGfxAlign() 296 | end 297 | 298 | if playerNames then 299 | local players = GetActivePlayers() 300 | local coords = GetEntityCoords(cache.ped) 301 | for i = 1, #players do 302 | local player = players[i] 303 | local ped = GetPlayerPed(player) 304 | local pedCoords = GetEntityCoords(ped) 305 | if #(pedCoords - coords) < playerNamesDistance then 306 | if not gamerTags[player] then 307 | gamerTags[player] = CreateFakeMpGamerTag(ped, GetPlayerName(player) .. ' [' .. GetPlayerServerId(player) .. ']', false, false, '', 0) 308 | end 309 | SetMpGamerTagVisibility(gamerTags[player], 2, true) 310 | local wantedLevel = GetPlayerWantedLevel(player) 311 | if wantedLevel > 0 then 312 | SetMpGamerTagVisibility(gamerTags[player], 7, true) 313 | SetMpGamerTagWantedLevel(gamerTags[player], wantedLevel) 314 | else 315 | SetMpGamerTagVisibility(gamerTags[player], 7, false) 316 | end 317 | elseif gamerTags[player] then 318 | RemoveMpGamerTag(gamerTags[player]) 319 | gamerTags[player] = nil 320 | end 321 | end 322 | else 323 | if table.type(gamerTags) ~= 'empty' then 324 | for _, v in pairs(gamerTags) do 325 | RemoveMpGamerTag(v) 326 | end 327 | table.wipe(gamerTags) 328 | end 329 | end 330 | end 331 | end) 332 | 333 | --#endregion Threads 334 | -------------------------------------------------------------------------------- /client/menus/misc/teleport.lua: -------------------------------------------------------------------------------- 1 | --#region Variables 2 | 3 | local locations = {} 4 | 5 | --#endregion Variables 6 | 7 | --#region Functions 8 | 9 | local function refreshLocations() 10 | local newLocations = lib.callback.await('bMenu:server:getConfig', false, 'locations') 11 | 12 | if newLocations and type(newLocations) == 'table' then 13 | for i = 1, #newLocations do 14 | locations[i] = newLocations[i] 15 | end 16 | end 17 | end 18 | 19 | local function createLocationsMenu() 20 | local menuOptions = {} 21 | for i = 1, #locations do 22 | menuOptions[i] = {label = locations[i].name, args = locations[i], close = false} 23 | end 24 | 25 | lib.registerMenu({ 26 | id = 'bMenu_misc_options_teleport_options_locations', 27 | title = 'Teleport Locations', 28 | position = MenuPosition, 29 | onClose = function(keyPressed) 30 | CloseMenu(false, keyPressed, 'bMenu_misc_options_teleport_options') 31 | end, 32 | onSelected = function(selected) 33 | MenuIndexes['bMenu_misc_options_teleport_options_locations'] = selected 34 | end, 35 | options = menuOptions 36 | }, function(_, _, args) 37 | if IsInVehicle(true) then 38 | SetEntityCoords(cache.vehicle, args.coords.x, args.coords.y, args.coords.z, true, false, false, false) 39 | SetEntityHeading(currentVeh, args.heading) 40 | else 41 | SetEntityCoords(cache.ped, args.coords.x, args.coords.y, args.coords.z, true, false, false, false) 42 | SetEntityHeading(cache.ped, args.heading) 43 | end 44 | 45 | lib.notify({ 46 | description = ('Successfully teleport to %s'):format(args.name), 47 | type = 'success' 48 | }) 49 | end) 50 | end 51 | 52 | function SetupTeleportOptions() 53 | local perms = lib.callback.await('bMenu:server:hasConvarPermission', false, {'Misc', 'TeleportOptions'}, {'To_Waypoint', 'To_Coords', 'Locations'}) 54 | local menuOptions = { 55 | {label = 'No access', description = 'You don\'t have access to any options, press enter to return', args = {'bMenu_misc_options'}} 56 | } 57 | local index = 1 58 | 59 | if perms.To_Waypoint then 60 | menuOptions[index] = {label = 'Teleport To Waypoint', args = {'teleport_waypoint'}, close = false} 61 | index += 1 62 | end 63 | 64 | if perms.To_Coords then 65 | menuOptions[index] = {label = 'Teleport To Coords', args = {'teleport_coords'}} 66 | index += 1 67 | end 68 | 69 | if perms.Locations then 70 | if table.type(locations) ~= 'empty' then 71 | menuOptions[index] = {label = 'Teleport To Location', description = 'Teleport to pre-configured locations', args = {'bMenu_misc_options_teleport_options_locations'}} 72 | index += 1 73 | end 74 | 75 | menuOptions[index] = {label = 'Save Teleport Location', description = 'Adds your current location to the teleport locations menu', args = {'save_location'}} 76 | index += 1 77 | end 78 | 79 | lib.registerMenu({ 80 | id = 'bMenu_misc_options_teleport_options', 81 | title = 'Teleport Options', 82 | position = MenuPosition, 83 | onClose = function(keyPressed) 84 | CloseMenu(false, keyPressed, 'bMenu_misc_options') 85 | end, 86 | onSelected = function(selected) 87 | MenuIndexes['bMenu_misc_options_teleport_options'] = selected 88 | end, 89 | options = menuOptions 90 | }, function(_, _, args) 91 | if args[1] == 'bMenu_misc_options' then 92 | lib.showMenu(args[1], MenuIndexes[args[1]]) 93 | return 94 | end 95 | 96 | if args[1] == 'teleport_waypoint' then 97 | if not IsWaypointActive() then 98 | lib.notify({ 99 | description = 'No waypoint set', 100 | type = 'error' 101 | }) 102 | return 103 | end 104 | 105 | TeleportToWaypoint() 106 | elseif args[1] == 'teleport_coords' then 107 | local dialog = lib.inputDialog('Teleport To Coords', {'Coords'}) 108 | 109 | if not dialog or not dialog[1] or dialog[1] == '' then 110 | Wait(200) 111 | lib.showMenu('bMenu_misc_options_teleport_options', MenuIndexes['bMenu_misc_options_teleport_options']) 112 | return 113 | end 114 | 115 | local actualValues = {} 116 | for word in dialog[1]:gmatch('[^,]*') do 117 | local val = tonumber(word) 118 | if val then 119 | actualValues[#actualValues + 1] = val 120 | end 121 | end 122 | 123 | if #actualValues < 3 then 124 | lib.notify({ 125 | description = 'Wrong format used, the format is `x, y, z`', 126 | type = 'error' 127 | }) 128 | Wait(200) 129 | lib.showMenu('bMenu_misc_options_teleport_options', MenuIndexes['bMenu_misc_options_teleport_options']) 130 | return 131 | end 132 | 133 | for i = 4, #actualValues do 134 | actualValues[i] = nil 135 | end 136 | 137 | if IsInVehicle(true) then 138 | SetEntityCoords(cache.vehicle, actualValues[1], actualValues[2], actualValues[3], true, false, false, false) 139 | else 140 | SetEntityCoords(cache.ped, actualValues[1], actualValues[2], actualValues[3], true, false, false, false) 141 | end 142 | 143 | Wait(200) 144 | lib.showMenu('bMenu_misc_options_teleport_options', MenuIndexes['bMenu_misc_options_teleport_options']) 145 | elseif args[1] == 'bMenu_misc_options_teleport_options_locations' then 146 | createLocationsMenu() 147 | lib.showMenu(args[1], MenuIndexes[args[1]]) 148 | elseif args[1] == 'save_location' then 149 | local dialog = lib.inputDialog('Save Location', {'Location Name'}) 150 | 151 | if not dialog or not dialog[1] or dialog[1] == '' then 152 | Wait(200) 153 | lib.showMenu('bMenu_misc_options_teleport_options', MenuIndexes['bMenu_misc_options_teleport_options']) 154 | return 155 | end 156 | 157 | local result, notification = lib.callback.await('bMenu:server:saveTeleportLocation', false, dialog[1]) 158 | 159 | lib.notify({ 160 | description = notification, 161 | type = result and 'success' or 'error' 162 | }) 163 | 164 | refreshLocations() 165 | SetupTeleportOptions() 166 | 167 | Wait(200) 168 | lib.showMenu('bMenu_misc_options_teleport_options', MenuIndexes['bMenu_misc_options_teleport_options']) 169 | end 170 | end) 171 | end 172 | 173 | --#endregion Functions 174 | 175 | --#region Commands 176 | 177 | RegisterCommand('bMenu_tpToWaypoint', function() 178 | if not IsScreenFadedIn() or IsPlayerSwitchInProgress() or not IsUsingKeyboard(2) then return end 179 | 180 | if not IsWaypointActive() then 181 | return lib.notify({ 182 | description = 'There is no waypoint active', 183 | type = 'error' 184 | }) 185 | end 186 | 187 | TeleportToWaypoint() 188 | lib.notify({ 189 | description = 'Teleported to waypoint', 190 | type = 'success' 191 | }) 192 | end, true) 193 | 194 | RegisterKeyMapping('bMenu_tpToWaypoint', 'Teleport to waypoint', 'KEYBOARD', 'F7') 195 | 196 | --#endregion Commands 197 | 198 | --#region Threads 199 | 200 | CreateThread(function() 201 | refreshLocations() 202 | end) 203 | 204 | --#endregion Threads -------------------------------------------------------------------------------- /client/menus/online_players.lua: -------------------------------------------------------------------------------- 1 | --#region Variables 2 | 3 | local currentlySpectating = -1 4 | local previousPos = vec3(0, 0, 0) 5 | local playerBlips = {} 6 | local itemsOnYourself = { 7 | 'kill', 8 | } 9 | 10 | --#endregion Variables 11 | 12 | --#region Menu Registration 13 | 14 | lib.registerMenu({ 15 | id = 'bMenu_online_players', 16 | title = 'Online Players', 17 | position = MenuPosition, 18 | onClose = function(keyPressed) 19 | CloseMenu(false, keyPressed, 'bMenu_main') 20 | end, 21 | onSelected = function(selected) 22 | MenuIndexes['bMenu_online_players'] = selected 23 | end, 24 | options = { 25 | {label = 'No Players Online', icon = 'face-sad-tear', args = {'return'}, close = false} 26 | } 27 | }, function(_, _, args) 28 | if args[1] == 'return' then 29 | lib.hideMenu(true) 30 | return 31 | end 32 | lib.showMenu(args[1], MenuIndexes[args[1]]) 33 | end) 34 | 35 | --#endregion Menu Registration 36 | 37 | --#region Functions 38 | 39 | function CreatePlayerMenu() 40 | local id = 'bMenu_online_players' 41 | lib.setMenuOptions(id, {{label = 'No Players Online', icon = 'face-sad-tear', args = {'none'}}}) 42 | local onlinePlayers = lib.callback.await('bMenu:server:getOnlinePlayers', false) 43 | local messageArg = 'message' 44 | local teleportArg = 'teleport' 45 | local teleportVehicleArg = 'teleport_vehicle' 46 | local summonArg = 'summon' 47 | local spectateArg = 'spectate' 48 | local waypointArg = 'waypoint' 49 | local blipArg = 'blip' 50 | local killArg = 'kill' 51 | local perms = lib.callback.await('bMenu:server:hasConvarPermission', false, 'OnlinePlayers', {'Message', 'Teleport_To', 'Teleport_In_Vehicle', 'Summon', 'Spectate', 'Waypoint', 'Blip', 'Kill'}) 52 | local menuOptions = { 53 | {label = 'You don\'t have access to any options', icon = 'face-sad-tear', args = {'return'}, close = false} 54 | } 55 | 56 | local index = 1 57 | if perms.Message then 58 | menuOptions[index] = {label = 'Send Message', icon = 'comment-dots', description = 'Send a message to this player, note that staff can see these', args = {messageArg}, close = true} 59 | index += 1 60 | end 61 | 62 | if perms.Teleport_To then 63 | menuOptions[index] = {label = 'Teleport To Player', icon = 'hat-wizard', description = 'Teleport to the player', args = {teleportArg}, close = false} 64 | index += 1 65 | end 66 | 67 | if perms.Teleport_In_Vehicle then 68 | menuOptions[index] = {label = 'Teleport Into Vehicle', icon = 'car-side', description = 'Teleport into the vehicle of the player', args = {teleportVehicleArg}, close = false} 69 | index += 1 70 | end 71 | 72 | if perms.Summon then 73 | menuOptions[index] = {label = 'Summon Player', icon = 'hat-wizard', description = 'Summon the player to your location', args = {summonArg}, close = false} 74 | index += 1 75 | end 76 | 77 | if perms.Spectate then 78 | menuOptions[index] = {label = 'Spectate Player', icon = 'glasses', description = 'Spectate the player', args = {spectateArg}, close = false} 79 | index += 1 80 | end 81 | 82 | if perms.Waypoint then 83 | menuOptions[index] = {label = 'Set Waypoint', icon = 'location-dot', description = 'Set your waypoint on the player', args = {waypointArg}, close = false} 84 | index += 1 85 | end 86 | 87 | if perms.Blip then 88 | menuOptions[index] = {label = 'Toggle Blip', icon = 'magnifying-glass-location', description = 'Toggle a blip on the map following the player', args = {blipArg}, close = false} 89 | index += 1 90 | end 91 | 92 | if perms.Kill then 93 | menuOptions[index] = {label = 'Kill Player', icon = 'bullseye', description = 'Kill the player, just because you can', args = {killArg}, close = false} 94 | index += 1 95 | end 96 | 97 | for i = 1, #onlinePlayers do 98 | local data = onlinePlayers[i] 99 | local formattedId = ('%s_%s'):format(id, i) 100 | lib.registerMenu({ 101 | id = formattedId, 102 | title = ('%s (%s/%s)'):format(data.name, i, #onlinePlayers), 103 | position = MenuPosition, 104 | onClose = function(keyPressed) 105 | CloseMenu(false, keyPressed, id) 106 | end, 107 | onSelected = function(selected) 108 | MenuIndexes[formattedId] = selected 109 | end, 110 | options = menuOptions 111 | }, function(_, _, args) 112 | if args[1] == 'return' then 113 | lib.hideMenu(true) 114 | return 115 | end 116 | 117 | local canActOnSelf = ArrayIncludes(args[1], itemsOnYourself) 118 | if data.source == ServerId and not canActOnSelf then 119 | lib.notify({ 120 | description = 'You can\'t do this on yourself', 121 | type = 'error' 122 | }) 123 | if args[1] == messageArg then 124 | lib.showMenu(formattedId, MenuIndexes[formattedId]) 125 | end 126 | 127 | return 128 | end 129 | 130 | local message = args[1] == messageArg and lib.hideMenu(true) and lib.inputDialog(('Send a message to %s'):format(data.name), {'Message'}) 131 | 132 | if args[1] == messageArg and (not message or not message[1] or message[1] == '') then 133 | Wait(500) 134 | lib.showMenu(formattedId, MenuIndexes[formattedId]) 135 | return 136 | end 137 | 138 | ---@diagnostic disable-next-line: need-check-nil 139 | local success, reason, extraArg1 = lib.callback.await('bMenu:server:playerListAction', false, args[1], data.source, canActOnSelf, message and message[1] or nil) 140 | 141 | if args[1] == teleportVehicleArg and success then 142 | local veh = NetToVeh(extraArg1) 143 | if not AreAnyVehicleSeatsFree(veh) then 144 | success = false 145 | reason = 'There are no seats available' 146 | end 147 | 148 | local found = false 149 | 150 | for i2 = -1, GetVehicleModelNumberOfSeats(GetEntityModel(veh)) do 151 | if IsVehicleSeatFree(veh, i2) then 152 | found = true 153 | TaskWarpPedIntoVehicle(cache.ped, veh, i2) 154 | break 155 | end 156 | end 157 | 158 | if not found then 159 | success = false 160 | reason = 'There are no seats available' 161 | end 162 | elseif args[1] == waypointArg and success then 163 | SetNewWaypoint(extraArg1.x, extraArg1.y) 164 | elseif args[1] == blipArg and success then 165 | if playerBlips[data.source] then 166 | if DoesBlipExist(playerBlips[data.source]) then 167 | RemoveBlip(playerBlips[data.source]) 168 | end 169 | 170 | playerBlips[data.source] = nil 171 | else 172 | local ent = NetToEnt(extraArg1) 173 | playerBlips[data.source] = AddBlipForEntity(ent) 174 | SetBlipNameToPlayerName(playerBlips[data.source], NetworkGetPlayerIndexFromPed(ent)) 175 | SetBlipCategory(playerBlips[data.source], 7) 176 | SetBlipColour(playerBlips[data.source], 0) 177 | ShowHeadingIndicatorOnBlip(playerBlips[data.source], true) 178 | ShowHeightOnBlip(playerBlips[data.source], true) 179 | end 180 | elseif args[1] == killArg and success then 181 | SetEntityHealth(NetToEnt(extraArg1), 0) 182 | elseif args[1] == spectateArg and success then 183 | local player = GetPlayerFromServerId(data.source) 184 | local playerPed = GetPlayerPed(player) 185 | 186 | if NetworkIsInSpectatorMode() then 187 | if currentlySpectating ~= player and playerPed ~= 0 then 188 | DoScreenFadeOut(500) 189 | while IsScreenFadingOut() do 190 | Wait(0) 191 | end 192 | 193 | RequestCollisionAtCoord(extraArg1.x, extraArg1.y, extraArg1.z + 3) 194 | SetEntityVisible(cache.ped, false, false) 195 | NetworkSetEntityInvisibleToNetwork(cache.ped, true) 196 | SetEntityCollision(cache.ped, false, true) 197 | FreezeEntityPosition(cache.ped, true) 198 | SetEntityCoords(cache.ped, extraArg1.x, extraArg1.y, extraArg1.z + 3, true, false, false, false) 199 | 200 | NetworkSetInSpectatorMode(false, 0) 201 | NetworkSetInSpectatorMode(true, playerPed) 202 | 203 | DoScreenFadeIn(500) 204 | currentlySpectating = player 205 | else 206 | DoScreenFadeOut(500) 207 | while IsScreenFadingOut() do 208 | Wait(0) 209 | end 210 | 211 | RequestCollisionAtCoord(previousPos.x, previousPos.y, previousPos.z) 212 | 213 | NetworkSetInSpectatorMode(false, 0) 214 | 215 | SetEntityCoords(cache.ped, previousPos.x, previousPos.y, previousPos.z, true, false, false, false) 216 | previousPos = vec3(0, 0, 0) 217 | SetEntityVisible(cache.ped, true, false) 218 | NetworkSetEntityInvisibleToNetwork(cache.ped, false) 219 | SetEntityCollision(cache.ped, true, true) 220 | FreezeEntityPosition(cache.ped, false) 221 | 222 | DoScreenFadeIn(500) 223 | reason = 'Successfully stopped spectating' 224 | currentlySpectating = -1 225 | end 226 | else 227 | if playerPed ~= 0 then 228 | DoScreenFadeOut(500) 229 | while IsScreenFadingOut() do 230 | Wait(0) 231 | end 232 | 233 | RequestCollisionAtCoord(extraArg1.x, extraArg1.y, extraArg1.z + 3) 234 | 235 | SetEntityVisible(cache.ped, false, false) 236 | NetworkSetEntityInvisibleToNetwork(cache.ped, true) 237 | SetEntityCollision(cache.ped, false, true) 238 | FreezeEntityPosition(cache.ped, true) 239 | previousPos = GetEntityCoords(cache.ped) 240 | SetEntityCoords(cache.ped, extraArg1.x, extraArg1.y, extraArg1.z + 3, true, false, false, false) 241 | 242 | NetworkSetInSpectatorMode(false, 0) 243 | NetworkSetInSpectatorMode(true, playerPed) 244 | 245 | DoScreenFadeIn(500) 246 | currentlySpectating = player 247 | end 248 | end 249 | end 250 | 251 | lib.notify({ 252 | description = reason, 253 | type = success and 'success' or 'error' 254 | }) 255 | 256 | if args[1] ~= messageArg then return end 257 | 258 | Wait(500) 259 | lib.showMenu(formattedId, MenuIndexes[formattedId]) 260 | end) 261 | 262 | lib.setMenuOptions(id, {label = ('[%s] %s'):format(data.source, data.name), args = {formattedId}}, i) 263 | end 264 | end 265 | 266 | --#endregion Functions -------------------------------------------------------------------------------- /client/menus/player/main.lua: -------------------------------------------------------------------------------- 1 | --#region Variables 2 | 3 | local noClipEnabled = false 4 | local followCamMode = true 5 | local currentScalefrom = -1 6 | local movingSpeed = 0 7 | local movingSpeeds = { 8 | [0] = 'Very Slow', 9 | [1] = 'Slow', 10 | [2] = 'Normal', 11 | [3] = 'Fast', 12 | [4] = 'Very Fast', 13 | [5] = 'Extremely Fast', 14 | [6] = 'Staggeringly Fast', 15 | [7] = 'Max Speed' 16 | } 17 | 18 | --#endregion Variables 19 | 20 | --#region Functions 21 | 22 | local function toggleNoClip() 23 | if not lib.callback.await('bMenu:server:hasCommandPermission', false, 'toggle_noclip') then return end 24 | 25 | local noclipEntity = cache.vehicle or cache.ped 26 | noClipEnabled = not noClipEnabled 27 | if noClipEnabled then 28 | currentScalefrom = RequestScaleformMovie('INSTRUCTIONAL_BUTTONS') 29 | while not HasScaleformMovieLoaded(currentScalefrom) do 30 | Wait(0) 31 | end 32 | 33 | BeginScaleformMovieMethod(currentScalefrom, 'CLEAR_ALL') 34 | EndScaleformMovieMethod() 35 | 36 | BeginScaleformMovieMethod(currentScalefrom, 'SET_DATA_SLOT') 37 | ScaleformMovieMethodAddParamInt(0) 38 | ScaleformMovieMethodAddParamTextureNameString('~INPUT_STRING~') 39 | ScaleformMovieMethodAddParamTextureNameString(('Change Speed: %s'):format(movingSpeeds[movingSpeed])) 40 | EndScaleformMovieMethod() 41 | 42 | BeginScaleformMovieMethod(currentScalefrom, 'SET_DATA_SLOT') 43 | ScaleformMovieMethodAddParamInt(1) 44 | ScaleformMovieMethodAddParamTextureNameString('~INPUT_MOVE_LR~') 45 | ScaleformMovieMethodAddParamTextureNameString('Turn Left/Right') 46 | EndScaleformMovieMethod() 47 | 48 | BeginScaleformMovieMethod(currentScalefrom, 'SET_DATA_SLOT') 49 | ScaleformMovieMethodAddParamInt(2) 50 | ScaleformMovieMethodAddParamTextureNameString('~INPUT_MOVE_UD~') 51 | ScaleformMovieMethodAddParamTextureNameString('Move') 52 | EndScaleformMovieMethod() 53 | 54 | BeginScaleformMovieMethod(currentScalefrom, 'SET_DATA_SLOT') 55 | ScaleformMovieMethodAddParamInt(3) 56 | ScaleformMovieMethodAddParamTextureNameString('~INPUT_MULTIPLAYER_INFO~') 57 | ScaleformMovieMethodAddParamTextureNameString('Down') 58 | EndScaleformMovieMethod() 59 | 60 | BeginScaleformMovieMethod(currentScalefrom, 'SET_DATA_SLOT') 61 | ScaleformMovieMethodAddParamInt(4) 62 | ScaleformMovieMethodAddParamTextureNameString('~INPUT_COVER~') 63 | ScaleformMovieMethodAddParamTextureNameString('Up') 64 | EndScaleformMovieMethod() 65 | 66 | BeginScaleformMovieMethod(currentScalefrom, 'SET_DATA_SLOT') 67 | ScaleformMovieMethodAddParamInt(5) 68 | ScaleformMovieMethodAddParamTextureNameString('~INPUT_VEH_HEADLIGHT~') 69 | ScaleformMovieMethodAddParamTextureNameString('Cam Mode') 70 | EndScaleformMovieMethod() 71 | 72 | BeginScaleformMovieMethod(currentScalefrom, 'DRAW_INSTRUCTIONAL_BUTTONS') 73 | ScaleformMovieMethodAddParamInt(0) 74 | EndScaleformMovieMethod() 75 | 76 | DrawScaleformMovieFullscreen(currentScalefrom, 255, 255, 255, 0, 0) 77 | 78 | FreezeEntityPosition(noclipEntity, true) 79 | SetEntityInvincible(noclipEntity, true) 80 | 81 | SetEntityVisible(noclipEntity, false, false) 82 | SetEntityCollision(noclipEntity, false, false) 83 | SetLocalPlayerVisibleLocally(true) 84 | SetEntityAlpha(noclipEntity, 51, false) 85 | 86 | SetEveryoneIgnorePlayer(cache.playerId, true) 87 | SetPoliceIgnorePlayer(cache.playerId, true) 88 | else 89 | SetScaleformMovieAsNoLongerNeeded(currentScalefrom) 90 | currentScalefrom = -1 91 | 92 | FreezeEntityPosition(noclipEntity, false) 93 | SetEntityInvincible(noclipEntity, false) 94 | 95 | SetEntityVisible(noclipEntity, true, false) 96 | SetEntityCollision(noclipEntity, true, true) 97 | SetLocalPlayerVisibleLocally(true) 98 | ResetEntityAlpha(noclipEntity) 99 | 100 | SetEveryoneIgnorePlayer(cache.playerId, false) 101 | SetPoliceIgnorePlayer(cache.playerId, false) 102 | end 103 | end 104 | 105 | function CreatePlayerOptionsMenu() 106 | local perms = lib.callback.await('bMenu:server:hasConvarPermission', false, 'PlayerRelated', {'PlayerOptions', 'WeaponOptions', 'Toggle_NoClip'}) 107 | local menuOptions = { 108 | {label = 'No access', description = 'You don\'t have access to any options, press enter to return', args = {'bMenu_main'}} 109 | } 110 | local index = 1 111 | 112 | if perms.PlayerOptions then 113 | menuOptions[index] = {label = 'Player Options', description = 'Common player options can be accessed here', args = {'bMenu_player_options'}} 114 | index += 1 115 | end 116 | 117 | if perms.WeaponOptions then 118 | menuOptions[index] = {label = 'Weapon Options', description = 'Add/remove weapons, modify weapons and set ammo options', args = {'bMenu_player_weapon_options'}} 119 | index += 1 120 | end 121 | 122 | if perms.Toggle_NoClip then 123 | menuOptions[index] = {label = 'Toggle NoClip', description = 'Toggle NoClip on or off', args = {'toggle_noclip'}, close = false} 124 | index += 1 125 | end 126 | 127 | lib.registerMenu({ 128 | id = 'bMenu_player_related_options', 129 | title = 'Player Related Options', 130 | position = MenuPosition, 131 | onClose = function(keyPressed) 132 | CloseMenu(false, keyPressed, 'bMenu_main') 133 | end, 134 | onSelected = function(selected) 135 | MenuIndexes['bMenu_player_related_options'] = selected 136 | end, 137 | options = menuOptions 138 | }, function(_, _, args) 139 | if args[1] == 'toggle_noclip' then 140 | toggleNoClip() 141 | else 142 | if args[1] == 'bMenu_player_options' then 143 | SetupPlayerOptions() 144 | elseif args[1] == 'bMenu_player_weapon_options' then 145 | SetupWeaponsMenu() 146 | end 147 | 148 | lib.showMenu(args[1], MenuIndexes[args[1]]) 149 | end 150 | end) 151 | end 152 | 153 | --#endregion Functions 154 | 155 | --#region Commands 156 | 157 | RegisterCommand('bMenu_toggleNoClip', toggleNoClip, true) 158 | 159 | RegisterKeyMapping('bMenu_toggleNoClip', 'Toggle NoClip', 'KEYBOARD', 'F2') 160 | 161 | --#endregion Commands 162 | 163 | --#region Threads 164 | 165 | CreateThread(function() 166 | while true do 167 | if noClipEnabled then 168 | if not IsHudHidden() and currentScalefrom ~= -1 then 169 | DrawScaleformMovieFullscreen(currentScalefrom, 255, 255, 255, 255, 0) 170 | end 171 | 172 | local noclipEntity = cache.vehicle or cache.ped 173 | 174 | DisableControlAction(0, 20, true) 175 | DisableControlAction(0, 30, true) 176 | DisableControlAction(0, 31, true) 177 | DisableControlAction(0, 32, true) 178 | DisableControlAction(0, 33, true) 179 | DisableControlAction(0, 34, true) 180 | DisableControlAction(0, 35, true) 181 | DisableControlAction(0, 44, true) 182 | DisableControlAction(0, 74, true) 183 | if cache.vehicle then 184 | DisableControlAction(0, 75, true) 185 | DisableControlAction(0, 85, true) 186 | end 187 | DisableControlAction(0, 266, true) 188 | DisableControlAction(0, 267, true) 189 | DisableControlAction(0, 268, true) 190 | DisableControlAction(0, 269, true) 191 | 192 | local yOff = 0.0 193 | local zOff = 0.0 194 | local currentHeading = GetEntityHeading(noclipEntity) 195 | 196 | if IsUsingKeyboard(2) and UpdateOnscreenKeyboard() ~= 0 and not IsPauseMenuActive() then 197 | if IsControlJustPressed(0, 21) then 198 | movingSpeed += 1 199 | if movingSpeed == 8 then 200 | movingSpeed = 0 201 | end 202 | end 203 | 204 | if IsDisabledControlPressed(0, 32) then 205 | yOff = 0.5 206 | end 207 | 208 | if IsDisabledControlPressed(0, 33) then 209 | yOff = -0.5 210 | end 211 | 212 | if not followCamMode and IsDisabledControlPressed(0, 34) then 213 | currentHeading += 3 214 | end 215 | 216 | if not followCamMode and IsDisabledControlPressed(0, 35) then 217 | currentHeading -= 3 218 | end 219 | 220 | if IsDisabledControlPressed(0, 44) then 221 | zOff = 0.21 222 | end 223 | 224 | if IsDisabledControlPressed(0, 20) then 225 | zOff = -0.21 226 | end 227 | 228 | if IsDisabledControlJustPressed(0, 74) then 229 | followCamMode = not followCamMode 230 | end 231 | end 232 | 233 | local moveSpeed = movingSpeed 234 | 235 | if movingSpeed > 4 then 236 | moveSpeed *= 1.8 237 | end 238 | 239 | moveSpeed = moveSpeed / (1 / GetFrameTime()) * 60 240 | local newPos = GetOffsetFromEntityInWorldCoords(noclipEntity, 0.0, yOff * (moveSpeed + 0.3), zOff * (moveSpeed + 0.3)) 241 | 242 | SetEntityVelocity(noclipEntity, 0.0, 0.0, 0.0) 243 | SetEntityRotation(noclipEntity, followCamMode and GetGameplayCamRelativePitch() or 0.0, 0.0, 0.0, 0, false) 244 | SetEntityHeading(noclipEntity, followCamMode and GetGameplayCamRelativeHeading() or currentHeading) 245 | SetEntityCoordsNoOffset(noclipEntity, newPos.x, newPos.y, newPos.z, true, false, false) 246 | 247 | SetLocalPlayerVisibleLocally(true) 248 | end 249 | 250 | Wait(0) 251 | end 252 | end) 253 | 254 | --#endregion Threads -------------------------------------------------------------------------------- /client/menus/player/options.lua: -------------------------------------------------------------------------------- 1 | --#region Variables 2 | 3 | IgnorePlayer = false 4 | StayInVehicle = false 5 | 6 | local godmode = false 7 | local invisible = false 8 | local unlimitedStamina = false 9 | local fastRun = false 10 | local fastSwim = false 11 | local superJump = false 12 | local noRagdoll = false 13 | local neverWanted = true 14 | local ghostMode = false 15 | local customDrivingStyleList = {} 16 | local customDrivingStyle = '' 17 | local freezePlayer = false 18 | local drivingStyle = 1 19 | local actualDrivingStyle = 443 20 | 21 | --#endregion Variables 22 | 23 | --#region Functions 24 | 25 | local function setupDrivingStyleOptions() 26 | lib.registerMenu({ 27 | id = 'bMenu_player_autopilot_options_custom_driving_style', 28 | title = 'Custom Driving Style', 29 | position = MenuPosition, 30 | onClose = function(keyPressed) 31 | CloseMenu(false, keyPressed, 'bMenu_player_autopilot_options') 32 | end, 33 | onSelected = function(selected) 34 | MenuIndexes['bMenu_player_autopilot_options_custom_driving_style'] = selected 35 | end, 36 | onSideScroll = function(_, scrollIndex, args) 37 | customDrivingStyleList[args[1]] = scrollIndex == 1 38 | if drivingStyle == 5 then 39 | customDrivingStyle = '' 40 | for i = 0, 30 do 41 | customDrivingStyle = ('%s%s'):format(customDrivingStyle, customDrivingStyleList[i] and 1 or 0) 42 | end 43 | actualDrivingStyle = tonumber(customDrivingStyle) --[[@as integer]] 44 | SetDriveTaskDrivingStyle(cache.ped, actualDrivingStyle) 45 | end 46 | end, 47 | options = { 48 | {label = 'Stop Before Vehicles', args = {0}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 49 | {label = 'Stop Before Peds', args = {1}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 50 | {label = 'Avoid Vehicles', args = {2}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 51 | {label = 'Avoid Empty Vehicles', args = {3}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 52 | {label = 'Avoid Peds', args = {4}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 53 | {label = 'Avoid Objects', args = {5}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 54 | {label = 'Unknown Flag', args = {6}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 55 | {label = 'Stop At Traffic Lights', args = {7}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 56 | {label = 'Use Blinkers', args = {8}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 57 | {label = 'Allow Going Wrong Way', args = {9}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 58 | {label = 'Go In Reverse Gear', args = {10}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 59 | {label = 'Unknown Flag', args = {11}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 60 | {label = 'Unknown Flag', args = {12}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 61 | {label = 'Unknown Flag', args = {13}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 62 | {label = 'Unknown Flag', args = {14}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 63 | {label = 'Unknown Flag', args = {15}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 64 | {label = 'Unknown Flag', args = {16}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 65 | {label = 'Unknown Flag', args = {17}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 66 | {label = 'Use Shortest Path', args = {18}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 67 | {label = 'Unknown Flag', args = {19}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 68 | {label = 'Unknown Flag', args = {20}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 69 | {label = 'Unknown Flag', args = {21}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 70 | {label = 'Ignore Roads', args = {22}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 71 | {label = 'Unknown Flag', args = {23}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 72 | {label = 'Ignore All Pathing', args = {24}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 73 | {label = 'Unknown Flag', args = {25}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 74 | {label = 'Unknown Flag', args = {26}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 75 | {label = 'Unknown Flag', args = {27}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 76 | {label = 'Unknown Flag', args = {28}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 77 | {label = 'Avoid Highways (If Possible)', args = {29}, values = {'Yes', 'No'}, defaultIndex = 2, close = false}, 78 | {label = 'Unknown Flag', args = {30}, values = {'Yes', 'No'}, defaultIndex = 2, close = false} 79 | } 80 | }) 81 | end 82 | 83 | local function setupAutoPilotOptions() 84 | lib.registerMenu({ 85 | id = 'bMenu_player_autopilot_options', 86 | title = 'Vehicle Auto Pilot', 87 | position = MenuPosition, 88 | onClose = function(keyPressed) 89 | CloseMenu(false, keyPressed, 'bMenu_player_options') 90 | end, 91 | onSelected = function(selected) 92 | MenuIndexes['bMenu_player_autopilot_options'] = selected 93 | end, 94 | onSideScroll = function(selected, scrollIndex, args) 95 | if args[1] == 'driving_style' then 96 | drivingStyle = scrollIndex 97 | if scrollIndex == 1 then 98 | SetDriveTaskDrivingStyle(cache.ped, 443) 99 | actualDrivingStyle = 443 100 | elseif scrollIndex == 2 then 101 | SetDriveTaskDrivingStyle(cache.ped, 575) 102 | actualDrivingStyle = 575 103 | elseif scrollIndex == 3 then 104 | SetDriveTaskDrivingStyle(cache.ped, 536871355) 105 | actualDrivingStyle = 536871355 106 | elseif scrollIndex == 4 then 107 | SetDriveTaskDrivingStyle(cache.ped, 1467) 108 | actualDrivingStyle = 1467 109 | elseif scrollIndex == 5 then 110 | customDrivingStyle = '' 111 | for i = 0, 30 do 112 | customDrivingStyle = ('%s%s'):format(customDrivingStyle, customDrivingStyleList[i] and 1 or 0) 113 | end 114 | actualDrivingStyle = tonumber(customDrivingStyle) --[[@as integer]] 115 | SetDriveTaskDrivingStyle(cache.ped, actualDrivingStyle) 116 | end 117 | lib.setMenuOptions('bMenu_player_autopilot_options', {label = 'Driving Style', description = 'Set the driving style that is used for the Drive to Waypoint and Drive Around Randomly functions', args = {'driving_style'}, values = {'Normal', 'Rushed', 'Avoid highways', 'Drive in reverse', 'Custom'}, defaultIndex = scrollIndex, close = false}, selected) 118 | end 119 | end, 120 | options = { 121 | {label = 'Driving Style', description = 'Set the driving style that is used for the Drive to Waypoint and Drive Around Randomly functions', args = {'driving_style'}, values = {'Normal', 'Rushed', 'Avoid highways', 'Drive in reverse', 'Custom'}, defaultIndex = drivingStyle, close = false}, 122 | {label = 'Custom Driving Style', description = 'Select a custom driving style. Make sure to also enable it by selecting the \'Custom\' driving style in the driving styles list', args = {'bMenu_player_autopilot_options_custom_driving_style'}}, 123 | {label = 'Drive To Waypoint', description = 'Make your player ped drive your vehicle to your waypoint', args = {'to_waypoint'}, close = false}, 124 | {label = 'Drive Around Randomly', description = 'Make your player ped drive your vehicle randomly around the map', args = {'around_randomly'}, close = false}, 125 | {label = 'Stop Driving', description = 'The player ped will find a suitable place to stop the vehicle. The task will be stopped once the vehicle has reached the suitable stop location', args = {'stop_driving'}, close = false}, 126 | {label = 'Force Stop Driving', description = 'This will stop the driving task immediately without finding a suitable place to stop', args = {'force_stop_driving'}, close = false} 127 | } 128 | }, function(_, _, args) 129 | if args[1] == 'bMenu_player_autopilot_options_custom_driving_style' then 130 | setupDrivingStyleOptions() 131 | lib.showMenu(args[1], MenuIndexes[args[1]]) 132 | else 133 | local inVeh, reason = IsInVehicle(true) 134 | if not inVeh then 135 | lib.notify({ 136 | description = reason, 137 | type = 'error' 138 | }) 139 | return 140 | end 141 | 142 | if args[1] == 'to_waypoint' then 143 | if not IsWaypointActive() then 144 | lib.notify({ 145 | description = 'You need a waypoint set to drive to', 146 | type = 'error' 147 | }) 148 | return 149 | end 150 | 151 | ClearPedTasks(cache.ped) 152 | 153 | local waypointBlipInfo = GetFirstBlipInfoId(GetWaypointBlipEnumId()) 154 | local waypointBlipPos = waypointBlipInfo ~= 0 and GetBlipInfoIdType(waypointBlipInfo) == 4 and GetBlipInfoIdCoord(waypointBlipInfo) or vec2(0, 0) 155 | RequestCollisionAtCoord(waypointBlipPos.x, waypointBlipPos.y, 1000) 156 | local result, z = GetGroundZFor_3dCoord(waypointBlipPos.x, waypointBlipPos.y, 1000, false) 157 | if not result then 158 | z = 0 159 | end 160 | waypointBlipPos = vec3(waypointBlipPos.x, waypointBlipPos.y, z) 161 | 162 | SetDriverAbility(cache.ped, 1.0) 163 | SetDriverAggressiveness(cache.ped, 0.0) 164 | 165 | TaskVehicleDriveToCoordLongrange(cache.ped, cache.vehicle, waypointBlipPos.x, waypointBlipPos.y, waypointBlipPos.z, GetVehicleModelEstimatedMaxSpeed(GetEntityModel(cache.vehicle)), actualDrivingStyle, 10.0) 166 | 167 | lib.notify({ 168 | description = 'Your player ped is now driving the vehicle for you. You can cancel any time by pressing the Stop Driving button. The vehicle will stop when it has reached the destination', 169 | type = 'inform' 170 | }) 171 | elseif args[1] == 'around_randomly' then 172 | ClearPedTasks(cache.ped) 173 | 174 | SetDriverAbility(cache.ped, 1.0) 175 | SetDriverAggressiveness(cache.ped, 0.0) 176 | 177 | TaskVehicleDriveWander(cache.ped, cache.vehicle, GetVehicleModelEstimatedMaxSpeed(GetEntityModel(cache.vehicle)), actualDrivingStyle) 178 | 179 | lib.notify({ 180 | description = 'Your player ped is now driving the vehicle for you. You can cancel any time by pressing the Stop Driving button', 181 | type = 'inform' 182 | }) 183 | elseif args[1] == 'stop_driving' then 184 | local coords = GetEntityCoords(cache.ped) 185 | local success, closestNode = GetNthClosestVehicleNode(coords.x, coords.y, coords.z, 3, 0, 0, 0) 186 | 187 | if not success then return end 188 | 189 | lib.notify({ 190 | description = 'The player ped will find a suitable place to park the car and will then stop driving. Please wait', 191 | type = 'inform' 192 | }) 193 | ClearPedTasks(cache.ped) 194 | TaskVehiclePark(cache.ped, cache.vehicle, closestNode.x, closestNode.y, closestNode.z, GetEntityHeading(cache.ped), 3, 60, true) 195 | 196 | while #(GetEntityCoords(cache.ped).xy - closestNode.xy) > 3 do 197 | Wait(0) 198 | end 199 | 200 | BringVehicleToHalt(cache.vehicle, 3, 0, false) 201 | ClearPedTasks(cache.ped) 202 | 203 | lib.notify({ 204 | description = 'Your vehicle is now stopped', 205 | type = 'inform' 206 | }) 207 | elseif args[1] == 'force_stop_driving' then 208 | ClearPedTasks(cache.ped) 209 | end 210 | end 211 | end) 212 | end 213 | 214 | function SetupPlayerOptions() 215 | local perms = lib.callback.await('bMenu:server:hasConvarPermission', false, {'PlayerRelated', 'PlayerOptions'}, {'Godmode', 'Invisible', 'Unlimited_Stamina', 'Fast_Run', 'Fast_Swim', 'Super_Jump', 'No_Ragdoll', 'Ghost_Mode', 'Never_Wanted', 'Set_Wanted_Level', 'Everyone_Ignore_Player', 'Stay_In_Vehicle', 'Heal_Player', 'Set_Armor_Type', 'Adjust_Player_Clothes', 'Suicide', 'AutoPilot', 'Freeze_Player'}) 216 | local menuOptions = { 217 | {label = 'No access', description = 'You don\'t have access to any options, press enter to return', args = {'bMenu_player_related_options'}} 218 | } 219 | local index = 1 220 | 221 | if perms.Godmode then 222 | menuOptions[index] = {label = 'Godmode', description = 'Makes you invincible', args = {'godmode'}, checked = godmode, close = false} 223 | index += 1 224 | end 225 | 226 | if perms.Invisible then 227 | menuOptions[index] = {label = 'Invisible', description = 'Makes you invisible to yourself and others', args = {'invisible'}, checked = invisible, close = false} 228 | index += 1 229 | end 230 | 231 | if perms.Unlimited_Stamina then 232 | menuOptions[index] = {label = 'Unlimited Stamina', description = 'Allows you to run forever without slowing down or taking damage', args = {'unlimited_stamina'}, checked = unlimitedStamina, close = false} 233 | index += 1 234 | end 235 | 236 | if perms.Fast_Run then 237 | menuOptions[index] = {label = 'Fast Run', description = 'Get Snail powers and run very fast', args = {'fast_run'}, checked = fastRun, close = false} 238 | index += 1 239 | end 240 | 241 | if perms.Fast_Swim then 242 | menuOptions[index] = {label = 'Fast Swim', description = 'Get Snail 2.0 powers and swim super fast', args = {'fast_swim'}, checked = fastSwim, close = false} 243 | index += 1 244 | end 245 | 246 | if perms.Super_Jump then 247 | menuOptions[index] = {label = 'Super Jump', description = 'Get Snail 3.0 powers and jump like a champ', args = {'super_jump'}, checked = superJump, close = false} 248 | index += 1 249 | end 250 | 251 | if perms.No_Ragdoll then 252 | menuOptions[index] = {label = 'No Ragdoll', description = 'Disables player ragdoll, makes you not fall off your bike anymore', args = {'no_ragdoll'}, checked = noRagdoll, close = false} 253 | index += 1 254 | end 255 | 256 | if perms.Ghost_Mode then 257 | menuOptions[index] = {label = 'Ghost Mode', description = 'Be barely visible and untouchable', args = {'ghost'}, checked = ghostMode, close = false} 258 | index += 1 259 | end 260 | 261 | if perms.Never_Wanted then 262 | menuOptions[index] = {label = 'Never Wanted', description = 'Disables all wanted levels', args = {'never_wanted'}, checked = neverWanted, close = false} 263 | index += 1 264 | end 265 | 266 | if perms.Set_Wanted_Level then 267 | menuOptions[index] = {label = 'Set Wanted Level', args = {'set_wanted_level'}, values = {'0', '1', '2', '3', '4', '5'}, defaultIndex = 1, close = false} 268 | index += 1 269 | end 270 | 271 | if perms.Everyone_Ignore_Player then 272 | menuOptions[index] = {label = 'Everyone Ignore Player', description = 'Introverts love this', args = {'ignore_player'}, checked = IgnorePlayer, close = false} 273 | index += 1 274 | end 275 | 276 | if perms.Stay_In_Vehicle then 277 | menuOptions[index] = {label = 'Stay In Vehicle', description = 'When this is enabled, NPCs will not be able to drag you out of your vehicle if they get angry at you', args = {'stay_in_vehicle'}, checked = StayInVehicle, close = false} 278 | index += 1 279 | end 280 | 281 | if perms.Heal_Player then 282 | menuOptions[index] = {label = 'Heal Player', description = 'Give the player max health', args = {'heal_player'}, close = false} 283 | index += 1 284 | end 285 | 286 | if perms.Set_Armor_Type then 287 | menuOptions[index] = {label = 'Set Armor Type', description = 'Set the armor level/type for your player', args = {'set_armor_type'}, values = {'No Armor', GetLabelText('WT_BA_0'), GetLabelText('WT_BA_1'), GetLabelText('WT_BA_2'), GetLabelText('WT_BA_3'), GetLabelText('WT_BA_4')}, defaultIndex = 1, close = false} 288 | index += 1 289 | end 290 | 291 | if perms.Adjust_Player_Clothes then 292 | menuOptions[index] = {label = 'Clean Player Clothes', description = 'Clean your player clothes', args = {'clean_player_clothes'}, close = false} 293 | index += 1 294 | 295 | menuOptions[index] = {label = 'Dry Player Clothes', description = 'Dry your player clothes', args = {'dry_player_clothes'}, close = false} 296 | index += 1 297 | 298 | menuOptions[index] = {label = 'Wet Player Clothes', description = 'Wet your player clothes', args = {'wet_player_clothes'}, close = false} 299 | index += 1 300 | end 301 | 302 | if perms.Suicide then 303 | menuOptions[index] = {label = 'Commit Suicide', description = 'Kill yourself by taking the pill. Or by using a pistol if you have one', args = {'suicide'}} 304 | index += 1 305 | end 306 | 307 | if perms.AutoPilot then 308 | menuOptions[index] = {label = 'Auto Pilot', description = 'Vehicle auto pilot options', args = {'bMenu_player_autopilot_options'}} 309 | index += 1 310 | end 311 | 312 | if perms.Freeze_Player then 313 | menuOptions[index] = {label = 'Freeze Player', description = 'Freezes your ped at the current location', args = {'freeze'}, checked = freezePlayer, close = false} 314 | index += 1 315 | end 316 | 317 | lib.registerMenu({ 318 | id = 'bMenu_player_options', 319 | title = 'Player Options', 320 | position = MenuPosition, 321 | onClose = function(keyPressed) 322 | CloseMenu(false, keyPressed, 'bMenu_player_related_options') 323 | end, 324 | onSelected = function(selected) 325 | MenuIndexes['bMenu_player_options'] = selected 326 | end, 327 | onCheck = function(selected, checked, args) 328 | if args[1] == 'godmode' then 329 | godmode = checked 330 | SetEntityInvincible(cache.ped, godmode) 331 | lib.setMenuOptions('bMenu_player_options', {label = 'Godmode', description = 'Makes you invincible', args = {'godmode'}, checked = godmode, close = false}, selected) 332 | elseif args[1] == 'invisible' then 333 | invisible = checked 334 | SetEntityVisible(cache.ped, not invisible, false) 335 | lib.setMenuOptions('bMenu_player_options', {label = 'Invisible', description = 'Makes you invisible to yourself and others', args = {'invisible'}, checked = invisible, close = false}, selected) 336 | elseif args[1] == 'unlimited_stamina' then 337 | unlimitedStamina = checked 338 | StatSetInt(`MP0_STAMINA`, unlimitedStamina and 100 or 0, true) 339 | lib.setMenuOptions('bMenu_player_options', {label = 'Unlimited Stamina', description = 'Allows you to run forever without slowing down or taking damage', args = {'unlimited_stamina'}, checked = unlimitedStamina, close = false}, selected) 340 | elseif args[1] == 'fast_run' then 341 | fastRun = checked 342 | SetRunSprintMultiplierForPlayer(cache.playerId, fastRun and 1.49 or 1) 343 | lib.setMenuOptions('bMenu_player_options', {label = 'Fast Run', description = 'Get Snail powers and run very fast', args = {'fast_run'}, checked = fastRun, close = false}, selected) 344 | elseif args[1] == 'fast_swim' then 345 | fastSwim = checked 346 | SetSwimMultiplierForPlayer(cache.playerId, fastSwim and 1.49 or 1) 347 | lib.setMenuOptions('bMenu_player_options', {label = 'Fast Swim', description = 'Get Snail 2.0 powers and swim super fast', args = {'fast_swim'}, checked = fastSwim, close = false}, selected) 348 | elseif args[1] == 'super_jump' then 349 | superJump = checked 350 | lib.setMenuOptions('bMenu_player_options', {label = 'Super Jump', description = 'Get Snail 3.0 powers and jump like a champ', args = {'super_jump'}, checked = superJump, close = false}, selected) 351 | elseif args[1] == 'no_ragdoll' then 352 | noRagdoll = checked 353 | SetPedCanRagdoll(cache.ped, not noRagdoll) 354 | lib.setMenuOptions('bMenu_player_options', {label = 'No Ragdoll', description = 'Disables player ragdoll, makes you not fall off your bike anymore', args = {'no_ragdoll'}, checked = noRagdoll, close = false}, selected) 355 | elseif args[1] == 'ghost' then 356 | ghostMode = checked 357 | SetLocalPlayerAsGhost(ghostMode) 358 | lib.setMenuOptions('bMenu_player_options', {label = 'Ghost Mode', description = 'Be a invisible and untouchable', args = {'ghost'}, checked = ghostMode, close = false}, selected) 359 | elseif args[1] == 'never_wanted' then 360 | neverWanted = checked 361 | SetMaxWantedLevel(neverWanted and 0 or 5) 362 | if neverWanted then 363 | ClearPlayerWantedLevel(cache.playerId) 364 | end 365 | lib.setMenuOptions('bMenu_player_options', {label = 'Never Wanted', description = 'Disables all wanted levels', args = {'never_wanted'}, checked = neverWanted, close = false}, selected) 366 | elseif args[1] == 'ignore_player' then 367 | IgnorePlayer = checked 368 | SetEveryoneIgnorePlayer(cache.playerId, ignorePlayer) 369 | SetPoliceIgnorePlayer(cache.playerId, ignorePlayer) 370 | SetPlayerCanBeHassledByGangs(cache.playerId, not ignorePlayer) 371 | lib.setMenuOptions('bMenu_player_options', {label = 'Everyone Ignore Player', description = 'Introverts love this', args = {'ignore_player'}, checked = IgnorePlayer, close = false}, selected) 372 | elseif args[1] == 'stay_in_vehicle' then 373 | StayInVehicle = checked 374 | lib.setMenuOptions('bMenu_player_options', {label = 'Stay In Vehicle', description = 'When this is enabled, NPCs will not be able to drag you out of your vehicle if they get angry at you', args = {'stay_in_vehicle'}, checked = StayInVehicle, close = false}, selected) 375 | elseif args[1] == 'freeze' then 376 | freezePlayer = checked 377 | FreezeEntityPosition(cache.ped, freezePlayer) 378 | lib.setMenuOptions('bMenu_player_options', {label = 'Freeze Player', description = 'Freezes your ped at the current location', args = {'freeze'}, checked = freezePlayer, close = false}, selected) 379 | end 380 | end, 381 | onSideScroll = function(selected, scrollIndex, args) 382 | if args[1] == 'set_wanted_level' then 383 | lib.setMenuOptions('bMenu_player_options', {label = 'Set Wanted Level', args = {'set_wanted_level'}, values = {'0', '1', '2', '3', '4', '5'}, defaultIndex = scrollIndex, close = false}, selected) 384 | if neverWanted then return end 385 | SetPlayerWantedLevel(cache.playerId, scrollIndex - 1, false) 386 | SetPlayerWantedLevelNow(cache.playerId, false) 387 | elseif args[1] == 'set_armor_type' then 388 | SetPedArmour(cache.ped, (scrollIndex - 1) * 20) 389 | lib.setMenuOptions('bMenu_player_options', {label = 'Set Armor Type', description = 'Set the armor level/type for your player', args = {'set_armor_type'}, values = {'No Armor', GetLabelText('WT_BA_0'), GetLabelText('WT_BA_1'), GetLabelText('WT_BA_2'), GetLabelText('WT_BA_3'), GetLabelText('WT_BA_4')}, defaultIndex = 1, close = false}, selected) 390 | end 391 | end, 392 | options = menuOptions 393 | }, function(_, _, args) 394 | if args[1] == 'heal_player' then 395 | SetEntityHealth(cache.ped, GetEntityMaxHealth(cache.ped)) 396 | lib.notify({ 397 | description = 'You have been healed', 398 | type = 'success' 399 | }) 400 | elseif args[1] == 'clean_player_clothes' then 401 | ClearPedBloodDamage(cache.ped) 402 | lib.notify({ 403 | description = 'Your clothes have been cleaned', 404 | type = 'success' 405 | }) 406 | elseif args[1] == 'dry_player_clothes' then 407 | SetPedWetnessHeight(cache.ped, 0) 408 | lib.notify({ 409 | description = 'Your clothes are now dry', 410 | type = 'success' 411 | }) 412 | elseif args[1] == 'wet_player_clothes' then 413 | SetPedWetnessHeight(cache.ped, 2) 414 | lib.notify({ 415 | description = 'Your clothes are now wet', 416 | type = 'success' 417 | }) 418 | elseif args[1] == 'suicide' then 419 | local alert = lib.alertDialog({ 420 | header = 'Sure?', 421 | content = 'Are you sure you want to commit suicide? \n This action cannot be undone.', 422 | centered = true, 423 | cancel = true 424 | }) 425 | 426 | if alert == 'confirm' then 427 | if cache.vehicle then 428 | SetEntityHealth(cache.ped, 0) 429 | return 430 | end 431 | 432 | MenuOpen = false 433 | 434 | lib.requestAnimDict('mp_suicide') 435 | 436 | local weaponHash 437 | local takePill = false 438 | 439 | if HasPedGotWeapon(cache.ped, `WEAPON_PISTOL_MK2`, false) then 440 | weaponHash = `WEAPON_PISTOL_MK2` 441 | elseif HasPedGotWeapon(cache.ped, `WEAPON_COMBATPISTOL`, false) then 442 | weaponHash = `WEAPON_COMBATPISTOL` 443 | elseif HasPedGotWeapon(cache.ped, `WEAPON_PISTOL`, false) then 444 | weaponHash = `WEAPON_PISTOL` 445 | elseif HasPedGotWeapon(cache.ped, `WEAPON_SNSPISTOL_MK2`, false) then 446 | weaponHash = `WEAPON_SNSPISTOL_MK2` 447 | elseif HasPedGotWeapon(cache.ped, `WEAPON_SNSPISTOL`, false) then 448 | weaponHash = `WEAPON_SNSPISTOL` 449 | elseif HasPedGotWeapon(cache.ped, `WEAPON_PISTOL50`, false) then 450 | weaponHash = `WEAPON_PISTOL50` 451 | elseif HasPedGotWeapon(cache.ped, `WEAPON_HEAVYPISTOL`, false) then 452 | weaponHash = `WEAPON_HEAVYPISTOL` 453 | elseif HasPedGotWeapon(cache.ped, `WEAPON_VINTAGEPISTOL`, false) then 454 | weaponHash = `WEAPON_VINTAGEPISTOL` 455 | else 456 | takePill = true 457 | end 458 | 459 | if takePill then 460 | SetCurrentPedWeapon(cache.ped, `WEAPON_UNARMED`, true) 461 | else 462 | SetCurrentPedWeapon(cache.ped, weaponHash, true) 463 | SetPedDropsWeaponsWhenDead(cache.ped, true) 464 | end 465 | 466 | ClearPedTasks(cache.ped) 467 | TaskPlayAnim(cache.ped, 'mp_suicide', takePill and 'pill' or 'pistol', 8, -8, -1, 270540800, 0, false, false, false) 468 | 469 | local shot = false 470 | while true do 471 | local time = GetEntityAnimCurrentTime(cache.ped, 'mp_suicide', takePill and 'pill' or 'pistol') 472 | if HasAnimEventFired(cache.ped, `Fire`) and not shot then 473 | ClearEntityLastDamageEntity(cache.ped) 474 | SetPedShootsAtCoord(cache.ped, 0, 0, 0, false) 475 | shot = true 476 | end 477 | 478 | if time > (takePill and 0.536 or 0.365) then 479 | ClearEntityLastDamageEntity(cache.ped) 480 | SetEntityHealth(cache.ped, 0) 481 | break 482 | end 483 | Wait(0) 484 | end 485 | 486 | RemoveAnimDict('mp_suicide') 487 | elseif alert == 'cancel' then 488 | Wait(200) 489 | lib.showMenu('bMenu_player_options', MenuIndexes['bMenu_player_options']) 490 | end 491 | elseif string.match(args[1], 'bMenu') then 492 | if args[1] == 'bMenu_player_autopilot_options' then 493 | setupAutoPilotOptions() 494 | end 495 | 496 | lib.showMenu(args[1], MenuIndexes[args[1]]) 497 | end 498 | end) 499 | end 500 | 501 | --#endregion Functions 502 | 503 | --#region Listeners 504 | 505 | lib.onCache('ped', function(value) 506 | SetEntityInvincible(value, godmode) 507 | SetEntityVisible(value, not invisible, false) 508 | SetPedCanRagdoll(value, not noRagdoll) 509 | end) 510 | 511 | --#endregion Listeners 512 | 513 | --#region Threads 514 | 515 | CreateThread(function() 516 | SetRunSprintMultiplierForPlayer(cache.playerId, fastRun and 1.49 or 1) 517 | SetSwimMultiplierForPlayer(cache.playerId, fastSwim and 1.49 or 1) 518 | while true do 519 | if superJump then 520 | SetSuperJumpThisFrame(cache.playerId) 521 | end 522 | 523 | Wait(0) 524 | end 525 | end) 526 | 527 | CreateThread(function() 528 | while true do 529 | if neverWanted and GetPlayerWantedLevel(cache.playerId) > 0 then 530 | ClearPlayerWantedLevel(cache.playerId) 531 | if GetMaxWantedLevel() > 0 then 532 | SetMaxWantedLevel(0) 533 | end 534 | end 535 | 536 | Wait(100) 537 | end 538 | end) 539 | 540 | --#endregion Threads -------------------------------------------------------------------------------- /client/menus/player/weapons.lua: -------------------------------------------------------------------------------- 1 | --#region Variables 2 | 3 | local weapons = { 4 | -- Melee 5 | [`WEAPON_DAGGER`] = { 6 | name = 'WEAPON_DAGGER', 7 | hash = `WEAPON_DAGGER`, 8 | displayName = GetLabelText('WT_DAGGER'), 9 | weaponType = 'melee' 10 | }, 11 | [`WEAPON_BAT`] = { 12 | name = 'WEAPON_BAT', 13 | hash = `WEAPON_BAT`, 14 | displayName = GetLabelText('WT_BAT'), 15 | weaponType = 'melee' 16 | }, 17 | [`WEAPON_BOTTLE`] = { 18 | name = 'WEAPON_BOTTLE', 19 | hash = `WEAPON_BOTTLE`, 20 | displayName = GetLabelText('WT_BOTTLE'), 21 | weaponType = 'melee' 22 | }, 23 | [`WEAPON_CROWBAR`] = { 24 | name = 'WEAPON_CROWBAR', 25 | hash = `WEAPON_CROWBAR`, 26 | displayName = GetLabelText('WT_CROWBAR'), 27 | weaponType = 'melee' 28 | }, 29 | [`WEAPON_UNARMED`] = { 30 | name = 'WEAPON_UNARMED', 31 | hash = `WEAPON_UNARMED`, 32 | displayName = GetLabelText('WT_UNARMED'), 33 | weaponType = 'melee' 34 | }, 35 | [`WEAPON_FLASHLIGHT`] = { 36 | name = 'WEAPON_FLASHLIGHT', 37 | hash = `WEAPON_FLASHLIGHT`, 38 | displayName = GetLabelText('WT_FLASHLIGHT'), 39 | weaponType = 'melee' 40 | }, 41 | [`WEAPON_GOLFCLUB`] = { 42 | name = 'WEAPON_GOLFCLUB', 43 | hash = `WEAPON_GOLFCLUB`, 44 | displayName = GetLabelText('WT_GOLFCLUB'), 45 | weaponType = 'melee' 46 | }, 47 | [`WEAPON_HAMMER`] = { 48 | name = 'WEAPON_HAMMER', 49 | hash = `WEAPON_HAMMER`, 50 | displayName = GetLabelText('WT_HAMMER'), 51 | weaponType = 'melee' 52 | }, 53 | [`WEAPON_HATCHET`] = { 54 | name = 'WEAPON_HATCHET', 55 | hash = `WEAPON_HATCHET`, 56 | displayName = GetLabelText('WT_HATCHET'), 57 | weaponType = 'melee' 58 | }, 59 | [`WEAPON_KNUCKLE`] = { 60 | name = 'WEAPON_KNUCKLE', 61 | hash = `WEAPON_KNUCKLE`, 62 | displayName = GetLabelText('WT_KNUCKLE'), 63 | weaponType = 'melee' 64 | }, 65 | [`WEAPON_KNIFE`] = { 66 | name = 'WEAPON_KNIFE', 67 | hash = `WEAPON_KNIFE`, 68 | displayName = GetLabelText('WT_KNIFE'), 69 | weaponType = 'melee' 70 | }, 71 | [`WEAPON_MACHETE`] = { 72 | name = 'WEAPON_MACHETE', 73 | hash = `WEAPON_MACHETE`, 74 | displayName = GetLabelText('WT_MACHETE'), 75 | weaponType = 'melee' 76 | }, 77 | [`WEAPON_SWITCHBLADE`] = { 78 | name = 'WEAPON_SWITCHBLADE', 79 | hash = `WEAPON_SWITCHBLADE`, 80 | displayName = GetLabelText('WT_SWBLADE'), 81 | weaponType = 'melee' 82 | }, 83 | [`WEAPON_NIGHTSTICK`] = { 84 | name = 'WEAPON_NIGHTSTICK', 85 | hash = `WEAPON_NIGHTSTICK`, 86 | displayName = GetLabelText('WT_NGTSTK'), 87 | weaponType = 'melee' 88 | }, 89 | [`WEAPON_WRENCH`] = { 90 | name = 'WEAPON_WRENCH', 91 | hash = `WEAPON_WRENCH`, 92 | displayName = GetLabelText('WT_WRENCH'), 93 | weaponType = 'melee' 94 | }, 95 | [`WEAPON_BATTLEAXE`] = { 96 | name = 'WEAPON_BATTLEAXE', 97 | hash = `WEAPON_BATTLEAXE`, 98 | displayName = GetLabelText('WT_BATTLEAXE'), 99 | weaponType = 'melee' 100 | }, 101 | [`WEAPON_POOLCUE`] = { 102 | name = 'WEAPON_POOLCUE', 103 | hash = `WEAPON_POOLCUE`, 104 | displayName = GetLabelText('WT_POOLCUE'), 105 | weaponType = 'melee' 106 | }, 107 | [`WEAPON_STONE_HATCHET`] = { 108 | name = 'WEAPON_STONE_HATCHET', 109 | hash = `WEAPON_STONE_HATCHET`, 110 | displayName = GetLabelText('WT_SHATCHET'), 111 | weaponType = 'melee' 112 | }, 113 | [`WEAPON_CANDYCANE`] = { 114 | name = 'WEAPON_CANDYCANE', 115 | hash = `WEAPON_CANDYCANE`, 116 | displayName = GetLabelText('WT_CANDYCANE'), 117 | weaponType = 'melee' 118 | }, 119 | 120 | -- Handguns 121 | [`WEAPON_PISTOL`] = { 122 | name = 'WEAPON_PISTOL', 123 | hash = `WEAPON_PISTOL`, 124 | displayName = GetLabelText('WT_PIST'), 125 | weaponType = 'handgun' 126 | }, 127 | [`WEAPON_PISTOL_MK2`] = { 128 | name = 'WEAPON_PISTOL_MK2', 129 | hash = `WEAPON_PISTOL_MK2`, 130 | displayName = GetLabelText('WT_PIST2'), 131 | weaponType = 'handgun' 132 | }, 133 | [`WEAPON_COMBATPISTOL`] = { 134 | name = 'WEAPON_COMBATPISTOL', 135 | hash = `WEAPON_COMBATPISTOL`, 136 | displayName = GetLabelText('WT_PIST_CBT'), 137 | weaponType = 'handgun' 138 | }, 139 | [`WEAPON_APPISTOL`] = { 140 | name = 'WEAPON_APPISTOL', 141 | hash = `WEAPON_APPISTOL`, 142 | displayName = GetLabelText('WT_PIST_AP'), 143 | weaponType = 'handgun' 144 | }, 145 | [`WEAPON_STUNGUN`] = { 146 | name = 'WEAPON_STUNGUN', 147 | hash = `WEAPON_STUNGUN`, 148 | displayName = GetLabelText('WT_STUN'), 149 | weaponType = 'handgun' 150 | }, 151 | [`WEAPON_PISTOL50`] = { 152 | name = 'WEAPON_PISTOL50', 153 | hash = `WEAPON_PISTOL50`, 154 | displayName = GetLabelText('WT_PIST_50'), 155 | weaponType = 'handgun' 156 | }, 157 | [`WEAPON_SNSPISTOL`] = { 158 | name = 'WEAPON_SNSPISTOL', 159 | hash = `WEAPON_SNSPISTOL`, 160 | displayName = GetLabelText('WT_SNSPISTOL'), 161 | weaponType = 'handgun' 162 | }, 163 | [`WEAPON_SNSPISTOL_MK2`] = { 164 | name = 'WEAPON_SNSPISTOL_MK2', 165 | hash = `WEAPON_SNSPISTOL_MK2`, 166 | displayName = GetLabelText('WT_SNSPISTOL2'), 167 | weaponType = 'handgun' 168 | }, 169 | [`WEAPON_HEAVYPISTOL`] = { 170 | name = 'WEAPON_HEAVYPISTOL', 171 | hash = `WEAPON_HEAVYPISTOL`, 172 | displayName = GetLabelText('WT_HVYPISTOL'), 173 | weaponType = 'handgun' 174 | }, 175 | [`WEAPON_VINTAGEPISTOL`] = { 176 | name = 'WEAPON_VINTAGEPISTOL', 177 | hash = `WEAPON_VINTAGEPISTOL`, 178 | displayName = GetLabelText('WT_VPISTOL'), 179 | weaponType = 'handgun' 180 | }, 181 | [`WEAPON_FLAREGUN`] = { 182 | name = 'WEAPON_FLAREGUN', 183 | hash = `WEAPON_FLAREGUN`, 184 | displayName = GetLabelText('WT_FLAREGUN'), 185 | weaponType = 'handgun' 186 | }, 187 | [`WEAPON_MARKSMANPISTOL`] = { 188 | name = 'WEAPON_MARKSMANPISTOL', 189 | hash = `WEAPON_MARKSMANPISTOL`, 190 | displayName = GetLabelText('WT_MKPISTOL'), 191 | weaponType = 'handgun' 192 | }, 193 | [`WEAPON_REVOLVER`] = { 194 | name = 'WEAPON_REVOLVER', 195 | hash = `WEAPON_REVOLVER`, 196 | displayName = GetLabelText('WT_REVOLVER'), 197 | weaponType = 'handgun' 198 | }, 199 | [`WEAPON_REVOLVER_MK2`] = { 200 | name = 'WEAPON_REVOLVER_MK2', 201 | hash = `WEAPON_REVOLVER_MK2`, 202 | displayName = GetLabelText('WT_REVOLVER2'), 203 | weaponType = 'handgun' 204 | }, 205 | [`WEAPON_DOUBLEACTION`] = { 206 | name = 'WEAPON_DOUBLEACTION', 207 | hash = `WEAPON_DOUBLEACTION`, 208 | displayName = GetLabelText('WT_REV_DA'), 209 | weaponType = 'handgun' 210 | }, 211 | [`WEAPON_RAYPISTOL`] = { 212 | name = 'WEAPON_RAYPISTOL', 213 | hash = `WEAPON_RAYPISTOL`, 214 | displayName = GetLabelText('WT_RAYPISTOL'), 215 | weaponType = 'handgun' 216 | }, 217 | [`WEAPON_CERAMICPISTOL`] = { 218 | name = 'WEAPON_CERAMICPISTOL', 219 | hash = `WEAPON_CERAMICPISTOL`, 220 | displayName = GetLabelText('WT_CERPST'), 221 | weaponType = 'handgun' 222 | }, 223 | [`WEAPON_NAVYREVOLVER`] = { 224 | name = 'WEAPON_NAVYREVOLVER', 225 | hash = `WEAPON_NAVYREVOLVER`, 226 | displayName = GetLabelText('WT_REV_NV'), 227 | weaponType = 'handgun' 228 | }, 229 | [`WEAPON_GADGETPISTOL`] = { 230 | name = 'WEAPON_GADGETPISTOL', 231 | hash = `WEAPON_GADGETPISTOL`, 232 | displayName = GetLabelText('WT_GDGTPST'), 233 | weaponType = 'handgun' 234 | }, 235 | [`WEAPON_STUNGUN_MP`] = { 236 | name = 'WEAPON_STUNGUN_MP', 237 | hash = `WEAPON_STUNGUN_MP`, 238 | displayName = GetLabelText('WT_STNGUNMP'), 239 | weaponType = 'handgun' 240 | }, 241 | [`WEAPON_PISTOLXM3`] = { 242 | name = 'WEAPON_PISTOLXM3', 243 | hash = `WEAPON_PISTOLXM3`, 244 | displayName = GetLabelText('WT_PISTOLXM3'), 245 | weaponType = 'handgun' 246 | }, 247 | 248 | -- Submachine Guns 249 | [`WEAPON_MICROSMG`] = { 250 | name = 'WEAPON_MICROSMG', 251 | hash = `WEAPON_MICROSMG`, 252 | displayName = GetLabelText('WT_SMG_MCR'), 253 | weaponType = 'slmg' 254 | }, 255 | [`WEAPON_SMG`] = { 256 | name = 'WEAPON_SMG', 257 | hash = `WEAPON_SMG`, 258 | displayName = GetLabelText('WT_SMG'), 259 | weaponType = 'slmg' 260 | }, 261 | [`WEAPON_SMG_MK2`] = { 262 | name = 'WEAPON_SMG_MK2', 263 | hash = `WEAPON_SMG_MK2`, 264 | displayName = GetLabelText('WT_SMG2'), 265 | weaponType = 'slmg' 266 | }, 267 | [`WEAPON_ASSAULTSMG`] = { 268 | name = 'WEAPON_ASSAULTSMG', 269 | hash = `WEAPON_ASSAULTSMG`, 270 | displayName = GetLabelText('WT_SMG_ASL'), 271 | weaponType = 'slmg' 272 | }, 273 | [`WEAPON_COMBATPDW`] = { 274 | name = 'WEAPON_COMBATPDW', 275 | hash = `WEAPON_COMBATPDW`, 276 | displayName = GetLabelText('WT_COMBATPDW'), 277 | weaponType = 'slmg' 278 | }, 279 | [`WEAPON_MACHINEPISTOL`] = { 280 | name = 'WEAPON_MACHINEPISTOL', 281 | hash = `WEAPON_MACHINEPISTOL`, 282 | displayName = GetLabelText('WT_MCHPIST'), 283 | weaponType = 'slmg' 284 | }, 285 | [`WEAPON_MINISMG`] = { 286 | name = 'WEAPON_MINISMG', 287 | hash = `WEAPON_MINISMG`, 288 | displayName = GetLabelText('WT_MINISMG'), 289 | weaponType = 'slmg' 290 | }, 291 | [`WEAPON_RAYCARBINE`] = { 292 | name = 'WEAPON_RAYCARBINE', 293 | hash = `WEAPON_RAYCARBINE`, 294 | displayName = GetLabelText('WT_RAYCARBINE'), 295 | weaponType = 'slmg' 296 | }, 297 | 298 | -- Shotguns 299 | [`WEAPON_PUMPSHOTGUN`] = { 300 | name = 'WEAPON_PUMPSHOTGUN', 301 | hash = `WEAPON_PUMPSHOTGUN`, 302 | displayName = GetLabelText('WT_SG_PMP'), 303 | weaponType = 'shotgun' 304 | }, 305 | [`WEAPON_PUMPSHOTGUN_MK2`] = { 306 | name = 'WEAPON_PUMPSHOTGUN_MK2', 307 | hash = `WEAPON_PUMPSHOTGUN_MK2`, 308 | displayName = GetLabelText('WT_SG_PMP2'), 309 | weaponType = 'shotgun' 310 | }, 311 | [`WEAPON_SAWNOFFSHOTGUN`] = { 312 | name = 'WEAPON_SAWNOFFSHOTGUN', 313 | hash = `WEAPON_SAWNOFFSHOTGUN`, 314 | displayName = GetLabelText('WT_SG_SOF'), 315 | weaponType = 'shotgun' 316 | }, 317 | [`WEAPON_ASSAULTSHOTGUN`] = { 318 | name = 'WEAPON_ASSAULTSHOTGUN', 319 | hash = `WEAPON_ASSAULTSHOTGUN`, 320 | displayName = GetLabelText('WT_SG_ASL'), 321 | weaponType = 'shotgun' 322 | }, 323 | [`WEAPON_BULLPUPSHOTGUN`] = { 324 | name = 'WEAPON_BULLPUPSHOTGUN', 325 | hash = `WEAPON_BULLPUPSHOTGUN`, 326 | displayName = GetLabelText('WT_SG_BLP'), 327 | weaponType = 'shotgun' 328 | }, 329 | [`WEAPON_MUSKET`] = { 330 | name = 'WEAPON_MUSKET', 331 | hash = `WEAPON_MUSKET`, 332 | displayName = GetLabelText('WT_MUSKET'), 333 | weaponType = 'shotgun' 334 | }, 335 | [`WEAPON_HEAVYSHOTGUN`] = { 336 | name = 'WEAPON_HEAVYSHOTGUN', 337 | hash = `WEAPON_HEAVYSHOTGUN`, 338 | displayName = GetLabelText('WT_HVYSHGN'), 339 | weaponType = 'shotgun' 340 | }, 341 | [`WEAPON_DBSHOTGUN`] = { 342 | name = 'WEAPON_DBSHOTGUN', 343 | hash = `WEAPON_DBSHOTGUN`, 344 | displayName = GetLabelText('WT_DBSHGN'), 345 | weaponType = 'shotgun' 346 | }, 347 | [`WEAPON_AUTOSHOTGUN`] = { 348 | name = 'WEAPON_AUTOSHOTGUN', 349 | hash = `WEAPON_AUTOSHOTGUN`, 350 | displayName = GetLabelText('WT_AUTOSHGN'), 351 | weaponType = 'shotgun' 352 | }, 353 | [`WEAPON_COMBATSHOTGUN`] = { 354 | name = 'WEAPON_COMBATSHOTGUN', 355 | hash = `WEAPON_COMBATSHOTGUN`, 356 | displayName = GetLabelText('WT_CMBSHGN'), 357 | weaponType = 'shotgun' 358 | }, 359 | 360 | -- Assault Rifles 361 | [`WEAPON_ASSAULTRIFLE`] = { 362 | name = 'WEAPON_ASSAULTRIFLE', 363 | hash = `WEAPON_ASSAULTRIFLE`, 364 | displayName = GetLabelText('WT_RIFLE_ASL'), 365 | weaponType = 'rifle' 366 | }, 367 | [`WEAPON_ASSAULTRIFLE_MK2`] = { 368 | name = 'WEAPON_ASSAULTRIFLE_MK2', 369 | hash = `WEAPON_ASSAULTRIFLE_MK2`, 370 | displayName = GetLabelText('WT_RIFLE_ASL2'), 371 | weaponType = 'rifle' 372 | }, 373 | [`WEAPON_CARBINERIFLE`] = { 374 | name = 'WEAPON_CARBINERIFLE', 375 | hash = `WEAPON_CARBINERIFLE`, 376 | displayName = GetLabelText('WT_RIFLE_CBN'), 377 | weaponType = 'rifle' 378 | }, 379 | [`WEAPON_CARBINERIFLE_MK2`] = { 380 | name = 'WEAPON_CARBINERIFLE_MK2', 381 | hash = `WEAPON_CARBINERIFLE_MK2`, 382 | displayName = GetLabelText('WT_RIFLE_CBN2'), 383 | weaponType = 'rifle' 384 | }, 385 | [`WEAPON_ADVANCEDRIFLE`] = { 386 | name = 'WEAPON_ADVANCEDRIFLE', 387 | hash = `WEAPON_ADVANCEDRIFLE`, 388 | displayName = GetLabelText('WT_RIFLE_ADV'), 389 | weaponType = 'rifle' 390 | }, 391 | [`WEAPON_SPECIALCARBINE`] = { 392 | name = 'WEAPON_SPECIALCARBINE', 393 | hash = `WEAPON_SPECIALCARBINE`, 394 | displayName = GetLabelText('WT_SPCARBINE'), 395 | weaponType = 'rifle' 396 | }, 397 | [`WEAPON_SPECIALCARBINE_MK2`] = { 398 | name = 'WEAPON_SPECIALCARBINE_MK2', 399 | hash = `WEAPON_SPECIALCARBINE_MK2`, 400 | displayName = GetLabelText('WT_SPCARBINE2'), 401 | weaponType = 'rifle' 402 | }, 403 | [`WEAPON_BULLPUPRIFLE`] = { 404 | name = 'WEAPON_BULLPUPRIFLE', 405 | hash = `WEAPON_BULLPUPRIFLE`, 406 | displayName = GetLabelText('WT_BULLRIFLE'), 407 | weaponType = 'rifle' 408 | }, 409 | [`WEAPON_BULLPUPRIFLE_MK2`] = { 410 | name = 'WEAPON_BULLPUPRIFLE_MK2', 411 | hash = `WEAPON_BULLPUPRIFLE_MK2`, 412 | displayName = GetLabelText('WT_BULLRIFLE2'), 413 | weaponType = 'rifle' 414 | }, 415 | [`WEAPON_COMPACTRIFLE`] = { 416 | name = 'WEAPON_COMPACTRIFLE', 417 | hash = `WEAPON_COMPACTRIFLE`, 418 | displayName = GetLabelText('WT_CMPRIFLE'), 419 | weaponType = 'rifle' 420 | }, 421 | [`WEAPON_MILITARYRIFLE`] = { 422 | name = 'WEAPON_MILITARYRIFLE', 423 | hash = `WEAPON_MILITARYRIFLE`, 424 | displayName = GetLabelText('WT_MLTRYRFL'), 425 | weaponType = 'rifle' 426 | }, 427 | [`WEAPON_HEAVYRIFLE`] = { 428 | name = 'WEAPON_HEAVYRIFLE', 429 | hash = `WEAPON_HEAVYRIFLE`, 430 | displayName = GetLabelText('WT_HEAVYRIFLE'), 431 | weaponType = 'rifle' 432 | }, 433 | [`WEAPON_TACTICALRIFLE`] = { 434 | name = 'WEAPON_TACTICALRIFLE', 435 | hash = `WEAPON_TACTICALRIFLE`, 436 | displayName = GetLabelText('WT_TACRIFLE'), 437 | weaponType = 'rifle' 438 | }, 439 | 440 | -- Light Machine Guns 441 | [`WEAPON_MG`] = { 442 | name = 'WEAPON_MG', 443 | hash = `WEAPON_MG`, 444 | displayName = GetLabelText('WT_MG'), 445 | weaponType = 'slmg' 446 | }, 447 | [`WEAPON_COMBATMG`] = { 448 | name = 'WEAPON_COMBATMG', 449 | hash = `WEAPON_COMBATMG`, 450 | displayName = GetLabelText('WT_MG_CBT'), 451 | weaponType = 'slmg' 452 | }, 453 | [`WEAPON_COMBATMG_MK2`] = { 454 | name = 'WEAPON_COMBATMG_MK2', 455 | hash = `WEAPON_COMBATMG_MK2`, 456 | displayName = GetLabelText('WT_MG_CBT2'), 457 | weaponType = 'slmg' 458 | }, 459 | [`WEAPON_GUSENBERG`] = { 460 | name = 'WEAPON_GUSENBERG', 461 | hash = `WEAPON_GUSENBERG`, 462 | displayName = GetLabelText('WT_GUSNBRG'), 463 | weaponType = 'slmg' 464 | }, 465 | 466 | -- Sniper Rifles 467 | [`WEAPON_SNIPERRIFLE`] = { 468 | name = 'WEAPON_SNIPERRIFLE', 469 | hash = `WEAPON_SNIPERRIFLE`, 470 | displayName = GetLabelText('WT_SNIP_RIF'), 471 | weaponType = 'sniper' 472 | }, 473 | [`WEAPON_HEAVYSNIPER`] = { 474 | name = 'WEAPON_HEAVYSNIPER', 475 | hash = `WEAPON_HEAVYSNIPER`, 476 | displayName = GetLabelText('WT_SNIP_HVY'), 477 | weaponType = 'sniper' 478 | }, 479 | [`WEAPON_HEAVYSNIPER_MK2`] = { 480 | name = 'WEAPON_HEAVYSNIPER_MK2', 481 | hash = `WEAPON_HEAVYSNIPER_MK2`, 482 | displayName = GetLabelText('WT_SNIP_HVY2'), 483 | weaponType = 'sniper' 484 | }, 485 | [`WEAPON_MARKSMANRIFLE`] = { 486 | name = 'WEAPON_MARKSMANRIFLE', 487 | hash = `WEAPON_MARKSMANRIFLE`, 488 | displayName = GetLabelText('WT_MKRIFLE'), 489 | weaponType = 'sniper' 490 | }, 491 | [`WEAPON_MARKSMANRIFLE_MK2`] = { 492 | name = 'WEAPON_MARKSMANRIFLE_MK2', 493 | hash = `WEAPON_MARKSMANRIFLE_MK2`, 494 | displayName = GetLabelText('WT_MKRIFLE2'), 495 | weaponType = 'sniper' 496 | }, 497 | [`WEAPON_PRECISIONRIFLE`] = { 498 | name = 'WEAPON_PRECISIONRIFLE', 499 | hash = `WEAPON_PRECISIONRIFLE`, 500 | displayName = GetLabelText('WT_PRCSRIFLE'), 501 | weaponType = 'sniper' 502 | }, 503 | 504 | -- Heavy Weapons 505 | [`WEAPON_RPG`] = { 506 | name = 'WEAPON_RPG', 507 | hash = `WEAPON_RPG`, 508 | displayName = GetLabelText('WT_RPG'), 509 | weaponType = 'heavy' 510 | }, 511 | [`WEAPON_GRENADELAUNCHER`] = { 512 | name = 'WEAPON_GRENADELAUNCHER', 513 | hash = `WEAPON_GRENADELAUNCHER`, 514 | displayName = GetLabelText('WT_GL'), 515 | weaponType = 'heavy' 516 | }, 517 | [`WEAPON_GRENADELAUNCHER_SMOKE`] = { 518 | name = 'WEAPON_GRENADELAUNCHER_SMOKE', 519 | hash = `WEAPON_GRENADELAUNCHER_SMOKE`, 520 | displayName = GetLabelText('WT_GL_SMOKE'), 521 | weaponType = 'heavy' 522 | }, 523 | [`WEAPON_MINIGUN`] = { 524 | name = 'WEAPON_MINIGUN', 525 | hash = `WEAPON_MINIGUN`, 526 | displayName = GetLabelText('WT_MINIGUN'), 527 | weaponType = 'heavy' 528 | }, 529 | [`WEAPON_FIREWORK`] = { 530 | name = 'WEAPON_FIREWORK', 531 | hash = `WEAPON_FIREWORK`, 532 | displayName = GetLabelText('WT_FIREWRK'), 533 | weaponType = 'heavy' 534 | }, 535 | [`WEAPON_RAILGUN`] = { 536 | name = 'WEAPON_RAILGUN', 537 | hash = `WEAPON_RAILGUN`, 538 | displayName = GetLabelText('WT_RAILGUN'), 539 | weaponType = 'heavy' 540 | }, 541 | [`WEAPON_HOMINGLAUNCHER`] = { 542 | name = 'WEAPON_HOMINGLAUNCHER', 543 | hash = `WEAPON_HOMINGLAUNCHER`, 544 | displayName = GetLabelText('WT_HOMLNCH'), 545 | weaponType = 'heavy' 546 | }, 547 | [`WEAPON_COMPACTLAUNCHER`] = { 548 | name = 'WEAPON_COMPACTLAUNCHER', 549 | hash = `WEAPON_COMPACTLAUNCHER`, 550 | displayName = GetLabelText('WT_CMPGL'), 551 | weaponType = 'heavy' 552 | }, 553 | [`WEAPON_RAYMINIGUN`] = { 554 | name = 'WEAPON_RAYMINIGUN', 555 | hash = `WEAPON_RAYMINIGUN`, 556 | displayName = GetLabelText('WT_RAYMINIGUN'), 557 | weaponType = 'heavy' 558 | }, 559 | [`WEAPON_EMPLAUNCHER`] = { 560 | name = 'WEAPON_EMPLAUNCHER', 561 | hash = `WEAPON_EMPLAUNCHER`, 562 | displayName = GetLabelText('WT_EMPL'), 563 | weaponType = 'heavy' 564 | }, 565 | [`WEAPON_RAILGUNXM3`] = { 566 | name = 'WEAPON_RAILGUNXM3', 567 | hash = `WEAPON_RAILGUNXM3`, 568 | displayName = GetLabelText('WT_RAILGUN'), 569 | weaponType = 'heavy' 570 | }, 571 | 572 | -- Throwables 573 | [`WEAPON_GRENADE`] = { 574 | name = 'WEAPON_GRENADE', 575 | hash = `WEAPON_GRENADE`, 576 | displayName = GetLabelText('WT_GNADE'), 577 | weaponType = 'throwable' 578 | }, 579 | [`WEAPON_BZGAS`] = { 580 | name = 'WEAPON_BZGAS', 581 | hash = `WEAPON_BZGAS`, 582 | displayName = GetLabelText('WT_BZGAS'), 583 | weaponType = 'throwable' 584 | }, 585 | [`WEAPON_MOLOTOV`] = { 586 | name = 'WEAPON_MOLOTOV', 587 | hash = `WEAPON_MOLOTOV`, 588 | displayName = GetLabelText('WT_MOLOTOV'), 589 | weaponType = 'throwable' 590 | }, 591 | [`WEAPON_STICKYBOMB`] = { 592 | name = 'WEAPON_STICKYBOMB', 593 | hash = `WEAPON_STICKYBOMB`, 594 | displayName = GetLabelText('WT_GNADE_STK'), 595 | weaponType = 'throwable' 596 | }, 597 | [`WEAPON_PROXMINE`] = { 598 | name = 'WEAPON_PROXMINE', 599 | hash = `WEAPON_PROXMINE`, 600 | displayName = GetLabelText('WT_PRXMINE'), 601 | weaponType = 'throwable' 602 | }, 603 | [`WEAPON_SNOWBALL`] = { 604 | name = 'WEAPON_SNOWBALL', 605 | hash = `WEAPON_SNOWBALL`, 606 | displayName = GetLabelText('WT_SNWBALL'), 607 | weaponType = 'throwable' 608 | }, 609 | [`WEAPON_PIPEBOMB`] = { 610 | name = 'WEAPON_PIPEBOMB', 611 | hash = `WEAPON_PIPEBOMB`, 612 | displayName = GetLabelText('WT_PIPEBOMB'), 613 | weaponType = 'throwable' 614 | }, 615 | [`WEAPON_BALL`] = { 616 | name = 'WEAPON_BALL', 617 | hash = `WEAPON_BALL`, 618 | displayName = GetLabelText('WT_BALL'), 619 | weaponType = 'throwable' 620 | }, 621 | [`WEAPON_SMOKEGRENADE`] = { 622 | name = 'WEAPON_SMOKEGRENADE', 623 | hash = `WEAPON_SMOKEGRENADE`, 624 | displayName = GetLabelText('WT_GNADE_SMK'), 625 | weaponType = 'throwable' 626 | }, 627 | [`WEAPON_FLARE`] = { 628 | name = 'WEAPON_FLARE', 629 | hash = `WEAPON_FLARE`, 630 | displayName = GetLabelText('WT_FLARE'), 631 | weaponType = 'throwable' 632 | }, 633 | 634 | -- Miscellaneous 635 | [`WEAPON_PETROLCAN`] = { 636 | name = 'WEAPON_PETROLCAN', 637 | hash = `WEAPON_PETROLCAN`, 638 | displayName = GetLabelText('WT_PETROL'), 639 | weaponType = 'misc' 640 | }, 641 | [`GADGET_PARACHUTE`] = { 642 | name = 'GADGET_PARACHUTE', 643 | hash = `GADGET_PARACHUTE`, 644 | displayName = GetLabelText('WT_PARA'), 645 | weaponType = 'misc' 646 | }, 647 | [`WEAPON_FIREEXTINGUISHER`] = { 648 | name = 'WEAPON_FIREEXTINGUISHER', 649 | hash = `WEAPON_FIREEXTINGUISHER`, 650 | displayName = GetLabelText('WT_FIRE'), 651 | weaponType = 'misc' 652 | }, 653 | [`WEAPON_HAZARDCAN`] = { 654 | name = 'WEAPON_HAZARDCAN', 655 | hash = `WEAPON_HAZARDCAN`, 656 | displayName = GetLabelText('WT_HAZARDCAN'), 657 | weaponType = 'misc' 658 | }, 659 | [`WEAPON_FERTILIZERCAN`] = { 660 | name = 'WEAPON_FERTILIZERCAN', 661 | hash = `WEAPON_FERTILIZERCAN`, 662 | displayName = GetLabelText('WT_FERTILIZERCAN'), 663 | weaponType = 'misc' 664 | }, 665 | [`GADGET_NIGHTVISION`] = { 666 | name = 'GADGET_NIGHTVISION', 667 | hash = `GADGET_NIGHTVISION`, 668 | displayName = GetLabelText('WT_NV'), 669 | weaponType = 'misc' 670 | }, 671 | [`WEAPON_TRANQUILIZER`] = { 672 | name = 'WEAPON_TRANQUILIZER', 673 | hash = `WEAPON_TRANQUILIZER`, 674 | displayName = GetLabelText('WT_STUN'), 675 | weaponType = 'misc' 676 | }, 677 | [`WEAPON_METALDETECTOR`] = { 678 | name = 'WEAPON_METALDETECTOR', 679 | hash = `WEAPON_METALDETECTOR`, 680 | displayName = GetLabelText('WT_METALDETECTOR'), 681 | weaponType = 'misc' 682 | } 683 | } 684 | 685 | local weaponCategories = { 686 | handgun = 'Handguns', 687 | rifle = 'Assault Rifles', 688 | shotgun = 'Shotguns', 689 | slmg = 'Sub/Light Machine Guns', 690 | throwable = 'Throwables', 691 | melee = 'Melee', 692 | heavy = 'Heavy Weapons', 693 | sniper = 'Snipers', 694 | misc = 'Miscellaneous' 695 | } 696 | 697 | local weaponCategoriesArray = { 698 | 'rifle', 699 | 'handgun', 700 | 'heavy', 701 | 'melee', 702 | 'misc', 703 | 'shotgun', 704 | 'sniper', 705 | 'slmg', 706 | 'throwable' 707 | } 708 | 709 | local unlimitedAmmo = false 710 | local unlimitedClip = false 711 | 712 | --#endregion Variables 713 | 714 | --#region Functions 715 | 716 | function SetupWeaponsMenu() 717 | local perms = lib.callback.await('bMenu:server:hasConvarPermission', false, {'PlayerRelated', 'WeaponOptions'}, {'Get_All_Weapons', 'Remove_All_Weapons', 'Unlimited_Ammo', 'No_Reload', 'Set_All_Ammo_Count', 'Set_Max_Ammo', 'Spawn_Weapon_By_Name'}) 718 | local categoryPerms = lib.callback.await('bMenu:server:hasConvarPermission', false, {'PlayerRelated', 'WeaponOptions', 'Category'}, weaponCategoriesArray) 719 | local menuOptions = { 720 | {label = 'No access', description = 'You don\'t have access to any options, press enter to return', args = {'bMenu_player_related_options'}} 721 | } 722 | local index = 1 723 | local id = 'bMenu_player_weapon_options' 724 | local indexes = {} 725 | 726 | if perms.Get_All_Weapons then 727 | menuOptions[index] = {label = 'Get All Weapons', args = {'get_all_weapons'}, close = false} 728 | index += 1 729 | end 730 | 731 | if perms.Remove_All_Weapons then 732 | menuOptions[index] = {label = 'Remove All Weapons', args = {'remove_all_weapons'}, close = false} 733 | index += 1 734 | end 735 | 736 | if perms.Unlimited_Ammo then 737 | menuOptions[index] = {label = 'Unlimited Ammo', args = {'unlimited_ammo'}, checked = unlimitedAmmo, close = false} 738 | index += 1 739 | end 740 | 741 | if perms.No_Reload then 742 | menuOptions[index] = {label = 'No Reload', description = 'Never have to reload your weapon anymore', args = {'no_reload'}, checked = unlimitedClip, close = false} 743 | index += 1 744 | end 745 | 746 | if perms.Set_All_Ammo_Count then 747 | menuOptions[index] = {label = 'Set All Ammo Count', description = 'Set the amount of ammo in all your weapons', args = {'set_all_ammo'}} 748 | index += 1 749 | end 750 | 751 | if perms.Set_Max_Ammo then 752 | menuOptions[index] = {label = 'Set Max Ammo', description = 'Give all your weapons max ammo', args = {'max_ammo'}, close = false} 753 | index += 1 754 | end 755 | 756 | if perms.Spawn_Weapon_By_Name then 757 | menuOptions[index] = {label = 'Spawn Weapon By Name', description = 'Enter a weapon name to spawn', args = {'weapon_name'}} 758 | index += 1 759 | end 760 | 761 | for i = 1, #weaponCategoriesArray do 762 | local category = weaponCategoriesArray[i] 763 | if categoryPerms[category] then 764 | local formattedId = ('%s_%s'):format(id, category) 765 | lib.registerMenu({ 766 | id = formattedId, 767 | title = weaponCategories[category], 768 | position = MenuPosition, 769 | onClose = function(keyPressed) 770 | CloseMenu(false, keyPressed, 'bMenu_player_weapon_options') 771 | end, 772 | options = {} 773 | }, function(_, _, args) 774 | if HasPedGotWeapon(cache.ped, args[1], false) then return end 775 | 776 | local _, ammo = GetMaxAmmo(cache.ped, args[1]) 777 | GiveWeaponToPed(cache.ped, args[1], ammo == 0 and 200 or ammo, false, false) 778 | end) 779 | 780 | indexes[formattedId] = 1 781 | 782 | menuOptions[index] = {label = weaponCategories[category], args = {'weapon_category', formattedId}} 783 | index += 1 784 | end 785 | end 786 | 787 | for k, v in pairs(weapons) do 788 | local formattedId = ('%s_%s'):format(id, v.weaponType) 789 | if v.name ~= 'WEAPON_UNARMED' and indexes[formattedId] then 790 | lib.setMenuOptions(formattedId, {label = v.displayName, args = {k}, close = false}, indexes[formattedId]) 791 | indexes[formattedId] += 1 792 | end 793 | end 794 | 795 | lib.registerMenu({ 796 | id = 'bMenu_player_weapon_options', 797 | title = 'Weapon Options', 798 | position = MenuPosition, 799 | onClose = function(keyPressed) 800 | CloseMenu(false, keyPressed, 'bMenu_player_related_options') 801 | end, 802 | onSelected = function(selected) 803 | MenuIndexes['bMenu_player_weapon_options'] = selected 804 | end, 805 | onCheck = function(selected, checked, args) 806 | if args[1] == 'unlimited_ammo' then 807 | unlimitedAmmo = checked 808 | lib.setMenuOptions('bMenu_player_weapon_options', {label = 'Unlimited Ammo', args = {'unlimited_ammo'}, checked = checked, close = false}, selected) 809 | local hasWeapon, currentWeapon = GetCurrentPedWeapon(cache.ped, true) 810 | if not hasWeapon then return end 811 | SetPedInfiniteAmmo(cache.ped, unlimitedAmmo, currentWeapon) 812 | elseif args[1] == 'no_reload' then 813 | unlimitedClip = checked 814 | lib.setMenuOptions('bMenu_player_weapon_options', {label = 'No Reload', description = 'Never have to reload your weapon anymore', args = {'no_reload'}, checked = checked, close = false}, selected) 815 | SetPedInfiniteAmmoClip(cache.ped, unlimitedClip) 816 | end 817 | end, 818 | options = menuOptions 819 | }, function(_, _, args) 820 | if args[1] == 'get_all_weapons' then 821 | for k in pairs(weapons) do 822 | local _, ammo = GetMaxAmmo(cache.ped, k) 823 | GiveWeaponToPed(cache.ped, k, ammo == 0 and 200 or ammo, false, false) 824 | end 825 | elseif args[1] == 'remove_all_weapons' then 826 | RemoveAllPedWeapons(cache.ped, false) 827 | elseif args[1] == 'set_all_ammo' then 828 | local dialog = lib.inputDialog('Set All Ammo Count', { 829 | { type = 'number', label = 'Ammo Count', default = 1 } 830 | }) 831 | 832 | if not dialog or not dialog[1] or dialog[1] < 0 then 833 | if dialog[1] < 0 then 834 | lib.notify({ 835 | description = 'The amount of ammo has to be 0 or above', 836 | type = 'error' 837 | }) 838 | end 839 | Wait(200) 840 | lib.showMenu('bMenu_player_weapon_options', MenuIndexes['bMenu_player_weapon_options']) 841 | return 842 | end 843 | 844 | for k in pairs(weapons) do 845 | if HasPedGotWeapon(cache.ped, k, false) then 846 | SetPedAmmo(cache.ped, k, dialog[1]) 847 | end 848 | end 849 | 850 | lib.notify({ 851 | description = ('All your weapons now have %s ammo'):format(dialog[1]), 852 | type = 'success' 853 | }) 854 | 855 | Wait(200) 856 | lib.showMenu('bMenu_player_weapon_options', MenuIndexes['bMenu_player_weapon_options']) 857 | elseif args[1] == 'max_ammo' then 858 | for k in pairs(weapons) do 859 | local _, ammo = GetMaxAmmo(cache.ped, k) 860 | if HasPedGotWeapon(cache.ped, k, false) then 861 | SetPedAmmo(cache.ped, k, ammo == 0 and 200 or ammo) 862 | end 863 | end 864 | elseif args[1] == 'weapon_name' then 865 | local dialog = lib.inputDialog('Spawn Weapon By Name', { 866 | { type = 'input', label = 'Weapon Name', placeholder = 'weapon_unarmed' }, 867 | { type = 'checkbox', label = 'Force In Hand', description = 'Force the weapon to be in your hand when it spawns', checked = false } 868 | }) 869 | 870 | if not dialog or not dialog[1] or dialog[1] == '' then 871 | Wait(200) 872 | lib.showMenu('bMenu_player_weapon_options', MenuIndexes['bMenu_player_weapon_options']) 873 | return 874 | end 875 | 876 | local data = weapons[joaat(dialog[1])] 877 | 878 | if not data then 879 | lib.notify({ 880 | description = ('Weapon %s doesn\'t exist, if this is an existing weapon, add it to the weapons.lua file in the config folder. If this weapon is a weapon from the game, make an issue on github about it and it will be added very soon.'):format(dialog[1]), 881 | type = 'error', 882 | duration = 10000 883 | }) 884 | Wait(200) 885 | lib.showMenu('bMenu_player_weapon_options', MenuIndexes['bMenu_player_weapon_options']) 886 | return 887 | end 888 | 889 | local _, ammo = GetMaxAmmo(cache.ped, data.hash) 890 | GiveWeaponToPed(cache.ped, data.hash, ammo == 0 and 200 or ammo, false, dialog[2]) 891 | 892 | Wait(200) 893 | lib.showMenu('bMenu_player_weapon_options', MenuIndexes['bMenu_player_weapon_options']) 894 | elseif args[1] == 'weapon_category' then 895 | lib.showMenu(args[2], MenuIndexes[args[2]]) 896 | elseif args[1] == 'bMenu_player_related_options' then 897 | lib.showMenu(args[1], MenuIndexes[args[1]]) 898 | end 899 | end) 900 | end 901 | 902 | --#endregion Functions 903 | 904 | --#region Listeners 905 | 906 | lib.onCache('weapon', function(value) 907 | if not value then return end 908 | 909 | SetPedInfiniteAmmo(cache.ped, unlimitedAmmo, value) 910 | SetPedInfiniteAmmoClip(cache.ped, unlimitedClip) 911 | end) 912 | 913 | --#endregion Listeners 914 | 915 | --#region Threads 916 | 917 | CreateThread(function() 918 | local newWeapons = lib.callback.await('bMenu:server:getConfig', false, 'weapons') 919 | 920 | if newWeapons and type(newWeapons) == 'table' then 921 | for k, v in pairs(newWeapons) do 922 | weapons[k] = v 923 | end 924 | end 925 | end) 926 | 927 | --#endregion Threads -------------------------------------------------------------------------------- /client/menus/recording.lua: -------------------------------------------------------------------------------- 1 | --#region Variables 2 | 3 | local quitSession = true 4 | 5 | --#endregion Variables 6 | 7 | --#region Functions 8 | 9 | function CreateRecordingMenu() 10 | local perms = lib.callback.await('bMenu:server:hasConvarPermission', false, 'Recording', {'Start_Stop', 'Editor'}) 11 | local menuOptions = { 12 | {label = 'No access', description = 'You don\'t have access to any options, press enter to return', args = {'return'}} 13 | } 14 | local index = 1 15 | 16 | if perms.Start_Stop then 17 | menuOptions[index] = {label = 'Start Recording', description = 'Start a new game recording using GTA V\'s built in recording', args = {'start'}, close = false} 18 | index += 1 19 | menuOptions[index] = {label = 'Stop Recording', description = 'Stop and save your current recording', args = {'stop'}, close = false} 20 | index += 1 21 | end 22 | 23 | if perms.Editor then 24 | menuOptions[index] = {label = 'Quit Session', description = 'Quit the current session before opening the rockstar editor, this can prevent some issues with the editor', args = {'quit_session'}, checked = quitSession, close = false} 25 | index += 1 26 | menuOptions[index] = {label = 'Rockstar Editor', description = 'Open the rockstar editor', args = {'editor'}} 27 | index += 1 28 | end 29 | 30 | lib.registerMenu({ 31 | id = 'bMenu_recording_options', 32 | title = 'Recording Options', 33 | position = MenuPosition, 34 | onClose = function(keyPressed) 35 | CloseMenu(false, keyPressed, 'bMenu_main') 36 | end, 37 | onSelected = function(selected) 38 | MenuIndexes['bMenu_recording_options'] = selected 39 | end, 40 | onCheck = function(selected, checked, args) 41 | if args[1] == 'quit_session' then 42 | quitSession = checked 43 | lib.setMenuOptions('bMenu_recording_options', {label = 'Quit Session', description = 'Quit the current session before opening the rockstar editor, this can prevent some issues with the editor', args = {'quit_session'}, checked = checked, close = false}, selected) 44 | end 45 | end, 46 | options = menuOptions 47 | }, function(_, _, args) 48 | if args[1] == 'start' then 49 | if IsRecording() then 50 | lib.notify({ 51 | description = 'You\'re already recording', 52 | type = 'error' 53 | }) 54 | return 55 | end 56 | 57 | StartRecording(1) 58 | elseif args[1] == 'stop' then 59 | if not IsRecording() then 60 | lib.notify({ 61 | description = 'You\'re not recording', 62 | type = 'error' 63 | }) 64 | return 65 | end 66 | 67 | StopRecordingAndSaveClip() 68 | elseif args[1] == 'editor' then 69 | CreateThread(function() 70 | if quitSession then 71 | NetworkSessionEnd(true, true) 72 | end 73 | 74 | ActivateRockstarEditor() 75 | 76 | while IsPauseMenuActive() do 77 | Wait(0) 78 | end 79 | 80 | DoScreenFadeIn(1) 81 | 82 | if quitSession then 83 | lib.notify({ 84 | description = 'You have left your previous session before opening the Rockstar Editor. Restart the game to be able to rejoin the server\'s main session', 85 | type = 'inform' 86 | }) 87 | end 88 | end) 89 | elseif args[1] == 'return' then 90 | lib.showMenu('bMenu_main', MenuIndexes['bMenu_main']) 91 | end 92 | end) 93 | end 94 | 95 | --#endregion Functions -------------------------------------------------------------------------------- /client/menus/vehicles/main.lua: -------------------------------------------------------------------------------- 1 | --#region Variables 2 | 3 | Vehicles = {} 4 | VehicleDefaultRadio = 'OFF' 5 | 6 | --#endregion Variables 7 | 8 | --#region Functions 9 | 10 | function IsInVehicle(checkDriver) 11 | if not cache.vehicle then 12 | return false, 'You need to be in a vehicle to perform this action' 13 | end 14 | 15 | if checkDriver and cache.seat ~= -1 then 16 | return false, 'You need to be the driver of the vehicle to perform this action' 17 | end 18 | 19 | return true 20 | end 21 | 22 | function CreateVehicleOptionsMenu() 23 | local perms = lib.callback.await('bMenu:server:hasConvarPermission', false, 'VehicleRelated', {'Options', 'PersonalVehicle', 'Spawner'}) 24 | local menuOptions = { 25 | {label = 'No access', description = 'You don\'t have access to any options, press enter to return', args = {'bMenu_main'}} 26 | } 27 | local index = 1 28 | 29 | if perms.Options then 30 | menuOptions[index] = {label = 'Options', icon = 'wrench', description = 'Common vehicle options including tuning and styling', args = {'bMenu_vehicle_options'}} 31 | index += 1 32 | end 33 | 34 | if perms.PersonalVehicle then 35 | menuOptions[index] = {label = 'Personal Vehicle', icon = 'user-gear', description = 'Control your personal vehicle or change it', args = {'bMenu_vehicle_personal'}} 36 | index += 1 37 | end 38 | 39 | if perms.Spawner then 40 | menuOptions[index] = {label = 'Spawner', icon = 'car', description = 'Spawn any vehicle that is registered in the game, including addon vehicles', args = {'bMenu_vehicle_spawner'}} 41 | index += 1 42 | end 43 | 44 | lib.registerMenu({ 45 | id = 'bMenu_vehicle_related_options', 46 | title = 'Vehicle Related Options', 47 | position = MenuPosition, 48 | onClose = function(keyPressed) 49 | CloseMenu(false, keyPressed, 'bMenu_main') 50 | end, 51 | onSelected = function(selected) 52 | MenuIndexes['bMenu_vehicle_related_options'] = selected 53 | end, 54 | options = menuOptions 55 | }, function(_, _, args) 56 | if args[1] == 'bMenu_vehicle_options' then 57 | SetupVehicleOptionsMenu() 58 | elseif args[1] == 'bMenu_vehicle_personal' then 59 | SetupVehiclePersonalMenu() 60 | elseif args[1] == 'bMenu_vehicle_spawner' then 61 | SetupVehicleSpawnerMenu() 62 | end 63 | 64 | lib.showMenu(args[1], MenuIndexes[args[1]]) 65 | end) 66 | end 67 | 68 | --#endregion Functions -------------------------------------------------------------------------------- /client/menus/vehicles/personal.lua: -------------------------------------------------------------------------------- 1 | --#region Variables 2 | 3 | local currentVehicle = 0 4 | local enableVehicleBlip = false 5 | local vehicleRemoveDoors = false 6 | local vehicleDoors = { 7 | 'Left Front Door', 8 | 'Right Front Door', 9 | 'Left Rear Door', 10 | 'Right Rear Door', 11 | 'Hood', 12 | 'Trunk', 13 | 'Extra Door (#1)', 14 | 'Extra Door (#2)' 15 | } 16 | 17 | --#endregion Variables 18 | 19 | --#region Functions 20 | 21 | local function pressKeyFob() 22 | if not cache.playerId or cache.playerId == 0 or currentVehicle == 0 or IsPlayerDead(cache.playerId) or IsPedInAnyVehicle(cache.ped, false) then return end 23 | 24 | local keyFobHash = `p_car_keys_01` 25 | lib.requestModel(keyFobHash) 26 | 27 | local keyFobObject = CreateObject(keyFobHash, 0, 0, 0, true, true, true) 28 | AttachEntityToEntity(keyFobObject, cache.ped, GetPedBoneIndex(cache.ped, 57005), 0.09, 0.03, -0.02, -76, 13, 28, false, true, true, true, 0, true) 29 | SetModelAsNoLongerNeeded(keyFobHash) 30 | 31 | ClearPedTasks(cache.ped) 32 | SetCurrentPedWeapon(cache.ped, `weapon_unarmed`, true) 33 | 34 | TaskTurnPedToFaceEntity(cache.ped, currentVehicle, 500) 35 | 36 | lib.requestAnimDict('anim@mp_player_intmenu@key_fob@') 37 | TaskPlayAnim(cache.ped, 'anim@mp_player_intmenu@key_fob@', 'fob_click', 3, -8, 1000, 16, 0, false, false, false) 38 | PlaySoundFromEntity(-1, 'Remote_Control_Fob', cache.ped, 'PI_Menu_Sounds', true, 0) 39 | 40 | Wait(1250) 41 | DetachEntity(keyFobObject, false, false) 42 | SetEntityAsMissionEntity(keyFobObject, false, false) 43 | DeleteObject(keyFobObject) 44 | RemoveAnimDict('anim@mp_player_intmenu@key_fob@') 45 | end 46 | 47 | local function createDoorMenu() 48 | local index = 1 49 | local menuOptions = {} 50 | 51 | for i = 0, 7 do 52 | if GetIsDoorValid(curVeh, i) then 53 | menuOptions[index] = {label = vehicleDoors[i + 1], description = ('Open/close the %s'):format(vehicleDoors[i + 1]:lower()), args = {i}, close = false} 54 | index += 1 55 | end 56 | end 57 | 58 | if GetEntityBoneIndexByName(curVeh, 'door_hatch_l') ~= -1 and GetEntityBoneIndexByName(curVeh, 'door_hatch_r') ~= -1 then 59 | menuOptions[index] = {label = 'Bomb Bay', description = 'Open/close the bomb bay', args = {'bomb_bay'}, close = false} 60 | index += 1 61 | end 62 | 63 | menuOptions = {label = 'Open All Doors', args = {'open_all_doors'}, close = false} 64 | index += 1 65 | 66 | menuOptions[index] = {label = 'Close All Doors', args = {'close_all_doors'}, close = false} 67 | index += 1 68 | 69 | menuOptions[index] = {label = 'Remove Doors', description = 'If this is enabled, the doors will be deleted when using the remove door option, otherwise they will be dropped to the ground', args = {'remove_doors'}, checked = vehicleRemoveDoors, close = false} 70 | index += 1 71 | 72 | menuOptions[index] = {label = 'Remove Door', description = 'Remove the specified door from the vehicle, press enter to apply it', args = {'remove_door'}, values = vehicleDoors, defaultIndex = 1, close = false} 73 | index += 1 74 | 75 | lib.registerMenu({ 76 | id = 'bMenu_vehicle_personal_doors', 77 | title = 'Vehicle Doors', 78 | position = MenuPosition, 79 | onClose = function(keyPressed) 80 | CloseMenu(false, keyPressed, 'bMenu_vehicle_personal') 81 | end, 82 | onSelected = function(selected) 83 | MenuIndexes['bMenu_vehicle_personal_doors'] = selected 84 | end, 85 | onCheck = function(_, checked, args) 86 | if args[1] == 'remove_doors' then 87 | vehicleRemoveDoors = checked 88 | end 89 | end, 90 | options = menuOptions 91 | }, function(_, scrollIndex, args) 92 | local curVeh = NetToVeh(currentVehicle) 93 | if type(args[1]) == 'number' then 94 | pressKeyFob() 95 | local isOpen = GetVehicleDoorAngleRatio(curVeh, args) > 0.1 96 | if isOpen then 97 | SetVehicleDoorShut(curVeh, args, false) 98 | else 99 | SetVehicleDoorOpen(curVeh, args, false, false) 100 | end 101 | elseif args[1] == 'bomb_bay' then 102 | pressKeyFob() 103 | if AreBombBayDoorsOpen(curVeh) then 104 | CloseBombBayDoors(curVeh) 105 | else 106 | OpenBombBayDoors(curVeh) 107 | end 108 | elseif args[1] == 'open_all_doors' then 109 | pressKeyFob() 110 | for i = 0, 7 do 111 | SetVehicleDoorOpen(curVeh, i, false, false) 112 | end 113 | if GetEntityBoneIndexByName(curVeh, 'door_hatch_l') ~= -1 and GetEntityBoneIndexByName(curVeh, 'door_hatch_r') ~= -1 then 114 | OpenBombBayDoors(curVeh) 115 | end 116 | elseif args[1] == 'close_all_doors' then 117 | pressKeyFob() 118 | SetVehicleDoorsShut(curVeh, false) 119 | if GetEntityBoneIndexByName(curVeh, 'door_hatch_l') ~= -1 and GetEntityBoneIndexByName(curVeh, 'door_hatch_r') ~= -1 then 120 | CloseBombBayDoors(curVeh) 121 | end 122 | elseif args[1] == 'remove_door' then 123 | pressKeyFob() 124 | SetVehicleDoorBroken(curVeh, scrollIndex - 1, vehicleRemoveDoors) 125 | end 126 | end) 127 | end 128 | 129 | function SetupVehiclePersonalMenu() 130 | local perms = lib.callback.await('bMenu:server:hasConvarPermission', false, {'VehicleRelated', 'PersonalVehicle'}, {'Toggle_Engine', 'Set_Lights', 'Manage_Doors', 'Toggle_Alarm', 'Toggle_Blip', 'Exclusive_Driver'}) 131 | local menuOptions = { 132 | {label = 'Set Vehicle', description = 'Sets your current vehicle as your personal vehicle. If you already have a personal vehicle set then this will override your selection', args = {'set_vehicle'}}, 133 | {label = 'Current Vehicle: None', close = false} 134 | } 135 | local index = 3 136 | 137 | if perms.Toggle_Engine then 138 | menuOptions[index] = {label = 'Toggle Engine', description = 'Toggles the engine on or off, even when you\'re not inside of the vehicle. This does not work if someone else is currently using your vehicle', args = {'toggle_engine'}, close = false} 139 | index += 1 140 | end 141 | 142 | if perms.Set_Lights then 143 | menuOptions[index] = {label = 'Set Vehicle Lights', description = 'This will enable or disable your vehicle headlights, the engine of your vehicle needs to be running for this to work', args = {'set_vehicle_lights'}, values = {'Force On', 'Force Off', 'Reset'}, defaultIndex = 1, close = false} 144 | index += 1 145 | end 146 | 147 | if perms.Manage_Doors then 148 | menuOptions[index] = {label = 'Lock Vehicle Doors', description = 'This will lock all your vehicle doors for all players. Anyone already inside will always be able to leave the vehicle, even if the doors are locked', args = {'lock_doors'}, close = false} 149 | index += 1 150 | 151 | menuOptions[index] = {label = 'Unlock Vehicle Doors', description = 'This will unlock all your vehicle doors for all players', args = {'unlock_doors'}, close = false} 152 | index += 1 153 | 154 | menuOptions[index] = {label = 'Vehicle Doors', description = 'Open, close, remove and restore vehicle doors here', args = {'bMenu_vehicle_personal_doors'}, close = false} 155 | index += 1 156 | end 157 | 158 | if perms.Toggle_Alarm then 159 | menuOptions[index] = {label = 'Toggle Alarm Sound', description = 'Toggles the vehicle alarm sound on or off. This does not set an alarm. It only toggles the current sounding status of the alarm', args = {'alarm_sound'}, close = false} 160 | index += 1 161 | end 162 | 163 | if perms.Toggle_Blip then 164 | menuOptions[index] = {label = 'Add Blip', description = 'Enables or disables the blip that gets added when you mark a vehicle as your personal vehicle', args = {'add_blip'}, checked = enableVehicleBlip, close = false} 165 | index += 1 166 | end 167 | 168 | if perms.Exclusive_Driver then 169 | menuOptions[index] = {label = 'Exclusive Driver', description = 'If enabled, then you will be the only one that can enter the drivers seat. Other players will not be able to drive the car. They can still be passengers', args = {'exclusive_driver'}, checked = false, close = false} 170 | index += 1 171 | end 172 | 173 | lib.registerMenu({ 174 | id = 'bMenu_vehicle_personal', 175 | title = 'Personal Vehicle', 176 | position = MenuPosition, 177 | onClose = function(keyPressed) 178 | CloseMenu(false, keyPressed, 'bMenu_vehicle_related_options') 179 | end, 180 | onSelected = function(selected) 181 | MenuIndexes['bMenu_vehicle_personal'] = selected 182 | end, 183 | onCheck = function(selected, checked, args) 184 | local curVeh = NetToVeh(currentVehicle) 185 | 186 | if not NetworkHasControlOfEntity(curVeh) then 187 | if not NetworkRequestControlOfEntity(curVeh) then 188 | lib.notify({ 189 | description = 'You currently can\'t control this vehicle. Is someone else currently driving your car? Please try again after making sure other players are not controlling your vehicle', 190 | type = 'error' 191 | }) 192 | return 193 | end 194 | end 195 | 196 | if args[1] == 'add_blip' then 197 | enableVehicleBlip = checked 198 | lib.setMenuOptions('bMenu_vehicle_personal', {label = 'Add Blip', description = 'Enables or disables the blip that gets added when you mark a vehicle as your personal vehicle', args = {'add_blip'}, checked = checked, close = false}, selected) 199 | if enableVehicleBlip then 200 | local entityState = Entity(curVeh) 201 | if not entityState.state.bMenu_blip or not DoesBlipExist(entityState.state.bMenu_blip) then 202 | local blip = AddBlipForEntity(curVeh) 203 | SetBlipSprite(blip, 225) 204 | BeginTextCommandSetBlipName('STRING') 205 | AddTextComponentSubstringPlayerName('Personal Vehicle') 206 | EndTextCommandSetBlipName(blip) 207 | entityState.state:set('bMenu_blip', blip) 208 | end 209 | else 210 | local entityState = Entity(curVeh) 211 | if entityState.state.bMenu_blip and DoesBlipExist(entityState.state.bMenu_blip) then 212 | RemoveBlip(entityState.state.bMenu_blip) 213 | end 214 | entityState.state:set('bMenu_blip', nil) 215 | end 216 | elseif args[1] == 'exclusive_driver' then 217 | SetVehicleExclusiveDriver(curVeh, checked) 218 | SetVehicleExclusiveDriver_2(curVeh, checked and cache.ped or 0, 1) 219 | lib.setMenuOptions('bMenu_vehicle_personal', {label = 'Exclusive Driver', description = 'If enabled, then you will be the only one that can enter the drivers seat. Other players will not be able to drive the car. They can still be passengers', args = {'exclusive_driver'}, checked = checked, close = false}, selected) 220 | end 221 | end, 222 | onSideScroll = function(selected, scrollIndex, args) 223 | local curVeh = NetToVeh(currentVehicle) 224 | 225 | if not NetworkHasControlOfEntity(curVeh) then 226 | if not NetworkRequestControlOfEntity(curVeh) then 227 | lib.notify({ 228 | description = 'You currently can\'t control this vehicle. Is someone else currently driving your car? Please try again after making sure other players are not controlling your vehicle', 229 | type = 'error' 230 | }) 231 | return 232 | end 233 | end 234 | 235 | if args[1] == 'set_vehicle_lights' then 236 | lib.setMenuOptions('bMenu_vehicle_personal', {label = 'Set Vehicle Lights', description = 'This will enable or disable your vehicle headlights, the engine of your vehicle needs to be running for this to work', args = {'set_vehicle_lights'}, values = {'Force On', 'Force Off', 'Reset'}, defaultIndex = scrollIndex, close = false}, selected) 237 | end 238 | end, 239 | options = menuOptions 240 | }, function(selected, scrollIndex, args) 241 | if not args or not args[1] then return end 242 | 243 | if args[1] == 'set_vehicle' then 244 | local inVeh, reason = IsInVehicle(true) 245 | if not inVeh then 246 | lib.notify({ 247 | description = reason, 248 | type = 'error' 249 | }) 250 | else 251 | local curVeh = NetToVeh(currentVehicle) 252 | if DoesEntityExist(curVeh) then 253 | local entityState = Entity(curVeh) 254 | if entityState.state.bMenu_blip and DoesBlipExist(entityState.state.bMenu_blip) then 255 | RemoveBlip(entityState.state.bMenu_blip) 256 | end 257 | entityState.state:set('bMenu_blip', nil) 258 | end 259 | curVeh = GetVehiclePedIsIn(cache.ped, false) 260 | SetVehicleHasBeenOwnedByPlayer(curVeh, true) 261 | SetEntityAsMissionEntity(curVeh, true, false) 262 | if enableVehicleBlip then 263 | local entityState = Entity(curVeh) 264 | if not entityState.state.bMenu_blip or not DoesBlipExist(entityState.state.bMenu_blip) then 265 | local blip = AddBlipForEntity(curVeh) 266 | SetBlipSprite(blip, 225) 267 | BeginTextCommandSetBlipName('STRING') 268 | AddTextComponentSubstringPlayerName('Personal Vehicle') 269 | EndTextCommandSetBlipName(blip) 270 | entityState.state:set('bMenu_blip', blip) 271 | end 272 | end 273 | local name = GetDisplayNameFromVehicleModel(GetEntityModel(curVeh)) 274 | local labelText = GetLabelText(name) 275 | local vehicleName = labelText and labelText ~= '' and labelText ~= 'NULL' and ToProperCase(labelText) or ToProperCase(name) 276 | lib.setMenuOptions('bMenu_vehicle_personal', {label = ('Current Vehicle: %s'):format(vehicleName)}, selected) 277 | currentVehicle = VehToNet(curVeh) 278 | end 279 | lib.showMenu('bMenu_vehicle_personal', MenuIndexes['bMenu_vehicle_personal']) 280 | elseif DoesEntityExist(NetToVeh(currentVehicle)) then 281 | if args[1] == 'kick_passengers' then 282 | local curVeh = NetToVeh(currentVehicle) 283 | if IsPedInVehicle(cache.ped, curVeh, false) or (GetVehicleNumberOfPassengers(curVeh) == 0 and not IsVehicleSeatFree(curVeh, -1)) then 284 | lib.notify({ 285 | description = 'No one to kick out of the vehicle', 286 | type = 'inform' 287 | }) 288 | return 289 | end 290 | 291 | TaskEveryoneLeaveVehicle(curVeh) 292 | else 293 | local curVeh = NetToVeh(currentVehicle) 294 | 295 | if not NetworkHasControlOfEntity(curVeh) then 296 | if not NetworkRequestControlOfEntity(curVeh) then 297 | lib.notify({ 298 | description = 'You currently can\'t control this vehicle. Is someone else currently driving your car? Please try again after making sure other players are not controlling your vehicle', 299 | type = 'error' 300 | }) 301 | return 302 | end 303 | end 304 | 305 | if args[1] == 'toggle_engine' then 306 | pressKeyFob() 307 | SetVehicleEngineOn(curVeh, not GetIsVehicleEngineRunning(curVeh), true, true) 308 | elseif args[1] == 'set_vehicle_lights' then 309 | pressKeyFob() 310 | if scrollIndex == 1 then 311 | SetVehicleLights(curVeh, 3) 312 | elseif scrollIndex == 2 then 313 | SetVehicleLights(curVeh, 1) 314 | else 315 | SetVehicleLights(curVeh, 0) 316 | end 317 | elseif args[1] == 'unlock_doors' or args[1] == 'lock_doors' then 318 | local lock = args[1] == 'lock_doors' 319 | pressKeyFob() 320 | for _ = 1, 2 do 321 | local timer = GetGameTimer() 322 | while GetGameTimer() - timer < 50 do 323 | SoundVehicleHornThisFrame(curVeh) 324 | Wait(0) 325 | end 326 | 327 | Wait(50) 328 | end 329 | 330 | lib.notify({ 331 | description = ('Vehicle doors are now %s'):format(lock and 'locked' or 'unlocked'), 332 | type = 'inform' 333 | }) 334 | SetVehicleDoorsLockedForAllPlayers(curVeh, lock) 335 | elseif args[1] == 'alarm_sound' then 336 | pressKeyFob() 337 | if IsVehicleAlarmActivated(curVeh) then 338 | SetVehicleAlarmTimeLeft(curVeh, 0) 339 | SetVehicleAlarm(curVeh, false) 340 | else 341 | SetVehicleAlarm(curVeh, true) 342 | SetVehicleAlarmTimeLeft(curVeh, math.random(8000, 45000)) 343 | StartVehicleAlarm(curVeh) 344 | end 345 | elseif args[1] == 'bMenu_vehicle_personal_doors' then 346 | lib.hideMenu(false) 347 | createDoorMenu() 348 | lib.showMenu(args[1], MenuIndexes[args[1]]) 349 | end 350 | end 351 | else 352 | lib.notify({ 353 | description = 'You have not selected a current vehicle or it doesn\'t exist anymore', 354 | type = 'error' 355 | }) 356 | end 357 | end) 358 | end 359 | 360 | --#endregion Functions -------------------------------------------------------------------------------- /client/menus/vehicles/spawner.lua: -------------------------------------------------------------------------------- 1 | --#region Variables 2 | 3 | local vehicleClassNames = { 4 | [0] = GetLabelText('VEH_CLASS_0'), -- Compacts 5 | [1] = GetLabelText('VEH_CLASS_1'), -- Sedans 6 | [2] = GetLabelText('VEH_CLASS_2'), -- SUVs 7 | [3] = GetLabelText('VEH_CLASS_3'), -- Coupes 8 | [4] = GetLabelText('VEH_CLASS_4'), -- Muscle 9 | [5] = GetLabelText('VEH_CLASS_5'), -- Sports Classics 10 | [6] = GetLabelText('VEH_CLASS_6'), -- Sports 11 | [7] = GetLabelText('VEH_CLASS_7'), -- Super 12 | [8] = GetLabelText('VEH_CLASS_8'), -- Motorcycles 13 | [9] = GetLabelText('VEH_CLASS_9'), -- Offroad 14 | [10] = GetLabelText('VEH_CLASS_10'), -- Industrial 15 | [11] = GetLabelText('VEH_CLASS_11'), -- Utility 16 | [12] = GetLabelText('VEH_CLASS_12'), -- Vans 17 | [13] = GetLabelText('VEH_CLASS_13'), -- Cycles 18 | [14] = GetLabelText('VEH_CLASS_14'), -- Boats 19 | [15] = GetLabelText('VEH_CLASS_15'), -- Helicopters 20 | [16] = GetLabelText('VEH_CLASS_16'), -- Planes 21 | [17] = GetLabelText('VEH_CLASS_17'), -- Service 22 | [18] = GetLabelText('VEH_CLASS_18'), -- Emergency 23 | [19] = GetLabelText('VEH_CLASS_19'), -- Military 24 | [20] = GetLabelText('VEH_CLASS_20'), -- Commercial 25 | [21] = GetLabelText('VEH_CLASS_21'), -- Trains 26 | [22] = GetLabelText('VEH_CLASS_22'), -- Open Wheel 27 | [69] = 'Misc' -- For any other vehicles that haven't got a class set for some reason. 28 | } 29 | 30 | local spawnInVehicle = true -- Teleport into the vehicle you're spawning 31 | local replacePreviousVehicle = true -- Replace the previous vehicle you were in when spawning a new vehicle 32 | 33 | --#endregion Variables 34 | 35 | --#region Functions 36 | 37 | local function spawnVehicleOnPlayer(model) 38 | if not IsModelAVehicle(model) then return end 39 | 40 | local speed = 0.0 41 | local rpm = 0.0 42 | 43 | if IsPedInAnyVehicle(cache.ped, false) and spawnInVehicle then 44 | local oldVeh = GetVehiclePedIsIn(cache.ped, false) 45 | speed = GetEntitySpeedVector(oldVeh, true).y 46 | rpm = GetVehicleCurrentRpm(oldVeh) 47 | end 48 | 49 | lib.requestModel(model) 50 | 51 | local otherCoords = spawnInVehicle and GetEntityCoords(cache.ped, true) or GetOffsetFromEntityInWorldCoords(cache.ped, 0.0, 8.0, 0.0) 52 | local coords = vec4(otherCoords.x, otherCoords.y, otherCoords.z + 1, GetEntityHeading(cache.ped) + (spawnInVehicle and 0 or 90)) 53 | local previousVehicle = GetVehiclePedIsIn(cache.ped, true) 54 | 55 | if previousVehicle ~= 0 then 56 | if IsVehiclePreviouslyOwnedByPlayer(previousVehicle) and ((GetVehicleNumberOfPassengers(previousVehicle) == 0 and IsVehicleSeatFree(previousVehicle, -1)) or cache.seat == -1) then 57 | if replacePreviousVehicle then 58 | SetVehicleHasBeenOwnedByPlayer(previousVehicle, false) 59 | SetEntityAsMissionEntity(previousVehicle, false, true) 60 | DeleteEntity(previousVehicle) 61 | else 62 | SetEntityAsMissionEntity(previousVehicle, false, false) 63 | end 64 | previousVehicle = 0 65 | end 66 | end 67 | 68 | if IsPedInAnyVehicle(cache.ped, false) and replacePreviousVehicle then 69 | local tempVeh = GetVehiclePedIsIn(cache.ped, false) 70 | SetVehicleHasBeenOwnedByPlayer(tempVeh, false) 71 | SetEntityAsMissionEntity(cache.ped, true, true) 72 | 73 | if previousVehicle ~= 0 and previousVehicle == tempVeh then 74 | previousVehicle = 0 75 | end 76 | 77 | SetEntityAsMissionEntity(tempVeh, false, true) 78 | DeleteEntity(tempVeh) 79 | tempVeh = 0 80 | end 81 | 82 | if previousVehicle ~= 0 then 83 | SetVehicleHasBeenOwnedByPlayer(previousVehicle, false) 84 | end 85 | 86 | if IsPedInAnyVehicle(cache.ped, false) then 87 | local offset = GetOffsetFromEntityInWorldCoords(cache.vehicle, 0, 10 + GetEntitySpeed(cache.vehicle) / 2, 0) 88 | coords = vec4(offset.x, offset.y, offset.z, coords.w) 89 | end 90 | 91 | local netVeh = lib.callback.await('bMenu:server:spawnVehicle', false, model, coords) 92 | while netVeh == 0 or not DoesEntityExist(NetToVeh(netVeh)) do 93 | Wait(0) 94 | end 95 | 96 | local vehicle = NetToVeh(netVeh) 97 | if vehicle == 0 then 98 | lib.notify({ 99 | description = 'Something went wrong spawning the vehicle', 100 | type = 'error' 101 | }) 102 | return 103 | end 104 | 105 | SetVehicleNeedsToBeHotwired(vehicle, false) 106 | SetVehicleHasBeenOwnedByPlayer(vehicle, true) 107 | SetEntityAsMissionEntity(vehicle, true, false) 108 | SetVehicleIsStolen(vehicle, false) 109 | SetVehicleIsWanted(vehicle, false) 110 | 111 | if spawnInVehicle then 112 | SetVehicleEngineOn(vehicle, true, true, true) 113 | 114 | SetPedIntoVehicle(cache.ped, vehicle, -1) 115 | 116 | if GetVehicleClass(vehicle) == 15 and GetEntityHeightAboveGround(vehicle) > 10 then 117 | SetHeliBladesFullSpeed(vehicle) 118 | else 119 | SetVehicleOnGroundProperly(vehicle) 120 | end 121 | end 122 | 123 | if not IsThisModelATrain(model) then -- I don't know why, but it's to be careful, so never remove this 124 | SetVehicleForwardSpeed(vehicle, speed) 125 | end 126 | 127 | SetVehicleCurrentRpm(vehicle, rpm) 128 | 129 | SetVehicleRadioEnabled(vehicle, true) 130 | Wait(10) -- SetVehicleRadioEnabled sets the radio to a random one or the previous one, so we have to wait before overriding it 131 | SetVehRadioStation(vehicle, VehicleDefaultRadio) 132 | 133 | SetModelAsNoLongerNeeded(model) 134 | end 135 | 136 | local function getVehiclesFromClassName(className) 137 | local result = {} 138 | for _, v in pairs(Vehicles) do 139 | if v.className == className then 140 | result[#result+1] = v 141 | end 142 | end 143 | return result 144 | end 145 | 146 | local function createVehiclesForSpawner(vehs, id) 147 | table.sort(vehs, function(a, b) 148 | return a.name < b.name 149 | end) 150 | 151 | for i = 1, #vehs do 152 | local data = vehs[i] 153 | local displayName = GetDisplayNameFromVehicleModel(data.model) 154 | local label = GetLabelText(displayName) 155 | label = label ~= 'NULL' and label or displayName 156 | label = label ~= 'CARNOTFOUND' and label or data.modelName 157 | lib.setMenuOptions(id, {label = ToProperCase(label:lower()), args = {data.model}, close = false}, i) 158 | end 159 | end 160 | 161 | function SetupVehicleSpawnerMenu() 162 | local id = 'bMenu_vehicle_spawner' 163 | local sorted = {} 164 | local perms = lib.callback.await('bMenu:server:hasConvarPermission', false, {'VehicleRelated', 'Spawner'}, {'Spawn_By_Name', 'Spawn_In_Vehicle', 'Replace_Previous', 'Spawn_By_Category'}) 165 | local menuOptions = { 166 | {label = 'No access', description = 'You don\'t have access to any options, press enter to return', args = {'bMenu_vehicle_related_options'}} 167 | } 168 | local index = 1 169 | 170 | if perms.Spawn_By_Name then 171 | menuOptions[index] = {label = 'Spawn Vehicle By Model Name', description = 'Enter the name of the vehicle you want to spawn', args = {'spawn_by_model'}} 172 | index += 1 173 | end 174 | 175 | if perms.Spawn_In_Vehicle then 176 | menuOptions[index] = {label = 'Spawn Inside Vehicle', description = 'This will teleport you into the vehicle when it spawns', args = {'inside_vehicle'}, checked = spawnInVehicle, close = false} 177 | index += 1 178 | end 179 | 180 | if perms.Replace_Previous then 181 | menuOptions[index] = {label = 'Replace Previous Vehicle', description = 'This will delete the vehicle you were previously in when spawning a new vehicle', args = {'replace_vehicle'}, checked = replacePreviousVehicle, close = false} 182 | index += 1 183 | end 184 | 185 | if perms.Spawn_By_Category then 186 | for _, v in pairs(vehicleClassNames) do 187 | sorted[#sorted+1] = v 188 | end 189 | 190 | table.sort(sorted, function(a, b) 191 | return a < b 192 | end) 193 | 194 | for i2 = 1, #sorted do 195 | local v = sorted[i2] 196 | local formattedId = ('%s_%s'):format(id, v) 197 | local vehs = getVehiclesFromClassName(v) 198 | 199 | if table.type(vehs) == 'empty' then goto skipLoop end 200 | 201 | lib.registerMenu({ 202 | id = formattedId, 203 | title = v, 204 | position = MenuPosition, 205 | onClose = function(keyPressed) 206 | CloseMenu(false, keyPressed, id) 207 | end, 208 | onSelected = function(selected) 209 | MenuIndexes[formattedId] = selected 210 | end, 211 | options = {} 212 | }, function(_, _, args) 213 | spawnVehicleOnPlayer(args[1]) 214 | end) 215 | 216 | menuOptions[index] = {label = v, args = {formattedId}} 217 | createVehiclesForSpawner(vehs, formattedId) 218 | 219 | index += 1 220 | 221 | :: skipLoop :: 222 | end 223 | end 224 | 225 | lib.registerMenu({ 226 | id = id, 227 | title = 'Vehicle Spawner', 228 | position = MenuPosition, 229 | onClose = function(keyPressed) 230 | CloseMenu(false, keyPressed, 'bMenu_vehicle_related_options') 231 | end, 232 | onSelected = function(selected) 233 | MenuIndexes[id] = selected 234 | end, 235 | onCheck = function(selected, checked, args) 236 | if args[1] == 'inside_vehicle' then 237 | spawnInVehicle = checked 238 | lib.setMenuOptions(id, {label = 'Spawn Inside Vehicle', description = 'This will teleport you into the vehicle when it spawns', args = {'inside_vehicle'}, checked = checked, close = false}, selected) 239 | elseif args[1] == 'replace_vehicle' then 240 | replacePreviousVehicle = checked 241 | lib.setMenuOptions(id, {label = 'Replace Previous Vehicle', description = 'This will delete the vehicle you were previously in when spawning a new vehicle', args = {'replace_vehicle'}, checked = checked, close = false}, selected) 242 | end 243 | end, 244 | options = menuOptions 245 | }, function(_, _, args) 246 | if args[1] == 'spawn_by_model' then 247 | local vehicle = lib.inputDialog('Spawn Vehicle', {'Vehicle Model Name'}) 248 | if vehicle and table.type(vehicle) ~= 'empty' then 249 | local model = joaat(vehicle[1]) 250 | if IsModelInCdimage(model) then 251 | spawnVehicleOnPlayer(model) 252 | else 253 | lib.notify({ 254 | description = ('Vehicle model %s doesn\'t exist in the game'):format(vehicle[1]), 255 | type = 'error' 256 | }) 257 | end 258 | end 259 | Wait(200) 260 | lib.showMenu(id, MenuIndexes[id]) 261 | elseif args[1] ~= 'inside_vehicle' and args[1] ~= 'replace_vehicle' then 262 | lib.showMenu(args[1], MenuIndexes[args[1]]) 263 | end 264 | end) 265 | end 266 | 267 | --#endregion Functions 268 | 269 | --#region Threads 270 | 271 | CreateThread(function() 272 | local vehicleModels = GetAllVehicleModels() 273 | for i = 1, #vehicleModels do 274 | local modelName = vehicleModels[i] 275 | local model = joaat(modelName) 276 | local vehicleClass = GetVehicleClassFromName(model) 277 | local vehicleClassName = vehicleClassNames[vehicleClass] or vehicleClassNames[69] 278 | Vehicles[model] = { 279 | name = modelName, 280 | model = model, 281 | class = vehicleClassName ~= vehicleClassNames[69] and vehicleClass or 69, 282 | className = vehicleClassName 283 | } 284 | end 285 | end) 286 | 287 | --#endregion Threads -------------------------------------------------------------------------------- /client/menus/world.lua: -------------------------------------------------------------------------------- 1 | --#region Variables 2 | 3 | local weatherIndexes = { 4 | EXTRASUNNY = {4, 'Extra Sunny'}, 5 | CLEAR = {5, 'Clear'}, 6 | NEUTRAL = {6, 'Neutral'}, 7 | SMOG = {7, 'Smog'}, 8 | FOGGY = {8, 'Foggy'}, 9 | CLOUDS = {9, 'Cloudy'}, 10 | OVERCAST = {10, 'Overcast'}, 11 | CLEARING = {11, 'Clearing'}, 12 | RAIN = {12, 'Rainy'}, 13 | THUNDER = {13, 'Thunder'}, 14 | BLIZZARD = {14, 'Blizzard'}, 15 | SNOW = {15, 'Snow'}, 16 | SNOWLIGHT = {16, 'Light Snow'}, 17 | XMAS = {17, 'X-MAS Snow'}, 18 | HALLOWEEN = {18, 'Halloween'} 19 | } 20 | 21 | local timeSyncedWithMachine = GetConvar('bMenu.Sync_Time_To_Machine_Time', 'false') == 'true' 22 | local timeFrozen = GetConvar('bMenu.Freeze_Time', 'false') == 'true' 23 | local currentHour = tonumber(GetConvar('bMenu.Current_Hour', '7')) --[[@as number]] 24 | local currentMinute = tonumber(GetConvar('bMenu.Current_Minute', '0')) --[[@as number]] 25 | currentHour = currentHour < 0 and 0 or currentHour > 23 and 23 or currentHour 26 | currentMinute = currentMinute < 0 and 0 or currentMinute > 23 and 23 or currentMinute 27 | local showTimeOnScreen = false 28 | local dynamicWeather = GetConvar('bMenu.Dynamic_Weather', 'true') == 'true' 29 | local blackout = GetConvar('bMenu.Enable_Blackout', 'false') == 'true' 30 | local snowEffects = GetConvar('bMenu.Enable_Snow_Effects', 'false') == 'true' 31 | local checkedDynamicWeather = dynamicWeather 32 | local checkedBlackout = blackout 33 | local checkedSnowEffects = snowEffects 34 | local currentWeather = GetConvar('bMenu.Current_Weather', 'EXTRASUNNY'):upper() 35 | currentWeather = not weatherIndexes[currentWeather] and 'EXTRASUNNY' or currentWeather 36 | local currentChecked = 'EXTRASUNNY' -- Leave this so the checkmark can move itself accordingly in the loop 37 | local weatherChangeTime = tonumber(GetConvar('bMenu.Weather_Change_Time', '5')) --[[@as number]] 38 | weatherChangeTime = weatherChangeTime < 0 and 0 or weatherChangeTime 39 | local changingWeather = false 40 | 41 | --#endregion Variables 42 | 43 | --#region Functions 44 | 45 | local function createTimeOptions() 46 | local perms = lib.callback.await('bMenu:server:hasConvarPermission', false, {'WorldRelated', 'TimeOptions'}, {'Freeze_Unfreeze_Time', 'Sync_Time', 'Show_Time', 'Presets', 'Set_Hour', 'Set_Minute'}) 47 | local menuOptions = { 48 | {label = 'No access', description = 'You don\'t have access to any options, press enter to return', args = {'bMenu_world_related_options'}} 49 | } 50 | local index = 1 51 | 52 | if perms.Freeze_Unfreeze_Time then 53 | menuOptions[index] = {label = 'Freeze/Unfreeze Time', args = {'freeze_time'}, close = false} 54 | index += 1 55 | end 56 | 57 | if perms.Sync_Time then 58 | menuOptions[index] = {label = 'Sync Time To Server', args = {'sync_to_server'}, close = false} 59 | index += 1 60 | end 61 | 62 | if perms.Show_Time then 63 | menuOptions[index] = {label = 'Show Time On Screen', args = {'show_time'}, checked = showTimeOnScreen, close = false} 64 | index += 1 65 | end 66 | 67 | if perms.Presets then 68 | menuOptions[index] = {label = 'Early Morning (06:00, 6 AM)', args = {'set_time_preset', 6}, close = false} 69 | index += 1 70 | 71 | menuOptions[index] = {label = 'Morning (09:00, 9 AM)', args = {'set_time_preset', 9}, close = false} 72 | index += 1 73 | 74 | menuOptions[index] = {label = 'Noon (12:00, 12 PM)', args = {'set_time_preset', 12}, close = false} 75 | index += 1 76 | 77 | menuOptions[index] = {label = 'Early Afternoon (15:00, 3 PM)', args = {'set_time_preset', 15}, close = false} 78 | index += 1 79 | 80 | menuOptions[index] = {label = 'Afternoon (18:00, 6 PM)', args = {'set_time_preset', 18}, close = false} 81 | index += 1 82 | 83 | menuOptions[index] = {label = 'Evening (21:00, 9 PM)', args = {'set_time_preset', 21}, close = false} 84 | index += 1 85 | 86 | menuOptions[index] = {label = 'Midnight (00:00, 12 AM)', args = {'set_time_preset', 0}, close = false} 87 | index += 1 88 | 89 | menuOptions[index] = {label = 'Night (03:00, 3 AM)', args = {'set_time_preset', 3}, close = false} 90 | index += 1 91 | end 92 | 93 | if perms.Set_Hour then 94 | menuOptions[index] = {label = 'Set Custom Hour', args = {'set_time_custom', 'hours'}, values = {'00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'}, defaultIndex = 1, close = false} 95 | index += 1 96 | end 97 | 98 | if perms.Set_Minute then 99 | menuOptions[index] = {label = 'Set Custom Minute', args = {'set_time_custom', 'minutes'}, values = {'00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59'}, defaultIndex = 1, close = false} 100 | index += 1 101 | end 102 | 103 | lib.registerMenu({ 104 | id = 'bMenu_time_options', 105 | title = 'Time Options', 106 | position = MenuPosition, 107 | onClose = function(keyPressed) 108 | CloseMenu(false, keyPressed, 'bMenu_world_related_options') 109 | end, 110 | onSelected = function(selected) 111 | MenuIndexes['bMenu_time_options'] = selected 112 | end, 113 | onCheck = function(selected, checked, args) 114 | if args[1] == 'show_time' then 115 | showTimeOnScreen = checked 116 | lib.setMenuOptions('bMenu_time_options', {label = 'Show Time On Screen', args = {'show_time'}, checked = showTimeOnScreen, close = false}, selected) 117 | end 118 | end, 119 | options = menuOptions 120 | }, function(_, scrollIndex, args) 121 | if args[1] == 'bMenu_world_related_options' then 122 | lib.showMenu(args[1], MenuIndexes[args[1]]) 123 | return 124 | end 125 | 126 | timeSyncedWithMachine = GetConvar('bMenu.Sync_Time_To_Machine_Time', 'false') == 'true' 127 | 128 | if args[1] == 'sync_to_server' then 129 | TriggerServerEvent('bMenu:server:updateTime', currentHour, currentMinute, timeFrozen, not timeSyncedWithMachine) 130 | end 131 | 132 | if timeSyncedWithMachine and args[1] ~= 'sync_to_server' then 133 | lib.notify({ 134 | description = 'Can\'t change the time when the time is synced to the server', 135 | type = 'error' 136 | }) 137 | return 138 | end 139 | 140 | timeFrozen = GetConvar('bMenu.Freeze_Time', 'false') == 'true' 141 | if args[1] == 'freeze_time' then 142 | TriggerServerEvent('bMenu:server:updateTime', currentHour, currentMinute, not timeFrozen, timeSyncedWithMachine) 143 | elseif args[1] == 'set_time_preset' then 144 | TriggerServerEvent('bMenu:server:updateTime', args[2], 0, timeFrozen, timeSyncedWithMachine) 145 | elseif args[1] == 'set_time_custom' then 146 | if args[2] == 'hours' then 147 | local hour = scrollIndex - 1 148 | TriggerServerEvent('bMenu:server:updateTime', hour, currentMinute, timeFrozen, timeSyncedWithMachine) 149 | elseif args[2] == 'minutes' then 150 | local minute = scrollIndex - 1 151 | TriggerServerEvent('bMenu:server:updateTime', currentHour, minute, timeFrozen, timeSyncedWithMachine) 152 | end 153 | end 154 | end) 155 | end 156 | 157 | local function createWeatherOptions() 158 | local perms = lib.callback.await('bMenu:server:hasConvarPermission', false, {'WorldRelated', 'WeatherOptions'}, {'Toggle_Dynamic_Weather', 'Toggle_Blackout', 'Toggle_Snow_Effects', 'Change_Weather_Type', 'Set_Clouds'}) 159 | local menuOptions = { 160 | {label = 'No access', description = 'You don\'t have access to any options, press enter to return', args = {'bMenu_world_related_options'}} 161 | } 162 | local index = 1 163 | 164 | if perms.Toggle_Dynamic_Weather then 165 | menuOptions[index] = {label = 'Dynamic Weather', description = 'Whether to randomize the state of the weather or not', args = {'dynamic_weather'}, checked = checkedDynamicWeather, close = false} 166 | index += 1 167 | end 168 | 169 | if perms.Toggle_Blackout then 170 | menuOptions[index] = {label = 'Blackout', description = 'If turned on, disables all light sources', args = {'blackout'}, checked = checkedBlackout, close = false} 171 | index += 1 172 | end 173 | 174 | if perms.Toggle_Snow_Effects then 175 | menuOptions[index] = {label = 'Snow Effects', description = 'This will force snow to appear on the ground and enable snow particles for peds and vehicles. Combine with X-MAS or Light Snow for the best results', args = {'snow_effects'}, checked = checkedSnowEffects, close = false} 176 | index += 1 177 | end 178 | 179 | if perms.Change_Weather_Type then 180 | menuOptions[index] = {label = 'Extra Sunny', icon = currentWeather == 'EXTRASUNNY' and 'circle-check', args = {'set_weather', 'EXTRASUNNY'}, close = false} 181 | index += 1 182 | 183 | menuOptions[index] = {label = 'Clear', icon = currentWeather == 'CLEAR' and 'circle-check', args = {'set_weather', 'CLEAR'}, close = false} 184 | index += 1 185 | 186 | menuOptions[index] = {label = 'Neutral', icon = currentWeather == 'NEUTRAL' and 'circle-check', args = {'set_weather', 'NEUTRAL'}, close = false} 187 | index += 1 188 | 189 | menuOptions[index] = {label = 'Smog', icon = currentWeather == 'SMOG' and 'circle-check', args = {'set_weather', 'SMOG'}, close = false} 190 | index += 1 191 | 192 | menuOptions[index] = {label = 'Foggy', icon = currentWeather == 'FOGGY' and 'circle-check', args = {'set_weather', 'FOGGY'}, close = false} 193 | index += 1 194 | 195 | menuOptions[index] = {label = 'Cloudy', icon = currentWeather == 'CLOUDS' and 'circle-check', args = {'set_weather', 'CLOUDS'}, close = false} 196 | index += 1 197 | 198 | menuOptions[index] = {label = 'Overcast', icon = currentWeather == 'OVERCAST' and 'circle-check', args = {'set_weather', 'OVERCAST'}, close = false} 199 | index += 1 200 | 201 | menuOptions[index] = {label = 'Clearing', icon = currentWeather == 'CLEARING' and 'circle-check', args = {'set_weather', 'CLEARING'}, close = false} 202 | index += 1 203 | 204 | menuOptions[index] = {label = 'Rainy', icon = currentWeather == 'RAIN' and 'circle-check', args = {'set_weather', 'RAIN'}, close = false} 205 | index += 1 206 | 207 | menuOptions[index] = {label = 'Thunder', icon = currentWeather == 'THUNDER' and 'circle-check', args = {'set_weather', 'THUNDER'}, close = false} 208 | index += 1 209 | 210 | menuOptions[index] = {label = 'Blizzard', icon = currentWeather == 'BLIZZARD' and 'circle-check', args = {'set_weather', 'BLIZZARD'}, close = false} 211 | index += 1 212 | 213 | menuOptions[index] = {label = 'Snow', icon = currentWeather == 'SNOW' and 'circle-check', args = {'set_weather', 'SNOW'}, close = false} 214 | index += 1 215 | 216 | menuOptions[index] = {label = 'Light Snow', icon = currentWeather == 'SNOWLIGHT' and 'circle-check', args = {'set_weather', 'SNOWLIGHT'}, close = false} 217 | index += 1 218 | 219 | menuOptions[index] = {label = 'X-MAS Snow', icon = currentWeather == 'XMAS' and 'circle-check', args = {'set_weather', 'XMAS'}, close = false} 220 | index += 1 221 | 222 | menuOptions[index] = {label = 'Halloween', icon = currentWeather == 'HALLOWEEN' and 'circle-check', args = {'set_weather', 'HALLOWEEN'}, close = false} 223 | index += 1 224 | end 225 | 226 | if perms.Set_Clouds then 227 | menuOptions[index] = {label = 'Remove All Clouds', args = {'remove_clouds'}, close = false} 228 | index += 1 229 | 230 | menuOptions[index] = {label = 'Randomize Clouds', args = {'randomize_clouds'}, close = false} 231 | index += 1 232 | end 233 | 234 | lib.registerMenu({ 235 | id = 'bMenu_weather_options', 236 | title = 'Weather Options', 237 | position = MenuPosition, 238 | onClose = function(keyPressed) 239 | CloseMenu(false, keyPressed, 'bMenu_world_related_options') 240 | end, 241 | onSelected = function(selected) 242 | MenuIndexes['bMenu_weather_options'] = selected 243 | end, 244 | onCheck = function(selected, checked, args) 245 | blackout = GetConvar('bMenu.Enable_Blackout', 'false') == 'true' 246 | snowEffects = GetConvar('bMenu.Enable_Snow_Effects', 'false') == 'true' 247 | dynamicWeather = GetConvar('bMenu.Dynamic_Weather', 'true') == 'true' 248 | 249 | if args[1] == 'dynamic_weather' then 250 | checkedDynamicWeather = checked 251 | TriggerServerEvent('bMenu:server:updateWeather', currentWeather, blackout, checked, snowEffects) 252 | lib.setMenuOptions('bMenu_weather_options', {label = 'Dynamic Weather', description = 'Whether to randomize the state of the weather or not', args = {'dynamic_weather'}, checked = checked, close = false}, selected) 253 | elseif args[1] == 'blackout' then 254 | checkedBlackout = checked 255 | TriggerServerEvent('bMenu:server:updateWeather', currentWeather, checked, dynamicWeather, snowEffects) 256 | lib.setMenuOptions('bMenu_weather_options', {label = 'Blackout', description = 'If turned on, disables all light sources', args = {'blackout'}, checked = checked, close = false}, selected) 257 | elseif args[1] == 'snow_effects' then 258 | checkedSnowEffects = checked 259 | TriggerServerEvent('bMenu:server:updateWeather', currentWeather, blackout, dynamicWeather, checked) 260 | lib.setMenuOptions('bMenu_weather_options', {label = 'Snow Effects', description = 'This will force snow to appear on the ground and enable snow particles for peds and vehicles. Combine with X-MAS or Light Snow for the best results', args = {'snow_effects'}, checked = checked, close = false}, selected) 261 | end 262 | end, 263 | options = menuOptions 264 | }, function(_, _, args) 265 | if args[1] == 'bMenu_world_related_options' then 266 | lib.showMenu(args[1], MenuIndexes[args[1]]) 267 | return 268 | end 269 | 270 | if args[1] == 'set_weather' then 271 | if changingWeather then 272 | lib.notify({ 273 | description = 'Already changing weather, please wait', 274 | type = 'error' 275 | }) 276 | return 277 | end 278 | 279 | lib.notify({ 280 | description = ('Changing weather to %s'):format(weatherIndexes[args[2]][2]), 281 | type = 'inform', 282 | duration = weatherChangeTime * 1000 + 2000 283 | }) 284 | changingWeather = true 285 | blackout = GetConvar('bMenu.Enable_Blackout', 'false') == 'true' 286 | snowEffects = GetConvar('bMenu.Enable_Snow_Effects', 'false') == 'true' 287 | dynamicWeather = GetConvar('bMenu.Dynamic_Weather', 'true') == 'true' 288 | TriggerServerEvent('bMenu:server:updateWeather', args[2], blackout, dynamicWeather, snowEffects) 289 | elseif args[1] == 'remove_clouds' then 290 | TriggerServerEvent('bMenu:server:setClouds', true) 291 | elseif args[1] == 'randomize_clouds' then 292 | TriggerServerEvent('bMenu:server:setClouds', false) 293 | end 294 | end) 295 | end 296 | 297 | function CreateWorldMenu() 298 | local perms = lib.callback.await('bMenu:server:hasConvarPermission', false, 'WorldRelated', {'TimeOptions', 'WeatherOptions'}) 299 | local menuOptions = { 300 | {label = 'No access', description = 'You don\'t have access to any options, press enter to return', args = {'bMenu_main'}} 301 | } 302 | local index = 1 303 | 304 | if perms.TimeOptions then 305 | menuOptions[index] = {label = 'Time Options', args = {'bMenu_time_options'}} 306 | index += 1 307 | end 308 | 309 | if perms.WeatherOptions then 310 | menuOptions[index] = {label = 'Weather Options', args = {'bMenu_weather_options'}} 311 | index += 1 312 | end 313 | 314 | lib.registerMenu({ 315 | id = 'bMenu_world_related_options', 316 | title = 'World Related Options', 317 | position = MenuPosition, 318 | onClose = function(keyPressed) 319 | CloseMenu(false, keyPressed, 'bMenu_main') 320 | end, 321 | onSelected = function(selected) 322 | MenuIndexes['bMenu_world_related_options'] = selected 323 | end, 324 | options = menuOptions 325 | }, function(_, _, args) 326 | if args[1] == 'bMenu_time_options' then 327 | createTimeOptions() 328 | elseif args[1] == 'bMenu_weather_options' then 329 | createWeatherOptions() 330 | end 331 | 332 | lib.showMenu(args[1], MenuIndexes[args[1]]) 333 | end) 334 | end 335 | 336 | --#endregion Functions 337 | 338 | --#region Events 339 | 340 | RegisterNetEvent('bMenu:client:setClouds', function(opacity, cloudType) 341 | if opacity == 0 and cloudType == 'removed' then 342 | ClearCloudHat() 343 | return 344 | end 345 | 346 | SetCloudsAlpha(opacity) 347 | LoadCloudHat(cloudType, 4) 348 | end) 349 | 350 | --#endregion Events 351 | 352 | --#region Threads 353 | 354 | CreateThread(function() 355 | local sleep = 100 356 | while true do 357 | if showTimeOnScreen then 358 | sleep = 0 359 | DrawTextOnScreen(('Current Time: %s:%s'):format(currentHour < 10 and '0'..currentHour or currentHour, currentMinute < 10 and '0'..currentMinute or currentMinute), 0.5, 0.0) 360 | end 361 | Wait(sleep) 362 | end 363 | end) 364 | 365 | CreateThread(function() 366 | while true do 367 | currentHour = tonumber(GetConvar('bMenu.Current_Hour', '7')) --[[@as number]] 368 | currentMinute = tonumber(GetConvar('bMenu.Current_Minute', '0')) --[[@as number]] 369 | currentHour = currentHour < 0 and 0 or currentHour > 23 and 23 or currentHour 370 | currentMinute = currentMinute < 0 and 0 or currentMinute > 59 and 59 or currentMinute 371 | NetworkOverrideClockTime(currentHour, currentMinute, 0) 372 | Wait(1000) 373 | end 374 | end) 375 | 376 | CreateThread(function() 377 | local changedThings = false 378 | while true do 379 | local openMenu = lib.getOpenMenu() 380 | blackout = GetConvar('bMenu.Enable_Blackout', 'false') == 'true' 381 | snowEffects = GetConvar('bMenu.Enable_Snow_Effects', 'false') == 'true' 382 | dynamicWeather = GetConvar('bMenu.Dynamic_Weather', 'true') == 'true' 383 | 384 | if checkedBlackout ~= blackout and openMenu == 'bMenu_weather_options' then 385 | lib.setMenuOptions('bMenu_weather_options', {label = 'Blackout', description = 'If turned on, disables all light sources', args = {'blackout'}, checked = blackout, close = false}, 2) 386 | checkedBlackout = blackout 387 | changedThings = true 388 | end 389 | 390 | if checkedSnowEffects ~= snowEffects and openMenu == 'bMenu_weather_options' then 391 | lib.setMenuOptions('bMenu_weather_options', {label = 'Snow Effects', description = 'This will force snow to appear on the ground and enable snow particles for peds and vehicles. Combine with X-MAS or Light Snow for the best results', args = {'snow_effects'}, checked = snowEffects, close = false}, 3) 392 | checkedSnowEffects = snowEffects 393 | changedThings = true 394 | end 395 | 396 | if checkedDynamicWeather ~= dynamicWeather and openMenu == 'bMenu_weather_options' then 397 | lib.setMenuOptions('bMenu_weather_options', {label = 'Dynamic Weather', description = 'Whether to randomize the state of the weather or not', args = {'dynamic_weather'}, checked = dynamicWeather, close = false}, 1) 398 | checkedDynamicWeather = dynamicWeather 399 | changedThings = true 400 | end 401 | 402 | ForceSnowPass(snowEffects) 403 | SetForceVehicleTrails(snowEffects) 404 | SetForcePedFootstepsTracks(snowEffects) 405 | 406 | if snowEffects then 407 | lib.requestNamedPtfxAsset('core_snow') 408 | UseParticleFxAsset('core_snow') 409 | else 410 | RemoveNamedPtfxAsset('core_snow') 411 | end 412 | 413 | SetArtificialLightsState(blackout) 414 | 415 | currentWeather = GetConvar('bMenu.Current_Weather', 'EXTRASUNNY'):upper() 416 | currentWeather = not weatherIndexes[currentWeather] and 'EXTRASUNNY' or currentWeather 417 | 418 | if currentChecked ~= currentWeather and openMenu == 'bMenu_weather_options' then 419 | local oldData = weatherIndexes[currentChecked] 420 | local newData = weatherIndexes[currentWeather] 421 | lib.setMenuOptions('bMenu_weather_options', {label = oldData[2], args = {'set_weather', currentChecked}, close = false}, oldData[1]) 422 | lib.setMenuOptions('bMenu_weather_options', {label = newData[2], icon = 'circle-check', args = {'set_weather', currentWeather}, close = false}, newData[1]) 423 | currentChecked = currentWeather 424 | changedThings = true 425 | end 426 | 427 | if changedThings and openMenu == 'bMenu_weather_options' then 428 | lib.hideMenu(false) 429 | Wait(100) 430 | lib.showMenu('bMenu_weather_options', MenuIndexes['bMenu_weather_options']) 431 | changedThings = false 432 | elseif changedThings then 433 | changedThings = false 434 | end 435 | 436 | if GetNextWeatherTypeHashName() ~= joaat(currentWeather) then 437 | SetWeatherTypeOvertimePersist(currentWeather, weatherChangeTime) 438 | Wait(weatherChangeTime * 1000 + 2000) 439 | if changingWeather then 440 | lib.notify({ 441 | description = ('Changed weather to %s'):format(weatherIndexes[currentWeather][2]), 442 | type = 'success' 443 | }) 444 | end 445 | changingWeather = false 446 | TriggerEvent('bMenu:client:weatherChangeComplete', currentWeather) 447 | end 448 | Wait(1000) 449 | end 450 | end) 451 | 452 | --#endregion Threads -------------------------------------------------------------------------------- /config/locations.lua: -------------------------------------------------------------------------------- 1 | return {} -------------------------------------------------------------------------------- /config/weapons.lua: -------------------------------------------------------------------------------- 1 | -- In this file you can add addon weapons and/or replace the data of exisiting weapons, the data structure can be found in the example provided below 2 | 3 | return { 4 | -- example 5 | [`WEAPON_UNARMED`] = { 6 | name = 'WEAPON_UNARMED', 7 | hash = `WEAPON_UNARMED` 8 | }, 9 | -- add the rest of your weapons under here, you can also replace the example as it's just an example and not an actual entry that does something 10 | } -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'cerulean' 2 | game 'gta5' 3 | 4 | author 'BerkieB' 5 | description 'A standalone menu containing lots of useful options for FiveM' 6 | version 'don\'t use in live environment yet' 7 | repository 'https://github.com/BerkieBb/bMenu' 8 | 9 | lua54 'yes' 10 | use_experimental_fxv2_oal 'yes' 11 | 12 | shared_scripts { 13 | '@ox_lib/init.lua' 14 | } 15 | 16 | client_scripts { 17 | 'client/**/*.lua', 18 | } 19 | 20 | server_scripts { 21 | 'server/**/*.lua' 22 | } 23 | 24 | files { 25 | 'config/**/*.lua' 26 | } 27 | 28 | dependencies { 29 | '/server:5904', 30 | '/onesync', 31 | 'ox_lib' 32 | } 33 | -------------------------------------------------------------------------------- /perms.cfg: -------------------------------------------------------------------------------- 1 | ############################ Berkie Menu ############################ 2 | 3 | ### Convars 4 | 5 | # Enable or disable the permissions system, if disabled everyone will be able to do everything. If not set will default to true 6 | setr bMenu.Use_Permissions true 7 | 8 | # Weather convars, these are all changeable in-game as well 9 | setr bMenu.Sync_Time_To_Machine_Time false # Sync time in-game to your server 10 | setr bMenu.Freeze_Time false # Freeze time upon join 11 | setr bMenu.Current_Hour 7 # Starting hour when joining the game 12 | setr bMenu.Current_Minute 0 # Starting minute when joining the game 13 | setr bMenu.Dynamic_Weather true # Change weather in an interval 14 | setr bMenu.Enable_Blackout false # Have the city blacked out when you join 15 | setr bMenu.Enable_Snow_Effects false # Enable snow particles 16 | setr bMenu.Current_Weather EXTRASUNNY # Current weather, available weather states: EXTRASUNNY, CLEAR, NEUTRAL, SMOG, FOGGY, CLOUDS, OVERCAST, CLEARING, RAIN, THUNDER, BLIZZARD, SNOW, SNOWLIGHT, XMAS, HALLOWEEN 17 | setr bMenu.Weather_Change_Time 5 # Time it takes to change weather in seconds when dynamic weather is enabled 18 | 19 | ### Aces 20 | 21 | ## Commands 22 | 23 | add_ace builtin.everyone command.bmenu allow # Allow access to use the command 24 | add_ace builtin.everyone command.bMenu_toggleNoClip allow # Allow access to the toggle noclip command 25 | add_ace builtin.everyone command.bMenu_tpToWaypoint allow # Allow access to the teleport to waypoint option 26 | add_ace builtin.everyone command.bMenu_toggleDriftMode allow # Allow access to the toggle drift mode command 27 | 28 | ## Main Menu 29 | 30 | #add_ace builtin.everyone bMenu.Main.All allow # Allow access to all menus in the main menu 31 | add_ace builtin.everyone bMenu.Main.OnlinePlayers allow # Allow access to Online Players menu 32 | add_ace builtin.everyone bMenu.Main.PlayerRelated allow # Allow access to Player Related Options menu 33 | add_ace builtin.everyone bMenu.Main.VehicleRelated allow # Allow access to Vehicle Related Options menu 34 | add_ace builtin.everyone bMenu.Main.WorldRelated allow # Allow access to World Related Options menu 35 | add_ace builtin.everyone bMenu.Main.Recording allow # Allow access to Recording Options menu 36 | add_ace builtin.everyone bMenu.Main.Misc allow # Allow access to Miscellaneous Options menu 37 | 38 | ## Online Players 39 | 40 | #add_ace builtin.everyone bMenu.OnlinePlayers.All allow # Allow all player options 41 | add_ace builtin.everyone bMenu.OnlinePlayers.Message allow # Allow private messaging 42 | add_ace builtin.everyone bMenu.OnlinePlayers.Teleport_To allow # Allow teleporting to a player 43 | add_ace builtin.everyone bMenu.OnlinePlayers.Teleport_In_Vehicle allow # Allow teleporting into a players vehicle 44 | add_ace builtin.everyone bMenu.OnlinePlayers.Summon allow # Allow summoning players 45 | add_ace builtin.everyone bMenu.OnlinePlayers.Spectate allow # Allow spectating 46 | add_ace builtin.everyone bMenu.OnlinePlayers.Waypoint allow # Allow setting a waypoint to a player 47 | add_ace builtin.everyone bMenu.OnlinePlayers.Blip allow # Allow adding a blip to a player 48 | add_ace builtin.everyone bMenu.OnlinePlayers.Kill allow # Allow killing a player 49 | 50 | ## Recording 51 | 52 | #add_ace builtin.everyone bMenu.Recording.All allow # Allow all recording options 53 | add_ace builtin.everyone bMenu.Recording.Start_Stop allow # Allow starting and stopping a recording 54 | add_ace builtin.everyone bMenu.Recording.Editor allow # Allow access to the Rockstar Editor 55 | 56 | ## World Related 57 | 58 | #add_ace builtin.everyone bMenu.WorldRelated.All allow # Allow all world related options 59 | add_ace builtin.everyone bMenu.WorldRelated.TimeOptions allow # Allow time altering options 60 | add_ace builtin.everyone bMenu.WorldRelated.WeatherOptions allow # Allow weather altering options 61 | 62 | ### Time Options 63 | 64 | #add_ace builtin.everyone bMenu.WorldRelated.TimeOptions.All allow # Allow all time options 65 | add_ace builtin.everyone bMenu.WorldRelated.TimeOptions.Freeze_Unfreeze_Time allow # Allow freezing and unfreezing the time 66 | add_ace builtin.everyone bMenu.WorldRelated.TimeOptions.Sync_Time allow # Allow syncing the time to the server 67 | add_ace builtin.everyone bMenu.WorldRelated.TimeOptions.Show_Time allow # Allow showing the time on screen 68 | add_ace builtin.everyone bMenu.WorldRelated.TimeOptions.Presets allow # Allow changing time through presets 69 | add_ace builtin.everyone bMenu.WorldRelated.TimeOptions.Set_Hour allow # Allow changing the hour 70 | add_ace builtin.everyone bMenu.WorldRelated.TimeOptions.Set_Minute allow # Allow changing the minute 71 | 72 | ### Weather Options 73 | 74 | #add_ace builtin.everyone bMenu.WorldRelated.WeatherOptions.All allow # Allow all weather options 75 | add_ace builtin.everyone bMenu.WorldRelated.WeatherOptions.Toggle_Dynamic_Weather allow # Allow toggling dynamic weather 76 | add_ace builtin.everyone bMenu.WorldRelated.WeatherOptions.Toggle_Blackout allow # Allow toggling blackout 77 | add_ace builtin.everyone bMenu.WorldRelated.WeatherOptions.Toggle_Snow_Effects allow # Allow toggling snow effects used in X-MAS and Light Snow 78 | add_ace builtin.everyone bMenu.WorldRelated.WeatherOptions.Change_Weather_Type allow # Allow changing the weather type 79 | add_ace builtin.everyone bMenu.WorldRelated.WeatherOptions.Set_Clouds allow # Allow altering the clouds 80 | 81 | ## Misc Options 82 | 83 | #add_ace builtin.everyone bMenu.Misc.All allow # Allow all misc options 84 | add_ace builtin.everyone bMenu.Misc.TeleportOptions allow # Allow access to the teleport options menu 85 | add_ace builtin.everyone bMenu.Misc.DevTools allow # Allow access to developer tools 86 | add_ace builtin.everyone bMenu.Misc.ConnectionOptions allow # Allow access to the connection options menu 87 | add_ace builtin.everyone bMenu.Misc.Menu_Position allow # Allow changing the position of the menu 88 | add_ace builtin.everyone bMenu.Misc.Toggle_PM allow # Allow toggling private messages server-wide 89 | add_ace builtin.everyone bMenu.Misc.Show_Speed allow # Allow access to toggle a speedometer on the screen 90 | add_ace builtin.everyone bMenu.Misc.Display_Location allow # Allow access to displaying your location 91 | add_ace builtin.everyone bMenu.Misc.Show_Time allow # Allow access to show the time on the screen 92 | add_ace builtin.everyone bMenu.Misc.Toggle_Join_Notifications allow # Allow toggling join notifications 93 | add_ace builtin.everyone bMenu.Misc.Toggle_Quit_Notifications allow # Allow toggling quit notifications 94 | add_ace builtin.everyone bMenu.Misc.Toggle_Death_Notifications allow # Allow toggling death notifications 95 | add_ace builtin.everyone bMenu.Misc.Toggle_Night_Vision allow # Allow toggling night vision 96 | add_ace builtin.everyone bMenu.Misc.Toggle_Thermal_Vision allow # Allow toggling thermal vision 97 | add_ace builtin.everyone bMenu.Misc.Toggle_Player_Names allow # Allow toggling player names 98 | 99 | ### Teleport Options 100 | 101 | #add_ace builtin.everyone bMenu.Misc.TeleportOptions.All allow # Allow access to all teleport options 102 | add_ace builtin.everyone bMenu.Misc.TeleportOptions.To_Waypoint # Allow teleporting to their waypoint 103 | add_ace builtin.everyone bMenu.Misc.TeleportOptions.To_Coords # Allow teleporting to coords they input 104 | add_ace builtin.everyone bMenu.Misc.TeleportOptions.Locations # Allow access to pre-configured locations and access to change them in-game 105 | 106 | ### Developer Options 107 | 108 | #add_ace builtin.everyone bMenu.Misc.DevTools.All allow # Allow all developer options 109 | add_ace builtin.everyone bMenu.Misc.DevTools.Clear_Area allow # Allow clearing an area of 100 meters 110 | add_ace builtin.everyone bMenu.Misc.DevTools.Show_Coords allow # Allow showing coords on the screen 111 | add_ace builtin.everyone bMenu.Misc.DevTools.Show_Entity_Data allow # Allow showing data about entities on the screen 112 | add_ace builtin.everyone bMenu.Misc.DevTools.Modify_Timecycle allow # Allow modifiying the timecycle 113 | add_ace builtin.everyone bMenu.Misc.DevTools.EntitySpawner allow # Allow accessing the entity spawner 114 | 115 | ### Connection Options 116 | 117 | #add_ace builtin.everyone bMenu.Misc.ConnectionOptions.All allow # Allow all connection options 118 | add_ace builtin.everyone bMenu.Misc.ConnectionOptions.Quit_Join_Session # Allow quitting and rejoining the session 119 | add_ace builtin.everyone bMenu.Misc.ConnectionOptions.Quit_Game # Allow quitting the game from the menu 120 | 121 | ## Player Related Options 122 | 123 | #add_ace builtin.everyone bMenu.PlayerRelated.All allow # Allow all player related options 124 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions allow # Allow access to the player options menu 125 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions allow # Allow access to the weapon options menu 126 | add_ace builtin.everyone bMenu.PlayerRelated.Toggle_NoClip allow # Allow access to toggling noclip 127 | 128 | ### Player Options 129 | 130 | #add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.All allow # Allow all player options 131 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Godmode allow # Allow toggling godmode 132 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Invisible allow # Allow toggling invisibility 133 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Unlimited_Stamina allow # Allow having unlimited stamina 134 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Fast_Run allow # Allow having a faster sprint speed 135 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Fast_Swim allow # Allow having a faster swim speed 136 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Super_Jump allow # Allow toggling super jump to increase jump height 137 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.No_Ragdoll allow # Allow toggling no ragdoll 138 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Ghost_Mode allow # Allow toggling ghost mode 139 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Never_Wanted allow # Allow toggling never wanted 140 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Set_Wanted_Level allow # Allow setting your wanted level 141 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Everyone_Ignore_Player allow # Allow toggling the ignorance of every npc 142 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Stay_In_Vehicle allow # Allow toggling sticky seats 143 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Heal_Player allow # Allow healing yourself 144 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Set_Armor_Type allow # Allow setting the armor type 145 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Adjust_Player_Clothes allow # Allow adjusting the wetness and dirtiness of the player clothes 146 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Suicide allow # Allow commiting animation-assisted suicide 147 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.AutoPilot # Allow accessing the auto pilot menu 148 | add_ace builtin.everyone bMenu.PlayerRelated.PlayerOptions.Freeze_Player # Allow freezing yourself in place 149 | 150 | ### Weapon Options 151 | 152 | #add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.All allow # Allow all weapon options 153 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Get_All_Weapons allow # Allow getting all weapons 154 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Remove_All_Weapons allow # Allow removing all weapons 155 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Unlimited_Ammo allow # Allow toggling unlimited ammo 156 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.No_Reload allow # Allow toggling no reload 157 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Set_All_Ammo_Count allow # Allow setting ammo counts for all ammo types 158 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Set_Max_Ammo allow # Allow setting max ammo for all weapons 159 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Spawn_Weapon_By_Name allow # Allow spawning a weapon by name 160 | 161 | #add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Category.All allow # Allow accessing all categories to spawn weapons 162 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Category.rifle allow # Allow accessing the category 'rifle' 163 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Category.handgun allow # Allow accessing the category 'handgun' 164 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Category.heavy allow # Allow accessing the category 'heavy' 165 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Category.melee allow # Allow accessing the category 'melee' 166 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Category.misc allow # Allow accessing the category 'misc' 167 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Category.shotgun allow # Allow accessing the category 'shotgun' 168 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Category.sniper allow # Allow accessing the category 'sniper' 169 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Category.slmg allow # Allow accessing the category 'slmg' 170 | add_ace builtin.everyone bMenu.PlayerRelated.WeaponOptions.Category.throwable allow # Allow accessing the category 'throwable' 171 | 172 | ## Vehicle Related Options 173 | 174 | #add_ace builtin.everyone bMenu.VehicleRelated.All allow # Allow all vehicle related options 175 | add_ace builtin.everyone bMenu.VehicleRelated.Options allow # Allow access to other vehicle options 176 | add_ace builtin.everyone bMenu.VehicleRelated.PersonalVehicle allow # Allow access to the personal vehicle menu 177 | add_ace builtin.everyone bMenu.VehicleRelated.Spawner allow # Allow access to the vehicle spawner menu 178 | 179 | ### Vehicle Options 180 | 181 | #add_ace builtin.everyone bMenu.VehicleRelated.Options.All allow # Allow all vehicle options 182 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Vehicle_God_Mode allow # Allow enabling vehicle god mode 183 | add_ace builtin.everyone bMenu.VehicleRelated.Options.God_Mode_Options allow # Allow setting different god mode types 184 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Repair_Vehicle allow # Allow repairing your vehicle 185 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Keep_Vehicle_Clean allow # Allow keeping your vehicle clean 186 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Wash_Vehicle allow # Allow washing your vehicle 187 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Set_Dirt_Level allow # Allow setting the dirt level on your vehicle 188 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Mod_Menu allow # Allow access to the mod menu 189 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Change_Colors allow # Allow access to the menu to change your vehicles color 190 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Set_Neon_Kits allow # Allow adding and removing neon kits on your vehicle 191 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Manage_Extras allow # Allow managing extras on your vehicle 192 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Toggle_Engine allow # Allow toggling the engine of your vehicle 193 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Set_License_Plate allow # Allow changing the license plate text 194 | add_ace builtin.everyone bMenu.VehicleRelated.Options.License_Plate_Type allow # Allow changing the type of license plate 195 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Manage_Doors allow # Allow managing the doors on your vehicle 196 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Manage_Windows allow # Allow managing the windows on your vehicle 197 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Bike_Seatbelt allow # Allow a imaginary seatbelt on a bike to make sure you don't fall off 198 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Speed_Limiter allow # Allow to adjust the speed limiter 199 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Torque_Multiplier allow # Allow access to the torque multiplier 200 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Power_Multiplier allow # Allow access to the power multiplier 201 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Disable_Plane_Turbulence allow # Allow disabling plane turbulence 202 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Flip_Vehicle allow # Allow the option to flip your vehicle the right side up 203 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Toggle_Alarm allow # Allow toggling the alarm of your vehicle 204 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Cycle_Seats allow # Allow cycling through the seats on your vehicle 205 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Manage_Lights allow # Allow managing the exterior lights of your vehicle 206 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Fix_Destroy_Tires allow # Allow fixing and destroying your own tires 207 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Freeze_Vehicle allow # Allow freezing your vehicle in place 208 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Toggle_Visibility allow # Allow toggling the visibility of your vehicle in the world 209 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Engine_Always_On allow # Allow the engine to always stay running 210 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Infinite_Fuel allow # Allow for infinite fuel 211 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Show_Health allow # Allow showing the health of the vehicle 212 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Set_Radio_Station allow # Allow setting the default radio station 213 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Bike_Helmet allow # Allow toggling the option to auto-equip bike helmets 214 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Highbeams_On_Honk allow # Allow toggling the option to flash the highbeams when honking 215 | add_ace builtin.everyone bMenu.VehicleRelated.Options.Delete_Vehicle allow # Allow deleting your own vehicle 216 | 217 | ### Vehicle God Options 218 | 219 | #add_ace builtin.everyone bMenu.VehicleRelated.Options.God.All allow # Allow all vehicle god options 220 | add_ace builtin.everyone bMenu.VehicleRelated.Options.God.Invincible allow # Allow setting the vehicle invincible 221 | add_ace builtin.everyone bMenu.VehicleRelated.Options.God.Engine_Damage allow # Allow toggling engine damage 222 | add_ace builtin.everyone bMenu.VehicleRelated.Options.God.Visible_Damage allow # Allow toggling visible damage 223 | add_ace builtin.everyone bMenu.VehicleRelated.Options.God.Strong_Wheels allow # Allow toggling strong wheels 224 | add_ace builtin.everyone bMenu.VehicleRelated.Options.God.Ramp_Damage allow # Allow toggling ramp damage 225 | add_ace builtin.everyone bMenu.VehicleRelated.Options.God.Auto_Repair allow # Allow toggling auto repair 226 | 227 | ### Personal Vehicle Options 228 | 229 | #add_ace builtin.everyone bMenu.VehicleRelated.PersonalVehicle.All allow # Allow all personal vehicle options 230 | add_ace builtin.everyone bMenu.VehicleRelated.PersonalVehicle.Toggle_Engine allow # Allow toggling the engine of your personal vehicle 231 | add_ace builtin.everyone bMenu.VehicleRelated.PersonalVehicle.Set_Lights allow # Allow managing the exterior lights of your personal vehicle 232 | add_ace builtin.everyone bMenu.VehicleRelated.PersonalVehicle.Manage_Doors allow # Allow locking/unlocking and opening/closing the doors of your personal vehicle 233 | add_ace builtin.everyone bMenu.VehicleRelated.PersonalVehicle.Toggle_Alarm allow # Allow toggling the alarm of the vehicle 234 | add_ace builtin.everyone bMenu.VehicleRelated.PersonalVehicle.Toggle_Blip allow # Allow toggling the blip of your personal vehicle 235 | add_ace builtin.everyone bMenu.VehicleRelated.PersonalVehicle.Exclusive_Driver allow # Allow toggling the option to have yourself only as the driver of your personal vehicle 236 | 237 | ### Spawner Options 238 | 239 | #add_ace builtin.everyone bMenu.VehicleRelated.Spawner.All allow # Allow all spawner options 240 | add_ace builtin.everyone bMenu.VehicleRelated.Spawner.Spawn_By_Name allow # Allow spawning vehicles by their model name 241 | add_ace builtin.everyone bMenu.VehicleRelated.Spawner.Spawn_In_Vehicle allow # Allow toggling the option to spawn inside the vehicle you want to spawn 242 | add_ace builtin.everyone bMenu.VehicleRelated.Spawner.Replace_Previous allow # Allow toggling the option to replace the vehicle you spawned previously with a new one 243 | add_ace builtin.everyone bMenu.VehicleRelated.Spawner.Spawn_By_Category allow # Allow spawning vehicles by their category -------------------------------------------------------------------------------- /server/main.lua: -------------------------------------------------------------------------------- 1 | --#region Startup 2 | 3 | -- I don't want you renaming this because this uses kvp and that requires the resource name to be the same across servers to work correctly 4 | if GetCurrentResourceName() ~= 'bMenu' then 5 | error('Please don\'t rename this resource, change the folder name (back) to \'bMenu\' (case sensitive) to make sure the saved data can be saved and fetched accordingly from the cache.') 6 | return 7 | end 8 | 9 | --#endregion Startup 10 | 11 | --#region Callbacks 12 | 13 | lib.callback.register('bMenu:server:setConvar', function(_, convar, value, replicated, sendUpdate, menuId, option, optionId) 14 | if sendUpdate then 15 | TriggerClientEvent('bMenu:client:updateConvar', -1, convar, value, menuId, option, optionId) 16 | end 17 | return replicated and SetConvarReplicated(convar, value) or SetConvar(convar, value) 18 | end) 19 | 20 | lib.callback.register('bMenu:server:hasCommandPermission', function(source, command) 21 | return IsPlayerAceAllowed(source, ('command.%s'):format(command)) 22 | end) 23 | 24 | lib.callback.register('bMenu:server:hasConvarPermission', function(source, category, convar) 25 | if not convar then return end 26 | 27 | if type(convar) == 'table' then 28 | local allowed = {} 29 | 30 | if GetConvar('bMenu.Use_Permissions', 'true') == 'false' then 31 | for i = 1, #convar do 32 | allowed[convar[i]] = true 33 | end 34 | 35 | return allowed 36 | end 37 | 38 | local categoryType = type(category) 39 | local categoryText = category 40 | if categoryType == 'table' then 41 | categoryText = '' 42 | for i = 1, #category do 43 | local text = category[i] 44 | local length = #text 45 | local endStr = text:sub(length, length) 46 | text = endStr == '.' and text or text..'.' 47 | categoryText = string.format('%s%s', categoryText, text) 48 | end 49 | elseif categoryType == 'string' then 50 | local length = #categoryText 51 | local endStr = categoryText:sub(length, length) 52 | categoryText = endStr == '.' and categoryText or categoryText..'.' 53 | end 54 | 55 | local hasAllPermission = IsPlayerAceAllowed(source, string.format('%sAll', categoryText)) 56 | for i = 1, #convar do 57 | local c = convar[i] 58 | allowed[c] = hasAllPermission or IsPlayerAceAllowed(source, string.format('bMenu.%s%s', categoryText or '', c)) 59 | end 60 | 61 | return allowed 62 | end 63 | 64 | local categoryType = type(category) 65 | local categoryText = category 66 | if categoryType == 'table' then 67 | categoryText = '' 68 | for i = 1, #category do 69 | local text = category[i] 70 | local length = #text 71 | local endStr = text:sub(length, length) 72 | text = endStr == '.' and text or text..'.' 73 | categoryText = string.format('%s%s', categoryText, text) 74 | end 75 | elseif categoryType == 'string' then 76 | local length = #categoryText 77 | local endStr = categoryText:sub(length, length) 78 | categoryText = endStr == '.' and categoryText or (categoryText .. '.') 79 | end 80 | 81 | return GetConvar('bMenu.Use_Permissions', 'false') == 'false' or IsPlayerAceAllowed(source, string.format('%sAll', categoryText)) or IsPlayerAceAllowed(source, string.format('bMenu.%s%s', categoryText or '', convar)) 82 | end) 83 | 84 | --#endregion Callbacks 85 | -------------------------------------------------------------------------------- /server/misc.lua: -------------------------------------------------------------------------------- 1 | --#region Events 2 | 3 | RegisterNetEvent('bMenu:server:clearArea', function(pos) 4 | TriggerClientEvent('bMenu:client:clearArea', -1, pos) 5 | end) 6 | 7 | --#endregion Events 8 | 9 | --#region Callbacks 10 | 11 | lib.callback.register('bMenu:server:getMaxClients', function() 12 | return GetConvarInt('sv_maxclients', 48) 13 | end) 14 | 15 | --#endregion Callbacks -------------------------------------------------------------------------------- /server/players.lua: -------------------------------------------------------------------------------- 1 | --#region Callbacks 2 | 3 | lib.callback.register('bMenu:server:getOnlinePlayers', function() 4 | local players = GetPlayers() 5 | local data = {} 6 | for i = 1, #players do 7 | local src = players[i] 8 | data[#data + 1] = { 9 | source = tonumber(src), 10 | name = GetPlayerName(src) 11 | } 12 | end 13 | return data 14 | end) 15 | 16 | lib.callback.register('bMenu:server:playerListAction', function(source, action, playerSource, canActOnSelf, message) 17 | if source == playerSource and not canActOnSelf then return false, 'You can\'t act on yourself' end 18 | 19 | local messageArg = action == 'message' 20 | local teleportVehicleArg = action == 'teleport_vehicle' 21 | local teleportArg = action == 'teleport' 22 | local summonArg = action == 'summon' 23 | local spectateArg = action == 'spectate' 24 | local waypointArg = action == 'waypoint' 25 | local blipArg = action == 'blip' 26 | local killArg = action == 'kill' 27 | local playerName = GetPlayerName(playerSource) 28 | local playerPed = GetPlayerPed(playerSource) 29 | local ped = GetPlayerPed(source) 30 | 31 | if messageArg then 32 | local canSendMessage = lib.callback.await('bMenu:client:canSendMessage', playerSource) 33 | if not canSendMessage then 34 | return false, ('%s has their PMs disabled'):format(playerName) 35 | end 36 | TriggerClientEvent('chat:addMessage', playerSource, { 37 | color = { 255, 0, 0 }, 38 | multiline = true, 39 | args = { ('PM from %s'):format(GetPlayerName(source)), message } 40 | }) 41 | return true, ('Successfully sent a message to %s'):format(playerName) 42 | elseif teleportArg then 43 | local playerCoords = GetEntityCoords(playerPed) 44 | SetEntityCoords(ped, playerCoords.x, playerCoords.y, playerCoords.z, false, false, false, false) 45 | return true, ('Successfully teleported to %s'):format(playerName) 46 | elseif teleportVehicleArg then 47 | local veh = GetVehiclePedIsIn(playerPed, false) 48 | if veh == 0 then return false, ('%s is not in a vehicle'):format(playerName) end 49 | return true, ('Successfully teleported into the vehicle of %s'):format(playerName), NetworkGetNetworkIdFromEntity(veh) 50 | elseif summonArg then 51 | local playerCoords = GetEntityCoords(ped) 52 | SetEntityCoords(playerPed, playerCoords.x, playerCoords.y, playerCoords.z, false, false, false, false) 53 | return true, ('Successfully summoned %s'):format(playerName) 54 | elseif spectateArg then 55 | return true, ('Successfully spectating %s'):format(playerName), GetEntityCoords(playerPed) 56 | elseif waypointArg then 57 | return true, ('Successfully set your waypoint to %s'):format(playerName), GetEntityCoords(playerPed).xy 58 | elseif blipArg then 59 | return true, ('Successfully toggled the blip to %s'):format(playerName), NetworkGetNetworkIdFromEntity(playerPed) 60 | elseif killArg then 61 | return true, ('Successfully killed %s'):format(playerName), NetworkGetNetworkIdFromEntity(playerPed) 62 | end 63 | 64 | return false, ('Invalid action %s'):format(action) 65 | end) 66 | 67 | --#endregion Callbacks -------------------------------------------------------------------------------- /server/teleports.lua: -------------------------------------------------------------------------------- 1 | --#region Callbacks 2 | 3 | lib.callback.register('bMenu:server:saveTeleportLocation', function(source, teleportName) 4 | local file = {string.strtrim(LoadResourceFile('bMenu', 'config/locations.lua'))} 5 | 6 | if file then 7 | file[1] = file[1]:gsub('}$', '') 8 | 9 | local playerPed = GetPlayerPed(source) 10 | 11 | file[2] = [[ 12 | 13 | { 14 | name = '%s', 15 | coords = %s, 16 | heading = %s 17 | }, 18 | ]] 19 | 20 | file[2] = file[2]:format(teleportName, GetEntityCoords(playerPed), GetEntityHeading(playerPed)) 21 | 22 | file[3] = '}' 23 | 24 | SaveResourceFile('bMenu', 'config/locations.lua', table.concat(file), -1) 25 | 26 | return true, ('Successfully added location %s'):format(teleportName) 27 | end 28 | 29 | return false, 'Something went wrong with loading the locations file' 30 | end) 31 | 32 | lib.callback.register('bMenu:server:getConfig', function(_, fileName) 33 | local file = LoadResourceFile('bMenu', ('config/%s.lua'):format(fileName)) 34 | if not file then return end 35 | 36 | local returnVal = load(file, ('@@bMenu/config/%s.lua'):format(fileName)) 37 | if not returnVal then return end 38 | 39 | return returnVal() 40 | end) 41 | 42 | --#endregion Callbacks 43 | -------------------------------------------------------------------------------- /server/time.lua: -------------------------------------------------------------------------------- 1 | --#region Variables 2 | 3 | local currentHour = tonumber(GetConvar('bMenu.Current_Hour', '7')) --[[@as number]] 4 | local currentMinute = tonumber(GetConvar('bMenu.Current_Minute', '0')) --[[@as number]] 5 | local timeFrozen = GetConvar('bMenu.Freeze_Time', 'false') == 'true' 6 | local timeSyncedWithMachine = GetConvar('bMenu.Sync_Time_To_Machine_Time', 'false') == 'true' 7 | currentHour = currentHour < 0 and 0 or currentHour > 23 and 23 or currentHour 8 | currentMinute = currentMinute < 0 and 0 or currentMinute > 59 and 59 or currentMinute 9 | 10 | --#endregion Variables 11 | 12 | --#region Events 13 | 14 | RegisterNetEvent('bMenu:server:updateTime', function(newHour, newMinute, newFreezeState, newSyncState) 15 | newHour = newHour < 0 and 0 or newHour > 23 and 23 or newHour 16 | newMinute = newMinute < 0 and 0 or newMinute > 23 and 23 or newMinute 17 | SetConvarReplicated('bMenu.Current_Hour', tostring(newHour)) 18 | SetConvarReplicated('bMenu.Current_Minute', tostring(newMinute)) 19 | SetConvarReplicated('bMenu.Freeze_Time', tostring(newFreezeState)) 20 | SetConvarReplicated('bMenu.Sync_Time_To_Machine_Time', tostring(newSyncState)) 21 | timeSyncedWithMachine = newSyncState 22 | timeFrozen = newFreezeState 23 | currentHour = newHour 24 | currentMinute = newMinute 25 | end) 26 | 27 | --#endregion Events 28 | 29 | --#region Threads 30 | 31 | CreateThread(function() 32 | local sleep = tonumber(GetConvar('bMenu_ingame_minute_duration', '2000')) --[[@as number]] 33 | sleep = sleep < 100 and 100 or sleep 34 | while true do 35 | if timeSyncedWithMachine then 36 | local hour = os.date('%H') 37 | local minute = os.date('%M') 38 | currentHour = tonumber(hour) --[[@as number]] 39 | currentMinute = tonumber(minute) --[[@as number]] 40 | SetConvarReplicated('bMenu.Current_Hour', hour --[[@as string]]) 41 | SetConvarReplicated('bMenu.Current_Minute', minute --[[@as string]]) 42 | Wait(10000) 43 | else 44 | if not timeFrozen then 45 | if currentMinute + 1 > 59 then 46 | currentMinute = 0 47 | if currentHour + 1 > 23 then 48 | currentHour = 0 49 | else 50 | currentHour += 1 51 | end 52 | else 53 | currentMinute += 1 54 | end 55 | SetConvarReplicated('bMenu.Current_Hour', tostring(currentHour)) 56 | SetConvarReplicated('bMenu.Current_Minute', tostring(currentMinute)) 57 | end 58 | Wait(sleep) 59 | end 60 | end 61 | end) 62 | 63 | --#endregion Threads -------------------------------------------------------------------------------- /server/vehicles.lua: -------------------------------------------------------------------------------- 1 | --#region Callbacks 2 | 3 | lib.callback.register('bMenu:server:spawnVehicle', function(_, model, coords) 4 | local tempVehicle = CreateVehicle(model, 0, 0, 0, 0, true, true) 5 | while not DoesEntityExist(tempVehicle) do 6 | Wait(0) 7 | end 8 | local entityType = GetVehicleType(tempVehicle) 9 | DeleteEntity(tempVehicle) 10 | local veh = CreateVehicleServerSetter(model, entityType, coords.x, coords.y, coords.z, coords.w) 11 | while not DoesEntityExist(veh) do 12 | Wait(0) 13 | end 14 | return NetworkGetNetworkIdFromEntity(veh) 15 | end) 16 | 17 | --#endregion Callbacks -------------------------------------------------------------------------------- /server/weather.lua: -------------------------------------------------------------------------------- 1 | --#region Variables 2 | 3 | local cloudTypes = { 4 | 'Cloudy 01', 5 | 'RAIN', 6 | 'horizonband1', 7 | 'horizonband2', 8 | 'Puffs', 9 | 'Wispy', 10 | 'Horizon', 11 | 'Stormy 01', 12 | 'Clear 01', 13 | 'Snowy 01', 14 | 'Contrails', 15 | 'altostratus', 16 | 'Nimbus', 17 | 'Cirrus', 18 | 'cirrocumulus', 19 | 'stratoscumulus', 20 | 'horizonband3', 21 | 'Stripey', 22 | 'horsey', 23 | 'shower' 24 | } 25 | 26 | local weatherTypes = { 27 | ['EXTRASUNNY'] = 'Extra Sunny', 28 | ['CLEAR'] = 'Clear', 29 | ['NEUTRAL'] = 'Neutral', 30 | ['SMOG'] = 'Smog', 31 | ['FOGGY'] = 'Foggy', 32 | ['CLOUDS'] = 'Cloudy', 33 | ['OVERCAST'] = 'Overcast', 34 | ['CLEARING'] = 'Clearing', 35 | ['RAIN'] = 'Rainy', 36 | ['THUNDER'] = 'Thunder', 37 | ['BLIZZARD'] = 'Blizzard', 38 | ['SNOW'] = 'Snow', 39 | ['SNOWLIGHT'] = 'Light Snow', 40 | ['XMAS'] = 'X-MAS Snow', 41 | ['HALLOWEEN'] = 'Halloween' 42 | } 43 | 44 | local dynamicWeather = GetConvar('bMenu.Dynamic_Weather', 'true') == 'true' 45 | local dynamicWeatherTimer = tonumber(GetConvar('bMenu.Dynamic_Weather_timer', '1')) --[[@as number]] 46 | dynamicWeatherTimer = dynamicWeatherTimer < 0 and 1 or dynamicWeatherTimer 47 | local currentWeather = GetConvar('bMenu.Current_Weather', 'EXTRASUNNY'):upper() 48 | currentWeather = not weatherTypes[currentWeather] and 'EXTRASUNNY' or currentWeather 49 | local lastWeatherChange = 0 50 | 51 | --#endregion Variables 52 | 53 | --#region Events 54 | 55 | RegisterNetEvent('bMenu:server:updateWeather', function(newWeather, newBlackoutState, newDynamicState, newSnowState) 56 | if not weatherTypes[newWeather] then return end 57 | 58 | SetConvarReplicated('bMenu.Current_Weather', newWeather) 59 | SetConvarReplicated('bMenu.Enable_Blackout', tostring(newBlackoutState)) 60 | SetConvarReplicated('bMenu.Dynamic_Weather', tostring(newDynamicState)) 61 | SetConvarReplicated('bMenu.Enable_Snow_Effects', tostring(newSnowState)) 62 | currentWeather = newWeather 63 | dynamicWeather = newDynamicState 64 | lastWeatherChange = GetGameTimer() 65 | 66 | if newWeather == 'XMAS' or newWeather == 'SNOWLIGHT' or newWeather == 'SNOW' or newWeather == 'BLIZZARD' then 67 | SetConvarReplicated('bMenu.Enable_Snow_Effects', 'true') 68 | elseif GetConvar('bMenu.Enable_Snow_Effects', 'false') == 'true' then 69 | SetConvarReplicated('bMenu.Enable_Snow_Effects', 'false') 70 | end 71 | end) 72 | 73 | RegisterNetEvent('bMenu:server:setClouds', function(removeClouds) 74 | if removeClouds then 75 | TriggerClientEvent('bMenu:client:setClouds', -1, 0, 'removed') 76 | return 77 | end 78 | 79 | TriggerClientEvent('bMenu:client:setClouds', -1, math.random(), cloudTypes[math.random(#cloudTypes)]) 80 | end) 81 | 82 | --#endregion Events 83 | 84 | --#region Threads 85 | 86 | CreateThread(function() 87 | while true do 88 | if dynamicWeather then 89 | Wait(dynamicWeatherTimer * 60000) 90 | 91 | if currentWeather == 'XMAS' or currentWeather == 'HALLOWEEN' or currentWeather == 'NEUTRAL' then 92 | SetConvarReplicated('bMenu.Dynamic_Weather', 'false') 93 | dynamicWeather = false 94 | else 95 | if GetGameTimer() - lastWeatherChange > (dynamicWeatherTimer * 60000) then 96 | if currentWeather == 'RAIN' or currentWeather == 'THUNDER' then 97 | currentWeather = 'CLEARING' 98 | elseif currentWeather == 'CLEARING' then 99 | currentWeather = 'CLOUDS' 100 | else 101 | local rand = math.random(0, 20) 102 | if rand <= 5 then 103 | currentWeather = currentWeather == 'EXTRASUNNY' and 'CLEAR' or 'EXTRASUNNY' 104 | elseif rand >= 6 and rand <= 8 then 105 | currentWeather = currentWeather == 'SMOG' and 'FOGGY' or 'SMOG' 106 | elseif rand >= 9 and rand <= 14 then 107 | currentWeather = currentWeather == 'CLOUDS' and 'OVERCAST' or 'CLOUDS' 108 | elseif rand == 15 then 109 | currentWeather = currentWeather == 'OVERCAST' and 'THUNDER' or 'OVERCAST' 110 | elseif rand == 16 then 111 | currentWeather = currentWeather == 'CLOUDS' and 'EXTRASUNNY' or 'RAIN' 112 | else 113 | currentWeather = currentWeather == 'FOGGY' and 'SMOG' or 'FOGGY' 114 | end 115 | 116 | SetConvarReplicated('bMenu.Current_Weather', currentWeather) 117 | 118 | lastWeatherChange = GetGameTimer() 119 | end 120 | end 121 | end 122 | else 123 | Wait(5000) 124 | end 125 | end 126 | end) 127 | 128 | --#endregion Threads --------------------------------------------------------------------------------