398 lines
16 KiB
Lua
398 lines
16 KiB
Lua
vRP = nil
|
|
ESX = nil
|
|
|
|
if ConfigHose.Notifications.Enabled and ConfigHose.Notifications.Framework.vRP then
|
|
local Tunnel = module("vrp", "lib/Tunnel")
|
|
local Proxy = module("vrp", "lib/Proxy")
|
|
vRP = Proxy.getInterface("vRP")
|
|
end
|
|
|
|
if ConfigHose.Notifications.Enabled and ConfigHose.Notifications.Framework.ESX then
|
|
ESX = exports["es_extended"]:getSharedObject()
|
|
end
|
|
|
|
if ConfigHose.EnablePositioningCommand then
|
|
TriggerEvent('chat:addSuggestion', '/'.."findhosepositioning", "Find rope spawn point with rotation", {
|
|
{ name="type", help="hose or hand" }
|
|
})
|
|
|
|
RegisterCommand("findhosepositioning", function(source, args)
|
|
local ropeLabel = (args[1] or "hose"):lower()
|
|
local ped = PlayerPedId()
|
|
|
|
local ropeType = 4
|
|
if RopeManager.RopeTypes and RopeManager.RopeTypes[ropeLabel] then
|
|
ropeType = RopeManager.RopeTypes[ropeLabel]
|
|
end
|
|
|
|
local targetVehicle = GetVehiclePedIsIn(ped, false)
|
|
if targetVehicle == 0 then targetVehicle = GetClosestVehicle(GetEntityCoords(ped)) end
|
|
if targetVehicle == 0 then return Notify("No vehicle found!") end
|
|
|
|
RopeLoadTextures()
|
|
local vehPos = GetEntityCoords(targetVehicle)
|
|
local previewRope = AddRope(vehPos.x, vehPos.y, vehPos.z, 0.0, 0.0, 0.0, 20.0, ropeType, 20.0, 0.5, 1.0, false, false, false, 5.0, false, 0)
|
|
|
|
local offSet = {x = 0.0, y = 0.0, z = 0.0}
|
|
local rotation = {x = 0.0, y = 0.0, z = 0.0}
|
|
local depth = 0.2
|
|
local mode = "MOVE"
|
|
local editing = true
|
|
|
|
Notify("~b~POSITIONING MODE~w~\n[TAB] Toggle Move/Rotate\n[Arrows] Adjust\n[INSERT/DELETE] Depth\n[ENTER] Save")
|
|
|
|
SetEntityAlpha(targetVehicle, 150, false)
|
|
|
|
while editing do
|
|
Wait(0)
|
|
local speed = IsControlPressed(0, 21) and 0.05 or 0.005
|
|
|
|
if IsControlJustPressed(0, 192) then
|
|
mode = (mode == "MOVE") and "ROTATE" or "MOVE"
|
|
Notify("Mode: ~y~" .. mode)
|
|
end
|
|
|
|
if IsControlPressed(0, 121) then depth = depth + 0.005 end
|
|
if IsControlPressed(0, 178) then depth = depth - 0.005 end
|
|
|
|
local up = IsControlPressed(0, ConfigHose.PositioningControls.up)
|
|
local down = IsControlPressed(0, ConfigHose.PositioningControls.down)
|
|
local forward = IsControlPressed(0, ConfigHose.PositioningControls.forwards)
|
|
local backward = IsControlPressed(0, ConfigHose.PositioningControls.backwards)
|
|
local left = IsControlPressed(0, ConfigHose.PositioningControls.left)
|
|
local right = IsControlPressed(0, ConfigHose.PositioningControls.right)
|
|
|
|
if mode == "MOVE" then
|
|
if up then offSet.z = offSet.z + speed end
|
|
if down then offSet.z = offSet.z - speed end
|
|
if forward then offSet.y = offSet.y + speed end
|
|
if backward then offSet.y = offSet.y - speed end
|
|
if left then offSet.x = offSet.x - speed end
|
|
if right then offSet.x = offSet.x + speed end
|
|
else
|
|
local rSpeed = speed * 50.0
|
|
if up then rotation.x = rotation.x + rSpeed end
|
|
if down then rotation.x = rotation.x - rSpeed end
|
|
if left then rotation.z = rotation.z + rSpeed end
|
|
if right then rotation.z = rotation.z - rSpeed end
|
|
end
|
|
|
|
local forwardVec = RotationToDirection(rotation)
|
|
local localPortPos = vector3(offSet.x, offSet.y, offSet.z)
|
|
local localBendPosA = localPortPos - (forwardVec * depth)
|
|
local localBendPosB = localPortPos - (forwardVec * (depth * 0.5))
|
|
|
|
local worldPointC = GetOffsetFromEntityInWorldCoords(targetVehicle, localPortPos.x, localPortPos.y, localPortPos.z)
|
|
local worldPointA = GetOffsetFromEntityInWorldCoords(targetVehicle, localBendPosA.x, localBendPosA.y, localBendPosA.z)
|
|
local worldPointB = GetOffsetFromEntityInWorldCoords(targetVehicle, localBendPosB.x, localBendPosB.y, localBendPosB.z)
|
|
local handPos = GetWorldPositionOfEntityBone(ped, GetPedBoneIndex(ped, 6286))
|
|
local vertexCount = GetRopeVertexCount(previewRope)
|
|
|
|
PinRopeVertex(previewRope, 0, handPos.x, handPos.y, handPos.z)
|
|
PinRopeVertex(previewRope, vertexCount - 1, worldPointA.x, worldPointA.y, worldPointA.z)
|
|
PinRopeVertex(previewRope, vertexCount - 2, worldPointB.x, worldPointB.y, worldPointB.z)
|
|
PinRopeVertex(previewRope, vertexCount - 3, worldPointC.x, worldPointC.y, worldPointC.z)
|
|
|
|
if IsControlJustPressed(0, ConfigHose.PositioningControls.enter) then editing = false end
|
|
end
|
|
|
|
ResetEntityAlpha(targetVehicle)
|
|
|
|
print(string.format("^2--- %s RESULT ---^7", ropeLabel:upper()))
|
|
if ropeLabel == "hose" then
|
|
print(string.format('{ x = %.3f, y = %.3f, z = %.3f, rx = %.3f, ry = %.3f, rz = %.3f, depth = %.3f, type = "%s", portId = "attack1" },', offSet.x, offSet.y, offSet.z, rotation.x, rotation.y, rotation.z, depth, ropeLabel))
|
|
else
|
|
print(string.format('{ x = %.3f, y = %.3f, z = %.3f, rx = %.3f, ry = %.3f, rz = %.3f, depth = %.3f, type = "%s" },', offSet.x, offSet.y, offSet.z, rotation.x, rotation.y, rotation.z, depth, ropeLabel))
|
|
end
|
|
|
|
DeleteRope(previewRope)
|
|
Notify("Data printed to F8 console.")
|
|
end, false)
|
|
end
|
|
|
|
function Notify(text)
|
|
|
|
if not ConfigHose.Notifications.Enabled then
|
|
return
|
|
end
|
|
|
|
if ConfigHose.Notifications.Framework.ESX then
|
|
if ESX ~= nil then
|
|
ESX.ShowNotification(text)
|
|
end
|
|
elseif ConfigHose.Notifications.Framework.QBCore then
|
|
TriggerEvent('QBCore:Notify', text, 'primary')
|
|
elseif ConfigHose.Notifications.Framework.QBX then
|
|
exports.qbx_core:Notify(text, 'primary')
|
|
elseif ConfigHose.Notifications.Framework.vRP then
|
|
vRP.notify(source, {text})
|
|
elseif ConfigHose.Notifications.Framework.okok then
|
|
exports['okokNotify']:Alert("Smart Hose", text, 2000, 'info', true)
|
|
else
|
|
showBaseNotification(text)
|
|
end
|
|
end
|
|
|
|
function showBaseNotification(message)
|
|
-- Base game notifications
|
|
SetNotificationTextEntry("STRING")
|
|
AddTextComponentString(message)
|
|
DrawNotification(0,1)
|
|
end
|
|
|
|
-- @GeneralFunctions
|
|
function DisplayHelpText(text)
|
|
SetTextComponentFormat("STRING")
|
|
AddTextComponentString(text)
|
|
DisplayHelpTextFromStringLabel(0, 0, ConfigHose.Notifications.HelpTextSound, -1)
|
|
end
|
|
|
|
function drawInstructionalText(msg, coords)
|
|
AddTextEntry('instructionalText', msg)
|
|
SetFloatingHelpTextWorldPosition(1, coords)
|
|
SetFloatingHelpTextStyle(1, 1, 2, -1, 3, 0)
|
|
BeginTextCommandDisplayHelp('instructionalText')
|
|
AddTextComponentSubstringPlayerName(msg)
|
|
EndTextCommandDisplayHelp(2, false, false, -1)
|
|
end
|
|
|
|
function IsFireHoseEnabled()
|
|
return (HoseManager.hasFireHose and not HoseManager.nozzleDropped)
|
|
end
|
|
exports("IsFireHoseEnabled", IsFireHoseEnabled)
|
|
|
|
function IsFireHoseShooting()
|
|
return HoseManager.pressed
|
|
end
|
|
exports("IsFireHoseShooting", IsFireHoseShooting)
|
|
|
|
function IsFireHoseShootingWater()
|
|
return ParticleManager.isWaterDecal
|
|
end
|
|
exports("IsFireHoseShootingWater", IsFireHoseShootingWater)
|
|
|
|
function IsVehicleUsingWater(vehicleNetID)
|
|
local settings = HoseManager.vehicleSettings[vehicleNetID]
|
|
if not settings then return true end
|
|
return settings.decalType
|
|
end
|
|
exports("IsVehicleUsingWater", IsVehicleUsingWater)
|
|
|
|
function GetCurrentDecalCoords()
|
|
return ParticleManager.CurrentDecalCoords
|
|
end
|
|
exports("GetCurrentDecalCoords", GetCurrentDecalCoords)
|
|
|
|
function GetCurrentHosePressure()
|
|
return ParticleManager.particleScale
|
|
end
|
|
exports("GetCurrentHosePressure", GetCurrentHosePressure)
|
|
|
|
if ConfigHose.InteractType.TruckInteraction.Custom then
|
|
local function _toNetId(ent)
|
|
if ent and DoesEntityExist(ent) then return ToNetId(ent) end
|
|
return nil
|
|
end
|
|
|
|
local function _neighborsFor(netId)
|
|
local list = {}
|
|
if not netId then return list end
|
|
local RM = RopeManager or {}
|
|
local cv = RM.connectedVehicles and RM.connectedVehicles[netId]
|
|
if type(cv) == 'number' then list[#list+1] = cv
|
|
elseif type(cv) == 'table' then for nid,_ in pairs(cv) do list[#list+1] = tonumber(nid) or nid end end
|
|
local ic = RM.incomingConnections and RM.incomingConnections[netId]
|
|
if type(ic) == 'number' then list[#list+1] = ic
|
|
elseif type(ic) == 'table' then for nid,_ in pairs(ic) do list[#list+1] = tonumber(nid) or nid end end
|
|
return list
|
|
end
|
|
|
|
local function _buildCtx(vehicle)
|
|
local ped = PlayerPedId()
|
|
local pos = GetEntityCoords(ped)
|
|
|
|
local model = GetEntityModel(vehicle)
|
|
local vs = (ConfigHose.Rope.VehicleSettings[model] or {})
|
|
local useBone = vs.useBone or false
|
|
local bones = vs.bones or {}
|
|
local offsets = vs.offsets or { { x = 0.0, y = 0.0, z = 0.0 } }
|
|
local pts = getAttachmentPoints(vehicle, useBone, bones, offsets)
|
|
if type(pts) ~= 'table' or #pts == 0 then pts = { GetEntityCoords(vehicle) } end
|
|
|
|
local closest = pts[1]
|
|
local minDist = #(pos - closest)
|
|
for i = 2, #pts do
|
|
local p = pts[i]
|
|
local d = #(pos - p)
|
|
if d < minDist then closest, minDist = p, d end
|
|
end
|
|
|
|
local meId = GetPlayerServerId(PlayerId())
|
|
local vehNet = _toNetId(vehicle)
|
|
local neigh = _neighborsFor(vehNet)
|
|
|
|
return {
|
|
ped = ped,
|
|
playerServerId = meId,
|
|
|
|
vehicle = vehicle,
|
|
vehNet = vehNet,
|
|
vehModel = model,
|
|
promptCoords = closest,
|
|
vehicleDistance = minDist,
|
|
neighbors = neigh,
|
|
|
|
nozzle = HoseManager.nozzle,
|
|
nozzleDropped = HoseManager.nozzleDropped,
|
|
currentVehicle = HoseManager.currentVehicle,
|
|
handLineActive = HoseManager.handLine[meId] and true or false,
|
|
currentVehicleRelay = HoseManager.currentVehicleRelay,
|
|
supplyEnabled = IsSupplyLineEnabled(),
|
|
}
|
|
end
|
|
|
|
-- Actions you can call from your custom interactions
|
|
HoseCustomActions = {
|
|
GrabHose = function(ctx)
|
|
HoseManager.currentVehicle = ctx.vehicle
|
|
InitializeFireHose()
|
|
AttachRopeToProp()
|
|
end,
|
|
StoreHose = function(ctx)
|
|
if HoseManager.nozzle and HoseManager.currentVehicle == ctx.vehicle and not HoseManager.nozzleDropped then
|
|
UnequipFireHose()
|
|
else
|
|
Notify(ConfigHose.Translations.cannotStoreHoseWhileOnGround)
|
|
end
|
|
end,
|
|
GrabRelay = function(ctx)
|
|
HoseManager.currentVehicleRelay = ctx.vehicle
|
|
InitializeHandLine()
|
|
AttachRopeToHand()
|
|
end,
|
|
StoreRelay = function(_ctx)
|
|
DeinitializeHandLine()
|
|
end,
|
|
ConnectRelayHere = function(ctx)
|
|
DeinitializeHandLine()
|
|
ConnectRelayToVehicle(ctx.vehicle)
|
|
end,
|
|
QuickDisconnectNearest = function(ctx)
|
|
if not ctx.vehNet or #ctx.neighbors == 0 then return end
|
|
local best, bestD, myPos = nil, 1e9, GetEntityCoords(PlayerPedId())
|
|
for _, n in ipairs(ctx.neighbors) do
|
|
local ent = NetEnt(n)
|
|
if ent and DoesEntityExist(ent) then
|
|
local d = #(myPos - GetEntityCoords(ent))
|
|
if d < bestD then best, bestD = n, d end
|
|
elseif not best then
|
|
best = n
|
|
end
|
|
end
|
|
if best then
|
|
local sId, tId = canonicalPair(ctx.vehNet, best)
|
|
DisconnectRelayLine(sId, tId, ctx.vehNet)
|
|
end
|
|
end,
|
|
OpenPanel = function(_ctx)
|
|
OpenControlPanel()
|
|
end
|
|
}
|
|
|
|
-- ctx = _buildCtx(vehicle) (state for the vehicle/player)
|
|
-- A = HoseCustomActions (ready actions)
|
|
-- Draw helpers available: drawInstructionalText() / DisplayHelpText() from this file.
|
|
-- Example below mirrors the original E/G/Z flow aka ConfigHose.TruckInteraction.InteractType.Drawtext.
|
|
if not CustomInteractThink then
|
|
-- CustomInteractThink = function(ctx, A)
|
|
-- local lines = {}
|
|
|
|
-- if ctx.nozzle then
|
|
-- if ctx.currentVehicle == ctx.vehicle and not ctx.nozzleDropped then
|
|
-- lines[#lines+1] = ConfigHose.Translations.storeHoseMessage
|
|
-- if IsControlJustReleased(0, ConfigHose.Keys.Interact) then A.StoreHose(ctx) end
|
|
-- else
|
|
-- lines[#lines+1] = ConfigHose.Translations.cannotStoreHoseWhileOnGround
|
|
-- end
|
|
|
|
-- elseif ctx.handLineActive then
|
|
-- if ctx.supplyEnabled then
|
|
-- if ctx.currentVehicleRelay == ctx.vehicle then
|
|
-- if #ctx.neighbors > 0 then lines[#lines+1] = ConfigHose.Translations.disconnectLine end
|
|
-- lines[#lines+1] = ConfigHose.Translations.storeSupplyLine
|
|
-- if IsControlJustReleased(0, ConfigHose.Keys.GrabLine) then
|
|
-- if #ctx.neighbors > 0 then A.QuickDisconnectNearest(ctx) else A.StoreRelay(ctx) end
|
|
-- end
|
|
-- else
|
|
-- if #ctx.neighbors > 0 then
|
|
-- lines[#lines+1] = ConfigHose.Translations.disconnectLine
|
|
-- if IsControlJustReleased(0, ConfigHose.Keys.GrabLine) then A.QuickDisconnectNearest(ctx) end
|
|
-- else
|
|
-- lines[#lines+1] = ConfigHose.Translations.connectRelayLineMessage
|
|
-- if IsControlJustReleased(0, ConfigHose.Keys.GrabLine) then A.ConnectRelayHere(ctx) end
|
|
-- end
|
|
-- end
|
|
-- end
|
|
|
|
-- else
|
|
-- if ctx.supplyEnabled then
|
|
-- if #ctx.neighbors > 0 then
|
|
-- lines[#lines+1] = ConfigHose.Translations.grabHoseMessage
|
|
-- lines[#lines+1] = ConfigHose.Translations.disconnectLine
|
|
-- else
|
|
-- lines[#lines+1] = ConfigHose.Translations.grabHoseMessage
|
|
-- lines[#lines+1] = ConfigHose.Translations.SupplyLine
|
|
-- end
|
|
-- else
|
|
-- lines[#lines+1] = ConfigHose.Translations.grabHoseMessage
|
|
-- end
|
|
|
|
-- if IsControlJustReleased(0, ConfigHose.Keys.Interact) then A.GrabHose(ctx) end
|
|
-- if ctx.supplyEnabled and IsControlJustReleased(0, ConfigHose.Keys.GrabLine) then A.GrabRelay(ctx) end
|
|
-- end
|
|
|
|
-- -- SHS Panel
|
|
-- if not ctx.nozzle and ctx.supplyEnabled then
|
|
-- local showPanelHint = (ctx.handLineActive and ctx.currentVehicleRelay == ctx.vehicle) or (not ctx.nozzle and not ctx.handLineActive)
|
|
-- if showPanelHint then
|
|
-- lines[#lines+1] = ConfigHose.Translations.interactDisplay
|
|
-- if IsControlJustReleased(0, ConfigHose.Keys.OpenDisplayPrompt) then A.OpenPanel(ctx) end
|
|
-- end
|
|
-- end
|
|
|
|
-- if #lines > 0 then drawInstructionalText(table.concat(lines, "\n"), ctx.promptCoords) end
|
|
-- end
|
|
|
|
CustomInteractThink = function(ctx, A)
|
|
-- Write logic here
|
|
-- ctx = live state for the closest fire vehicle
|
|
-- A = actions you can call
|
|
end
|
|
end
|
|
|
|
CreateThread(function()
|
|
while true do
|
|
Wait(1)
|
|
|
|
local ped = PlayerPedId()
|
|
if IsPedInAnyVehicle(ped, false) then
|
|
Wait(500)
|
|
else
|
|
local pos = GetEntityCoords(ped)
|
|
local vehicle = GetClosestVehicle(pos)
|
|
|
|
if vehicle and DoesEntityExist(vehicle) and IsVehicleDriveable(vehicle, false) and HasWaterTank(vehicle) then
|
|
local ctx = _buildCtx(vehicle)
|
|
if ctx.vehicleDistance < 3.0 then
|
|
local ok, err = pcall(CustomInteractThink, ctx, HoseCustomActions)
|
|
if not ok then print("^1[CustomInteract] error:^0", err) end
|
|
else
|
|
Wait(250)
|
|
end
|
|
else
|
|
Wait(750)
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
end |