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
 | 
