vRP = nil ESX = nil if ConfigHose.Notifications.Enabled and ConfigHose.Notifications.Framework.vRP then local Tunnel = module("vrp", "lib/Tunnel") local Proxy = module("vrp", "lib/Proxy") vRP = Proxy.getInterface("vRP") end if ConfigHose.Notifications.Enabled and ConfigHose.Notifications.Framework.ESX then ESX = exports["es_extended"]:getSharedObject() end if ConfigHose.EnablePositioningCommand then TriggerEvent('chat:addSuggestion', '/'.."findhosepositioning", "Find positioning of the hose/supply line on your fire truck") RegisterCommand("findhosepositioning", function(source, args) local ped = PlayerPedId() local targetVehicle = GetVehiclePedIsIn(ped, false) if targetVehicle == nil or targetVehicle == 0 then Notify("No vehicle found!") else local model = `p_ld_soc_ball_01` SetEntityAlpha(targetVehicle, 150, false) RequestModel(model) while not HasModelLoaded(model) do Wait(0) end local ballProp = CreateObject(model, coords, false, false, false) while not DoesEntityExist(ballProp) do Wait(0) end SetModelAsNoLongerNeeded(model) local offSet = {0.0, 0.0, 0.0} local rotation = {0.0, 0.0, 0.0} local offSetComplete = false while not offSetComplete do if targetVehicle ~= nil and targetVehicle ~= 0 then DetachEntity(ballProp, true, false) AttachEntityToEntity(ballProp, targetVehicle, -1, offSet[1], offSet[2], offSet[3], rotation[1], rotation[2], rotation[3], true, false, true, false, 1, true) if not IsControlReleased(0, 207) then --page down offSet = {offSet[1], offSet[2], offSet[3] - 0.01} end if not IsControlReleased(0, 208) then --page up offSet = {offSet[1], offSet[2], offSet[3] + 0.01} end if not IsControlReleased(0, 173) then --arrow down offSet = {offSet[1], offSet[2] - 0.01, offSet[3]} end if not IsControlReleased(0, 172) then --arrow up offSet = {offSet[1], offSet[2] + 0.01, offSet[3]} end if not IsControlReleased(0, 174) then --arrow left offSet = {offSet[1] - 0.01, offSet[2], offSet[3]} end if not IsControlReleased(0, 175) then --arrow right offSet = {offSet[1] + 0.01, offSet[2], offSet[3]} end if IsControlJustPressed(0, 191) then -- enter - finish offSetComplete = true end end Wait(0) end Notify("OffSet Values are now printed in your console") print("OffSet: {"..offSet[1]..", "..offSet[2]..", "..offSet[3].."}") DeleteEntity(ballProp) ResetEntityAlpha(targetVehicle) end end, false) end function Notify(text) if not ConfigHose.Notifications.Enabled then return end if ConfigHose.Notifications.Framework.ESX then if ESX ~= nil then ESX.ShowNotification(text) end elseif ConfigHose.Notifications.Framework.QBCore then TriggerEvent('QBCore:Notify', text, 'primary') elseif ConfigHose.Notifications.Framework.QBX then exports.qbx_core:Notify(text, 'primary') elseif ConfigHose.Notifications.Framework.vRP then vRP.notify(source, {text}) elseif ConfigHose.Notifications.Framework.okok then exports['okokNotify']:Alert("Smart Hose", text, 2000, 'info', true) else showBaseNotification(text) end end function showBaseNotification(message) -- Base game notifications SetNotificationTextEntry("STRING") AddTextComponentString(message) DrawNotification(0,1) end -- General Functions @GeneralFunctions 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) SetFloatingHelpTextStyle(1, 1, 2, -1, 3, 0) BeginTextCommandDisplayHelp('instructionalText') AddTextComponentSubstringPlayerName(msg) EndTextCommandDisplayHelp(2, false, false, -1) end function IsFireHoseEnabled() return (HoseManager.nozzle and not HoseManager.nozzleDropped) end exports("IsFireHoseEnabled", IsFireHoseEnabled) function IsFireHoseShooting() return HoseManager.pressed end exports("IsFireHoseShooting", IsFireHoseShooting) function IsFireHoseShootingWater() return ParticleManager.isWaterDecal end exports("IsFireHoseShootingWater", IsFireHoseShootingWater) function GetCurrentDecalCoords() return ParticleManager.CurrentDecalCoords end exports("GetCurrentDecalCoords", GetCurrentDecalCoords) function GetCurrentHosePressure() return ParticleManager.particleScale end exports("GetCurrentHosePressure", GetCurrentHosePressure) local vehicleModels = getVehicleModelsFromCapacities() local interactionId = "menuId" if ConfigHose.InteractType.TruckInteraction.Drawtext or ConfigHose.InteractType.TruckInteraction.ScreenPrompt then CreateThread(function() while true do Wait(1) local pos = GetEntityCoords(PlayerPedId()) local vehicle = GetClosestVehicle(pos) local inRange = false if vehicle and DoesEntityExist(vehicle) and IsVehicleDriveable(vehicle, false) then local vehicleModel = GetEntityModel(vehicle) local vehicleSettings = ConfigHose.Rope.VehicleSettings[vehicleModel] or {} local useBone = vehicleSettings.useBone or false local bones = vehicleSettings.bones or {} local offsets = vehicleSettings.offsets or {{x = 0.0, y = 0.0, z = 0.0}} local attachmentPoints = {} if useBone then for _, boneName in ipairs(bones) do local boneIndex = GetEntityBoneIndexByName(vehicle, boneName) if boneIndex ~= -1 then local bonePos = GetWorldPositionOfEntityBone(vehicle, boneIndex) table.insert(attachmentPoints, bonePos) else print("Bone " .. boneName .. " not found in vehicle model.") end end else for _, offset in ipairs(offsets) do local offsetWorldPos = GetOffsetFromEntityInWorldCoords(vehicle, offset.x, offset.y, offset.z) table.insert(attachmentPoints, offsetWorldPos) end end local closestPoint = attachmentPoints[1] local minDistance = #(pos - closestPoint) for _, point in ipairs(attachmentPoints) do local distance = #(pos - point) if distance < minDistance then closestPoint = point minDistance = distance end end local promptCoords = closestPoint local vehicleDistance = #(pos - promptCoords) if not IsPedInAnyVehicle(PlayerPedId(), false) and vehicleDistance < 3.0 then inRange = true local text = HoseManager.nozzle and ConfigHose.Translations.storeHoseMessage or ConfigHose.Translations.grabHoseMessage if ConfigHose.InteractType.TruckInteraction.Drawtext then drawInstructionalText(text, promptCoords) elseif ConfigHose.InteractType.TruckInteraction.ScreenPrompt then DisplayHelpText(text) end if IsControlJustReleased(0, ConfigHose.Keys.Interact) then if not HoseManager.nozzle and isPermissionAllowed() then InitializeFireHose() AttachRopeToProp() else UnequipFireHose() end end end end if not inRange then Wait(2500) end end end) elseif ConfigHose.InteractType.TruckInteraction.ThirdEye.enabled then if ConfigHose.InteractType.TruckInteraction.ThirdEye.oxTarget then local VehicleOptions = { { label = ConfigHose.Translations.thirdEyeMessage, icon = "fa-solid fa-water", onSelect = function() if not HoseManager.nozzle and isPermissionAllowed() then InitializeFireHose() AttachRopeToProp() else UnequipFireHose() end end, } } exports.ox_target:addModel(vehicleModels, VehicleOptions) end if ConfigHose.InteractType.TruckInteraction.ThirdEye.qbTarget then exports['qb-target']:AddTargetModel(vehicleModels, { options = { { label = ConfigHose.Translations.thirdEyeMessage, icon = "fa-solid fa-water", action = function() if not HoseManager.nozzle and isPermissionAllowed() then InitializeFireHose() AttachRopeToProp() else UnequipFireHose() end end, }, }, distance = 3.0 }) end elseif ConfigHose.InteractType.TruckInteraction.WorldInteraction then CreateThread(function() local interactionActive = false local idleWait = 1000 while true do Wait(idleWait) if IsPedInAnyVehicle(PlayerPedId(), false) then if interactionActive then for _, model in ipairs(vehicleModels) do exports.interact:RemoveModelInteraction(model, interactionId) end interactionActive = false end idleWait = 1500 else local pos = GetEntityCoords(PlayerPedId()) local vehicle = GetClosestVehicle(pos) if vehicle and DoesEntityExist(vehicle) then local vehicleModel = GetEntityModel(vehicle) local isAllowedModel = false for _, model in ipairs(vehicleModels) do if model == vehicleModel then isAllowedModel = true break end end if isAllowedModel then local vehicleSettings = ConfigHose.Rope.VehicleSettings[vehicleModel] or {} local useBone = vehicleSettings.useBone or false local bones = vehicleSettings.bones or {} local offsets = vehicleSettings.offsets or {{x = 0.0, y = 0.0, z = 0.0}} local attachmentPoints = {} if useBone then for _, boneName in ipairs(bones) do local boneIndex = GetEntityBoneIndexByName(vehicle, boneName) if boneIndex ~= -1 then local bonePos = GetWorldPositionOfEntityBone(vehicle, boneIndex) table.insert(attachmentPoints, bonePos) else print("Bone " .. boneName .. " not found in vehicle model.") end end else for _, offset in ipairs(offsets) do local offsetWorldPos = GetOffsetFromEntityInWorldCoords(vehicle, offset.x, offset.y, offset.z) table.insert(attachmentPoints, offsetWorldPos) end end local closestPoint = attachmentPoints[1] local minDistance = #(pos - closestPoint) for _, point in ipairs(attachmentPoints) do local distance = #(pos - point) if distance < minDistance then closestPoint = point minDistance = distance end end local promptCoords = closestPoint local vehicleDistance = #(pos - promptCoords) if vehicleDistance < 4.0 then if not interactionActive then exports.interact:AddModelInteraction({ model = vehicleModel, offset = promptCoords - GetEntityCoords(vehicle), name = 'Menu', id = interactionId, distance = 8.0, interactDst = 2.5, ignoreLos = true, bone = 'bodyshell', options = { { label = ConfigHose.Translations.grabHoseWorldInteraction, action = function() if not HoseManager.nozzle then InitializeFireHose() AttachRopeToProp() else UnequipFireHose() end end, }, } }) interactionActive = true end idleWait = 0 else if interactionActive then exports.interact:RemoveModelInteraction(vehicleModel, interactionId) interactionActive = false end idleWait = 500 end else if interactionActive then exports.interact:RemoveModelInteraction(vehicleModel, interactionId) interactionActive = false end idleWait = 1000 end else if interactionActive then for _, model in ipairs(vehicleModels) do exports.interact:RemoveModelInteraction(model, interactionId) end interactionActive = false end idleWait = 1500 end end end end) else warn('Invalid interaction configuration') end