1
0
Fork 0
forked from Simnation/Main
Main/resources/[carscripts]/re_boat_winch/main.lua
2025-07-02 07:08:25 +02:00

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