local QBCore = exports['qb-core']:GetCoreObject() local loadedKayaks = {} local currentTrailer = nil -- Function to check if a model is a kayak local function IsKayakModel(model) for _, kayakModel in ipairs(Config.KayakModels) do if GetHashKey(kayakModel) == model then return true end end return false end -- Function to get nearby kayak local function GetNearbyKayak() local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) local kayak = nil local minDistance = Config.InteractionRange for _, entity in ipairs(GetGamePool('CVehicle')) do if IsKayakModel(GetEntityModel(entity)) then local distance = #(playerCoords - GetEntityCoords(entity)) if distance < minDistance then minDistance = distance kayak = entity end end end return kayak end -- Function to get nearby trailer local function GetNearbyTrailer() local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) local trailer = nil local minDistance = Config.InteractionRange for _, entity in ipairs(GetGamePool('CVehicle')) do if GetEntityModel(entity) == GetHashKey(Config.TrailerModel) then local distance = #(playerCoords - GetEntityCoords(entity)) if distance < minDistance then minDistance = distance trailer = entity end end end return trailer end -- Function to load kayak onto trailer local function LoadKayakOntoTrailer(trailer, kayak) if not trailer or not kayak then return end local trailerNetId = NetworkGetNetworkIdFromEntity(trailer) local kayakNetId = NetworkGetNetworkIdFromEntity(kayak) -- Check if trailer already has max kayaks if #loadedKayaks >= Config.MaxKayaks then QBCore.Functions.Notify("Trailer rack is already fully loaded with kayaks", "error") return end -- Add kayak to loaded kayaks table.insert(loadedKayaks, kayakNetId) -- Update kayak position on trailer local position = Config.KayakPositions[#loadedKayaks] local trailerCoords = GetEntityCoords(trailer) local trailerHeading = GetEntityHeading(trailer) -- Calculate world position based on trailer position and offset local kayakX = trailerCoords.x + (math.cos(math.rad(trailerHeading)) * position.x) - (math.sin(math.rad(trailerHeading)) * position.y) local kayakY = trailerCoords.y + (math.sin(math.rad(trailerHeading)) * position.x) + (math.cos(math.rad(trailerHeading)) * position.y) local kayakZ = trailerCoords.z + position.z local kayakHeading = trailerHeading + position.heading -- Set kayak position and attach to trailer SetEntityCoords(kayak, kayakX, kayakY, kayakZ, false, false, false, false) SetEntityHeading(kayak, kayakHeading) -- Attach kayak to trailer AttachEntityToEntity(kayak, trailer, 0, position.x, position.y, position.z, 0.0, 0.0, position.heading, true, false, true, false, 0, true) -- Display which rack position was loaded local side = #loadedKayaks <= 3 and "left" or "right" local level = (#loadedKayaks - 1) % 3 + 1 local levelNames = {"bottom", "middle", "top"} QBCore.Functions.Notify("Kayak loaded onto " .. levelNames[level] .. " rack, " .. side .. " side", "success") -- Save current trailer currentTrailer = trailerNetId -- Sync with server TriggerServerEvent('kayak_trailer:syncLoadedKayaks', trailerNetId, loadedKayaks) end -- Function to unload kayak from trailer local function UnloadKayakFromTrailer(trailer) if not trailer or #loadedKayaks == 0 then return end local trailerNetId = NetworkGetNetworkIdFromEntity(trailer) -- Get last kayak local kayakNetId = loadedKayaks[#loadedKayaks] local kayak = NetworkGetEntityFromNetworkId(kayakNetId) -- Get position info for notification local side = #loadedKayaks <= 3 and "left" or "right" local level = (#loadedKayaks - 1) % 3 + 1 local levelNames = {"bottom", "middle", "top"} -- Remove from loaded kayaks table.remove(loadedKayaks, #loadedKayaks) -- Detach kayak from trailer DetachEntity(kayak, true, true) -- Place kayak behind trailer local trailerCoords = GetEntityCoords(trailer) local trailerHeading = GetEntityHeading(trailer) local offsetX = -3.0 -- 3 meters behind trailer local kayakX = trailerCoords.x - (math.cos(math.rad(trailerHeading)) * offsetX) local kayakY = trailerCoords.y - (math.sin(math.rad(trailerHeading)) * offsetX) local kayakZ = trailerCoords.z SetEntityCoords(kayak, kayakX, kayakY, kayakZ, false, false, false, false) SetEntityHeading(kayak, trailerHeading) QBCore.Functions.Notify("Kayak unloaded from " .. levelNames[level] .. " rack, " .. side .. " side", "success") -- Sync with server TriggerServerEvent('kayak_trailer:syncLoadedKayaks', trailerNetId, loadedKayaks) end -- Initialize qb-target Citizen.CreateThread(function() -- Target for loading kayaks onto trailer exports['qb-target']:AddTargetModel(Config.TrailerModel, { options = { { type = "client", event = "kayak_trailer:loadKayak", icon = "fas fa-truck-loading", label = "Load Kayak to Rack", canInteract = function() local kayak = GetNearbyKayak() return kayak ~= nil and #loadedKayaks < Config.MaxKayaks end }, { type = "client", event = "kayak_trailer:unloadKayak", icon = "fas fa-dolly", label = "Unload Kayak from Rack", canInteract = function() return #loadedKayaks > 0 end } }, distance = Config.InteractionRange }) -- Target for kayaks for _, model in ipairs(Config.KayakModels) do exports['qb-target']:AddTargetModel(model, { options = { { type = "client", event = "kayak_trailer:loadKayak", icon = "fas fa-truck-loading", label = "Load onto Trailer Rack", canInteract = function() local trailer = GetNearbyTrailer() return trailer ~= nil and #loadedKayaks < Config.MaxKayaks end } }, distance = Config.InteractionRange }) end end) -- Event handlers RegisterNetEvent('kayak_trailer:loadKayak', function() local trailer = GetNearbyTrailer() local kayak = GetNearbyKayak() if trailer and kayak then LoadKayakOntoTrailer(trailer, kayak) else QBCore.Functions.Notify("No trailer or kayak nearby", "error") end end) RegisterNetEvent('kayak_trailer:unloadKayak', function() local trailer = GetNearbyTrailer() if trailer then UnloadKayakFromTrailer(trailer) else QBCore.Functions.Notify("No trailer nearby", "error") end end) -- Sync loaded kayaks from server RegisterNetEvent('kayak_trailer:syncLoadedKayaksClient', function(trailerNetId, kayakList) if NetworkDoesNetworkIdExist(trailerNetId) then local trailer = NetworkGetEntityFromNetworkId(trailerNetId) -- Clear existing kayaks for _, kayakNetId in ipairs(loadedKayaks) do if NetworkDoesNetworkIdExist(kayakNetId) then local kayak = NetworkGetEntityFromNetworkId(kayakNetId) if DoesEntityExist(kayak) then DetachEntity(kayak, true, true) end end end -- Update loaded kayaks loadedKayaks = kayakList -- Reattach kayaks for i, kayakNetId in ipairs(loadedKayaks) do if NetworkDoesNetworkIdExist(kayakNetId) then local kayak = NetworkGetEntityFromNetworkId(kayakNetId) if DoesEntityExist(kayak) then local position = Config.KayakPositions[i] AttachEntityToEntity(kayak, trailer, 0, position.x, position.y, position.z, 0.0, 0.0, position.heading, true, false, true, false, 0, true) end end end currentTrailer = trailerNetId end end)