643 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			643 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
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
 |