local createdRope = nil local ropeLength = 10.0 local minRopeLength = 1.0 local lengthTick = 0.02 local enteredCloseVehicle = false local closestTrailer = nil local closestBoat = nil local hasTakenRope = false local hookedToBoat = false local controlRope = false local colObj = nil local boatOnTrailer = false local function getTrailerOffset(trailer) local model = GetEntityModel(trailer) if model == `boattrailer6` then return vector3(0.0, 6.0, 0.3) -- Adjusted values for boattrailer6 (moved forward toward hitch) else return vector3(0.0, 1.8, 0.0) -- Original boattrailer values end end local function getBoatAttachmentOffset(trailer) local model = GetEntityModel(trailer) if model == `boattrailer6` then return vector3(0.0, -1.0, 0.75) -- Adjusted values for boattrailer6 else return vector3(0.0, -1.02, 0.3) -- Original boattrailer values end end local function isSupportedBoat(closest) if not closest then return false end for _, model in pairs(Config.SupportedBoats) do if GetEntityModel(closest) == model then return true end end return false end local function isTrailer(vehicle) if not vehicle then return false end local model = GetEntityModel(vehicle) return model == `boattrailer` or model == `boattrailer6` end local function getClosestVehicle(coords, maxDistance) local vehicles = GetGamePool('CVehicle') local closestVehicle, closestCoords maxDistance = maxDistance or 2.0 for i = 1, #vehicles do local vehicle = vehicles[i] local vehicleCoords = GetEntityCoords(vehicle) local distance = #(coords - vehicleCoords) if distance < maxDistance then maxDistance = distance closestVehicle = vehicle closestCoords = vehicleCoords end end return closestVehicle, closestCoords end local function notify(text) SetNotificationTextEntry("STRING") AddTextComponentString(text) DrawNotification(true, false) end -- Replace the IsBoatOverTrailer function with this improved version local function IsBoatOverTrailer(boat) if not boat or not isSupportedBoat(boat) then return false end local boatCoords = GetEntityCoords(boat) local vehicles = GetGamePool('CVehicle') local foundTrailer = nil for i = 1, #vehicles do local vehicle = vehicles[i] if isTrailer(vehicle) then local trailerCoords = GetEntityCoords(vehicle) local distance = #(boatCoords - trailerCoords) if distance < 3.0 then -- Additional check for proper alignment local trailerHeading = GetEntityHeading(vehicle) local boatHeading = GetEntityHeading(boat) local headingDiff = math.abs(trailerHeading - boatHeading) if headingDiff < 30.0 or headingDiff > 330.0 then foundTrailer = vehicle break end end end end return foundTrailer end -- Add this new thread to handle E key press for securing boat CreateThread(function() while true do Wait(0) local playerPed = PlayerPedId() -- Only check if player is in a vehicle if IsPedInAnyVehicle(playerPed, false) then local boat = GetVehiclePedIsIn(playerPed, false) -- Check if it's a supported boat if isSupportedBoat(boat) then -- Check if boat is over a trailer local trailer = IsBoatOverTrailer(boat) if trailer then -- Display help text BeginTextCommandDisplayHelp("STRING") AddTextComponentSubstringPlayerName("Press ~INPUT_CONTEXT~ to secure boat to trailer") EndTextCommandDisplayHelp(0, false, true, -1) -- Check for E key press if IsControlJustReleased(0, 38) then -- 38 is E key -- Exit the boat TaskLeaveVehicle(playerPed, boat, 0) -- Wait for player to exit Wait(1500) -- Attach boat to trailer local attachOffset = getBoatAttachmentOffset(trailer) AttachEntityToEntity(boat, trailer, 0, attachOffset.x, attachOffset.y, attachOffset.z, 0.0, 0.0, 0.0, false, false, false, false, 2, true) closestBoat = boat closestTrailer = trailer boatOnTrailer = true notify("Boot am Anhänger befestigt") end end end else Wait(1000) -- Wait longer if not in a vehicle end end end) CreateThread(function() if GetResourceState('qb-target') == 'started' then exports['qb-target']:AddGlobalVehicle({ options = { { type = "client", event = "re_boat_winch:client:takeRope", icon = 'fas fa-clipboard', label = "Take the rope", canInteract = function(entity) if closestTrailer ~= entity or not isTrailer(entity) then return false end return not boatOnTrailer and not hookedToBoat and not hasTakenRope end }, { type = "client", event = "re_boat_winch:client:returnRope", icon = 'fas fa-clipboard', label = "Return the rope", canInteract = function(entity) if closestTrailer ~= entity or not isTrailer(entity) then return false end return hasTakenRope and not hookedToBoat and not boatOnTrailer end }, { type = "client", event = "re_boat_winch:client:controlRope", icon = 'fas fa-clipboard', label = "Winde bedienen", canInteract = function(entity) if closestTrailer ~= entity or not isTrailer(entity) then return false end return not hasTakenRope and hookedToBoat end }, { type = "client", event = "re_boat_winch:client:detachBoat", icon = 'fas fa-clipboard', label = "Boot vom Hänger lösen", canInteract = function(entity) if closestTrailer ~= entity or not isTrailer(entity) then return false end return boatOnTrailer end }, { type = "client", event = "re_boat_winch:client:hookUpRope", icon = 'fas fa-clipboard', label = "Seil befestigen", canInteract = function(entity) if closestBoat ~= entity or not isSupportedBoat(entity) then return false end return hasTakenRope and not hookedToBoat end }, { type = "client", event = "re_boat_winch:client:unHookRope", icon = 'fas fa-clipboard', label = "Seil lösen", canInteract = function(entity) if closestBoat ~= entity or not isSupportedBoat(entity) then return false end return not hasTakenRope and hookedToBoat and createdRope end }, { type = "client", event = "re_boat_winch:client:selfDockBoat", icon = 'fas fa-anchor', label = "Boot auf Hänger sichern", canInteract = function(entity) if not isSupportedBoat(entity) then return false end local trailer = IsBoatOverTrailer(entity) return trailer ~= nil end } }, distance = 2.5 }) else exports.ox_target:addGlobalVehicle({ { event = "re_boat_winch:client:takeRope", icon = 'fas fa-clipboard', label = "Take the rope", name = "re_boat_winch:client:takeRope", distance = 2.5, canInteract = function(entity) if closestTrailer ~= entity or not isTrailer(entity) then return false end return not boatOnTrailer and not hookedToBoat and not hasTakenRope end }, { event = "re_boat_winch:client:returnRope", icon = 'fas fa-clipboard', label = "Return the rope", name = "re_boat_winch:client:returnRope", distance = 2.5, canInteract = function(entity) if closestTrailer ~= entity or not isTrailer(entity) then return false end return hasTakenRope and not hookedToBoat and not boatOnTrailer end }, { event = "re_boat_winch:client:controlRope", icon = 'fas fa-clipboard', label = "Control the rope", name = "re_boat_winch:client:controlRope", distance = 2.5, canInteract = function(entity) if closestTrailer ~= entity or not isTrailer(entity) then return false end return not hasTakenRope and hookedToBoat end }, { event = "re_boat_winch:client:detachBoat", icon = 'fas fa-clipboard', label = "Detach boat from trailer", name = "re_boat_winch:client:detachBoat", distance = 2.5, canInteract = function(entity) if closestTrailer ~= entity or not isTrailer(entity) then return false end return boatOnTrailer end }, { event = "re_boat_winch:client:hookUpRope", icon = 'fas fa-clipboard', label = "Hook up the rope", name = "re_boat_winch:client:hookUpRope", distance = 2.5, canInteract = function(entity) if closestBoat ~= entity or not isSupportedBoat(entity) then return false end return hasTakenRope and not hookedToBoat end }, { event = "re_boat_winch:client:unHookRope", icon = 'fas fa-clipboard', label = "Unhook the rope", name = "re_boat_winch:client:unHookRope", distance = 2.5, canInteract = function(entity) if closestBoat ~= entity or not isSupportedBoat(entity) then return false end return not hasTakenRope and hookedToBoat and createdRope end }, { event = "re_boat_winch:client:selfDockBoat", icon = 'fas fa-anchor', label = "Secure boat to trailer", name = "re_boat_winch:client:selfDockBoat", distance = 2.5, canInteract = function(entity) if not isSupportedBoat(entity) then return false end local trailer = IsBoatOverTrailer(entity) return trailer ~= nil end } }) end end) AddEventHandler('onResourceStop', function(resourceName) if resourceName ~= "re_boat_winch" then return end if GetResourceState('qb-target') == 'started' then exports['qb-target']:RemoveGlobalVehicle("Nimm das Seil") exports['qb-target']:RemoveGlobalVehicle("Häng das SEil zurück") exports['qb-target']:RemoveGlobalVehicle("Winde kontrollieren") exports['qb-target']:RemoveGlobalVehicle("Boot vom Anhänger lösen") exports['qb-target']:RemoveGlobalVehicle("Seil einhängen") exports['qb-target']:RemoveGlobalVehicle("Seil aushängen") exports['qb-target']:RemoveGlobalVehicle("Boot am Anhänger befestigen") else exports.ox_target:removeGlobalVehicle("re_boat_winch:client:takeRope") exports.ox_target:removeGlobalVehicle("re_boat_winch:client:returnRope") exports.ox_target:removeGlobalVehicle("re_boat_winch:client:controlRope") exports.ox_target:removeGlobalVehicle("re_boat_winch:client:detachBoat") exports.ox_target:removeGlobalVehicle("re_boat_winch:client:hookUpRope") exports.ox_target:removeGlobalVehicle("re_boat_winch:client:unHookRope") exports.ox_target:removeGlobalVehicle("re_boat_winch:client:selfDockBoat") end end) local function createRope(obj1, obj2, obj1Coords, obj2Coords, lenght) DeleteRope(createdRope) RopeLoadTextures() while not RopeAreTexturesLoaded() do Wait(0) RopeLoadTextures() end RopeLoadTextures() createdRope = AddRope(obj1Coords.x, obj1Coords.y, obj1Coords.z, 0.0, 0.0, 0.0, lenght, 1, Config.MaxRopeLength, 1.0, 1.0, false, true, false, 5.0, false, 0) while not createdRope do Wait(0) end ActivatePhysics(createdRope) Wait(50) AttachEntitiesToRope(createdRope, obj1, obj2, obj1Coords.x, obj1Coords.y, obj1Coords.z, obj2Coords.x, obj2Coords.y, obj2Coords.z, lenght, false, false, nil, nil) end RegisterNetEvent('re_boat_winch:client:unHookRope', function() hookedToBoat = false controlRope = false hasTakenRope = true notify("Du hast das Seil ausgehakt") local playerPed = PlayerPedId() local trailerOffset = getTrailerOffset(closestTrailer) local trailerCoords = GetOffsetFromEntityInWorldCoords(closestTrailer, trailerOffset.x, trailerOffset.y, trailerOffset.z) local boneIndex = GetPedBoneIndex(playerPed, 0x49D9) ropeLength = 5.0 colObj = CreateObject(`xm3_prop_xm3_hook_01a`, 0, 0, 0, true, true, true) AttachEntityToEntity(colObj, playerPed, boneIndex, 0.11, 0.02, 0.02, -80.0, -90.0, 15.0, true, true, false, true, 1, true) local objCoords = GetEntityCoords(colObj) createRope(closestTrailer, colObj, trailerCoords, objCoords, ropeLength) end) RegisterNetEvent('re_boat_winch:client:returnRope', function() hasTakenRope = false DeleteEntity(colObj) DeleteRope(createdRope) -- Unfreeze the trailer when returning the rope FreezeEntityPosition(closestTrailer, false) SetEntityInvincible(closestTrailer, false) end) RegisterNetEvent('re_boat_winch:client:detachBoat', function() boatOnTrailer = false DetachEntity(closestBoat, true, true) local trailerOffset = getTrailerOffset(closestTrailer) local trailerCoords = GetOffsetFromEntityInWorldCoords(closestTrailer, 0.0, -8.0, 0.0) SetEntityCoords(closestBoat, trailerCoords.x, trailerCoords.y, trailerCoords.z) end) RegisterNetEvent('re_boat_winch:client:takeRope', function() hasTakenRope = true FreezeEntityPosition(closestTrailer, true) SetEntityInvincible(closestTrailer, true) local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) local trailerOffset = getTrailerOffset(closestTrailer) local trailerCoords = GetOffsetFromEntityInWorldCoords(closestTrailer, trailerOffset.x, trailerOffset.y, trailerOffset.z) local coordsDiff = #(trailerCoords - playerCoords) local boneIndex = GetPedBoneIndex(playerPed, 0x49D9) ropeLength = coordsDiff colObj = CreateObject(`xm3_prop_xm3_hook_01a`, 0, 0, 0, true, true, true) AttachEntityToEntity(colObj, playerPed, boneIndex, 0.11, 0.02, 0.02, -80.0, -90.0, 15.0, true, true, false, true, 1, true) local objCoords = GetEntityCoords(colObj) createRope(closestTrailer, colObj, trailerCoords, objCoords, ropeLength) end) RegisterNetEvent('re_boat_winch:client:hookUpRope', function() hookedToBoat = true hasTakenRope = false notify("Du hast den Haken genommen") local trailerOffset = getTrailerOffset(closestTrailer) local trailerCoords = GetOffsetFromEntityInWorldCoords(closestTrailer, trailerOffset.x, trailerOffset.y, trailerOffset.z) local boatCoords = GetOffsetFromEntityInWorldCoords(closestBoat, 0.0, 3.9, 0.9) local coordsDiff = #(trailerCoords - boatCoords) ropeLength = coordsDiff SetEntityInvincible(closestBoat, true) DeleteEntity(colObj) createRope(closestTrailer, closestBoat, trailerCoords, boatCoords, ropeLength) end) RegisterNetEvent('re_boat_winch:client:controlRope', function() controlRope = true notify("Du kontrollierst die Winde") end) -- New event for self-docking boats RegisterNetEvent('re_boat_winch:client:selfDockBoat', function() local playerPed = PlayerPedId() local boat = GetVehiclePedIsIn(playerPed, false) if not boat or not isSupportedBoat(boat) then notify("Du musst in einem unterstützten Boot sein") return end local trailer = IsBoatOverTrailer(boat) if not trailer then notify("Kein Anhänger in der Nähe gefunden") return end -- Exit the boat TaskLeaveVehicle(playerPed, boat, 0) Wait(1500) -- Attach boat to trailer local attachOffset = getBoatAttachmentOffset(trailer) AttachEntityToEntity(boat, trailer, 0, attachOffset.x, attachOffset.y, attachOffset.z, 0.0, 0.0, 0.0, false, false, false, false, 2, true) closestBoat = boat closestTrailer = trailer boatOnTrailer = true notify("Boot am Anhänger befestigt") end) local function attachBoatToTrailer() if not DoesEntityExist(closestTrailer) or not DoesEntityExist(closestBoat) then return end local attachOffset = getBoatAttachmentOffset(closestTrailer) AttachEntityToEntity(closestBoat, closestTrailer, 0, attachOffset.x, attachOffset.y, attachOffset.z, 0.0, 0.0, 0.0, false, false, false, false, 2, true) DeleteRope(createdRope) FreezeEntityPosition(closestTrailer, false) SetEntityInvincible(closestTrailer, false) SetEntityInvincible(closestBoat, false) hasTakenRope = false hookedToBoat = false controlRope = false createdRope = nil colObj = nil boatOnTrailer = true end -- Replace the existing winch control thread with this improved version CreateThread(function() while true do Wait(0) if createdRope and controlRope then -- Display help text for winch controls BeginTextCommandDisplayHelp("STRING") AddTextComponentSubstringPlayerName("~INPUT_FRONTEND_UP~ Extend winch | ~INPUT_FRONTEND_DOWN~ Retract winch | ~INPUT_FRONTEND_RRIGHT~ Cancel") EndTextCommandDisplayHelp(0, false, true, -1) -- Show current rope length DrawTextOnScreen("Rope Length: " .. string.format("%.1f", ropeLength) .. "m", 0.5, 0.05, 0.4, {r=255, g=255, b=255, a=200}) -- Extend rope (Arrow Up) if IsControlPressed(0, 172) then ropeLength = ropeLength + lengthTick * 2 -- Doubled speed for better responsiveness if ropeLength > Config.MaxRopeLength then ropeLength = Config.MaxRopeLength end StopRopeWinding(createdRope) StartRopeUnwindingFront(createdRope) RopeForceLength(createdRope, ropeLength) -- Add visual feedback PlaySoundFrontend(-1, "NAV_UP_DOWN", "HUD_FRONTEND_DEFAULT_SOUNDSET", true) elseif IsControlJustReleased(0, 172) then StopRopeUnwindingFront(createdRope) StopRopeWinding(createdRope) RopeConvertToSimple(createdRope) -- Retract rope (Arrow Down) elseif IsControlPressed(0, 173) then ropeLength = ropeLength - lengthTick * 2 -- Doubled speed for better responsiveness if ropeLength < minRopeLength then ropeLength = minRopeLength -- Auto-attach boat when fully retracted if hookedToBoat then attachBoatToTrailer() notify("Boot erfolgreich auf den Anhänger gezogen") end end StopRopeUnwindingFront(createdRope) StartRopeWinding(createdRope) RopeForceLength(createdRope, ropeLength) -- Add visual feedback PlaySoundFrontend(-1, "NAV_UP_DOWN", "HUD_FRONTEND_DEFAULT_SOUNDSET", true) elseif IsControlJustReleased(0, 173) then StopRopeUnwindingFront(createdRope) StopRopeWinding(createdRope) RopeConvertToSimple(createdRope) -- Cancel winch control (Backspace) elseif IsControlJustPressed(0, 194) then controlRope = false notify("Windensteuerung beendet") end -- Visual indicator for rope tension local tensionColor = {r=255, g=255, b=255, a=200} if ropeLength < 3.0 then -- High tension (red) tensionColor = {r=255, g=0, b=0, a=200} elseif ropeLength < 6.0 then -- Medium tension (yellow) tensionColor = {r=255, g=255, b=0, a=200} end DrawTextOnScreen("Tension: " .. GetTensionText(ropeLength), 0.5, 0.08, 0.4, tensionColor) else Wait(1000) -- Wait longer if not controlling the winch end end end) -- Helper function to draw text on screen function DrawTextOnScreen(text, x, y, scale, color) SetTextFont(4) SetTextProportional(true) SetTextScale(scale, scale) SetTextColour(color.r, color.g, color.b, color.a) SetTextDropShadow(0, 0, 0, 0, 255) SetTextEdge(2, 0, 0, 0, 150) SetTextDropShadow() SetTextOutline() SetTextCentre(true) SetTextEntry("STRING") AddTextComponentString(text) DrawText(x, y) end -- Helper function to get tension text based on rope length function GetTensionText(length) if length < 3.0 then return "HIGH" elseif length < 6.0 then return "MEDIUM" else return "LOW" end end