Deleted the cache and batch event libraries from the codebase, including all related client, server, and shared files. Removed server-side particle library and associated client event handlers. Updated utility and clothing modules to remove cache dependencies. Improved utility logging and deprecated point functions with notices. Updated all locales to use a consistent 'PurchasedItem' message format and added Bulgarian (bg) translation. Bumped version to 0.13.1 in fxmanifest.
641 lines
20 KiB
Lua
641 lines
20 KiB
Lua
Utility = Utility or {}
|
|
local blipIDs = {}
|
|
local spawnedPeds = {}
|
|
local spawnedProps = {}
|
|
|
|
Locales = Locales or Require('modules/locales/shared.lua')
|
|
Point = Point or Require('lib/points/client/points.lua')
|
|
|
|
---Get the hash of a model (string or number)
|
|
---@param model string|number
|
|
---@return number
|
|
local function getModelHash(model)
|
|
if type(model) ~= 'number' then
|
|
return joaat(model)
|
|
end
|
|
return model
|
|
end
|
|
|
|
---Ensure a model is loaded into memory
|
|
---@param model string|number
|
|
---@return boolean, number
|
|
local function ensureModelLoaded(model)
|
|
local hash = getModelHash(model)
|
|
if HasModelLoaded(hash) then return true, hash end
|
|
if not IsModelValid(hash) and not IsModelInCdimage(hash) then return false, hash end
|
|
RequestModel(hash)
|
|
local count = 0
|
|
while not HasModelLoaded(hash) and count < 30000 do
|
|
Wait(0)
|
|
count = count + 1
|
|
end
|
|
return HasModelLoaded(hash), hash
|
|
end
|
|
|
|
---Create a prop with the given model and coordinates
|
|
---@param model string|number
|
|
---@param coords vector3
|
|
---@param heading number
|
|
---@param networked boolean
|
|
---@return number|nil
|
|
function Utility.CreateProp(model, coords, heading, networked)
|
|
local loaded, hash = ensureModelLoaded(model)
|
|
if not loaded then return nil, print("^6 community_bridge model was unable to load "..tostring(model).." ^0") end
|
|
local propEntity = CreateObject(hash, coords.x, coords.y, coords.z, networked, false, false)
|
|
SetEntityHeading(propEntity, heading)
|
|
SetModelAsNoLongerNeeded(hash)
|
|
spawnedProps[tostring(propEntity)] = true
|
|
return propEntity
|
|
end
|
|
|
|
---Get street and crossing names at given coordinates
|
|
---@param coords vector3
|
|
---@return string, string
|
|
function Utility.GetStreetNameAtCoords(coords)
|
|
local streetHash, crossingHash = GetStreetNameAtCoord(coords.x, coords.y, coords.z)
|
|
return GetStreetNameFromHashKey(streetHash), GetStreetNameFromHashKey(crossingHash)
|
|
end
|
|
|
|
---Create a vehicle with the given model and coordinates
|
|
---@param model string|number
|
|
---@param coords vector3
|
|
---@param heading number
|
|
---@param networked boolean
|
|
---@return number|nil, table
|
|
function Utility.CreateVehicle(model, coords, heading, networked)
|
|
local loaded, hash = ensureModelLoaded(model)
|
|
if not loaded then return nil, {}, print("^6 community_bridge model was unable to load "..tostring(model).." ^0") end
|
|
local vehicle = CreateVehicle(hash, coords.x, coords.y, coords.z, heading, networked, false)
|
|
SetVehicleHasBeenOwnedByPlayer(vehicle, true)
|
|
SetVehicleNeedsToBeHotwired(vehicle, false)
|
|
SetVehRadioStation(vehicle, "OFF")
|
|
SetModelAsNoLongerNeeded(hash)
|
|
return vehicle, {networkid = NetworkGetNetworkIdFromEntity(vehicle) or 0,coords = GetEntityCoords(vehicle),heading = GetEntityHeading(vehicle),}
|
|
end
|
|
|
|
---Create a ped with the given model and coordinates
|
|
---@param model string|number
|
|
---@param coords vector3
|
|
---@param heading number
|
|
---@param networked boolean
|
|
---@param settings table|nil
|
|
---@return number|nil
|
|
function Utility.CreatePed(model, coords, heading, networked, settings)
|
|
local loaded, hash = ensureModelLoaded(model)
|
|
if not loaded then return nil, print("^6 community_bridge model was unable to load "..tostring(model).." ^0") end
|
|
local spawnedEntity = CreatePed(0, hash, coords.x, coords.y, coords.z, heading, networked, false)
|
|
SetModelAsNoLongerNeeded(hash)
|
|
spawnedPeds[tostring(spawnedEntity)] = true
|
|
return spawnedEntity
|
|
end
|
|
|
|
---Show a busy spinner with the given text
|
|
---@param text string
|
|
---@return boolean
|
|
function Utility.StartBusySpinner(text)
|
|
AddTextEntry(text, text)
|
|
BeginTextCommandBusyString(text)
|
|
AddTextComponentSubstringPlayerName(text)
|
|
EndTextCommandBusyString(0)
|
|
return true
|
|
end
|
|
|
|
---Stop the busy spinner if active
|
|
---@return boolean
|
|
function Utility.StopBusySpinner()
|
|
if BusyspinnerIsOn() then
|
|
BusyspinnerOff()
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
---Create a blip at the given coordinates
|
|
---@param coords vector3
|
|
---@param sprite number
|
|
---@param color number
|
|
---@param scale number
|
|
---@param label string
|
|
---@param shortRange boolean
|
|
---@param displayType number
|
|
---@return number
|
|
function Utility.CreateBlip(coords, sprite, color, scale, label, shortRange, displayType)
|
|
local blip = AddBlipForCoord(coords.x, coords.y, coords.z)
|
|
SetBlipSprite(blip, sprite or 8)
|
|
SetBlipColour(blip, color or 3)
|
|
SetBlipScale(blip, scale or 0.8)
|
|
SetBlipDisplay(blip, displayType or 2)
|
|
SetBlipAsShortRange(blip, shortRange)
|
|
BeginTextCommandSetBlipName("STRING")
|
|
AddTextComponentSubstringPlayerName(label)
|
|
EndTextCommandSetBlipName(blip)
|
|
blipIDs[tostring(blip)] = blip
|
|
return blip
|
|
end
|
|
|
|
function Utility.CreateRadiusBlip(coords, radius, color, alpha, label, shortRange, displayType)
|
|
local blip = AddBlipForRadius(coords.x, coords.y, coords.z, radius)
|
|
SetBlipColour(blip, color or 3)
|
|
SetBlipAlpha(blip, alpha or 255)
|
|
SetBlipDisplay(blip, displayType or 2)
|
|
SetBlipAsShortRange(blip, shortRange)
|
|
AddTextEntry(label, label)
|
|
BeginTextCommandSetBlipName(label)
|
|
EndTextCommandSetBlipName(blip)
|
|
blipIDs[tostring(blip)] = blip
|
|
return blip
|
|
end
|
|
|
|
---Create a blip on the provided entity
|
|
---@param entity number
|
|
---@param sprite number
|
|
---@param color number
|
|
---@param scale number
|
|
---@param label string
|
|
---@param shortRange boolean
|
|
---@param displayType number
|
|
---@return number
|
|
function Utility.CreateEntityBlip(entity, sprite, color, scale, label, shortRange, displayType)
|
|
local blip = AddBlipForEntity(entity)
|
|
SetBlipSprite(blip, sprite or 8)
|
|
SetBlipColour(blip, color or 3)
|
|
SetBlipScale(blip, scale or 0.8)
|
|
SetBlipDisplay(blip, displayType or 2)
|
|
SetBlipAsShortRange(blip, shortRange)
|
|
ShowHeadingIndicatorOnBlip(blip, true)
|
|
AddTextEntry(label, label)
|
|
BeginTextCommandSetBlipName(label)
|
|
EndTextCommandSetBlipName(blip)
|
|
blipIDs[tostring(blip)] = blip
|
|
return blip
|
|
end
|
|
|
|
---Remove a blip if it exists
|
|
---@param blip number
|
|
---@return boolean
|
|
function Utility.RemoveBlip(blip)
|
|
if not blipIDs[tostring(blip)] then return false end
|
|
RemoveBlip(blipIDs[tostring(blip)])
|
|
blipIDs[tostring(blip)] = nil
|
|
return true
|
|
end
|
|
|
|
---Load a model into memory
|
|
---@param model string|number
|
|
---@return boolean, number
|
|
function Utility.LoadModel(model)
|
|
return ensureModelLoaded(model)
|
|
end
|
|
|
|
---Request an animation dictionary
|
|
---@param dict string
|
|
---@return boolean
|
|
function Utility.RequestAnimDict(dict)
|
|
if HasAnimDictLoaded(dict) then return true end
|
|
RequestAnimDict(dict)
|
|
local count = 0
|
|
while not HasAnimDictLoaded(dict) and count < 30000 do
|
|
Wait(0)
|
|
count = count + 1
|
|
end
|
|
return HasAnimDictLoaded(dict)
|
|
end
|
|
|
|
---Remove a ped if it exists
|
|
---@param entity number
|
|
---@return boolean
|
|
function Utility.RemovePed(entity)
|
|
if not spawnedPeds[tostring(entity)] then return false end
|
|
spawnedPeds[tostring(entity)] = nil
|
|
if not DoesEntityExist(entity) then return false end
|
|
SetEntityAsMissionEntity(entity, true, true)
|
|
DeleteEntity(entity)
|
|
return true
|
|
end
|
|
|
|
---Remove a ped if it exists
|
|
---@param entity number
|
|
---@return boolean
|
|
function Utility.RemoveProp(entity)
|
|
if not spawnedProps[tostring(entity)] then return false end
|
|
spawnedProps[tostring(entity)] = nil
|
|
if not DoesEntityExist(entity) then return false end
|
|
SetEntityAsMissionEntity(entity, true, true)
|
|
DeleteEntity(entity)
|
|
return true
|
|
end
|
|
|
|
---Show a native input menu and return the result
|
|
---@param text string
|
|
---@param length number
|
|
---@return string|boolean
|
|
function Utility.NativeInputMenu(text, length)
|
|
local maxLength = Math and Math.Clamp and Math.Clamp(length, 1, 50) or math.min(math.max(length or 10, 1), 50)
|
|
local menuText = text or 'enter text'
|
|
AddTextEntry(menuText, menuText)
|
|
DisplayOnscreenKeyboard(1, menuText, "", "", "", "", "", maxLength)
|
|
while (UpdateOnscreenKeyboard() == 0) do
|
|
DisableAllControlActions(0)
|
|
Wait(0)
|
|
end
|
|
if (GetOnscreenKeyboardResult()) then
|
|
return GetOnscreenKeyboardResult()
|
|
end
|
|
return false
|
|
end
|
|
|
|
---Get the skin data of a ped
|
|
---@param entity number
|
|
---@return table
|
|
function Utility.GetEntitySkinData(entity)
|
|
local skinData = { clothing = {}, props = {} }
|
|
for i = 0, 11 do
|
|
skinData.clothing[i] = { GetPedDrawableVariation(entity, i), GetPedTextureVariation(entity, i) }
|
|
end
|
|
for i = 0, 13 do
|
|
skinData.props[i] = { GetPedPropIndex(entity, i), GetPedPropTextureIndex(entity, i) }
|
|
end
|
|
return skinData
|
|
end
|
|
|
|
---Apply skin data to a ped
|
|
---@param entity number
|
|
---@param skinData table
|
|
---@return boolean
|
|
function Utility.SetEntitySkinData(entity, skinData)
|
|
for i = 0, 11 do
|
|
SetPedComponentVariation(entity, i, skinData.clothing[i][1], skinData.clothing[i][2], 0)
|
|
end
|
|
for i = 0, 13 do
|
|
SetPedPropIndex(entity, i, skinData.props[i][1], skinData.props[i][2], 0)
|
|
end
|
|
return true
|
|
end
|
|
|
|
---Reload the player's skin and remove attached objects
|
|
---@return boolean
|
|
function Utility.ReloadSkin()
|
|
local ped = PlayerPedId()
|
|
local skinData = Utility.GetEntitySkinData(ped)
|
|
Utility.SetEntitySkinData(ped, skinData)
|
|
local poolObject = GetGamePool("CObject")
|
|
for _, props in pairs(poolObject) do
|
|
if IsEntityAttachedToEntity(ped, props) then
|
|
SetEntityAsMissionEntity(props, true, true)
|
|
DeleteObject(props)
|
|
DeleteEntity(props)
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
---Show a native help text
|
|
---@param text string
|
|
---@param duration number
|
|
function Utility.HelpText(text, duration)
|
|
AddTextEntry(text, text)
|
|
BeginTextCommandDisplayHelp(text)
|
|
EndTextCommandDisplayHelp(0, false, true, duration or 5000)
|
|
end
|
|
|
|
---Show a floating help text in the world
|
|
---@param text string The text to show
|
|
---@param coords table The coords to show the message at
|
|
---@return nil
|
|
function Utility.FloatingHelpText(text, coords)
|
|
AddTextEntry("community_bridge_"..text, text)
|
|
SetFloatingHelpTextWorldPosition(1, coords.x, coords.y, coords.z)
|
|
SetFloatingHelpTextStyle(1, 1, 2, -1, 3, 0)
|
|
BeginTextCommandDisplayHelp("community_bridge_"..text)
|
|
EndTextCommandDisplayHelp(2, false, false, 100)
|
|
end
|
|
|
|
---Draw 3D help text in the world
|
|
---@param coords vector3
|
|
---@param text string
|
|
---@param scale number
|
|
function Utility.Draw3DHelpText(coords, text, scale)
|
|
local onScreen, x, y = GetScreenCoordFromWorldCoord(coords.x, coords.y, coords.z)
|
|
if not onScreen then return end
|
|
local lineCount = 0
|
|
local maxLineLen = 0
|
|
for line in string.gmatch(text, "[^\n]+") do
|
|
lineCount = lineCount + 1
|
|
maxLineLen = math.max(maxLineLen, string.len(line))
|
|
end
|
|
local widthFactor = maxLineLen * 0.012 * scale
|
|
local height = 0.06 * scale * lineCount
|
|
|
|
-- Set text properties
|
|
SetTextScale(scale or 0.35, scale or 0.35)
|
|
SetTextFont(4)
|
|
SetTextProportional(true)
|
|
SetTextColour(255, 255, 255, 215)
|
|
SetTextEntry("STRING")
|
|
SetTextCentre(true)
|
|
AddTextComponentString(text)
|
|
DrawText(x, y)
|
|
|
|
-- Draw background rectangle
|
|
DrawRect(x, y + height / 2, widthFactor + 0.015 + 0.006, height + 0.015 + 0.006, 10, 25, 47, 200)
|
|
DrawRect(x, y + height / 2, widthFactor + 0.015, height + 0.015, 17, 45, 78, 50)
|
|
end
|
|
|
|
function Utility.Draw3DHelpTextExt(coords, text, scale)
|
|
|
|
end
|
|
|
|
---Show a native notification
|
|
---@param text string
|
|
function Utility.NotifyText(text)
|
|
AddTextEntry(text, text)
|
|
SetNotificationTextEntry(text)
|
|
DrawNotification(false, true)
|
|
end
|
|
|
|
---Teleport the player to given coordinates
|
|
---@param coords vector3
|
|
---@param conditionFunction function|nil
|
|
---@param afterTeleportFunction function|nil
|
|
function Utility.TeleportPlayer(coords, conditionFunction, afterTeleportFunction)
|
|
local ped = PlayerPedId()
|
|
if conditionFunction ~= nil then
|
|
if not conditionFunction() then
|
|
return
|
|
end
|
|
end
|
|
DoScreenFadeOut(2500)
|
|
Wait(2500)
|
|
SetEntityCoords(ped, coords.x, coords.y, coords.z, false, false, false, false)
|
|
if coords.w then SetEntityHeading(ped, coords.w) end
|
|
FreezeEntityPosition(ped, true)
|
|
local count = 0
|
|
while not HasCollisionLoadedAroundEntity(ped) and count <= 30000 do
|
|
RequestCollisionAtCoord(coords.x, coords.y, coords.z)
|
|
Wait(0)
|
|
count = count + 1
|
|
end
|
|
FreezeEntityPosition(ped, false)
|
|
DoScreenFadeIn(1000)
|
|
if afterTeleportFunction ~= nil then
|
|
afterTeleportFunction()
|
|
end
|
|
end
|
|
|
|
---Get the hash from a model
|
|
---@param model string|number
|
|
---@return number
|
|
function Utility.GetEntityHashFromModel(model)
|
|
return getModelHash(model)
|
|
end
|
|
|
|
---Get the closest player to given coordinates
|
|
---@param coords vector3|nil
|
|
---@param distanceScope number|nil
|
|
---@param includeMe boolean|nil
|
|
---@return number, number, number
|
|
function Utility.GetClosestPlayer(coords, distanceScope, includeMe)
|
|
local players = GetActivePlayers()
|
|
local closestPlayer = 0
|
|
local selfPed = PlayerPedId()
|
|
local selfCoords = coords or GetEntityCoords(selfPed)
|
|
local closestDistance = distanceScope or 5
|
|
|
|
for _, player in ipairs(players) do
|
|
local playerPed = GetPlayerPed(player)
|
|
if includeMe or playerPed ~= selfPed then
|
|
local playerCoords = GetEntityCoords(playerPed)
|
|
local distance = #(selfCoords - playerCoords)
|
|
if closestDistance == -1 or distance < closestDistance then
|
|
closestPlayer = player
|
|
closestDistance = distance
|
|
end
|
|
end
|
|
end
|
|
|
|
return closestPlayer, closestDistance, GetPlayerServerId(closestPlayer)
|
|
end
|
|
|
|
---Get the closest vehicle to given coordinates
|
|
---@param coords vector3|nil
|
|
---@param distanceScope number|nil
|
|
---@param includePlayerVeh boolean|nil
|
|
---@return number|nil, vector3|nil, number|nil
|
|
function Utility.GetClosestVehicle(coords, distanceScope, includePlayerVeh)
|
|
local vehicleEntity = nil
|
|
local vehicleNetID = nil
|
|
local vehicleCoords = nil
|
|
local ped = PlayerPedId()
|
|
local selfCoords = coords or GetEntityCoords(ped)
|
|
local closestDistance = distanceScope or 5
|
|
local includeMyVeh = includePlayerVeh or false
|
|
local gamePoolVehicles = GetGamePool("CVehicle")
|
|
|
|
local playerVehicle = IsPedInAnyVehicle(ped, false) and GetVehiclePedIsIn(ped, false) or 0
|
|
|
|
for i = 1, #gamePoolVehicles do
|
|
local thisVehicle = gamePoolVehicles[i]
|
|
if DoesEntityExist(thisVehicle) and (includeMyVeh or thisVehicle ~= playerVehicle) then
|
|
local thisVehicleCoords = GetEntityCoords(thisVehicle)
|
|
local distance = #(selfCoords - thisVehicleCoords)
|
|
if closestDistance == -1 or distance < closestDistance then
|
|
vehicleEntity = thisVehicle
|
|
vehicleNetID = NetworkGetNetworkIdFromEntity(thisVehicle) or nil
|
|
vehicleCoords = thisVehicleCoords
|
|
closestDistance = distance
|
|
end
|
|
end
|
|
end
|
|
|
|
return vehicleEntity, vehicleCoords, vehicleNetID
|
|
end
|
|
|
|
-- Deprecated point functions (no changes) 12/14/25
|
|
function Utility.RegisterPoint(pointID, pointCoords, pointDistance, _onEnter, _onExit)
|
|
print("^6 community_bridge ^: ^3Utility.RegisterPoint is deprecated. Please use Point.Register instead. ^0")
|
|
return Point.Register(pointID, pointCoords, pointDistance, nil, _onEnter, _onExit)
|
|
end
|
|
|
|
function Utility.GetPointById(pointID)
|
|
print("^6 community_bridge ^: ^3Utility.GetPointById is deprecated. Please use Point.Get instead. ^0")
|
|
return Point.Get(pointID)
|
|
end
|
|
|
|
function Utility.GetActivePoints()
|
|
print("^6 community_bridge ^: ^3Utility.GetActivePoints is deprecated. Please use Point.GetAll instead. ^0")
|
|
return Point.GetAll()
|
|
end
|
|
|
|
function Utility.RemovePoint(pointID)
|
|
print("^6 community_bridge ^: ^3Utility.RemovePoint is deprecated. Please use Point.Remove instead. ^0")
|
|
return Point.Remove(pointID)
|
|
end
|
|
|
|
---Simple switch-case function
|
|
---@generic T
|
|
---@param value T The value to match against the cases
|
|
---@param cases table<T|false, fun(): any> Table with case functions and an optional default (false key)
|
|
---@return any|false result The return value of the matched case function, or false if none matched
|
|
function Utility.Switch(value, cases)
|
|
local caseFunc = cases[value] or cases[false]
|
|
|
|
if caseFunc and type(caseFunc) == "function" then
|
|
local ok, result = pcall(caseFunc)
|
|
return ok and result or false
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function Utility.CopyToClipboard(text)
|
|
if not text then return false end
|
|
if type(text) ~= "string" then
|
|
text = json.encode(text, { indent = true })
|
|
end
|
|
SendNUIMessage({
|
|
type = "copytoclipboard",
|
|
text = text
|
|
})
|
|
local message = Locales and Locales.Locale("clipboard.copy")
|
|
--TriggerEvent('community_bridge:Client:Notify', message, 'success')
|
|
return true
|
|
end
|
|
|
|
--- Pattern match-like function
|
|
---@generic T
|
|
---@param value T The value to match
|
|
---@param patterns table<T|fun(T):boolean|false, fun(): any> A list of matchers and their handlers
|
|
---@return any|false result The result of the first matched case, or false if none
|
|
function Utility.Match(value, patterns)
|
|
for pattern, handler in pairs(patterns) do
|
|
if type(pattern) == "function" then
|
|
local ok, matched = pcall(pattern, value)
|
|
if ok and matched then
|
|
local success, result = pcall(handler)
|
|
return success and result or false
|
|
end
|
|
elseif pattern == value then
|
|
local success, result = pcall(handler)
|
|
return success and result or false
|
|
end
|
|
end
|
|
|
|
if patterns[false] then
|
|
local ok, result = pcall(patterns[false])
|
|
return ok and result or false
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
---Get zone name at coordinates
|
|
---@param coords vector3
|
|
---@return string
|
|
function Utility.GetZoneName(coords)
|
|
local zoneHash = GetNameOfZone(coords.x, coords.y, coords.z)
|
|
return GetLabelText(zoneHash)
|
|
end
|
|
|
|
local SpecialKeyCodes = {
|
|
['b_116'] = 'Scroll Up',
|
|
['b_115'] = 'Scroll Down',
|
|
['b_100'] = 'LMB',
|
|
['b_101'] = 'RMB',
|
|
['b_102'] = 'MMB',
|
|
['b_103'] = 'Extra 1',
|
|
['b_104'] = 'Extra 2',
|
|
['b_105'] = 'Extra 3',
|
|
['b_106'] = 'Extra 4',
|
|
['b_107'] = 'Extra 5',
|
|
['b_108'] = 'Extra 6',
|
|
['b_109'] = 'Extra 7',
|
|
['b_110'] = 'Extra 8',
|
|
['b_1015'] = 'AltLeft',
|
|
['b_1000'] = 'ShiftLeft',
|
|
['b_2000'] = 'Space',
|
|
['b_1013'] = 'ControlLeft',
|
|
['b_1002'] = 'Tab',
|
|
['b_1014'] = 'ControlRight',
|
|
['b_140'] = 'Numpad4',
|
|
['b_142'] = 'Numpad6',
|
|
['b_144'] = 'Numpad8',
|
|
['b_141'] = 'Numpad5',
|
|
['b_143'] = 'Numpad7',
|
|
['b_145'] = 'Numpad9',
|
|
['b_200'] = 'Insert',
|
|
['b_1012'] = 'CapsLock',
|
|
['b_170'] = 'F1',
|
|
['b_171'] = 'F2',
|
|
['b_172'] = 'F3',
|
|
['b_173'] = 'F4',
|
|
['b_174'] = 'F5',
|
|
['b_175'] = 'F6',
|
|
['b_176'] = 'F7',
|
|
['b_177'] = 'F8',
|
|
['b_178'] = 'F9',
|
|
['b_179'] = 'F10',
|
|
['b_180'] = 'F11',
|
|
['b_181'] = 'F12',
|
|
['b_194'] = 'ArrowUp',
|
|
['b_195'] = 'ArrowDown',
|
|
['b_196'] = 'ArrowLeft',
|
|
['b_197'] = 'ArrowRight',
|
|
['b_1003'] = 'Enter',
|
|
['b_1004'] = 'Backspace',
|
|
['b_198'] = 'Delete',
|
|
['b_199'] = 'Escape',
|
|
['b_1009'] = 'PageUp',
|
|
['b_1010'] = 'PageDown',
|
|
['b_1008'] = 'Home',
|
|
['b_131'] = 'NumpadAdd',
|
|
['b_130'] = 'NumpadSubstract',
|
|
['b_211'] = 'Insert',
|
|
['b_210'] = 'Delete',
|
|
['b_212'] = 'End',
|
|
['b_1055'] = 'Home',
|
|
['b_1056'] = 'PageUp',
|
|
}
|
|
|
|
local function translateKey(key)
|
|
if string.find(key, "t_") then
|
|
return string.gsub(key, "t_", "")
|
|
elseif SpecialKeyCodes[key] then
|
|
return SpecialKeyCodes[key]
|
|
else
|
|
return key
|
|
end
|
|
end
|
|
|
|
function Utility.GetCommandKey(commandName)
|
|
local hash = GetHashKey(commandName) | 0x80000000
|
|
local button = GetControlInstructionalButton(2, hash, true)
|
|
if not button or button == "" or button == "NULL" then
|
|
hash = GetHashKey(commandName)
|
|
button = GetControlInstructionalButton(2, hash, true)
|
|
end
|
|
|
|
return translateKey(button)
|
|
end
|
|
|
|
AddEventHandler('onResourceStop', function(resource)
|
|
if resource ~= GetCurrentResourceName() then return end
|
|
for _, blip in pairs(blipIDs) do
|
|
if blip and DoesBlipExist(tonumber(blip)) then
|
|
RemoveBlip(tonumber(blip))
|
|
end
|
|
end
|
|
for _, ped in pairs(spawnedPeds) do
|
|
if ped and DoesEntityExist(tonumber(ped)) then
|
|
DeleteEntity(tonumber(ped))
|
|
end
|
|
end
|
|
for _, prop in pairs(spawnedProps) do
|
|
if prop and DoesEntityExist(tonumber(prop)) then
|
|
DeleteEntity(tonumber(prop))
|
|
end
|
|
end
|
|
end)
|
|
|
|
exports('Utility', Utility)
|
|
return Utility
|