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

580 lines
18 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, 0.5, 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.25) -- 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
-- Function to check if boat is positioned over trailer
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
CreateThread(function()
while true do
Wait(1000)
local playerPed = PlayerPedId()
local playerCoords = GetEntityCoords(playerPed)
local closestVehicle, closestCoords = getClosestVehicle(playerCoords, 3.0)
if closestVehicle ~= nil and #(playerCoords - closestCoords) < 3.0 then
if not enteredCloseVehicle then
local isBoat = isSupportedBoat(closestVehicle)
if isTrailer(closestVehicle) then
closestTrailer = closestVehicle
elseif isBoat then
closestBoat = closestVehicle
end
end
enteredCloseVehicle = true
else
enteredCloseVehicle = false
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 = "Control the rope",
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 = "Detach boat from trailer",
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 = "Hook up the rope",
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 = "Unhook the rope",
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 = "Secure boat to trailer",
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
CreateThread(function()
while true do
Wait(createdRope and controlRope and 0 or 1000)
if createdRope and controlRope then
if ropeLength == 1.0 then
attachBoatToTrailer()
ropeLength = 10.0
end
if IsControlPressed(0, 172) then
ropeLength = ropeLength + lengthTick
if ropeLength > Config.MaxRopeLength then
ropeLength = Config.MaxRopeLength
end
StopRopeWinding(createdRope)
StartRopeUnwindingFront(createdRope)
RopeForceLength(createdRope, ropeLength)
elseif IsControlJustReleased(0, 172) then
StopRopeUnwindingFront(createdRope)
StopRopeWinding(createdRope)
RopeConvertToSimple(createdRope)
elseif IsControlPressed(0, 173) then
ropeLength = ropeLength - lengthTick
if ropeLength < minRopeLength then
ropeLength = minRopeLength
end
StopRopeUnwindingFront(createdRope)
StartRopeWinding(createdRope)
RopeForceLength(createdRope, ropeLength)
elseif IsControlJustReleased(0, 173) then
StopRopeUnwindingFront(createdRope)
StopRopeWinding(createdRope)
RopeConvertToSimple(createdRope)
end
end
if createdRope and hasTakenRope then
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)
if coordsDiff > Config.MaxRopeLength then
notify("Du bist zu weit vom Anhänger weg")
DeleteRope(createdRope)
DeleteEntity(colObj)
FreezeEntityPosition(closestTrailer, false)
SetEntityInvincible(closestTrailer, false)
SetEntityInvincible(closestBoat, false)
closestTrailer = nil
closestBoat = nil
hasTakenRope = false
hookedToBoat = false
controlRope = false
colObj = nil
boatOnTrailer = false
end
end
end
end)