Files
Elite-Gaming-FiveM/resources/Supply-Line/cl_utils.lua
T
KingMcDonalds 765aa151a3 fixing vehicles
2026-03-13 23:31:07 -07:00

591 lines
28 KiB
Lua

vRP = nil
ESX = nil
if Config.Notifications.Enabled and Config.Notifications.Framework.vRP then
local Tunnel = module("vrp", "lib/Tunnel")
local Proxy = module("vrp", "lib/Proxy")
vRP = Proxy.getInterface("vRP")
end
if Config.Notifications.Enabled and Config.Notifications.Framework.ESX then
ESX = exports["es_extended"]:getSharedObject()
end
if Config.PlaceGeneratorCommand.Inventory.quasarInventory or Config.PlaceGeneratorCommand.Inventory.coreInventory then
function inventorySetupGenerator()
TriggerEvent("SupplyLine:Client:EquipPortableGenerator")
end
exports("inventorySetupGenerator", inventorySetupGenerator)
end
if Config.EnablePositioningCommand then
TriggerEvent('chat:addSuggestion', '/'.."findhydrantpositioning", "Find rope spawn point and rotation for a hydrant", {
{ name="type", help="supplyLine or lppLine" }
})
RegisterCommand("findhydrantpositioning", function(source, args)
local ropeLabel = args[1] or "supplyLine"
local ped = PlayerPedId()
local pedCoords = GetEntityCoords(ped)
local targetHydrant = 0
local ropeType = 4
if HydrantRopes and HydrantRopes.RopeTypes and HydrantRopes.RopeTypes[ropeLabel] then
ropeType = HydrantRopes.RopeTypes[ropeLabel]
end
-- Add your server's custom hydrant models to this list if you have them!
local hydrantModels = {
`prop_fire_hydrant_1`,
`prop_fire_hydrant_2`,
`prop_fire_hydrant_4`,
`bv_water_pump`
}
for _, model in ipairs(hydrantModels) do
local obj = GetClosestObjectOfType(pedCoords.x, pedCoords.y, pedCoords.z, 5.0, model, false, false, false)
if obj ~= 0 then
targetHydrant = obj
break
end
end
if targetHydrant == 0 then return Notify("No hydrant found nearby! Stand closer.") end
RopeLoadTextures()
local hydPos = GetEntityCoords(targetHydrant)
local previewRope = AddRope(hydPos.x, hydPos.y, hydPos.z, 0.0, 0.0, 0.0, 20.0, ropeType, 20.0, 0.5, 1.0, false, false, false, 5.0, false, 0)
local offSet = {x = 0.0, y = 0.0, z = 0.0}
local rotation = {x = 0.0, y = 0.0, z = 0.0}
local depth = 0.2
local mode = "MOVE"
local editing = true
Notify("~b~POSITIONING MODE~w~\n[TAB] Toggle Move/Rotate\n[Arrows] Adjust\n[INSERT/DELETE] Depth\n[ENTER] Save")
SetEntityAlpha(targetHydrant, 150, false)
local function RotationToDirection(rot)
local radZ = math.rad(rot.z)
local radX = math.rad(rot.x)
local num = math.abs(math.cos(radX))
return vector3(-math.sin(radZ) * num, math.cos(radZ) * num, math.sin(radX))
end
while editing do
Wait(0)
local speed = IsControlPressed(0, 21) and 0.05 or 0.005
if IsControlJustPressed(0, 192) then
mode = (mode == "MOVE") and "ROTATE" or "MOVE"
Notify("Mode: ~y~" .. mode)
end
if IsControlPressed(0, 121) then depth = depth + 0.005 end
if IsControlPressed(0, 178) then depth = depth - 0.005 end
local up = IsControlPressed(0, Config.PositioningControls.up)
local down = IsControlPressed(0, Config.PositioningControls.down)
local forward = IsControlPressed(0, Config.PositioningControls.forwards)
local backward = IsControlPressed(0, Config.PositioningControls.backwards)
local left = IsControlPressed(0, Config.PositioningControls.left)
local right = IsControlPressed(0, Config.PositioningControls.right)
if mode == "MOVE" then
if up then offSet.z = offSet.z + speed end
if down then offSet.z = offSet.z - speed end
if forward then offSet.y = offSet.y + speed end
if backward then offSet.y = offSet.y - speed end
if left then offSet.x = offSet.x - speed end
if right then offSet.x = offSet.x + speed end
else
local rSpeed = speed * 50.0
if up then rotation.x = rotation.x + rSpeed end
if down then rotation.x = rotation.x - rSpeed end
if left then rotation.z = rotation.z + rSpeed end
if right then rotation.z = rotation.z - rSpeed end
end
local forwardVec = RotationToDirection(rotation)
local localPortPos = vector3(offSet.x, offSet.y, offSet.z)
local localBendPosA = localPortPos - (forwardVec * depth)
local localBendPosB = localPortPos - (forwardVec * (depth * 0.5))
local worldPointC = GetOffsetFromEntityInWorldCoords(targetHydrant, localPortPos.x, localPortPos.y, localPortPos.z)
local worldPointA = GetOffsetFromEntityInWorldCoords(targetHydrant, localBendPosA.x, localBendPosA.y, localBendPosA.z)
local worldPointB = GetOffsetFromEntityInWorldCoords(targetHydrant, localBendPosB.x, localBendPosB.y, localBendPosB.z)
local handPos = GetWorldPositionOfEntityBone(ped, GetPedBoneIndex(ped, 6286))
local vertexCount = GetRopeVertexCount(previewRope)
PinRopeVertex(previewRope, 0, handPos.x, handPos.y, handPos.z)
PinRopeVertex(previewRope, vertexCount - 1, worldPointA.x, worldPointA.y, worldPointA.z)
PinRopeVertex(previewRope, vertexCount - 2, worldPointB.x, worldPointB.y, worldPointB.z)
PinRopeVertex(previewRope, vertexCount - 3, worldPointC.x, worldPointC.y, worldPointC.z)
if IsControlJustPressed(0, Config.PositioningControls.enter) then editing = false end
end
ResetEntityAlpha(targetHydrant)
print(string.format("^2--- %s OFFSET RESULT ---^7", ropeLabel:upper()))
print(string.format("{ x = %.3f, y = %.3f, z = %.3f, rx = %.3f, ry = %.3f, rz = %.3f, depth = %.3f },", offSet.x, offSet.y, offSet.z, rotation.x, rotation.y, rotation.z, depth))
DeleteRope(previewRope)
Notify("Data printed to F8 console.")
end, false)
end
function Notify(text)
if not Config.Notifications.Enabled then
return
end
if Config.Notifications.Framework.ESX then
if ESX ~= nil then
ESX.ShowNotification(text)
end
elseif Config.Notifications.Framework.QBCore then
TriggerEvent('QBCore:Notify', text, 'primary')
elseif Config.Notifications.Framework.QBX then
exports.qbx_core:Notify(text, 'primary')
elseif Config.Notifications.Framework.vRP then
vRP.notify(source, {text})
elseif Config.Notifications.Framework.okok then
exports['okokNotify']:Alert("Supply Line", text, 2000, 'info', true)
else
showBaseNotification(text)
end
end
RegisterNetEvent('SupplyLine:Client:showNotification', function(message)
Notify(message)
end)
function showBaseNotification(message)
-- Base game notifications
SetNotificationTextEntry("STRING")
AddTextComponentString(message)
DrawNotification(0,1)
end
function DisplayHelpText(text)
SetTextComponentFormat("STRING")
AddTextComponentString(text)
DisplayHelpTextFromStringLabel(0, 0, ConfigHose.Notifications.HelpTextSound, -1)
end
function drawInstructionalText(msg, coords)
AddTextEntry('instructionalText', msg)
SetFloatingHelpTextWorldPosition(1, coords.x, coords.y, coords.z)
SetFloatingHelpTextStyle(1, 1, 2, -1, 3, 0)
BeginTextCommandDisplayHelp('instructionalText')
AddTextComponentSubstringPlayerName(msg)
EndTextCommandDisplayHelp(2, false, false, -1)
end
function DrawTextOnScreen(text, x, y, scale, r, g, b, a, center)
SetTextFont(0)
SetTextProportional(1)
SetTextScale(scale, scale)
SetTextColour(r, g, b, a)
SetTextDropshadow(0, 0, 0, 0, 255)
SetTextEdge(2, 0, 0, 0, 150)
SetTextDropShadow()
SetTextOutline()
SetTextEntry("STRING")
if center then
SetTextCentre(1)
end
AddTextComponentString(text)
DrawText(x, y)
end
local hydrantInteractionId = 'hydrantInteraction'
local hydrantInteractionActive = false
if Config.InteractType.HydrantInteraction.Drawtext or Config.InteractType.HydrantInteraction.ScreenPrompt then
StartHoseWaterLevelDisplayThread()
CreateThread(function()
while true do
local waitTime = 2500
local player = GetPlayerPed(PlayerId())
local playerPos = GetEntityCoords(player, false)
local fillingVehicle = nil
if not IsPedInAnyVehicle(player, false) then
local closestHydrant = GetClosestHydrant(playerPos)
if closestHydrant and DoesEntityExist(closestHydrant) then
if not NetworkGetEntityIsNetworked(closestHydrant) then
NetworkRegisterEntityAsNetworked(closestHydrant)
end
local hydrantId = NetworkGetNetworkIdFromEntity(closestHydrant)
if NetworkDoesNetworkIdExist(hydrantId) then
local hydrantPos = GetEntityCoords(closestHydrant)
local rope = HydrantRopes.currentRopes[hydrantId]
fillingVehicle = HydrantManager.fillingVehicles[hydrantId]
if rope then
if fillingVehicle then
if Config.InteractType.HydrantInteraction.Drawtext then
drawInstructionalText(Config.Translations.stopFilling, vec3(hydrantPos.x, hydrantPos.y, hydrantPos.z + 1.0))
elseif Config.InteractType.HydrantInteraction.ScreenPrompt then
DisplayHelpText(Config.Translations.stopFilling)
end
else
if Config.InteractType.HydrantInteraction.Drawtext then
drawInstructionalText(Config.Translations.startFilling, vec3(hydrantPos.x, hydrantPos.y, hydrantPos.z + 1.0))
elseif Config.InteractType.HydrantInteraction.ScreenPrompt then
DisplayHelpText(Config.Translations.startFilling)
end
end
else
if HoseBridge.isLineEquipped and Config.InteractType.HydrantInteraction.Drawtext then
drawInstructionalText(Config.Translations.connectLine, vec3(hydrantPos.x, hydrantPos.y, hydrantPos.z + 1.0))
elseif Config.InteractType.HydrantInteraction.ScreenPrompt then
DisplayHelpText(Config.Translations.connectLine)
end
end
waitTime = 1
if IsControlJustPressed(1, Config.SupplyLineKeys.ConnectHydrant) and isPermissionAllowed() then
HandleHydrantInteraction(hydrantId, hydrantPos)
TriggerEvent('SmartHose:Client:Bridge:DeleteHandRopeS')
end
if IsControlJustPressed(1, Config.SupplyLineKeys.StartStopFilling) and rope and isPermissionAllowed() then
local fillingVehicle = HydrantManager.fillingVehicles[hydrantId]
if fillingVehicle then
HandleStartStopFilling(hydrantId, false)
else
HandleStartStopFilling(hydrantId, true)
end
end
end
end
end
Wait(waitTime)
end
end)
elseif Config.InteractType.HydrantInteraction.ThirdEye.enabled then
local hydrantModels = {}
for _, offset in ipairs(Config.HydrantOffsets) do
table.insert(hydrantModels, offset.model)
end
StartHoseWaterLevelDisplayThread()
if Config.InteractType.HydrantInteraction.ThirdEye.oxTarget then
local jobGroups = GetAllowedJobGroups()
local HydrantOptions = {
{
name = 'connect_line',
label = Config.Translations.thirdEyeConnect,
icon = 'fa-solid fa-link',
groups = jobGroups,
canInteract = function(entity, distance, data)
if not HoseBridge.isLineEquipped then return false end
if HasObjectBeenBroken(entity) then return false end
if not NetworkGetEntityIsNetworked(entity) then
NetworkRegisterEntityAsNetworked(entity)
if not NetworkGetEntityIsNetworked(entity) then
return false
end
end
local netId = NetworkGetNetworkIdFromEntity(entity)
local rope = HydrantRopes.currentRopes[netId]
if rope then return false end
return true
end,
onSelect = function(data)
local hydrantEntity = data.entity
HandleHydrantInteractionEntity(hydrantEntity)
TriggerEvent('SmartHose:Client:Bridge:DeleteHandRopeS')
end,
},
{
name = 'start_filling',
label = Config.Translations.thirdEyeStartFill,
icon = 'fa-solid fa-play',
groups = jobGroups,
canInteract = function(entity, distance, data)
if HasObjectBeenBroken(entity) then return false end
if not NetworkGetEntityIsNetworked(entity) then return false end
local hydrantId = NetworkGetNetworkIdFromEntity(entity)
local rope = HydrantRopes.currentRopes[hydrantId]
local fillingVehicle = HydrantManager.fillingVehicles[hydrantId]
return rope and not fillingVehicle
end,
onSelect = function(data)
local hydrantEntity = data.entity
HandleStartFillingEntity(hydrantEntity)
end,
},
{
name = 'stop_filling',
label = Config.Translations.thirdEyeStopFill,
icon = 'fa-solid fa-stop',
groups = jobGroups,
canInteract = function(entity, distance, data)
if HasObjectBeenBroken(entity) then return false end
if not NetworkGetEntityIsNetworked(entity) then return false end
local fillingVehicle = HydrantManager.fillingVehicles[NetworkGetNetworkIdFromEntity(entity)]
return fillingVehicle ~= nil
end,
onSelect = function(data)
local hydrantEntity = data.entity
HandleStopFillingEntity(hydrantEntity)
end,
},
{
name = 'disconnect_line',
label = Config.Translations.thirdEyeDisconnect,
icon = 'fa-solid fa-unlink',
groups = jobGroups,
canInteract = function(entity, distance, data)
if HasObjectBeenBroken(entity) then return false end
if not NetworkGetEntityIsNetworked(entity) then return false end
local rope = HydrantRopes.currentRopes[NetworkGetNetworkIdFromEntity(entity)]
return rope ~= nil
end,
onSelect = function(data)
local hydrantEntity = data.entity
HandleHydrantInteractionEntity(hydrantEntity)
end,
},
}
exports.ox_target:addModel(hydrantModels, HydrantOptions)
elseif Config.InteractType.HydrantInteraction.ThirdEye.qbTarget then
local allowedJobs = GetAllowedJobGroups()
exports['qb-target']:AddTargetModel(hydrantModels, {
options = {
{
label = Config.Translations.thirdEyeConnect,
icon = 'fa-solid fa-link',
job = allowedJobs,
canInteract = function(entity)
if not HoseBridge.isLineEquipped then return false end
if HasObjectBeenBroken(entity) then
return false
end
if not NetworkGetEntityIsNetworked(entity) then
NetworkRegisterEntityAsNetworked(entity)
Wait(0)
end
local rope = HydrantRopes.currentRopes[NetworkGetNetworkIdFromEntity(entity)]
return not rope
end,
action = function(entity)
if not NetworkGetEntityIsNetworked(entity) then
NetworkRegisterEntityAsNetworked(entity)
Wait(0)
end
HandleHydrantInteractionEntity(entity)
TriggerEvent('SmartHose:Client:Bridge:DeleteHandRopeS')
end,
},
{
label = Config.Translations.thirdEyeStartFill,
icon = 'fa-solid fa-play',
job = allowedJobs,
canInteract = function(entity)
if HasObjectBeenBroken(entity) then
return false
end
if not NetworkGetEntityIsNetworked(entity) then
NetworkRegisterEntityAsNetworked(entity)
Wait(0)
end
local hydrantId = NetworkGetNetworkIdFromEntity(entity)
local rope = HydrantRopes.currentRopes[hydrantId]
local fillingVehicle = HydrantManager.fillingVehicles[hydrantId]
return rope and not fillingVehicle
end,
action = function(entity)
if not NetworkGetEntityIsNetworked(entity) then
NetworkRegisterEntityAsNetworked(entity)
Wait(0)
end
HandleStartFillingEntity(entity)
end,
},
{
label = Config.Translations.thirdEyeStopFill,
icon = 'fa-solid fa-stop',
job = allowedJobs,
canInteract = function(entity)
if HasObjectBeenBroken(entity) then
return false
end
if not NetworkGetEntityIsNetworked(entity) then
NetworkRegisterEntityAsNetworked(entity)
Wait(0)
end
local fillingVehicle = HydrantManager.fillingVehicles[NetworkGetNetworkIdFromEntity(entity)]
return fillingVehicle ~= nil
end,
action = function(entity)
if not NetworkGetEntityIsNetworked(entity) then
NetworkRegisterEntityAsNetworked(entity)
Wait(0)
end
HandleStopFillingEntity(entity)
end,
},
{
label = Config.Translations.thirdEyeDisconnect,
icon = 'fa-solid fa-unlink',
job = allowedJobs,
canInteract = function(entity)
if HasObjectBeenBroken(entity) then
return false
end
if not NetworkGetEntityIsNetworked(entity) then
NetworkRegisterEntityAsNetworked(entity)
Wait(0)
end
local rope = HydrantRopes.currentRopes[NetworkGetNetworkIdFromEntity(entity)]
return rope ~= nil
end,
action = function(entity)
if not NetworkGetEntityIsNetworked(entity) then
NetworkRegisterEntityAsNetworked(entity)
Wait(0)
end
HandleHydrantInteractionEntity(entity)
end,
},
},
distance = 2.5,
})
end
elseif Config.InteractType.HydrantInteraction.WorldInteraction then
CreateThread(function()
local idleWait = 1000
local playerPed = PlayerPedId()
local playerPos = GetEntityCoords(playerPed)
while true do
Wait(idleWait)
playerPed = PlayerPedId()
playerPos = GetEntityCoords(playerPed)
if IsPedInAnyVehicle(playerPed, false) then
if hydrantInteractionActive then
for _, hydrantConfig in ipairs(Config.HydrantOffsets) do
exports.interact:RemoveModelInteraction(hydrantConfig.model, hydrantInteractionId)
end
hydrantInteractionActive = false
end
idleWait = 1500
else
local hydrant = GetClosestHydrant(playerPos)
if hydrant and DoesEntityExist(hydrant) and not HasObjectBeenBroken(hydrant) then
local hydrantPos = GetEntityCoords(hydrant)
local distanceToHydrant = #(playerPos - hydrantPos)
if distanceToHydrant < 4.0 then
if not hydrantInteractionActive then
for _, hydrantConfig in ipairs(Config.HydrantOffsets) do
exports.interact:AddModelInteraction({
model = hydrantConfig.model,
offset = vec3(0.0, 0.0, 0.5),
name = 'Hydrant Interaction',
id = hydrantInteractionId,
distance = 5.0,
interactDst = 1.5,
ignoreLos = true,
options = {
{
label = Config.Translations.thirdEyeConnect,
action = function(entity)
HandleHydrantInteractionEntity(entity)
end,
canInteract = function(entity)
if not isPermissionAllowed() or HasObjectBeenBroken(entity) then
return false
end
local hydrantId = NetworkGetNetworkIdFromEntity(entity)
local rope = HydrantRopes.currentRopes[hydrantId]
return not rope
end,
},
{
label = Config.Translations.thirdEyeStartFill,
action = function(entity)
HandleStartFillingEntity(entity)
end,
canInteract = function(entity)
if not isPermissionAllowed() or HasObjectBeenBroken(entity) then
return false
end
local hydrantId = NetworkGetNetworkIdFromEntity(entity)
local rope = HydrantRopes.currentRopes[hydrantId]
local fillingVehicle = HydrantManager.fillingVehicles[hydrantId]
return rope and not fillingVehicle
end,
},
{
label = Config.Translations.thirdEyeStopFill,
action = function(entity)
HandleStopFillingEntity(entity)
end,
canInteract = function(entity)
if not isPermissionAllowed() or HasObjectBeenBroken(entity) then
return false
end
local hydrantId = NetworkGetNetworkIdFromEntity(entity)
local fillingVehicle = HydrantManager.fillingVehicles[hydrantId]
return fillingVehicle ~= nil
end,
},
{
label = Config.Translations.thirdEyeDisconnect,
action = function(entity)
HandleHydrantInteractionEntity(entity)
end,
canInteract = function(entity)
if not isPermissionAllowed() or HasObjectBeenBroken(entity) then
return false
end
local hydrantId = NetworkGetNetworkIdFromEntity(entity)
local rope = HydrantRopes.currentRopes[hydrantId]
return rope ~= nil
end,
},
}
})
end
hydrantInteractionActive = true
end
idleWait = 0
else
if hydrantInteractionActive then
for _, hydrantConfig in ipairs(Config.HydrantOffsets) do
exports.interact:RemoveModelInteraction(hydrantConfig.model, hydrantInteractionId)
end
hydrantInteractionActive = false
end
idleWait = 500
end
else
if hydrantInteractionActive then
for _, hydrantConfig in ipairs(Config.HydrantOffsets) do
exports.interact:RemoveModelInteraction(hydrantConfig.model, hydrantInteractionId)
end
hydrantInteractionActive = false
end
idleWait = 1000
end
end
end
end)
else
warn('Invalid interaction configuration')
end