Compare commits

...

125 Commits
v1.0 ... v2.0

Author SHA1 Message Date
Jim Shield
e72fad0bfa Upload beta 1.2 2025-02-21 13:47:15 +00:00
Jim Shield
61a582a330 Remove old files 2025-02-21 13:45:08 +00:00
Jim Shield
80c357986d Merge pull request #22 from jimathy/1.0.13
1.0.14
2024-02-25 11:49:30 +00:00
Jim Shield
6c2b0b487f Merge branch 'main' into 1.0.13 2024-02-25 11:49:13 +00:00
Jim Shield
ad3d2b534f update version.txt to 14 2024-02-23 15:51:52 +00:00
Jim Shield
a4e9333ab8 1.0.14 2024-02-23 15:47:08 +00:00
Jim Shield
e08b349e31 Version Bump 2024-02-20 12:44:25 +00:00
Jim Shield
2bf7a28508 fix typo 2024-02-20 12:43:51 +00:00
Jim Shield
e6bee2ae2e dui request callback 2024-02-19 17:57:51 +00:00
Jim Shield
fc832d73f9 dui request callback 2024-02-19 17:57:15 +00:00
Jim Shield
e8e9b53f53 port dui texture changes (needs mutliscripting) 2024-02-19 17:53:23 +00:00
Jim Shield
7304376e90 getDurability updates 2024-02-19 17:52:19 +00:00
Jim Shield
7e9914afda grav inv fixes 2024-02-19 17:50:19 +00:00
Jim Shield
89f6f6deb0 add jpr-phonesystem support 2024-02-16 22:45:22 +00:00
Jim Shield
faa4a123fd Merge pull request #21 from SirOMGitsYOU/patch-2
add jpr-phonesystem support
2024-02-16 22:45:07 +00:00
Jim Shield
f1ae24d0fe separate create dui event 2024-02-16 22:42:13 +00:00
Peter
4670d87c96 add jpr-phonesystem support
Update wrapper.lua - Added code in for JPR-Phonesystem, Not sure if I should have called the QBCore export a different way, JPR-Phone system only supports qbcore and esx, 

Info for mails taken directly from their docs https://joaos-organization-3.gitbook.io/jpresources-documentation/installation/phone-system/events-and-commands#phone-mails
2024-02-15 10:45:50 +00:00
Jim Shield
4848b51ecc Update README.md 2024-02-14 17:34:04 +00:00
Jim Shield
2aa0cc9d5d Start adding manual durability changes 2024-02-14 00:19:36 +00:00
Jim Shield
5e3ace804d Adjust ox/qb target name creation 2024-02-14 00:11:08 +00:00
Jim Shield
42b109a5f1 readability 2024-02-09 17:02:45 +00:00
Jim Shield
5a8afb516b Update README.md 2024-02-09 16:51:20 +00:00
Jim Shield
ef2771cc83 Update README.md 2024-02-09 16:49:29 +00:00
Jim Shield
942c11af1d Update README.md
Start adding documentation
2024-02-09 00:46:20 +00:00
Jim Shield
b291e3f4b6 Update functions.lua 2024-02-08 21:54:28 +00:00
Jim Shield
46e3f18edc Merge pull request #20 from jimathy/1.0.12
1.0.12
2024-02-08 18:58:37 +00:00
Jim Shield
0886d8a4b6 Version Bump 2024-02-08 18:39:30 +00:00
Jim Shield
6a23e4ac91 Move loadModel time reset 2024-02-08 18:39:02 +00:00
Jim Shield
f7b7718679 remove unused variable 2024-02-08 18:38:22 +00:00
Jim Shield
9ae034da77 remove unused variable 2024-02-08 18:38:11 +00:00
Jim Shield
4d1b2abd3d blip_info support preview imgs 2024-02-08 18:37:14 +00:00
Jim Shield
f8e3f64907 Origen Inv Fixes 2024-02-08 18:35:54 +00:00
Jim Shield
3702835958 Added sellMenu() 2024-02-08 18:34:17 +00:00
Jim Shield
23c2e1472e Merge pull request #19 from jimathy/1.0.11
1.0.11
2024-01-30 19:21:33 +00:00
Jim Shield
6367c9ed3b remove loadmodel debug timeout print 2024-01-30 18:47:23 +00:00
Jim Shield
2d299a6a4a chargePlayer & fundPlayer server events/fixes 2024-01-30 18:40:26 +00:00
Jim Shield
ced629d4dc dupewarn info addition 2024-01-30 18:39:51 +00:00
Jim Shield
714121d272 crafting menu workarounds for missing info 2024-01-30 18:38:57 +00:00
Jim Shield
14c140cf85 load model and prop fixes 2024-01-29 22:23:23 +00:00
Jim Shield
881aec7c04 remove ox_lib model check as it was erroring out 2024-01-29 13:27:00 +00:00
Jim Shield
85b31e7e1d getRandomReward added for reward item support 2024-01-29 13:23:02 +00:00
Jim Shield
66c9287202 v1.0.11 - Version Bump 2024-01-28 19:24:19 +00:00
Jim Shield
f0f59b8568 remove debug print 2024-01-28 19:19:58 +00:00
Jim Shield
e03efa6687 update stopAnim to choose ped 2024-01-28 19:19:24 +00:00
Jim Shield
5a22651961 fix ConsumeSuccess and other typos 2024-01-28 19:18:55 +00:00
Jim Shield
236689452e custom stash slot sizes at "register" 2024-01-28 19:17:57 +00:00
Jim Shield
b686f267e1 Remove debug print 2024-01-28 01:44:53 +00:00
Jim Shield
096e323409 Merge pull request #18 from jimathy/1.0.10
1.0.10
2024-01-27 14:27:44 +00:00
Jim Shield
b71ec14615 sendPhoneMail - mutli phone support 2024-01-27 12:55:56 +00:00
Jim Shield
5a2304b785 quick linting 2024-01-26 20:31:46 +00:00
Jim Shield
6f413c555d add basic canCarry function 2024-01-26 20:31:10 +00:00
Jim Shield
fbc92aea41 update common functions to be smurter 2024-01-26 19:02:02 +00:00
Jim Shield
7b31343991 Merge pull request #17 from jimathy/1.0.9
1.0.9
2024-01-25 18:42:19 +00:00
Jim Shield
872cb39715 Fix crafting from pockets and add debugs 2024-01-25 18:40:42 +00:00
Jim Shield
dd35674307 i hate github 2024-01-25 18:31:34 +00:00
Jim Shield
1d102e94b4 Fix loading of ESX Core shared info 2024-01-25 18:31:00 +00:00
Jim Shield
c18932443a Testing fixing stashes 2024-01-25 17:32:40 +00:00
Jim Shield
7563b4abdf Merge pull request #16 from SirOMGitsYOU/patch-1
Fix Typo
2024-01-25 10:56:55 +00:00
Peter
20509e5cc2 Fix Typo
Update wrapper.lua - Fixed a typo on line 141 prevents this update from loading.
2024-01-24 23:33:39 +00:00
Jim Shield
43e30e95bc Tidy up and make ESX timeout longer 2024-01-24 17:10:44 +00:00
Jim Shield
9efff1060b Merge pull request #15 from jimathy/1.0.8
1.0.8 - Fixes crafting issues
2024-01-24 01:13:39 +00:00
Jim Shield
3b608e2b4f Update wrapper.lua 2024-01-24 01:13:30 +00:00
Jim Shield
bfed10c019 more fixes and optmization 2024-01-23 22:39:46 +00:00
Jim Shield
4bb29b8f14 cheeky little core_skills addition 2024-01-23 22:05:07 +00:00
Jim Shield
6eca0dd1d1 Cut down code and optimize crafting functions 2024-01-23 22:04:30 +00:00
Jim Shield
cc96bcde34 Fix stashCrafting not finding items 2024-01-23 22:03:27 +00:00
Jim Shield
ab61e40567 Merge pull request #14 from jimathy/1.0.7
1.0.7
2024-01-23 19:02:13 +00:00
Jim Shield
e17e2fb865 bit'o linting 2024-01-23 19:01:03 +00:00
Jim Shield
a6bd82a41c Fixes for ox showMenu support 2024-01-23 19:00:39 +00:00
Jim Shield
fac495c0ef Change how core information is grabbed 2024-01-23 19:00:04 +00:00
Jim Shield
15bea85bfb Version Bump v1.0.7 2024-01-23 18:59:02 +00:00
Jim Shield
f889080bc0 Merge pull request #13 from jimathy/1.0.6
1.0.6
2024-01-23 00:37:55 +00:00
Jim Shield
7d62358558 ox input default "select" 2024-01-23 00:37:18 +00:00
Jim Shield
f7a7e89c56 lib.showMenu support 2024-01-23 00:36:04 +00:00
Jim Shield
5f6720d37b Merge pull request #12 from solareon/patch-1
fix: job crafting restrictions
2024-01-23 00:34:15 +00:00
solareon
e541201a08 fix: skip amount and job tables 2024-01-22 22:39:16 +00:00
Solareon
2bf04f69cf fix: hasjob
Fixes broken crafting because the wrapper function is called hasJob with a capital J while the variable is called hasjob
2024-01-22 12:50:03 -07:00
Jim Shield
8479aea1b0 Origen inv experimental support 2024-01-22 17:14:56 +00:00
Jim Shield
d17a1da5b2 CoreInv Fully Supported 2024-01-22 16:55:58 +00:00
Jim Shield
f1ff06c098 CoreInv hasItem events updated 2024-01-22 15:31:04 +00:00
Jim Shield
df785043e8 CoreInv add/remove Item fixes 2024-01-22 14:51:51 +00:00
Jim Shield
5ae4799a5f add auto check for ps/lj-inv 2024-01-22 14:41:38 +00:00
Jim Shield
8cf4a6bd23 Version Bump - 1.0.6 2024-01-22 14:35:52 +00:00
Jim Shield
3e067c8492 Merge pull request #9 from jimathy/1.0.5
1.0.5
2024-01-21 22:28:03 +00:00
Jim Shield
6847639c65 Fix version checking system being dumb 2024-01-21 17:24:32 +00:00
Jim Shield
0592c3ebc8 Fix stashRemoveItem for QS-Inv 2024-01-21 17:24:10 +00:00
Jim Shield
2ca1cbd110 fxmanifest.lua - Version Bump v1.0.5 2024-01-21 17:20:15 +00:00
Jim Shield
8642733b78 version.txt - Version bump - 1.0.5 2024-01-21 17:17:55 +00:00
Jim Shield
5a4293182c version number plz work 2024-01-21 17:10:32 +00:00
Jim Shield
6075878038 Update version.txt 2024-01-21 00:56:19 +00:00
Jim Shield
7eaeac886f Merge pull request #8 from jimathy/1.0.4
1.0.4
2024-01-21 00:27:08 +00:00
Jim Shield
fe27a1ca1c speed playAnim up + add default input message 2024-01-21 00:25:42 +00:00
Jim Shield
0caea0b473 advanced checking and handling of setVehicle 2024-01-20 14:28:10 +00:00
Jim Shield
370c77433b Version Bump - v1.0.4 2024-01-20 13:01:26 +00:00
Jim Shield
ac85d35097 Merge pull request #7 from jimathy/1.0.3
1.0.3
2024-01-19 23:58:42 +00:00
Jim Shield
85c3e06910 update addItem/removeItem prints 2024-01-19 23:57:22 +00:00
Jim Shield
7d0736bfc2 Change how bridge decides what get/setvehicleproperties to use 2024-01-19 22:33:35 +00:00
Jim Shield
b0b011ac56 update readme -main 2024-01-19 20:25:44 +00:00
Jim Shield
23091c2e73 add spam free ensureNetToVeh() 2024-01-19 20:25:04 +00:00
Jim Shield
ef2ba3ea7a tidy up new prints more 2024-01-19 20:24:25 +00:00
Jim Shield
df60f3b2b1 tidy prints for setVehicleProperties 2024-01-19 18:13:22 +00:00
Jim Shield
4cc001d2f0 fix typo in stashhasitem 2024-01-19 18:10:05 +00:00
Jim Shield
e2bb32d9de Optimize Crafting/Multicraft Menu + better hasItem prints 2024-01-19 14:46:45 +00:00
Jim Shield
725125a274 add debug prints to new ox setVehicleProperties call 2024-01-19 14:46:09 +00:00
Jim Shield
dcf2018ec6 check for destroyProp if received any info 2024-01-19 13:02:09 +00:00
Jim Shield
e5316f45aa Rewrite + Optimize hasItem() & stashhasItem() 2024-01-19 13:00:50 +00:00
Jim Shield
02c202d075 Version Bump - v1.0.3 2024-01-19 12:53:45 +00:00
Jim Shield
9e9fb14f55 Merge pull request #3 from bandgeekndb/stashoptions
Allow for passing through stash options when opening a stash
2024-01-19 12:50:10 +00:00
Jim Shield
61e0d777a8 Attempt to fix ox_lib's setVehicleProperites + 1.0.2 2024-01-18 20:21:22 +00:00
Jim Shield
78c3e33d38 version bump 1.0.1 2024-01-18 17:50:42 +00:00
Jim Shield
3be5b5e1ee Merge pull request #6 from jimathy/1.0.1
1.0.1
2024-01-18 17:49:05 +00:00
Jim Shield
f550479298 Revert "version bump"
This reverts commit d82366b59f.
2024-01-18 17:48:14 +00:00
Jim Shield
5fe0ab8815 Revert "Resolve merge conflict"
This reverts commit 56ccb43c2b.
2024-01-18 17:48:03 +00:00
Jim Shield
56ccb43c2b Resolve merge conflict 2024-01-18 17:44:25 +00:00
Jim Shield
93d0754935 Make qbx use correct export for getCoreObject
This should be fine as qbox takes over the export but just to be sure
2024-01-18 17:41:28 +00:00
Jim Shield
d82366b59f version bump 2024-01-18 15:38:28 +00:00
Jim Shield
35202f7000 add fix for recipes having no final amount added 2024-01-18 15:33:51 +00:00
Jim Shield
bc5aadc394 add export error check to stashRemoveItem 2024-01-18 15:14:38 +00:00
Jim Shield
181c480ba6 return duty check for hasJob 2024-01-18 15:06:20 +00:00
Jim Shield
b973c71638 Update version.txt(?) 2024-01-18 14:45:41 +00:00
Jim Shield
ede0a44251 fix version.txt? 2024-01-18 14:42:23 +00:00
Jim Shield
e4b0f8601a fix bridge version check looking in wrong place 2024-01-18 14:17:01 +00:00
Jim Shield
d12c615f0b Merge pull request #4 from jimathy/duty-fixes
Duty fixes + CheckVersion + better debug
2024-01-18 13:16:45 +00:00
Jim Shield
e58163d631 CheckVersion supports free script checking 2024-01-18 13:08:16 +00:00
Nicholas Blew
63c2d5d83d Allow for passing through stash options when opening a stash 2024-01-18 02:20:20 -05:00
44 changed files with 7819 additions and 2658 deletions

View File

@@ -1,47 +0,0 @@
# Jim_Bridge
This script is intended to be used with my all my scripts (soon)
It was started due to wanting to bring the same features from some scripts into others with minimal work and multiple updates
- Having certain functions in one place(this script) makes it easier to update, enchance and fix things
- This brings the possibility of branching to mutliple frameworks as I've added some already:
- `"qb-core"`
- `"qbx-core"`
- `"ox_core"`
- `"es_extended"` (requires ox_lib and ox_inventory)
All the next updates of my scripts will use this script and be added as a dependancy
------
It was a tough decision to put it up on github instead of tebex and encrypted
But I want this script to grow with help of others who know more about other cores
---
The installation of this script is simple
- it just needs to start before any script that requires it
- it can start before core scripts if you want
- for `qb-core` I personally place this script in `resources > [standalone]`
---
### Support for different exports and scripts
In exports.lua is the list of script folder names
This is for people who have customised/renamed scripts
eg. for people who use `ps-inventory`, this is mainly based on qb-inventory
so you need to rename
```lua
QBInv = "qb-inventory",
```
to
```lua
QBInv = "ps-inventory",
```
This will now use events from `ps-inventory` and use it through out the scripts.

View File

@@ -1,503 +0,0 @@
if IsDuplicityVersion() then
if GetResourceState(OXLibExport):find("start") then
createCallback(GetCurrentResourceName()..':server:GetStashItems', function(source, stashName) local stash = getStash(stashName) return stash end)
else
createCallback(GetCurrentResourceName()..':server:GetStashItems', function(source, cb, stashName) local stash = getStash(stashName) cb(stash) end)
end
end
local timeout = 0
local timing = false
local stashItems = {}
function GetStashTimeout(stashName, stop)
if stop then stashItems = {} timing = false timeout = 0 return end
if stashItems[1] then return true else
if timeout <= 0 then
stashItems = triggerCallback(GetCurrentResourceName()..':server:GetStashItems', stashName)
timeout = 15000
if not timing then
CreateThread(function()
timing = true
while timeout > 0 do timeout -= 1000 Wait(1000) end
timing = false stashItems = {} timeout = 0
end)
end
end
return false
end
end
local CraftLock = false
function craftingMenu(data) local hasjob = false
if CraftLock then return end
if data.stashName and not GetStashTimeout(data.stashName) then
--triggerNotify(nil, "Chacking", "success")
end
if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end
local Menu = {}
local Recipes = data.craftable.Recipes
for i = 1, #Recipes do
for k, v in pairs(Recipes[i]) do
if k ~= "amount" and k ~= "job" and k ~= "gang" then
if Recipes[i].job then hasjob = false
for l, b in pairs(Recipes[i].job) do
hasjob = hasJob(l, nil, b)
if hasJob == true then break end
end
end
local setheader, settext = "", ""
local disable = false
local checktable = {}
if Recipes[i].job and hasjob == false then else
for l, b in pairs(Recipes[i][tostring(k)]) do
if not Items[l] then print("^3Error^7: ^2Script can't find ingredient item in Shared Items - ^1"..l.."^7") return end
settext = settext..(settext ~= "" and br or "")..Items[l].label..(b > 1 and " x"..b or "")
if data.stashName then checktable[l] = stashhasItem(stashItems, l, b)
else checktable[l] = hasItem(l, b) end
Wait(0)
end
for _, v in pairs(checktable) do
if not v then
disable = true
break
end
end
setheader = Items[tostring(k)].label..(Recipes[i]["amount"] > 1 and " x"..Recipes[i]["amount"] or "")..(not disable and " ✔️" or "")
Menu[#Menu + 1] = {
isMenuHeader = disable,
icon = invImg(tostring(k)),
header = setheader, txt = settext,
onSelect = function()
local transdata = { item = k, craft = data.craftable.Recipes[i], craftable = data.craftable, coords = data.coords, stashName = data.stashName, onBack = data.onBack }
if Config.Crafting.MultiCraft then
multiCraft(transdata)
else
makeItem(transdata)
end
end,
}
end
end
end
end
openMenu(Menu, { header = data.craftable.Header, onBack = data.onBack or nil, canClose = true, onExit = function() end, })
lookEnt(data.coords)
end
function multiCraft(data) local Menu = {}
local success = Config.Crafting.MultiCraftAmounts
if data.stashName and not GetStashTimeout(data.stashName) then
--triggerNotify(nil, "Refreshing stashinfo", "success")
end
Menu[#Menu+1] = {
isMenuHeader = true,
icon = invImg(data.item),
header = Items[data.item].label,
}
for k in pairsByKeys(success) do
local settext = ""
for l, b in pairs(data.craft[data.item]) do
if data.stashName then
success[k] = stashhasItem(stashItems, l, (b * k))
else
success[k] = hasItem(l, (b * k))
end
settext = settext..(settext ~= "" and br or "")..Items[l].label..(b*k > 1 and "- x"..b*k or "")
Wait(0)
end
Menu[#Menu + 1] = {
isMenuHeader = not success[k],
header = "Craft - x"..k * data.craft.amount,
txt = settext,
onSelect = function ()
makeItem({item = data.item, craft = data.craft, craftable = data.craftable, amount = k, coords = data.coords, stashName = data.stashName, onBack = data.onBack })
end,
}
end
openMenu(Menu, { header = data.craftable.Header, onBack = function() craftingMenu(data) end, })
end
function makeItem(data)
if not CraftLock then CraftLock = true else return end
local bartime, bartext, animDict, anim = nil, nil, nil, nil
if data.craftable.progressBar then
bartime = data.craftable.progressBar.time
bartext = data.craftable.progressBar.label
else
bartime = 5000
bartext = Loc[Config.Lan].progressbar["progress_make"]
end
animDict = data.craftable.Anims.animDict or "amb@prop_human_parking_meter@male@idle_a"
anim = data.craftable.Anims.anim or "idle_a"
local amount = (data.amount and data.amount ~= 1) and data.amount or 1
local crafted = true
local crafting = true
local cam = createTempCam(PlayerPedId(), data.coords)
startTempCam(cam)
for i = 1, amount do
for _, v in pairs(data.craft) do
if type(v) == "table" then
for l, b in pairs(v) do
if crafting and progressBar({
label = "Using "..b.." "..Items[l].label,
time = 1000,
cancel = true,
dict = 'pickup_object',
anim = "putdown_low",
flag = 48,
icon = l,
}) then
--TriggerEvent('inventory:client:ItemBox', Items[l], "use", b) -- Show item box for each item
else
crafted = false
crafting = false
break
end
Wait(200)
end
if crafted then
if crafting and progressBar({
label = bartext..Items[data.item].label,
time = bartime,
cancel = true,
dict = animDict,
anim = anim,
flag = 8,
icon = data.item,
}) then
TriggerServerEvent(GetCurrentResourceName()..":Crafting:GetItem", data.item, data.craft, data.stashName)
else
crafting = false
break
end
end
end
end
Wait(500)
end
stopTempCam()
CraftLock = false
lockInv(false)
craftingMenu(data)
ClearPedTasks(PlayerPedId())
end
RegisterNetEvent(GetCurrentResourceName()..":Crafting:GetItem", function(ItemMake, craftable, stashName)
local src = source
local amount = 1
if stashName then
local stashItems = getStash(stashName)
if craftable["amount"] then amount = craftable["amount"] end
local itemRemove = {}
for k, v in pairs(craftable[ItemMake]) do
for l, b in pairs(stashItems) do
if k == b.name then
itemRemove[k] = v
end
end
end
stashRemoveItem(stashItems, stashName, itemRemove)
else
if craftable then
if craftable["amount"] then amount = craftable["amount"] end
for k, v in pairs(craftable[ItemMake]) do
TriggerEvent(GetCurrentResourceName()..":server:toggleItem", false, tostring(k), v, src)
end
end
end
TriggerEvent(GetCurrentResourceName()..":server:toggleItem", true, ItemMake, amount, src)
end)
--[[SHOPS]]--
function openShop(data)
if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end
if GetResourceState(OXInv):find("start") then
exports[OXInv]:openInventory('shop', { type = data.shop })
else
TriggerServerEvent(Config.General.JimShops and "jim-shops:ShopOpen" or "inventory:server:OpenInventory", "shop", data.items.label, data.items)
end
lookEnt(data.coords)
end
-- Client & Server side
function hasItem(items, amount, src) local amount, count = amount and amount or 1, 0
if src then
if GetResourceState(OXInv):find("start") then
local item = exports[OXInv]:GetItem(src, items)
local amount = (amount or 1)
if item.count >= amount then
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^5FOUND^7 ^3"..item.count.."^7/^3"..amount.." "..tostring(items)) end
return true
else if Config.System.Debug then
print("^6Bridge^7: ^3HasItem^7: ^2"..tostring(items).." ^1NOT FOUND^7") end
return false
end
elseif GetResourceState(QSInv):find("start") then
for _, itemData in pairs(exports[QSInv]:GetInventory(src)) do
if itemData and (itemData.name == items) then
--if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^2Item^7: '^3"..tostring(items).."^7' ^2Slot^7: ^3"..itemData.slot.." ^7x(^3"..tostring(itemData.amount).."^7)") end
count += (itemData.amount or 1)
end
end
if count >= amount then
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^5FOUND^7 ^3"..count.."^7/^3"..amount.." "..tostring(items)) end
return true
else
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^2"..tostring(items).." ^1NOT FOUND^7") end
return false
end
elseif GetResourceState(CoreInv):find("start") then
local item = exports[CoreInv]:getItems('primary-'..src, items)
if item.amount >= amount then
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^5FOUND^7 ^3"..item.amount.."^7/^3"..amount.." "..tostring(items)) end
return true
else if Config.System.Debug then
print("^6Bridge^7: ^3HasItem^7: ^2"..tostring(items).." ^1NOT FOUND^7") end
return false
end
elseif GetResourceState(CodeMInv):find("start") then
local success = exports["codem-inventory"]:CheckItemValid(src, items, amount)
if success then
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^5FOUND^7 ^3"..amount.." "..tostring(items)) end
return true
end
elseif GetResourceState(CodeMInv):find("start") then
for _, itemData in pairs(exports[CodeMInv]:GetUserInventory(src)) do
if itemData and (itemData.name == items) then
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^2Item^7: '^3"..tostring(items).."^7' ^2Slot^7: ^3"..itemData.slot.." ^7x(^3"..tostring(itemData.amount).."^7)") end
count += (itemData.amount or 1)
end
end
if count >= amount then
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^5FOUND^7 ^3"..count.."^7/^3"..amount.." "..tostring(items)) end
return true
else
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^2"..tostring(items).." ^1NOT FOUND^7") end
return false
end
else
for _, itemData in pairs(Core.Functions.GetPlayer(src).PlayerData.items) do
if itemData and (itemData.name == items) then
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^2Item^7: '^3"..tostring(items).."^7' ^2Slot^7: ^3"..itemData.slot.." ^7x(^3"..tostring(itemData.amount).."^7)") end
count += (itemData.amount or 1)
end
end
if count >= amount then
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^5FOUND^7 ^3"..count.."^7/^3"..amount.." "..tostring(items)) end
return true
else
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^2"..tostring(items).." ^1NOT FOUND^7") end
return false
end
end
else
if GetResourceState(OXInv):find("start") then
local count = tonumber(exports[OXInv]:Search('count', items))
local amount = (amount or 1)
if count >= amount then if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^5FOUND^7 ^3"..count.."^7/^3"..amount.." "..tostring(items)) end return true
else if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^2"..tostring(items).." ^1NOT FOUND^7") end return false end
elseif GetResourceState(QSInv):find("start") then
for _, itemData in pairs(exports[QSInv]:getUserInventory()) do
if itemData and (itemData.name == items) then
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^2Item^7: '^3"..tostring(items).."^7' ^2Slot^7: ^3"..itemData.slot.." ^7x(^3"..tostring(itemData.amount).."^7)") end
count += (itemData.amount or 1)
end
end
if count >= amount then
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^5FOUND^7 ^3"..count.."^7/^3"..amount.." "..tostring(items)) end
return true
else
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^2"..tostring(items).." ^1NOT FOUND^7") end
return false
end
elseif GetResourceState(CoreInv):find("start") then
for _, itemData in pairs(exports[CoreInv]:getInventory('primary-'..GetPlayerServerId(PlayerPedId()))) do
if itemData and (itemData.name == items) then
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^2Item^7: '^3"..tostring(items).."^7' ^2Slot^7: ^3"..itemData.slot.." ^7x(^3"..tostring(itemData.amount).."^7)") end
count += (itemData.amount or 1)
end
end
if count >= amount then
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^5FOUND^7 ^3"..count.."^7/^3"..amount.." "..tostring(items)) end
return true
else
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^2"..tostring(items).." ^1NOT FOUND^7") end
return false
end
elseif GetResourceState(CodeMInv):find("start") then
local success = exports[CodeMInv]:CheckItemValid(items, amount)
if success then
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^5FOUND^7 ^3"..amount.." "..tostring(items)) end
return true
end
elseif GetResourceState(QBInv):find("start") then
--if tonumber(GetResourceMetadata(QBInv, 'version')) <= 1.2 then
-- return exports[QBInv]:HasItem(items, amount)
--else
for _, itemData in pairs(Core.Functions.GetPlayerData().items) do
if itemData and (itemData.name == items) then
if Config.System.Debug then print("^6Bridge^7: ^3HasItem^7: ^2Item^7: '^3"..tostring(items).."^7' ^2Slot^7: ^3"..itemData.slot.." ^7x(^3"..tostring(itemData.amount).."^7)") end
count += (itemData.amount or 1)
end
end
if count >= amount then
if Config.System.Debug then
print("^6Bridge^7: ^3HasItem^7: ^5FOUND^7 ^3"..count.."^7/^3"..amount.." "..tostring(items))
end
return true
else
if Config.System.Debug then
print("^6Bridge^7: ^3HasItem^7: ^2"..tostring(items).." ^1NOT FOUND^7")
end
return false
end
-- end
end
end
end
-- Stash Items
function openStash(data)
if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end
if GetResourceState(OXInv):find("start") then
exports[OXInv]:openInventory('stash', data.stash)
elseif GetResourceState(CodeMInv):find("start") then
exports[CodeMInv]:OpenStash(data.stash, 400000, 100)
else
TriggerEvent("inventory:client:SetCurrentStash", data.stash)
TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash)
end
lookEnt(data.coords)
end
function getStash(stashName)
local stashItems, items = {}, {}
if GetResourceState(OXInv):find("start") then
if Config.System.Debug then print("^6Bridge^7: ^2Retrieving ^3Stash^2 with ^7ox_inventory") end
stashItems = exports[OXInv]:Inventory(stashName).items
elseif GetResourceState(QSInv):find("start") then
if Config.System.Debug then print("^6Bridge^7: ^2Retrieving ^3Stash^2 with ^7qs-inventory") end
stashItems = exports[QSInv]:GetStashItems(stashName)
elseif GetResourceState(CodeMInv):find("start") then
if Config.System.Debug then print("^6Bridge^7: ^2Retrieving ^3Stash^2 with ^7qs-inventory") end
stashItems = exports[CodeMInv]:GetInventoryItems('Stash', stashName)
elseif GetResourceState(QBInv):find("start") then
if Config.System.Debug then print("^6Bridge^7: ^2Retrieving ^3Stash^2 with ^7qb-inventory") end
local result = MySQL.scalar.await('SELECT items FROM stashitems WHERE stash = ?', { stashName })
if result then stashItems = json.decode(result) end
end
if stashItems then
for _, item in pairs(stashItems) do
local itemInfo = Items[item.name:lower()]
if itemInfo then
items[item.slot] = {
name = itemInfo["name"],
amount = tonumber(item.amount) or tonumber(item.count),
info = item.info ~= nil and item.info or "",
label = itemInfo["label"],
description = itemInfo["description"] ~= nil and itemInfo["description"] or "",
weight = itemInfo["weight"],
type = itemInfo["type"],
unique = itemInfo["unique"],
useable = itemInfo["useable"],
image = itemInfo["image"],
slot = item.slot,
}
end
end
if Config.System.Debug then print("^6Bridge^7: ^3GetStashItems^7: ^2Stash information for ^7'^6"..stashName.."^7' ^2retrieved^7") end
end
return items
end
function stashRemoveItem(stashItems, stashName, items) local amount = amount and amount or 1
if GetResourceState(OXInv):find("start") then
for k, v in pairs(items) do
exports[OXInv]:RemoveItem(stashName, k, v)
if Config.System.Debug then print("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..OXInv, k, v) end
end
elseif GetResourceState(QSInv):find("start") then
for k, v in pairs(items) do
exports[QSInv]:RemoveItemIntoStash(stashName, k, v)
if Config.System.Debug then print("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QSInv, k, v) end
end
elseif GetResourceState(CodeMInv):find("start") then
for k, v in pairs(items) do
for l in pairs(stashItems) do
if stashItems[l].name == k then
if (stashItems[l].amount - v) <= 0 then
if Config.System.Debug then
print("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v)
end
stashItems[l] = nil
else
if Config.System.Debug then
print("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..CodeMInv, k, v)
end
stashItems[l].amount -= v
end
end
end
end
if Config.System.Debug then
print("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash ^7'^6"..stashName.."^7'")
end
else
for k, v in pairs(items) do
for l in pairs(stashItems) do
if stashItems[l].name == k then
if (stashItems[l].amount - v) <= 0 then
if Config.System.Debug then
print("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v)
end
stashItems[l] = nil
else
if Config.System.Debug then
print("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QBInv, k, v)
end
stashItems[l].amount -= v
end
end
end
end
if Config.System.Debug then
print("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash ^7'^6"..stashName.."^7'")
end
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { ['stash'] = stashName, ['items'] = json.encode(stashItems) })
end
end
RegisterNetEvent(GetCurrentResourceName()..":server:stashRemoveItem", stashRemoveItem)
function stashhasItem(stashItems, item, amount) local amount, count = amount and amount or 1, 0
for k, itemData in pairs(stashItems) do
if itemData and (itemData.name == item) then
--if Config.System.Debug then
-- print("^6Bridge^7: ^3stashHasItem^7: ^2Item^7: '^3"..tostring(item).."^7' ^2Slot^7: ^3"..itemData.slot.." ^7x(^3"..tostring(itemData.amount).."^7)")
--end
count += (itemData.amount or 1)
end
end
if count >= amount then
if Config.System.Debug then
print("^6Bridge^7: ^3stashHasItem^7: ^2Items ^3"..item.." ^5FOUND^7 x^3"..count.."^7/^3"..amount.."^7") end
return
true
else
if Config.System.Debug then print("^6Bridge^7: ^3stashHasItem^7: ^2Items ^1NOT FOUND^7 "..json.encode(item)) end
return false
end
end

View File

@@ -1,19 +0,0 @@
Exports = {
QBExport = "qb-core",
QBXExport = "qbx_core",
ESXExport = "es_extended",
OXCoreExport = "ox_core",
OXInv = "ox_inventory",
QBInv = "qb-inventory",
QSInv = "qs-inventory",
CoreInv = "core-inventory",
CodeMInv = "codem-inventory",
OXLibExport = "ox_lib",
QBMenuExport = "qb-menu",
QBTargetExport = "qb-target",
OXTargetExport = "ox_target"
}

View File

@@ -1,857 +0,0 @@
onDuty = false
function jobCheck(job)
canDo = true
if not hasJob(job) or not onDuty then
triggerNotify(nil, Loc[Config.Lan].error["not_clockedin"])
canDo = false
end
return canDo
end
local time = 100
function loadModel(model)
if not HasModelLoaded(model) then if Config.System.Debug then print("^6Bridge^7: ^2Loading Model^7: '^6"..model.."^7'") end
while not HasModelLoaded(model) do if time > 0 then time = time - 1 RequestModel(model)
else time = 1000 print("^6Bridge^7: ^3LoadModel^7: ^2Timed out loading model ^7'^6"..model.."^7'") break end
Wait(10) end
end
end
function unloadModel(model) if Config.System.Debug then print("^6Bridge^7: ^2Removing Model^7: '^6"..model.."^7'") end SetModelAsNoLongerNeeded(model) end
function loadAnimDict(dict) if Config.System.Debug then print("^6Bridge^7: ^2Loading Anim Dictionary^7: '^6"..dict.."^7'") end while not HasAnimDictLoaded(dict) do RequestAnimDict(dict) Wait(5) end end
function unloadAnimDict(dict) if Config.System.Debug then print("^6Bridge^7: ^2Removing Anim Dictionary^7: '^6"..dict.."^7'") end RemoveAnimDict(dict) end
function loadPtfxDict(dict) if Config.System.Debug then if not HasNamedPtfxAssetLoaded(dict) then print("^6Bridge^7: ^2Loading Ptfx Dictionary^7: '^6"..dict.."^7'") end end while not HasNamedPtfxAssetLoaded(dict) do RequestNamedPtfxAsset(dict) Wait(5) end end
function unloadPtfxDict(dict) if Config.System.Debug then print("^6Bridge^7: ^2Removing Ptfx Dictionary^7: '^6"..dict.."^7'") end RemoveNamedPtfxAsset(dict) end
function loadTextureDict(dict) if Config.System.Debug then print("^6Bridge^7: ^2Loading Texture Dictionary^7: '^6"..dict.."^7'") end while not HasStreamedTextureDictLoaded(dict) do RequestNamedPtfxAsset(dict) Wait(5) end end
function countTable(table) local i = 0 for keys in pairs(table) do i += 1 end return i end
function pairsByKeys(t) local a = {} for n in pairs(t) do a[#a+1] = n end table.sort(a) local i = 0 local iter = function() i += 1 if a[i] == nil then return nil else return a[i], t[a[i]] end end return iter end
function playAnim(animDict, animName, duration, flag, ped)
loadAnimDict(animDict)
TaskPlayAnim(ped and ped or PlayerPedId(), animDict, animName, 1.0, -1.0, duration or 30000, flag or 50, 1, false, false, false)
end
function stopAnim(animDict, animName)
StopAnimTask(PlayerPedId(), animName, animDict)
unloadAnimDict(animDict)
end
function makeVeh(model, coords)
loadModel(model)
local veh = CreateVehicle(model, coords.x, coords.y, coords.z, coords.w, true, false)
SetVehicleHasBeenOwnedByPlayer(veh, true)
SetNetworkIdCanMigrate(NetworkGetNetworkIdFromEntity(veh), true)
Wait(100)
SetVehicleNeedsToBeHotwired(veh, false)
SetVehRadioStation(veh, 'OFF')
SetVehicleFuelLevel(veh, 100.0)
SetVehicleModKit(veh, 0)
SetVehicleOnGroundProperly(veh)
if Config.System.Debug then
local coords = { string.format("%.2f", coords.x), string.format("%.2f", coords.y), string.format("%.2f", coords.z), (string.format("%.2f", coords.w or 0.0)) }
print("^6Bridge^7: ^1Veh ^2Created^7: '^6"..veh.."^7' | ^2Hash^7: ^7'^6"..(model).."^7' | ^2Coord^7: ^5vec4^7(^6"..(coords[1]).."^7, ^6"..(coords[2]).."^7, ^6"..(coords[3]).."^7, ^6"..(coords[4]).."^7)")
end
unloadModel(model)
return veh
end
local Peds = {}
local Props = {}
function makePed(model, coords, freeze, collision, scenario, anim, synced)
loadModel(model)
local ped = CreatePed(0, model, coords.x, coords.y, coords.z-1.03, coords.w, synced and synced or true, false)
SetEntityInvincible(ped, true)
SetBlockingOfNonTemporaryEvents(ped, true)
FreezeEntityPosition(ped, freeze and freeze or true)
if collision then SetEntityNoCollisionEntity(ped, PlayerPedId(), false) end
if scenario then TaskStartScenarioInPlace(ped, scenario, 0, true) end
if anim then
loadAnimDict(anim[1])
TaskPlayAnim(ped, anim[1], anim[2], 0.5, 1.0, -1, 1, 0.2, 0, 0, 0)
end
if Config.System.Debug then
local coords = { string.format("%.2f", coords.x), string.format("%.2f", coords.y), string.format("%.2f", coords.z), (string.format("%.2f", coords.w or 0.0)) }
print("^6Bridge^7: ^1Ped ^2Created^7: '^6"..ped.."^7' | ^2Hash^7: ^7'^6"..(model).."^7' | ^2Coord^7: ^5vec4^7(^6"..(coords[1]).."^7, ^6"..(coords[2]).."^7, ^6"..(coords[3]).."^7, ^6"..(coords[4]).."^7)")
end
unloadModel(model)
Peds[#Peds+1] = ped
return ped
end
function makeProp(data, freeze, synced)
loadModel(data.prop)
local prop = CreateObject(data.prop, data.coords.x, data.coords.y, data.coords.z-1.03, synced or false, synced or false, false)
SetEntityHeading(prop, data.coords.w + 180.0)
FreezeEntityPosition(prop, freeze and freeze or 0)
if Config.System.Debug then
local coords = { string.format("%.2f", data.coords.x), string.format("%.2f", data.coords.y), string.format("%.2f", data.coords.z), (string.format("%.2f", data.coords.w or 0.0)) }
print("^6Bridge^7: ^1Prop ^2Created^7: '^6"..prop.."^7' | ^2Hash^7: ^7'^6"..(data.prop).."^7' | ^2Coord^7: ^5vec4^7(^6"..(coords[1]).."^7, ^6"..(coords[2]).."^7, ^6"..(coords[3]).."^7, ^6"..(coords[4]).."^7)")
end
unloadModel(data.prop)
Props[#Props+1] = prop
return prop
end
function cv(amount)
local formatted = amount or "0"
while true do formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2') if (k==0) then break end Wait(0) end
return formatted
end
function DrawText3D(x, y, z, text)
SetTextScale(0.35, 0.35)
SetTextFont(4)
SetTextProportional(true)
SetTextColour(255, 255, 255, 215)
BeginTextCommandDisplayText("STRING")
SetTextCentre(true)
AddTextComponentSubstringPlayerName(text)
SetDrawOrigin(x,y,z, 0)
EndTextCommandDisplayText(0.0, 0.0)
local factor = string.len(text) / 370
DrawRect(0.0, 0.0 + 0.0125, 0.017 + factor, 0.03, 0, 0, 0, 75)
ClearDrawOrigin()
end
AddEventHandler('onResourceStop', function(r)
if r ~= GetCurrentResourceName() then return end
stopSpinner()
for i = 1, #Peds do DeletePed(Peds[i]) end
for i = 1, #Props do destroyProp(Props[i]) end
end)
function instantLookEnt(ent, ent2)
local p1 = GetEntityCoords(ent, true)
local p2 = GetEntityCoords(ent2, true)
local dx = p2.x - p1.x
local dy = p2.y - p1.y
local heading = GetHeadingFromVector_2d(dx, dy)
SetEntityHeading( ent, heading )
end
function lookEnt(entity) local ped = PlayerPedId()
if entity then
if type(entity) == "vector3" or type(entity) == "vector4" then
if not IsPedHeadingTowardsPosition(ped, entity.xyz, 30.0) then
TaskTurnPedToFaceCoord(ped, entity.xyz, 1500)
if Config.System.Debug then print("^6Bridge^7: ^2Turning Player to^7: '^6"..json.encode(entity).."^7'") end
Wait(1500)
end
else
if DoesEntityExist(entity) then
if not IsPedHeadingTowardsPosition(ped, GetEntityCoords(entity), 30.0) then
TaskTurnPedToFaceCoord(ped, GetEntityCoords(entity), 1500)
if Config.System.Debug then print("^6Bridge^7: ^2Turning Player to^7: '^6"..entity.."^7'") end
Wait(1500)
end
end
end
end
end
function destroyProp(entity)
if Config.System.Debug then print("^6Bridge^7: ^2Destroying Prop^7: '^6"..entity.."^7'") end
if IsEntityAttachedToEntity(entity, PlayerPedId()) then
SetEntityAsMissionEntity(entity)
DetachEntity(entity, true, true)
end
DeleteObject(entity)
end
function pushVehicle(entity)
SetVehicleModKit(entity, 0)
if entity ~= 0 and DoesEntityExist(entity) then
if not NetworkHasControlOfEntity(entity) then
if Config.System.Debug then print("^6Bridge^7: ^3pushVehicle^7: ^2Requesting network control of vehicle^7.") end
NetworkRequestControlOfEntity(entity)
local timeout = 2000
while timeout > 0 and not NetworkHasControlOfEntity(entity) do
Wait(100)
timeout = timeout - 100
end
if NetworkHasControlOfEntity(entity) and Config.System.Debug then print("^6Bridge^7: ^3pushVehicle^7: ^2Network has control of entity^7.") end
end
if not IsEntityAMissionEntity(entity) then
if Config.System.Debug then print("^6Bridge^7: ^3pushVehicle^7: ^2Setting vehicle as a ^7'^2mission^7' &2entity^7.") end
SetEntityAsMissionEntity(entity, true, true)
local timeout = 2000
while timeout > 0 and not IsEntityAMissionEntity(entity) do
Wait(100)
timeout = timeout - 100
end
if IsEntityAMissionEntity(entity) and Config.System.Debug then print("^6Bridge^7: ^3pushVehicle^7: ^2Vehicle is a ^7'^2mission^7'^2 entity^7.") end
end
end
end
function makeBlip(data)
local blip = AddBlipForCoord(data.coords)
SetBlipAsShortRange(blip, true)
SetBlipSprite(blip, data.sprite or 106)
SetBlipColour(blip, data.col or 5)
SetBlipScale(blip, data.scale or 0.7)
SetBlipDisplay(blip, (data.disp or 6))
if data.category then SetBlipCategory(blip, data.category) end
BeginTextCommandSetBlipName('STRING')
AddTextComponentString(tostring(data.name))
EndTextCommandSetBlipName(blip)
if Config.System.Debug then print("^6Bridge^7: ^6Blip ^2created for location^7: '^6"..data.name.."^7'") end
return blip
end
function makeEntityBlip(data)
AddBlipForEntity(data.entity)
local blip = GetBlipFromEntity(data.entity)
SetBlipAsShortRange(blip, true)
SetBlipSprite(blip, data.sprite or 106)
SetBlipColour(blip, data.col or 5)
SetBlipScale(blip, data.scale or 0.7)
SetBlipDisplay(blip, (data.disp or 6))
if data.category then SetBlipCategory(blip, data.category) end
BeginTextCommandSetBlipName('STRING')
AddTextComponentString(tostring(data.name))
EndTextCommandSetBlipName(blip)
if Config.System.Debug then print("^6Bridge^7: ^6Blip ^2created for Entity^7: '^6"..data.name.."^7'") end
return blip
end
function lockInv(toggle)
FreezeEntityPosition(PlayerPedId(), toggle)
LocalPlayer.state:set("inv_busy", toggle, true)
TriggerEvent('inventory:client:busy:status', toggle)
TriggerEvent('canUseInventoryAndHotbar:toggle', not toggle)
end
function createTempCam(ent, coords)
local cam = nil
if Config.Crafting.craftCam then
if Config.System.Debug then
triggerNotify(nil, "ModCam Created", "success")
end
local camCoords = nil
if type(ent) ~= "vector3" then
camCoords = GetOffsetFromEntityInWorldCoords(ent, 1.2, -0.3, 0.8)
else
camCoords = ent
end
--local pedCoords = GetEntityCoords(PlayerPedId())
cam = CreateCamWithParams("DEFAULT_SCRIPTED_CAMERA", camCoords.x, camCoords.y, camCoords.z+0.5, 1.0, 0.0, 0.0, 60.00, false, 0)
PointCamAtCoord(cam, coords)
end
return cam
end
function keyGen()
local charset = {
"q","w","e","r","t","y","u","i","o","p","a","s","d","f","g","h","j","k","l","z","x","c","v","b","n","m",
"Q","W","E","R","T","Y","U","I","O","P","A","S","D","F","G","H","J","K","L","Z","X","C","V","B","N","M",
"1","2","3","4","5","6","7","8","9","0"
}
local GeneratedID = ""
for i = 1, 3 do GeneratedID = GeneratedID..charset[math.random(1, #charset)] end
return GeneratedID
end
function startTempCam(cam)
if Config.Crafting.craftCam then
SetCamActive(cam, true)
RenderScriptCams(true, true, 1000, true, true)
end
end
function stopTempCam()
if Config.Crafting.craftCam then
CreateThread(function()
Wait(1000)
RenderScriptCams(false, true, 500, true, true)
DestroyAllCams()
end)
end
end
local inProgress = false
function progressBar(data)
local result = nil
if data.cam then
startTempCam(data.cam)
end
if Config.System.ProgressBar == "ox" then
if exports[OXLibExport]:progressBar({
duration = Config.System.Debug and 1000 or data.time,
label = data.label,
useWhileDead = data.dead and data.dead or false,
canCancel = data.cancel and data.cancel or true,
anim = {
dict = data.dict,
clip = data.anim,
flag = (data.flag == 8 and 32 or data.flag) or nil,
scenario = data.task
},
disable = {
combat = true
},
}) then
result = true
lockInv(false)
if data.cam then stopTempCam(data.cam) end
else
result = false
lockInv(false)
if data.cam then stopTempCam(data.cam) end
end
elseif Config.System.ProgressBar == "qb" then
Core.Functions.Progressbar("mechbar",
data.label,
Config.System.Debug and 1000 or data.time,
data.dead and data.dead or false,
data.cancel or true,
{ disableMovement = true, disableCarMovement = true, disableMouse = false, disableCombat = true, },
{ animDict = data.dict, anim = data.anim, flags = data.flag and data.flag or 32, task = data.task }, {}, {},
function()
result = true
lockInv(false)
if data.cam then
stopTempCam(data.cam)
end
end, function()
result = false
lockInv(false)
if data.cam then
stopTempCam(data.cam)
end
end, data.icon)
elseif Config.System.ProgressBar == "esx" then
ESX.Progressbar(data.label, Config.System.Debug and 1000 or data.time, {
FreezePlayer = true,
animation ={
type =data.anim,
dict = data.dict,
scenario = data.task,
},
onFinish = function()
result = true
FreezeEntityPosition(PlayerPedId(), false)
lockInv(false)
if data.cam then
stopTempCam(data.cam)
end
end, onCancel = function()
result = false
FreezeEntityPosition(PlayerPedId(), false)
lockInv(false)
if data.cam then
stopTempCam(data.cam)
end
end
})
elseif Config.System.ProgressBar == "gta" then
local wait, inProgress = (Config.System.Debug and 1000 or data.time), true
--local wait = data.time
inProgress = true
if not (data.dead and data.dead or false) then
lockInv(true)
displaySpinner(data.label)
local ped = PlayerPedId()
if data.dict then
playAnim(data.dict, data.anim, -1, (data.flag == 8 and 32 or data.flag) or nil)
end
if data.task then
TaskStartScenarioInPlace(ped, data.task, -1, true)
end
while inProgress and wait > 0 do wait -= 15
local waitTimer = 0
DisablePlayerFiring(ped, true)
DisableControlAction(0, 25, true)
DisableControlAction(0, 21, true)
DisableControlAction(0, 30, true)
DisableControlAction(0, 31, true)
DisableControlAction(0, 36, true)
if data.cam ~= nil then
DisableControlAction(0, 1, true)
DisableControlAction(0, 2, true)
DisableControlAction(0, 106, true)
end
if data.cancel then
if IsControlJustReleased(0, 202) or IsControlJustReleased(0, 77) then
inProgress = false
waitTimer = 1500
displaySpinner(Loc[Config.Lan].error["cancel"])
end
end
Wait(waitTimer)
end
inProgress = false
if data.cam then stopTempCam(data.cam) end
if data.dict then stopAnim(data.dict, data.anim, ped) end
ClearPedTasks(ped)
end
lockInv(false)
stopSpinner()
result = (wait <= 0)
end
while result == nil do Wait(10) end
return result
end
function displaySpinner(text)
BeginTextCommandBusyspinnerOn('STRING')
AddTextComponentSubstringPlayerName(text)
EndTextCommandBusyspinnerOn(4)
end
function stopSpinner() if not IsDuplicityVersion() then BusyspinnerOff() end end
function stopPropgressBar()
if Config.System.ProgressBar == "ox" then
exports[OXLibExport]:cancelProgress()
elseif Config.System.ProgressBar == "qb" then
TriggerEvent("progressbar:client:cancel")
elseif Config.System.ProgressBar == "gta" then
inProgress = false
BusyspinnerOff()
end
end
-- [[ OTHER ]] --
function washHands(data)
lookEnt(data.coords)
local cam = createTempCam(PlayerPedId(), data.coords)
if progressBar({
label = Loc[Config.Lan].progressbar["progress_washing"],
time = 5000,
cancel = true,
dict = "mp_arresting",
anim = "a_uncuff",
flag = 32,
icon = "fas fa-hand-holding-droplet",
cam = cam
}) then
triggerNotify(nil, Loc[Config.Lan].success["washed_hands"], "success")
else
triggerNotify(nil, Loc[Config.Lan].error["cancel"], 'error')
end
ClearPedTasks(PlayerPedId())
end
function useToilet(data)
if data.urinal then
if progressBar({
label = "Using Urinal",
time = 5000,
cancel = true,
dict = "misscarsteal2peeing",
anim = "peeing_loop",
flag = 32
}) then
TriggerServerEvent(GetCurrentResourceName().."server:Urinal")else
lockInv(false)
triggerNotify(nil, Loc[Config.Lan].error["cancelled"], 'error')
end
else
TaskStartScenarioAtPosition(PlayerPedId(), "PROP_HUMAN_SEAT_CHAIR_MP_PLAYER", data.sitcoords.x, data.sitcoords.y, data.sitcoords.z, data.sitcoords[4], 0, 1, true)
if progressBar({
label = "Using Toilet",
time = 10000,
cancel = true
}) then
TriggerServerEvent(GetCurrentResourceName().."server:Urinal")
ClearPedTasks(PlayerPedId())
else
lockInv(false)
triggerNotify(nil, Loc[Config.Lan].error["cancelled"], 'error')
end
end
end
RegisterNetEvent(GetCurrentResourceName()..":server:Urinal", function()
local src = source
local Player = getPlayer(src)
local thirstamt = math.random(10,30)
local thirst = Player.thirst - thirstamt
setThirst(src, getPlayer(src).thirst - thirst)
end)
RegisterNetEvent(GetCurrentResourceName()..":server:setNeed", function(type, amount) local src = source
if type == "thirst" then
setThirst(src, amount)
elseif type == "thirst" then
setHunger(src, amount)
end
end)
function setThirst(src, thirst)
if GetResourceState(ESXExport):find("start") then
TriggerClientEvent('esx_status:add', src, 'thirst', thirst)
elseif GetResourceState(QBExport):find("start") or GetResourceState(QBXExport):find("start") then
local Player = QBCore.Functions.GetPlayer(src)
Player.Functions.SetMetaData('thirst', thirst)
TriggerClientEvent("hud:client:UpdateNeeds", src, thirst, Player.PlayerData.metadata.thirst)
end
end
function setHunger(src, hunger)
if GetResourceState(ESXExport):find("start") then
TriggerClientEvent('esx_status:add', src, 'hunger', hunger)
elseif GetResourceState(QBExport):find("start") or GetResourceState(QBXExport):find("start") then
local Player = QBCore.Functions.GetPlayer(src)
Player.Functions.SetMetaData('hunger', hunger)
TriggerClientEvent("hud:client:UpdateNeeds", src, hunger, Player.PlayerData.metadata.hunger)
end
end
function useDoor(data)
DoScreenFadeOut(500)
while not IsScreenFadedOut() do Wait(10) end
SetEntityCoords(PlayerPedId(), data.telecoords.xyz, 0, 0, 0, false)
SetEntityHeading(PlayerPedId(), data.telecoords.w)
DoScreenFadeIn(1000)
Wait(100)
end
-- [[CONSUME]] --
function ConsumeSuccess(itemName, type)
ExecuteCommand("e c")
toggleItem(false, itemName, 1)
if GetResourceState(ESXEport):find("start") then
if Items[itemName].hunger then
TriggerServerEvent(GetCurrentResourceName()..":server:setNeed", "hunger", Items[itemName].hunger * 10000)
end
if Items[itemName].thirst then
TriggerServerEvent(GetCurrentResourceName()..":server:setNeed", "thirst", Items[itemName].thirst * 10000)
end
else
if Items[itemName].hunger then
TriggerServerEvent(GetCurrentResourceName()..":server:setNeed", "hunger", Core.Functions.GetPlayerData().metadata["hunger"] + Items[itemName].hunger)
end
if Items[itemName].thirst then
TriggerServerEvent(GetCurrentResourceName()..":server:setNeed", "thirst", Core.Functions.GetPlayerData().metadata["thirst"] + Items[itemName].thirst)
end
end
if type == "alcohol" then alcoholCount += 1
if alcoholCount > 1 and alcoholCount < 4 then TriggerEvent("evidence:client:SetStatus", "alcohol", 200) elseif alcoholCount >= 4 then TriggerEvent("evidence:client:SetStatus", "heavyalcohol", 200) AlienEffect() end
end
if Config.RewardItem == itemName then toggleItem(true, Config.RewardPool[math.random(1, #Config.RewardPool)], 1) end
end
function addItem(item, amount, info)
TriggerServerEvent(GetCurrentResourceName()..":server:toggleItem", true, item, amount)
end
function removeItem(item, amount)
TriggerServerEvent(GetCurrentResourceName()..":server:toggleItem", false, item, amount)
end
RegisterNetEvent(GetCurrentResourceName()..":server:toggleItem", function(give, item, amount, newsrc)
local src = newsrc or source
if Config.System.Debug then
print("^6Bridge^7: ^3toggleItem ^2triggered^7: ^6"..(tostring(give) == "true" and "addItem" or "removeItem").."^7 - ['^8"..tostring(item).."^7'] x"..(tostring(amount) or "1"))
end
local remamount = (amount and amount or 1)
if item == nil then return end
if give == 0 or give == false then
if hasItem(item, amount and amount or 1, src) then -- check if you still have the item
if GetResourceState(OXInv):find("start") then
local success = exports[OXInv]:RemoveItem(src, item, (amount and amount or 1), nil)
if Config.System.Debug then
print("^6Bridge^7: ^1Removing ^2from Player^7(^2"..src.."^7) '^6"..Items[item].label.."^7(^2x^6"..((amount and amount or "1")).."^7) - '"..OXInv)
end
elseif GetResourceState(QSInv):find("start") then
local success = exports[QSInv]:RemoveItem(src, item, amount)
if Config.System.Debug then
print("^6Bridge^7: ^1Removing ^2from Player^7(^2"..src.."^7) '^6"..Items[item].label.."^7(^2x^6"..((amount and amount or "1")).."^7) - '"..QSInv)
end
elseif GetResourceState(CoreInv):find("start") then
local success = exports[CoreInv]:removeItemExact('primary-'..src, item, amount)
if Config.System.Debug then
print("^6Bridge^7: ^1Removing ^2from Player^7(^2"..src.."^7) '^6"..Items[item].label.."^7(^2x^6"..((amount and amount or "1")).."^7) - '"..CoreInv)
end
elseif GetResourceState(CodeMInv):find("start") then
local success = exports[CodeMInv]:RemoveItem(src, item, amount)
if Config.System.Debug then
print("^6Bridge^7: ^1Removing ^2from Player^7(^2"..src.."^7) '^6"..Items[item].label.."^7(^2x^6"..((amount and amount or "1")).."^7) - '"..CodeMInv)
end
elseif GetResourceState(QBInv):find("start") then
while remamount > 0 do
if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1) then end
remamount -= 1
end
if Config.Crafting.showItemBox then
TriggerClientEvent('inventory:client:ItemBox', src, Items[item], "remove", (amount and amount or 1))
end
if Config.System.Debug then
print("^6Bridge^7: ^1Removing ^2from Player^7(^2"..src.."^7) '^6"..Items[item].label.."^7(^2x^6"..((amount and amount or "1")).."^7) - '"..QBInv)
end
else
print("^4Error^7: ^2No Inventory detected ^7- ^2Check ^3exports^1.^2lua^7")
end
else
dupeWarn(src, item) -- if not boot the player
end
else
local amount = amount and amount or 1
if GetResourceState(OXInv):find("start") then
local success = exports[OXInv]:AddItem(src, item, amount or 1, nil)
if Config.System.Debug then
print("^6Bridge^7: ^4Giving ^2Player^7(^2"..src.."^7) '^6"..Items[item].label.."^7(^2x^6"..((amount and amount or "1")).."^7) ^7"..OXInv)
end
elseif GetResourceState(QSInv):find("start") then
local success = exports[QSInv]:AddItem(src, item, amount)
if Config.System.Debug then
print("^6Bridge^7: ^4Giving ^2Player^7(^2"..src.."^7) '^6"..Items[item].label.."^7(^2x^6"..((amount and amount or "1")).."^7) ^7"..QSInv)
end
elseif GetResourceState(CoreInv):find("start") then
local success = exports[CoreInv]:addItem('primary-'..src, item, amount)
if Config.System.Debug then
print("^6Bridge^7: ^4Giving ^2Player^7(^2"..src.."^7) '^6"..Items[item].label.."^7(^2x^6"..((amount and amount or "1")).."^7) ^7"..CoreInv)
end
elseif GetResourceState(CodeMInv):find("start") then
local success = exports[CodeMInv]:AddItem(src, item, amount)
if Config.System.Debug then
print("^6Bridge^7: ^4Giving ^2Player^7(^2"..src.."^7) '^6"..Items[item].label.."^7(^2x^6"..((amount and amount or "1")).."^7) ^7"..CodeMInv)
end
elseif GetResourceState(QBInv):find("start") then
if Core.Functions.GetPlayer(src).Functions.AddItem(item, amount or 1) then
--if Config.Crafting.showItemBox then
TriggerClientEvent("inventory:client:ItemBox", src, Items[item], "add", amount and amount or 1)
--end
end
if Config.System.Debug then
print("^6Bridge^7: ^4Giving ^2Player^7(^2"..src.."^7) '^6"..Items[item].label.."^7(^2x^6"..((amount and amount or "1")).."^7) ^7"..QBInv)
end
else
print("^4Error^7: ^2No Inventory detected ^7- ^2Check ^3exports^1.^2lua^7")
end
end
end)
--Item Exploit protection
function dupeWarn(src, item)
print("^5DupeWarn^7: (^1"..tostring(src).."^7) ^2Tried to remove item ^7('^3"..item.."^7')^2 but it wasn't there^7")
if Config.System.Debug == false then
DropPlayer(src, src.." ^1Kicked for attempting to duplicate items")
end
print("^5DupeWarn:^7: (^1"..tostring(src).."^7) ^2Dropped from server - exploit protection detected an item not being found in players inventory^7")
end
function toggleDuty()
if GetResourceState(QBExport):find("start") or GetResourceState(QBXExport):find("start") then
TriggerServerEvent("QBCore:ToggleDuty")
else
onDuty = not onDuty
if onDuty then
triggerNotify(nil, "Now on duty", "success")
else
triggerNotify(nil, "Now off duty", "success")
end
end
end
local function CheckVersion()
if IsDuplicityVersion() then
PerformHttpRequest('https://raw.githubusercontent.com/jimathy/UpdateVersions/master/'..GetCurrentResourceName()..'.txt', function(err, newestVersion, headers)
if not newestVersion then print("Currently unable to run a version check.") return end
local currentVersion = "^3"..GetResourceMetadata(GetCurrentResourceName(), 'version'):gsub("%.", "^7.^3").."^7"
newestVersion = "^3"..newestVersion:sub(1, -2):gsub("%.", "^7.^3").."^7"
print("^6Version Check^7: ^2Running^7: "..currentVersion.." ^2Latest^7: "..newestVersion)
print(newestVersion == currentVersion and '^6You are running the latest version.^7 ('..currentVersion..')' or "^1You are currently running an outdated version^7, ^1please update^7!")
end)
end
end
CheckVersion()
--Screen Effects
local alienEffect = false
function AlienEffect()
if alienEffect then return else alienEffect = true end
if Config.System.Debug then print("^5Debug^7: ^3AlienEffect^7() ^2activated") end
AnimpostfxPlay("DrugsMichaelAliensFightIn", 3.0, 0)
Wait(math.random(5000, 8000))
local Ped = PlayerPedId()
local animDict = "MOVE_M@DRUNK@VERYDRUNK"
loadAnimDict(animDict)
SetPedCanRagdoll(Ped, true)
ShakeGameplayCam('DRUNK_SHAKE', 2.80)
SetTimecycleModifier("Drunk")
SetPedMovementClipset(Ped, animDict, 1)
SetPedMotionBlur(Ped, true)
SetPedIsDrunk(Ped, true)
Wait(1500)
SetPedToRagdoll(Ped, 5000, 1000, 1, 0, 0, 0)
Wait(13500)
SetPedToRagdoll(Ped, 5000, 1000, 1, 0, 0, 0)
Wait(120500)
ClearTimecycleModifier()
ResetScenarioTypesEnabled()
ResetPedMovementClipset(Ped, 0)
SetPedIsDrunk(Ped, false)
SetPedMotionBlur(Ped, false)
AnimpostfxStopAll()
ShakeGameplayCam('DRUNK_SHAKE', 0.0)
AnimpostfxPlay("DrugsMichaelAliensFight", 3.0, 0)
Wait(math.random(45000, 60000))
AnimpostfxPlay("DrugsMichaelAliensFightOut", 3.0, 0)
AnimpostfxStop("DrugsMichaelAliensFightIn")
AnimpostfxStop("DrugsMichaelAliensFight")
AnimpostfxStop("DrugsMichaelAliensFightOut")
alienEffect = false
if Config.System.Debug then print("^5Debug^7: ^3AlienEffect^7() ^2stopped") end
end
local weedEffect = false
function WeedEffect()
if weedEffect then return else weedEffect = true end
if Config.System.Debug then print("^5Debug^7: ^3WeedEffect^7() ^2activated") end
AnimpostfxPlay("DrugsMichaelAliensFightIn", 3.0, 0)
Wait(math.random(3000, 20000))
AnimpostfxPlay("DrugsMichaelAliensFight", 3.0, 0)
Wait(math.random(15000, 20000))
AnimpostfxPlay("DrugsMichaelAliensFightOut", 3.0, 0)
AnimpostfxStop("DrugsMichaelAliensFightIn")
AnimpostfxStop("DrugsMichaelAliensFight")
AnimpostfxStop("DrugsMichaelAliensFightOut")
weedEffect = false
if Config.System.Debug then print("^5Debug^7: ^3WeedEffect^7() ^2stopped") end
end
local trevorEffect = false
function TrevorEffect()
if trevorEffect then return else trevorEffect = true end
if Config.System.Debug then print("^5Debug^7: ^3TrevorEffect^7() ^2activated") end
AnimpostfxPlay("DrugsTrevorClownsFightIn", 3.0, 0)
Wait(3000)
AnimpostfxPlay("DrugsTrevorClownsFight", 3.0, 0)
Wait(30000)
AnimpostfxPlay("DrugsTrevorClownsFightOut", 3.0, 0)
AnimpostfxStop("DrugsTrevorClownsFight")
AnimpostfxStop("DrugsTrevorClownsFightIn")
AnimpostfxStop("DrugsTrevorClownsFightOut")
trevorEffect = false
if Config.System.Debug then print("^5Debug^7: ^3TrevorEffect^7() ^2stopped") end
end
local turboEffect = false
function TurboEffect()
if turboEffect then return else turboEffect = true end
if Config.System.Debug then print("^5Debug^7: ^3TurboEffect^7() ^2activated") end
AnimpostfxPlay('RaceTurbo', 0, true)
SetTimecycleModifier('rply_motionblur')
ShakeGameplayCam('SKY_DIVING_SHAKE', 0.25)
Wait(30000)
StopGameplayCamShaking(true)
SetTransitionTimecycleModifier('default', 0.35)
Wait(1000)
ClearTimecycleModifier()
AnimpostfxStop('RaceTurbo')
turboEffect = false
if Config.System.Debug then print("^5Debug^7: ^3TurboEffect^7() ^2stopped") end
end
local rampageEffect = false
function RampageEffect()
if rampageEffect then return else rampageEffect = true end
if Config.System.Debug then print("^5Debug^7: ^3RampageEffect^7() ^2activated") end
AnimpostfxPlay('Rampage', 0, true)
SetTimecycleModifier('rply_motionblur')
ShakeGameplayCam('SKY_DIVING_SHAKE', 0.25)
Wait(30000)
StopGameplayCamShaking(true)
SetTransitionTimecycleModifier('default', 0.35)
Wait(1000)
ClearTimecycleModifier()
AnimpostfxStop('Rampage')
rampageEffect = false
if Config.System.Debug then print("^5Debug^7: ^3RampageEffect^7() ^2stopped") end
end
local focusEffect = false
function FocusEffect()
if focusEffect then return else focusEffect = true end
if Config.System.Debug then print("^5Debug^7: ^3FocusEffect^7() ^2activated") end
Wait(1000)
AnimpostfxPlay('FocusIn', 0, true)
Wait(30000)
AnimpostfxStop('FocusIn')
focusEffect = false
if Config.System.Debug then print("^5Debug^7: ^3FocusEffect^7() ^2stopped") end
end
local NightVisionEffect = false
function NightVisionEffect()
if NightVisionEffect then return else NightVisionEffect = true end
if Config.System.Debug then print("^5Debug^7: ^3NightVisionEffect^7() ^2activated") end
SetNightvision(true)
Wait(math.random(3000, 4000)) -- FEEL FREE TO CHANGE THIS
SetNightvision(false)
SetSeethrough(false)
NightVisionEffect = false
if Config.System.Debug then print("^5Debug^7: ^3NightVisionEffect^7() ^2stopped") end
end
local thermalEffect = false
function ThermalEffect()
if thermalEffect then return else thermalEffect = true end
if Config.System.Debug then print("^5Debug^7: ^3ThermalEffect^7() ^2activated") end
SetNightvision(true)
SetSeethrough(true)
Wait(math.random(2000, 3000)) -- FEEL FREE TO CHANGE THIS
SetNightvision(false)
SetSeethrough(false)
thermalEffect = false
if Config.System.Debug then print("^5Debug^7: ^3ThermalEffect^7() ^2stopped") end
end
--Built-in Buff effects
local healEffect = false
function HealEffect(data)
if healEffect then return end
if Config.System.Debug then print("^5Debug^7: ^3HealEffect^7() ^2activated") end
healEffect = true
local count = (data[1] / 1000)
while count > 0 do
Wait(1000)
count -= 1
SetEntityHealth(PlayerPedId(), GetEntityHealth(PlayerPedId()) + data[2])
end
healEffect = false
if Config.System.Debug then print("^5Debug^7: ^3HealEffect^7() ^2stopped") end
end
local staminaEffect = false
function StaminaEffect(data)
if staminaEffect then return end
if Config.System.Debug then print("^5Debug^7: ^3StaminaEffect^7() ^2activated") end
staminaEffect = true
local startStamina = (data[1] / 1000)
SetRunSprintMultiplierForPlayer(PlayerId(), 1.49)
while startStamina > 0 do
Wait(1000)
if math.random(5, 100) < 10 then RestorePlayerStamina(PlayerId(), data[2]) end
startStamina -= 1
if math.random(5, 100) < 51 then end
end
startStamina = 0
SetRunSprintMultiplierForPlayer(PlayerId(), 1.0)
staminaEffect = false
if Config.System.Debug then print("^5Debug^7: ^3StaminaEffect^7() ^2stopped") end
end
function StopEffects() -- Used to clear up any effects stuck on screen
if Config.System.Debug then print("^5Debug^7: ^2All screen effects stopped") end
ShakeGameplayCam('DRUNK_SHAKE', 0.0)
SetPedToRagdoll(PlayerPedId(), 5000, 1000, 1, 0, 0, 0)
ClearTimecycleModifier()
ResetScenarioTypesEnabled()
ResetPedMovementClipset(PlayerPedId(), 0)
SetPedIsDrunk(PlayerPedId(), false)
SetPedMotionBlur(PlayerPedId(), false)
SetNightvision(false)
SetSeethrough(false)
AnimpostfxStop("DrugsMichaelAliensFightIn")
AnimpostfxStop("DrugsMichaelAliensFight")
AnimpostfxStop("DrugsMichaelAliensFightOut")
AnimpostfxStop("DrugsTrevorClownsFight")
AnimpostfxStop("DrugsTrevorClownsFightIn")
AnimpostfxStop("DrugsTrevorClownsFightOut")
AnimpostfxStop('RaceTurbo')
AnimpostfxStop('FocusIn')
AnimpostfxStop('Rampage')
end

View File

@@ -1,14 +1,14 @@
name "Jim_Bridge"
author "Jimathy"
version "1.0"
description "Framework Bridge By Jimathy"
fx_version "cerulean"
game "gta5"
lua54 'yes'
files {
'exports.lua',
'functions.lua',
'wrapper.lua',
'crafting.lua',
}
name "Jim_Bridge"
author "Jimathy"
version "2.0"
description "Framework Bridge By Jimathy"
fx_version "cerulean"
game "gta5"
lua54 'yes'
files {
'starter.lua',
'shared/*.lua',
'shared/make/*.lua',
'shared/scaleforms/*.lua',
}

149
shared/_eventDebug.lua Normal file
View File

@@ -0,0 +1,149 @@
-- IN NO WAY PERFECT -- ** Experimental debugging
function toggleDebug()
Config.System.Debug = not Config.System.Debug
print("Debug Prints = "..tostring(Config.System.Debug))
end
exports("toggleDebug", toggleDebug)
function getDebug() return Config.System.Debug end
exports("getDebug", getDebug)
local origRegisterNetEvent = RegisterNetEvent
local origTriggerEvent = TriggerEvent
local origTriggerServerEvent = TriggerServerEvent
local origTriggerClientEvent = TriggerClientEvent
local origExecuteCommand = ExecuteCommand
local origRegisterCommand = RegisterCommand
local origPairs = pairs
local origiPairs = ipairs
function getDebugInfo(info)
local info = info
local level = 2
if info and info.short_src:match("scheduler.lua") then
repeat
info = debug.getinfo(level, "nSl")
level += 1
local found = false
for _, v in pairs({
"deffered.lua",
"scheduler.lua",
"_eventDebug.lua",
"targets.lua",
"init.lua",
"MySQL.lua",
"helpers.lua",
}) do
if info and info.short_src:match(v) then
found = true
end
end
until not info or (info.short_src and found == false)
end
return " ^7[^3"..(info and info.short_src:match("^.+/(.+)$") or "unknown").."^7:^3"..(info and info.currentline or "unknown").."^7]"
end
--This is just a for debugging, not important, just announces which events are being registered triggered when these functions are used
function RegisterNetEvent(name, funct)
if Config.System.EventDebug then
if name:find("__ox_cb_") then
print("^6Bridge^7: ^2Registered ^3"..(isServer() and "Server" or "Client").." ^2Callback^7: ^6"..name:gsub("__ox_cb_", ""):gsub("%:", "^7:^4").."^7"..getDebugInfo(debug.getinfo(2, "nSl")))
else
print("^6Bridge^7: ^2Registering ^3"..(isServer() and "Server" or "Client").." ^2Net event^7: ^6"..name:gsub("%:", "^7:^4").."^7"..getDebugInfo(debug.getinfo(2, "nSl")))
end
end
origRegisterNetEvent(name, funct)
end
function TriggerEvent(name, ...)
local data = {...}
if Config.System.EventDebug then
if name:find("__cfx_export") then
print("^6Bridge^7: "..GetPrintTime().." ^2Triggering ^3"..(isServer() and "Server" or "Client").." ^2Export^7: ^6"..name:gsub("__cfx_export_", ""):gsub("%:", "^7:^4").."^7"..getDebugInfo(debug.getinfo(2, "nSl")))
else
print("^6Bridge^7: "..GetPrintTime().." ^2Triggering ^3"..(isServer() and "Server" or "Client").." ^2Net event^7: ^6"..name:gsub("%:", "^7:^4").."^7"..getDebugInfo(debug.getinfo(2, "nSl")))
end
for i, value in ipairs(data) do
if value then
local valueStr = (type(value) == "table" and json.encode(value) or tostring(value))
print(string.format("^6Bridge^7: ^7[^3%d^7]: ^7(^5%s^7): %s".."^7", i, type(value), valueStr))
end
end end
origTriggerEvent(name, ...)
end
function TriggerServerEvent(name, ...) -- Client side, trigger a server event
local data = {...}
if Config.System.EventDebug then
if name:find("__ox_cb") then
print("^6Bridge^7: "..GetPrintTime().." ^2Triggered ^3Server ^2Callback: ^6"..name:gsub("__ox_cb_", "").."^7"..getDebugInfo(debug.getinfo(2, "nSl")))
else
print("^6Bridge^7: "..GetPrintTime().." ^2Triggering ^3Server ^2Net event^7: ^6"..name:gsub("%:", "^7:^4").."^7"..getDebugInfo(debug.getinfo(2, "nSl")))
end
for i, value in ipairs(data) do
if value then
local valueStr = (type(value) == "table" and json.encode(value) or tostring(value))
print(string.format("^6Bridge^7: ^7[^3%d^7]: ^7(^5%s^7): %s".."^7", i, type(value), valueStr))
end
end end
origTriggerServerEvent(name, ...)
end
function TriggerClientEvent(name, ...) -- Server side, trigger a client event
local data = {...}
if Config.System.EventDebug then
if name:find("__ox_cb") then
print("^6Bridge^7: "..GetPrintTime().." ^2Triggered ^3Client ^2Callback: ^6"..name:gsub("__ox_cb_", "").."^7"..getDebugInfo(debug.getinfo(2, "nSl")))
else
print("^6Bridge^7: "..GetPrintTime().." ^2Triggering ^3Client ^2Net event^7: ^6"..name:gsub("%:", "^7:^4").."^7"..getDebugInfo(debug.getinfo(2, "nSl")))
end
for i, value in ipairs(data) do
if value then
local valueStr = (type(value) == "table" and json.encode(value) or tostring(value))
print(string.format("^6Bridge^7: ^7[^3%d^7]: ^7(^5%s^7): %s".."^7", i, type(value), valueStr))
end
end end
origTriggerClientEvent(name, ...)
end
function RegisterCommand(command, funct, restrict)
if Config.System.EventDebug then
print("^6Bridge^7: ^2Registering ^2Command^7: /"..command.." ^7| ^4Funct^7: "..tostring(funct):gsub("function: ", "").." ^7| ^4Admin^7: "..(restict and "true" or "false")..getDebugInfo(debug.getinfo(2, "nSl")))
end
origRegisterCommand(command, funct, restrict)
end
function ExecuteCommand(comm) -- Client side, execute /command
if Config.System.EventDebug then
print("^6Bridge^7: "..GetPrintTime().." ^2Triggering ^3ExecuteCommand^7: /"..comm..getDebugInfo(debug.getinfo(2, "nSl")))
end
origExecuteCommand(comm)
end
function pairs(tbl)
if not tbl then
print("^1Error^7: ^1nil ^2for ^3pairs^7(), ^2setting to ^7{} ^2to prevent break^7"..getDebugInfo(debug.getinfo(2, "nSl")))
return origPairs({})
end
return origPairs(tbl)
end
function ipairs(tbl)
local tbl = tbl
if not tbl then
if Config.System.EventDebug then
print("^1Error^7: ^3iPairs^7() ^1nil ^2recieved^7, ^2setting to ^7{} ^2to prevent break^7"..getDebugInfo(debug.getinfo(2, "nSl")))
end
tbl = {}
end
return pairsByKeys(tbl) -- change to pairsByKeys for less errors
end
--[[
local origPrint = print
function print(...)
origPrint(getDebugInfo(debug.getinfo(2, "nSl"))..":")
origPrint(...)
end
]]

110
shared/_loaders.lua Normal file
View File

@@ -0,0 +1,110 @@
--- Executes a function when the player character is loaded into the game.
---
--- This function sets up event listeners for player loading based on the game framework detected (e.g., QB, ESX, OX).
---
--- If `onStart` is `true`, it will also attempt to execute the function on resource start after ensuring the player is logged in. (Helpful for debugging)
---
--- @param func function The function to execute when the player is loaded.
--- @param onStart boolean (optional) If `true`, the function will also execute on resource start. Default is `false`.
---
--- @usage
--- ```lua
--- onPlayerLoaded(function()
--- -- Your code here
--- end, true)
--- ```
function onPlayerLoaded(func, onStart)
local onPlayerName = ""
local loaded = false
if onStart then
onResourceStart(function()
if not LocalPlayer.state.isLoggedIn then
Wait(3000)
if not LocalPlayer.state.isLoggedIn then -- If the player is not logged in after waiting, skip execution
return
end
end
loaded = true -- Mark as already loaded
debugPrint("^6Bridge^7: ^2Loading ^3onResourceStart^7() ^2through ^3onPlayerLoaded^7()")
Wait(2000)
func()
end, true)
end
if not loaded then
local tempFunc = function()
debugPrint("^6Bridge^7: ^2Executing ^3onPlayerLoaded^7()")
func()
end
if isStarted(QBExport) or isStarted(QBXExport) then onPlayerName = QBExport
AddEventHandler('QBCore:Client:OnPlayerLoaded', tempFunc)
elseif isStarted(ESXExport) then onPlayerName = ESXExport
AddEventHandler('esx:playerLoaded', tempFunc)
elseif isStarted(OXCoreExport) then onPlayerName = OXCoreExport
AddEventHandler('ox:playerLoaded', tempFunc)
end
if onPlayerName ~= "" then
debugPrint("^6Bridge^7: ^2Registering ^3onPlayerLoaded^2 with ^7"..onPlayerName)
else
print("^4ERROR^7: ^2No Core detected for onPlayerLoaded ^7- ^2Check ^3exports^1.^2lua^7")
end
end
end
--- Executes a function when the resource starts.
---
--- This function wraps the `onResourceStart` event, allowing you to execute code when the resource starts.
---
--- @param func function The function to execute on resource start.
--- @param thisScript boolean (optional) If `true`, only runs the function when this resource starts. Default is `true`.
---
--- @usage
--- ```lua
--- onResourceStart(function()
--- -- Your code here
--- end, true)
--- ```
function onResourceStart(func, thisScript)
debugPrint("^6Bridge^7: ^2Registering ^3onResourceStart^2")
AddEventHandler('onResourceStart', function(resourceName)
if getScript() == resourceName and (thisScript or true) then
func()
end
end)
end
--- Executes a function when the resource stops.
---
--- This function wraps the `onResourceStop` event, allowing you to execute code when the resource stops.
---
--- @param func function The function to execute on resource stop.
--- @param thisScript boolean (optional) If `true`, only runs the function when this resource stops. Default is `true`.
---
--- @usage
--- ```lua
--- onResourceStop(function()
--- -- Cleanup code here
--- end, true)
--- ```
function onResourceStop(func, thisScript)
debugPrint("^6Bridge^7: ^2Registering ^3onResourceStop^2")
AddEventHandler('onResourceStop', function(resourceName)
if getScript() == resourceName and (thisScript or true) then
func()
end
end)
end
--- Waits until the player is logged in before continuing execution.
---
--- This function blocks execution until `LocalPlayer.state.isLoggedIn` is `true`.
---
---@usage
--- ```lua
--- waitForLogin()
--- ```
function waitForLogin()
while not LocalPlayer.state.isLoggedIn do
debugPrint("Waiting")
Wait(100)
end
end

68
shared/callback.lua Normal file
View File

@@ -0,0 +1,68 @@
--- Registers a callback function with the appropriate framework.
---
--- This function checks which framework is started (e.g., OX, QB, ESX) and registers the callback accordingly.
--- It adapts the callback function to match the expected signature for the framework.
---
---@param callbackName string The name of the callback to register.
---@param funct function The function to be called when the callback is triggered.
---
---@usage
--- ```lua
--- createCallback('myCallback', function(source, ...)
--- -- Your callback code here
--- end)
--- ```
function createCallback(callbackName, funct)
if isStarted(OXLibExport) then
lib.callback.register(callbackName, funct)
else
local adaptedFunction = function(source, cb, ...)
local result = funct(source, ...)
cb(result)
end
if isStarted(QBExport) then
Core = Core or exports[QBExport]:GetCoreObject()
Core.Functions.CreateCallback(callbackName, adaptedFunction)
elseif isStarted(ESXExport) then
ESX.RegisterServerCallback(callbackName, adaptedFunction)
else
print("^6Bridge^7: ^1ERROR^7: ^3Can't find any script to register callback with", callbackName)
end
end
end
--- Triggers a server callback and returns the result.
---
--- This function triggers a server callback using the appropriate framework's method and awaits the result.
---
---@param callbackName string The name of the callback to trigger.
---@param ... any Additional arguments to pass to the callback.
---
---@return any any The result returned by the callback function.
---
---@usage
--- ```lua
--- local result = triggerCallback('myCallback', arg1, arg2)
--- ```
function triggerCallback(callbackName, ...)
local result = nil
if isStarted(OXLibExport) then
result = lib.callback.await(callbackName, false, ...)
elseif isStarted(QBExport) then
local p = promise.new()
Core.Functions.TriggerCallback(callbackName, function(cbResult)
p:resolve(cbResult)
end, ...)
result = Citizen.Await(p)
elseif isStarted(ESXExport) then
local p = promise.new()
ESX.TriggerServerCallback(callbackName, function(cbResult)
p:resolve(cbResult)
end, ...)
result = Citizen.Await(p)
else
print("^6Bridge^7: ^1ERROR^7: ^3Can't find any script to trigger callback with", callbackName)
end
return result
end

290
shared/contextmenus.lua Normal file
View File

@@ -0,0 +1,290 @@
--- Opens a menu using the configured menu system.
---
--- This function translates code and creates menus in several different menu scripts, depending on the configured menu system specified in `Config.System.Menu`.
---
---@param Menu table A table containing the menu options to display.
--- Each menu item can include:
--- - **header** (`string`): The text to display for the menu item.
--- - **txt** (`string`, optional): Additional text or description.
--- - **icon** (`string`, optional): Icon to display with the menu item.
--- - **onSelect** (`function`, optional): Function to execute when the menu item is selected.
--- - **arrow** (`boolean`, optional): Whether to display an arrow next to the item (for certain menus).
--- - **params** (`table`, optional): Additional parameters, such as events and arguments.
--- - **isMenuHeader** (`boolean`, optional): Marks the item as a header.
--- - **disabled** (`boolean`, optional): Disables the menu item if `true`.
---
---@param data table A table containing configuration data for the menu.
--- - **header** (`string`): The header/title of the menu.
--- - **headertxt** (`string`, optional): Additional header text.
--- - **onBack** (`function`, optional): Function to call when the "Return" option is selected.
--- - **onExit** (`function`, optional): Function to call when the menu is exited.
--- - **onSelected** (`function`, optional): Function to call when a menu item is selected (for certain menu systems).
--- - **canClose** (`boolean`, optional): Whether the menu can be closed by the user.
---
---@usage
--- ```lua
--- openMenu({
--- { header = "Option 1", txt = "Description 1", onSelect = function() print("Option 1 selected") end },
--- { header = "Option 2", txt = "Description 2", onSelect = function() print("Option 2 selected") end },
--- }, {
--- header = "Main Menu",
--- headertxt = "Select an option",
--- onBack = function() print("Return selected") end,
--- onExit = function() print("Menu closed") end,
--- canClose = true,
--- })
--- ```
function openMenu(Menu, data)
if Config.System.Menu == "jim" then
if data.onBack then
table.insert(Menu, 1, {
icon = "fas fa-circle-arrow-left",
title = "Return",
onSelect = data.onBack,
})
end
exports["jim-nui"]:openMenu({
title = data.header..(data.headertxt and " -- "..data.headertxt or ""),
canClose = data.canClose and data.canClose or nil,
onClose = (data.onBack and data.onBack) or (data.onExit and data.onExit) or nil,
onExit = data.onExit and data.onExit or nil,
options = Menu,
})
elseif Config.System.Menu == "ox" then
local index = nil
if data.onBack and not data.onSelected then
table.insert(Menu, 1, {
icon = "fas fa-circle-arrow-left",
title = "Return",
onSelect = data.onBack,
label = "Return",
})
end
for k in pairs(Menu) do
if data.onSelected and Menu[k].arrow then
Menu[k].icon = "fas fa-angle-right"
end
if not Menu[k].title then
if Menu[k].header ~= nil and Menu[k].header ~= "" then
Menu[k].title = Menu[k].header
Menu[k].label = Menu[k].header
if Menu[k].txt then Menu[k].description = Menu[k].txt else Menu[k].description = "" end
else
Menu[k].title = Menu[k].txt
Menu[k].label = Menu[k].txt
end
end
if Menu[k].params then
Menu[k].event = Menu[k].params.event
Menu[k].args = Menu[k].params.args or {}
end
if Menu[k].isMenuHeader then
Menu[k].disabled = true
end
end
local menuID = 'Menu'
(data.onSelected and lib.registerMenu or lib.registerContext)({
id = menuID,
title = data.header..br..br..(data.headertxt and data.headertxt or ""),
position = 'top-right',
options = Menu,
canClose = data.canClose and data.canClose or nil,
onClose = (data.onBack and data.onBack) or (data.onExit and data.onExit) or nil,
onExit = data.onExit and data.onExit or nil,
onSelected = data.onSelected and (function(selected) index = selected end) or nil,
}, data.onSelected and (function(x, y, args)
if Menu[x].refresh then
if Menu[x].onSelect then
Menu[x].onSelect()
end
lib.showMenu(menuID, index)
else
if Menu[x].onSelect then
Menu[x].onSelect()
else
lib.showMenu(menuID, index)
end
end
end) or nil)
if data.onSelected then
lib.showMenu(menuID, 1)
else
lib.showContext(menuID)
end
elseif Config.System.Menu == "qb" then
if data.onBack then
table.insert(Menu, 1, {
icon = "fas fa-circle-arrow-left",
header = " ",
txt = "Return",
params = {
isAction = true,
event = data.onBack,
},
})
elseif data.canClose then
table.insert(Menu, 1, {
icon = "fas fa-circle-xmark",
header = " ",
txt = "Close",
params = {
isAction = true,
event = data.onExit and data.onExit or (function() exports[QBMenuExport]:closeMenu() end),
},
})
end
if data.header ~= nil then
local tempMenu = {}
for k, v in pairs(Menu) do tempMenu[k + 1] = v end
tempMenu[1] = { header = data.header, txt = data.headertxt or "", isMenuHeader = true }
Menu = tempMenu
end
for k in pairs(Menu) do
if not Menu[k].params or not Menu[k].params.event then
if Menu[k].onSelect then
Menu[k].params = {
isAction = true,
event = Menu[k].onSelect,
}
else
Menu[k].params = {
isAction = true,
event = function() end,
}
end
end
if not Menu[k].header then Menu[k].header = " " end
if Menu[k].arrow then Menu[k].icon = "fas fa-angle-right" end
end
exports[QBMenuExport]:openMenu(Menu)
elseif Config.System.Menu == "gta" then
WarMenu.CreateMenu(tostring(Menu),
data.header,
data.headertxt or " ",
{
titleColor = { 222, 255, 255 },
maxOptionCountOnScreen = 15,
width = 0.25,
x = 0.7,
})
if WarMenu.IsAnyMenuOpened() then return end
WarMenu.OpenMenu(tostring(Menu))
CreateThread(function()
local close = true
while true do
if WarMenu.Begin(tostring(Menu)) then
if data.onBack then
if WarMenu.SpriteButton("Return", 'commonmenu', "arrowleft", 127, 127, 127) then
WarMenu.CloseMenu()
Wait(10)
data.onBack()
end
end
for k in pairs(Menu) do
local pressed = WarMenu.Button(Menu[k].header)
if not Menu[k].header then
Menu[k].header = Menu[k].txt
Menu[k].txt = nil
end
if Menu[k].txt and Menu[k].txt ~= "" and WarMenu.IsItemHovered() then
if Menu[k].disabled or Menu[k].isMenuHeader then
WarMenu.ToolTip("~r~"..Menu[k].txt, 0.18, true)
else
WarMenu.ToolTip(
(Menu[k].blip and "~BLIP_".."8".."~ " or "")..
Menu[k].txt:gsub("%:", ":~g~"):gsub("%\n", "\n~s~"), 0.18,
true)
end
end
if pressed and not Menu[k].isMenuHeader then
WarMenu.CloseMenu()
close = false
Menu[k].onSelect()
end
end
WarMenu.End()
else
return
end
if not WarMenu.IsAnyMenuOpened() and close then
stopTempCam(cam)
if data.onExit then data.onExit() end
end
Wait(0)
end
end)
elseif Config.System.Menu == "esx" then
for k in pairs(Menu) do
Menu[k].label = Menu[k].header
Menu[k].name = "button"..k
end
if data.canClose then
table.insert(Menu, 1, {
icon = "fas fa-circle-xmark",
label = "Close",
name = "close",
onSelect = data.onExit,
})
end
if data.onBack then
table.insert(Menu, 1, {
icon = "fas fa-circle-arrow-left",
label = "Return",
name = "return",
onSelect = data.onBack,
})
end
ESX.UI.Menu.Open("default", getScript(), "Example_Menu", {
title = data.header,
align = 'top-right',
elements = Menu,
},
function(menuData, menu)
for k in pairs(Menu) do
if menuData.current.name == Menu[k].name then
menu.close()
Wait(10)
Menu[k].onSelect()
end
end
end,
function(data, menu)
menu.close()
end)
end
end
--- A line break constant used for formatting menu headers.
br = (Config.System.Menu == "ox" or Config.System.Menu == "gta") and "\n" or "<br>"
--- Checks if the menu system is classified as 'ox' or 'gta'.
---
--- This function is used to decide how to make line breaks in menu headers.
---
--- @return boolean Returns `true` if the menu system is 'ox' or 'gta'; otherwise, `false`.
---
--- @usage
--- ```lua
--- if isOx() then
--- -- Use specific formatting
--- end
--- ```
function isOx() return (Config.System.Menu == "ox" or Config.System.Menu == "gta") end
--- Checks if any WarMenu menu is currently open.
---
--- @return boolean Returns `true` if a WarMenu menu is open; otherwise, `false`.
---
--- @usage
--- ```lua
--- if isWarMenuOpen() then
--- -- Do something
--- end
--- ```
function isWarMenuOpen() if Config.System.Menu == "gta" then return WarMenu.IsAnyMenuOpened() else return false end end

170
shared/coreloader.lua Normal file
View File

@@ -0,0 +1,170 @@
-- Create empty Variables --
Items, Vehicles, Jobs, Gangs, Core, ESX = {}, nil, nil, nil, nil, nil
-- Correct QB inventory export (if needed) from 'qb-inventory' to 'ps-inventory' or 'lj-inventory' --
Exports.QBInv = (isStarted("ps-inventory") and "ps-inventory") or (isStarted("lj-inventory") and "lj-inventory") or Exports.QBInv
-- Create simple variables based on the corresponding framework exports --
OXLibExport, QBXExport, QBExport, ESXExport, OXCoreExport = Exports.OXLibExport or "", Exports.QBXExport or "", Exports.QBExport or "", Exports.ESXExport or "", Exports.OXCoreExport or ""
-- Create simple variables based on the corresponding inventory names --
OXInv, QBInv, PSInv, QSInv, CoreInv, CodeMInv, OrigenInv = Exports.OXInv or "", Exports.QBInv or "", Exports.PSInv or "", Exports.QSInv or "", Exports.CoreInv or "", Exports.CodeMInv or "", Exports.OrigenInv or ""
-- QB-Menu export name grabbed from exports.lua --
QBMenuExport = Exports.QBMenuExport or ""
-- Target exports based on what is loaded --
QBTargetExport, OXTargetExport = Exports.QBTargetExport or "", Exports.OXTargetExport or ""
-- If Debug mode is on in the loading script, print the list of found exports --
-- Some may "lie", 'ox_target' attempts to use 'qb-target' exports and this print will say its loaded (which is technically true) --
for _, v in pairs(Exports) do
if isStarted(v) then debugPrint("^6Bridge^7: '^3"..v.."^7' ^2export found ^7") end
end
local itemResource, jobResource, vehResource = "", "", ""
-- Load item lists --
-- Complies the items from ox_inventory, qb-core or esx into 'Items' and loads them in a layout similar to qb-core's Shared items.lua --
-- For example this makes it so instead of QBCore.Shared.Items[item], you can load 'Item[item]' in the script --
if isStarted(OXInv) then itemResource = OXInv
Items = exports[OXInv]:Items()
for k, v in pairs(Items) do
if v.client and v.client.image then
Items[k].image = (v.client.image):gsub("nui://"..OXInv.."/web/images/", "")
else
Items[k].image = k..".png"
end
Items[k].hunger = v.client and v.client.hunger or nil
Items[k].thirst = v.client and v.client.thirst or nil
end
elseif isStarted(QBExport) then itemResource = QBExport
Core = Core or exports[QBExport]:GetCoreObject()
Items = Core and Core.Shared.Items or nil
if isStarted(QBExport) and not isStarted(QBXExport) then
RegisterNetEvent('QBCore:Client:UpdateObject', function()
Core = Core or exports[QBExport]:GetCoreObject()
Items = Core and Core.Shared.Items or nil
end)
end
elseif isStarted(ESXExport) then itemResource = ESXExport
ESX = exports[ESXExport]:getSharedObject()
Items = ESX and ESX.Items or nil
end
-- If it fails to load items, then it will print the error below --
-- If it loads them and debug is on, print how many items and where from --
if not Items then
print("^4ERROR^7: ^2No Core Items detected ^7- ^2Check ^3exports^1.^2lua^7")
else
debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Items).." ^3Items^2 from ^7" .. itemResource)
end
-- Load Vehicles --
-- Complies the vehicles from the detected frameworks into a table in the style of qb-cores shared vehicles.lua --
-- For example, instead of using QBCore.Shared.Vehicles[vehicle] you can load 'Vehicles[vehicle]' in the script --
if isStarted(QBXExport) or isStarted(QBExport) then
Core = Core or exports[QBExport]:GetCoreObject()
Vehicles = Core and Core.Shared.Vehicles
if isStarted(QBExport) and not isStarted(QBXExport) then
RegisterNetEvent('QBCore:Client:UpdateObject', function()
Core = Core or exports[QBExport]:GetCoreObject()
Vehicles = Core and Core.Shared.Vehicles
end)
end
vehResource = QBExport
elseif isStarted(OXCoreExport) then
Vehicles = {}
for k, v in pairs(Ox.GetVehicleData()) do
Vehicles[k] = { model = k, price = v.price, name = v.name, brand = v.make }
end
vehResource = OXCoreExport
elseif isStarted(ESXExport) then
-- print("^6Bridge^7: ^2Loading ^3Vehicles^2 from ^7"..ESXExport)
-- print("^6Bridge^7: ^2Loading ^6"..countTable(Vehicles).." ^3Vehicles^2 from ^7"..ESXExport)
CreateThread(function()
if isServer() then
createCallback(getScript()..":getVehiclesPrices", function(source)
Vehicles = MySQL.query.await('SELECT model, price, name FROM vehicles')
vehResource = ESXExport
return Vehicles
end)
end
if not isServer() then
local TempVehicles = triggerCallback(getScript()..":getVehiclesPrices")
for _, v in pairs(TempVehicles) do
Vehicles = Vehicles or {}
Vehicles[v.model] = { model = v.model, price = v.price, name = v.name, brand = GetMakeNameFromVehicleModel(v.model):lower():gsub("^%l", string.upper) }
end
end
end)
end
if vehResource == nil then
print("^4ERROR^7: ^2No Vehicle info detected ^7- ^2Check ^3exports^1.^2lua^7")
else
debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Vehicles).." ^3Vehicles^2 from ^7"..vehResource)
end
-- Load Jobs --
-- Attempts to load the details of jobs and gangs and compile into tables --
-- For example, instead of using QBCore.Shared.Jobs[job] you can load 'Jobs[job]' in the script --
if isStarted(QBXExport) then jobResource = QBXExport
Core = Core or exports[QBExport]:GetCoreObject()
Jobs, Gangs = exports[QBXExport]:GetJobs(), exports[QBXExport]:GetGangs()
elseif isStarted(OXCoreExport) then jobResource = OXExport
CreateThread(function()
if isServer() then
createCallback(getScript()..":getOxGroups", function(source)
Jobs = MySQL.query.await('SELECT * FROM `ox_groups`') return Jobs
end)
else
local TempJobs = triggerCallback(getScript()..":getOxGroups")
Jobs = TempJobs or {}
for k, v in pairs(TempJobs) do
local grades = {}
for i = 1, #v.grades do grades[i] = { name = v.grades[i], isboss = (i == #v.grades)} end
Jobs[v.name] = { label = v.label, grades = grades }
end
Gangs = Jobs
end
end)
elseif isStarted(QBExport) then jobResource = QBExport
Core = Core or exports[QBExport]:GetCoreObject()
Jobs, Gangs = Core.Shared.Jobs, Core.Shared.Gangs
if isStarted(QBExport) and not isStarted(QBXExport) then
RegisterNetEvent('QBCore:Client:UpdateObject', function()
Core = exports[QBExport]:GetCoreObject()
Jobs, Gangs = Core.Shared.Jobs, Core.Shared.Gangs
end)
end
elseif isStarted(ESXExport) then
--print("^6Bridge^7: ^2Loading ^3Jobs^7/^3Gangs^2 from ^7"..ESXExport)
ESX = exports[ESXExport]:getSharedObject()
if isServer() then
Jobs = ESX.GetJobs()
for k, v in pairs(Jobs) do
local count = countTable(Jobs[k].grades)-1
Jobs[k].grades[tostring(count)].isBoss = true
end
Gangs = Jobs
end
CreateThread(function()
while not ESX do Wait(0) end
if isServer() then
createCallback(getScript()..":getJobs", function(source)
return Jobs
end)
end
if not isServer() then
Jobs = triggerCallback(getScript()..":getJobs")
Gangs = Jobs
end
end)
end
if not isStarted(ESXExport) and Jobs then
debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Jobs).." ^3Jobs^2 from ^7"..jobResource, "^6Bridge^7: ^2Loading ^6"..countTable(Gangs).." ^3Gangs^2 from ^7"..jobResource)
end

481
shared/crafting.lua Normal file
View File

@@ -0,0 +1,481 @@
local CraftLock = false
--- Opens a crafting menu based on the provided data.
---
--- This function checks job requirements, prepares the menu options, and opens the crafting menu.
--- It handles item availability, crafting recipes, and displays appropriate icons and labels.
---
---@param data table A table containing crafting menu data.
--- - **craftable** (`table`): The crafting options and settings.
--- - **Header** (`string`): The header/title of the crafting menu.
--- - **Recipes** (`table`): A list of crafting recipes.
--- - **coords** (`vector3`): The coordinates where the crafting menu is being opened.
--- - **stashTable** (`string` or `table`, optional): The stash name(s) to check for item availability.
--- - **stashName** (`string` or `table`, optional): Alias for `stashTable`.
--- - **job** (`string` or `table`, optional): Job(s) required to access the crafting menu.
--- - **gang** (`string` or `table`, optional): Gang(s) required to access the crafting menu.
--- - **onBack** (`function`, optional): Function to call when returning from the menu.
---
---@usage
--- ```lua
--- craftingMenu({
--- craftable = {
--- Header = "Weapon Crafting",
--- Recipes = {
--- [1] = {
--- ["weapon_pistol"] = { ["steel"] = 5, ["plastic"] = 2 },
--- amount = 1,
--- },
--- -- More recipes...
--- },
--- Anims = {
--- animDict = "amb@prop_human_parking_meter@male@idle_a",
--- anim = "idle_a",
--- },
--- },
--- coords = vector3(100.0, 200.0, 300.0),
--- stashTable = "crafting_stash",
--- job = "mechanic", -- Optional
--- onBack = function() print("Returning to previous menu") end,
--- })
--- ```
function craftingMenu(data)
if CraftLock then return end
if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end
if Config.System.Menu == "jim" then
triggerNotify(nil, "Thinking", "info")
else
openMenu({ { header = "Thinking...", icon = "fas fa-hourglass-end", isMenuHeader = true } }, { header = "Crafting Menu" } )
end
if data.stashTable then data.stashName = data.stashTable end
local Menu, hasjob = {}, false
local Recipes = data.craftable.Recipes
local tempCarryTable = {}
for i = 1, #Recipes do
for k in pairs(Recipes[i]) do
if k ~= "amount" and k ~= "metadata" and k ~= "job" and k ~= "gang" then
tempCarryTable[k] = Recipes[i].amount or 1
end
end
end
local canCarryTable = triggerCallback(getScript()..':server:canCarry', tempCarryTable)
for i = 1, #Recipes do
if not Recipes[i]["amount"] then Recipes[i]["amount"] = 1 end
for k, v in pairs(Recipes[i]) do
if k ~= "amount" and k ~= "metadata" and k ~= "job" and k ~= "gang" then
if Recipes[i].job then
for l, b in pairs(Recipes[i].job) do
hasjob = hasJob(l, nil, b)
if hasjob == true then break end
end
else hasjob = true end
local setheader, settext, disable, metadata = "", "", false, (Recipes[i]["metadata"] or nil)
if hasjob then
local itemTable = {}
local metaTable = {}
for l, b in pairs(Recipes[i][tostring(k)]) do
settext = settext..(settext ~= "" and br or "")..(Items[l] and Items[l].label or "error - "..l)..(b > 1 and " x"..b or "")
metaTable[Items[l] and Items[l].label or "error - "..l] = b
itemTable[l] = b
Wait(0)
end
while not canCarryTable do Wait(0) end
disable = not checkHasItem(data.stashName, itemTable)
setheader = ((metadata and metadata.label) or (Items[tostring(k)] and Items[tostring(k)].label) or "error - " .. tostring(k)) .. (Recipes[i]["amount"] > 1 and " x" .. Recipes[i]["amount"] or "")
if not disable then
if not canCarryTable[k] then setheader = setheader .. " 📦"
else setheader = setheader .. " ✔️" end
elseif not canCarryTable[k] then setheader = setheader .. " 📦" end
Menu[#Menu + 1] = {
arrow = not disable and canCarryTable[k],
disable = isStarted(QBMenuExport) and disable and not canCarryTable[k],
icon = invImg((metadata and metadata.image) or tostring(k)),
image = invImg((metadata and metadata.image) or tostring(k)),
header = setheader..((disable or not canCarryTable[k]) and "" or ""),
txt = isStarted(QBMenuExport) and settext or nil,
--metadata = debugMode and Recipes[i]["metadata"] or nil,
metadata = metaTable,
onSelect = ((not disable and canCarryTable[k]) and (function()
local transdata = { item = k, craft = data.craftable.Recipes[i], craftable = data.craftable, coords = data.coords, stashName = data.stashName, onBack = data.onBack, metadata = Recipes[i]["metadata"] }
if Config.Crafting.MultiCraft then multiCraft(transdata) else makeItem(transdata) end
end) or nil),
}
end
end
Wait(0)
end
end
openMenu(Menu, { header = data.craftable.Header, onBack = data.onBack or nil, canClose = true, onExit = function() end, })
lookEnt(data.coords)
end
--- Opens a menu for selecting the quantity to craft.
---
--- This function presents the player with options to craft multiple quantities of an item, based on `Config.Crafting.MultiCraftAmounts`.
---
---@param data table A table containing crafting data.
--- - **item** (`string`): The item to craft.
--- - **craft** (`table`): The crafting recipe for the item.
--- - **craftable** (`table`): The crafting options and settings.
--- - **coords** (`vector3`): The coordinates where the crafting is taking place.
--- - **stashName** (`string` or `table`, optional): The stash name(s) to check for item availability.
--- - **onBack** (`function`, optional): Function to call when returning from the menu.
--- - **metadata** (`table`, optional): Metadata for the crafted item.
---
---@usage
--- ```lua
--- multiCraft({
--- item = "weapon_pistol",
--- craft = { ["weapon_pistol"] = { ["steel"] = 5, ["plastic"] = 2 }, amount = 1 },
--- craftable = craftingOptions,
--- coords = vector3(100.0, 200.0, 300.0),
--- stashName = "crafting_stash",
--- onBack = function() craftingMenu(data) end,
--- metadata = { label = "Custom Pistol", image = "custom_pistol.png" },
--- })
--- ```
function multiCraft(data)
local Menu = {}
local success = Config.Crafting.MultiCraftAmounts
local metadata = data.metadata or nil
Menu[#Menu+1] = {
isMenuHeader = true,
icon = invImg(metadata and metadata.image or data.item),
header = metadata and metadata.label or Items[data.item].label,
}
for k in pairsByKeys(success) do
local settext = ""
local itemTable = {}
for l, b in pairs(data.craft[data.item]) do
itemTable[l] = (b * k)
settext = settext..(settext ~= "" and br or "")..Items[l].label..(b*k > 1 and "- x"..b*k or "")
Wait(0)
end
local disable, stashname = checkHasItem(data.stashName, itemTable)
Menu[#Menu + 1] = {
isMenuHeader = not disable,
arrow = disable,
header = "Craft - x"..k * data.craft.amount,
txt = settext,
onSelect = function ()
makeItem({item = data.item, craft = data.craft, craftable = data.craftable, amount = k, coords = data.coords, stashName = stashname, stashTable = data.stashName, onBack = data.onBack, metadata = data.metadata })
end,
}
end
openMenu(Menu, { header = data.craftable.Header, onBack = function() craftingMenu(data) end, })
end
--- Initiates the crafting process for a specified item.
---
--- This function handles the crafting animation, progress bar, item removal, and item creation.
---
---@param data table A table containing crafting data.
--- - **item** (`string`): The item to craft.
--- - **craft** (`table`): The crafting recipe for the item.
--- - **craftable** (`table`): The crafting options and settings.
--- - **amount** (`number`, optional): The quantity to craft. Default is `1`.
--- - **coords** (`vector3`): The coordinates where the crafting is taking place.
--- - **stashName** (`string` or `table`, optional): The stash name(s) to remove items from.
--- - **stashTable** (`string` or `table`, optional): Alias for `stashName`.
--- - **onBack** (`function`, optional): Function to call when returning from the menu.
--- - **metadata** (`table`, optional): Metadata for the crafted item.
---
---@usage
--- ```lua
--- makeItem({
--- item = "weapon_pistol",
--- craft = { ["weapon_pistol"] = { ["steel"] = 5, ["plastic"] = 2 }, amount = 1 },
--- craftable = craftingOptions,
--- amount = 2,
--- coords = vector3(100.0, 200.0, 300.0),
--- stashName = "crafting_stash",
--- onBack = function() craftingMenu(data) end,
--- metadata = { label = "Custom Pistol", image = "custom_pistol.png" },
--- })
--- ```
function makeItem(data)
if CraftLock then return end
CraftLock = true
if data.stashTable then data.stashName = data.stashTable end
local bartime = data.craftable.progressBar and data.craftable.progressBar.time or 5000
local bartext = (data.craftable.progressBar and data.craftable.progressBar.label) or (Loc[Config.Lan].progressbar and Loc[Config.Lan].progressbar["progress_make"]) or "Making a"
local animDict = data.craftable.Anims and data.craftable.Anims.animDict or "amb@prop_human_parking_meter@male@idle_a"
local anim = data.craftable.Anims and data.craftable.Anims.anim or "idle_a"
local amount = data.amount and (data.amount ~= 1) and data.amount or 1
local metadata = data.metadata or nil
local prop = data.craftable.Anims and data.craftable.Anims.prop or nil
local crafted, crafting = true, true
local cam = createTempCam(PlayerPedId(), data.coords)
startTempCam(cam)
for i = 1, amount do
for k, v in pairs(data.craft) do
if k ~= "amount" and k ~= "metadata" and k ~= "job" and k ~= "gang" then
if type(v) == "table" then
for l, b in pairs(v) do
if crafting and progressBar({
label = "Using "..b.." "..Items[l].label,
time = 1000,
cancel = true,
dict = 'pickup_object',
anim = "putdown_low",
flag = 48,
icon = l,
}) then
TriggerEvent((isStarted(QBInv) and QBInvNew and "qb-" or "").."inventory:client:ItemBox", Items[l], "use", b) -- Show item box for each item
else
crafted, crafting = false, false
break
end
Wait(200)
end
if crafted then
local craftProp = nil
if prop then
local model, pos, rot, bone = prop.model, prop.pos, prop.rot, prop.bone
craftProp = makeProp({ prop = model, coords = vec4(0, 0, 0, 0), true, true })
AttachEntityToEntity(craftProp, PlayerPedId(), GetPedBoneIndex(PlayerPedId(), bone), pos.x, pos.y, pos.z, rot.x, rot.y, rot.z, true, true, false, true, 1, true)
end
if crafting and progressBar({
label = bartext..((metadata and metadata.label) or Items[data.item].label),
time = bartime,
cancel = true,
dict = animDict,
anim = anim,
flag = 49,
icon = data.item,
}) then
TriggerServerEvent(getScript()..":Crafting:GetItem", data.item, data.craft, data.stashName, metadata)
else
crafting = false
break
end
if craftProp then destroyProp(craftProp) end
end
end
end
end
Wait(500)
end
stopTempCam()
CraftLock = false
lockInv(false)
craftingMenu(data)
ClearPedTasks(PlayerPedId())
end
--- Server event handler for giving the crafted item to the player.
---
--- This event is triggered when the crafting process is completed successfully.
---
--- @param ItemMake string The item being crafted.
--- @param craftable table The crafting recipe and details.
--- @param stashName string|table The stash name(s) to remove items from.
--- @param metadata table (optional) Metadata for the crafted item.
RegisterNetEvent(getScript()..":Crafting:GetItem", function(ItemMake, craftable, stashName, metadata)
local src, amount, stashItems = source, craftable and craftable.amount or 1, nil
if stashName then
local itemRemove = {}
if type(stashName) == "table" then
for _, name in pairs(stashName) do
stashItems = getStash(name)
for k, v in pairs(craftable[ItemMake] or {}) do
for _, b in pairs(stashItems or {}) do
if k == b.name then itemRemove[k] = v end
end
end
end
else
stashItems = getStash(stashName)
for k, v in pairs(craftable[ItemMake] or {}) do
for _, b in pairs(stashItems or {}) do
if k == b.name then itemRemove[k] = v end
end
end
end
stashRemoveItem(stashItems, stashName, itemRemove)
else
if craftable then
for k, v in pairs(craftable[ItemMake] or {}) do
TriggerEvent(getScript()..":server:toggleItem", false, tostring(k), v, src)
end
end
end
TriggerEvent(getScript()..":server:toggleItem", true, ItemMake, amount, src, metadata)
--if isStarted("core_skills") then exports["core_skills"]:AddExperience(src, 2) end
end)
--- Opens a selling menu based on the provided data.
---
--- This function checks available items to sell, prepares the menu options, and opens the selling menu.
---
---@param data table A table containing selling menu data.
--- - **sellTable** (`table`): The selling options and settings.
--- - **Items** (`table`): A list of items that can be sold with their prices.
--- - **Header** (`string`, optional): The header/title of the selling menu.
--- - **ped** (`number`, optional): The ped entity involved in the selling interaction.
--- - **onBack** (`function`, optional): Function to call when returning from the menu.
---
---@usage
--- ```lua
--- sellMenu({
--- sellTable = {
--- Header = "Sell Items",
--- Items = {
--- ["gold_ring"] = 100,
--- ["diamond"] = 500,
--- },
--- },
--- ped = pedEntity,
--- onBack = function() print("Returning to previous menu") end,
--- })
--- ```
function sellMenu(data)
local origData = data
local Menu = {}
if data.sellTable.Items then
local itemList = {}
for k, v in pairs(data.sellTable.Items) do itemList[k] = 1 end
local _, hasTable = hasItem(itemList)
for k, v in pairsByKeys(data.sellTable.Items) do
Menu[#Menu +1] = {
isMenuHeader = not hasTable[k].hasItem,
icon = invImg(k),
header = Items[k].label.. (hasTable[k].hasItem and "💰 (x"..hasTable[k].count..")" or ""),
txt = Loc[Config.Lan].info["sell_all"]..v.." "..Loc[Config.Lan].info["sell_each"],
onSelect = function()
sellAnim({ item = k, price = v, ped = data.ped, onBack = function() sellMenu(data) end })
end,
}
end
else
for k, v in pairsByKeys(data.sellTable) do
if type(v) == "table" then
Menu[#Menu +1] = {
arrow = true,
header = k,
txt = "Amount of items: "..countTable(v.Items),
onSelect = function()
v.onBack = function() sellMenu(origData) end
v.sellTable = data.sellTable[k]
sellMenu(v)
end,
}
end
end
end
openMenu(Menu, { header = data.sellTable.Header or "Amount of items: "..countTable(data.sellTable.Items), headertxt = data.sellTable.Header and "Amount of items: "..countTable(data.sellTable.Items) or "", canClose = true, onBack = data.onBack })
end
--- Handles the selling animation and item transaction.
---
--- This function plays the selling animation, removes the item from the player's inventory, and gives the player money.
---
---@param data table A table containing selling data.
--- - **item** (`string`): The item to sell.
--- - **price** (`number`): The price per item.
--- - **ped** (`number`, optional): The ped entity involved in the selling interaction.
--- - **onBack** (`function`, optional): Function to call when returning from the menu.
---
---@usage
--- ```lua
--- sellAnim({
--- item = "gold_ring",
--- price = 100,
--- ped = pedEntity,
--- onBack = function() sellMenu(data) end,
--- })
--- ```
function sellAnim(data)
if not hasItem(data.item, 1) then
triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..Items[data.item].label, "error")
return
end
for k, v in pairs(GetGamePool('CObject')) do
for _, model in pairs({`p_cs_clipboard`}) do
if GetEntityModel(v) == model then
if IsEntityAttachedToEntity(data.ped, v) then
DeleteObject(v) DetachEntity(v, 0, 0) SetEntityAsMissionEntity(v, true, true)
Wait(100) DeleteEntity(v)
end
end
end
end
TriggerServerEvent(getScript().."Sellitems", data)
lookEnt(data.ped)
local dict = "mp_common"
playAnim(dict, "givetake2_a", 0.3, 2)
playAnim(dict, "givetake2_b", 0.3, 2, data.ped)
Wait(2000)
StopAnimTask(PlayerPedId(), dict, "givetake2_a", 0.5)
StopAnimTask(data.ped, dict, "givetake2_b", 0.5)
if data.onBack then data.onBack() end
end
--- Server event handler for processing the item sale.
---
--- This event removes the sold item from the player's inventory and adds money to their account.
---
---@param data table The data containing item and price information.
RegisterNetEvent(getScript().."Sellitems", function(data)
local src = source
local hasItems, hasTable = hasItem(data.item, 1, src)
if hasItems then
TriggerEvent(getScript()..":server:toggleItem", false, data.item, hasTable[data.item].count, src)
TriggerEvent(getScript()..":server:FundPlayer", (hasTable[data.item].count * data.price), "cash", src)
else
triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..Items[data.item].label, "error", src)
end
end)
--- Opens a shop interface for the player.
---
--- This function checks job requirements and opens the shop using the appropriate inventory system.
---
---@param data table A table containing shop data.
--- - **shop** (`string`): The shop identifier.
--- - **items** (`table`): The items available in the shop.
--- - **coords** (`vector3`): The coordinates where the shop interaction is happening.
--- - **job** (`string` or `table`, optional): Job(s) required to access the shop.
--- - **gang** (`string` or `table`, optional): Gang(s) required to access the shop.
---
---@usage
--- ```lua
--- openShop({
--- shop = "weapon_shop",
--- items = weaponShopItems,
--- coords = vector3(100.0, 200.0, 300.0),
--- job = "police",
--- })
--- ```
function openShop(data)
if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end
if isStarted(OXInv) then
exports[OXInv]:openInventory('shop', { type = data.shop })
elseif isStarted(QBInv) then
if QBInvNew then
TriggerServerEvent(getScript()..':server:OpenShopNewQB', data.shop) -- i hate qb-inv
else
TriggerServerEvent(Config.General.JimShops and "jim-shops:ShopOpen" or "inventory:server:OpenInventory", "shop", data.items.label, data.items)
end
else
TriggerServerEvent(Config.General.JimShops and "jim-shops:ShopOpen" or "inventory:server:OpenInventory", "shop", data.items.label, data.items)
end
lookEnt(data.coords)
end
--- Server event handler for opening a new QB inventory shop.
---
--- This event is triggered when using the new QB inventory system.
---
---@param data table The shop data to open.
RegisterNetEvent(getScript()..':server:OpenShopNewQB', function(data)
exports[QBInv]:OpenShop(source, data)
end)
--- Server-side callback registration for checking if the player can carry items.
if isServer() then
createCallback(getScript()..':server:canCarry', function(source, itemTable) local result = canCarry(itemTable, source) return result end)
end

61
shared/drawText.lua Normal file
View File

@@ -0,0 +1,61 @@
local radarTable = {}
--- Displays text on the screen using the configured draw text system.
---
--- This function handles displaying text with optional images or icons using different frameworks like 'qb', 'ox', 'gta', and 'esx'.
---
---@param image string|nil An optional image or icon to display with the text. Can be a URL, path, or a reference to an icon.
---@param input table A table of strings, each representing a line of text to display.
---@param style string|nil An optional style code for default GTA popups (e.g., '~g~' for green text).
---@param oxStyleTable table|nil An optional table specifying style parameters for the 'ox' draw text system.
---
---@usage
--- ```lua
--- drawText("img_link", { "Test line 1", "Test Line 2" }, "~g~")
--- ```
function drawText(image, input, style, oxStyleTable) local text = ""
if Config.System.drawText == "qb" then
for i = 1, #input do
text = text..input[i].."</span>"..(input[i+1] ~= nil and "<br>" or "") end
local text = text:gsub("%:", ":<span style='color:yellow'>")
if image then
text = '<img src="'..(radarTable[image] or nil)..'" style="width:12px;height:12px">'..text
end
exports[QBExport]:DrawText(text, 'left')
elseif Config.System.drawText == "ox" then
for k, v in pairs(input) do
input[k] = v.." \n"
end
lib.showTextUI(table.concat(input), { icon = (image and radarTable[image] or image) or nil, position = 'left-center', style = oxStyleTable})
elseif Config.System.drawText == "gta" then
for i = 1, #input do if input[i] ~= "" then text = text..input[i].."\n~s~" end end
if image then text = "~BLIP_"..image.."~ "..text end
DisplayHelpMsg(text:gsub("%:", ":~"..(style or "g").."~"))
elseif Config.System.drawText == "esx" then
for i = 1, #input do
text = text..input[i].."</span>"..(input[i+1] ~= nil and "<br>" or "") end
local text = text:gsub("%:", ":<span style='color:yellow'>")
if image then
text = '<img src="'..radarTable[image]..'" style="width:12px;height:12px">'..text
end
ESX.TextUI(text, nil)
end
end
--- Hides any text currently being displayed on the screen.
---
--- This function clears the text displayed by the `drawText` function, using the appropriate method based on the configured draw text system.
function hideText()
if Config.System.drawText == "qb" then
exports[QBExport]:HideText()
elseif Config.System.drawText == "ox" then
lib.hideTextUI()
elseif Config.System.drawText == "gta" then
ClearAllHelpMessages()
elseif Config.System.drawText == "esx" then
ESX.HideUI()
end
end

122
shared/duifunctions.lua Normal file
View File

@@ -0,0 +1,122 @@
-- DUI STUFF -- * Experimental * --
scriptTxd = not isServer() and CreateRuntimeTxd(getScript()..'scriptTxd') or nil
customDUIList = {}
-- DUI CLIENT
function createDui(name, http, size, txd)
--print(name, http, size, txd)
if not customDUIList[name] then
local newTxt = CreateDui(http, math.floor(size.x), math.floor(size.y))
while not GetDuiHandle(newTxt) do Wait(0) end
CreateRuntimeTextureFromDuiHandle(txd, name, GetDuiHandle(newTxt))
customDUIList[name] = newTxt
SetDuiUrl(customDUIList[name], http)
else
SetDuiUrl(customDUIList[name], http)
end
end
function DuiSelect(data)
local image = ""
for k, v in pairs(duiList[data.name]) do
if v.tex.texn == data.texn then
if duiList[data.name][k] then
image = "<center>- Current Image -<br>"..
"<img src="..duiList[data.name][k].url.." width=150px><br>"..
"Size: ["..math.floor(data.size.x)..", "..math.floor(data.size.y).."]<br><br>"
end
end
end
local dialog = exports['qb-input']:ShowInput({
header = image..Loc[Config.Lan].menu["dui_new"],
submitText = Loc[Config.Lan].menu["dui_change"],
inputs = { { type = 'text', isRequired = true, name = 'url', text = Loc[Config.Lan].menu["dui_url"] } } })
if dialog then
if not dialog.url then return end
data.url = dialog.url
--Scan the link to see if it has an image extention otherwise, stop here.
local searchList = { "png", "jpg", "jpeg", "gif", "webp", "bmp" }
--Scan the link for certain terms that will flag it and refuse to show it
local banList = { "porn" } -- I dunno, let me know what links people manage to find
local searchFound = false
for k, v in pairs(searchList) do
if string.find(tostring(data.url), tostring(v))then
searchFound = true
end
end
for k, v in pairs(banList) do
if string.find(tostring(data.url), tostring(v)) then
searchFound = false print("BANNED WORD: "..v)
end
end
if searchFound then
TriggerServerEvent(getScript()..":Server:ChangeDUI", data)
end
end
end
RegisterNetEvent(getScript()..":Client:ChangeDUI", function(data)
debugPrint("^6Bridge^7: ^2Recieving new DUI ^7- ^6"..data.url.."^7")
if tostring(data.url) ~= "-" then
createDui(data.texn, tostring(data.url), data.size, scriptTxd)
AddReplaceTexture(tostring(data.texd), tostring(data.texn), getScript()..'scriptTxd', tostring(data.texn))
end
end)
RegisterNetEvent(getScript()..":Client:ClearDUI", function(data)
if customDUIList[tostring(data.texn)] then
RemoveReplaceTexture(tostring(data.texd), tostring(data.texn))
if IsDuiAvailable(customDUIList[tostring(data.texn)]) then
SetDuiUrl(customDUIList[data.name], nil)
end
end
end)
-- DUI SERVER
RegisterNetEvent(getScript()..":Server:ChangeDUI", function(data)
-- if no url given, "reset" it back to preset
if not data.url then
for k, v in pairs(duiList[data.name]) do
if v.tex.texn == data.texn then
debugPrint("^6Bridge^7: ^2Preset^7: ^6"..tostring(duiList[data.name][k].preset).."^7")
data.url = duiList[data.name][k].preset
end
end
end
-- if it has a url, update server DUI list and send to players
for k, v in pairs(duiList[data.name]) do
if v.tex.texn == data.texn then
duiList[data.name][k].url = data.url
end
end
debugPrint("^6Bridge^7: ^3DUI^2 Sending new DUI to all players^7 - ^6"..data.url.."^7")
TriggerClientEvent(getScript()..":Client:ChangeDUI", -1, data)
end)
RegisterNetEvent(getScript()..":Server:ClearDUI", function(data)
if data.url == "-" then
for k, v in pairs(duiList[data.name]) do
if v.tex.texn == data.texn then
duiList[data.name][k].url = "-"
end
end
end
-- Clear the DUI from loading
TriggerClientEvent(getScript()..":Client:ClearDUI", -1, data)
--duiList[tostring(data.tex)].url = ""
end)
AddEventHandler('onResourceStop', function(r) if r ~= getScript() then return end
for k, v in pairs(duiList or {}) do
for i = 1, #v do
RemoveReplaceTexture(tostring(v[i].tex.texd), tostring(v[i].tex.texn))
end
end
end)
if isServer() then
createCallback(getScript()..":Server:duiList", function(source)
return duiList
end)
end

190
shared/effects.lua Normal file
View File

@@ -0,0 +1,190 @@
--Screen Effects
local alienEffect = false
function AlienEffect()
if alienEffect then return else alienEffect = true end
debugPrint("^5Debug^7: ^3AlienEffect^7() ^2activated")
AnimpostfxPlay("DrugsMichaelAliensFightIn", 3.0, 0)
Wait(math.random(5000, 8000))
local Ped = PlayerPedId()
local animDict = "MOVE_M@DRUNK@VERYDRUNK"
loadAnimDict(animDict)
SetPedCanRagdoll(Ped, true)
ShakeGameplayCam('DRUNK_SHAKE', 2.80)
SetTimecycleModifier("Drunk")
SetPedMovementClipset(Ped, animDict, 1)
SetPedMotionBlur(Ped, true)
SetPedIsDrunk(Ped, true)
Wait(1500)
SetPedToRagdoll(Ped, 5000, 1000, 1, 0, 0, 0)
Wait(13500)
SetPedToRagdoll(Ped, 5000, 1000, 1, 0, 0, 0)
Wait(120500)
ClearTimecycleModifier()
ResetScenarioTypesEnabled()
ResetPedMovementClipset(Ped, 0)
SetPedIsDrunk(Ped, false)
SetPedMotionBlur(Ped, false)
AnimpostfxStopAll()
ShakeGameplayCam('DRUNK_SHAKE', 0.0)
AnimpostfxPlay("DrugsMichaelAliensFight", 3.0, 0)
Wait(math.random(45000, 60000))
AnimpostfxPlay("DrugsMichaelAliensFightOut", 3.0, 0)
AnimpostfxStop("DrugsMichaelAliensFightIn")
AnimpostfxStop("DrugsMichaelAliensFight")
AnimpostfxStop("DrugsMichaelAliensFightOut")
alienEffect = false
debugPrint("^5Debug^7: ^3AlienEffect^7() ^2stopped")
end
local weedEffect = false
function WeedEffect()
if weedEffect then return else weedEffect = true end
debugPrint("^5Debug^7: ^3WeedEffect^7() ^2activated")
AnimpostfxPlay("DrugsMichaelAliensFightIn", 3.0, 0)
Wait(math.random(3000, 20000))
AnimpostfxPlay("DrugsMichaelAliensFight", 3.0, 0)
Wait(math.random(15000, 20000))
AnimpostfxPlay("DrugsMichaelAliensFightOut", 3.0, 0)
AnimpostfxStop("DrugsMichaelAliensFightIn")
AnimpostfxStop("DrugsMichaelAliensFight")
AnimpostfxStop("DrugsMichaelAliensFightOut")
weedEffect = false
debugPrint("^5Debug^7: ^3WeedEffect^7() ^2stopped")
end
local trevorEffect = false
function TrevorEffect()
if trevorEffect then return else trevorEffect = true end
debugPrint("^5Debug^7: ^3TrevorEffect^7() ^2activated")
AnimpostfxPlay("DrugsTrevorClownsFightIn", 3.0, 0)
Wait(3000)
AnimpostfxPlay("DrugsTrevorClownsFight", 3.0, 0)
Wait(30000)
AnimpostfxPlay("DrugsTrevorClownsFightOut", 3.0, 0)
AnimpostfxStop("DrugsTrevorClownsFight")
AnimpostfxStop("DrugsTrevorClownsFightIn")
AnimpostfxStop("DrugsTrevorClownsFightOut")
trevorEffect = false
debugPrint("^5Debug^7: ^3TrevorEffect^7() ^2stopped")
end
local turboEffect = false
function TurboEffect()
if turboEffect then return else turboEffect = true end
debugPrint("^5Debug^7: ^3TurboEffect^7() ^2activated")
AnimpostfxPlay('RaceTurbo', 0, true)
SetTimecycleModifier('rply_motionblur')
ShakeGameplayCam('SKY_DIVING_SHAKE', 0.25)
Wait(30000)
StopGameplayCamShaking(true)
SetTransitionTimecycleModifier('default', 0.35)
Wait(1000)
ClearTimecycleModifier()
AnimpostfxStop('RaceTurbo')
turboEffect = false
debugPrint("^5Debug^7: ^3TurboEffect^7() ^2stopped")
end
local rampageEffect = false
function RampageEffect()
if rampageEffect then return else rampageEffect = true end
debugPrint("^5Debug^7: ^3RampageEffect^7() ^2activated")
AnimpostfxPlay('Rampage', 0, true)
SetTimecycleModifier('rply_motionblur')
ShakeGameplayCam('SKY_DIVING_SHAKE', 0.25)
Wait(30000)
StopGameplayCamShaking(true)
SetTransitionTimecycleModifier('default', 0.35)
Wait(1000)
ClearTimecycleModifier()
AnimpostfxStop('Rampage')
rampageEffect = false
debugPrint("^5Debug^7: ^3RampageEffect^7() ^2stopped")
end
local focusEffect = false
function FocusEffect()
if focusEffect then return else focusEffect = true end
debugPrint("^5Debug^7: ^3FocusEffect^7() ^2activated")
Wait(1000)
AnimpostfxPlay('FocusIn', 0, true)
Wait(30000)
AnimpostfxStop('FocusIn')
focusEffect = false
debugPrint("^5Debug^7: ^3FocusEffect^7() ^2stopped")
end
local nightVisionEffect = false
function NightVisionEffect()
if nightVisionEffect then return else nightVisionEffect = true end
debugPrint("^5Debug^7: ^3NightVisionEffect^7() ^2activated")
SetNightvision(true)
Wait(math.random(3000, 4000)) -- FEEL FREE TO CHANGE THIS
SetNightvision(false)
SetSeethrough(false)
nightVisionEffect = false
debugPrint("^5Debug^7: ^3NightVisionEffect^7() ^2stopped")
end
local thermalEffect = false
function ThermalEffect()
if thermalEffect then return else thermalEffect = true end
debugPrint("^5Debug^7: ^3ThermalEffect^7() ^2activated")
SetNightvision(true)
SetSeethrough(true)
Wait(math.random(2000, 3000)) -- FEEL FREE TO CHANGE THIS
SetNightvision(false)
SetSeethrough(false)
thermalEffect = false
debugPrint("^5Debug^7: ^3ThermalEffect^7() ^2stopped")
end
--Built-in Buff effects
local healEffect = false
function HealEffect(data)
if healEffect then return end
debugPrint("^5Debug^7: ^3HealEffect^7() ^2activated")
healEffect = true
local count = (data[1] / 1000)
while count > 0 do
Wait(1000)
count -= 1
SetEntityHealth(PlayerPedId(), GetEntityHealth(PlayerPedId()) + data[2])
end
healEffect = false
debugPrint("^5Debug^7: ^3HealEffect^7() ^2stopped")
end
local staminaEffect = false
function StaminaEffect(data)
if staminaEffect then return end
debugPrint("^5Bridge^7: ^3StaminaEffect^7() ^2activated")
staminaEffect = true
local startStamina = (data[1] / 1000)
SetRunSprintMultiplierForPlayer(PlayerId(), 1.49)
while startStamina > 0 do
Wait(1000)
if math.random(5, 100) < 10 then RestorePlayerStamina(PlayerId(), data[2]) end
startStamina -= 1
if math.random(5, 100) < 51 then end
end
startStamina = 0
SetRunSprintMultiplierForPlayer(PlayerId(), 1.0)
staminaEffect = false
debugPrint("^5Bridge^7: ^3StaminaEffect^7() ^2stopped")
end
function StopEffects() -- Used to clear up any effects stuck on screen
debugPrint("^5Bridge^7: ^2All screen effects stopped")
ShakeGameplayCam('DRUNK_SHAKE', 0.0)
SetPedToRagdoll(PlayerPedId(), 5000, 1000, 1, 0, 0, 0)
ClearTimecycleModifier()
ResetScenarioTypesEnabled()
ResetPedMovementClipset(PlayerPedId(), 0)
SetPedIsDrunk(PlayerPedId(), false)
SetPedMotionBlur(PlayerPedId(), false)
SetNightvision(false)
SetSeethrough(false)
AnimpostfxStop("DrugsMichaelAliensFightIn")
AnimpostfxStop("DrugsMichaelAliensFight")
AnimpostfxStop("DrugsMichaelAliensFightOut")
AnimpostfxStop("DrugsTrevorClownsFight")
AnimpostfxStop("DrugsTrevorClownsFightIn")
AnimpostfxStop("DrugsTrevorClownsFightOut")
AnimpostfxStop('RaceTurbo')
AnimpostfxStop('FocusIn')
AnimpostfxStop('Rampage')
end

920
shared/helpers.lua Normal file
View File

@@ -0,0 +1,920 @@
--- Utility Functions for Resource Management and Debugging
---
--- This script provides a set of utility functions for managing resources, debugging, and handling various common tasks within the game environment.
--- It includes functions for checking resource states, generating unique keys, formatting numbers and coordinates, handling JSON data, and more.
--[[ Resource and Environment Checks ]]--
--- Checks if a specific resource is started.
---
---@param script string The name of the resource to check.
---@return boolean `true` if the resource state contains "start", otherwise `false`.
---
---@usage
--- ```lua
--- if isStarted("myResource") then
--- print("Resource is running")
--- end
--- ```
function isStarted(script)
return GetResourceState(script):find("start")
end
local scriptName = nil
--- Retrieves the current resource name.
---
--- Caches the resource name after the first call for efficiency.
---
--- @return string scriptName The name of the current resource.
---
--- @usage
--- ```lua
--- local currentScript = getScript()
--- print("Current script:", currentScript)
--- ```
function getScript()
if not scriptName then scriptName = GetCurrentResourceName() end
return scriptName
end
--- Determines if the current execution context is the server.
---
--- Very helpful for shared files complaining about client functions running on server or vice versa
---
--- @return boolean Returns `true` if running on the server, otherwise `false`.
---
---@usage
--- ```lua
--- if isServer() then
--- -- Server-specific code
--- else
--- -- Client-specific code
--- end
--- ```
function isServer()
return IsDuplicityVersion()
end
--[[ Debugging Functions ]]--
--- Prints debug messages if debugging mode is enabled.
---
--- Concatenates all arguments and prints them along with debug information.
---
--- @param ... any Multiple arguments to be concatenated and printed.
---
--- @usage
--- ```lua
--- debugPrint("Player has joined:", playerName)
--- ```
function debugPrint(...)
if debugMode then
local args = {...}
local output = table.concat(args, " ") -- Concatenate all arguments with a space
print(output, getDebugInfo(debug.getinfo(2, "nSl")))
end
end
--- Prints event-related debug messages if event debugging is enabled.
---
--- @param ... any Multiple arguments to be printed.
---
--- @usage
--- ```lua
--- eventPrint("Event triggered:", eventName)
--- ```
function eventPrint(...)
if Config.System.EventDebug then
print(...)
end
end
-- Function to recursively colorize the JSON data
function getSortedKeys(tbl)
local keys = {}
for k in pairs(tbl) do keys[#keys + 1] = k end
table.sort(keys, function(a, b)
local numA, numB = tonumber(a), tonumber(b)
if numA and numB then return numA < numB
else return tostring(a) < tostring(b) end
end)
return keys
end
--- Recursively colorizes a table for debug printing.
---
--- @param tbl table The table to colorize.
--- @return table colourizedTable The colorized table.
---
--- @usage
--- ```lua
--- local colorizedData = colorizeTable(myTable)
--- jsonPrint(colorizedData)
--- ```
function colorizeTable(tbl)
local newData, sortedKeys = {}, getSortedKeys(tbl)
for _, k in ipairs(sortedKeys) do
local v = tbl[k]
newData["^6"..tostring(k).."^7"] = ((type(v) == "table") and colorizeTable(v)) or (type(v):find("vector") and formatCoord(v)) or "^2"..tostring(v).."^7"
end
return newData
end
--- Encodes a table into an ordered JSON string with indentation.
---
--- @param data table The table to encode.
--- @param indent string The string used for indentation (e.g., " ").
--- @param level number The current indentation level.
--- @return string The formatted JSON string.
---
--- @usage
--- ```lua
--- local jsonString = encodeOrderedJSON(myTable, " ", 0)
--- print(jsonString)
--- ```
function encodeOrderedJSON(data, indent, level)
local jsonParts, prefix, sortedKeys = {"{"}, string.rep(indent, level), getSortedKeys(data)
for i, k in ipairs(sortedKeys) do
jsonParts[#jsonParts + 1] = (i > 1 and ",\n" or "\n")..prefix..indent..json.encode(k)..": "
jsonParts[#jsonParts + 1] = (type(data[k]) == "table") and encodeOrderedJSON(data[k], indent, level + 1) or json.encode(data[k])
end
jsonParts[#jsonParts + 1] = "\n"..prefix.."}"
return table.concat(jsonParts)
end
--- Prints a table as a colorized and ordered JSON string if debugging mode is enabled.
---
--- @param data table The table to print.
---
--- @usage
--- ```lua
--- jsonPrint(myTable)
--- ```
function jsonPrint(data)
if debugMode then
print(encodeOrderedJSON(colorizeTable(data), " ", 0), getDebugInfo(debug.getinfo(2, "nSl")))
end
end
--- Retrieves the current time formatted for debug prints.
---
--- @return string string The formatted time string, e.g., "^7(14:23:45)".
---
--- @usage
--- ```lua
--- local currentTime = GetPrintTime()
--- debugPrint("Current Time:", currentTime)
--- ```
function GetPrintTime()
if isServer() then
local hour, min, sec = os.date('%H'), os.date('%M'), os.date('%S')
return "^7("..string.format("%02d", hour)..":"..string.format("%02d", min)..":"..string.format("%02d", sec)..")"
else
local _, _, _, hour, min, sec = GetLocalTime()
return "^7("..string.format("%02d", hour)..":"..string.format("%02d", min)..":"..string.format("%02d", sec)..")"
end
end
--- Generates a unique 3-character alphanumeric key.
---
--- @return string GeneratedString A randomly generated 3-character string.
---
--- @usage
--- ```lua
--- local uniqueKey = keyGen()
--- print("Generated Key:", uniqueKey)
--- ```
function keyGen()
local charset = {
"q","w","e","r","t","y","u","i","o","p","a","s","d","f","g","h","j","k","l","z","x","c","v","b","n","m",
"Q","W","E","R","T","Y","U","I","O","P","A","S","D","F","G","H","J","K","L","Z","X","C","V","B","N","M",
"1","2","3","4","5","6","7","8","9","0"
}
local GeneratedID = ""
for i = 1, 3 do GeneratedID = GeneratedID..charset[math.random(1, #charset)] end
return GeneratedID
end
--- Formats a number with commas as thousand separators.
---
--- @param amount number The number to format.
--- @return string commaValue The formatted number string with commas.
---
--- @usage
--- ```lua
--- local formattedNumber = cv(1000000) -- "1,000,000"
--- print(formattedNumber)
--- ``
function cv(amount)
local formatted = tostring(amount or "0")
while true do formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2') if (k==0) then break end Wait(0) end
return formatted
end
--- Formats a coordinate vector for debug printing.
---
--- @param coord table A vector3 or vector4 table with `x`, `y`, `z`, and optional `w` components.
--- @return string The formatted coordinate string with color codes.
---
--- @usage
--- ```lua
--- local formattedCoord = formatCoord(vector3(100.0, 200.0, 300.0))
--- debugPrint("Player Position:", formattedCoord)
--- ```
function formatCoord(coord)
local vecType = type(coord):gsub("tor", "")
local components = {
[1] = coord.x and ("^6"..string.format("%.1f", coord.x)) or "",
[2] = coord.y and ("^7, ^6"..string.format("%.1f", coord.y)) or "",
[3] = coord.z and ("^7, ^6"..string.format("%.1f", coord.z)) or "",
[4] = coord.w and ("^7, ^6"..string.format("%.1f", coord.w)) or "",
}
return "^5"..vecType.."^7("..components[1]..components[2]..components[3]..components[4].."^7)"
end
--- Calculates the center point of a list of zones (coordinates).
---
--- @param table table A table of vector3 coordinates.
--- @return vector3 vector3 The center coordinate.
---
--- @usage
--- ```lua
--- local center = getCenterOfZones({vector3(100, 200, 300), vector3(110, 210, 310)})
--- print("Center of Zones:", center)
--- ```
function getCenterOfZones(table)
local totalX, totalY, totalZ = 0, 0, 0
for _, coord in ipairs(table) do
totalX = totalX + coord.x
totalY = totalY + coord.y
totalZ = totalZ + coord.z
end
local count = #table
return vector3(totalX / count, totalY / count, totalZ / count)
end
--- Counts the number of keys in a table.
---
--- @param table table The table to count keys in.
--- @return number number The number of keys in the table.
---
--- @usage
--- ```lua
--- local count = countTable(myTable)
--- print("Number of keys:", count)
--- ```
function countTable(table) local i = 0 for keys in pairs(table) do i += 1 end return i end
--- Returns an iterator that iterates over a table's keys in sorted order.
---
--- @param t table The table to iterate over.
--- @return function function An iterator function.
---
--- @usage
--- ```lua
--- for k, v in pairsByKeys(myTable) do
--- print(k, v)
--- end
--- ```
function pairsByKeys(t)
local t = t
if not t then
print("^1Error^7: ^3Nil ^2table recieved for ^3pairsByKeys^7(), ^2setting to ^7{} ^2to prevent break^7")
t = {}
end
local a = {} for n in pairs(t) do a[#a+1] = n end table.sort(a) local i = 0 local iter = function() i += 1 if a[i] == nil then return nil else return a[i], t[a[i]] end end return iter
end
--- Creates a new table with consecutive numerical indices sorted by the `id` field.
---
--- @param originalTable table The original table with entries containing an `id` field.
--- @return table The new table with sorted entries and consecutive `id` values.
---
--- @usage
--- local sortedTable = createConsecutiveTable(originalTable)
--- for i, entry in ipairs(sortedTable) do
--- print(i, entry)
--- end
function createConsecutiveTable(originalTable)
local sortedEntries = {}
for _, entry in pairs(originalTable) do
table.insert(sortedEntries, entry)
end
table.sort(sortedEntries, function(a, b)
return a.id < b.id
end)
local newTable = {}
for newIndex, entry in ipairs(sortedEntries) do
entry.id = newIndex
newTable[newIndex] = entry
end
return newTable
end
--[[ Drawing Functions ]]--
--- Draws 3D text at specified coordinates.
---
--- @param coord table A vector3 table with `x`, `y`, and `z` coordinates.
--- @param text string The text to display.
--- @param highlight boolean (optional) Whether to highlight certain parts of the text.
---
--- @usage
--- ```lua
--- CreateThread(function()
--- while true do
--- DrawText3D(vector3(100, 200, 300), "Hello World", true)
--- Wait(0)
--- end
--- end)
--- ```
function DrawText3D(coord, text, highlight)
SetTextScale(0.30, 0.30)
SetTextFont(0)
SetTextProportional(1)
SetTextColour(255, 255, 255, 215)
SetTextEntry("STRING")
SetTextCentre(true)
local totalLength = string.len(text)
local textMaxLength = textMaxLength or 99 -- max 99
local text = totalLength > textMaxLength and text:sub(1, totalLength - (totalLength - textMaxLength)) or text
AddTextComponentString(highlight and text:gsub("%~w~", "~y~") or text)
SetDrawOrigin(coord.x, coord.y, coord.z, 0)
DrawText(0.0, 0.0)
local count, length = GetLineCountAndMaxLength(text)
local padding = 0.005
local heightFactor = (count / 43) + padding
local weightFactor = (length / 150) + padding
local height = (heightFactor / 2) - padding / 1
local width = (weightFactor / 2) - padding / 1
DrawRect(0.0, height, width, heightFactor, 0, 0, 0, 150)
ClearDrawOrigin()
end
--- Displays a help message on the screen.
---
--- @param text string The text to display as a help message.
---
--- @usage
--- DisplayHelpMsg("Press E to interact")
function DisplayHelpMsg(text)
BeginTextCommandDisplayHelp("STRING")
AddTextComponentScaleform(text)
EndTextCommandDisplayHelp(0, true, false, -1)
end
--- Displays a "Saving/Loading" spinner with a custom message.
---
--- @param text string The message to display alongside the spinner.
---
--- @usage
--- ```lua
--- displaySpinner("Saving data...")
--- ```
function displaySpinner(text)
BeginTextCommandBusyspinnerOn('STRING')
AddTextComponentSubstringPlayerName(text)
EndTextCommandBusyspinnerOn(4)
end
--- Stops the "Saving/Loading" spinner.
---
--- This function is client-side only.
---
--- @usage
--- ```lua
--- stopSpinner()
--- ```
function stopSpinner()
if not isServer() then
BusyspinnerOff()
end
end
--- Concatenates a table of strings into a single string separated by newlines.
---
--- @param tbl table A table containing string elements.
--- @return string string The concatenated string with newline separators.
---
--- @usage
--- ```lua
--- local combinedText = concatenateText({"Line 1", "Line 2", "Line 3"})
--- print(combinedText)
--- ```
function concatenateText(tbl)
local result = ""
for i = 1, #tbl do
result = result..tbl[i]
if i < #tbl then
result = result.."\n" -- Add newline only if it's not the last element
end
end
return result
end
--- Converts rotation to a direction vector.
---
--- @param rot vector3 A vector3 containing rotation values
--- @return vector3 vector3 A vector3 representing the direction.
---
--- @usage
--- ```lua
--- local direction = RotationToDirection({ z = 90 })
--- print(direction)
--- ```
function RotationToDirection(rot)
local adjust = (math.pi / 180)
return vec3(-math.sin(adjust * rot.z) * math.abs(math.cos(adjust * rot.x)), math.cos(adjust * rot.z) * math.abs(math.cos(adjust * rot.x)), math.sin(adjust * rot.x))
end
--- Creates a simple text-based progress bar.
---
--- @param percentage number The completion percentage (0-100).
--- @return string string A string representing the progress bar, e.g., "█████░░░░░".
---
--- @usage
--- ```lua
--- local bar = basicBar(50) -- "█████░░░░░"
--- print(bar)
--- ```
function basicBar(percentage)
local percentage = math.ceil(percentage)
local totalBlocks = 10
local filledBlocks = math.floor((percentage / 100) * totalBlocks)
local emptyBlocks = totalBlocks - filledBlocks
local bar = string.rep("", filledBlocks)..string.rep("", emptyBlocks)
return bar
end
--- Normalizes a 3D vector.
---
--- @param vec vector3 A vector3 table with `x`, `y`, and `z` components.
--- @return vector3 vector3 The normalized vector3.
---
--- @usage
--- ```lua
--- local normalizedVec = normalizeVector(vector3(1, 2, 3))
--- print(normalizedVec)
--- ```
function normalizeVector(vec)
local length = math.sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z)
if length ~= 0 then
return vec3(vec.x / length, vec.y / length, vec.z / length)
else
return vec3(0, 0, 0)
end
end
--- Draws a line between two coordinates for debugging purposes.
---
--- @param startCoords vector3 A vector3 table representing the start point.
--- @param endCoords vector3 A vector3 table representing the end point.
--- @param col vector4 A table with `x`, `y`, `z`, `w` representing the color and opacity.
---
--- @usage
--- ```lua
--- drawLine(vector3(100, 200, 300), vector3(150, 250, 350), vector4(255, 0, 0, 255))
--- ```
function drawLine(startCoords, endCoords, col)
if debugMode then
CreateThread(function()
local showCount = 1000
while showCount >= 0 do
DrawLine(startCoords.x, startCoords.y, startCoords.z, endCoords.x, endCoords.y, endCoords.z, col.x, col.y, col.z, col.w)
showCount -= 10
Wait(0)
end
end)
end
end
--- Draws a sphere at specified coordinates for debugging purposes.
---
--- @param coords vector3 A vector3 table representing the center of the sphere.
--- @param col vector4 A table with `x`, `y`, `z`, `w` representing the color and opacity.
---
--- @usage
--- ```lua
--- drawSphere(vector3(100, 200, 300), vector4(0, 255, 0, 255))
--- ```
function drawSphere(coords, col)
if debugMode then
CreateThread(function()
local showCount = 1000
while showCount >= 0 do
DrawSphere(coords.x, coords.y, coords.z, 0.5, col.x, col.y, col.z, col.w)
showCount -= 1
Wait(10)
end
end)
end
end
--- Performs a raycast between two coordinates and returns the result.
---
--- @param startCoords table A vector3 table representing the start point.
--- @param endCoords table A vector3 table representing the end point.
--- @param entity number|nil The entity to ignore during the raycast.
--- @param flags number|nil Raycast flags to customize the raycast behavior. Defaults to `4294967295`.
--- @return multiple multiple Returns multiple values from `GetShapeTestResultIncludingMaterial`.
---
--- @usage
--- ```lua
--- local hit, hitPos, material = PerformRaycast(startVec, endVec, playerPed, 1)
--- if hit == 1 then
--- print("Hit at position:", hitPos)
--- print("Material:", material)
--- end
--- ```
function PerformRaycast(startCoords, endCoords, entity, flags)
drawLine(startCoords, endCoords, vec4(0, 0, 255, 255))
local val1, val2, val3, val4, val5, val6 = GetShapeTestResult(StartExpensiveSynchronousShapeTestLosProbe(startCoords.x, startCoords.y, startCoords.z, endCoords.x, endCoords.y, endCoords.z, flags or 4294967295, entity, 0))
if val2 then
--drawSphere(val3, vec4(255, 0, 255, 0.5))
end
return val1, val2, val3, val4, val5, val6
end
--- Adjusts the Z-coordinate of a position to align with the ground.
---
--- @param coords vector4 A vector3 or vector4 table with `x`, `y`, `z`, and optional `w` components.
--- @return vector3|vector4 vector adjusted coordinate with the Z value set to the ground level.
---
--- @usage
--- ```lua
--- local groundCoords = adjustForGround(playerCoords)
--- print("Ground Position:", groundCoords)
--- ```
function adjustForGround(coords)
local coords = coords
local foundGround, zPos = GetGroundZFor_3dCoord(coords.x, coords.y, coords.z + 1.0)
if foundGround then
if coords.w then
coords = vec4(coords.x, coords.y, zPos, coords.w)
else
coords = vec3(coords.x, coords.y, zPos)
end
--debugPrint("^6Bridge^7: Adjusting for ground pos ", coords.z, zPos)
return coords
else
return coords
end
end
--- Ensures that a network vehicle exists by verifying its network ID.
---
--- @param vehNetID number The network ID of the vehicle.
--- @return number number The vehicle entity if it exists, otherwise `0`.
---
--- @usage
--- ```lua
--- local vehicle = ensureNetToVeh(netID)
--- if vehicle ~= 0 then
--- print("Vehicle exists:", vehicle)
--- end
--- ```
function ensureNetToVeh(vehNetID)
debugPrint("^6Bridge^7: ^3ensureNetToVeh^7: ^2Requesting NetworkDoesNetworkIdExist^7(^6"..vehNetID.."^7)")
local timeout = 100
while not NetworkDoesNetworkIdExist(vehNetID) and timeout > 0 do
timeout -= 1
Wait(10)
end
if not NetworkDoesNetworkIdExist(vehNetID) then return 0 end
timeout = 100
local vehicle = NetToVeh(vehNetID)
while not DoesEntityExist(vehicle) and vehicle ~= 0 and timeout > 0 do
timeout -= 1
Wait(10)
end
if not DoesEntityExist(vehicle) then return 0 end
return vehicle
end
--- Ensures that a network entity exists by verifying its network ID.
---
--- @param entNetID number The network ID of the entity.
--- @return number The entity if it exists, otherwise `0`.
---
--- @usage
--- local entity = ensureNetToEnt(netID)
--- if entity ~= 0 then
--- print("Entity exists:", entity)
--- end
function ensureNetToEnt(entNetID)
debugPrint("^6Bridge^7: ^3ensureNetToEnt^7: ^2Requesting NetworkDoesNetworkIdExist^7(^6"..entNetID.."^7)")
local timeout = 100
while not NetworkDoesNetworkIdExist(entNetID) and timeout > 0 do
timeout -= 1
Wait(10)
end
if not NetworkDoesNetworkIdExist(entNetID) then return 0 end
timeout = 100
local entity = NetworkGetEntityFromNetworkId(entNetID)
while not DoesEntityExist(entity) and entity ~= 0 and timeout > 0 do
timeout -= 1
Wait(10)
end
if not DoesEntityExist(entity) then return 0 end
return entity
end
--[[ Material Definitions ]]--
--- A table mapping material names to their corresponding hash values.
---
--- Used for identifying materials based on hash codes.
local materials = {
none = -1,
Unknown = -1775485061,
concrete = 1187676648,
concrete_pothole = 359120722,
concrete_dusty = -1084640111,
tarmac = 282940568,
tarmac_painted = -1301352528,
tarmac_pothole = 1886546517,
rumble_strip = -250168275,
breeze_block = -954112554,
rock = -840216541,
rock_mossy = -124769592,
stone = 765206029,
cobblestone = 576169331,
brick = 1639053622,
marble = 1945073303,
paving_slab = 1907048430,
sandstone_solid = 592446772,
sandstone_brittle = 1913209870,
sand_loose = -1595148316,
sand_compact = 510490462,
sand_wet = 909950165,
sand_track = -1907520769,
sand_underwater = -1136057692,
sand_dry_deep = 509508168,
sand_wet_deep = 1288448767,
ice = -786060715,
ice_tarmac = -1931024423,
snow_loose = -1937569590,
snow_compact = -878560889,
snow_deep = 1619704960,
snow_tarmac = 1550304810,
gravel_small = 951832588,
gravel_large = 2128369009,
gravel_deep = -356706482,
gravel_train_track = 1925605558,
dirt_track = -1885547121,
mud_hard = -1942898710,
mud_pothole = 312396330,
mud_soft = 1635937914,
mud_underwater = -273490167,
mud_deep = 1109728704,
marsh = 223086562,
marsh_deep = 1584636462,
soil = -700658213,
clay_hard = 1144315879,
clay_soft = 560985072,
grass_long = -461750719,
grass = 1333033863,
grass_short = -1286696947,
hay = -1833527165,
bushes = 581794674,
twigs = -913351839,
leaves = -2041329971,
woodchips = -309121453,
tree_bark = -1915425863,
metal_solid_small = -1447280105,
metal_solid_medium = -365631240,
metal_solid_large = 752131025,
metal_hollow_small = 15972667,
metal_hollow_medium = 1849540536,
metal_hollow_large = -583213831,
metal_chainlink_small = 762193613,
metal_chainlink_large = 125958708,
metal_corrugated_iron = 834144982,
metal_grille = -426118011,
metal_railing = 2100727187,
metal_duct = 1761524221,
metal_garage_door = -231260695,
metal_manhole = -754997699,
wood_solid_small = -399872228,
wood_solid_medium = 555004797,
wood_solid_large = 815762359,
wood_solid_polished = 126470059,
wood_floor_dusty = -749452322,
wood_hollow_small = 1993976879,
wood_hollow_medium = -365476163,
wood_hollow_large = -925419289,
wood_chipboard = 1176309403,
wood_old_creaky = 722686013,
wood_high_density = -1742843392,
wood_lattice = 2011204130,
ceramic = -1186320715,
roof_tile = 1755188853,
roof_felt = -1417164731,
fibreglass = 1354180827,
tarpaulin = -642658848,
plastic = -2073312001,
plastic_hollow = 627123000,
plastic_high_density = -1625995479,
plastic_clear = -1859721013,
plastic_hollow_clear = 772722531,
plastic_high_density_clear = -1338473170,
fibreglass_hollow = -766055098,
rubber = -145735917,
rubber_hollow = -783934672,
linoleum = 289630530,
laminate = 1845676458,
carpet_solid = 669292054,
carpet_solid_dusty = 158576196,
carpet_floorboard = -1396484943,
cloth = 122789469,
plaster_solid = -574122433,
plaster_brittle = -251888898,
cardboard_sheet = 236511221,
cardboard_box = -1409054440,
paper = 474149820,
foam = 808719444,
feather_pillow = 1341866303,
polystyrene = -1756927331,
leather = -570470900,
tvscreen = 1429989756,
slatted_blinds = 673696729,
glass_shoot_through = 937503243,
glass_bulletproof = 244521486,
glass_opaque = 1500272081,
perspex = -1619794068,
car_metal = -93061983,
car_plastic = 2137197282,
car_softtop = -979647862,
car_softtop_clear = 2130571536,
car_glass_weak = 1247281098,
car_glass_medium = 602884284,
car_glass_strong = 1070994698,
car_glass_bulletproof = -1721915930,
car_glass_opaque = 513061559,
water = 435688960,
blood = 5236042,
oil = -634481305,
petrol = -1634184340,
fresh_meat = 868733839,
dried_meat = -1445160429,
emissive_glass = 1501078253,
emissive_plastic = 1059629996,
vfx_metal_electrified = -309134265,
vfx_metal_water_tower = 611561919,
vfx_metal_steam = -691277294,
vfx_metal_flame = 332778253,
phys_no_friction = 1666473731,
phys_golf_ball = -1693813558,
phys_tennis_ball = -256704763,
phys_caster = -235302683,
phys_caster_rusty = 2016463089,
phys_car_void = 1345867677,
phys_ped_capsule = -291631035,
phys_electric_fence = -1170043733,
phys_electric_metal = -2013761145,
phys_barbed_wire = -1543323456,
phys_pooltable_surface = 605776921,
phys_pooltable_cushion = 972939963,
phys_pooltable_ball = -748341562,
buttocks = 483400232,
thigh_left = -460535871,
shin_left = 652772852,
foot_left = 1926285543,
thigh_right = -236981255,
shin_right = -446036155,
foot_right = -1369136684,
spine0 = -1922286884,
spine1 = -1140112869,
spine2 = 1457572381,
spine3 = 32752644,
clavicle_left = -1469616465,
upper_arm_left = -510342358,
lower_arm_left = 1045062756,
hand_left = 113101985,
clavicle_right = -1557288998,
upper_arm_right = 1501153539,
lower_arm_right = 1777921590,
hand_right = 2000961972,
neck = 1718294164,
head = -735392753,
animal_default = 286224918,
car_engine = -1916939624,
puddle = 999829011,
concrete_pavement = 2015599386,
brick_pavement = -1147361576,
phys_dynamic_cover_bound = -2047468855,
vfx_wood_beer_barrel = 998201806,
wood_high_friction = -2140087047,
rock_noinst = 127813971,
bushes_noinst = 1441114862,
metal_solid_road_surface = -729112334,
stunt_ramp_surface = -2088174996,
temp_01 = 746881105,
temp_02 = -1977970111,
temp_03 = 1911121241,
temp_04 = 1923995104,
temp_05 = -1393662448,
temp_06 = 1061250033,
temp_07 = -1765523682,
temp_08 = 1343679702,
temp_09 = 1026054937,
temp_10 = 63305994,
temp_11 = 47470226,
temp_12 = 702596674,
temp_13 = -1637485913,
temp_14 = -645955574,
temp_15 = -1583997931,
temp_16 = -1512735273,
temp_17 = 1011960114,
temp_18 = 1354993138,
temp_19 = -801804446,
temp_20 = -2052880405,
temp_21 = -1037756060,
temp_22 = -620388353,
temp_23 = 465002639,
temp_24 = 1963820161,
temp_25 = 1952288305,
temp_26 = -1116253098,
temp_27 = 889255498,
temp_28 = -1179674098,
temp_29 = 1078418101,
temp_30 = 13626292
}
--- Retrieves the ground material at a specified position.
---
--- This function performs a raycast downwards from the given coordinates to determine the material type of the ground.
---
--- @param coords vector3 The coordinates from which to perform the raycast.
--- @return number|nil number The material hash if found; otherwise, `nil`.
--- @return string string The name of the material.
---
--- @usage
--- ```lua
--- local materialHash, materialName = GetGroundMaterialAtPosition(vector3(100, 200, 300))
--- print("Ground material:", materialName)
--- ```
function GetGroundMaterialAtPosition(coords)
local endX, endY, endZ = coords.x, coords.y, coords.z - 1.0
local rayHandle = StartShapeTestCapsule(coords.x, coords.y, coords.z, endX, endY, endZ, 1.0, 1, playerPed, 7)
local _, hit, _, _, materialHash, _ = GetShapeTestResultIncludingMaterial(rayHandle)
local materialName = "Unknown"
for k, v in pairs(materials) do
if v == materialHash then
materialName = k
break
end
end
if hit then return materialHash, materialName
else return nil, materialName end
end
--- Retrieves the dimensions of a prop/model.
---
--- This function loads the specified model and returns its width, depth, and height based on its bounding box.
---
--- @param model string The name or hash of the model.
--- @return number number The width of the prop.
--- @return number number The depth of the prop.
--- @return number number The height of the prop.
---
--- @usage
--- ```lua
--- local width, depth, height = GetPropDimensions("prop_barrel_01a")
--- print("Dimensions:", width, depth, height)
--- ```
function GetPropDimensions(model)
loadModel(model)
local minDim, maxDim = GetModelDimensions(model)
local width, depth, height = maxDim.x - minDim.x, maxDim.y - minDim.y, maxDim.z - minDim.z
return width, depth, height
end
--- Retrieves the forward direction vector of an entity based on its heading.
---
--- This function calculates the forward direction vector using the entity's heading angle.
---
--- @param entity number The entity whose forward vector is to be calculated.
--- @return vector3 vector3 The forward direction vector.
---
--- @usage
--- ```lua
--- local forwardVec = GetEntityForwardVector(playerPed)
--- print("Forward Vector:", forwardVec)
--- ```
function GetEntityForwardVector(entity)
local heading = math.rad(GetEntityHeading(entity) + 90)
return vec3(math.cos(heading), math.sin(heading), 0.0)
end

156
shared/input.lua Normal file
View File

@@ -0,0 +1,156 @@
-- INPUT --
-- Multiscript input script function to create simple input text boxes --
--- Creates a simple input dialog compatible with multiple menu systems.
---
--- This function generates input dialogs for different frameworks (OX, QB, GTA) based on the configuration.
--- It supports various input types such as radio buttons, numbers, text, and select dropdowns.
---
---@param title string The title/header of the input dialog.
---@param opts table A table containing input options. Each option should have a `type` and other relevant fields based on the type.
--- - **type** (`string`): The type of input. Supported types: "radio", "number", "text", "select".
--- - **label** (`string`, optional): The label for the input (used for "radio" and "select" types in OX).
--- - **text** (`string`, optional): The text prompt for the input.
--- - **name** (`string`): The identifier name for the input.
--- - **isRequired** (`boolean`, optional): Whether the input is required.
--- - **default** (`any`, optional): The default value for the input.
--- - **options** (`table`, optional): A table of options for "radio" and "select" types.
--- - **min** (`number`, optional): The minimum value (used for "select" type).
--- - **max** (`number`, optional): The maximum value (used for "number" and "select" types).
--- - **txt** (`string`, optional): Additional text or description for the input.
---
---@return table|nil table Returns the user's input as a table if the dialog is submitted, otherwise returns `nil`.
---
---@usage
--- ```lua
--- local userInput = createInput("Enter Details", {
--- { type = "text", text = "Name", name = "playerName", isRequired = true },
--- { type = "number", text = "Age", name = "playerAge", min = 18, max = 99 },
--- { type = "radio", label = "Gender", name = "playerGender", options = {
--- { text = "Male", value = "male" },
--- { text = "Female", value = "female" },
--- { text = "Other", value = "other" },
--- }},
--- })
--- ```
function createInput(title, opts)
local dialog = nil
local options = {}
if Config.System.Menu == "ox" then
for i = 1, #opts do
if opts[i].type == "radio" then
-- Convert radio options to select type for OX
for k in pairs(opts[i].options) do
opts[i].options[k].label = opts[i].options[k].text
end
options[i] = {
type = "select",
isRequired = opts[i].isRequired,
label = opts[i].label or opts[i].text,
name = opts[i].name,
default = opts[i].default or opts[i].options[1].value,
options = opts[i].options,
}
end
if opts[i].type == "number" then
options[i] = {
type = "number",
label = opts[i].text..(opts[i].txt and " - "..opts[i].txt or ""),
isRequired = opts[i].isRequired,
name = opts[i].name,
options = opts[i].options,
}
end
if opts[i].type == "text" then
options[i] = {
type = "input",
label = opts[i].text..(opts[i].txt and " - "..opts[i].txt or ""),
default = opts[i].default,
isRequired = opts[i].isRequired,
}
end
if opts[i].type == "select" then
options[i] = {
type = "select",
label = opts[i].text..(opts[i].txt and " - "..opts[i].txt or ""),
isRequired = opts[i].isRequired,
name = opts[i].name,
options = opts[i].options,
min = opts[i].min,
max = opts[i].max,
default = opts[i].default,
}
end
end
dialog = exports[OXLibExport]:inputDialog(title, options)
return dialog
end
if Config.System.Menu == "qb" then
dialog = exports['qb-input']:ShowInput({ header = title, submitText = "Accept", inputs = opts })
return dialog
end
if Config.System.Menu == "gta" then
WarMenu.CreateMenu(tostring(opts),
title,
" ",
{
titleColor = { 222, 255, 255 },
maxOptionCountOnScreen = 15,
width = 0.25,
x = 0.7,
})
if WarMenu.IsAnyMenuOpened() then return end
WarMenu.OpenMenu(tostring(opts))
local close = true
local _comboBoxItems = {}
local _comboBoxIndex = { 1, 1 }
while true do
if WarMenu.Begin(tostring(opts)) then
for i = 1, #opts do
if opts[i].type == "radio" then
for k in pairs(opts[i].options) do
if not _comboBoxItems[i] then _comboBoxItems[i] = {} end
_comboBoxItems[i][k] = opts[i].options[k].text
end
local _, comboBoxIndex = WarMenu.ComboBox(opts[i].label, _comboBoxItems[i], _comboBoxIndex[i])
if _comboBoxIndex[i] ~= comboBoxIndex then
_comboBoxIndex[i] = comboBoxIndex
end
end
if opts[i].type == "number" then
for b = 1, opts[i].max do
if not _comboBoxItems[i] then _comboBoxItems[i] = {} end
_comboBoxItems[i][b] = b
end
local _, comboBoxIndex = WarMenu.ComboBox(opts[i].text, _comboBoxItems[i], _comboBoxIndex[i])
if _comboBoxIndex[i] ~= comboBoxIndex then
_comboBoxIndex[i] = comboBoxIndex
end
end
end
local pressed = WarMenu.Button("Pay")
if pressed then
WarMenu.CloseMenu()
close = false
local result = {}
for i = 1, #_comboBoxIndex do
result[i] = _comboBoxItems[i][_comboBoxIndex[i]]
end
return result
end
WarMenu.End()
else
return
end
if not WarMenu.IsAnyMenuOpened() and close then
if data.onExit then data.onExit() end
end
Wait(0)
end
end
end

442
shared/isAnimal.lua Normal file
View File

@@ -0,0 +1,442 @@
isCat, isDog, isBigDog, isSmallDog, isCoyote, isAnimal = false, false, false, false, false, false
if not isServer() then
onPlayerLoaded(function()
Wait(2000)
isCat, isDog, isBigDog, isSmallDog, isCoyote = false, false, false, false, false
isPedAnimal()
if isAnimal then
local ped = PlayerPedId()
local pedModel = GetEntityModel(ped)
isCat = (isCat(ped) or pedModel == `ft-raccoon`) and (pedModel ~= `ft-sphynx`)
isDog, isBigDog = isDog(ped)
isSmallDog = not isBigDog
if isDog and pedModel == `a_c_coyote` then isDog = false end
isCoyote = (pedModel == `ft-sphynx` or pedModel == `a_c_coyote`)
if pedModel == `ft-capmonkey2` then isDog = true end
end
end, true)
--- Determines if a given Ped is classified as an animal.
---
--- This function checks whether the specified Ped (or the player's Ped if none is provided)
--- is listed within the predefined `AnimalPeds` tables. It iterates through all animal types
--- to verify if the Ped's model hash matches any known animal models.
---
---@param ped number|nil Optional. The Ped entity to check. Defaults to the player's Ped (`PlayerPedId()`).
---
---@return boolean `true` if the Ped is an animal, otherwise `false`.
---
--- @usage
--- ```lua
--- local isPlayerAnimal = isAnimal()
--- local isSpecificPedAnimal = isAnimal(somePedEntity)
--- ```
function isPedAnimal(ped)
local PedModel = GetEntityModel(ped or PlayerPedId())
for _, animalTypeTable in pairs(AnimalPeds) do
for animalModelHash, _ in pairs(animalTypeTable) do
if PedModel == animalModelHash then
isAnimal = true
break
end
end
if isAnimal then
debugPrint("^6Debug^7: ^2Ped is Animal^1")
break
end
end
return isAnimal
end
--- Checks if a given Ped is classified specifically as a cat.
---
--- This function verifies whether the specified Ped (or the player's Ped if none is provided)
--- matches any of the model hashes listed under `AnimalPeds.CatPeds`. It returns `true` if a match is found.
---
---@param ped number|nil Optional. The Ped entity to check. Defaults to the player's Ped (`PlayerPedId()`).
---
---@return boolean `true` if the Ped is a cat, otherwise `false`.
---
---@usage
--- ```lua
--- if isCat() then
--- print("Player is a cat!")
--- end
---
--- local anotherPed = GetPedInVehicleSeat(vehicle, -1)
--- if isCat(anotherPed) then
--- print("Driver is a cat!")
--- end
--- ```
function isCat(ped)
local PedModel = GetEntityModel(ped or PlayerPedId())
for k, v in pairs(AnimalPeds.CatPeds) do
if PedModel == k then
return true
end
end
return false
end
--- Determines if a given Ped is classified as a dog and identifies its size category.
---
--- This function checks whether the specified Ped (or the player's Ped if none is provided)
--- matches any model hashes listed under `AnimalPeds.BigDogs` or `AnimalPeds.SmallDogs`. It returns
--- two values: the first indicates if the Ped is a dog, and the second specifies whether it's a
--- large dog (`true`) or a small dog (`false`). If the Ped is not a dog, the second return value is `nil`.
---
---@param ped number|nil Optional. The Ped entity to check. Defaults to the player's Ped (`PlayerPedId()`).
---
---@return boolean, boolean|nil Returns `true` and `true` if the Ped is a big dog,
--- `true` and `false` if it's a small dog,
--- or `false` and `nil` if it's not a dog.
---
---@usage
--- ```lua
--- local isDog, isBigDog = isDog()
--- if isDog then
--- if isBigDog then
--- print("Player is a big dog!")
--- else
--- print("Player is a small dog!")
--- end
--- else
--- print("Player is not a dog.")
--- end
---
--- local somePed = GetPedInVehicleSeat(vehicle, 0)
--- local isPetDog, isLargeDog = isDog(somePed)
--- if isPetDog then
--- if isLargeDog then
--- print("Passenger is a big dog!")
--- else
--- print("Passenger is a small dog!")
--- end
--- end
--- ```
function isDog(ped)
local PedModel = GetEntityModel(ped or PlayerPedId())
for k, v in pairs(AnimalPeds.BigDogs) do
if PedModel == k then
return true, true
end
end
for k, v in pairs(AnimalPeds.SmallDogs) do
if PedModel == k then
return true, false
end
end
return false, nil
end
--- Retrieves a list of all animal model hashes.
---
--- This function compiles and returns a flat table containing all model hashes
--- from the various animal categories defined within the `AnimalPeds` table.
--- It's useful for iterating over or performing bulk operations on all animal models.
---
---@return table table A table containing all animal model hashes.
---
---@usage
--- ```lua
--- local allAnimalModels = getAnimalModels()
--- for _, modelHash in ipairs(allAnimalModels) do
--- print("Animal Model Hash:", modelHash)
--- end
--- ```
function getAnimalModels()
local animalTable = {}
for k in pairs(AnimalPeds) do
for v in pairs(AnimalPeds[k]) do
animalTable[#animalTable+1] = v
end
end
return animalTable
end
end
AnimalPeds = {
BigDogs = {
-- Big Dogs
[`a_c_chop`] = {
deathAnim = "dead_right", deathDict = "creatures@chop@move",
exitAnim = "getup_r", exitDict = "creatures@chop@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`a_c_k9`] = {
deathAnim = "dead_right", deathDict = "creatures@chop@move",
exitAnim = "getup_r", exitDict = "creatures@chop@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`a_c_husky`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`a_c_retriever`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`a_c_shepherd`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`a_c_rottweiler`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`ft-aushep`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`golden_r`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`ft-dobermanv2`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`doberman`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`ft-gs`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`k9_husky`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`ft-bloodhound`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`bernard`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`ft-pterrier`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`ft-labrador`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`dane`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`ft_malinois`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`abdog`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`dalmatian`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`a_c_dalmatian`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`ft-boxer`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`ft-bs`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`chowchow`] = {
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
},
[`a_c_coyote`] = {
deathAnim = "dead_right", deathDict = "creatures@coyote@move",
exitAnim = "getup_r", exitDict = "creatures@coyote@getup",
sitAnim = "base", sitDict = "creatures@coyote@amb@world_coyote_howl@base"
},
[`a_c_coyote_02`] = {
deathAnim = "dead_right", deathDict = "creatures@coyote@move",
exitAnim = "getup_r", exitDict = "creatures@coyote@getup",
sitAnim = "base", sitDict = "creatures@coyote@amb@world_coyote_howl@base"
},
},
SmallDogs = {
-- Small Dogs
[`a_c_poodle`] = {
deathAnim = "dead_right", deathDict = "creatures@pug@move",
exitAnim = "getup_r", exitDict = "creatures@pug@getup",
sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
},
[`ft-chihuahua`] = {
deathAnim = "dead_right", deathDict = "creatures@pug@move",
exitAnim = "getup_r", exitDict = "creatures@pug@getup",
sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
},
[`a_c_pug`] = {
deathAnim = "dead_right", deathDict = "creatures@pug@move",
exitAnim = "getup_r", exitDict = "creatures@pug@getup",
sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
},
[`a_c_pug_02`] = {
deathAnim = "dead_right", deathDict = "creatures@pug@move",
exitAnim = "getup_r", exitDict = "creatures@pug@getup",
sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
},
[`a_c_westy`] = {
deathAnim = "dead_right", deathDict = "creatures@pug@move",
exitAnim = "getup_r", exitDict = "creatures@pug@getup",
sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
},
[`ft-pretriever`] = {
deathAnim = "dead_right", deathDict = "creatures@pug@move",
exitAnim = "getup_r", exitDict = "creatures@pug@getup",
sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
},
[`ft-shepk9`] = {
deathAnim = "dead_right", deathDict = "creatures@pug@move",
exitAnim = "getup_r", exitDict = "creatures@pug@getup",
sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
},
},
CatPeds = {
-- Cat
[`bshorthair`] = {
deathAnim = "dead_right", deathDict = "creatures@cat@move",
exitAnim = "getup_r", exitDict = "creatures@cat@getup",
sitAnim = "base", sitDict = "creatures@cat@amb@world_cat_sleeping_ledge@base"
},
[`a_c_cat_01`] = {
deathAnim = "dead_right", deathDict = "creatures@cat@move",
exitAnim = "getup_r", exitDict = "creatures@cat@getup",
sitAnim = "base", sitDict = "creatures@cat@amb@world_cat_sleeping_ledge@base"
},
[`ft-sphynx`] = {
deathAnim = "dead_right", deathDict = "creatures@coyote@move",
exitAnim = "getup_r", exitDict = "creatures@coyote@getup",
sitAnim = "base", sitDict = "creatures@coyote@amb@world_coyote_howl@base"
},
},
OtherPeds = {
-- Other Animals
[`ft-raccoon`] = {
deathAnim = "dead_right", deathDict = "creatures@cat@move",
exitAnim = "getup_r", exitDict = "creatures@cat@getup",
sitAnim = "base", sitDict = "creatures@cat@amb@world_cat_sleeping_ledge@base"
},
[`a_c_hen`] = {
deathAnim = "dead_right", deathDict = "creatures@hen@move",
exitAnim = "getup_r", exitDict = "creatures@hen@getup"
},
[`a_c_rabbit_01`] = {
deathAnim = "dead_right", deathDict = "creatures@rabbit@move",
exitAnim = "getup_r", exitDict = "creatures@rabbit@getup",
sitAnim = "idle_c", sitDict = "creatures@coyote@amb@world_coyote_howl@base"
},
[`a_c_rabbit_02`] = {
deathAnim = "dead_right", deathDict = "creatures@rabbit@move",
exitAnim = "getup_r", exitDict = "creatures@rabbit@getup",
sitAnim = "idle_c", sitDict = "creatures@coyote@amb@world_coyote_howl@base"
},
[`a_c_rat`] = {
deathAnim = "dead_right", deathDict = "creatures@rat@move",
exitAnim = "getup_r", exitDict = "creatures@rat@getup"
},
[`a_c_deer`] = {
deathAnim = "dead_right", deathDict = "creatures@deer@move",
exitAnim = "getup_r", exitDict = "creatures@deer@getup"
},
[`a_c_boar`] = {
deathAnim = "dead_right", deathDict = "creatures@boar@move",
exitAnim = "getup_r", exitDict = "creatures@boar@getup"
},
[`a_c_boar_02`] = {
deathAnim = "dead_right", deathDict = "creatures@boar@move",
exitAnim = "getup_r", exitDict = "creatures@boar@getup"
},
[`a_c_chicken`] = {
deathAnim = "dead_right", deathDict = "creatures@chicken@move",
exitAnim = "getup_r", exitDict = "creatures@chicken@getup"
},
[`a_c_pig`] = {
deathAnim = "dead_right", deathDict = "creatures@pig@move",
exitAnim = "getup_r", exitDict = "creatures@pig@getup"
},
[`a_c_sharkhammer`] = {
deathAnim = "dead_right", deathDict = "creatures@sharkhammer@move",
exitAnim = "getup_r", exitDict = "creatures@sharkhammer@getup"
},
[`a_c_sharktiger`] = {
deathAnim = "dead_right", deathDict = "creatures@sharktiger@move",
exitAnim = "getup_r", exitDict = "creatures@sharktiger@getup"
},
[`a_c_crow`] = {
deathAnim = "dead_down", deathDict = "creatures@crow@move",
exitAnim = "nill", exitDict = "creatures@pug@move", -- no get up anim
},
[`a_c_pigeon`] = {
deathAnim = "dead_down", deathDict = "creatures@pigeon@move",
exitAnim = "nill", exitDict = "creatures@pug@move", -- no get up anim
},
},
Monekys = {
[`ft-chimpanzee`] = {
deathAnim = "dead", deathDict = "dead_a",
exitAnim = "get_up@sat_on_floor@to_stand", exitDict = "getup_0"
},
[`a_c_chimp`] = {
deathAnim = "dead", deathDict = "dead_a",
exitAnim = "get_up@sat_on_floor@to_stand", exitDict = "getup_0"
},
[`a_c_chimp_02`] = {
deathAnim = "dead", deathDict = "dead_a",
exitAnim = "get_up@sat_on_floor@to_stand", exitDict = "getup_0"
},
[`a_c_rhesus`] = {
deathAnim = "dead", deathDict = "dead_a",
exitAnim = "get_up@sat_on_floor@to_stand", exitDict = "getup_0"
},
[`ft-capmonkey2`] = {
deathAnim = "dead_right", deathDict = "creatures@pug@move",
exitAnim = "getup_r", exitDict = "creatures@pug@getup",
sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
},
}
}

606
shared/itemcontrol.lua Normal file
View File

@@ -0,0 +1,606 @@
-- Function to register items as usable for ESX, QBX, and QBcore --
---
--- This function registers an item as usable across different inventory systems such as ESX, QBcore, and QBX.
--- It checks which inventory system is active and registers the usable item accordingly.
---
---@param item string The name of the item to be registered as usable.
---@param funct function The function to execute when the item is used.
---
---@usage
--- ```lua
--- createUseableItem("health_potion", function(source)
--- -- Code to consume the health potion
--- end)
--- ```
function createUseableItem(item, funct)
if isStarted(ESXExport) then
debugPrint("^6Bridge^7: ^2Registering item as ^3Usable^2 with ^7es_extended", item)
while not ESX do Wait(0) end
ESX.RegisterUsableItem(item, funct)
elseif isStarted(QBExport) and not isStarted(QBXExport) then
debugPrint("^6Bridge^7: ^2Registering item as ^3Usable^2 with ^7qb-core", item)
Core.Functions.CreateUseableItem(item, funct)
elseif isStarted(QBXExport) then
debugPrint("^6Bridge^7: ^2Registering item as ^3Usable^2 with ^7qbx_core", item)
exports[QBXExport]:CreateUseableItem(item, funct)
end
end
-- Simple function to grab the item's image from inventories and retrieve it as a nui:// link --
---
--- This function retrieves the image URL of an item from various inventory systems and formats it as a `nui://` link.
--- It supports multiple inventory systems such as OXInv, QSInv, CoreInv, OrigenInv, QBInv, and CodeMInv.
---
---@param item string The name of the item whose image is to be retrieved.
---@return string link The `nui://` link to the item's image. Returns an empty string if the inventory system is not detected or the item doesn't exist.
---
---@usage
--- ```lua
--- local imageLink = invImg("health_potion")
--- if imageLink ~= "" then
--- print(imageLink)
--- end
--- ```
function invImg(item)
local imgLink = ""
if item ~= "" and Items[item] then
if isStarted(OXInv) then
imgLink = "nui://"..OXInv.."/web/images/"..(Items[item].image or "")
elseif isStarted(QSInv) then
imgLink = "nui://"..QSInv.."/html/images/"..(Items[item].image or "")
elseif isStarted(CoreInv) then
imgLink = "nui://"..CoreInv.."/html/img/"..(Items[item].image or "")
elseif isStarted(OrigenInv) then
imgLink = "nui://"..OrigenInv.."/html/img/"..(Items[item].image or "")
elseif isStarted(QBInv) then
imgLink = "nui://"..QBInv.."/html/images/"..(Items[item].image or "")
elseif isStarted(CodeMInv) then
imgLink = "nui://"..CodeMInv.."/html/itemimages/"..(Items[item].image or "")
else
print("^4ERROR^7: ^2No Inventory detected for invImg ^7- ^2Check ^3exports^1.^2lua^7")
end
end
return imgLink
end
--- Adds an item to a player's inventory.
---
--- This function triggers a server event to add a specified amount of an item to the player's inventory.
---
---@param item string The name of the item to add.
---@param amount number The quantity of the item to add.
---@param info table|nil Additional information or metadata for the item.
---
---@usage
--- ```lua
--- addItem("health_potion", 2, { quality = "high" })
--- ```
function addItem(item, amount, info, src)
if not Items[item] then print("^6Debug^7: ^1Error^7 - ^2Tried to give ^7'^3"..item.."^7'^2 but it doesn't exist") return end
if src then
TriggerEvent(getScript()..":server:toggleItem", true, item, amount, src, info)
else
TriggerServerEvent(getScript()..":server:toggleItem", true, item, amount, nil, info)
end
end
--- Removes an item from a player's inventory.
---
--- This function triggers a server event to remove a specified amount of an item from the player's inventory.
---
---@param item string The name of the item to remove.
---@param amount number The quantity of the item to remove.
---
---@usage
--- ```lua
--- removeItem("health_potion", 1)
--- ```
function removeItem(item, amount, src)
if not Items[item] then print("^6Debug^7: ^1Error^7 - ^2Tried to remove ^7'^3"..item.."^7'^2 but it doesn't exist") return end
if src then
TriggerEvent(getScript()..":server:toggleItem", false, item, amount, src, info)
else
TriggerServerEvent(getScript()..":server:toggleItem", false, item, amount, nil, info)
end
end
--- Server event handler to toggle items in a player's inventory.
---
--- This event handles adding or removing items based on the parameters received.
--- It supports multiple inventory systems and includes exploit protection to prevent duplication.
---
---@param give boolean Indicates whether to add (`true`) or remove (`false`) the item.
---@param item string The name of the item to toggle.
---@param amount number The quantity of the item to toggle.
---@param newsrc number|nil The source ID of the player. If `nil`, it defaults to the event source.
---@param info table|nil Additional information or metadata for the item.
---
---@usage
--- ```lua
--- TriggerServerEvent("script:server:toggleItem", true, "health_potion", 1)
--- ```
RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount, newsrc, info)
if not Items[item] then print("^6Debug^7: ^1Error^7 - ^2Tried to "..(tostring(give) == "true" and "add" or "remove").." ^7'^3"..item.."^7'^2 but it doesn't exist") return end
local src = newsrc or source
local addremove = (tostring(give) == "true" and "addItem" or "removeItem")
debugPrint("^6Bridge^7: ^3toggleItem ^2triggered^7: ^6"..addremove.."^7 - '"..tostring(item).."' x"..(tostring(amount) or "1"))
local remamount = (amount and amount or 1)
if item == nil then return end
if give == 0 or give == false then
if hasItem(item, amount and amount or 1, src) then -- Check if the player has the item
if isStarted(OXInv) then
local success = exports[OXInv]:RemoveItem(src, item, (amount and amount or 1), nil)
debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..OXInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
elseif isStarted(QSInv) then
local success = exports[QSInv]:RemoveItem(src, item, amount)
debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..QSInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
elseif isStarted(CoreInv) then
if isStarted(QBExport) then
Core.Functions.GetPlayer(src).Functions.RemoveItem(item, amount, nil)
elseif isStarted(ESXExport) then
ESX.GetPlayerFromId(src).removeInventoryItem(item, count)
end
debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..CoreInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
elseif isStarted(OrigenInv) then
local success = exports[OrigenInv]:RemoveItem(src, item, amount)
debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..OrigenInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
elseif isStarted(CodeMInv) then
local success = exports[CodeMInv]:RemoveItem(src, item, amount)
debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..CodeMInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
elseif isStarted(QBInv) then
while remamount > 0 do
if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1) then
remamount -= 1
else
print("^1Error removing "..data.item.." Amount left to remove: "..remamount.."^7")
break
end
end
if Config.Crafting.showItemBox then
TriggerClientEvent((isStarted(QBInv) and QBInvNew and "qb-" or "")..'inventory:client:ItemBox', src, Items[item], "remove", (amount and amount or 1))
end
debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..QBInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
elseif isStarted(PSInv) then
while remamount > 0 do
if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1) then
remamount -= 1
else
print("^1Error removing "..data.item.." Amount left to remove: "..remamount.."^7")
break
end
end
if Config.Crafting.showItemBox then
TriggerClientEvent('inventory:client:ItemBox', src, Items[item], "remove", (amount and amount or 1))
end
debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..PSInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
else
print("^4ERROR^7: ^2No Inventory detected ^7- ^2Check ^3exports^1.^2lua^7")
end
else
dupeWarn(src, item, amount) -- Trigger exploit protection
end
else
local amount = amount and amount or 1
if isStarted(OXInv) then
local success = exports[OXInv]:AddItem(src, item, amount or 1, info)
if not Items[item] or not Items[item].label then
print("^1Error^7: "..addremove.." ["..OXInv.."] Player("..src..") "..Items[item]?.label.."("..item..") x"..(amount or 1))
end
debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..OXInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
elseif isStarted(QSInv) then
local success = exports[QSInv]:AddItem(src, item, amount)
debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..QSInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
elseif isStarted(CoreInv) then
if isStarted(QBExport) or isStarted(QBXExport) then
Core.Functions.GetPlayer(src).Functions.AddItem(item, amount, nil, nil)
elseif isStarted(ESXExport) then
ESX.GetPlayerFromId(src).addInventoryItem(item, amount)
end
debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..CoreInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
elseif isStarted(CodeMInv) then
local success = exports[CodeMInv]:AddItem(src, item, amount)
debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..CodeMInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
elseif isStarted(OrigenInv) then
local success = exports[OrigenInv]:AddItem(src, item, amount)
debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..OrigenInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
elseif isStarted(QBInv) then
if Core.Functions.GetPlayer(src).Functions.AddItem(item, amount or 1, nil, info) then
TriggerClientEvent((isStarted(QBInv) and QBInvNew and "qb-" or "")..'inventory:client:ItemBox', src, Items[item], "add", amount and amount or 1)
end
debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..QBInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
elseif isStarted(PSInv) then
if Core.Functions.GetPlayer(src).Functions.AddItem(item, amount or 1, nil, info) then
if Config.Crafting.showItemBox then
TriggerClientEvent("inventory:client:ItemBox", src, Items[item], "add", amount and amount or 1)
end
end
debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..PSInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
else
print("^4ERROR^7: ^2No Inventory detected ^7- ^2Check ^3exports^1.^2lua^7")
end
end
end)
--- Protects against item duplication exploits by warning and potentially kicking the player.
---
--- This function is called when an attempt is made to remove an item that the player does not possess.
--- It logs the incident and kicks the player if `debugMode` is not enabled.
---
--- @param src number The source ID of the player attempting the exploit.
--- @param item string The name of the item being exploited.
---
--- @usage
--- ```lua
--- dupeWarn(playerId, "health_potion")
--- ```
function dupeWarn(src, item)
local name = getPlayer(src).name
print("^5DupeWarn^7: "..name.." (^1"..tostring(src).."^7) ^2Tried to remove item ^7'^3"..item.."^7'^2 but it wasn't there^7")
if not debugMode then
DropPlayer(src, name.."("..tostring(src)..") Kicked for suspected duplicating items: "..item)
end
print("^5DupeWarn^7: "..name.."(^1"..tostring(src).."^7) ^2Dropped from server - exploit protection detected an item not being found in players inventory^7")
end
--- Breaks a tool by reducing its durability or removing it if durability reaches zero.
---
--- This function handles the durability mechanics for tools. If a tool's durability drops to zero or below,
--- it removes the tool from the player's inventory and plays a breaking sound.
---
--- @param data table A table containing data about the tool being used.
--- - **item** (`string`): The name of the tool item.
--- - **damage** (`number`): The amount of durability damage to apply.
---
--- @usage
--- ```lua
--- breakTool({ item = "drill", damage = 10 })
--- ```
function breakTool(data) -- WIP
local durability, slot = getDurability(data.item)
if not durability then durability = 100 end
durability -= data.damage
if durability <= 0 then
removeItem(data.item, 1)
local breakId = GetSoundId()
PlaySoundFromEntity(breakId, "Drill_Pin_Break", PlayerPedId(), "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
else
TriggerServerEvent(getScript()..":server:setMetaData", { item = data.item, slot = slot, metadata = { durability = durability } })
end
end
--- Retrieves the durability and slot of an item in a player's inventory.
---
--- This function searches the player's inventory for the specified item and returns its durability and slot number.
---
--- @param item string The name of the item to check.
--- @return number|nil The durability of the item. Returns `nil` if not found.
--- @return number|nil The slot number of the item. Returns `nil` if not found.
---
--- @usage
--- ```lua
--- local durability, slot = getDurability("drill")
--- if durability then
--- print("Durability:", durability)
--- end
--- ```
function getDurability(item)
local lowestSlot = 100
local durability = nil
if isStarted(QBInv) or isStarted(PSInv) then
local itemcheck = Core.Functions.GetPlayerData().items
for k, v in pairs(itemcheck) do
if v.name == item then
if v.slot <= lowestSlot then
lowestSlot = v.slot
durability = itemcheck[k].info.durability
end
end
end
end
if isStarted(OXInv) then
local itemcheck = exports[OXInv]:Search('slots', item)
for k, v in pairs(itemcheck) do
if v.slot <= lowestSlot then
debugPrint(v.slot, itemcheck[k].metadata.durability)
lowestSlot = v.slot
durability = itemcheck[k].metadata.durability
end
end
end
if isStarted(QSInv) then
local itemcheck = exports[QSInv]:getUserInventory()
for k, v in pairs(itemcheck) do
if v.name == item and v.slot <= lowestSlot then
lowestSlot = v.slot
durability = itemcheck[k].metadata.durability
end
end
end
if isStarted(OrigenInv) then
local itemcheck = exports[OrigenInv]:getPlayerInventory()
for k, v in pairs(itemcheck) do
if v.name == item and v.slot <= lowestSlot then
lowestSlot = v.slot
durability = itemcheck[k].metadata.durability
end
end
end
return durability, lowestSlot
end
--- Server event handler to set metadata for an item in a player's inventory.
---
--- This event updates the metadata (e.g., durability) of an item in the player's inventory.
---
---@param data table A table containing metadata information.
--- - **item** (`string`): The name of the item.
--- - **slot** (`number`): The slot number of the item in the inventory.
--- - **metadata** (`table`): The metadata to set for the item.
---
---@usage
--- ```lua
--- TriggerServerEvent("script:server:setMetaData", { item = "drill", slot = 5, metadata = { durability = 80 } })
--- ```
RegisterNetEvent(getScript()..":server:setMetaData", function(data)
local src = source
if isStarted(QBInv) or isStarted(PSInv) then
debugPrint(src, data.item, 1, data.slot)
local Player = Core.Functions.GetPlayer(src)
Player.PlayerData.items[data.slot].info = data.metadata
Player.PlayerData.items[data.slot].description = "HP : "..data.metadata.durability
Player.Functions.SetInventory(Player.PlayerData.items)
end
if isStarted(OXInv) then
exports[OXInv]:SetMetadata(source, data.slot, data.metadata)
end
if isStarted(QSInv) then
exports[QSInv]:SetItemMetadata(source, data.slot, data.metadata)
end
if isStarted(OrigenInv) then
local item = exports[OrigenInv]:GetItemBySlot(source, data.slot)
if item then
exports[OrigenInv]:SetItemData(source, item.name, "durability", data.metadata.durability)
end
end
end)
--- Checks if a player has the specified items in their inventory.
---
--- This function verifies whether a player possesses the required quantity of specified items.
--- It supports multiple inventory systems and provides detailed feedback on item availability.
---
---@param items string|table A single item name or a table of item names with their required amounts.
---@param amount number The quantity required for each item. Defaults to `1` if not specified.
---@param src number|nil The source ID of the player. If `nil`, it defaults to the caller.
---@return boolean Returns `true` if the player has all the required items in the specified amounts.
---@return table|nil Returns a table detailing which items are present or missing if not all items are found.
---
---@usage
--- ```lua
--- local hasAll, details = hasItem({"health_potion", "mana_potion"}, 2, playerId)
--- if hasAll then
--- -- Proceed with action
--- else
--- -- Inform the player about missing items
--- end
--- ```
function hasItem(items, amount, src)
local amount = amount and amount or 1
local grabInv, foundInv = getPlayerInv(src)
if type(items) ~= "table" then items = { [items] = amount and amount or 1, } end
if grabInv then
local hasTable = {}
for item, amt in pairs(items) do
if not Items[item] then print("^4ERROR^7: ^2Script can't find ingredient item in Shared Items - ^1"..item.."^7") end
local count = 0
for _, itemData in pairs(grabInv) do
if itemData and (itemData.name == item) then count += (itemData.count or itemData.amount or 1) end
end
foundInv = foundInv:gsub("%-", "^7-^6"):gsub("%_", "^7_^6")
local foundMessage = "^6Bridge^7: ^3hasItem^7[^6"..foundInv.."^7]: "..tostring(item).." ^3"..count.."^7/^3"..amt
if count >= amt then foundMessage = foundMessage.." ^5FOUND^7" else foundMessage = foundMessage .." ^1NOT FOUND^7" end
debugPrint(foundMessage)
hasTable[item] = { hasItem = count >= amt, count = count }
end
for k, v in pairs(hasTable) do if not v.hasItem then return false, hasTable end end
return true, hasTable
end
end
--- Retrieves a player's inventory from the active inventory system.
---
--- This function fetches the player's inventory based on the active inventory system.
--- It supports multiple systems including OXInv, QSInv, OrigenInv, CoreInv, CodeMInv, QBInv, and PSInv.
---
---@param src number|nil The source ID of the player. If `nil`, it fetches the current player's inventory.
---@return table|nil The inventory items of the player.
---@return string|nil The name of the inventory system being used.
---
---@usage
--- ```lua
--- local inventory, system = getPlayerInv(playerId)
--- if inventory then
--- -- Process inventory
--- end
--- ```
function getPlayerInv(src)
local grabInv = nil
local foundInv = ""
if isStarted(OXInv) then
foundInv = OXInv
if src then grabInv = exports[OXInv]:GetInventoryItems(src)
else grabInv = exports[OXInv]:GetPlayerItems() end
elseif isStarted(QSInv) then
foundInv = QSInv
if src then grabInv = exports[QSInv]:GetInventory(src)
else grabInv = exports[QSInv]:getUserInventory() end
elseif isStarted(OrigenInv) then
foundInv = OrigenInv
if src then grabInv = exports[OrigenInv]:GetInventory(src)
else grabInv = exports[OrigenInv]:getPlayerInventory() end
elseif isStarted(CoreInv) then
foundInv = CoreInv
if src then
if isStarted(QBExport) or isStarted(QBXExport) then
grabInv = Core.Functions.GetPlayer(src).PlayerData.items
elseif isStarted(ESXExport) then
local Player = ESX.GetPlayerFromId(src)
grabInv = Player.getInventory(false)
end
else
local p = promise.new()
Core.Functions.TriggerCallback('core_inventory:server:getInventory', function(cb) p:resolve(cb) end)
local result = Citizen.Await(p)
if type(result) == "string" then result = json.decode(result) end
grabInv = result
end
elseif isStarted(CodeMInv) then
foundInv = CodeMInv
if src then grabInv = exports[CodeMInv]:GetInventory(src)
else grabInv = exports[CodeMInv]:GetClientPlayerInventory() end
elseif isStarted(QBInv) then
foundInv = QBInv
if src then grabInv = Core.Functions.GetPlayer(src).PlayerData.items
else grabInv = Core.Functions.GetPlayerData().items end
elseif isStarted(PSInv) then
foundInv = PSInv
if src then grabInv = Core.Functions.GetPlayer(src).PlayerData.items
else grabInv = Core.Functions.GetPlayerData().items end
else
print("^4ERROR^7: ^2No Inventory detected ^7- ^2Check ^3exports^1.^2lua^7")
end
return grabInv, foundInv
end
--- Generates a random reward from a predefined reward pool.
---
--- This function is intended for job scripts where players receive random rewards upon completing certain tasks.
--- It ensures that the player has the required item before attempting to grant a reward.
---
---@param itemName string The name of the item to check for eligibility to receive a reward.
---
---@usage
--- ```lua
--- getRandomReward("gold_ring")
--- ```
function getRandomReward(itemName) -- Intended for job scripts
if Config.Rewards.RewardPool then
local reward = false
if type(Config.Rewards.RewardItem) == "string" then Config.Rewards.RewardItem = { Config.Rewards.RewardItem } end
for k, v in pairs(Config.Rewards.RewardItem) do
if v == itemName then reward = true break end
end
if reward then
removeItem(itemName, 1)
local totalRarity = 0
for i=1, #Config.Rewards.RewardPool do
totalRarity += Config.Rewards.RewardPool[i].rarity
end
debugPrint("^6Bridge^7: ^3getRandomReward^7: ^2Total Rarity ^7'^6"..totalRarity.."^7'")
local randomNum = math.random(1, totalRarity)
debugPrint("^6Bridge^7: ^3getRandomReward^7: ^2Random Number ^7'^6"..randomNum.."^7'")
local currentRarity = 0
for i=1, #Config.Rewards.RewardPool do
currentRarity += Config.Rewards.RewardPool[i].rarity
if randomNum <= currentRarity then
debugPrint("^6Bridge^7: ^3getRandomReward^7: ^2Selected toy ^7'^6"..Config.Rewards.RewardPool[i].item.."^7'")
addItem(Config.Rewards.RewardPool[i].item, 1)
return
end
end
end
end
end
--- Checks if a player can carry specific items in their inventory.
---
--- This function determines whether a player has enough capacity to carry the specified items.
--- It considers the weight of each item and the player's current inventory weight.
---
---@param itemTable table A table where keys are item names and values are the quantities to check.
---@param src number The source ID of the player.
---@return table A table where keys are item names and values are booleans indicating if the player can carry the specified quantity.
---
---@usage
--- ```lua
--- local canCarry = canCarry({ ["health_potion"] = 2, ["mana_potion"] = 3 }, playerId)
--- if canCarry["health_potion"] and canCarry["mana_potion"] then
--- -- Proceed with adding items
--- else
--- -- Inform the player they can't carry all items
--- end
--- ```
function canCarry(itemTable, src)
local resultTable = {}
if src then
if isStarted(OXInv) then
for k, v in pairs(itemTable) do
resultTable[k] = exports[OXInv]:CanCarryItem(src, k, v)
end
elseif isStarted(QSInv) then
for k, v in pairs(itemTable) do
resultTable[k] = exports[OXInv]:CanCarryItem(src, k, v)
end
elseif isStarted(CoreInv) then
--??
elseif isStarted(CodeMInv) then
for k, v in pairs(itemTable) do
local weight = Items[k].weight
resultTable[k] = exports[CodeMInv]:CanCarryItem(src, weight, v)
end
elseif isStarted(OrigenInv) then
for k, v in pairs(itemTable) do
resultTable[k] = exports[OrigenInv]:canCarryItem(src, k, v)
end
elseif isStarted(QBInv) or isStarted(PSInv) then
local Player = Core.Functions.GetPlayer(src)
local items = Player.PlayerData.items
local weight, totalWeight = 0, 0
if not items then return false end
for _, item in pairs(items) do weight += item.weight * item.amount end
totalWeight = tonumber(weight)
for k, v in pairs(itemTable) do
local itemInfo = Items[k]
if not itemInfo and not Player.Offline then
triggerNotify(nil, 'Item does not exist', 'error', src)
resultTable[k] = true
else
resultTable[k] = (totalWeight + (Items[k]['weight'] * v)) <= InventoryWeight
end
end
end
end
return resultTable
end

196
shared/jobfunctions.lua Normal file
View File

@@ -0,0 +1,196 @@
-- Global variable to track duty status
onDuty = false
--- Scans the 'Jobs' and 'Gangs' tables to identify roles classified as Bosses.
---
--- This function iterates through the specified role's grades within the `Jobs` or `Gangs` tables.
--- It identifies which grades are marked as bosses (`isboss`) or have bank authorization (`bankAuth`).
--- The function returns a table where each role maps to the lowest grade number that qualifies as a boss.
---
---@param role string The name of the job or gang role to check for boss grades.
---
---@return table table A table containing roles mapped to their respective boss grade numbers.
---
---@usage
--- ```lua
--- local bosses = makeBossRoles("police")
--- if bosses["police"] then
--- print("Police role has a boss grade.")
--- end
--- ```
function makeBossRoles(role)
local boss = {}
local data = (Jobs and Jobs[role]) or (Gangs and Gangs[role])
if data then
for grade, info in pairs(data.grades) do
if info.isboss or info.bankAuth then
boss[role] = boss[role] and math.min(boss[role], tonumber(grade)) or tonumber(grade)
end
end
end
return boss
end
--- Checks if the player has a specific job and is on duty.
---
--- This function verifies whether the player possesses the specified job and, if applicable,
--- whether they are currently on duty. It provides a notification if the player fails these checks.
---
---@param job string The name of the job or gang to check.
---
---@return boolean Returns `true` if the player has the job (and is on duty if required), otherwise `false`.
---
---@usage
--- ```lua
--- if jobCheck("mechanic") then
--- -- Allow access to mechanic-related features
--- else
--- -- Deny access or notify the player
--- end
--- ```
function jobCheck(job)
canDo = true
if Jobs[job] then
if not hasJob(job) or not onDuty then
triggerNotify(nil, Loc[Config.Lan].error["not_clockedin"])
canDo = false
end
end
if Gangs[job] then
if not hasJob(job) then
canDo = false
end
end
return canDo
end
--- Toggles the player's duty status.
---
--- This function switches the player's duty state between on-duty and off-duty.
--- It integrates with QBcore's duty system if available; otherwise, it manually toggles the `onDuty` variable
--- and sends a notification to the player about their new duty status.
---
---@usage
--- ```lua
--- toggleDuty()
--- -- Player will receive a notification indicating their new duty status
--- ```
function toggleDuty()
if isStarted(QBExport) or isStarted(QBXExport) then
TriggerServerEvent("QBCore:ToggleDuty")
else
onDuty = not onDuty
if onDuty then
triggerNotify(nil, "Now on duty", "success")
else
triggerNotify(nil, "Now off duty", "success")
end
end
end
--- Initiates the hand-washing action for the player.
---
--- This function triggers an animation and a progress bar to simulate the player washing their hands.
--- Upon completion, it sends a success notification. If the action is canceled, it notifies the player of the cancellation.
---
---@param data table A table containing the coordinates where the hand-washing action takes place.
--- - **coords** (`vector3`): The position where the hand-washing animation and camera are focused.
---
---@return void
---
---@usage
--- ```lua
--- washHands({ coords = vector3(200.0, 300.0, 40.0) })
--- -- Player will perform the hand-washing animation at the specified location
--- ```
function washHands(data) local ped = PlayerPedId()
lookEnt(data.coords)
local cam = createTempCam(ped, data.coords)
if progressBar({
label = Loc[Config.Lan].progressbar["progress_washing"],
time = 5000,
cancel = true,
dict = "mp_arresting",
anim = "a_uncuff",
flag = 32,
icon = "fas fa-hand-holding-droplet",
cam = cam
}) then
triggerNotify(nil, Loc[Config.Lan].success["washed_hands"], "success")
else
triggerNotify(nil, Loc[Config.Lan].error["cancel"], 'error')
end
ClearPedTasks(ped)
end
--- Handles the player's interaction with a toilet or urinal.
---
--- This function manages the animations and progress bars associated with using a toilet or urinal.
--- Depending on whether the interaction is with a urinal (`data.urinal`), it plays the appropriate animation
--- and triggers server events upon successful completion. If the action is canceled, it notifies the player.
---
---@param data table A table containing data about the toilet interaction.
--- - **urinal** (`boolean`): Indicates whether the interaction is with a urinal (`true`) or a toilet (`false`).
--- - **sitcoords** (`vector4`): The coordinates and heading for the seating animation when using a toilet.
---
---@usage
--- ```lua
--- useToilet({ urinal = true })
--- -- Player uses a urinal with corresponding animations and notifications
---
--- useToilet({ urinal = false, sitcoords = vector4(215.76, -810.12, 29.73, 90.0) })
--- -- Player sits down to use a toilet with corresponding animations and notifications
--- ```
function useToilet(data)
if data.urinal then
if progressBar({
label = "Using Urinal",
time = 5000,
cancel = true,
dict = "misscarsteal2peeing",
anim = "peeing_loop",
flag = 32
}) then
TriggerServerEvent(getScript().."server:Urinal")
else
lockInv(false)
triggerNotify(nil, Loc[Config.Lan].error["cancelled"], 'error')
end
else
TaskStartScenarioAtPosition(PlayerPedId(), "PROP_HUMAN_SEAT_CHAIR_MP_PLAYER", data.sitcoords.x, data.sitcoords.y, data.sitcoords.z, data.sitcoords[4], 0, 1, true)
if progressBar({
label = "Using Toilet",
time = 10000,
cancel = true
}) then
TriggerServerEvent(getScript().."server:Urinal")
ClearPedTasks(PlayerPedId())
else
lockInv(false)
triggerNotify(nil, Loc[Config.Lan].error["cancelled"], 'error')
end
end
end
--- Teleports the player to specified coordinates with a fade effect.
---
--- This function fades the screen out, moves the player to the target coordinates (`data.telecoords`),
--- sets the player's heading, and then fades the screen back in. It's commonly used for door interactions
--- or teleportation points within the game.
---
---@param data table A table containing teleportation data.
--- - **telecoords** (`vector4`): The target coordinates and heading for the teleportation.
---
---@usage
--- ```lua
--- useDoor({ telecoords = vector4(215.76, -810.12, 29.73, 90.0) })
--- -- Player is teleported to the specified coordinates with a fade effect
--- ```
function useDoor(data)
DoScreenFadeOut(500)
while not IsScreenFadedOut() do Wait(10) end
SetEntityCoords(PlayerPedId(), data.telecoords.xyz, 0, 0, 0, false)
SetEntityHeading(PlayerPedId(), data.telecoords.w)
DoScreenFadeIn(1000)
Wait(100)
end

75
shared/make/cameras.lua Normal file
View File

@@ -0,0 +1,75 @@
--- Creates a temporary camera at a specified position, pointing towards given coordinates.
--
-- This function creates a camera at a position relative to an entity or at a specified position and orients it to look at the target coordinates.
-- The camera is only created if `Config.Crafting.craftCam` is enabled in the configuration.
--
---@param ent entityId|coords The base position for the camera. Can be an entity handle or a `vector3` position.
-- If `ent` is an entity, the camera position is calculated as an offset from the entity's position using `GetOffsetFromEntityInWorldCoords`.
-- If `ent` is a `vector3`, it is used directly as the camera's position.
--
---@param coords vector3 The target `vector3` coordinates that the camera will point at.
--
---@return cam camID The handle of the created camera, or `nil` if the camera was not created (e.g., if `Config.Crafting.craftCam` is `false`).
--
---@usage
-- ```lua
-- local cam = createTempCam(entity, targetCoords)
-- ```
function createTempCam(ent, coords)
local cam = nil
if Config.Crafting.craftCam then
if debugMode then
triggerNotify(nil, "ModCam Created", "success")
end
local camCoords = nil
if type(ent) ~= "vector3" then
camCoords = GetOffsetFromEntityInWorldCoords(ent, 1.0, -0.3, 0.8)
else
camCoords = ent
end
-- Create the camera with specified parameters
cam = CreateCamWithParams("DEFAULT_SCRIPTED_CAMERA", camCoords.x, camCoords.y, camCoords.z + 0.5, 1.0, 0.0, 0.0, 60.00, false, 0)
-- Point the camera at the target coordinates
PointCamAtCoord(cam, coords)
end
return cam
end
--- Activates and starts rendering the temporary camera.
--
-- This function sets the specified camera as active and begins rendering it with a smooth transition.
-- The camera is only activated if `Config.Crafting.craftCam` is enabled in the configuration.
--
---@param cam camID The handle of the camera to activate and render.
--
---@usage
-- ```lua
-- startTempCam(cam)
-- ```
function startTempCam(cam)
if Config.Crafting.craftCam then
SetCamActive(cam, true)
RenderScriptCams(true, true, 1000, true, true)
end
end
--- Deactivates the temporary camera and stops rendering.
--
-- This function waits for one second, then stops rendering script cameras and destroys all cameras.
-- The delay allows for any transitions or animations to complete.
--
-- The camera is only deactivated if `Config.Crafting.craftCam` is enabled in the configuration.
--
---@usage
-- ```lua
-- stopTempCam()
-- ```
function stopTempCam()
if Config.Crafting.craftCam then
CreateThread(function()
Wait(1000)
RenderScriptCams(false, true, 500, true, true)
DestroyAllCams()
end)
end
end

239
shared/make/loaders.lua Normal file
View File

@@ -0,0 +1,239 @@
local time = 500
--- Loads a specified model into memory.
---
--- This function checks if the model is valid and not already loaded.
--- If not loaded, it requests the model and waits until it is loaded or times out.
---
---@param model string|number The name or hash of the model to load.
---
---@usage
--- ```lua
--- loadModel('prop_chair_01a')
--- ```
function loadModel(model)
if not IsModelValid(model) then print("^6Bridge^7: ^1ERROR^7: ^2Model^7 - '^6"..model.."^7' ^2does not exist in server") return
else
if not HasModelLoaded(model) then
debugPrint("^6Bridge^7: ^2Loading Model^7: '^6"..model.."^7'")
while not HasModelLoaded(model) and time > 0 do time -= 1 RequestModel(model) Wait(0) end
if not HasModelLoaded(model) then print("^6Bridge^7: ^3LoadModel^7: ^2Timed out loading model ^7'^6"..model.."^7'") end
end
time = 500
end
end
--- Unloads a model from memory.
---
--- This function marks a model as no longer needed, allowing the game to free up memory.
---
---@param model string|number The name or hash of the model to unload.
---
---@usage
--- ```lua
--- unloadModel('prop_chair_01a')
--- ```
function unloadModel(model)
debugPrint("^6Bridge^7: ^2Removing Model from memory cache^7: '^6"..model.."^7'")
SetModelAsNoLongerNeeded(model)
end
--- Loads an animation dictionary into memory.
---
--- This function checks if the animation dictionary exists and requests it.
--- It waits until the animation dictionary is loaded before proceeding.
---
---@param animDict string The name of the animation dictionary to load.
---
---@usage
--- ```lua
--- loadAnimDict('amb@world_human_hang_out_street@male_c@base')
--- ```
function loadAnimDict(animDict)
if not DoesAnimDictExist(animDict) then
print("^6Bridge^7: ^1ERROR^7: ^2Anim Dictionary^7 - '^6"..animDict.."^7' ^2does not exist in server") return
else
debugPrint("^6Bridge^7: ^2Loading Anim Dictionary^7: '^6"..animDict.."^7'")
while not HasAnimDictLoaded(animDict) do RequestAnimDict(animDict) Wait(5) end
end
end
--- Unloads an animation dictionary from memory.
---
--- This function removes the animation dictionary from the game's memory cache.
---
---@param animDict string The name of the animation dictionary to unload.
---
---@usage
---@
--- ```lua
--- unloadAnimDict('amb@world_human_hang_out_street@male_c@base')
--- ```
function unloadAnimDict(animDict)
debugPrint("^6Bridge^7: ^2Removing Anim Dictionary from memory cache^7: '^6"..animDict.."^7'")
RemoveAnimDict(animDict)
end
--- Loads a particle effects (ptfx) dictionary into memory.
---
--- This function requests the named particle effects asset and waits until it's loaded.
---
---@param ptFxName string The name of the particle effects dictionary to load.
---
---@usage
--- ```lua
--- loadPtfxDict('core')
--- ```
function loadPtfxDict(ptFxName)
if not HasNamedPtfxAssetLoaded(ptFxName) then
debugPrint("^6Bridge^7: ^2Loading Ptfx Dictionary^7: '^6"..ptFxName.."^7'")
while not HasNamedPtfxAssetLoaded(ptFxName) do RequestNamedPtfxAsset(ptFxName) Wait(5) end
end
end
--- Unloads a particle effects (ptfx) dictionary from memory.
---
--- This function removes the named particle effects asset from the game's memory cache.
---
---@param dict string The name of the particle effects dictionary to unload.
---
---@usage
--- ```lua
--- unloadPtfxDict('core')
--- ```
function unloadPtfxDict(dict)
debugPrint("^6Bridge^7: ^2Removing Ptfx Dictionary^7: '^6"..dict.."^7'")
RemoveNamedPtfxAsset(dict)
end
--- Loads a texture dictionary into memory.
---
--- This function requests the streamed texture dictionary and waits until it's loaded.
---
---@param dict string The name of the texture dictionary to load.
---
---@usage
--- ```lua
--- loadTextureDict('commonmenu')
--- ```
function loadTextureDict(dict)
if not HasStreamedTextureDictLoaded(dict) then
debugPrint("^6Bridge^7: ^2Loading Texture Dictionary^7: '^6"..dict.."^7'")
while not HasStreamedTextureDictLoaded(dict) do RequestStreamedTextureDict(dict) Wait(5) end
end
end
--- Loads a script audio bank into memory.
---
--- This function requests a script audio bank and waits until it's loaded or times out.
---
---@param bank string The name of the script audio bank to load.
---
---@return boolean `true` if the audio bank was successfully loaded; otherwise, `false`.
---
---@usage
--- ```lua
--- local success = loadScriptBank('DLC_HEISTS_GENERAL_FRONTEND_SOUNDS')
--- ```
function loadScriptBank(bank)
local timeout = 2000
debugPrint("^6Debug^7: ^2Loading ^3Script ^2AudioBank^7...")
while not RequestScriptAudioBank(bank, 0) do Wait(10) timeout -= 10 if timeout <= 0 then break end end
local success = RequestScriptAudioBank(bank, 0)
debugPrint("^6Debug^7: "..(success and "^3Successfully ^2loaded^7: '^4" or "^1Failed to ^2load^7: '^4")..bank.."^7'")
return success
end
--- Loads an ambient audio bank into memory.
---
--- This function requests an ambient audio bank and waits until it's loaded or times out.
---
---@param bank string The name of the ambient audio bank to load.
---
---@return boolean `true` if the audio bank was successfully loaded; otherwise, `false`.
---
---@usage
--- ```lua
--- local success = loadAmbientBank('AMB_REVERB_GENERIC')
--- ```
function loadAmbientBank(bank)
local timeout = 2000
debugPrint("^6Debug^7: ^2Loading ^3Ambient ^2AudioBank^7...")
while not RequestAmbientAudioBank(bank, 0) do
Wait(10)
timeout -= 10
if timeout <= 0 then break end
end
local success = RequestAmbientAudioBank(bank, 0)
debugPrint("^6Debug^7: "..(success and "^3Successfully ^2loaded^7: '^4" or "^1Failed to ^2load^7: '^4")..bank.."^7'")
return success
end
--- Plays an animation on a specified ped.
---
--- This function loads the animation dictionary and instructs the ped to play the animation.
---
---@param animDict string The name of the animation dictionary.
---@param animName string The name of the animation within the dictionary.
---@param duration number (optional) The duration to play the animation in milliseconds. Default is `30000`.
---@param flag number (optional) The animation flag controlling how the animation is played. Default is `50`.
---@param ped number (optional) The ped on which to play the animation. Defaults to the player's ped if not specified.
---@param speed number (optional) The speed multiplier for the animation. Default is `8.0`.
---
---@usage
--- ```lua
--- playAnim('amb@world_human_hang_out_street@male_c@base', 'base', 5000, 1, PlayerPedId(), 1.0)
--- ```
function playAnim(animDict, animName, duration, flag, ped, speed)
loadAnimDict(animDict)
debugPrint("Attempting to make player play anim", animDict, animName)
TaskPlayAnim(ped and ped or PlayerPedId(), animDict, animName, speed or 8.0, speed or -8.0, duration or 30000, flag or 50, 1, false, false, false)
end
--- Stops a specified animation on a ped.
---
--- This function stops the animation and unloads the animation dictionary from memory.
---
---@param animDict string The name of the animation dictionary.
---@param animName string The name of the animation within the dictionary.
---@param ped number (optional) The ped on which to stop the animation. Defaults to the player's ped if not specified.
---
---@usage
--- ```lua
--- stopAnim('amb@world_human_hang_out_street@male_c@base', 'base', PlayerPedId())
--- ```
function stopAnim(animDict, animName, ped)
debugPrint("Stopping anim for "..(ped or PlayerPedId()))
StopAnimTask(ped or PlayerPedId(), animDict, animName, 0.5)
StopAnimTask(ped or PlayerPedId(), animName, animDict, 0.5)
unloadAnimDict(animDict)
end
--- Plays a game sound from a specified coordinate or entity.
---
--- This function attempts to play a sound from either a coordinate or an entity, using the specified audio bank and sound name.
---
---@param bank string The name of the audio bank containing the sound.
---@param sound string The name of the sound to play.
---@param coords vector3|number A `vector3` coordinate or an entity handle from which to play the sound.
---@param synced boolean A boolean indicating whether the sound is synced across clients.
---@param range number (optional) The maximum range at which the sound can be heard. Default is `10.0`.
---
---@usage
--- ```lua
--- playGameSound('DLC_HEIST_HACKING_SNAKE_SOUNDS', 'Beep', vector3(0, 0, 0), false, 15.0)
--- ```
function playGameSound(bank, sound, coords, synced, range)
debugPrint("^6Debug^7: ^2Attempting to play: ^3"..sound.." ^7(^4"..bank.."^7')")
local range = range or 10.0
local soundId = GetSoundId()
while not soundId do Wait(10) end
if type(coords) == "vector3" or type(coords) == "vector4" then
debugPrint("^6Debug^7: ^2Playing sound from Coord^7: "..formatCoord(coords.xyz))
PlaySoundFromCoord(soundId, sound, coords.x, coords.y, coords.z, bank, synced, range, 0)
else
debugPrint("^6Debug^7: ^2Playing sound from Entity^7: ^4"..coords.."^7")
PlaySoundFromEntity(soundId, sound, coords, bank, synced, 0)
end
end

119
shared/make/makeBlip.lua Normal file
View File

@@ -0,0 +1,119 @@
--- Creates a blip at specified coordinates with given properties.
--
-- This function adds a map blip at the provided coordinates and sets various display properties such as sprite, color, scale, and more.
-- It also handles attaching a preview image to the blip if certain resources are running and a preview is provided.
--
---@param data A table containing blip data and properties.
-- - **coords**: A `vector3` containing x, y, z coordinates where the blip will be placed.
-- - **sprite** (optional): The blip sprite/icon ID. Default is `106`.
-- - **col** (optional): The color ID of the blip. Default is `5`.
-- - **scale** (optional): The scale of the blip. Default is `0.7`.
-- - **disp** (optional): The display option of the blip. Default is `6`.
-- - **category** (optional): The category ID for the blip.
-- - **name**: The name of the blip, used for display on the map.
-- - **preview** (optional): A URL or image path for a preview image to display with the blip.
--
---@return blip blipID The handle of the created blip.
--
---@usage
-- ```lua
-- local blipData = {
-- coords = vector3(123.4, 567.8, 90.1),
-- sprite = 1,
-- col = 2,
-- scale = 0.8,
-- disp = 4,
-- category = 7,
-- name = "My Blip",
-- preview = "http://example.com/preview.png"
-- }
-- local blip = makeBlip(blipData)
-- ```
function makeBlip(data)
local blip = AddBlipForCoord(vec3(data.coords.x, data.coords.y, data.coords.z))
SetBlipCoords(blip, data.coords.x, data.coords.y, data.coords.z) -- Manually set blip coordinates again as sometimes it just refuses
SetBlipAsShortRange(blip, true)
SetBlipSprite(blip, data.sprite or 106)
SetBlipColour(blip, data.col or 5)
SetBlipScale(blip, data.scale or 0.7)
SetBlipDisplay(blip, data.disp or 6)
if data.category then SetBlipCategory(blip, data.category) end
BeginTextCommandSetBlipName('STRING')
AddTextComponentString(tostring(data.name))
EndTextCommandSetBlipName(blip)
-- Handle preview image if certain resources are running
if isStarted("fs_smallresources") or isStarted("blip_info") or isStarted("blipinfo") then
if data.preview then
local txname = string.gsub(tostring(data.name..'preview'..string.gsub(data.coords.z, "%.", "")), "[ ()~]", "")
if data.preview:find("http") or data.preview:find("nui") then
createDui(txname, data.preview, vec2(512, 256), scriptTxd)
else
CreateRuntimeTextureFromImage(scriptTxd, txname, data.preview)
end
exports["fs_smallresources"]:SetBlipInfoImage(blip, getScript()..'scriptTxd', txname)
exports["fs_smallresources"]:SetBlipInfoTitle(blip, data.name, false)
end
end
debugPrint("^6Bridge^7: ^6Blip ^2created^7: '^6"..data.name.."^7' - '"..formatCoord(data.coords).."'")
return blip
end
--- Creates a blip attached to a specified entity with given properties.
--
-- This function adds a map blip attached to the provided entity and sets various display properties such as sprite, color, scale, and more.
-- It also handles attaching a preview image to the blip if certain resources are running and a preview is provided.
--
---@param data table A table containing blip data and properties.
-- - **entity**: The entity to which the blip will be attached.
-- - **sprite** (optional): The blip sprite/icon ID. Default is `106`.
-- - **col** (optional): The color ID of the blip. Default is `5`.
-- - **scale** (optional): The scale of the blip. Default is `0.7`.
-- - **disp** (optional): The display option of the blip. Default is `6`.
-- - **category** (optional): The category ID for the blip.
-- - **name**: The name of the blip, used for display on the map.
-- - **preview** (optional): A URL or image path for a preview image to display with the blip.
--
--
---@return number blipID The handle of the created blip.
---@usage
-- ```lua
-- local blipData = {
-- entity = myEntity,
-- sprite = 1,
-- col = 2,
-- scale = 0.8,
-- disp = 4,
-- category = 7,
-- name = "Entity Blip",
-- preview = "http://example.com/preview.png"
-- }
-- local blip = makeEntityBlip(blipData)
-- ```
function makeEntityBlip(data)
AddBlipForEntity(data.entity)
local blip = GetBlipFromEntity(data.entity)
SetBlipAsShortRange(blip, true)
SetBlipSprite(blip, data.sprite or 106)
SetBlipColour(blip, data.col or 5)
SetBlipScale(blip, data.scale or 0.7)
SetBlipDisplay(blip, data.disp or 6)
if data.category then SetBlipCategory(blip, data.category) end
BeginTextCommandSetBlipName('STRING')
AddTextComponentString(tostring(data.name))
EndTextCommandSetBlipName(blip)
-- Handle preview image if certain resources are running
if isStarted("fs_smallresources") or isStarted("blip-info") or isStarted("blipinfo") then
if data.preview then
local txname = string.gsub(tostring(data.name..'preview'), "[ ()~]", "")
if data.preview:find("http") or data.preview:find("nui") then
createDui(txname, data.preview, vec2(512, 256), scriptTxd)
else
CreateRuntimeTextureFromImage(scriptTxd, txname, data.preview)
end
exports["fs_smallresources"]:SetBlipInfoImage(blip, getScript()..'previewTxd', txname)
exports["fs_smallresources"]:SetBlipInfoTitle(blip, data.name, false)
end
end
debugPrint("^6Bridge^7: ^6Blip ^2created for Entity^7: '^6"..data.name.."^7'")
return blip
end

230
shared/make/makePed.lua Normal file
View File

@@ -0,0 +1,230 @@
--- A table to keep track of all created Peds.
local Peds = {}
--- Creates a distance-based Ped (pedestrian) that spawns when the player enters a specified area.
--
-- This function sets up a circular area using `createCirclePoly`. When the player enters this area, a Ped is created using `makePed`.
-- When the player exits the area, the Ped is deleted.
--
---@param data table A table containing Ped data and properties. Should include at least `model` and `coords`.
---@param coords vector4 A `vector3` or `vector4` specifying the coordinates where the Ped will be placed.
---@param freeze boolean (optional) Boolean indicating whether the Ped should be frozen in place. Default is `true`.
---@param collision boolean (optional) Boolean indicating whether collision with the Ped is enabled. Default is `false`.
---@param scenario boolean (optional) String specifying the scenario the Ped should perform.
---@param anim table (optional) A table containing animation dictionary and name `{animDict, animName}`.
---@param synced boolean (optional) Boolean indicating whether the Ped is synced across clients. Default is `false`.
--
---@usage
-- ```lua
-- makeDistPed(pedData, pedCoords, true, false, 'WORLD_HUMAN_STAND_IMPATIENT', nil, true)
-- ```
function makeDistPed(data, coords, freeze, collision, scenario, anim, synced)
createCirclePoly({
name = keyGen()..keyGen(),
coords = vec3(data.coords.x, data.coords.y, data.coords.z - 1.03),
radius = 50.0,
onEnter = function()
Peds[#Peds + 1] = makePed(data, coords, freeze, collision, scenario, anim, synced)
end,
onExit = function()
DeletePed(Peds[#Peds])
end,
debug = debugMode,
})
end
--- Creates a Ped (pedestrian character) with specified properties.
--
-- This function creates a Ped at the given coordinates and applies appearance and clothing based on the provided data.
--
-- If `data` is a table with `custom` properties, it customizes the Ped's appearance accordingly.
--
---@param data modelHash|table Either a string/model hash of the Ped model to use, or a table containing `model` and `custom` data.
---@param coords vector4 `vector3` or `vector4` specifying the coordinates where the Ped will be placed.
---@param freeze boolean (optional) Boolean indicating whether the Ped should be frozen in place. Default is `true`.
---@param collision boolean (optional) Boolean indicating whether collision with the Ped is enabled. Default is `false`.
---@param scenario string (optional) String specifying the scenario the Ped should perform.
---@param anim table (optional) A table containing animation dictionary and name `{animDict, animName}`.
---@param synced boolean (optional) Boolean indicating whether the Ped is synced across clients. Default is `false`.
--
---@return ped entityID The handle of the created Ped.
--
---@usage
-- ```lua
-- local ped = makePed(pedData, pedCoords, true, false, nil, {'animDict', 'animName'}, true)
-- ```
function makePed(data, coords, freeze, collision, scenario, anim, synced)
local ped = nil
local model = nil
if type(data) == "table" then
model = data.model
loadModel(data.model)
ped = CreatePed(0, model, coords.x, coords.y, coords.z - 1.03, coords.w, synced and synced or false, false)
-- Inheritance
SetPedHeadBlendData(ped, data.custom.faceFather, data.custom.faceMother, data.custom.raceShape, data.custom.skinFather, data.custom.skinMother, data.custom.raceSkin, data.custom.faceMix or 0, data.custom.skinMix or 0, data.custom.raceMix or 0, false)
-- Face Features
for k, v in pairs({
"noseWidth", "noseHeight", "noseSize", "noseBoneHeight", "nosePeakHeight", "noseBoneTwist",
"eyebrowHeight", "eyebrowDepth",
"cheekBoneHeight", "cheekBoneWidth", "cheeckWidth",
"eyeOpening", "lipThickness",
"jawWidth", "jawSize",
"chinLowering", "chinLength", "chinSize", "chinHole",
"neckThickness"
}) do
SetPedFaceFeature(ped, k - 1, data.custom[v])
end
-- Appearance
SetPedComponentVariation(ped, 2, data.custom.Hair, 0, 0)
SetPedHairColor(ped, data.custom.HairTexture, data.custom.HairHighlight or 0)
SetPedHeadOverlay(ped, 2, data.custom.Eyebrows, data.custom.EyebrowsOpacity)
SetPedHeadOverlayColor(ped, 2, 1, data.custom.EyebrowsColor, 0)
SetPedEyeColor(ped, data.custom.Eyecolor)
SetPedHeadOverlay(ped, 4, data.custom.Makeup, data.custom.MakeupOpacity)
SetPedHeadOverlayColor(ped, 4, 1, data.custom.MakeupColor, 0)
SetPedHeadOverlay(ped, 8, data.custom.Lipstick, data.custom.LipstickOpacity)
SetPedHeadOverlayColor(ped, 8, 1, data.custom.LipstickColor, 0)
SetPedHeadOverlay(ped, 1, data.custom.Beard, data.custom.BeardOpacity)
SetPedHeadOverlayColor(ped, 1, 1, data.custom.BeardColor, 0)
-- Clothes
SetPedComponentVariation(ped, 1, data.custom.Mask, data.custom.MaskVariant, 0)
SetPedComponentVariation(ped, 7, data.custom.Scarf, data.custom.ScarfVariant, 0)
SetPedComponentVariation(ped, 11, data.custom.Jacket, data.custom.JacketVariant, 0)
SetPedComponentVariation(ped, 8, data.custom.Shirt, data.custom.ShirtVariant, 0)
SetPedComponentVariation(ped, 9, data.custom.Vest, data.custom.VestVariant, 0)
SetPedComponentVariation(ped, 5, data.custom.Bags, data.custom.BagsVariant, 0)
SetPedComponentVariation(ped, 3, data.custom.Arms, data.custom.ArmsVariant, 0)
SetPedComponentVariation(ped, 4, data.custom.Pants, data.custom.PantsVariant, 0)
SetPedComponentVariation(ped, 6, data.custom.Shoes, data.custom.ShoesVariant, 0)
SetPedComponentVariation(ped, 10, data.custom.Decal, data.custom.DecalVariant, 0)
-- Accessories
SetPedPropIndex(ped, 0, data.custom.Hat, data.custom.HatVariant, true)
SetPedPropIndex(ped, 1, data.custom.Glasses, data.custom.GlassesVariant, true)
SetPedPropIndex(ped, 2, data.custom.Ear, data.custom.EarVariant, true)
SetPedPropIndex(ped, 6, data.custom.Watches, data.custom.WatchesVariant, true)
SetPedPropIndex(ped, 7, data.custom.Bracelets, data.custom.BraceletsVariant, true)
else
model = data
loadModel(model)
ped = CreatePed(0, model, coords.x, coords.y, coords.z - 1.03, coords.w, synced or false, false)
end
SetEntityInvincible(ped, true)
SetBlockingOfNonTemporaryEvents(ped, true)
FreezeEntityPosition(ped, freeze and freeze or true)
if collision then SetEntityNoCollisionEntity(ped, PlayerPedId(), false) end
if scenario then TaskStartScenarioInPlace(ped, scenario, 0, true) end
if anim then
loadAnimDict(anim[1])
TaskPlayAnim(ped, anim[1], anim[2], 0.5, 1.0, -1, 1, 0.2, 0, 0, 0)
end
debugPrint("^6Bridge^7: ^1Ped ^2Created^7: '^6"..ped.."^7' | ^2Hash^7: ^7'^5"..(model).."^7' | ^2Coord^7: "..formatCoord(coords))
unloadModel(model)
Peds[#Peds + 1] = ped
return ped
end
--- Generates random Ped data by filling in missing customization options with random values.
--
-- This function takes in a data table that may have some customization options missing in `data.custom`.
--
-- It generates random values for any missing options and returns a new data table with complete customization.
--
---@param data table A table containing at least a `model` field, and possibly a `custom` table with customization options.
--
---@return generatedTable table A new table containing `model` and `custom` with all customization options filled.
--
---@usage
-- ```lua
-- local pedData = GenerateRandomPedData({ model = `MP_M_Freemode_01`, custom = {} })
-- ```
function GenerateRandomPedData(data)
local newTable = {
model = data.model,
custom = {},
}
local isMale = data.model == `MP_M_Freemode_01`
local randomTable = {
-- Inheritance
faceFather = math.random(0, 45), faceMother = math.random(0, 45), faceMix = (math.random(0, 9) / 10),
skinFather = math.random(0, 45), skinMother = math.random(0, 45), skinMix = (math.random(0, 9) / 10),
raceShape = math.random(0, 45), raceSkin = math.random(0, 45), raceMix = (math.random(0, 9) / 10),
-- Face Features
noseWidth = (math.random(0, 9) / 10),
noseHeight = (math.random(0, 9) / 10),
noseSize = (math.random(0, 9) / 10),
noseBoneHeight = (math.random(0, 9) / 10),
nosePeakHeight = (math.random(0, 9) / 10),
noseBoneTwist = (math.random(0, 9) / 10),
eyebrowHeight = (math.random(0, 9) / 10),
eyebrowDepth = (math.random(0, 9) / 10),
cheekBoneHeight = (math.random(0, 9) / 10),
cheekBoneWidth = (math.random(0, 9) / 10),
cheeckWidth = (math.random(0, 9) / 10),
eyeOpening = (math.random(0, 9) / 10),
lipThickness = (math.random(0, 9) / 10),
jawWidth = (math.random(0, 9) / 10),
jawSize = (math.random(0, 9) / 10),
chinLowering = (math.random(0, 9) / 10),
chinLength = (math.random(0, 9) / 10),
chinSize = (math.random(0, 9) / 10),
chinHole = (math.random(0, 9) / 10),
neckThickness = (math.random(0, 9) / 10),
-- Appearance
Hair = math.random(0, isMale and 147 or 261), HairTexture = math.random(0, 63), HairHighlight = math.random(0, 63),
Eyebrows = math.random(0, 33),
EyebrowsOpacity = 0.9, EyebrowsColor = 0,
Eyecolor = math.random(0, 30),
Makeup = 0, MakeupOpacity = 0, MakeupColor = 0,
Lipstick = 0, LipstickOpacity = 0, LipstickColor = 0,
Beard = isMale and math.random(0, 28) or -1,
BeardOpacity = isMale and 0.9 or 0.0, BeardColor = 0,
-- Clothing
Mask = math.random(0, 252), MaskVariant = 0,
Scarf = math.random(0, isMale and 249 or 198), ScarfVariant = 0,
Jacket = math.random(0, isMale and 634 or 713), JacketVariant = 0,
Shirt = math.random(0, isMale and 237 or 299), ShirtVariant = 0,
Vest = math.random(0, isMale and 81 or 91), VestVariant = 0,
Bags = math.random(0, isMale and 138 or 148), BagsVariant = 0,
Arms = math.random(0, isMale and 224 or 261), ArmsVariant = 0,
Pants = math.random(0, isMale and 255 or 275), PantsVariant = 0,
Shoes = math.random(0, isMale and 157 or 199), ShoesVariant = 0,
Decal = math.random(0, isMale and 238 or 253), DecalVariant = 0,
-- Accessories
Hat = math.random(0, isMale and 232 or 229), HatVariant = 0,
Glasses = math.random(0, isMale and 68 or 71), GlassesVariant = 0,
Ear = math.random(0, isMale and 51 or 40), EarVariant = 0,
Watches = math.random(0, isMale and 46 or 35), WatchesVariant = 0,
Bracelets = math.random(0, isMale and 13 or 20), BraceletsVariant = 0,
}
for option in pairs(randomTable) do
if not data.custom[option] then
newTable.custom[option] = randomTable[option]
debugPrint("^6Bridge^7: ^2Picking Random Ped option ^7[^5"..option.."^7]: ^6"..newTable.custom[option].."^7")
else
newTable.custom[option] = data.custom[option]
end
end
return newTable
end
--- Cleans up all created Peds when the resource stops.
onResourceStop(function() for i = 1, #Peds do DeletePed(Peds[i]) end end, true)

90
shared/make/makeProp.lua Normal file
View File

@@ -0,0 +1,90 @@
local Props = {}
--- Creates a prop (object) in the game world at specified coordinates.
---
--- This function loads the model, creates the object, sets its heading, and freezes it if specified.
---
---@param data table A table containing prop data.
--- - **prop** `string`: The model name or hash of the prop to create.
--- - **coords** `vector4`: The coordinates where the prop will be placed. Should include x, y, z, and w (heading).
---@param freeze boolean (optional) Whether to freeze the prop in place. Defaults to `false`.
---@param synced boolean (optional) Whether the prop should be synced across clients. Defaults to `false`.
---
---@return number entityID The handle of the created prop object.
---
---@usage
--- ```lua
--- local propData = {
--- prop = 'prop_chair_01a',
--- coords = vector4(123.4, 567.8, 90.1, 180.0)
--- }
--- local prop = makeProp(propData, true, false)
--- ```
function makeProp(data, freeze, synced)
loadModel(data.prop)
local prop = CreateObject(data.prop, data.coords.x, data.coords.y, data.coords.z-1.03, synced and synced or false, synced and synced or false, false)
SetEntityHeading(prop, data.coords.w + 180.0)
FreezeEntityPosition(prop, freeze or false)
debugPrint("^6Bridge^7: ^1Prop ^2Created^7: '^6"..prop.."^7' | ^2Hash^7: ^7'^6"..data.prop.."^7' | ^2Coord^7: "..formatCoord(data.coords))
SetModelAsNoLongerNeeded(data.prop)
Props[#Props + 1] = prop
return prop
end
--- Creates a prop that appears when the player is within a certain distance.
---
--- This function sets up a proximity area, and when the player enters it, the prop is created.
--- When the player exits the area, the prop is destroyed.
---
---@param data table A table containing prop data.
--- - **prop** `string`: The model name or hash of the prop to create.
--- - **coords** `vector4`: The coordinates where the prop will be placed. Should include x, y, z, and w (heading).
---@param freeze boolean (optional) Whether to freeze the prop in place. Defaults to `false`.
---@param synced boolean (optional) Whether the prop should be synced across clients. Defaults to `false`.
---
---@usage
--- ```lua
--- local propData = {
--- prop = 'prop_chair_01a',
--- coords = vector4(123.4, 567.8, 90.1, 180.0)
--- }
--- makeDistProp(propData, true, false)
--- ```
function makeDistProp(data, freeze, synced)
local prop = nil
createCirclePoly({
name = keyGen()..keyGen(),
coords = vec3(data.coords.x, data.coords.y, data.coords.z - 1.03),
radius = 50.0,
onEnter = function()
prop = makeProp(data, freeze, synced)
end,
onExit = function()
destroyProp(prop)
end,
debug = debugMode,
})
end
--- Destroys a prop, detaching it if attached to the player beforehand.
---
---@param entity number The handle of the prop entity to destroy.
---
---@usage
--- ```lua
--- destroyProp(prop)
--- ```
function destroyProp(entity)
if entity then
debugPrint("^6Bridge^7: ^2Destroying Prop^7: '^6"..entity.."^7'")
if IsEntityAttachedToEntity(entity, PlayerPedId()) then
SetEntityAsMissionEntity(entity)
DetachEntity(entity, true, true)
end
DeleteObject(entity)
end
end
--- Cleans up all created props when the resource stops.
onResourceStop(function() for i = 1, #Props do destroyProp(Props[i]) end end, true)

73
shared/make/makeVeh.lua Normal file
View File

@@ -0,0 +1,73 @@
local Vehicles = {}
--- Creates a vehicle with the specified model and coordinates.
---
--- This function loads the vehicle model, creates the vehicle in the world at the given coordinates, sets initial properties, and returns the vehicle handle.
---
---@param model string|number The model name or hash of the vehicle to create.
---@param coords vector4 The coordinates where the vehicle will be placed, including x, y, z, and w (heading).
---
---@return number entityID The handle of the created vehicle.
---
---@usage
--- ```lua
--- local vehicle = makeVeh('adder', vector4(123.4, 567.8, 90.1, 180.0))
--- ```
function makeVeh(model, coords)
loadModel(model)
local veh = CreateVehicle(model, coords.x, coords.y, coords.z, coords.w, true, false)
SetVehicleHasBeenOwnedByPlayer(veh, true)
SetNetworkIdCanMigrate(NetworkGetNetworkIdFromEntity(veh), true)
Wait(100)
SetVehicleNeedsToBeHotwired(veh, false)
SetVehRadioStation(veh, 'OFF')
SetVehicleFuelLevel(veh, 100.0)
SetVehicleModKit(veh, 0)
SetVehicleOnGroundProperly(veh)
debugPrint("^6Bridge^7: ^1Veh ^2Created^7: '^6"..veh.."^7' | ^2Hash^7: ^7'^6"..model.."^7' | ^2Coord^7: "..formatCoord(coords))
unloadModel(model)
Vehicles[#Vehicles + 1] = veh
return veh
end
--- Attempts to gain network control of a vehicle and set it as a mission entity.
---
--- This function forces synchronization of a vehicle with other players by requesting network control and setting the vehicle as a mission entity.
---
---@param entity number The handle of the vehicle entity to push.
---
---@usage
--- ```lua
--- pushVehicle(vehicle)
--- ```
function pushVehicle(entity)
SetVehicleModKit(entity, 0)
if entity ~= 0 and DoesEntityExist(entity) then
if not NetworkHasControlOfEntity(entity) then
debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Requesting network control of vehicle^7.")
NetworkRequestControlOfEntity(entity)
local timeout = 2000
while timeout > 0 and not NetworkHasControlOfEntity(entity) do
Wait(100)
timeout -= 100
end
if NetworkHasControlOfEntity(entity) then debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Network has control of entity^7.") end
end
if not IsEntityAMissionEntity(entity) then
debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Setting vehicle as a ^7'^2mission^7' ^2entity^7.")
SetEntityAsMissionEntity(entity, true, true)
local timeout = 2000
while timeout > 0 and not IsEntityAMissionEntity(entity) do
Wait(100)
timeout -= 100
end
if IsEntityAMissionEntity(entity) then debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Vehicle is a ^7'^2mission^7'^2 entity^7.") end
end
end
end
--- Cleans up all created vehicles when the resource stops.
onResourceStop(function(r)
for i = 1, #Vehicles do DeleteVehicle(Vehicles[i]) end
end)

View File

@@ -0,0 +1,209 @@
local inProgress = false
--- Displays a progress bar using the configured progress bar system.
---
--- This function handles displaying a progress bar to the player using the specified progress bar system (e.g., ox, qb, esx, gta).
--- It supports shared progress bars between players, animations, camera effects, and more.
---
---@param data table A table containing the progress bar configuration.
--- - **label** (`string`): The text label to display on the progress bar.
--- - **time** (`number`): The duration of the progress bar in milliseconds.
--- - **dict** (`string`, optional): The animation dictionary to use.
--- - **anim** (`string`, optional): The animation name to play.
--- - **task** (`string`, optional): The task scenario to perform.
--- - **flag** (`number`, optional): The animation flag.
--- - **dead** (`boolean`, optional): Whether to allow the progress bar when the player is dead. Default is `false`.
--- - **cancel** (`boolean`, optional): Whether the progress bar can be canceled by the player. Default is `true`.
--- - **icon** (`string`, optional): The icon to display (for qb progress bar).
--- - **cam** (`number`, optional): The camera handle to use.
--- - **shared** (`table`, optional): Data for shared progress bars.
--- - **pid** (`number`): The player ID to share the progress bar with.
--- - **label** (`string`): The label to display on the shared progress bar.
---
--- @return boolean `true` if the progress bar completed successfully, or `false` if it was canceled.
---
---@usage
--- ```lua
--- local success = progressBar({
--- label = "Processing...",
--- time = 5000,
--- dict = "amb@world_human_hang_out_street@female_hold_arm@base",
--- anim = "base",
--- flag = 49,
--- cancel = true,
--- })
--- ```
function progressBar(data)
local ped = PlayerPedId()
if data.shared then
debugPrint("^6Bridge^7: ^6Sharing progressBar to player^7: ^6"..data.shared.pid.."^7")
storedPID = data.shared.pid
TriggerServerEvent(getScript()..":server:sharedProg:Start", data)
end
local result = nil
if data.cam then startTempCam(data.cam) end
if Config.System.ProgressBar == "ox" then
if exports[OXLibExport]:progressBar({
duration = debugMode and 1000 or data.time,
label = data.label,
useWhileDead = data.dead or false,
canCancel = data.cancel and data.cancel or true,
anim = {
dict = data.dict,
clip = data.anim,
flag = (data.flag == 8 and 32 or data.flag) or nil,
scenario = data.task
},
disable = {
combat = true
},
}) then
result = true
else
result = false
end
elseif Config.System.ProgressBar == "qb" then
Core.Functions.Progressbar("progbar",
data.label,
debugMode and 1000 or data.time,
data.dead or false,
data.cancel or true,
{ disableMovement = true, disableCarMovement = true, disableMouse = false, disableCombat = true },
{ animDict = data.dict, anim = data.anim, flags = data.flag or 32, task = data.task }, {}, {},
function()
result = true
end, function()
result = false
end, data.icon)
elseif Config.System.ProgressBar == "esx" then
ESX.Progressbar(data.label, debugMode and 1000 or data.time, {
FreezePlayer = true,
animation = {
type = data.anim,
dict = data.dict,
scenario = data.task,
},
onFinish = function()
result = true
end,
onCancel = function()
result = false
end
})
elseif Config.System.ProgressBar == "gta" then
local wait = debugMode and 1000 or data.time
inProgress = true
if not (data.dead or false) then
lockInv(true)
displaySpinner(data.label)
if data.dict then
playAnim(data.dict, data.anim, -1, (data.flag == 8 and 32 or data.flag) or nil)
end
if data.task then
TaskStartScenarioInPlace(ped, data.task, -1, true)
end
while inProgress and wait > 0 do
wait -= 15
local waitTimer = 0
DisablePlayerFiring(ped, true)
DisableControlAction(0, 25, true) -- Disable aim
DisableControlAction(0, 21, true) -- Disable sprint
DisableControlAction(0, 30, true) -- Disable move left/right
DisableControlAction(0, 31, true) -- Disable move forward/back
DisableControlAction(0, 36, true) -- Disable stealth
if data.cam ~= nil then
DisableControlAction(0, 1, true) -- Disable look left/right
DisableControlAction(0, 2, true) -- Disable look up/down
DisableControlAction(0, 106, true) -- Disable vehicle mouse control
end
if data.cancel then
if IsControlJustReleased(0, 202) or IsControlJustReleased(0, 77) then -- Cancel key (Backspace or Delete)
inProgress = false
waitTimer = 1500
displaySpinner(Loc[Config.Lan].error["cancel"])
end
end
Wait(waitTimer)
end
inProgress = false
if data.dict then stopAnim(data.dict, data.anim, ped) end
ClearPedTasks(ped)
end
stopSpinner()
result = (wait <= 0)
end
while result == nil do Wait(10) end
-- Cleanup
FreezeEntityPosition(ped, false)
lockInv(false)
if data.cam then stopTempCam(data.cam) end
if result == false and data.shared then
debugPrint("^6Bridge^7: ^2Sending cancel to ^6"..storedPID.."^7")
TriggerServerEvent(getScript().."server:sharedProg:cancel", storedPID)
end
storedPID = nil
return result
end
--- Stops the current progress bar.
---
--- This function cancels the progress bar based on the configured progress bar system, handling any necessary cleanup.
function stopPropgressBar()
if Config.System.ProgressBar == "ox" then
exports[OXLibExport]:cancelProgress()
elseif Config.System.ProgressBar == "qb" then
TriggerEvent("progressbar:client:cancel")
elseif Config.System.ProgressBar == "gta" then
inProgress = false
BusyspinnerOff()
end
end
-- System to handle sending/sharing progress bars between players --
-- For example, healing someone --
local storedPID = nil
--- Server event handler for starting a shared progress bar.
--- This event is triggered when a player wants to start a progress bar on another player.
--- It adjusts the data to prevent loops and sends the data to the target client.
RegisterNetEvent(getScript()..":server:sharedProg:Start", function(data)
local pid = data.shared.pid -- Get player ID from the client
data.label = data.shared.label -- Set progress bar label to the shared label
data.cancel = false -- Make it so it can't be canceled
data.dead = true -- Allow progress bar even if player is dead
data.shared = nil -- Remove shared info to prevent loops
data.anim = nil -- Remove animation so players don't share it
debugPrint("^6Bridge^7: ^6"..source.." ^2is sending shared progressBar to player^7, ^6"..pid.."^7")
TriggerClientEvent(getScript()..":client:sharedProg:Start", pid, data)
end)
--- Client event handler for starting a shared progress bar.
--- This event is triggered when the server wants the client to start a shared progress bar.
RegisterNetEvent(getScript()..":client:sharedProg:Start", function(data)
debugPrint("^6Bridge^7: ^2You have been sent a progressBar^7")
progressBar(data)
end)
--- Server event handler for canceling a shared progress bar.
--- This event is triggered when a progress bar is canceled and the server needs to notify the other player.
RegisterNetEvent(getScript()..":server:sharedProg:Cancel", function(pid)
debugPrint("^6Bridge^7: ^2Sending cancel progressBar to ^6"..pid.."^7")
TriggerClientEvent(getScript()..":client:sharedProg:Cancel", pid)
end)
--- Client event handler for canceling a shared progress bar.
--- This event is triggered when the server wants the client to cancel a shared progress bar.
RegisterNetEvent(getScript()..":client:sharedProg:Cancel", function()
debugPrint("^6Bridge^7: ^2Receiving cancel progressBar^7")
stopPropgressBar()
end)
--- Cleans up when the resource stops.
--- This event handler ensures that any active spinners or progress bars are stopped when the resource is stopped.
onResourceStop(function() stopSpinner() end, true)

88
shared/notify.lua Normal file
View File

@@ -0,0 +1,88 @@
-- NOTIFICATIONS --
-- This function is widely used to display notifications to the player, can be used server side or client side --
--- Displays notifications to the player using the configured notification system.
---
--- This function supports multiple notification systems based on the `Config.System.Notify` setting.
--- It can be triggered from both client-side and server-side scripts. Depending on the configuration,
--- it utilizes different exports or events to display the notification.
---
---@param title string|nil The title of the notification. Optional, used by certain notification systems.
---@param message string The main message content of the notification.
---@param type string The type/category of the notification (e.g., "success", "error", "info").
---@param src number|nil Optional. The server ID of the player to send the notification to. If `nil`, the notification is sent to the caller.
---
---@usage
--- ```lua
--- -- Client-side usage without specifying a player (shows to the current player)
--- triggerNotify("Success", "You have completed the task!", "success")
---
--- -- Server-side usage specifying a player by their server ID
--- triggerNotify("Alert", "You have been warned for misconduct.", "error", playerId)
--- ```
function triggerNotify(title, message, type, src)
if Config.System.Notify == "okok" then
if not src then TriggerEvent('okokNotify:Alert', title, message, 6000, type)
else TriggerClientEvent('okokNotify:Alert', src, title, message, 6000, type) end
elseif Config.System.Notify == "qb" then
if not src then TriggerEvent("QBCore:Notify", message, type)
else TriggerClientEvent("QBCore:Notify", src, message, type) end
elseif Config.System.Notify == "ox" then
if not src then TriggerEvent('ox_lib:notify', {title = title, description = message, type = type or "success"})
else TriggerClientEvent('ox_lib:notify', src, { type = type or "success", title = title, description = message }) end
elseif Config.System.Notify == "gta" then
if not src then TriggerEvent(getScript()..":DisplayGTANotify", title, message)
else TriggerClientEvent(getScript()..":DisplayGTANotify", src, title, message) end
elseif Config.System.Notify == "esx" then
if not src then exports["esx_notify"]:Notify(type, 4000, message)
else TriggerClientEvent(getScript()..":DisplayESXNotify", src, type, title, message) end
end
end
--- Registers a server-side event to display ESX notifications to clients.
---
--- This event listens for `DisplayESXNotify` and triggers the ESX notification on the client side.
---
--- @param type string The type/category of the notification (e.g., "success", "error", "info").
--- @param title string The title of the notification.
--- @param text string The main message content of the notification.
---
--- @usage
--- ```lua
--- -- Server-side event trigger
--- TriggerClientEvent(getScript()..":DisplayESXNotify", playerId, "success", "Achievement Unlocked", "You have unlocked a new achievement!")
--- ```
RegisterNetEvent(getScript()..":DisplayESXNotify", function(type, title, text)
exports["esx_notify"]:Notify(type, 4000, text)
end)
--- Displays default GTA-style text notifications.
---
--- This event handles displaying text-based notifications using GTA's native functions.
--- It supports specific scenarios by assigning different icons based on the script name.
---
---@param title string The title or identifier for the notification, used to select the appropriate icon.
---@param text string The main message content of the notification.
---
---@usage
--- ```lua
--- -- Client-side event trigger
--- TriggerEvent(getScript()..":DisplayGTANotify", "taxiname", "Taxi service has arrived.")
--- ```
RegisterNetEvent(getScript()..":DisplayGTANotify", function(title, text)
local iconTable = {}
if getScript() == "jim-npcservice" then
iconTable = {
[Loc[Config.Lan].notify["taxiname"]] = "CHAR_TAXI",
[Loc[Config.Lan].notify["limoname"]] = "CHAR_CASINO",
[Loc[Config.Lan].notify["ambiname"]] = "CHAR_CALL911",
[Loc[Config.Lan].notify["pilotname"]] = "CHAR_DEFAULT",
[Loc[Config.Lan].notify["planename"]] = "CHAR_BOATSITE2",
[Loc[Config.Lan].notify["heliname"]] = "CHAR_BOATSITE2",
}
end
BeginTextCommandThefeedPost("STRING")
AddTextComponentSubstringKeyboardDisplay(text)
EndTextCommandThefeedPostMessagetext(iconTable[title] or "CHAR_DEFAULT", iconTable[title] or "CHAR_DEFAULT", true, 1, title, nil, text)
EndTextCommandThefeedPostTicker(true, false)
end)

601
shared/playerfunctions.lua Normal file
View File

@@ -0,0 +1,601 @@
--- Locks or unlocks the player's inventory.
---
--- This function freezes or unfreezes the player's position, sets the inventory busy state,
--- and toggles the ability to use the inventory and hotbar based on the `toggle` parameter.
---
--- @param toggle boolean `true` to lock the inventory, `false` to unlock.
---
--- @usage
--- ```lua
--- -- Lock the player's inventory
--- lockInv(true)
---
--- -- Unlock the player's inventory
--- lockInv(false)
--- ```
function lockInv(toggle)
FreezeEntityPosition(PlayerPedId(), toggle)
LocalPlayer.state:set("inv_busy", toggle, true)
TriggerEvent('inventory:client:busy:status', toggle)
TriggerEvent('canUseInventoryAndHotbar:toggle', not toggle)
end
--- Instantly turns an entity to face a specific location or another entity.
---
--- This function calculates the heading from the first entity to the second entity or coordinates
--- and sets the entity's heading immediately without any animation.
---
--- @param ent number|nil The Ped entity to turn. Defaults to the player's Ped (`PlayerPedId()`).
--- @param ent2 number|vector3|nil The target entity or coordinates to face. If a vector, it uses the coordinates.
---
--- @usage
--- ```lua
--- -- Make the player instantly face a specific location
--- instantLookEnt(nil, vector3(200.0, 300.0, 40.0))
---
--- -- Make one entity face another entity
--- instantLookEnt(ped1, ped2)
--- ```
function instantLookEnt(ent, ent2)
local ent = ent or PlayerPedId()
local p1 = GetEntityCoords(ent, true)
local p2 = type(ent2):find("vector") and ent2 or GetEntityCoords(ent2, true)
local dx = p2.x - p1.x
local dy = p2.y - p1.y
local heading = GetHeadingFromVector_2d(dx, dy)
debugPrint("^6Bridge^7: ^1Forced ^2Turning Player to^7: '^6"..formatCoord(p2).."^7'")
SetEntityHeading(ent, heading)
end
--- Makes the player Ped look towards a specific entity or coordinates with animation.
---
--- This function checks if the player is already facing the target. If not, it triggers a turning animation
--- to face the specified entity or coordinates.
---
--- @param entity number|vector3|vector4|nil The target entity or coordinates to look at.
---
--- @usage
--- ```lua
--- -- Make the player look at a specific location
--- lookEnt(vector3(200.0, 300.0, 40.0))
---
--- -- Make the player look at another entity
--- lookEnt(pedEntity)
--- ```
function lookEnt(entity)
local ped = PlayerPedId()
if entity then
if type(entity) == "vector3" or type(entity) == "vector4" then
if not IsPedHeadingTowardsPosition(ped, entity.xyz, 30.0) then
TaskTurnPedToFaceCoord(ped, entity.xyz, 1500)
debugPrint("^6Bridge^7: ^2Turning Player to^7: '^6"..formatCoord(entity).."^7'")
Wait(1500)
end
else
if DoesEntityExist(entity) then
local entCoords = GetEntityCoords(entity)
if not IsPedHeadingTowardsPosition(ped, entCoords, 30.0) then
TaskTurnPedToFaceCoord(ped, entCoords, 1500)
debugPrint("^6Bridge^7: ^2Turning Player to^7: '^6"..entity.."^7' - '"..formatCoord(entCoords).."^7'")
Wait(1500)
end
end
end
end
end
--- Server event handler for handling urinal usage.
---
--- This event decreases the player's thirst based on a random amount and updates their thirst level.
---
--- @usage
--- ```lua
--- -- Triggered when a player uses a urinal
--- TriggerServerEvent(getScript()..":server:Urinal")
--- ```
RegisterNetEvent(getScript()..":server:Urinal", function()
local src = source
local Player = getPlayer(src)
local thirstamt = math.random(10, 30)
local thirst = Player.thirst - thirstamt
setThirst(src, getPlayer(src).thirst - thirst)
end)
--- Server event handler for setting player needs.
---
--- This event updates the player's thirst or hunger based on the provided type and amount.
---
--- @event
--- @param type string The type of need to set ("thirst" or "hunger").
--- @param amount number The amount to set the need to.
---
--- @return void
---
--- @usage
--- ```lua
--- -- Set the player's thirst level
--- TriggerServerEvent(getScript()..":server:setNeed", "thirst", 50)
---
--- -- Set the player's hunger level
--- TriggerServerEvent(getScript()..":server:setNeed", "hunger", 75)
--- ```
RegisterNetEvent(getScript()..":server:setNeed", function(type, amount)
local src = source
if type == "thirst" then
setThirst(src, amount)
elseif type == "hunger" then
setHunger(src, amount)
end
end)
--- Sets the player's thirst level.
---
--- This function updates the player's thirst based on the active inventory system.
---
--- @param src number The server ID of the player.
--- @param thirst number The new thirst level to set.
---
--- @usage
--- ```lua
--- -- Set a player's thirst to 80
--- setThirst(playerId, 80)
--- ```
function setThirst(src, thirst)
if isStarted(ESXExport) then
TriggerClientEvent('esx_status:add', src, 'thirst', thirst)
elseif isStarted(QBExport) or isStarted(QBXExport) then
local Player = Core.Functions.GetPlayer(src)
Player.Functions.SetMetaData('thirst', thirst)
TriggerClientEvent("hud:client:UpdateNeeds", src, thirst, Player.PlayerData.metadata.thirst)
end
end
--- Sets the player's hunger level.
---
--- This function updates the player's hunger based on the active inventory system.
---
--- @param src number The server ID of the player.
--- @param hunger number The new hunger level to set.
---
--- @usage
--- ```lua
--- -- Set a player's hunger to 60
--- setHunger(playerId, 60)
--- ```
function setHunger(src, hunger)
if isStarted(ESXExport) then
TriggerClientEvent('esx_status:add', src, 'hunger', hunger)
elseif isStarted(QBExport) or isStarted(QBXExport) then
local Player = Core.Functions.GetPlayer(src)
Player.Functions.SetMetaData('hunger', hunger)
TriggerClientEvent("hud:client:UpdateNeeds", src, hunger, Player.PlayerData.metadata.hunger)
end
end
--- Server event handler for charging a player.
---
--- This event removes money from a player based on the specified type ("cash" or "bank").
---
--- @event
--- @param cost number The amount of money to charge.
--- @param type string The type of money to charge ("cash" or "bank").
--- @param newsrc number|nil Optional. The server ID of the player. If `nil`, uses the event source.
---
--- @usage
--- ```lua
--- -- Charge a player $100 in cash
--- chargePlayer(100, "cash", playerId)
---
--- -- Charge the source $250 from the bank
--- chargePlayer(250, "bank", src,)
--- ```
function chargePlayer(cost, type, newsrc)
local src = newsrc or source
local fundResource = ""
if type == "cash" then
if isStarted(OXInv) then fundResource = OXInv
exports[OXInv]:RemoveItem(src, "money", cost)
elseif isStarted(QBExport) or isStarted(QBXExport) then fundResource = QBExport
Core.Functions.GetPlayer(src).Functions.RemoveMoney("cash", cost)
elseif isStarted(ESXExport) then fundResource = ESXExport
local Player = ESX.GetPlayerFromId(src)
Player.removeMoney(cost, "")
end
end
if type == "bank" then
if isStarted(QBExport) or isStarted(QBXExport) then fundResource = QBExport
Core.Functions.GetPlayer(src).Functions.RemoveMoney("bank", cost)
elseif isStarted(ESXExport) then fundResource = ESXExport
local Player = ESX.GetPlayerFromId(src)
Player.removeMoney(cost, "")
end
end
if fundResource == "" then print("error - check exports.lua")
else
debugPrint("^6Bridge^7: ^2Charging ^2Player^7: '^6"..cost.."^7'", type, fundResource)
end
end
RegisterNetEvent(getScript()..":server:ChargePlayer", chargePlayer)
--- Server event handler for funding a player.
---
--- This event adds money to a player based on the specified type ("cash" or "bank").
---
--- @event
--- @param fund number The amount of money to add.
--- @param type string The type of money to add ("cash" or "bank").
--- @param newsrc number|nil Optional. The server ID of the player. If `nil`, uses the event source.
---
--- @usage
--- ```lua
--- -- Add $150 to a player's cash
--- fundPlayer(playerId, 150, "cash")
---
--- -- Add $300 to the event source's bank account
--- fundPlayer(playerId, 300, "bank")
--- ```
function fundPlayer(fund, type, newsrc)
local src = newsrc or source
local fundResource = ""
if type == "cash" then
if isStarted(OXInv) then fundResource = OXInv
exports[OXInv]:AddItem(src, "money", fund)
elseif isStarted(QBExport) or isStarted(QBXExport) then fundResource = QBExport
Core.Functions.GetPlayer(src).Functions.AddMoney("cash", fund)
elseif isStarted(ESXExport) then fundResource = ESXExport
local Player = ESX.GetPlayerFromId(src)
Player.addMoney(fund, "")
end
end
if type == "bank" then
if isStarted(QBExport) or isStarted(QBXExport) then fundResource = QBExport
Core.Functions.GetPlayer(src).Functions.AddMoney("bank", fund)
elseif isStarted(ESXExport) then fundResource = ESXExport
local Player = ESX.GetPlayerFromId(src)
Player.addMoney(fund, "")
end
end
if fundResource == "" then print("error - check exports.lua")
else
debugPrint("^6Bridge^7: ^2Funding ^2Player^7: '^2"..fund.."^7'", type, fundResource)
end
end
RegisterNetEvent(getScript()..":server:FundPlayer", fundPlayer)
--- Handles successful consumption of an item.
---
--- This function plays a consumption animation, removes the item from the inventory,
--- updates the player's hunger and thirst based on the item consumed,
--- handles alcohol effects, and checks for random rewards.
---
--- @param itemName string The name of the item consumed.
--- @param type string The type/category of the item (e.g., "alcohol").
---
--- @usage
--- ```lua
--- -- Player consumes a health pack
--- ConsumeSuccess("health_pack", "health")
---
--- -- Player consumes an alcohol drink
--- ConsumeSuccess("beer", "alcohol")
--- ```
function ConsumeSuccess(itemName, type, data)
local hunger = data and data.hunger or Items[itemName].hunger or nil
local thirst = data and data.thirst or Items[itemName].thirst or nil
ExecuteCommand("e c")
removeItem(itemName, 1)
if isStarted(ESXExport) then
if hunger then
TriggerServerEvent(getScript()..":server:setNeed", "hunger", hunger * 10000)
end
if thirst then
TriggerServerEvent(getScript()..":server:setNeed", "thirst", thirst * 10000)
end
else
if hunger then
TriggerServerEvent(getScript()..":server:setNeed", "hunger", Core.Functions.GetPlayerData().metadata["hunger"] + hunger)
end
if thirst then
TriggerServerEvent(getScript()..":server:setNeed", "thirst", Core.Functions.GetPlayerData().metadata["thirst"] + thirst)
end
end
if type == "alcohol" then alcoholCount += 1
if alcoholCount > 1 and alcoholCount < 4 then
TriggerEvent("evidence:client:SetStatus", "alcohol", 200)
elseif alcoholCount >= 4 then
TriggerEvent("evidence:client:SetStatus", "heavyalcohol", 200)
AlienEffect()
end
end
getRandomReward(itemName) -- check if a reward item should be given
end
--- Checks if a player has a specific job and grade.
---
--- This function verifies whether the player has the specified job and, if a grade is provided,
--- whether the player's grade meets the required level. It supports multiple inventory systems.
---
--- @param job string The name of the job or gang to check.
--- @param source number|nil Optional. The server ID of the player to check. If `nil`, checks the current player.
--- @param grade number|nil Optional. The minimum grade level required.
---
--- @return boolean, boolean Returns `true` and `duty status` if the player has the job (and grade if specified), otherwise `false`.
---
--- @usage
--- ```lua
--- -- Check if the player has the 'police' job and is on duty
--- local hasPoliceJob, isOnDuty = hasJob("police")
--- if hasPoliceJob and isOnDuty then
--- -- Grant access to police-specific features
--- end
---
--- -- Check if a specific player has the 'gang_leader' job with at least grade 2
--- local hasGangLeaderJob, _ = hasJob("gang_leader", playerId, 2)
--- if hasGangLeaderJob then
--- -- Allow gang leader actions
--- end
--- ```
function hasJob(job, source, grade) local hasJob, duty = false, true
if source then
local src = tonumber(source)
if not src then print(tostring(source).." is not a valid player source") end
if isStarted(ESXExport) then
local info = ESX.GetPlayerFromId(src).job
while not info do
info = ESX.GetPlayerData(src).job
Wait(100)
end
if info.name == job then hasJob = true end
elseif isStarted(OXCoreExport) then
local chunk = assert(load(LoadResourceFile('ox_core', ('imports/%s.lua'):format('server')), ('@@ox_core/%s'):format(file)))
chunk()
local player = Ox.GetPlayer(tonumber(src))
for k, v in pairs(player.getGroups()) do
if k == job then hasJob = true end
end
elseif isStarted(QBXExport) then
local jobinfo = exports[QBXExport]:GetPlayer(src).PlayerData.job
if jobinfo.name == job then hasJob = true
duty = exports[QBXExport]:GetPlayer(src).PlayerData.job.onduty
if grade and not (grade <= jobinfo.grade.level) then hasJob = false end
end
local ganginfo = exports[QBXExport]:GetPlayer(src).PlayerData.gang
if ganginfo.name == job then hasJob = true
if grade and not (grade <= ganginfo.grade.level) then hasJob = false end
end
elseif isStarted(QBExport) and not isStarted(QBXExport) then
if Core.Functions.GetPlayer then -- support older qb-core functions
local player = Core.Functions.GetPlayer(src)
if not player then print("Player not found for src: "..src) end
local jobinfo = player.PlayerData.job
if jobinfo.name == job then hasJob = true
duty = Core.Functions.GetPlayer(src).PlayerData.job.onduty
if grade and not (grade <= jobinfo.grade.level) then hasJob = false end
end
local ganginfo = Core.Functions.GetPlayer(src).PlayerData.gang
if ganginfo.name == job then hasJob = true
if grade and not (grade <= ganginfo.grade.level) then hasJob = false end
end
else -- support newer qb-core exports
local jobinfo = exports[QBExport]:GetPlayer(src).PlayerData.job
if jobinfo.name == job then hasJob = true
duty = exports[QBExport]:GetPlayer(src).PlayerData.job.onduty
if grade and not (grade <= jobinfo.grade.level) then hasJob = false end
end
local ganginfo = exports[QBExport]:GetPlayer(src).PlayerData.gang
if ganginfo.name == job then hasJob = true
if grade and not (grade <= ganginfo.grade.level) then hasJob = false end
end
end
else
print("^4ERROR^7: ^2No Core detected for hasJob ^7- ^2Check ^3exports^1.^2lua^7")
end
else
if isStarted(ESXExport) then
while not ESX do Wait(10) end
local info = ESX.GetPlayerData().job
while not info do
info = ESX.GetPlayerData().job
Wait(100)
end
if info.name == job then hasJob = true end
elseif isStarted(OXCoreExport) then
for k, v in pairs(exports[OXCoreExport]:GetPlayerData().groups) do
if k == job then hasJob = true end break
end
elseif isStarted(QBXExport) then
local jobinfo = QBX.PlayerData.job
if jobinfo.name == job then hasJob = true
duty = QBX.PlayerData.job.onduty
if grade and not (grade <= jobinfo.grade.level) then hasJob = false end
end
local ganginfo = QBX.PlayerData.gang
if ganginfo.name == job then hasJob = true
if grade and not (grade <= ganginfo.grade.level) then hasJob = false end
end
elseif isStarted(QBExport) and not isStarted(QBXExport) then
local info = nil
Core.Functions.GetPlayerData(function(PlayerData)
info = PlayerData
end)
local jobinfo = info.job
if jobinfo.name == job then hasJob = true
duty = jobinfo.onduty
if grade and not (grade <= jobinfo.grade.level) then hasJob = false end
end
local ganginfo = info.gang
if ganginfo.name == job then
hasJob = true
if grade and not (grade <= ganginfo.grade.level) then hasJob = false end
end
else
print("^4ERROR^7: ^2No Core detected for hasJob() ^7- ^2Check ^3exports^1.^2lua^7")
end
end
return hasJob, duty
end
--- Retrieves basic information about a player.
---
--- This function gathers the player's name, cash balance, and bank balance
--- based on the active inventory system. It can be called server-side or client-side.
---
---@param source number|nil Optional. The server ID of the player. If `nil`, retrieves info for the current player.
---
---@return table table A table containing the player's `name`, `cash`, and `bank` balances.
---
---@usage
--- ```lua
--- -- Get information for a specific player
--- local playerInfo = getPlayer(playerId)
--- print(playerInfo.name, playerInfo.cash, playerInfo.bank)
---
--- -- Get information for the current player (client-side)
--- local myInfo = getPlayer()
--- print(myInfo.name, myInfo.cash, myInfo.bank)
--- ```
function getPlayer(source)
local Player = {}
debugPrint("^6Bridge^7: ^2Getting ^3Player^2 info^7")
if source then -- If called from server
local src = tonumber(source)
if isStarted(ESXExport) then
local info = ESX.GetPlayerFromId(src)
Player = {
name = info.getName(),
cash = info.getMoney(),
bank = info.getAccount("bank").money,
}
elseif isStarted(OXCoreExport) then
local file = ('imports/%s.lua'):format('server')
local import = LoadResourceFile('ox_core', file)
local chunk = assert(load(import, ('@@ox_core/%s'):format(file)))
chunk()
local player = Ox.GetPlayer(tonumber(src))
Player = {
name = ('%s %s'):format(player.firstName, player.lastName),
cash = exports[OXInv]:Search(src, 'count', "money"),
bank = 0,
}
elseif isStarted(QBXExport) then
local info = exports[QBXExport]:GetPlayer(src)
Player = {
name = info.PlayerData.charinfo.firstname.." "..info.PlayerData.charinfo.lastname,
cash = exports[OXInv]:Search(src, 'count', "money"),
bank = info.Functions.GetMoney("bank"),
}
elseif isStarted(QBExport) and not isStarted(QBXExport) then
if Core.Functions.GetPlayer ~= nil then -- support older qb-core functions
local info = Core.Functions.GetPlayer(src).PlayerData
Player = {
firstname = info.charinfo.firstname,
lastname = info.charinfo.lastname,
name = info.charinfo.firstname.." "..info.charinfo.lastname,
cash = info.money["cash"],
bank = info.money["bank"],
source = info.source,
job = info.job.name,
jobBoss = info.job.isboss,
gang = info.gang.name,
gangBoss = info.gang.isboss,
onDuty = info.job.onduty,
account = info.charinfo.account,
citizenId = info.citizenid,
}
else
local info = exports[QBExport]:GetPlayer(src).PlayerData -- this was added to new core then removed?
Player = {
firstname = info.charinfo.firstname,
lastname = info.charinfo.lastname,
name = info.charinfo.firstname.." "..info.charinfo.lastname,
cash = info.money["cash"],
bank = info.money["bank"],
source = info.source,
job = info.job.name,
jobBoss = info.job.isboss,
gang = info.gang.name,
gangBoss = info.gang.isboss,
onDuty = info.job.onduty,
account = info.charinfo.account,
citizenId = info.citizenid,
}
end
else
print("^4ERROR^7: ^2No Core detected for getPlayer() ^7- ^2Check ^3exports^1.^2lua^7")
end
else
if isStarted(ESXExport) and ESX ~= nil then
local info = ESX.GetPlayerData()
local cash, bank = 0, 0
for k, v in pairs(ESX.GetPlayerData().accounts) do
if v.name == "money" then cash = v.money end
if v.name == "bank" then bank = v.money end
end
Player = {
name = ('%s %s'):format(info.firstName, info.lastName),
cash = cash,
bank = bank,
}
elseif isStarted(OXCoreExport) then
local info = exports[OXCoreExport]:GetPlayerData()
Player = {
name = info.firstName.." "..info.lastName,
cash = exports[OXInv]:Search('count', "money"),
bank = 0,
}
elseif isStarted(QBXExport) then
local info = exports[QBXExport]:GetPlayerData()
Player = {
firstname = info.charinfo.firstname,
lastname = info.charinfo.lastname,
name = info.charinfo.firstname.." "..info.charinfo.lastname,
cash = info.money["cash"],
bank = info.money["bank"],
source = info.source,
job = info.job.name,
jobBoss = info.job.isboss,
gang = info.gang.name,
gangBoss = info.gang.isboss,
onDuty = info.job.onduty,
account = info.charinfo.account,
citizenId = info.citizenid,
}
elseif isStarted(QBExport) and not isStarted(QBXExport) then
local info = nil
Core.Functions.GetPlayerData(function(PlayerData) info = PlayerData end)
Player = {
firstname = info.charinfo.firstname,
lastname = info.charinfo.lastname,
name = info.charinfo.firstname.." "..info.charinfo.lastname,
cash = info.money["cash"],
bank = info.money["bank"],
source = info.source,
job = info.job.name,
jobBoss = info.job.isboss,
gang = info.gang.name,
gangBoss = info.gang.isboss,
onDuty = info.job.onduty,
account = info.charinfo.account,
citizenId = info.citizenid,
}
else
print("^4ERROR^7: ^2No Core detected for hasJob ^7- ^2Check ^3exports^1.^2lua^7")
end
end
return Player
end

116
shared/polyZone.lua Normal file
View File

@@ -0,0 +1,116 @@
-- This automatically detects what polyzone script it should use to create a polyzone --
-- if ox_lib is detected, it will automatically use that instead of PolyZone --
-- createPoly({ name = 'name', debug = true, points = { vec2(), vec2() }, onEnter = function() end, onExit = function() end, })
---
--- Creates a polygonal zone using the detected polyzone library (ox_lib or PolyZone).
---
--- This function automatically detects whether `ox_lib` or `PolyZone` is active and creates a polygonal zone accordingly.
--- It supports setting up entry and exit callbacks for the zone.
---
---@param data table A table containing the zone configuration.
--- - **name** (`string`): The name of the zone.
--- - **debug** (`boolean`): Whether to enable debug mode for the zone.
--- - **points** (`table`): A list of `vec2` points defining the polygon.
--- - **onEnter** (`function`): Callback function to execute when a player enters the zone.
--- - **onExit** (`function`): Callback function to execute when a player exits the zone.
---
---@return table|nil table Returns the created zone object or `nil` if creation failed.
---
---@usage
--- ```lua
--- createPoly({
--- name = 'testZone',
--- debug = true,
--- points = { vec2(100.0, 100.0), vec2(200.0, 100.0), vec2(200.0, 200.0), vec2(100.0, 200.0) },
--- onEnter = function() print("Entered Test Zone") end,
--- onExit = function() print("Exited Test Zone") end,
--- })
--- ```
function createPoly(data)
local Location = nil
if isStarted(OXLibExport) then -- if it finds ox_lib, use it instead of PolyZone
debugPrint("^6Bridge^7: ^2Creating new poly with ^7'^4"..OXLibExport.."^7': "..data.name)
for i = 1, #data.points do
data.points[i] = vec3(data.points[i].x, data.points[i].y, 12.0)
end
data.thickness = 1000
Location = lib.zones.poly(data)
elseif isStarted("PolyZone") then
debugPrint("^6Bridge^7: ^2Creating new poly with ^7'^4PolyZone^7': "..data.name)
Location = PolyZone:Create(data.points, { name = data.name, debugPoly = data.debug })
Location:onPlayerInOut(function(isPointInside)
if isPointInside then data.onEnter() else data.onExit() end
end)
else
print("^4ERROR^7: ^2No PolyZone creation script detected ^7- ^2Check ^3exports^1.^2lua^7")
end
return Location
end
--- Creates a circular zone using the detected polyzone library (ox_lib or PolyZone).
---
--- This function automatically detects whether `ox_lib` or `PolyZone` is active and creates a circular zone accordingly.
--- It supports setting up entry and exit callbacks for the zone.
---
---@param data table A table containing the circular zone configuration.
--- - **name** (`string`): The name of the circular zone.
--- - **coords** (`vector3`): The center coordinates of the circle.
--- - **radius** (`number`): The radius of the circle.
--- - **onEnter** (`function`): Callback function to execute when a player enters the zone.
--- - **onExit** (`function`): Callback function to execute when a player exits the zone.
---
---@return table|nil table Returns the created circular zone object or `nil` if creation failed.
---
---@usage
--- ```lua
--- createCirclePoly({
--- name = 'circleZone',
--- coords = vector3(150.0, 150.0, 20.0),
--- radius = 50.0,
--- onEnter = function() print("Entered Circle Zone") end,
--- onExit = function() print("Exited Circle Zone") end,
--- })
--- ```
function createCirclePoly(data)
local Location = nil
if isStarted(OXLibExport) then -- if it finds ox_lib, use it instead of PolyZone
debugPrint("^6Bridge^7: ^2Creating new ^3Cricle^2 poly with ^7"..OXLibExport.." "..data.name)
Location = lib.zones.sphere(data)
elseif isStarted("PolyZone") then
debugPrint("^6Bridge^7: ^2Creating new ^3Cricle^2 poly with ^7PolyZone ".. data.name)
Location = CircleZone:Create(data.coords, data.radius, { name = data.name, debugPoly = debugMode })
Location:onPlayerInOut(function(isPointInside)
if isPointInside then
data.onEnter()
else
data.onExit()
end
end)
else
print("^4ERROR^7: ^2No PolyZone creation script detected ^7- ^2Check ^3exports^1.^2lua^7")
end
debugPrint("^6Bridge^7: ^2Zone Stats - Coords: "..formatCoord(data.coords).." Radius: "..data.radius)
return Location
end
--- Removes a previously created polyzone.
---
--- This function detects the active polyzone library (`ox_lib` or `PolyZone`) and removes the specified zone accordingly.
---
--- @param Location table The zone object to be removed.
---
--- @usage
--- ```lua
--- local zone = createPoly({...})
--- -- Later in the code
--- removePolyZone(zone)
--- ```
function removePolyZone(Location)
if isStarted(OXLibExport) then -- if it finds ox_lib, use it instead of PolyZone
debugPrint("^6Bridge^7: ^2Removing ^2poly with ^7"..OXLibExport)
Location:remove()
elseif isStarted("PolyZone") then
debugPrint("^6Bridge^7: ^2poly with ^7PolyZone")
Location:destroy()
end
end

77
shared/scaleEntity.lua Normal file
View File

@@ -0,0 +1,77 @@
local cacheOrigScale = {}
local initialOffset = {}
--- Sets the scale of an entity.
---
--- This function scales an entity by adjusting its forward, right, and up vectors.
--- It also applies an initial offset to maintain the entity's position relative to the ground.
---
---@param entity number The entity ID to scale.
---@param scale number The scale factor to apply to the entity.
---
---@usage
--- ```lua
--- -- Scale an entity to twice its original size
--- SetEntityScale(entityId, 2.0)
--- ```
function SetEntityScale(entity, scale)
local forward, right, up = GetEntityMatrix(entity)
if not cacheOrigScale[entity] then
cacheOrigScale[entity] = {
forward = forward,
right = right,
up = up
}
end
local minDim, maxDim = GetModelDimensions(GetEntityModel(entity))
local originalHeight = maxDim.z - minDim.z
local newHeight = originalHeight * scale
initialOffset[entity] = (newHeight - originalHeight) / 3
local forwardTemp = cacheOrigScale[entity].forward * scale
local rightTemp = cacheOrigScale[entity].right * scale
local upTemp = cacheOrigScale[entity].up * scale
-- Apply the initial offset to the current position
local currentPosition = GetEntityCoords(entity)
local newPosition = vector3(currentPosition.x, currentPosition.y, currentPosition.z + initialOffset[entity])
SetEntityMatrix(entity, forwardTemp, rightTemp, upTemp, currentPosition)
end
--- Resets the scale of an entity to its original values.
---
--- This function restores an entity's original forward, right, and up vectors,
--- effectively undoing any scaling applied by `SetEntityScale`.
---
---@param entity number The entity ID to reset.
---
---@usage
--- ```lua
--- -- Reset the scale of an entity
--- resetScale(entityId)
--- ```
function resetScale(entity)
if cacheOrigScale[entity] then
SetEntityMatrix(entity, cacheOrigScale[entity].forward, cacheOrigScale[entity].right, cacheOrigScale[entity].up, GetEntityCoords(entity))
cacheOrigScale[entity] = nil
end
end
--[[
CreateThread(function()
-- Example usage:
-- local prop = makeProp({prop = "v_res_r_figcat", coords = vec4(-1025.88, -1417.58, 5.43, 76.30)}, false, false)
-- local ped = makePed(`a_c_cat_01`, vec4(-1022.42, -1429.97, 13.79, 68.36), true, false, nil)
-- SetEntityCollision(prop, false, true)
SetEntityScale(prop, 12)
--[[CreateThread(function()
while true do
Wait(1000)
resetScale(prop)
Wait(1000)
end
end)
end)
]]

61
shared/scaleforms.lua Normal file
View File

@@ -0,0 +1,61 @@
function makeInstructionalButtons(info)
local build = RequestScaleformMovie("instructional_buttons")
while not HasScaleformMovieLoaded(build) do Wait(0) end
DrawScaleformMovieFullscreen(build, 255, 255, 255, 0, 0)
BeginScaleformMovieMethod(build, "CLEAR_ALL")
EndScaleformMovieMethod()
BeginScaleformMovieMethod(build, "SET_CLEAR_SPACE")
ScaleformMovieMethodAddParamInt(200)
EndScaleformMovieMethod()
for i = 1, #info do
BeginScaleformMovieMethod(build, "SET_DATA_SLOT")
ScaleformMovieMethodAddParamInt(i - 1)
for k = 1, #info[i].keys do
ScaleformMovieMethodAddParamPlayerNameString(GetControlInstructionalButton(2, info[i].keys[k], true))
end
BeginTextCommandScaleformString("STRING")
AddTextComponentSubstringKeyboardDisplay(info[i].text)
EndTextCommandScaleformString()
EndScaleformMovieMethod()
end
BeginScaleformMovieMethod(build, "DRAW_INSTRUCTIONAL_BUTTONS")
EndScaleformMovieMethod()
BeginScaleformMovieMethod(build, "SET_BACKGROUND_COLOUR")
ScaleformMovieMethodAddParamInt(0)
ScaleformMovieMethodAddParamInt(0)
ScaleformMovieMethodAddParamInt(0)
ScaleformMovieMethodAddParamInt(80)
EndScaleformMovieMethod()
DrawScaleformMovieFullscreen(build, 255, 255, 255, 255, 0)
end
-- Testing showing variables on the screen instead of only in f8
function debugScaleForm(textTable, loc)
if debugMode then
-- Define the display position (top left corner)
local loc = loc or vec2(0.05, 0.65)
-- Calculate dynamic height based on the number of lines in the textTable
local lineHeight = 0.025 -- Height of each line of text
local totalHeight = #textTable * lineHeight -- Dynamic height based on number of lines
local boxPadding = 0.01 -- Padding to add around the text inside the box
local size = vec2(0.18, totalHeight + boxPadding * 2) -- Width remains fixed, height is dynamic
DrawRect(loc.x + size.x / 2, loc.y + size.y / 2, size.x, size.y, 0, 0, 0, 255)
for i = 1, #textTable do
local textLine = textTable[i]
SetTextScale(0.30, 0.30)
BeginTextCommandDisplayText("STRING")
AddTextComponentSubstringKeyboardDisplay(textLine)
EndTextCommandDisplayText(loc.x + 0.005, loc.y + (i - 1) * lineHeight + 0.01)
end
end
end

View File

@@ -0,0 +1,277 @@
BigMessage = {}
BigMessage.__index = BigMessage
function BigMessage:new()
local self = setmetatable({}, BigMessage)
self.scaleform = nil
self.startTime = 0
self.duration = 0
self.transition = "TRANSITION_OUT"
self.transitionDuration = 0.15
self.transitionPreventAutoExpansion = false
self.transitionExecuted = false
self.manualDispose = false
self.isDisplaying = false
return self
end
function BigMessage:Load()
if self.scaleform then return end
self.scaleform = RequestScaleformMovie("MP_BIG_MESSAGE_FREEMODE")
while not HasScaleformMovieLoaded(self.scaleform) do
Wait(0)
end
end
-- Dispose of the scaleform
function BigMessage:Dispose()
if not self.scaleform then return end
if self.manualDispose then
BeginScaleformMovieMethod(self.scaleform, self.transition)
ScaleformMovieMethodAddParamBool(false)
ScaleformMovieMethodAddParamFloat(self.transitionDuration)
ScaleformMovieMethodAddParamBool(self.transitionPreventAutoExpansion)
EndScaleformMovieMethod()
Wait((self.transitionDuration * 0.5) * 1000)
self.manualDispose = false
end
self.startTime = 0
self.transitionExecuted = false
SetScaleformMovieAsNoLongerNeeded(self.scaleform)
self.scaleform = nil
self.isDisplaying = false
end
function BigMessage:Update()
if not self.scaleform then return end
DrawScaleformMovieFullscreen(self.scaleform, 255, 255, 255, 255, 0)
if self.manualDispose then return end
if self.startTime ~= 0 and (GetGameTimer() - self.startTime) > self.duration then
if not self.transitionExecuted then
BeginScaleformMovieMethod(self.scaleform, self.transition)
ScaleformMovieMethodAddParamBool(false)
ScaleformMovieMethodAddParamFloat(self.transitionDuration)
ScaleformMovieMethodAddParamBool(self.transitionPreventAutoExpansion)
EndScaleformMovieMethod()
self.transitionExecuted = true
self.duration = self.duration + ((self.transitionDuration * 0.5) * 1000)
else
self:Dispose()
end
end
end
function BigMessage:SetTransition(transition, duration, preventAutoExpansion)
self.transition = transition or "TRANSITION_OUT"
self.transitionDuration = duration or 0.4
self.transitionPreventAutoExpansion = preventAutoExpansion or true
end
function BigMessage:StartUpdate()
if self.isDisplaying then return end
self.isDisplaying = true
CreateThread(function()
while self.isDisplaying do
Wait(0)
self:Update()
end
end)
end
--- Displays a mission passed message.
---
--- @param msg string The main message to display.
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
function BigMessage:ShowMissionPassedMessage(msg, duration, manualDispose)
duration = duration or 5000
self:Load()
self.startTime = GetGameTimer()
self.manualDispose = manualDispose or false
BeginScaleformMovieMethod(self.scaleform, "SHOW_MISSION_PASSED_MESSAGE")
ScaleformMovieMethodAddParamPlayerNameString(msg)
ScaleformMovieMethodAddParamPlayerNameString("")
ScaleformMovieMethodAddParamInt(100)
ScaleformMovieMethodAddParamBool(true)
ScaleformMovieMethodAddParamInt(0)
ScaleformMovieMethodAddParamBool(true)
EndScaleformMovieMethod()
self.duration = duration
self:StartUpdate()
end
--- Displays a colored shard message.
---
--- @param msg string The main message to display.
--- @param desc string The description text.
--- @param textColor number The color index for the text.
--- @param bgColor number The color index for the background.
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
function BigMessage:ShowColoredShard(msg, desc, textColor, bgColor, duration, manualDispose)
duration = duration or 5000
self:Load()
self.startTime = GetGameTimer()
self.manualDispose = manualDispose or false
BeginScaleformMovieMethod(self.scaleform, "SHOW_SHARD_CENTERED_MP_MESSAGE")
ScaleformMovieMethodAddParamPlayerNameString(msg)
ScaleformMovieMethodAddParamPlayerNameString(desc)
ScaleformMovieMethodAddParamInt(bgColor)
ScaleformMovieMethodAddParamInt(textColor)
EndScaleformMovieMethod()
self.duration = duration
self:StartUpdate()
end
--- Displays an old-style mission passed message.
---
--- @param msg string The main message to display.
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
---
--- @return void
function BigMessage:ShowOldMessage(msg, duration, manualDispose)
duration = duration or 5000
self:Load()
self.startTime = GetGameTimer()
self.manualDispose = manualDispose or false
BeginScaleformMovieMethod(self.scaleform, "SHOW_MISSION_PASSED_MESSAGE")
ScaleformMovieMethodAddParamPlayerNameString(msg)
EndScaleformMovieMethod()
self.duration = duration
self:StartUpdate()
end
--- Displays a simple shard message.
---
--- @param msg string The main message to display.
--- @param subtitle string The subtitle text.
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
---
--- @return void
function BigMessage:ShowSimpleShard(msg, subtitle, duration, manualDispose)
duration = duration or 5000
self:Load()
self.startTime = GetGameTimer()
self.manualDispose = manualDispose or false
BeginScaleformMovieMethod(self.scaleform, "SHOW_SHARD_CREW_RANKUP_MP_MESSAGE")
ScaleformMovieMethodAddParamPlayerNameString(msg)
ScaleformMovieMethodAddParamPlayerNameString(subtitle)
EndScaleformMovieMethod()
self.duration = duration
self:StartUpdate()
end
--- Displays a rank-up message.
---
--- @param msg string The main message to display.
--- @param subtitle string The subtitle text.
--- @param rank number The rank level achieved.
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
function BigMessage:ShowRankupMessage(msg, subtitle, rank, duration, manualDispose)
duration = duration or 5000
self:Load()
self.startTime = GetGameTimer()
self.manualDispose = manualDispose or false
BeginScaleformMovieMethod(self.scaleform, "SHOW_BIG_MP_MESSAGE")
ScaleformMovieMethodAddParamPlayerNameString(msg)
ScaleformMovieMethodAddParamPlayerNameString(subtitle)
ScaleformMovieMethodAddParamInt(rank)
ScaleformMovieMethodAddParamPlayerNameString("")
ScaleformMovieMethodAddParamPlayerNameString("")
EndScaleformMovieMethod()
self.duration = duration
self:StartUpdate()
end
--- Displays a weapon purchased message.
---
--- @param bigMessage string The main message to display.
--- @param weaponName string The name of the weapon purchased.
--- @param weaponHash number The hash identifier of the weapon.
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
function BigMessage:ShowWeaponPurchasedMessage(bigMessage, weaponName, weaponHash, duration, manualDispose)
duration = duration or 5000
self:Load()
self.startTime = GetGameTimer()
self.manualDispose = manualDispose or false
BeginScaleformMovieMethod(self.scaleform, "SHOW_WEAPON_PURCHASED")
ScaleformMovieMethodAddParamPlayerNameString(bigMessage)
ScaleformMovieMethodAddParamPlayerNameString(weaponName)
ScaleformMovieMethodAddParamInt(weaponHash)
ScaleformMovieMethodAddParamPlayerNameString("")
ScaleformMovieMethodAddParamInt(100)
EndScaleformMovieMethod()
self.duration = duration
self:StartUpdate()
end
--- Displays a large multiplayer message.
---
--- @param msg string The main message to display.
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
function BigMessage:ShowMpMessageLarge(msg, duration, manualDispose)
duration = duration or 5000
self:Load()
self.startTime = GetGameTimer()
self.manualDispose = manualDispose or false
BeginScaleformMovieMethod(self.scaleform, "SHOW_CENTERED_MP_MESSAGE_LARGE")
ScaleformMovieMethodAddParamPlayerNameString(msg)
ScaleformMovieMethodAddParamPlayerNameString("")
ScaleformMovieMethodAddParamInt(100)
ScaleformMovieMethodAddParamBool(true)
ScaleformMovieMethodAddParamInt(100)
EndScaleformMovieMethod()
BeginScaleformMovieMethod(self.scaleform, "TRANSITION_IN")
EndScaleformMovieMethod()
self.duration = duration
self:StartUpdate()
end
--- Displays a "Wasted" multiplayer message.
---
--- @param msg string The main message to display.
--- @param subtitle string The subtitle text.
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
function BigMessage:ShowMpWastedMessage(msg, subtitle, duration, manualDispose)
duration = duration or 5000
self:Load()
self.startTime = GetGameTimer()
self.manualDispose = manualDispose or false
BeginScaleformMovieMethod(self.scaleform, "SHOW_SHARD_WASTED_MP_MESSAGE")
ScaleformMovieMethodAddParamPlayerNameString(msg)
ScaleformMovieMethodAddParamPlayerNameString(subtitle)
EndScaleformMovieMethod()
self.duration = duration
self:StartUpdate()
end
return BigMessage

View File

@@ -0,0 +1,116 @@
CountdownHandler = {}
CountdownHandler.__index = CountdownHandler
function CountdownHandler:new()
local self = setmetatable({}, CountdownHandler)
self.scaleform = nil
self.renderCountdown = false
self.colour = { r = 255, g = 255, b = 255, a = 255 }
return self
end
function CountdownHandler:Load()
if self.scaleform then return end
self.scaleform = RequestScaleformMovie("COUNTDOWN")
while not HasScaleformMovieLoaded(self.scaleform) do
Wait(0)
end
end
function CountdownHandler:Dispose()
if self.scaleform then
SetScaleformMovieAsNoLongerNeeded(self.scaleform)
self.scaleform = nil
end
end
function CountdownHandler:Update()
if self.scaleform then
DrawScaleformMovieFullscreen(self.scaleform, 255, 255, 255, 255, 0)
end
end
function CountdownHandler:ShowMessage(message)
local r, g, b, a = self.colour.r, self.colour.g, self.colour.b, self.colour.a
BeginScaleformMovieMethod(self.scaleform, "SET_MESSAGE")
ScaleformMovieMethodAddParamPlayerNameString(message)
ScaleformMovieMethodAddParamInt(r)
ScaleformMovieMethodAddParamInt(g)
ScaleformMovieMethodAddParamInt(b)
ScaleformMovieMethodAddParamBool(true)
EndScaleformMovieMethod()
BeginScaleformMovieMethod(self.scaleform, "FADE_MP")
ScaleformMovieMethodAddParamPlayerNameString(message)
ScaleformMovieMethodAddParamInt(r)
ScaleformMovieMethodAddParamInt(g)
ScaleformMovieMethodAddParamInt(b)
EndScaleformMovieMethod()
end
--- Starts the countdown with the specified number and HUD color.
---
--- @param number number|nil The starting number for the countdown. Defaults to 3.
--- @param hudColour number|nil The HUD color index. Defaults to 18.
---
--- @return boolean `true` when the countdown has finished.
---
--- @usage
--- ```lua
--- -- Start a countdown of 5 seconds with HUD color 25
--- if CountdownHandler:Start(5, 25) then
--- print("Countdown Complete")
--- end
--- ```
function CountdownHandler:Start(number, hudColour)
local finished = false
number = number or 3
hudColour = hudColour or 18
local r, g, b, a = GetHudColour(hudColour)
self.colour = { r = r, g = g, b = b, a = a }
self:Load()
self.renderCountdown = true
CreateThread(function()
while self.renderCountdown do
Wait(0)
self:Update()
end
end)
-- Begin the countdown
CreateThread(function()
local currentNumber = number
while currentNumber > 0 do
-- Play countdown sound
playSound("Count")
self:ShowMessage(tostring(currentNumber))
Wait(1000)
currentNumber = currentNumber - 1
end
playSound("Go")
self:ShowMessage("GO")
finished = true
Wait(1000)
self.renderCountdown = false
self:Dispose()
finished = true
end)
while not finished do Wait(10) end
return true
end
-- Create an instance of CountdownHandler
CountdownHandler = CountdownHandler:new()
-- Optional: Register an event to start the countdown
RegisterNetEvent(getScript()..":startCountdown", function(number, hudColour)
CountdownHandler:Start(number, hudColour)
end)
return CountdownHandler

View File

@@ -0,0 +1,41 @@
--- Displays debug information on the player's screen.
---
--- This function renders a semi-transparent box with multiple lines of text for debugging purposes.
--- It is controlled by the `debugMode` flag and can be positioned dynamically on the screen.
---
--- @param textTable table A table containing strings to display.
--- @param loc vector2|nil Optional. The screen position to display the debug box. Defaults to `vec2(0.05, 0.65)`.
---
--- @usage
--- ```lua
--- debugScaleForm({
--- "Player Position: X=123.45 Y=678.90 Z=12.34",
--- "Current Action: Running",
--- })
--- ```
function debugScaleForm(textTable, loc)
if debugMode then
-- Define the display position (top left corner)
local loc = loc or vec2(0.05, 0.65)
-- Calculate dynamic height based on the number of lines in the textTable
local lineHeight = 0.025 -- Height of each line of text
local totalHeight = #textTable * lineHeight -- Dynamic height based on number of lines
local boxPadding = 0.01 -- Padding to add around the text inside the box
local size = vec2(0.18, totalHeight + boxPadding * 2) -- Width remains fixed, height is dynamic
DrawRect(loc.x + size.x / 2, loc.y + size.y / 2, size.x, size.y, 0, 0, 0, 255)
for i = 1, #textTable do
local textLine = textTable[i]
SetTextScale(0.30, 0.30)
BeginTextCommandDisplayText("STRING")
AddTextComponentSubstringKeyboardDisplay(textLine)
EndTextCommandDisplayText(loc.x + 0.005, loc.y + (i - 1) * lineHeight + 0.01)
end
end
end

View File

@@ -0,0 +1,50 @@
--- Creates instructional buttons using the detected polyzone library (ox_lib or PolyZone).
---
--- This function generates instructional buttons on the player's screen based on the provided information.
--- It supports different polyzone libraries by automatically detecting which one is active.
---
---@param info table A table containing the instructional buttons configuration.
--- - **keys** (`table`): A list of control keys to display.
--- - **text** (`string`): The description text for the buttons.
---
---@usage
--- ```lua
--- makeInstructionalButtons({
--- { keys = { 38 }, text = "Interact" },
--- { keys = { 47 }, text = "Pick Up" },
--- })
--- ```
function makeInstructionalButtons(info)
local build = RequestScaleformMovie("instructional_buttons")
while not HasScaleformMovieLoaded(build) do Wait(0) end
DrawScaleformMovieFullscreen(build, 255, 255, 255, 0, 0)
BeginScaleformMovieMethod(build, "CLEAR_ALL")
EndScaleformMovieMethod()
BeginScaleformMovieMethod(build, "SET_CLEAR_SPACE")
ScaleformMovieMethodAddParamInt(200)
EndScaleformMovieMethod()
for i = 1, #info do
BeginScaleformMovieMethod(build, "SET_DATA_SLOT")
ScaleformMovieMethodAddParamInt(i - 1)
for k = 1, #info[i].keys do
ScaleformMovieMethodAddParamPlayerNameString(GetControlInstructionalButton(2, info[i].keys[k], true))
end
BeginTextCommandScaleformString("STRING")
AddTextComponentSubstringKeyboardDisplay(info[i].text)
EndTextCommandScaleformString()
EndScaleformMovieMethod()
end
BeginScaleformMovieMethod(build, "DRAW_INSTRUCTIONAL_BUTTONS")
EndScaleformMovieMethod()
BeginScaleformMovieMethod(build, "SET_BACKGROUND_COLOUR")
ScaleformMovieMethodAddParamInt(0)
ScaleformMovieMethodAddParamInt(0)
ScaleformMovieMethodAddParamInt(0)
ScaleformMovieMethodAddParamInt(80)
EndScaleformMovieMethod()
DrawScaleformMovieFullscreen(build, 255, 255, 255, 255, 0)
end

View File

@@ -0,0 +1,60 @@
function createTimerHud(title, data, alpha)
loadTextureDict("timerbars")
local loc = vec2(0.89, 0.90)
alpha = alpha or 255 -- Default to fully opaque if alpha is not provided
if title then
local x = loc.x+0.037
local y = 0.1
DrawSprite("timerbars", "all_black_bg", x, y, 0.12, 0.05, 0.0, 255, 255, 255, alpha)
SetTextScale(0.80, 0.80)
SetTextWrap(0.75, 0.985)
SetTextJustification(2)
SetTextFont(4)
SetTextColour(255, 255, 255, alpha)
BeginTextCommandDisplayText("STRING")
AddTextComponentSubstringKeyboardDisplay("~y~"..title)
EndTextCommandDisplayText(x+0.06, y - 0.026)
end
local displayIndex = 0
for i = #data, 1, -1 do
local space = 0.044 * displayIndex
DrawSprite("timerbars", "all_black_bg", loc.x+0.02, loc.y - space, 0.15, 0.04, 0.0, 255, 255, 255, alpha)
SetTextScale(0.0, 0.35)
SetTextWrap(0.5, 0.92)
SetTextJustification(2)
SetTextColour(255, 255, 255, alpha)
BeginTextCommandDisplayText("STRING")
AddTextComponentSubstringKeyboardDisplay(data[i].stat:upper())
EndTextCommandDisplayText(loc.x-0.05, (loc.y - space) - 0.0125)
SetTextScale(0.55, 0.55)
SetTextWrap(0.85, 0.98 - (data[i].multi and 0.026 or 0.0))
SetTextFont(4)
SetTextJustification(2)
SetTextColour(255, 255, 255, alpha)
if data[i].multi then
local startX = 0.071
DrawSprite("timerbars", "circle_checkpoints",
loc.x + startX, (loc.y - space)+0.005,
0.011, 0.018, 0.0, 255, 191, 0, 200)
DrawSprite("timerbars", "circle_checkpoints",
loc.x + (startX + 0.008), (loc.y - space)+0.005,
0.011, 0.018, 0.0, 255, 191, 0, data[i].multi > 1 and 200 or 75)
DrawSprite("timerbars", "circle_checkpoints",
loc.x + (startX + 0.016), (loc.y - space)+0.005,
0.011, 0.018, 0.0, 255, 191, 0, data[i].multi > 2 and 200 or 75)
end
BeginTextCommandDisplayText("STRING")
AddTextComponentSubstringKeyboardDisplay(data[i].value)
EndTextCommandDisplayText(loc.x - 0.02, (loc.y - space) - 0.017)
displayIndex += 1
end
makeInstructionalButtons({ { text = "Exit", keys = { 194 }}})
end

271
shared/stashcontrol.lua Normal file
View File

@@ -0,0 +1,271 @@
if isServer() then
createCallback(getScript()..':server:GetStashItems', function(source, stashName) local stash = getStash(stashName) return stash end)
end
local stashCache ={}
function GetStashTimeout(stashName, stop)
if stop then stashCache = {} return end
local stash = stashCache[stashName]
if not stash then
stashCache[stashName] = { items = {}, timeout = 0 }
stash = stashCache[stashName]
end
if #stash.items > 0 then return true end
if stash.timeout <= 0 then
stash.items = triggerCallback(getScript()..':server:GetStashItems', stashName)
stash.timeout = 10000
CreateThread(function()
while stash.timeout > 0 do stash.timeout -= 1000 Wait(1000) end
stashCache[stashName] = nil
end)
end
return false
end
function checkHasItem(stashes, itemTable)
if not stashes then return hasItem(itemTable), nil end
if type(stashes) == "table" then
local succeses = 0
local itemCount = 0
for _, item in pairs(itemTable) do itemCount += 1 end
for _, name in pairs(stashes) do
GetStashTimeout(name)
for item, amount in pairs(itemTable) do
debugPrint("^6Bridge^7: ^2Checking"..(name and " ^7'^6"..name.."^7'" or "").." ^2ingredients^7 - ^6"..item.."^7")
if stashhasItem(stashCache[name].items, item, amount) then
succeses += 1
if succeses == itemCount then return true, name end
end
end
end
else
debugPrint("^6Bridge^7: ^2Checking"..(stashes and " ^7'^6"..stashes.."^7'" or "").." ^2ingredients^7 - ^6"..k.."^7")
GetStashTimeout(stashes)
return stashhasItem(stashCache[stashes].items, itemTable), stashes
end
return false, nil
end
-- Stash Items
function openStash(data)
if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end
if isStarted(OXInv) then
exports[OXInv]:openInventory('stash', data.stash)
elseif isStarted(CodeMInv) then
exports[CodeMInv]:OpenStash(data.stash, StashWeight, 100)
elseif isStarted(QBInv) then
if QBInvNew then
TriggerServerEvent(getScript()..':server:OpenStashQB', { stashName = data.stash, label = data.label, maxweight = data.maxWeight or 600000, slots = data.slots or 40 })
else
TriggerEvent("inventory:client:SetCurrentStash", data.stash)
TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, data.stashOptions)
end
else
TriggerEvent("inventory:client:SetCurrentStash", data.stash)
TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, data.stashOptions)
end
lookEnt(data.coords)
end
RegisterNetEvent(getScript()..':server:OpenStashQB', function(data)
exports[QBInv]:OpenInventory(source, data.stashName, data)
end)
function getStash(stashName) local stashResource = ""
if type(stashName) ~= "string" then return print("Stash name was not a string %s(%s)", stashName, type(stashName)) end
local stashItems, items = {}, {}
if isStarted(OXInv) then stashResource = OXInv
stashItems = exports[OXInv]:Inventory(stashName).items
elseif isStarted(QSInv) then stashResource = QSInv
stashItems = exports[QSInv]:GetStashItems(stashName)
elseif isStarted(CoreInv) then stashResource = CoreInv
stashItems = exports[CoreInv]:getInventory(stashName)
elseif isStarted(CodeMInv) then stashResource = CodeMInv
stashItems = exports[CodeMInv]:GetInventoryItems('Stash', stashName)
elseif isStarted(OrigenInv) then stashResource = OrigenInv
stashItems = exports[OrigenInv]:GetStashItems(stashName)
elseif isStarted(PSInv) then stashResource = PSInv
local result = MySQL.scalar.await('SELECT items FROM stashitems WHERE stash = ?', { stashName })
if result then stashItems = json.decode(result) end
elseif isStarted(QBInv) then stashResource = QBInv
local result = MySQL.scalar.await("SELECT items FROM "..(QBInvNew and "inventories" or "stashitem").." WHERE identifier = ?", { stashName })
if result then stashItems = json.decode(result) end
end
debugPrint("^6Bridge^7: ^2Retrieving ^3Stash^2 with ^7"..stashResource)
if stashItems then
for _, item in pairs(stashItems) do
local itemInfo = Items[item.name:lower()]
if itemInfo then
local indexNum = #items+1 -- Added to help recreate missing slot numbers
items[(item.slot and item.slot) or indexNum] = {
name = itemInfo.name or nil,
amount = tonumber(item.amount) or tonumber(item.count),
info = item.info or "",
label = itemInfo.label or nil,
description = itemInfo.description or "",
weight = itemInfo.weight or nil,
type = itemInfo.type or nil,
unique = itemInfo.unique or nil,
useable = itemInfo.useable or nil,
image = itemInfo.image or nil,
slot = (item.slot and item.slot) or indexNum,
metadata = (item.metadata and item.metadata) or nil,
}
end
end
debugPrint("^6Bridge^7: ^3GetStashItems^7: ^2Stash information for ^7'^6"..stashName.."^7' ^2retrieved^7")
end
return items
end
function stashRemoveItem(stashItems, stashName, items) local amount = amount and amount or 1
-- print("stashItems: "..json.encode(stashItems, { indent = true}))
-- print("stashName: "..json.encode(stashName, { indent = true}))
-- print("items: "..json.encode(items, { indent = true}))
if isStarted(OXInv) then
for k, v in pairs(items) do
debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..OXInv, k, v)
if type(stashName) == "table" then
for _, name in pairs(stashName) do
local success = exports[OXInv]:RemoveItem(name, k, v)
if success then
debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..OXInv, k, v)
break
end
end
else
exports[OXInv]:RemoveItem(stashName, k, v)
end
end
elseif isStarted(QSInv) then
for k, v in pairs(items) do
for l in pairs(stashItems) do
if stashItems[l].name == k then
if (stashItems[l].amount - v) <= 0 then
debugPrint("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v)
stashItems[l] = nil
else
debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QBInv, k, v)
exports[QSInv]:RemoveItemIntoStash(stashName, k, v, l)
end
end
end
end
elseif isStarted(CoreInv) then
for k, v in pairs(items) do
exports[CoreInv]:removeItemExact(stashName, k, v)
debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..CoreInv, k, v)
end
elseif isStarted(CodeMInv) then
for k, v in pairs(items) do
for l in pairs(stashItems) do
if stashItems[l].name == k then
if (stashItems[l].amount - v) <= 0 then
debugPrint("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v)
stashItems[l] = nil
else
debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..CodeMInv, k, v)
stashItems[l].amount -= v
end
end
end
end
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash ^7'^6"..stashName.."^7'")
elseif isStarted(OrigenInv) then
for k, v in pairs(items) do
exports[OrigenInv]:RemoveFromStash(stashName, k, v)
debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..OrigenInv, k, v)
end
elseif isStarted(PSInv) then
for k, v in pairs(items) do
for l in pairs(stashItems) do
if stashItems[l].name == k then
if (stashItems[l].amount - v) <= 0 then
debugPrint("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v)
stashItems[l] = nil
else
debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QBInv, k, v)
stashItems[l].amount -= v
end
end
end
end
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash ^7'^6"..stashName.."^7'")
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { ['stash'] = stashName, ['items'] = json.encode(stashItems) })
elseif isStarted(QBInv) then
if QBInvNew then
for k, v in pairs(items) do
exports[QBInv]:RemoveItem(stashName[1], k, v, false, 'crafting')
debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QBInv, k, v)
end
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash ^7'^6"..stashName[1].."^7'")
MySQL.Async.insert('INSERT INTO inventories (identifier, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { ['stash'] = stashName[1], ['items'] = json.encode(stashItems) })
else
for k, v in pairs(items) do
for l in pairs(stashItems) do
if stashItems[l].name == k then
if (stashItems[l].amount - v) <= 0 then
if Config.System.Debug then
print("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v)
end
stashItems[l] = nil
else
if Config.System.Debug then
print("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QBInv, k, v)
end
stashItems[l].amount -= v
end
end
end
end
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash ^7'^6"..stashName.."^7'")
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { ['stash'] = stashName, ['items'] = json.encode(stashItems) })
end
else
print("^4ERROR^7: ^2No Inventory detected ^7- ^2Check ^3exports^1.^2lua^7")
end
end
RegisterNetEvent(getScript()..":server:stashRemoveItem", stashRemoveItem)
function stashhasItem(stashItems, items, amount)
local invs = {OXInv, QSInv, CoreInv, CodeMInv, OrigenInv, QBInv, PSInv}
local foundInv = ""
for _, inv in ipairs(invs) do
if isStarted(inv) then
foundInv = inv:gsub("%-", "^7-^6"):gsub("%_", "^7_^6")
break
end
end
if type(items) ~= "table" then items = { [items] = amount and amount or 1, } end
local hasTable = {}
for item, amount in pairs(items) do
local count = 0
for _, itemData in pairs(stashItems) do
if itemData and (itemData.name == item) then
count += (itemData.amount or 1)
end
end
local debugMsg = string.format("^6Bridge^7: ^3stashHasItem^7[^6%s^7]: %s '%s' ^3%d^7/^3%d^7", foundInv, (count >= amount and "^5FOUND^7" or "^1NOT FOUND^7"), item, count, amount)
debugPrint(debugMsg)
hasTable[item] = { hasItem = (count >= amount), count = count }
end
for k, v in pairs(hasTable) do if v.hasItem == false then return false, hasTable end end
return true, hasTable
end

404
shared/targets.lua Normal file
View File

@@ -0,0 +1,404 @@
-- This is for experimental targets based on GTA in-world text prompts --
local TextTargets = {}
local Keys = {
[322] = "ESC", [288] = "F1", [289] = "F2", [170] = "F3", [166] = "F5",
[167] = "F6", [168] = "F7", [169] = "F8", [56] = "F9", [57] = "F10",
[243] = "~", [157] = "1", [158] = "2", [160] = "3", [164] = "4", [165] = "5",
[159] = "6", [161] = "7", [162] = "8", [163] = "9", [84] = "-", [83] = "=",
[177] = "BACKSPACE", [37] = "TAB",
[44] = "Q", [32] = "W", [38] = "E", [45] = "R", [245] = "T", [246] = "Y",
[303] = "U", [199] = "P",
[39] = "[", [40] = "]", [18] = "ENTER", [137] = "CAPS",
[34] = "A", [8] = "S", [9] = "D", [23] = "F", [47] = "G",
[74] = "H", [311] = "K", [182] = "L", [21] = "LEFTSHIFT",
[20] = "Z", [73] = "X", [26] = "C", [0] = "V", [29] = "B", [249] = "N",
[244] = "M", [82] = ",", [81] = "."
}
-- Target Creation --
-- Target Entities, this is more based on qb-target's style of target creation, and translates those into ox or qb-target code --
local targetEntities = {}
--- Creates a target for an entity with specified options and interaction distance.
---
--- This function supports different targeting systems (OX Target, QB Target, or custom DrawText3D targets)
--- based on the server configuration. It translates qb-target style options into the appropriate format
--- for the detected targeting system.
---
---@param entity number The entity ID to create a target for.
---@param opts table A table of option configurations for the target.
--- - **icon** (`string`): The icon to display for the option.
--- - **label** (`string`): The label text for the option.
--- - **item** (`string|nil`): (Optional) The item associated with the option.
--- - **job** (`string|nil`): (Optional) The job required to interact with the option.
--- - **gang** (`string|nil`): (Optional) The gang required to interact with the option.
--- - **action** (`function|nil`): (Optional) The function to execute when the option is selected.
---@param dist number The interaction distance for the target.
---
---@usage
--- ```lua
--- createEntityTarget(entityId, {
--- { icon = "fas fa-car", label = "Open Vehicle", action = openVehicle },
--- { icon = "fas fa-lock", label = "Lock Vehicle", action = lockVehicle }
--- }, 2.5)
--- ```
function createEntityTarget(entity, opts, dist)
targetEntities[#targetEntities + 1] = entity
local entityCoords = GetEntityCoords(entity)
if Config.System.DontUseTarget then
debugPrint("^6Bridge^7: ^2Creating new ^3Entity^2 target with ^6DrawText ^7"..entity)
local existingTarget = nil
for key, target in pairs(TextTargets) do
if #(target.coords - entityCoords) < 0.01 then -- Adjust the threshold for coordinate matching
existingTarget = target
break
end
end
if existingTarget then
-- Combine options
local keyTable = { 38, 29, 303, 45, 46, 47, 48 } -- Extend key table as needed
for i = 1, #opts do
local key = keyTable[#existingTarget.options + i]
opts[i].key = key
existingTarget.buttontext[#existingTarget.buttontext + 1] = " ~b~[~w~"..Keys[key].."~b~] ~w~"..opts[i].label
existingTarget.options[#existingTarget.options + 1] = opts[i]
end
else
-- Create new target
local tempText = {}
local keyTable = { 38, 29, 303, 45, 46, 47, 48 }
for i = 1, #opts do
opts[i].key = keyTable[i]
tempText[#tempText + 1] = " ~b~[~w~"..Keys[opts[i].key].."~b~] ~w~"..opts[i].label
end
TextTargets[entity] = { coords = vec3(entityCoords.x, entityCoords.y, entityCoords.z), buttontext = tempText, options = opts, dist = dist }
end
elseif isStarted(OXTargetExport) then
debugPrint("^6Bridge^7: ^2Creating new ^3Entity^2 target with ^6"..OXTargetExport.." ^7"..entity)
local options = {}
for i = 1, #opts do
options[i] = {
icon = opts[i].icon,
label = opts[i].label,
item = opts[i].item or nil,
groups = opts[i].job or opts[i].gang,
onSelect = opts[i].action,
canInteract = function(_, distance)
return distance < dist and true or false
end
}
end
exports[OXTargetExport]:addLocalEntity(entity, options)
elseif isStarted(QBTargetExport) then
debugPrint("^6Bridge^7: ^2Creating new ^3Entity^2 target with ^6"..QBTargetExport.." ^7"..entity)
local options = { options = opts, distance = dist }
exports[QBTargetExport]:AddTargetEntity(entity, options)
end
end
local boxTargets = {}
--- Creates a box-shaped target zone with specified options and interaction distance.
---
--- This function supports different targeting systems (OX Target, QB Target, or custom DrawText3D targets)
--- based on the server configuration. It translates qb-target style options into the appropriate format
--- for the detected targeting system.
---
---@param data table A table containing the box zone configuration.
--- - **name** (`string`): The name identifier for the zone.
--- - **coords** (`vector3`): The center coordinates of the box.
--- - **width** (`number`): The width of the box.
--- - **height** (`number`): The height of the box.
--- - **options** (`table`): A table with additional options:
--- - **heading** (`number`): The rotation angle of the box.
--- - **debugPoly** (`boolean`): Whether to enable debug mode for the zone.
---
---@param opts table A table of option configurations for the target.
--- - **icon** (`string`): The icon to display for the option.
--- - **label** (`string`): The label text for the option.
--- - **item** (`string|nil`): (Optional) The item associated with the option.
--- - **job** (`string|nil`): (Optional) The job required to interact with the option.
--- - **gang** (`string|nil`): (Optional) The gang required to interact with the option.
--- - **onSelect** (`function|nil`): (Optional) The function to execute when the option is selected.
---@param dist number The interaction distance for the target.
---
---@return string|table name identifier or target object of the created zone.
---
---@usage
--- ```lua
--- createBoxTarget({
--- name = 'storageBox',
--- coords = vector3(100.0, 200.0, 30.0),
--- width = 2.0,
--- height = 2.0,
--- options = { heading = 0, debugPoly = false }
--- }, {
--- { icon = "fas fa-box", label = "Open Storage", action = openStorage }
--- }, 1.5)
--- ```
function createBoxTarget(data, opts, dist)
if Config.System.DontUseTarget then
debugPrint("^6Bridge^7: ^2Creating new ^3Box^2 target with ^6DrawText ^7"..data[1])
local existingTarget = nil
for key, target in pairs(TextTargets) do
if #(target.coords - data[2]) < 0.01 then -- Adjust the threshold as needed for coordinate precision
existingTarget = target
break
end
end
local keyTable = { 38, 29, 303, 45, 46, 47, 48 }
if existingTarget then
-- Combine options
for i = 1, #opts do
local key = keyTable[#existingTarget.options + i]
opts[i].key = key
existingTarget.buttontext[#existingTarget.buttontext+1] = " ~b~[~w~"..Keys[key].."~b~] ~w~"..opts[i].label
existingTarget.options[#existingTarget.options+1] = opts[i]
end
else
-- Create new target
local tempText = {}
for i = 1, #opts do
opts[i].key = keyTable[i]
tempText[#tempText+1] = " ~b~[~w~"..Keys[opts[i].key].."~b~] ~w~"..opts[i].label
end
TextTargets[data[1]] = { coords = data[2], buttontext = tempText, options = opts, dist = 1.5 }
end
return data[1]
elseif isStarted(OXTargetExport) then
debugPrint("^6Bridge^7: ^2Creating new ^3Box^2 target with ^6"..OXTargetExport.." ^7"..data[1])
local options = {}
for i = 1, #opts do
options[i] = {
icon = opts[i].icon,
label = opts[i].label,
item = opts[i].item or nil,
groups = opts[i].job or opts[i].gang,
onSelect = opts[i].onSelect or opts[i].action,
canInteract = function(_, distance)
return distance < dist and true or false
end
}
end
if not data[5].useZ then
local z = data[2].z + math.abs(data[5].maxZ - data[5].minZ) / 2
data[2] = vec3(data[2].x, data[2].y, z)
end
local target = exports[OXTargetExport]:addBoxZone({
coords = data[2],
size = vec3(data[4], data[3], (data[5].useZ or not data[5].maxZ) and data[2].z or math.abs(data[5].maxZ - data[5].minZ)),
rotation = data[5].heading,
debug = data[5].debugPoly,
options = options
})
boxTargets[#boxTargets+1] = target
return target
elseif isStarted(QBTargetExport) then
debugPrint("^6Bridge^7: ^2Creating new ^3Box^2 target with ^6"..QBTargetExport.." ^7"..data[1])
local options = { options = opts, distance = dist }
local target = exports[QBTargetExport]:AddBoxZone(data[1], data[2], data[3], data[4], data[5], options)
boxTargets[#boxTargets+1] = target
return data[1]
end
end
local circleTargets = {}
--- Creates a circular target zone with specified options and interaction distance.
---
--- This function supports different targeting systems (OX Target, QB Target, or custom DrawText3D targets)
--- based on the server configuration. It translates qb-target style options into the appropriate format
--- for the detected targeting system.
---
---@param data table A table containing the circle zone configuration.
--- - **name** (`string`): The name identifier for the zone.
--- - **coords** (`vector3`): The center coordinates of the circle.
--- - **radius** (`number`): The radius of the circle.
--- - **options** (`table`): A table with additional options:
--- - **debugPoly** (`boolean`): Whether to enable debug mode for the zone.
---
---@param opts table A table of option configurations for the target.
--- - **icon** (`string`): The icon to display for the option.
--- - **label** (`string`): The label text for the option.
--- - **item** (`string|nil`): (Optional) The item associated with the option.
--- - **job** (`string|nil`): (Optional) The job required to interact with the option.
--- - **gang** (`string|nil`): (Optional) The gang required to interact with the option.
--- - **onSelect** (`function|nil`): (Optional) The function to execute when the option is selected.
---@param dist number The interaction distance for the target.
---
---@return string|table name identifier or target object of the created zone.
---
---@usage
--- ```lua
--- createCircleTarget({
--- name = 'centralPark',
--- coords = vector3(200.0, 300.0, 40.0),
--- radius = 50.0,
--- options = { debugPoly = false }
--- }, {
--- { icon = "fas fa-tree", label = "Relax", action = relaxAction }
--- }, 2.0)
--- ```
function createCircleTarget(data, opts, dist)
if Config.System.DontUseTarget then
debugPrint("^6Bridge^7: ^2Creating new ^3Circle^2 target with ^6DrawText ^7"..data[1])
local existingTarget = nil
for key, target in pairs(TextTargets) do
if #(target.coords - data[2]) < 0.01 then -- Adjust the threshold for precision
existingTarget = target
break
end
end
if existingTarget then
-- Combine options
local keyTable = { 38, 29, 303, 45, 46, 47, 48 } -- Extend key table as needed
for i = 1, #opts do
local key = keyTable[#existingTarget.options + i]
opts[i].key = key
existingTarget.buttontext[#existingTarget.buttontext+1] = " ~b~[~w~"..Keys[key].."~b~] ~w~"..opts[i].label
existingTarget.options[#existingTarget.options+1] = opts[i]
end
else
-- Create new target
local tempText = ""
local keyTable = { 38, 29, 303, 45, 46, 47, 48 }
for i = 1, #opts do
opts[i].key = keyTable[i]
tempText[#tempText+1] = " ~b~[~w~"..Keys[opts[i].key].."~b~] ~w~"..opts[i].label
end
TextTargets[data[1]] = { coords = data[2], buttontext = tempText, options = opts, dist = dist }
end
return data[1]
elseif isStarted(OXTargetExport) then
debugPrint("^6Bridge^7: ^2Creating new ^3Sphere^2 target with ^6"..OXTargetExport.." ^7"..data[1])
local options = {}
for i = 1, #opts do
options[i] = {
icon = opts[i].icon,
label = opts[i].label,
item = opts[i].item or nil,
groups = opts[i].job or opts[i].gang,
onSelect = opts[i].onSelect or opts[i].action,
canInteract = function(_, distance)
return distance < dist and true or false
end
}
end
local target = exports[OXTargetExport]:addSphereZone({
coords = data[2],
radius = data[3],
debug = data[4].debugPoly,
options = options
})
circleTargets[#circleTargets+1] = target
return target
elseif isStarted(QBTargetExport) then
debugPrint("^6Bridge^7: ^2Creating new ^3Circle^2 target with ^6"..QBTargetExport.." ^7"..data[1])
local options = { options = opts, distance = dist }
local target = exports[QBTargetExport]:AddCircleZone(data[1], data[2], data[3], data[4], options)
circleTargets[#circleTargets+1] = target
return data[1]
end
end
-- Simple function to remove an entity target created within the script --
--- Removes a previously created entity target.
---
--- This function removes the target associated with the specified entity based on the active targeting system.
---
--- @param entity number The entity ID whose target should be removed.
---
--- @usage
--- removeEntityTarget(entityId)
function removeEntityTarget(entity)
if isStarted(QBTargetExport) then exports[QBTargetExport]:RemoveTargetEntity(entity) end
if isStarted(OXTargetExport) then exports[OXTargetExport]:removeLocalEntity(entity, nil) end
if Config.System.DontUseTarget then TextTargets[entity] = nil end
end
-- Simple function to remove circle or box targets in the script --
--- Removes a previously created zone target.
---
--- This function removes the target associated with the specified zone based on the active targeting system.
---
--- @param target string|table The name identifier or target object of the zone to remove.
---
--- @usage
--- ```lua
--- removeZoneTarget('centralPark')
--- removeZoneTarget(targetObject)
--- ```
function removeZoneTarget(target)
if isStarted(QBTargetExport) then exports[QBTargetExport]:RemoveZone(target) end
if isStarted(OXTargetExport) then exports[OXTargetExport]:removeZone(target, true) end
if Config.System.DontUseTarget then TextTargets[target] = nil end
end
-- If no target script is found, default to DrawText3D targets -- * experimental *
if Config.System.DontUseTarget and not isServer() then
CreateThread(function()
while true do
local pedCoords = GetEntityCoords(PlayerPedId())
local camCoords = GetGameplayCamCoord()
local camRotation = GetGameplayCamRot(2) -- Get camera rotation in degrees
local camForwardVector = RotationToDirection(camRotation) -- Convert rotation to direction vector
local closestTarget = nil
local closestDist = math.huge
for k, v in pairs(TextTargets) do
local targetCoords = v.coords
local dist = #(pedCoords - targetCoords)
local vecToTarget = targetCoords - camCoords
-- Normalize the vector to the target
local vecToTargetNormalized = normalizeVector(vecToTarget)
-- Dot product to check if facing the target
local dot = camForwardVector.x * vecToTargetNormalized.x + camForwardVector.y * vecToTargetNormalized.y + camForwardVector.z * vecToTargetNormalized.z
local isFacingTarget = dot > 0.5 -- Adjust threshold as needed
if dist <= v.dist and isFacingTarget then
if dist < closestDist then
closestDist = dist
closestTarget = v
end
end
end
for k, v in pairs(TextTargets) do
local isClosest = (v == closestTarget)
if #(pedCoords - v.coords) <= v.dist then
for i = 1, #v.options do
if IsControlJustPressed(0, v.options[i].key) and isClosest then
if v.options[i].onSelect then v.options[i].onSelect() end
if v.options[i].action then v.options[i].action() end
end
end
DrawText3D(vec3(v.coords.x, v.coords.y, v.coords.z + 0.7), concatenateText(v.buttontext), isClosest)
end
end
Wait(0)
end
end)
end
-- If the current loaded script is stopped, automatically remove targets --
onResourceStop(function()
for i = 1, #targetEntities do
if isStarted(OXTargetExport) then exports[OXTargetExport]:removeLocalEntity(targetEntities[i], nil)
elseif isStarted(QBTargetExport) then exports[QBTargetExport]:RemoveTargetEntity(targetEntities[i]) end
end
for i = 1, #boxTargets do
if isStarted(OXTargetExport) then exports[OXTargetExport]:removeZone(boxTargets[i], true)
elseif isStarted(QBTargetExport) then exports[QBTargetExport]:RemoveZone(boxTargets[i].name) end
end
for i = 1, #circleTargets do
if isStarted(OXTargetExport) then exports[OXTargetExport]:removeZone(circleTargets[i], true)
elseif isStarted(QBTargetExport) then exports[QBTargetExport]:RemoveZone(circleTargets[i].name) end
end
end, true)

250
shared/vehicles.lua Normal file
View File

@@ -0,0 +1,250 @@
-- Get Vehicle Info --
local lastCar = nil
local carInfo = {}
--- Searches the 'Vehicles' table for a specific vehicle's 'name', 'price', and 'class'.
---
--- This function checks if the provided vehicle is different from the last searched vehicle.
--- If it's a new vehicle, it retrieves its model and searches the 'Vehicles' table for matching entries.
--- It populates the `carInfo` table with the vehicle's name, price, and class.
--- If the vehicle is not found in the table, it defaults to using the vehicle's display name and sets the price to 0.
---
---@param vehicle number The entity ID of the vehicle to search for.
---
---@return table|nil table containing the vehicle's `name`, `price`, and `class`, or `nil` if the vehicle is invalid.
---
---@usage
--- ```lua
--- local info = searchCar(vehicleEntity)
--- print(info.name, info.price, info.class)
--- ```
function searchCar(vehicle)
if lastCar ~= vehicle then -- If same car, use previous info
lastCar = vehicle
carInfo = {}
local model = GetEntityModel(vehicle)
local classlist = {
"Compacts", --1
"Sedans", --2
"SUVs", --3
"Coupes", --4
"Muscle", --5
"Sports Classics", --6
"Sports", --7
"Super", --8
"Motorcycles", --9
"Off-road", --10
"Industrial", --11
"Utility", --12
"Vans", --13
"Cycles", --14
"Boats", --15
"Helicopters", --16
"Planes", --17
"Service", --18
"Emergency", --19
"Military", --20
"Commercial", --21
"Trains", --22
}
if Vehicles then
for k, v in pairs(Vehicles) do
if tonumber(v.hash) == model or GetHashKey(v.hash) == model or GetHashKey(v.model) == model then
debugPrint("^6Bridge^7: ^2Vehicle info found in^7 ^4Vehicles^7 ^2table^7: ^6"..(v.hash and v.hash or v.model).. " ^7(^6"..Vehicles[k].name.."^7)")
carInfo.name = Vehicles[k].name.." "..Vehicles[k].brand
carInfo.price = Vehicles[k].price
carInfo.class = classlist[GetVehicleClass(vehicle) + 1], GetVehicleClass(vehicle)
break
end
end
if not carInfo.name then
debugPrint("^6Bridge^7: ^2Vehicle ^1not ^2found in ^4vehicles^7 ^2table^7: ^6"..model.." ^7(^6"..GetDisplayNameFromVehicleModel(model):lower().."^7)")
carInfo.name = string.upper(GetMakeNameFromVehicleModel(model).." "..GetDisplayNameFromVehicleModel(model))
carInfo.price = 0
carInfo.class = classlist[GetVehicleClass(vehicle) + 1], GetVehicleClass(vehicle)
end
return carInfo
else
if not carInfo.name then
debugPrint("^6Bridge^7: ^2Vehicle ^1not ^2found in ^4vehicles^7 ^2table^7: ^6"..model.." ^7(^6"..GetDisplayNameFromVehicleModel(model):lower().."^7)")
carInfo.name = string.upper(GetMakeNameFromVehicleModel(model).." "..GetDisplayNameFromVehicleModel(model))
carInfo.price = 0
carInfo.class = classlist[GetVehicleClass(vehicle) + 1], GetVehicleClass(vehicle)
end
end
else
return carInfo
end
end
-- Vehicle Properties --
--- Retrieves the properties of a given vehicle.
---
--- This function fetches the vehicle's properties based on the active framework (QBCore or ox).
--- It utilizes the framework's native functions or events to obtain the vehicle's mod list and other details.
---
--- @param vehicle number The entity ID of the vehicle.
---
--- @return table|nil table containing the vehicle's properties, or `nil` if the vehicle is invalid or the framework is not detected.
---
--- @usage
--- ```lua
--- local props = getVehicleProperties(vehicleEntity)
--- if props then
--- -- Manipulate vehicle properties
--- end
--- ```
function getVehicleProperties(vehicle)
local properties = {}
if vehicle == nil then return nil end
if isStarted(QBExport) and not isStarted(QBXExport) then
properties = Core.Functions.GetVehicleProperties(vehicle)
debugPrint("^6Bridge^7: ^2Getting Vehicle Properties ^7[^6"..QBExport.."^7] - [^3"..vehicle.."^7] - [^3"..GetEntityModel(vehicle).."^7/^3"..properties.model.."^7] - [^3"..properties.plate.."^7]")
elseif isStarted(OXLibExport) then
properties = lib.getVehicleProperties(vehicle)
debugPrint("^6Bridge^7: ^2Getting Vehicle Properties ^7[^6"..OXLibExport.."^7] - [^3"..vehicle.."^7] - [^3"..GetEntityModel(vehicle).."^7/^3"..properties.model.."^7] - [^3"..properties.plate.."^7]")
end
return properties
end
--- Sets the properties of a given vehicle.
---
--- This function applies the provided properties to the vehicle using the active framework's functions or events.
--- It first retrieves the current properties and checks for differences before applying the new ones.
---
---@param vehicle number The entity ID of the vehicle.
---@param props table The properties to set on the vehicle.
---
---@usage
--- ```lua
--- setVehicleProperties(vehicleEntity, newProperties)
--- ```
function setVehicleProperties(vehicle, props)
local oldProps = getVehicleProperties(vehicle)
if checkDifferences(vehicle, props) then
--if debugMode then debugDifferences(vehicle, props) end
if not DoesEntityExist(vehicle) then
print(("Unable to set vehicle properties for '%s' (entity does not exist)"):format(vehicle))
end
if isStarted(QBExport) and not isStarted(QBXExport) then
Core.Functions.SetVehicleProperties(vehicle, props)
debugPrint("^6Bridge^7: ^2Setting Vehicle Properties ^7[^6"..QBExport.."^7] - [^3"..vehicle.."^7] - [^3"..GetEntityModel(vehicle).."^7/^3"..props.model.."^7] - [^3"..props.plate.."^7]")
else
TriggerServerEvent(getScript()..":ox:setVehicleProperties", VehToNet(vehicle), props)
end
else
debugPrint("^6Bridge^7: ^2No Changes Found ^7 [^3"..vehicle.."^7] - [^3"..GetEntityModel(vehicle).."^7/^3"..props.model.."^7] - [^3"..props.plate.."^7]")
end
end
--- Checks for differences between the current and new vehicle properties.
---
--- This function compares each property of the vehicle to determine if any changes have been made.
--- It logs the differences for debugging purposes.
---
---@param vehicle number The entity ID of the vehicle.
---@param newProps table The new properties to compare against the current ones.
---
---@return boolean `true` if differences are found, `false` otherwise.
---
---@usage
--- ```lua
--- if checkDifferences(vehicleEntity, newProperties) then
--- setVehicleProperties(vehicleEntity, newProperties)
--- end
--- ```
function checkDifferences(vehicle, newProps)
local oldProps = getVehicleProperties(vehicle)
debugPrint("^6Bridge^7: ^2Finding differences in ^3Vehicle Properties^7")
local allow = false
for k in pairs(oldProps) do
if json.encode(oldProps[k]) ~= json.encode(newProps[k]) then
allow = true
debugPrint("^6Bridge^7: ^5Old ^7[^3"..k.."^7] - "..json.encode(oldProps[k], { indent = true }))
debugPrint("^6Bridge^7: ^5New ^7[^3"..k.."^7] - "..json.encode(newProps[k], { indent = true }))
end
end
return allow
end
--- Handles setting vehicle properties received from the server.
---
--- This event listens for the `ox:setVehicleProperties` event and applies the received properties to the vehicle.
---
---@event
---@param netId number The network ID of the vehicle.
---@param props table The properties to set on the vehicle.
---
---@usage
--- -- Server-side: TriggerClientEvent(getScript()..":ox:setVehicleProperties", netId, properties)
RegisterNetEvent(getScript()..":ox:setVehicleProperties", function(netId, props)
local vehicle = NetworkGetEntityFromNetworkId(netId)
local value = props
Entity(vehicle).state[getScript()..':setVehicleProperties'] = value
end)
--- Handles state bag changes for setting vehicle properties.
---
--- This handler listens for changes to the vehicle's state bag and applies the new properties accordingly.
---
---@param bagName string The name of the state bag.
---@param key string The key that changed.
---@param value table The new value of the state.
---
---@usage
--- -- Automatically handled when the state bag changes
AddStateBagChangeHandler(getScript()..':setVehicleProperties', '', function(bagName, _, value)
if not value or not GetEntityFromStateBagName then return end
local entity = GetEntityFromStateBagName(bagName)
local networked = not bagName:find('localEntity')
debugPrint("^6Bridge^7: ^2Setting Vehicle Properties ^7[^6"..OXLibExport.."^7] - [^3"..entity.."^7] - [^3"..GetEntityModel(entity).."^7] - [^3"..value.plate.."^7]")
if networked and NetworkGetEntityOwner(entity) ~= cache.playerId then return end
if lib.setVehicleProperties(entity, value) then
Entity(entity).state:set('setVehicleProperties', nil, true)
end
end)
--- Pushes a vehicle to other players by syncing it.
---
--- This function ensures that the vehicle is controlled by the current player and is set as a mission entity.
--- It requests network control and sets the vehicle accordingly to synchronize changes across clients.
---
---@param entity number The entity ID of the vehicle to push.
---
---@usage
--- ```lua
--- pushVehicle(vehicleEntity)
--- ```
function pushVehicle(entity)
SetVehicleModKit(entity, 0)
if entity ~= 0 and DoesEntityExist(entity) then
if not NetworkHasControlOfEntity(entity) then
debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Requesting network control of vehicle^7.")
NetworkRequestControlOfEntity(entity)
local timeout = 2000
while timeout > 0 and not NetworkHasControlOfEntity(entity) do
Wait(100)
timeout = timeout - 100
end
if NetworkHasControlOfEntity(entity) then
debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Network has control of entity^7.")
end
end
if not IsEntityAMissionEntity(entity) then
debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Setting vehicle as a ^7'^2mission^7' &2entity^7.")
SetEntityAsMissionEntity(entity, true, true)
local timeout = 2000
while timeout > 0 and not IsEntityAMissionEntity(entity) do
Wait(100)
timeout = timeout - 100
end
if IsEntityAMissionEntity(entity) then
debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Vehicle is a ^7'^2mission^7'^2 entity^7.")
end
end
end
end

47
shared/versioncheck.lua Normal file
View File

@@ -0,0 +1,47 @@
-- Version check for jim_bridge --
function CheckBridgeVersion()
if isServer() then
local currentVersion = "^3"..GetResourceMetadata("jim_bridge", 'version'):gsub("%.", "^7.^3").."^7"
PerformHttpRequest('https://raw.githubusercontent.com/jimathy/jim_bridge/master/version.txt', function(err, newestVersion, headers)
if not newestVersion then print("^1Currently unable to run a version check for ^7'^3jim_bridge^7' ("..currentVersion.."^7)") return end
newestVersion = "^3"..newestVersion:sub(1, -2):gsub("%.", "^7.^3"):gsub("%\r", "").."^7"
print(newestVersion == currentVersion and "^7'^3jim_bridge^7' - ^6You are running the latest version.^7 ("..currentVersion..")" or "^7'^3jim_bridge^7' - ^1You are currently running an outdated version^7, ^1please update^7!")
end)
end
end
--CheckBridgeVersion()
-- Print Script names
function capitalize(str)
return (str:gsub("^%l", string.upper):gsub("%-%l", function(letter) return "-" .. string.upper(letter:sub(2)) end))
end
local scriptName = ("^2"..capitalize(getScript()):gsub("%-", "^7-^2"):gsub("%_", "^7_^2")) or ""
local scriptVersion = ("^5"..GetResourceMetadata(getScript(), 'version', nil):gsub("%.", "^7.^5")) or ""
local scriptDescription = ("^2"..GetResourceMetadata(getScript(), 'description', nil)) or ""
local scriptAuthor = ("^2by ^4"..GetResourceMetadata(getScript(), 'author', nil):gsub("%and", "^7and^4")) or ""
print(scriptName.." ^7v"..scriptVersion.."^7 - "..scriptDescription.." "..scriptAuthor.."^7")
-- Loaded script Version Check, requires CheckVersion() to be placed in a server file
function CheckVersion()
if isServer() then
local currentVersion = "^3"..GetResourceMetadata(getScript(), 'version'):gsub("%.", "^7.^3").."^7"
PerformHttpRequest('https://raw.githubusercontent.com/jimathy/UpdateVersions/master/'..getScript()..'.txt', function(err, newestVersion, headers)
if not newestVersion then
PerformHttpRequest('https://raw.githubusercontent.com/jimathy/'..getScript()..'/master/version.txt', function(err, freeVersion, headers)
if not freeVersion then print("^1Currently unable to run a version check for ^7'^3"..getScript().."^7' ("..currentVersion.."^7)") return end
local currentVersion = "^3"..GetResourceMetadata(getScript(), 'version'):gsub("%.", "^7.^3").."^7"
freeVersion = "^3"..freeVersion:sub(1, -2):gsub("%.", "^7.^3"):gsub("%\r", "").."^7"
print("^6Version Check^7: ^2Running^7: "..currentVersion.." ^2Latest^7: "..freeVersion)
print(freeVersion == currentVersion and "^7'^3"..getScript().."^7' - ^6You are running the latest version.^7 ("..currentVersion..")" or "^7'^3"..getScript().."^7' - ^1You are currently running an outdated version^7, ^1please update^7!")
end)
else
newestVersion = "^3"..newestVersion:sub(1, -2):gsub("%.", "^7.^3"):gsub("%\r", "").."^7"
print("^6Version Check^7: ^2Running^7: "..currentVersion.." ^2Latest^7: "..newestVersion)
print(newestVersion == currentVersion and '^6You are running the latest version.^7 ('..currentVersion..')' or "^1You are currently running an outdated version^7, ^1please update^7!")
end
end)
end
end
--CheckVersion()

260
shared/wrapperfunctions.lua Normal file
View File

@@ -0,0 +1,260 @@
-- Phone Mails
--- Sends a phone mail using the detected phone system.
---
--- This function detects the active phone resource (e.g., gksphone, yflip-phone, qb-phone, etc.)
--- and sends a mail using the appropriate method for that phone system.
---
--- @param data table A table containing the mail data.
--- - **subject** (`string`): The subject of the email.
--- - **sender** (`string`): The sender of the email.
--- - **message** (`string`): The body content of the email.
--- - **actions** (`table|nil`): (Optional) Action buttons associated with the email.
---
--- @usage
--- ```lua
--- sendPhoneMail({
--- subject = "Welcome!",
--- sender = "Admin",
--- message = "Thank you for joining our server.",
--- actions = {
--- { label = "Reply", action = replyFunction }
--- }
--- })
--- ```
function sendPhoneMail(data) local phoneResource = ""
if isStarted("gksphone") then phoneResource = "gksphone"
exports["gksphone"]:SendNewMail(data)
elseif isStarted("yflip-phone") then phoneResource = "yflip-phone"
TriggerServerEvent(getScript()..":yflip:SendMail", data)
elseif isStarted("qs-smartphone") then phoneResource = "qs-smartphone"
TriggerServerEvent('qs-smartphone:server:sendNewMail', data)
elseif isStarted("qs-smartphone-pro") then phoneResource = "qs-smartphone-pro"
TriggerServerEvent('phone:sendNewMail', data)
elseif isStarted("roadphone") then phoneResource = "roadphone"
data.message = data.message:gsub("%<br>", "\n")
exports['roadphone']:sendMail(data)
elseif isStarted("lb-phone") then phoneResource = "lb-phone"
data.message = data.message:gsub("%<br>", "\n")
TriggerServerEvent(getScript()..":lbphone:SendMail", data)
elseif isStarted("qb-phone") then phoneResource = "qb-phone"
TriggerServerEvent('qb-phone:server:sendNewMail', data)
elseif isStarted("jpr-phonesystem") then phoneResource = "jpr-phonesystem"
TriggerServerEvent(getScript()..":jpr:SendMail", data)
end
if phoneResource ~= "" then debugPrint("^6Bridge^7[^3"..phoneResource.."^7]: ^2Sending mail to player")
else print("^6Bridge^7: ^1ERROR ^2Sending mail to player ^7 - ^2No supported phone found") end
end
--- Handles sending mail for lb-phone.
---
--- This event listens for the `lbphone:SendMail` event and sends an email using lb-phone's API.
---
--- @event
--- @param data table The mail data.
--- - **subject** (`string`): The subject of the email.
--- - **message** (`string`): The body content of the email.
--- - **buttons** (`table|nil`): (Optional) Action buttons associated with the email.
---
--- @usage
--- ```
--- -- Server-side:
--- TriggerClientEvent(getScript()..":lbphone:SendMail", data)
--- ```
RegisterNetEvent(getScript()..":lbphone:SendMail", function(data)
local src = source
local phoneNumber = exports["lb-phone"]:GetEquippedPhoneNumber(src)
local emailAddress = exports["lb-phone"]:GetEmailAddress(phoneNumber)
if data.actions then data.buttons = data.actions end
exports["lb-phone"]:SendMail({
to = emailAddress,
subject = data.subject,
message = data.message,
actions = data.buttons,
})
end)
--- Handles sending mail for yflip-phone.
---
--- This event listens for the `yflip:SendMail` event and sends an email using yflip-phone's API.
---
--- @event
--- @param data table The mail data.
--- - **subject** (`string`): The subject of the email.
--- - **sender** (`string`): The sender of the email.
--- - **message** (`string`): The body content of the email.
--- - **buttons** (`table|nil`): (Optional) Action buttons associated with the email.
---
--- @usage
--- ```lua
--- -- Server-side:
--- TriggerClientEvent(getScript()..":yflip:SendMail", data)
--- ```
RegisterNetEvent(getScript()..":yflip:SendMail", function(data)
local src = source
exports["yflip-phone"]:SendMail({
title = data.subject,
sender = data.sender,
senderDisplayName = data.sender,
content = data.message,
actions = data.buttons,
}, 'source', src)
end)
--- Handles sending mail for jpr-phonesystem.
---
--- This event listens for the `jpr:SendMail` event and sends an email using jpr-phonesystem's API.
---
--- @event
--- @param data table The mail data.
--- - **subject** (`string`): The subject of the email.
--- - **sender** (`string`): The sender of the email.
--- - **message** (`string`): The body content of the email.
--- - **buttons** (`table|nil`): (Optional) Action buttons associated with the email.
---
--- @return void
---
--- @usage
--- ```lua
--- -- Server-side:
--- TriggerClientEvent(getScript()..":jpr:SendMail", data)
--- ```
RegisterNetEvent(getScript()..":jpr:SendMail", function(data)
local src = source
local Player = Core.Functions.GetPlayer(src)
TriggerEvent('jpr-phonesystem:server:sendEmail', {
Assunto = data.subject, -- Subject
Conteudo = data.message, -- Content
Enviado = data.sender, -- Submitted by
Destinatario = Player.PlayerData.citizenid, -- Target
Event = {}, -- Optional
})
end)
-- Server-Side Functions for Registering Commands, Stashes, and Shops
--- Registers a command with the active command system.
---
--- This function detects whether the server is using OXLib or qb-core for command registration
--- and registers the command accordingly.
---
--- @param command string The name of the command to register.
--- @param options table A table containing command options.
--- - **help** (`string`): The help description for the command.
--- - **params** (`table`): A table of parameters for the command.
--- - **callback** (`function`): The function to execute when the command is called.
--- - **autocomplete** (`function|nil`): (Optional) A function for autocompletion.
--- - **restrictedGroup** (`string|nil`): (Optional) The user group required to execute the command.
---
--- @usage
--- ````lua
--- -- Server Side:
--- registerCommand("greet", {
--- "Greets the player",
--- { name = "name", help = "Name of the player to greet" },
--- function(source, args) print("Hello, " .. args[1] .. "!") end,
--- nil,
--- "admin"
--- })
--- ```
function registerCommand(command, options)
if isStarted(OXLibExport) then
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7"..OXLibExport, command)
lib.addCommand(command, { help = options[1], restricted = options[5] and "group."..options[5] or nil }, options[4])
elseif isStarted(QBExport) and not isStarted(QBXExport) then
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7 "..QBExport, command)
Core.Commands.Add(command, options[1], options[2], options[3], options[4], options[5] and options[5] or nil)
end
end
--- Registers a stash with the active inventory system.
---
--- This function detects whether the server is using OXInv or QSInv and registers the stash accordingly.
---
--- @param name string The unique identifier for the stash.
--- @param label string The display name for the stash.
--- @param slots number|nil (Optional) The number of slots in the stash. Defaults to 50.
--- @param weight number|nil (Optional) The maximum weight the stash can hold. Defaults to 4,000,000.
--- @param owner string|nil (Optional) The owner identifier for personal stashes.
--- @param coords table|nil (Optional) The coordinates for the stash location.
---
--- @usage
--- ```lua
--- registerStash("playerStash", "Player Stash", 100, 8000000, "player123", { x = 100.0, y = 200.0, z = 30.0 })
--- ```
function registerStash(name, label, slots, weight, owner, coords)
if isStarted(OXInv) then
debugPrint("^6Bridge^7: ^2Registering ^3OX ^2Stash^7:", name, label, owner or nil)
exports[OXInv]:RegisterStash(name, label, slots or 50, weight or 4000000, owner or nil)
elseif isStarted(QSInv) then
debugPrint("^6Bridge^7: ^2Registering ^3QS ^2Stash^7:", name, label)
exports[QSInv]:RegisterStash(name, slots or 50, weight or 4000000)
end
end
--- Registers a shop with the active inventory system.
---
--- This function detects whether the server is using OXInv or QBInv and registers the shop accordingly.
---
--- @param name string The unique identifier for the shop.
--- @param label string The display name for the shop.
--- @param items table The list of items available in the shop.
--- @param society string|nil (Optional) The society identifier for shared shops.
---
--- @usage
--- ```lua
--- registerShop("weaponShop", "Weapon Shop", weaponItems, "society_weapons")
--- ```
function registerShop(name, label, items, society)
if isStarted(OXInv) then
debugPrint("^6Bridge^7: ^2Registering ^3OX ^2Store^7:", name, label)
exports[OXInv]:RegisterShop(
name, {
name = label,
inventory = items,
society = society,
}
)
elseif isStarted(QBInv) and QBInvNew then
debugPrint("^6Bridge^7: ^2Registering ^3QB ^2Store^7:", name, label)
print(json.encode(items, {indent = true}))
exports[QBInv]:CreateShop({
name = name,
label = label,
slots = #items,
items = items,
society = society,
})
end
end
-- Server-Side Event Registration
if isServer() then
--- Registers an event to create an OX stash from the server.
---
--- @event
--- @param name string The unique identifier for the stash.
--- @param label string The display name for the stash.
--- @param slots number|nil (Optional) The number of slots in the stash.
--- @param weight number|nil (Optional) The maximum weight the stash can hold.
--- @param owner string|nil (Optional) The owner identifier for personal stashes.
--- @param coords table|nil (Optional) The coordinates for the stash location.
---
--- @usage
--- ```lua
--- -- Server-side:
--- TriggerEvent(getScript()..":server:makeOXStash", name, label, slots, weight, owner, coords)
--- ```
RegisterNetEvent(getScript()..":server:makeOXStash", function(name, label, slots, weight, owner, coords)
registerStash(name, label, slots, weight, owner, coords)
end)
end

89
starter.lua Normal file
View File

@@ -0,0 +1,89 @@
Exports = {
QBExport = "qb-core",
QBXExport = "qbx_core",
ESXExport = "es_extended",
OXCoreExport = "ox_core",
OXInv = "ox_inventory",
QBInv = "qb-inventory",
PSInv = "ps-inventory",
QSInv = "qs-inventory",
CoreInv = "core_inventory",
CodeMInv = "codem-inventory",
OrigenInv = "origen_inventory",
OXLibExport = "ox_lib",
QBMenuExport = "qb-menu",
QBTargetExport = "qb-target",
OXTargetExport = "ox_target"
}
-- Required variables
debugMode = Config.System.Debug
QBInvNew = true
InventoryWeight = 120000
-- Load files here into the invoking script
for _, v in pairs({ -- This is a specific load order
'helpers.lua', -- needs to be first
'_loaders.lua',
'_eventDebug.lua',
'coreloader.lua', -- needs to be second to load all core related stuff before everything else
'callback.lua',
'duifunctions.lua',
-- Native Scaleforms
'scaleforms/bigMessageInstance.lua',
'scaleforms/countDownHandler.lua',
'scaleforms/debugScaleform.lua',
'scaleforms/instructionalButtons.lua',
'scaleforms/timerBars.lua',
-- Required functions
'make/loaders.lua',
'make/makeBlip.lua',
'make/makePed.lua',
'make/makeProp.lua',
'make/makeVeh.lua',
'make/cameras.lua',
'make/progressBars.lua',
'wrapperfunctions.lua',
'polyZone.lua',
'itemcontrol.lua',
'playerfunctions.lua',
'jobfunctions.lua',
-- Interactions
'targets.lua',
'contextmenus.lua',
'input.lua',
'notify.lua',
'drawText.lua',
-- Crafting / Shops / Stashes
'crafting.lua',
'stashcontrol.lua',
-- Kind of "other"
'isAnimal.lua',
'scaleEntity.lua',
'vehicles.lua',
'effects.lua',
'versioncheck.lua'
}) do
if debugMode then
print("^5Loading^7: 'jim_bridge/shared/"..v.."' ^2into ^7'"..GetCurrentResourceName().."' ...")
end
local fileLoader = assert(load(LoadResourceFile('jim_bridge', ('shared/'..v)), ('@@jim_bridge/shared/'..v)))
fileLoader()
if debugMode then
print("^5Success^7: ^2loaded file^7: 'jim_bridge/shared/"..v.."'!")
end
end

View File

@@ -1 +1 @@
1.0
1.2

File diff suppressed because it is too large Load Diff