1
0
Fork 0
forked from Simnation/Main
Main/resources/[carscripts]/re_boat_winch/main.lua

644 lines
20 KiB
Lua
Raw Permalink Normal View History

2025-06-07 08:51:21 +02:00
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)
2025-07-02 06:24:51 +02:00
if model == `boattrailer6` then
2025-07-02 06:50:59 +02:00
return vector3(0.0, 6.0, 0.3) -- Adjusted values for boattrailer6 (moved forward toward hitch)
2025-06-07 08:51:21 +02:00
else
2025-07-02 06:35:52 +02:00
return vector3(0.0, 1.8, 0.0) -- Original boattrailer values
2025-06-07 08:51:21 +02:00
end
end
local function getBoatAttachmentOffset(trailer)
local model = GetEntityModel(trailer)
2025-07-02 06:24:51 +02:00
if model == `boattrailer6` then
2025-07-02 06:59:46 +02:00
return vector3(0.0, -1.0, 0.75) -- Adjusted values for boattrailer6
2025-06-07 08:51:21 +02:00
else
2025-07-02 06:35:52 +02:00
return vector3(0.0, -1.02, 0.3) -- Original boattrailer values
2025-06-07 08:51:21 +02:00
end
end
local function isSupportedBoat(closest)
2025-07-02 06:35:52 +02:00
if not closest then return false end
2025-06-07 08:51:21 +02:00
for _, model in pairs(Config.SupportedBoats) do
if GetEntityModel(closest) == model then
return true
end
end
2025-07-02 06:35:52 +02:00
return false
2025-06-07 08:51:21 +02:00
end
local function isTrailer(vehicle)
2025-07-02 06:35:52 +02:00
if not vehicle then return false end
2025-06-07 08:51:21 +02:00
local model = GetEntityModel(vehicle)
2025-07-02 06:24:51 +02:00
return model == `boattrailer` or model == `boattrailer6`
2025-06-07 08:51:21 +02:00
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
2025-07-02 06:50:59 +02:00
-- Replace the IsBoatOverTrailer function with this improved version
2025-07-02 06:35:52 +02:00
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
2025-07-02 06:50:59 +02:00
-- Add this new thread to handle E key press for securing boat
2025-06-07 08:51:21 +02:00
CreateThread(function()
while true do
2025-07-02 06:50:59 +02:00
Wait(0)
2025-06-07 08:51:21 +02:00
local playerPed = PlayerPedId()
2025-07-02 06:50:59 +02:00
-- 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)
2025-06-07 08:51:21 +02:00
2025-07-02 06:50:59 +02:00
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
2025-06-07 08:51:21 +02:00
end
end
else
2025-07-02 06:50:59 +02:00
Wait(1000) -- Wait longer if not in a vehicle
2025-06-07 08:51:21 +02:00
end
end
end)
2025-07-02 06:50:59 +02:00
2025-06-07 08:51:21 +02:00
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',
2025-07-02 07:08:25 +02:00
label = "Winde bedienen",
2025-06-07 08:51:21 +02:00
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',
2025-07-02 07:08:25 +02:00
label = "Boot vom Hänger lösen",
2025-06-07 08:51:21 +02:00
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',
2025-07-02 07:08:25 +02:00
label = "Seil befestigen",
2025-06-07 08:51:21 +02:00
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',
2025-07-02 07:08:25 +02:00
label = "Seil lösen",
2025-06-07 08:51:21 +02:00
canInteract = function(entity)
if closestBoat ~= entity or not isSupportedBoat(entity) then
return false
end
return not hasTakenRope and hookedToBoat and createdRope
end
2025-07-02 06:35:52 +02:00
},
{
type = "client",
event = "re_boat_winch:client:selfDockBoat",
icon = 'fas fa-anchor',
2025-07-02 07:08:25 +02:00
label = "Boot auf Hänger sichern",
2025-07-02 06:35:52 +02:00
canInteract = function(entity)
if not isSupportedBoat(entity) then
return false
end
local trailer = IsBoatOverTrailer(entity)
return trailer ~= nil
end
2025-06-07 08:51:21 +02:00
}
},
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
2025-07-02 06:35:52 +02:00
},
{
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
2025-06-07 08:51:21 +02:00
}
})
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")
2025-07-02 06:35:52 +02:00
exports['qb-target']:RemoveGlobalVehicle("Boot am Anhänger befestigen")
2025-06-07 08:51:21 +02:00
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")
2025-07-02 06:35:52 +02:00
exports.ox_target:removeGlobalVehicle("re_boat_winch:client:selfDockBoat")
2025-06-07 08:51:21 +02:00
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)
2025-07-02 06:35:52 +02:00
-- Unfreeze the trailer when returning the rope
FreezeEntityPosition(closestTrailer, false)
SetEntityInvincible(closestTrailer, false)
2025-06-07 08:51:21 +02:00
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)
2025-07-02 06:35:52 +02:00
-- 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)
2025-06-07 08:51:21 +02:00
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
2025-07-02 06:59:46 +02:00
-- Replace the existing winch control thread with this improved version
2025-06-07 08:51:21 +02:00
CreateThread(function()
while true do
2025-07-02 06:59:46 +02:00
Wait(0)
2025-06-07 08:51:21 +02:00
if createdRope and controlRope then
2025-07-02 06:59:46 +02:00
-- 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)
2025-06-07 08:51:21 +02:00
if IsControlPressed(0, 172) then
2025-07-02 06:59:46 +02:00
ropeLength = ropeLength + lengthTick * 2 -- Doubled speed for better responsiveness
2025-06-07 08:51:21 +02:00
if ropeLength > Config.MaxRopeLength then
ropeLength = Config.MaxRopeLength
end
StopRopeWinding(createdRope)
StartRopeUnwindingFront(createdRope)
RopeForceLength(createdRope, ropeLength)
2025-07-02 06:59:46 +02:00
-- Add visual feedback
PlaySoundFrontend(-1, "NAV_UP_DOWN", "HUD_FRONTEND_DEFAULT_SOUNDSET", true)
2025-06-07 08:51:21 +02:00
elseif IsControlJustReleased(0, 172) then
StopRopeUnwindingFront(createdRope)
StopRopeWinding(createdRope)
RopeConvertToSimple(createdRope)
2025-07-02 06:59:46 +02:00
-- Retract rope (Arrow Down)
2025-06-07 08:51:21 +02:00
elseif IsControlPressed(0, 173) then
2025-07-02 06:59:46 +02:00
ropeLength = ropeLength - lengthTick * 2 -- Doubled speed for better responsiveness
2025-06-07 08:51:21 +02:00
if ropeLength < minRopeLength then
ropeLength = minRopeLength
2025-07-02 06:59:46 +02:00
-- Auto-attach boat when fully retracted
if hookedToBoat then
attachBoatToTrailer()
notify("Boot erfolgreich auf den Anhänger gezogen")
end
2025-06-07 08:51:21 +02:00
end
StopRopeUnwindingFront(createdRope)
StartRopeWinding(createdRope)
RopeForceLength(createdRope, ropeLength)
2025-07-02 06:59:46 +02:00
-- Add visual feedback
PlaySoundFrontend(-1, "NAV_UP_DOWN", "HUD_FRONTEND_DEFAULT_SOUNDSET", true)
2025-06-07 08:51:21 +02:00
elseif IsControlJustReleased(0, 173) then
StopRopeUnwindingFront(createdRope)
StopRopeWinding(createdRope)
RopeConvertToSimple(createdRope)
2025-07-02 06:59:46 +02:00
-- Cancel winch control (Backspace)
elseif IsControlJustPressed(0, 194) then
2025-06-07 08:51:21 +02:00
controlRope = false
2025-07-02 06:59:46 +02:00
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}
2025-06-07 08:51:21 +02:00
end
2025-07-02 06:59:46 +02:00
DrawTextOnScreen("Tension: " .. GetTensionText(ropeLength), 0.5, 0.08, 0.4, tensionColor)
else
Wait(1000) -- Wait longer if not controlling the winch
2025-06-07 08:51:21 +02:00
end
end
end)
2025-07-02 06:59:46 +02:00
-- 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