mirror of
https://github.com/CommunityOx/ox_lib.git
synced 2026-08-17 15:06:02 +01:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae16b78bc4 | ||
|
|
3ea020cc50 | ||
|
|
db0bd16f0f | ||
|
|
01935adf8e | ||
|
|
220c0c91ec | ||
|
|
855e1e4a36 | ||
|
|
a2f6b9a9e9 | ||
|
|
13a24baee4 | ||
|
|
df7a6b9c21 | ||
|
|
faa9dda57f | ||
|
|
619ecc9e55 | ||
|
|
56c0a91022 | ||
|
|
d6417b9a84 | ||
|
|
ac9625d33a | ||
|
|
2ab9114d2f | ||
|
|
88c68c7068 | ||
|
|
1e178180bb | ||
|
|
fef441c456 | ||
|
|
85a3879a03 | ||
|
|
65813e75e6 | ||
|
|
a9648fb45f |
@@ -6,7 +6,7 @@ rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aw
|
|||||||
|
|
||||||
name 'ox_lib'
|
name 'ox_lib'
|
||||||
author 'Overextended'
|
author 'Overextended'
|
||||||
version '3.31.0'
|
version '3.32.2'
|
||||||
license 'LGPL-3.0-or-later'
|
license 'LGPL-3.0-or-later'
|
||||||
repository 'https://github.com/communityox/ox_lib'
|
repository 'https://github.com/communityox/ox_lib'
|
||||||
description 'A library of shared functions to utilise in other resources.'
|
description 'A library of shared functions to utilise in other resources.'
|
||||||
|
|||||||
@@ -84,6 +84,29 @@ function lib.dui:sendMessage(message)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param x number
|
||||||
|
---@param y number
|
||||||
|
function lib.dui:sendMouseMove(x, y)
|
||||||
|
SendDuiMouseMove(self.duiObject, x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param button 'left' | 'middle' | 'right'
|
||||||
|
function lib.dui:sendMouseDown(button)
|
||||||
|
SendDuiMouseDown(self.duiObject, button)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param button 'left' | 'middle' | 'right'
|
||||||
|
function lib.dui:sendMouseUp(button)
|
||||||
|
SendDuiMouseUp(self.duiObject, button)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param deltaX number
|
||||||
|
---@param deltaY number
|
||||||
|
function lib.dui:sendMouseWheel(deltaX, deltaY)
|
||||||
|
SendDuiMouseWheel(self.duiObject, deltaY, deltaX)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
AddEventHandler('onResourceStop', function(resourceName)
|
AddEventHandler('onResourceStop', function(resourceName)
|
||||||
if cache.resource ~= resourceName then return end
|
if cache.resource ~= resourceName then return end
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ end
|
|||||||
|
|
||||||
if service == 'fivemanage' then
|
if service == 'fivemanage' then
|
||||||
local key = GetConvar('fivemanage:key', '')
|
local key = GetConvar('fivemanage:key', '')
|
||||||
|
local dataset = GetConvar('fivemanage:dataset', '')
|
||||||
|
|
||||||
if key ~= '' then
|
if key ~= '' then
|
||||||
local endpoint = 'https://api.fivemanage.com/api/logs/batch'
|
local endpoint = 'https://api.fivemanage.com/api/logs/batch'
|
||||||
@@ -101,9 +102,13 @@ if service == 'fivemanage' then
|
|||||||
local headers = {
|
local headers = {
|
||||||
['Content-Type'] = 'application/json',
|
['Content-Type'] = 'application/json',
|
||||||
['Authorization'] = key,
|
['Authorization'] = key,
|
||||||
['User-Agent'] = 'ox_lib'
|
['User-Agent'] = 'ox_lib',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dataset ~= "" then
|
||||||
|
headers['X-Fivemanage-Dataset'] = dataset
|
||||||
|
end
|
||||||
|
|
||||||
function lib.logger(source, event, message, ...)
|
function lib.logger(source, event, message, ...)
|
||||||
if not buffer then
|
if not buffer then
|
||||||
buffer = {}
|
buffer = {}
|
||||||
@@ -123,17 +128,43 @@ if service == 'fivemanage' then
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local metadata = {
|
||||||
|
hostname = hostname,
|
||||||
|
service = event,
|
||||||
|
source = source,
|
||||||
|
}
|
||||||
|
|
||||||
|
local playerTags = formatTags(source, nil)
|
||||||
|
if playerTags and type(playerTags) == 'string' then
|
||||||
|
local tempTable = { string.strsplit(',', playerTags) }
|
||||||
|
for _, v in pairs(tempTable) do
|
||||||
|
local key, value = string.strsplit(':', v)
|
||||||
|
if key and value then
|
||||||
|
metadata[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local args = { ... }
|
||||||
|
for _, arg in pairs(args) do
|
||||||
|
if type(arg) == 'table' then
|
||||||
|
for k, v in pairs(arg) do
|
||||||
|
metadata[k] = v
|
||||||
|
end
|
||||||
|
elseif type(arg) == 'string' then
|
||||||
|
local key, value = string.strsplit(':', arg)
|
||||||
|
if key and value then
|
||||||
|
metadata[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
bufferSize += 1
|
bufferSize += 1
|
||||||
buffer[bufferSize] = {
|
buffer[bufferSize] = {
|
||||||
level = "info",
|
level = "info",
|
||||||
message = message,
|
message = message,
|
||||||
resource = cache.resource,
|
resource = cache.resource,
|
||||||
metadata = {
|
metadata = metadata,
|
||||||
hostname = hostname,
|
|
||||||
service = event,
|
|
||||||
source = source,
|
|
||||||
tags = formatTags(source, ... and string.strjoin(',', string.tostringall(...)) or nil),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -39,10 +39,14 @@ local function removePoint(self)
|
|||||||
points[self.id] = nil
|
points[self.id] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function hasRemovePoint(entry)
|
||||||
|
return entry.remove == removePoint
|
||||||
|
end
|
||||||
|
|
||||||
CreateThread(function()
|
CreateThread(function()
|
||||||
while true do
|
while true do
|
||||||
local coords = GetEntityCoords(cache.ped)
|
local coords = GetEntityCoords(cache.ped)
|
||||||
local newPoints = lib.grid.getNearbyEntries(coords, function(entry) return entry.remove == removePoint end) --[[@as CPoint[] ]]
|
local newPoints = lib.grid.getNearbyEntries(coords, hasRemovePoint) --[[@as CPoint[] ]]
|
||||||
local cellX, cellY = lib.grid.getCellPosition(coords)
|
local cellX, cellY = lib.grid.getCellPosition(coords)
|
||||||
cache.coords = coords
|
cache.coords = coords
|
||||||
closestPoint = nil
|
closestPoint = nil
|
||||||
@@ -89,10 +93,12 @@ CreateThread(function()
|
|||||||
nearbyCount += 1
|
nearbyCount += 1
|
||||||
nearbyPoints[nearbyCount] = point
|
nearbyPoints[nearbyCount] = point
|
||||||
|
|
||||||
if point.onEnter and not point.inside then
|
if not point.inside then
|
||||||
point.inside = true
|
point.inside = true
|
||||||
|
if point.onEnter then
|
||||||
point:onEnter()
|
point:onEnter()
|
||||||
end
|
end
|
||||||
|
end
|
||||||
elseif point.currentDistance then
|
elseif point.currentDistance then
|
||||||
if point.onExit then point:onExit() end
|
if point.onExit then point:onExit() end
|
||||||
|
|
||||||
|
|||||||
175
imports/selector/shared.lua
Normal file
175
imports/selector/shared.lua
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
---@alias OxSelectorItem {[1]: number, [2]: any}
|
||||||
|
---@alias OxSelectorSet OxSelectorItem[]
|
||||||
|
|
||||||
|
---@class OxSelector: OxClass
|
||||||
|
---@field private sets OxSelectorSet | table<string, OxSelectorItem[]>
|
||||||
|
---@field private totalWeights table<string, number>
|
||||||
|
local OxSelector = lib.class("OxSelector")
|
||||||
|
|
||||||
|
local DEFAULT_SET = 'default'
|
||||||
|
local deepClone = lib.table.deepclone
|
||||||
|
|
||||||
|
|
||||||
|
local function calculateTotalWeight(set)
|
||||||
|
local total = 0
|
||||||
|
for i = 1, #set do
|
||||||
|
local item = set[i]
|
||||||
|
assert(type(item) == "table", "Each OxSelectorItem must be a table")
|
||||||
|
local weight = item[1]
|
||||||
|
assert(type(weight) == "number" and weight >= 0, "weight must be 0 or more")
|
||||||
|
total += weight
|
||||||
|
end
|
||||||
|
return total
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@param sets OxSelectorSet | table<string, OxSelectorItem[]>
|
||||||
|
function OxSelector:constructor(sets)
|
||||||
|
if type(sets) ~= "table" then
|
||||||
|
lib.print.error("Invalid sets provided to OxSelector constructor")
|
||||||
|
end
|
||||||
|
|
||||||
|
if lib.table.type(sets) == "array" then
|
||||||
|
sets = { [DEFAULT_SET] = sets }
|
||||||
|
end
|
||||||
|
|
||||||
|
self.private.totalWeights = {}
|
||||||
|
self.private.sets = {}
|
||||||
|
|
||||||
|
for setName, set in pairs(sets) do
|
||||||
|
assert(type(set) == "table" and lib.table.type(set) == "array", "Each set must be an array of OxSelectorItem")
|
||||||
|
assert(#set > 0, "Each set must contain at least one OxSelectorItem")
|
||||||
|
|
||||||
|
self.private.totalWeights[setName] = calculateTotalWeight(set)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.private.sets = sets
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a random non-weighted item from a specific set
|
||||||
|
---@param setName? string
|
||||||
|
---@return OxSelectorItem?
|
||||||
|
function OxSelector:getRandom(setName)
|
||||||
|
local set = (setName and self.private.sets[setName]) or self.private.sets[DEFAULT_SET]
|
||||||
|
if not set then return nil end
|
||||||
|
local item = set[math.random(#set)][2]
|
||||||
|
|
||||||
|
return type(item) == "table" and deepClone(item) or item
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a random weighted item from a specific set
|
||||||
|
---@param setName? string
|
||||||
|
---@return OxSelectorItem?
|
||||||
|
function OxSelector:getRandomWeighted(setName)
|
||||||
|
local set = (setName and self.private.sets[setName]) or self.private.sets[DEFAULT_SET]
|
||||||
|
if not set then return nil end
|
||||||
|
|
||||||
|
local totalWeight = self.private.totalWeights[setName or DEFAULT_SET]
|
||||||
|
if totalWeight == 0 then return nil end
|
||||||
|
|
||||||
|
local randomWeight = math.random() * totalWeight
|
||||||
|
local cumulativeWeight = 0
|
||||||
|
|
||||||
|
for i = 1, #set do
|
||||||
|
cumulativeWeight = cumulativeWeight + set[i][1]
|
||||||
|
if randomWeight <= cumulativeWeight then
|
||||||
|
local item = set[i][2]
|
||||||
|
return type(item) == "table" and deepClone(item) or item
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- get multiple non-weighted random items from a specific set
|
||||||
|
---@param setName? string
|
||||||
|
---@param count number
|
||||||
|
---@return OxSelectorItem[]
|
||||||
|
function OxSelector:getRandomAmount(setName, count)
|
||||||
|
assert(type(count) == "number" and count > 0, "Count must be a positive number")
|
||||||
|
local items = {}
|
||||||
|
for _ = 1, count do
|
||||||
|
local item = self:getRandom(setName)
|
||||||
|
if item then
|
||||||
|
table.insert(items, item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return items
|
||||||
|
end
|
||||||
|
|
||||||
|
--- get multiple weighted random items from a specific set
|
||||||
|
---@param setName? string
|
||||||
|
---@param count number
|
||||||
|
---@return OxSelectorItem[]
|
||||||
|
function OxSelector:getRandomWeightedAmount(setName, count)
|
||||||
|
assert(type(count) == "number" and count > 0, "Count must be a positive number")
|
||||||
|
local items = {}
|
||||||
|
for _ = 1, count do
|
||||||
|
local item = self:getRandomWeighted(setName)
|
||||||
|
if item then
|
||||||
|
table.insert(items, item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return items
|
||||||
|
end
|
||||||
|
|
||||||
|
--- get all items from a specific set
|
||||||
|
---@param setName? string
|
||||||
|
---@return OxSelectorItem[]
|
||||||
|
function OxSelector:getSet(setName)
|
||||||
|
return deepClone((setName and self.private.sets[setName]) or self.private.sets[DEFAULT_SET])
|
||||||
|
end
|
||||||
|
|
||||||
|
--- get all sets
|
||||||
|
---@return table<string, OxSelectorItem[]>
|
||||||
|
function OxSelector:getAllSets()
|
||||||
|
return deepClone(self.private.sets)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- add a new set
|
||||||
|
---@param setName string
|
||||||
|
---@param items OxSelectorItem[]
|
||||||
|
function OxSelector:addSet(setName, items)
|
||||||
|
assert(type(setName) == "string", "setName must be a string")
|
||||||
|
|
||||||
|
if self.private.sets[setName] then
|
||||||
|
lib.print.error("Selector set '" .. setName .. "' already exists.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(type(items) == "table" and lib.table.type(items) == "array", "items must be an array")
|
||||||
|
assert(#items > 0, "set must contain at least one OxSelectorItem")
|
||||||
|
|
||||||
|
self.private.totalWeights[setName] = calculateTotalWeight(items)
|
||||||
|
self.private.sets[setName] = items
|
||||||
|
end
|
||||||
|
|
||||||
|
--- update an existing set
|
||||||
|
---@param setName string
|
||||||
|
---@param newItems OxSelectorItem[]
|
||||||
|
function OxSelector:updateSet(setName, newItems)
|
||||||
|
assert(type(setName) == "string", "setName must be a string")
|
||||||
|
|
||||||
|
if not self.private.sets[setName] then
|
||||||
|
lib.print.error("Selector set '" .. setName .. "' does not exist.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(type(newItems) == "table" and lib.table.type(newItems) == "array", "newItems must be an array")
|
||||||
|
assert(#newItems > 0, "set must contain at least one OxSelectorItem")
|
||||||
|
|
||||||
|
self.private.totalWeights[setName] = calculateTotalWeight(newItems)
|
||||||
|
self.private.sets[setName] = newItems
|
||||||
|
end
|
||||||
|
|
||||||
|
--- remove a set
|
||||||
|
---@param setName string
|
||||||
|
function OxSelector:removeSet(setName)
|
||||||
|
assert(type(setName) == "string", "setName must be a string")
|
||||||
|
|
||||||
|
self.private.totalWeights[setName] = nil
|
||||||
|
self.private.sets[setName] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
lib.selector = OxSelector
|
||||||
|
return lib.selector
|
||||||
@@ -47,13 +47,10 @@ end
|
|||||||
---@return boolean
|
---@return boolean
|
||||||
---Compares if two values are equal, iterating over tables and matching both keys and values.
|
---Compares if two values are equal, iterating over tables and matching both keys and values.
|
||||||
local function table_matches(t1, t2)
|
local function table_matches(t1, t2)
|
||||||
local tabletype1 = table.type(t1)
|
local type1, type2 = type(t1), type(t2)
|
||||||
|
|
||||||
if not tabletype1 then return t1 == t2 end
|
if type1 ~= type2 then return false end
|
||||||
|
if type1 ~= 'table' and type2 ~= 'table' then return t1 == t2 end
|
||||||
if tabletype1 ~= table.type(t2) or (tabletype1 == 'array' and #t1 ~= #t2) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
for k, v1 in pairs(t1) do
|
for k, v1 in pairs(t1) do
|
||||||
local v2 = t2[k]
|
local v2 = t2[k]
|
||||||
@@ -123,11 +120,24 @@ local function shuffle(tbl)
|
|||||||
return tbl
|
return tbl
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@param tbl table
|
||||||
|
---@param fn function(value: any, key: any): any
|
||||||
|
---@return table
|
||||||
|
local function map(tbl, fn)
|
||||||
|
local result = {}
|
||||||
|
for k, v in pairs(tbl) do
|
||||||
|
result[k] = fn(v, k)
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
table.contains = contains
|
table.contains = contains
|
||||||
table.matches = table_matches
|
table.matches = table_matches
|
||||||
table.deepclone = table_deepclone
|
table.deepclone = table_deepclone
|
||||||
table.merge = table_merge
|
table.merge = table_merge
|
||||||
table.shuffle = shuffle
|
table.shuffle = shuffle
|
||||||
|
table.map = map
|
||||||
|
|
||||||
local frozenNewIndex = function(self) error(('cannot set values on a frozen table (%s)'):format(self), 2) end
|
local frozenNewIndex = function(self) error(('cannot set values on a frozen table (%s)'):format(self), 2) end
|
||||||
local _rawset = rawset
|
local _rawset = rawset
|
||||||
|
|||||||
@@ -345,6 +345,8 @@ local function setZone(data)
|
|||||||
data.contains = data.contains or contains
|
data.contains = data.contains or contains
|
||||||
|
|
||||||
if lib.context == 'client' then
|
if lib.context == 'client' then
|
||||||
|
local coords = cache.coords or GetEntityCoords(cache.ped)
|
||||||
|
data.distance = #(data.coords - coords)
|
||||||
data.setDebug = setDebug
|
data.setDebug = setDebug
|
||||||
|
|
||||||
if data.debug then
|
if data.debug then
|
||||||
|
|||||||
@@ -7,27 +7,27 @@
|
|||||||
"confirm": "Confirmer",
|
"confirm": "Confirmer",
|
||||||
"more": "Plus...",
|
"more": "Plus...",
|
||||||
"settings": {
|
"settings": {
|
||||||
"locale": "Change locale",
|
"locale": "Changer la langue",
|
||||||
"locale_description": "Current language: ${language} (%s)",
|
"locale_description": "Langue actuelle : ${language} (%s)",
|
||||||
"notification_audio": "Notification audio",
|
"notification_audio": "Audio des notifications",
|
||||||
"notification_position": "Notification position"
|
"notification_position": "Position des notifications"
|
||||||
},
|
},
|
||||||
"position": {
|
"position": {
|
||||||
"bottom": "Bottom",
|
"bottom": "Bas",
|
||||||
"bottom-left": "Bottom-left",
|
"bottom-left": "Bas-gauche",
|
||||||
"bottom-right": "Bottom-right",
|
"bottom-right": "Bas-droite",
|
||||||
"center-left": "Center-left",
|
"center-left": "Centre-gauche",
|
||||||
"center-right": "Center-right",
|
"center-right": "Centre-droite",
|
||||||
"top": "Top",
|
"top": "Haut",
|
||||||
"top-left": "Top-left",
|
"top-left": "Haut-gauche",
|
||||||
"top-right": "Top-right"
|
"top-right": "Haut-droite"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"open_radial_menu": "Open radial menu",
|
"open_radial_menu": "Ouvrir le menu radial",
|
||||||
"cancel_progress": "Cancel current progress bar",
|
"cancel_progress": "Annuler la barre de progression actuelle",
|
||||||
"txadmin_announcement": "Server announcement by %s",
|
"txadmin_announcement": "Annonce serveur par %s",
|
||||||
"txadmin_dm": "Direct Message from %s",
|
"txadmin_dm": "Message privé de %s",
|
||||||
"txadmin_warn": "You have been warned by %s",
|
"txadmin_warn": "Vous avez été averti par %s",
|
||||||
"txadmin_warn_content": "%s \nAction ID: %s",
|
"txadmin_warn_content": "%s\nID de l'action : %s",
|
||||||
"txadmin_scheduledrestart": "Scheduled Restart"
|
"txadmin_scheduledrestart": "Redémarrage programmé"
|
||||||
}
|
}
|
||||||
|
|||||||
120
package/client/resource/addKeybind/index.ts
Normal file
120
package/client/resource/addKeybind/index.ts
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
interface CKeybind extends KeybindProps {
|
||||||
|
currentKey: string;
|
||||||
|
disabled: boolean;
|
||||||
|
isPressed: boolean;
|
||||||
|
hash: number;
|
||||||
|
getCurrentKey(): string;
|
||||||
|
isControlPressed(): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KeybindProps {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
defaultMapper?: string;
|
||||||
|
defaultKey?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
disable?(this: CKeybind, toggle: boolean): void;
|
||||||
|
onPressed?(this: CKeybind): void;
|
||||||
|
onReleased?(this: CKeybind): void;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const keybinds: Record<string, CKeybind> = {};
|
||||||
|
|
||||||
|
class Keybind implements CKeybind {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
defaultMapper?: string;
|
||||||
|
defaultKey?: string;
|
||||||
|
onPressed?: (this: CKeybind) => void;
|
||||||
|
onReleased?: (this: CKeybind) => void;
|
||||||
|
secondaryKey?: string;
|
||||||
|
secondaryMapper?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
|
||||||
|
disabled: boolean = false;
|
||||||
|
isPressed: boolean = false;
|
||||||
|
hash: number;
|
||||||
|
|
||||||
|
constructor(data: KeybindProps) {
|
||||||
|
this.name = data.name;
|
||||||
|
this.description = data.description;
|
||||||
|
this.defaultMapper = data.defaultMapper ?? "keyboard";
|
||||||
|
this.defaultKey = data.defaultKey ?? "";
|
||||||
|
this.secondaryKey = data.secondaryKey;
|
||||||
|
this.secondaryMapper = data.secondaryMapper;
|
||||||
|
|
||||||
|
if (typeof data.disabled === "boolean") this.disabled = data.disabled;
|
||||||
|
this.onPressed = data.onPressed;
|
||||||
|
this.onReleased = data.onReleased;
|
||||||
|
|
||||||
|
this.hash = GetHashKey("+" + this.name) | 0x80000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
get currentKey(): string {
|
||||||
|
return this.getCurrentKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentKey(): string {
|
||||||
|
const label = GetControlInstructionalButton(0, this.hash, true);
|
||||||
|
return label.substring(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
isControlPressed(): boolean {
|
||||||
|
return this.isPressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
disable(toggle: boolean): void {
|
||||||
|
this.disabled = toggle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function addKeybind(data: KeybindProps): CKeybind {
|
||||||
|
const kb = new Keybind(data);
|
||||||
|
|
||||||
|
keybinds[kb.name] = kb;
|
||||||
|
|
||||||
|
RegisterCommand("+" + kb.name, () => {
|
||||||
|
if (kb.disabled || IsPauseMenuActive()) return;
|
||||||
|
kb.isPressed = true;
|
||||||
|
kb.onPressed?.call(kb);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
RegisterCommand("-" + kb.name, () => {
|
||||||
|
if (kb.disabled || IsPauseMenuActive()) return;
|
||||||
|
kb.isPressed = false;
|
||||||
|
kb.onReleased?.call(kb);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
RegisterKeyMapping(
|
||||||
|
"+" + kb.name,
|
||||||
|
kb.description,
|
||||||
|
kb.defaultMapper ?? "keyboard",
|
||||||
|
kb.defaultKey ?? ""
|
||||||
|
);
|
||||||
|
|
||||||
|
if (kb.secondaryKey) {
|
||||||
|
RegisterKeyMapping(
|
||||||
|
"~!+" + kb.name,
|
||||||
|
kb.description,
|
||||||
|
kb.secondaryMapper ?? kb.defaultMapper ?? "keyboard",
|
||||||
|
kb.secondaryKey
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
emit("chat:removeSuggestion", `/+${kb.name}`);
|
||||||
|
emit("chat:removeSuggestion", `/-${kb.name}`);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
return kb;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getKeybind(name: string): CKeybind | undefined {
|
||||||
|
return keybinds[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAllKeybinds(): Readonly<Record<string, CKeybind>> {
|
||||||
|
return keybinds;
|
||||||
|
}
|
||||||
@@ -14,3 +14,4 @@ export * from './vehicleProperties';
|
|||||||
export * from './callback';
|
export * from './callback';
|
||||||
export * from './points';
|
export * from './points';
|
||||||
export * from './dui';
|
export * from './dui';
|
||||||
|
export * from './addKeybind';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@communityox/ox_lib",
|
"name": "@communityox/ox_lib",
|
||||||
"author": "Overextended",
|
"author": "Overextended",
|
||||||
"version": "3.31.0",
|
"version": "3.32.2",
|
||||||
"description": "JS/TS wrapper for ox_lib exports",
|
"description": "JS/TS wrapper for ox_lib exports",
|
||||||
"main": "./shared/index.js",
|
"main": "./shared/index.js",
|
||||||
"types": "./shared/index.d.ts",
|
"types": "./shared/index.d.ts",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ local DisableControlAction = DisableControlAction
|
|||||||
local DisablePlayerFiring = DisablePlayerFiring
|
local DisablePlayerFiring = DisablePlayerFiring
|
||||||
local playerState = LocalPlayer.state
|
local playerState = LocalPlayer.state
|
||||||
local createdProps = {}
|
local createdProps = {}
|
||||||
|
local maxProps = GetConvarInt('ox:progressPropLimit', 2)
|
||||||
|
|
||||||
---@class ProgressPropProps
|
---@class ProgressPropProps
|
||||||
---@field model string
|
---@field model string
|
||||||
@@ -34,13 +35,16 @@ local createdProps = {}
|
|||||||
---@field disable? { move?: boolean, sprint?: boolean, car?: boolean, combat?: boolean, mouse?: boolean }
|
---@field disable? { move?: boolean, sprint?: boolean, car?: boolean, combat?: boolean, mouse?: boolean }
|
||||||
|
|
||||||
local function createProp(ped, prop)
|
local function createProp(ped, prop)
|
||||||
lib.requestModel(prop.model)
|
local ok, result = pcall(lib.requestModel, prop.model)
|
||||||
|
|
||||||
|
if not ok then return lib.print.error(result) end
|
||||||
|
|
||||||
local coords = GetEntityCoords(ped)
|
local coords = GetEntityCoords(ped)
|
||||||
local object = CreateObject(prop.model, coords.x, coords.y, coords.z, false, false, false)
|
local object = CreateObject(result, coords.x, coords.y, coords.z, false, false, false)
|
||||||
|
|
||||||
AttachEntityToEntity(object, ped, GetPedBoneIndex(ped, prop.bone or 60309), prop.pos.x, prop.pos.y, prop.pos.z, prop.rot.x, prop.rot.y, prop.rot.z, true,
|
AttachEntityToEntity(object, ped, GetPedBoneIndex(ped, prop.bone or 60309), prop.pos.x, prop.pos.y, prop.pos.z, prop.rot.x, prop.rot.y, prop.rot.z, true,
|
||||||
true, false, true, prop.rotOrder or 0, true)
|
true, false, true, prop.rotOrder or 0, true)
|
||||||
SetModelAsNoLongerNeeded(prop.model)
|
SetModelAsNoLongerNeeded(result)
|
||||||
|
|
||||||
return object
|
return object
|
||||||
end
|
end
|
||||||
@@ -90,7 +94,7 @@ local function startProgress(data)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if data.prop then
|
if data.prop then
|
||||||
playerState:set('lib:progressProps', data.prop, true)
|
TriggerServerEvent('ox_lib:progressProps', data.prop)
|
||||||
end
|
end
|
||||||
|
|
||||||
local disable = data.disable
|
local disable = data.disable
|
||||||
@@ -137,7 +141,7 @@ local function startProgress(data)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if data.prop then
|
if data.prop then
|
||||||
playerState:set('lib:progressProps', nil, true)
|
TriggerServerEvent('ox_lib:progressProps', nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
if anim then
|
if anim then
|
||||||
@@ -225,14 +229,18 @@ end
|
|||||||
|
|
||||||
local function deleteProgressProps(serverId)
|
local function deleteProgressProps(serverId)
|
||||||
local playerProps = createdProps[serverId]
|
local playerProps = createdProps[serverId]
|
||||||
|
|
||||||
if not playerProps then return end
|
if not playerProps then return end
|
||||||
|
|
||||||
|
createdProps[serverId] = nil
|
||||||
|
|
||||||
for i = 1, #playerProps do
|
for i = 1, #playerProps do
|
||||||
local prop = playerProps[i]
|
local prop = playerProps[i]
|
||||||
|
|
||||||
if DoesEntityExist(prop) then
|
if DoesEntityExist(prop) then
|
||||||
DeleteEntity(prop)
|
DeleteEntity(prop)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
createdProps[serverId] = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
RegisterNetEvent('onPlayerDropped', function(serverId)
|
RegisterNetEvent('onPlayerDropped', function(serverId)
|
||||||
@@ -248,22 +256,29 @@ AddStateBagChangeHandler('lib:progressProps', nil, function(bagName, key, value,
|
|||||||
local ped = GetPlayerPed(ply)
|
local ped = GetPlayerPed(ply)
|
||||||
local serverId = GetPlayerServerId(ply)
|
local serverId = GetPlayerServerId(ply)
|
||||||
|
|
||||||
if not value then
|
if not value or createdProps[serverId] then
|
||||||
return deleteProgressProps(serverId)
|
return deleteProgressProps(serverId)
|
||||||
end
|
end
|
||||||
|
|
||||||
createdProps[serverId] = {}
|
local playerProps = {}
|
||||||
local playerProps = createdProps[serverId]
|
|
||||||
|
|
||||||
if value.model then
|
if value.model then
|
||||||
playerProps[#playerProps + 1] = createProp(ped, value)
|
local prop = createProp(ped, value)
|
||||||
else
|
|
||||||
for i = 1, #value do
|
|
||||||
local prop = value[i]
|
|
||||||
|
|
||||||
if prop then
|
if prop then
|
||||||
playerProps[#playerProps + 1] = createProp(ped, prop)
|
playerProps[#playerProps + 1] = prop
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local propCount = math.min(maxProps, #value)
|
||||||
|
|
||||||
|
for i = 1, propCount do
|
||||||
|
local prop = createProp(ped, value[i])
|
||||||
|
|
||||||
|
if prop then
|
||||||
|
playerProps[#playerProps + 1] = prop
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
createdProps[serverId] = playerProps
|
||||||
end)
|
end)
|
||||||
|
|||||||
12
resource/interface/server/progress.lua
Normal file
12
resource/interface/server/progress.lua
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
local maxProps = GetConvarInt('ox:progressPropLimit', 2)
|
||||||
|
|
||||||
|
---@param props ProgressPropProps | ProgressPropProps[] | nil
|
||||||
|
RegisterNetEvent('ox_lib:progressProps', function(props)
|
||||||
|
if type(props) == 'table' then
|
||||||
|
props = #props > maxProps and { table.unpack(props, 1, maxProps) } or props
|
||||||
|
else
|
||||||
|
props = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
Player(source).state:set('lib:progressProps', props, true)
|
||||||
|
end)
|
||||||
Reference in New Issue
Block a user