I was in the passenger seat, in the helicam view, I pressed G to turn on the spotlight, the manual spotlight did not come on for others as I looked around unless I kept turning it on and off until they said they could see it. I then swapped over with someone else where we then had the same issue. We also tried looking at a vehicle and pressing space to lock on with the spotlight enabled, this resulted in the same issue.
Mysterious. In what sense did the manual spotlight (helicam) work at all for other clients, when you turned it on and off continuously?
Based on your description, I am guessing that when you turned it on and off continuously they saw a flickering spotlight and not a constant beam? From which we can presume that clients are in fact getting a spotlight but it gets cut off almost immediately, so the pilot flicking it on and off rapidly just provides a series of spotlight flashes that quickly disappear?
Great plugin! I’ve changed a few things, one of them is that I’ve added the ability to zoom in/out using a controller, since a controller doesn’t have a scroll wheel . I’ve also managed to have the spotlight follow the camera position.
The only thing I can’t figure out is to sync the helicopter cam with all people inside the helicopter (if they enable the camera), and have that spotlight (using the
DrawSpotLight(camcoords, forward_vector, 255, 255, 255, 300.0, 10.0, 0.0, 2.0, 1.0)
function) be synced to everyone on the server.
If anyone knows how I can achieve that, please let me know.
(Code) zoom controller support
- Zoom in (hold) L3 (press left stick)
- Zoom out (hold) R3 (press right stick)
function HandleZoom(cam)
if (IsControlJustPressed(0,241) or IsControlPressed(0, 230)) then -- Scrollup & Left Stick (L3)
fov = math.max(fov - zoomspeed, fov_min)
end
if (IsControlJustPressed(0,242) or IsControlPressed(0, 231)) then -- scrolldown & Right Stick (R3)
fov = math.min(fov + zoomspeed, fov_max) -- ScrollDown
end
local current_fov = GetCamFov(cam)
if math.abs(fov-current_fov) < 0.1 then -- the difference is too small, just set the value directly to avoid unneeded updates to FOV of order 10^-5
fov = current_fov
end
SetCamFov(cam, current_fov + (fov - current_fov)*0.05) -- Smoothing of camera zoom
end
To sync the free aiming spotlight you only need to sync the forward_vector, since the cam/heli coordinates can be obtained independently by each client. Syncing the forward_vector AFAIK must be done either by continuous client-server events or by client-client communication via decorators.
I have tried both methods and the former method creates a constant flickering light (as well as serious load on the server) and seems sub-optimal. The latter method seems more efficient and is attempted in the modified script above. I think it is workable but is somewhat laggy for remote clients when the camera/spotlight is repositioned.
I have reworked the syncing of the various spotlights modes on the modified version, which were disfunctional in multiplayer, and I will post something soon I hope, after testing a bit more.
Alright thanks for the info, I’ll keep an eye out for that
Well if anyone wants to give this a ‘whirl’ below is a revised attempt to modify mraes’ script, as I described above. This hopefully fixes the multiplayer sync issues. I also tried adding a new setting to increase/decrease the manual spotlight radius (using capslock/leftshift keys) and to sync this and rjross2013’s brightness setting across clients.
I have tested it on FX and CFX servers with one other person (no serious stress testing on a big server). The only anomaly I noticed is that periodically the tracking spotlight will disappear at far distances on the remote non-pilot client, a bit before it disappears for the heli pilot at the appropriate spotlight maximum target distance as set in the configuration. I believe this is because the target vehicle is lost on the network for the remote client, for some reason, even though the target is set as a mission entity. The syncing of the manual free-aiming spotlight works but is somewhat laggy during adjustments for non-pilot clients – AFAIK this is unavoidable but to me it is still worthwhile.
Under the hood there are other similar peculiarities because even with an newer resource manifest with reported networking updates, I am not always getting global networked entities with 100% reliable netid <-> entity conversions. But with some fail-safes to compensate, the target vehicles and corresponding spotlights do seem to be getting synced reliably (in my testing anyway).
__resource.lua:
-- Manifest
resource_manifest_version '05cfa83c-a124-4cfa-a768-c24a5811d8f9' -- newer resource manifest with changes to network code
description 'FiveM LSPD Heli Cam by mraes'
client_script 'heli_client.lua'
server_script 'heli_server.lua'
heli_client.lua:
-- FiveM Heli Cam by mraes, version 1.3 (2017-06-12)
-- Modified by rjross2013 (2017-06-23)
-- Further modified by Loque (2017-08-15) with credits to the following for tips gleaned from their scripts: Guadmaz's Simple Police Searchlight, devilkkw's Speed Camera, nynjardin's Simple Outlaw Alert and IllidanS4's FiveM Entity Iterators.
-- Config
local fov_max = 80.0
local fov_min = 5.0 -- max zoom level (smaller fov is more zoom)
local zoomspeed = 3.0 -- camera zoom speed
local speed_lr = 4.0 -- speed by which the camera pans left-right
local speed_ud = 4.0 -- speed by which the camera pans up-down
local toggle_helicam = 51 -- control id of the button by which to toggle the helicam mode. Default: INPUT_CONTEXT (E)
local toggle_vision = 25 -- control id to toggle vision mode. Default: INPUT_AIM (Right mouse btn)
local toggle_rappel = 154 -- control id to rappel out of the heli. Default: INPUT_DUCK (X)
local toggle_spotlight = 183 -- control id to toggle the various spotlight states Default: INPUT_PhoneCameraGrid (G)
local toggle_lock_on = 22 -- control id to lock onto a vehicle with the camera or unlock from vehicle (with or without camera). Default is INPUT_SPRINT (spacebar)
local toggle_display = 44 -- control id to toggle vehicle info display. Default: INPUT_COVER (Q)
local lightup_key = 246 -- control id to increase spotlight brightness. Default: INPUT_MP_TEXT_CHAT_TEAM (Y)
local lightdown_key = 173 -- control id to decrease spotlight brightness. Default: INPUT_CELLPHONE_DOWN (ARROW-DOWN)
local radiusup_key = 137 -- control id to increase manual spotlight radius. Default: INPUT_VEH_PUSHBIKE_SPRINT (CAPSLOCK)
local radiusdown_key = 21 -- control id to decrease spotlight radius. Default: INPUT_SPRINT (LEFT-SHIFT)
local maxtargetdistance = 700 -- max distance at which target lock is maintained
local brightness = 1.0 -- default spotlight brightness
local spotradius = 4.0 -- default manual spotlight radius
local speed_measure = "Km/h" -- default unit to measure vehicle speed but can be changed to "MPH". Use either exact string, "Km/h" or "MPH", or else functions break.
-- Script starts here
local target_vehicle = nil
local manual_spotlight = false
local tracking spotlight = false
local vehicle_display = 0 -- 0 is default full vehicle info display with speed/model/plate, 1 is model/plate, 2 turns off display
local helicam = false
local polmav_hash = GetHashKey("polmav")
local fov = (fov_max+fov_min)*0.5
local vision_state = 0 -- 0 is normal, 1 is nightmode, 2 is thermal vision
Citizen.CreateThread(function() -- Register ped decorators used to pass some variables from heli pilot to other players (variable settings: 1=false, 2=true)
while true do
Citizen.Wait(0)
if NetworkIsSessionStarted() then
DecorRegister("SpotvectorX", 3) -- For direction of manual spotlight
DecorRegister("SpotvectorY", 3)
DecorRegister("SpotvectorZ", 3)
DecorRegister("Target", 3) -- Backup method of target ID
return
end
end
end)
Citizen.CreateThread(function()
while true do
Citizen.Wait(0)
if IsPlayerInPolmav() then
local lPed = GetPlayerPed(-1)
local heli = GetVehiclePedIsIn(lPed)
if IsHeliHighEnough(heli) then
if IsControlJustPressed(0, toggle_helicam) then -- Toggle Helicam
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
helicam = true
end
if IsControlJustPressed(0, toggle_rappel) then -- Initiate rappel
Citizen.Trace("try to rappel")
if GetPedInVehicleSeat(heli, 1) == lPed or GetPedInVehicleSeat(heli, 2) == lPed then
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
TaskRappelFromHeli(GetPlayerPed(-1), 1)
else
SetNotificationTextEntry( "STRING" )
AddTextComponentString("~r~Can't rappel from this seat")
DrawNotification(false, false )
PlaySoundFrontend(-1, "5_Second_Timer", "DLC_HEISTS_GENERAL_FRONTEND_SOUNDS", false)
end
end
end
if IsControlJustPressed(0, toggle_spotlight) and GetPedInVehicleSeat(heli, -1) == lPed and not helicam then -- Toggle forward and tracking spotlight states
if target_vehicle then
if tracking_spotlight then
if not pause_Tspotlight then
pause_Tspotlight = true
TriggerServerEvent("heli:pause.tracking.spotlight", pause_Tspotlight)
else
pause_Tspotlight = false
TriggerServerEvent("heli:pause.tracking.spotlight", pause_Tspotlight)
end
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
else
if Fspotlight_state then
Fspotlight_state = false
TriggerServerEvent("heli:forward.spotlight", Fspotlight_state)
end
local target_netID = VehToNet(target_vehicle)
local target_plate = GetVehicleNumberPlateText(target_vehicle)
local targetposx, targetposy, targetposz = table.unpack(GetEntityCoords(target_vehicle))
pause_Tspotlight = false
tracking_spotlight = true
TriggerServerEvent("heli:tracking.spotlight", target_netID, target_plate, targetposx, targetposy, targetposz)
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
end
else
if tracking_spotlight then
pause_Tspotlight = false
tracking_spotlight = false
TriggerServerEvent("heli:tracking.spotlight.toggle")
end
Fspotlight_state = not Fspotlight_state
TriggerServerEvent("heli:forward.spotlight", Fspotlight_state)
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
end
end
if IsControlJustPressed(0, toggle_display) and GetPedInVehicleSeat(heli, -1) == lPed then
ChangeDisplay()
end
if target_vehicle and GetPedInVehicleSeat(heli, -1) == lPed then
local coords1 = GetEntityCoords(heli)
local coords2 = GetEntityCoords(target_vehicle)
local target_distance = GetDistanceBetweenCoords(coords1.x, coords1.y, coords1.z, coords2.x, coords2.y, coords2.z, false)
if IsControlJustPressed(0, toggle_lock_on) or target_distance > maxtargetdistance then
--Citizen.Trace("Heli: target vehicle released or lost")
DecorRemove(target_vehicle, "Target")
if tracking_spotlight then
TriggerServerEvent("heli:tracking.spotlight.toggle")
end
tracking_spotlight = false
pause_Tspotlight = false
target_vehicle = nil
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
end
end
end
if helicam then
SetTimecycleModifier("heliGunCam")
SetTimecycleModifierStrength(0.3)
local scaleform = RequestScaleformMovie("HELI_CAM")
while not HasScaleformMovieLoaded(scaleform) do
Citizen.Wait(0)
end
local lPed = GetPlayerPed(-1)
local heli = GetVehiclePedIsIn(lPed)
local cam = CreateCam("DEFAULT_SCRIPTED_FLY_CAMERA", true)
AttachCamToEntity(cam, heli, 0.0,0.0,-1.5, true)
SetCamRot(cam, 0.0,0.0,GetEntityHeading(heli))
SetCamFov(cam, fov)
RenderScriptCams(true, false, 0, 1, 0)
PushScaleformMovieFunction(scaleform, "SET_CAM_LOGO")
PushScaleformMovieFunctionParameterInt(0) -- 0 for nothing, 1 for LSPD logo
PopScaleformMovieFunctionVoid()
local locked_on_vehicle = nil
while helicam and not IsEntityDead(lPed) and (GetVehiclePedIsIn(lPed) == heli) and IsHeliHighEnough(heli) do
if IsControlJustPressed(0, toggle_helicam) then -- Toggle Helicam
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
if manual_spotlight and target_vehicle then -- If exiting helicam while manual spotlight is locked on a target, transition to non-helicam auto tracking spotlight
TriggerServerEvent("heli:manual.spotlight.toggle")
local target_netID = VehToNet(target_vehicle)
local target_plate = GetVehicleNumberPlateText(target_vehicle)
local targetposx, targetposy, targetposz = table.unpack(GetEntityCoords(target_vehicle))
pause_Tspotlight = false
tracking_spotlight = true
TriggerServerEvent("heli:tracking.spotlight", target_netID, target_plate, targetposx, targetposy, targetposz)
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
end
manual_spotlight = false
helicam = false
end
if IsControlJustPressed(0, toggle_vision) then
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
ChangeVision()
end
if IsControlJustPressed(0, toggle_spotlight) then -- Spotlight_toggles within helicam
if tracking_spotlight then -- If tracking spotlight active, pause it & toggle manual spotlight
pause_Tspotlight = true
TriggerServerEvent("heli:pause.tracking.spotlight", pause_Tspotlight)
manual_spotlight = not manual_spotlight
if manual_spotlight then
local rotation = GetCamRot(cam, 2)
local forward_vector = RotAnglesToVec(rotation)
local SpotvectorX, SpotvectorY, SpotvectorZ = table.unpack(forward_vector)
DecorSetInt(lPed, "SpotvectorX", SpotvectorX)
DecorSetInt(lPed, "SpotvectorY", SpotvectorY)
DecorSetInt(lPed, "SpotvectorZ", SpotvectorZ)
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
TriggerServerEvent("heli:manual.spotlight")
else
TriggerServerEvent("heli:manual.spotlight.toggle")
end
elseif Fspotlight_state then -- If forward spotlight active, disable it & toggle manual spotlight
Fspotlight_state = false
TriggerServerEvent("heli:forward.spotlight", Fspotlight_state)
manual_spotlight = not manual_spotlight
if manual_spotlight then
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
TriggerServerEvent("heli:manual.spotlight")
else
TriggerServerEvent("heli:manual.spotlight.toggle")
end
else -- If no other spotlight mode active, toggle manual spotlight
manual_spotlight = not manual_spotlight
if manual_spotlight then
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
TriggerServerEvent("heli:manual.spotlight")
else
TriggerServerEvent("heli:manual.spotlight.toggle")
end
end
end
if IsControlJustPressed(0, lightup_key) then
TriggerServerEvent("heli:light.up")
end
if IsControlJustPressed(0, lightdown_key) then
TriggerServerEvent("heli:light.down")
end
if IsControlJustPressed(0, radiusup_key) then
TriggerServerEvent("heli:radius.up")
end
if IsControlJustPressed(0, radiusdown_key) then
TriggerServerEvent("heli:radius.down")
end
if IsControlJustPressed(0, toggle_display) then
ChangeDisplay()
end
if locked_on_vehicle then
if DoesEntityExist(locked_on_vehicle) then
PointCamAtEntity(cam, locked_on_vehicle, 0.0, 0.0, 0.0, true)
RenderVehicleInfo(locked_on_vehicle)
local coords1 = GetEntityCoords(heli)
local coords2 = GetEntityCoords(locked_on_vehicle)
local target_distance = GetDistanceBetweenCoords(coords1.x, coords1.y, coords1.z, coords2.x, coords2.y, coords2.z, false)
if IsControlJustPressed(0, toggle_lock_on) or target_distance > maxtargetdistance then
--Citizen.Trace("Heli: locked_on_vehicle unlocked or lost")
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
DecorRemove(target_vehicle, "Target")
if tracking_spotlight then
TriggerServerEvent("heli:tracking.spotlight.toggle")
tracking_spotlight = false
end
target_vehicle = nil
locked_on_vehicle = nil
local rot = GetCamRot(cam, 2) -- All this because I can't seem to get the camera unlocked from the entity
local fov = GetCamFov(cam)
local old cam = cam
DestroyCam(old_cam, false)
cam = CreateCam("DEFAULT_SCRIPTED_FLY_CAMERA", true)
AttachCamToEntity(cam, heli, 0.0,0.0,-1.5, true)
SetCamRot(cam, rot, 2)
SetCamFov(cam, fov)
RenderScriptCams(true, false, 0, 1, 0)
end
else
locked_on_vehicle = nil -- Cam will auto unlock when entity doesn't exist anyway
target_vehicle = nil
end
else
local zoomvalue = (1.0/(fov_max-fov_min))*(fov-fov_min)
CheckInputRotation(cam, zoomvalue)
local vehicle_detected = GetVehicleInView(cam)
if DoesEntityExist(vehicle_detected) then
RenderVehicleInfo(vehicle_detected)
if IsControlJustPressed(0, toggle_lock_on) then
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
locked_on_vehicle = vehicle_detected
if target_vehicle then -- If previous target exists, remove old target decorator before updating target vehicle
DecorRemove(target_vehicle, "Target")
end
target_vehicle = vehicle_detected
NetworkRequestControlOfEntity(target_vehicle)
local target_netID = VehToNet(target_vehicle)
SetNetworkIdCanMigrate(target_netID, true)
NetworkRegisterEntityAsNetworked(VehToNet(target_vehicle))
SetNetworkIdExistsOnAllMachines(target_vehicle, true)
SetEntityAsMissionEntity(target_vehicle, true, true)
target_plate = GetVehicleNumberPlateText(target_vehicle)
DecorSetInt(locked_on_vehicle, "Target", 2)
if tracking_spotlight then -- If tracking previous target, terminate and start tracking new target
TriggerServerEvent("heli:tracking.spotlight.toggle")
target_vehicle = locked_on_vehicle
if not pause_Tspotlight then -- If spotlight was paused when tracking old target,
local target_netID = VehToNet(target_vehicle)
local target_plate = GetVehicleNumberPlateText(target_vehicle)
local targetposx, targetposy, targetposz = table.unpack(GetEntityCoords(target_vehicle))
pause_Tspotlight = false
tracking_spotlight = true
TriggerServerEvent("heli:tracking.spotlight", target_netID, target_plate, targetposx, targetposy, targetposz)
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
else
tracking_spotlight = false
pause_Tspotlight = false
end
end
end
end
end
HandleZoom(cam)
HideHUDThisFrame()
PushScaleformMovieFunction(scaleform, "SET_ALT_FOV_HEADING")
PushScaleformMovieFunctionParameterFloat(GetEntityCoords(heli).z)
PushScaleformMovieFunctionParameterFloat(zoomvalue)
PushScaleformMovieFunctionParameterFloat(GetCamRot(cam, 2).z)
PopScaleformMovieFunctionVoid()
DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255)
Citizen.Wait(0)
if manual_spotlight then -- Continuously update manual spotlight direction, sync client-client with decorators
local rotation = GetCamRot(cam, 2)
local forward_vector = RotAnglesToVec(rotation)
local SpotvectorX, SpotvectorY, SpotvectorZ = table.unpack(forward_vector)
local camcoords = GetCamCoord(cam)
DecorSetInt(lPed, "SpotvectorX", SpotvectorX)
DecorSetInt(lPed, "SpotvectorY", SpotvectorY)
DecorSetInt(lPed, "SpotvectorZ", SpotvectorZ)
DrawSpotLight(camcoords, forward_vector, 255, 255, 255, 800.0, 10.0, brightness, spotradius, 1.0, 1.0)
else
TriggerServerEvent("heli:manual.spotlight.toggle")
end
end
if manual_spotlight then
manual_spotlight = false
TriggerServerEvent("heli:manual.spotlight.toggle")
end
helicam = false
ClearTimecycleModifier()
fov = (fov_max+fov_min)*0.5 -- reset to starting zoom level
RenderScriptCams(false, false, 0, 1, 0) -- Return to gameplay camera
SetScaleformMovieAsNoLongerNeeded(scaleform) -- Cleanly release the scaleform
DestroyCam(cam, false)
SetNightvision(false)
SetSeethrough(false)
end
if IsPlayerInPolmav() and target_vehicle and not helicam and vehicle_display ~=2 then
RenderVehicleInfo(target_vehicle)
end
end
end)
RegisterNetEvent('heli:forward.spotlight')
AddEventHandler('heli:forward.spotlight', function(serverID, state)
local heli = GetVehiclePedIsIn(GetPlayerPed(GetPlayerFromServerId(serverID)), false)
SetVehicleSearchlight(heli, state, false)
end)
RegisterNetEvent('heli:Tspotlight')
AddEventHandler('heli:Tspotlight', function(serverID, target_netID, target_plate, targetposx, targetposy, targetposz)
-- Client target identification and verification, with fail-safes until FiveM code around global networked entities is sorted out
if GetVehicleNumberPlateText(NetToVeh(target_netID)) == target_plate then
Tspotlight_target = NetToVeh(target_netID)
elseif GetVehicleNumberPlateText(DoesVehicleExistWithDecorator("Target")) == target_plate then
Tspotlight_target = DoesVehicleExistWithDecorator("Target")
--Citizen.Trace("Client target ID by primary netID method failed! Secondary decorator-based method worked.")
elseif GetVehicleNumberPlateText(GetClosestVehicle(targetposx, targetposy, targetposz, 25.0, 0, 70)) == target_plate then
Tspotlight_target = GetClosestVehicle(targetposx, targetposy, targetposz, 25.0, 0, 70)
--Citizen.Trace("Heli: client target ID methods based on netID and decorator both failed! Tertiary method using target coordinates worked.")
else
vehicle_match = FindVehicleByPlate(target_plate)
if vehicle_match then
Tspotlight_target = vehicle_match
--Citizen.Trace("Heli: client target ID methods based on netID, decorator and coords all failed! Final method of searching vehicles by plate worked.")
else
Tspotlight_target = nil
--Citizen.Trace("Heli: all methods of client target ID failed!!")
end
end
local heli = GetVehiclePedIsIn(GetPlayerPed(GetPlayerFromServerId(serverID)), false)
local heliPed = GetPlayerPed(GetPlayerFromServerId(serverID))
Tspotlight_toggle = true
Tspotlight_pause = false
tracking_spotlight = true
while not IsEntityDead(heliPed) and (GetVehiclePedIsIn(heliPed) == heli) and Tspotlight_target and Tspotlight_toggle do
Citizen.Wait(1)
local helicoords = GetEntityCoords(heli)
local targetcoords = GetEntityCoords(Tspotlight_target)
local spotVector = targetcoords - helicoords
local target_distance = Vdist(targetcoords, helicoords)
if Tspotlight_target and Tspotlight_toggle and not Tspotlight_pause then -- Redundant condition seems needed here or a function breaks
DrawSpotLight(helicoords['x'], helicoords['y'], helicoords['z'], spotVector['x'], spotVector['y'], spotVector['z'], 255, 255, 255, (target_distance+20), 10.0, brightness, 4.0, 1.0, 0.0)
end
if Tspotlight_target and Tspotlight_toggle and target_distance > maxtargetdistance then -- Ditto for this target loss section
--Citizen.Trace("Heli: tracking spotlight target lost")
DecorRemove(Tspotlight_target, "Target")
target_vehicle = nil
tracking_spotlight = false
TriggerServerEvent("heli:tracking.spotlight.toggle")
Tspotlight_target = nil
break
end
end
Tspotlight_toggle = false
Tspotlight_pause = false
Tspotlight_target = nil
tracking_spotlight = false
end)
RegisterNetEvent('heli:Tspotlight.toggle')
AddEventHandler('heli:Tspotlight.toggle', function(serverID)
Tspotlight_toggle = false
tracking_spotlight = false
end)
RegisterNetEvent('heli:pause.Tspotlight')
AddEventHandler('heli:pause.Tspotlight', function(serverID, pause_Tspotlight)
if pause_Tspotlight then
Tspotlight_pause = true
else
Tspotlight_pause = false
end
end)
RegisterNetEvent('heli:Mspotlight')
AddEventHandler('heli:Mspotlight', function(serverID)
if GetPlayerServerId(PlayerId()) ~= serverID then -- Skip event for the source, since heli pilot already sees a more responsive manual spotlight
local heli = GetVehiclePedIsIn(GetPlayerPed(GetPlayerFromServerId(serverID)), false)
local heliPed = GetPlayerPed(GetPlayerFromServerId(serverID))
Mspotlight_toggle = true
while not IsEntityDead(heliPed) and (GetVehiclePedIsIn(heliPed) == heli) and Mspotlight_toggle do
Citizen.Wait(0)
local helicoords = GetEntityCoords(heli)
spotoffset = helicoords + vector3(0.0, 0.0, -1.5)
SpotvectorX = DecorGetInt(heliPed, "SpotvectorX")
SpotvectorY = DecorGetInt(heliPed, "SpotvectorY")
SpotvectorZ = DecorGetInt(heliPed, "SpotvectorZ")
if SpotvectorX then
DrawSpotLight(spotoffset['x'], spotoffset['y'], spotoffset['z'], SpotvectorX, SpotvectorY, SpotvectorZ, 255, 255, 255, 800.0, 10.0, brightness, spotradius, 1.0, 1.0)
end
end
Mspotlight_toggle = false
DecorSetInt(heliPed, "SpotvectorX", nil)
DecorSetInt(heliPed, "SpotvectorY", nil)
DecorSetInt(heliPed, "SpotvectorZ", nil)
end
end)
RegisterNetEvent('heli:Mspotlight.toggle')
AddEventHandler('heli:Mspotlight.toggle', function(serverID)
Mspotlight_toggle = false
end)
RegisterNetEvent('heli:light.up')
AddEventHandler('heli:light.up', function(serverID)
if brightness < 10 then
brightness = brightness + 1.0
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
end
end)
RegisterNetEvent('heli:light.down')
AddEventHandler('heli:light.down', function(serverID)
if brightness > 1.0 then
brightness = brightness - 1.0
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
end
end)
RegisterNetEvent('heli:radius.up')
AddEventHandler('heli:radius.up', function(serverID)
if spotradius < 10.0 then
spotradius = spotradius + 1.0
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
end
end)
RegisterNetEvent('heli:radius.down')
AddEventHandler('heli:radius.down', function(serverID)
if spotradius > 4.0 then
spotradius = spotradius - 1.0
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
end
end)
function IsPlayerInPolmav()
local lPed = GetPlayerPed(-1)
local vehicle = GetVehiclePedIsIn(lPed)
return IsVehicleModel(vehicle, polmav_hash)
end
function IsHeliHighEnough(heli)
return GetEntityHeightAboveGround(heli) > 1.5
end
function ChangeVision()
if vision_state == 0 then
SetNightvision(true)
vision_state = 1
elseif vision_state == 1 then
SetNightvision(false)
SetSeethrough(true)
vision_state = 2
else
SetSeethrough(false)
vision_state = 0
end
end
function ChangeDisplay()
if vehicle_display == 0 then
vehicle_display = 1
elseif vehicle_display == 1 then
vehicle_display = 2
else
vehicle_display = 0
end
end
function HideHUDThisFrame()
HideHelpTextThisFrame()
HideHudAndRadarThisFrame()
HideHudComponentThisFrame(19) -- weapon wheel
HideHudComponentThisFrame(1) -- Wanted Stars
HideHudComponentThisFrame(2) -- Weapon icon
HideHudComponentThisFrame(3) -- Cash
HideHudComponentThisFrame(4) -- MP CASH
HideHudComponentThisFrame(13) -- Cash Change
HideHudComponentThisFrame(11) -- Floating Help Text
HideHudComponentThisFrame(12) -- more floating help text
HideHudComponentThisFrame(15) -- Subtitle Text
HideHudComponentThisFrame(18) -- Game Stream
end
function CheckInputRotation(cam, zoomvalue)
local rightAxisX = GetDisabledControlNormal(0, 220)
local rightAxisY = GetDisabledControlNormal(0, 221)
local rotation = GetCamRot(cam, 2)
if rightAxisX ~= 0.0 or rightAxisY ~= 0.0 then
new_z = rotation.z + rightAxisX*-1.0*(speed_ud)*(zoomvalue+0.1)
new_x = math.max(math.min(20.0, rotation.x + rightAxisY*-1.0*(speed_lr)*(zoomvalue+0.1)), -89.5) -- Clamping at top (cant see top of heli) and at bottom (doesn't glitch out in -90deg)
SetCamRot(cam, new_x, 0.0, new_z, 2)
end
end
function HandleZoom(cam)
if IsControlJustPressed(0,241) then -- Scrollup
fov = math.max(fov - zoomspeed, fov_min)
end
if IsControlJustPressed(0,242) then
fov = math.min(fov + zoomspeed, fov_max) -- ScrollDown
end
local current_fov = GetCamFov(cam)
if math.abs(fov-current_fov) < 0.1 then -- the difference is too small, just set the value directly to avoid unneeded updates to FOV of order 10^-5
fov = current_fov
end
SetCamFov(cam, current_fov + (fov - current_fov)*0.05) -- Smoothing of camera zoom
end
function GetVehicleInView(cam)
local coords = GetCamCoord(cam)
local forward_vector = RotAnglesToVec(GetCamRot(cam, 2))
--DrawLine(coords, coords+(forward_vector*100.0), 255,0,0,255) -- debug line to show LOS of cam
local rayhandle = CastRayPointToPoint(coords, coords+(forward_vector*200.0), 10, GetVehiclePedIsIn(GetPlayerPed(-1)), 0)
local _, _, _, _, entityHit = GetRaycastResult(rayhandle)
if entityHit>0 and IsEntityAVehicle(entityHit) then
return entityHit
else
return nil
end
end
function RenderVehicleInfo(vehicle)
if DoesEntityExist(vehicle) then
local model = GetEntityModel(vehicle)
local vehname = GetLabelText(GetDisplayNameFromVehicleModel(model))
local licenseplate = GetVehicleNumberPlateText(vehicle)
if speed_measure == "MPH" then
vehspeed = GetEntitySpeed(vehicle)*2.236936
else
vehspeed = GetEntitySpeed(vehicle)*3.6
end
SetTextFont(0)
SetTextProportional(1)
if vehicle_display == 0 then
SetTextScale(0.0, 0.49)
elseif vehicle_display == 1 then
SetTextScale(0.0, 0.55)
end
SetTextColour(255, 255, 255, 255)
SetTextDropshadow(0, 0, 0, 0, 255)
SetTextEdge(1, 0, 0, 0, 255)
SetTextDropShadow()
SetTextOutline()
SetTextEntry("STRING")
if vehicle_display == 0 then
AddTextComponentString("Speed: " .. math.ceil(vehspeed) .. " " .. speed_measure .. "\nModel: " .. vehname .. "\nPlate: " .. licenseplate)
elseif vehicle_display == 1 then
AddTextComponentString("Model: " .. vehname .. "\nPlate: " .. licenseplate)
end
DrawText(0.45, 0.9)
end
end
function RotAnglesToVec(rot) -- input vector3
local z = math.rad(rot.z)
local x = math.rad(rot.x)
local num = math.abs(math.cos(x))
return vector3(-math.sin(z)*num, math.cos(z)*num, math.sin(x))
end
-- Following two functions from IllidanS4's entity enuerator script: https://gist.github.com/IllidanS4/9865ed17f60576425369fc1da70259b2
local entityEnumerator = {
__gc = function(enum)
if enum.destructor and enum.handle then
enum.destructor(enum.handle)
end
enum.destructor = nil
enum.handle = nil
end
}
local function EnumerateEntities(initFunc, moveFunc, disposeFunc)
return coroutine.wrap(function()
local iter, id = initFunc()
if not id or id == 0 then
disposeFunc(iter)
return
end
local enum = {handle = iter, destructor = disposeFunc}
setmetatable(enum, entityEnumerator)
local next = true
repeat
coroutine.yield(id)
next, id = moveFunc(iter)
until not next
enum.destructor, enum.handle = nil, nil
disposeFunc(iter)
end)
end
function EnumerateVehicles()
return EnumerateEntities(FindFirstVehicle, FindNextVehicle, EndFindVehicle)
end
function FindVehicleByPlate(plate) -- Search existing vehicles enumerated above for target plate and return the matching vehicle
for vehicle in EnumerateVehicles() do
if GetVehicleNumberPlateText(vehicle) == plate then
return vehicle
end
end
end
-- FiveM Heli Cam by mraes, Version 1.3 (2017-06-12)
-- Modified by rjross2013 (2017-06-23)
-- Further modified by Loque (2017-08-15)
RegisterServerEvent('heli:forward.spotlight')
AddEventHandler('heli:forward.spotlight', function(state)
local serverID = source
TriggerClientEvent('heli:forward.spotlight', -1, serverID, state)
end)
RegisterServerEvent('heli:tracking.spotlight')
AddEventHandler('heli:tracking.spotlight', function(target_netID, target_plate, targetposx, targetposy, targetposz)
local serverID = source
TriggerClientEvent('heli:Tspotlight', -1, serverID, target_netID, target_plate, targetposx, targetposy, targetposz)
end)
RegisterServerEvent('heli:tracking.spotlight.toggle')
AddEventHandler('heli:tracking.spotlight.toggle', function()
local serverID = source
TriggerClientEvent('heli:Tspotlight.toggle', -1, serverID)
end)
RegisterServerEvent('heli:pause.tracking.spotlight')
AddEventHandler('heli:pause.tracking.spotlight', function(pause_Tspotlight)
local serverID = source
TriggerClientEvent('heli:pause.Tspotlight', -1, serverID, pause_Tspotlight)
end)
RegisterServerEvent('heli:manual.spotlight')
AddEventHandler('heli:manual.spotlight', function()
local serverID = source
TriggerClientEvent('heli:Mspotlight', -1, serverID)
end)
RegisterServerEvent('heli:manual.spotlight.toggle')
AddEventHandler('heli:manual.spotlight.toggle', function()
local serverID = source
TriggerClientEvent('heli:Mspotlight.toggle', -1, serverID)
end)
RegisterServerEvent('heli:light.up')
AddEventHandler('heli:light.up', function()
local serverID = source
TriggerClientEvent('heli:light.up', -1, serverID)
end)
RegisterServerEvent('heli:light.down')
AddEventHandler('heli:light.down', function()
local serverID = source
TriggerClientEvent('heli:light.down', -1, serverID)
end)
RegisterServerEvent('heli:radius.up')
AddEventHandler('heli:radius.up', function()
local serverID = source
TriggerClientEvent('heli:radius.up', -1, serverID)
end)
RegisterServerEvent('heli:radius.down')
AddEventHandler('heli:radius.down', function()
local serverID = source
TriggerClientEvent('heli:radius.down', -1, serverID)
end)
Just tested it and it seems that its a little glitchy getting the spotlight to work when you have a pilot and copilot trying to get the spotlight on at the same time, but as soon as one of them exits the camera it works perfect! Thanks!
A struggle for control of the spotlight?! Somehow I thought mraes had restricted the helicam to the pilot seat. Yes, I could see problems with the spotlight functions in a situation of simultaneous helicaming, so that may need to be looked at.
Nope, no restrictions. Adding the restriction that only one person may control it is pretty easy though.
@Loque Thanks for the nice improvements!
Would you have the helicam user trigger client/server events to broadcast to other clients (only other clients) that the helicam is in use, and then update when that user exits helicam?
What makes me nervous/paranoid is some unexpected event that prevents the variable getting reset properly, resulting in everyone getting locked out of the helicam. If say the pilot crashes the heli during helicam use then your helicam cleanup section should still run and a helicam_in_use variable could be reset no problem, I assume. But what if instead a pilot’s FiveM client crashes during helicam use?
Actually I wonder if a ‘HelicamInUse’ decorator might be better. It would avoid some potential issues simply because it would be a property of one particular heli entity. Not to mention ease of use of these decorators makes them so tempting.
Sorry in advance if this is a stupid question. I am not a code guy. I am a streamer that wants to RP a news chopper.
Is it possible that a similar mod will work on the Frogger helicopter?
Yes, you only need to change a few lines of code. I can send you those lines later today if you want.
@Pitchit alright here you go.
heli_client.lua
Directly below this line:
local polmav_hash = GetHashKey("polmav")
Add one of the following lines for each type of helicopter that you want the helicam to work on:
local maverick_hash = GetHashKey("maverick") -- Maverick
local buzzard_hash = GetHashKey("buzzard") -- Buzzard
local buzzard2_hash = GetHashKey("buzzard2") -- Buzzard2 (without guns)
local valkyrie_hash = GetHashKey("valkyrie") -- Valkyrie
local frogger_hash = GetHashKey("frogger") -- Frogger
local frogger2_hash = GetHashKey("frogger2") -- Frogger2 (Trevor's Version)
Next, replace this function:
function IsPlayerInPolmav()
local lPed = GetPlayerPed(-1)
local vehicle = GetVehiclePedIsIn(lPed)
return IsVehicleModel(vehicle, polmav_hash)
end
With this:
function IsPlayerInPolmav()
local lPed = GetPlayerPed(-1)
local vehicle = GetVehiclePedIsIn(lPed)
local IsPolmav = IsVehicleModel(vehicle, polmav_hash)
local IsMaverick = IsVehicleModel(vehicle, maverick_hash)
local IsBuzzard = IsVehicleModel(vehicle, buzzard_hash)
local IsBuzzard2 = IsVehicleModel(vehicle, buzzard2_hash)
local IsValkyrie = IsVehicleModel(vehicle, valkyrie_hash)
local IsFrogger = IsVehicleModel(vehicle, frogger_hash)
local IsFrogger2 = IsVehicleModel(vehicle, frogger2_hash)
local isCorrect = false
if( IsPolmav or IsMaverick or IsBuzzard or IsBuzzard2 or IsValkyrie or IsFrogger or IsFrogger2) then
isCorrect = true
end
return isCorrect
end
Note that you can simply add/remove any of the “or IsXxxxx
” parts in the last “if statement
” to enable/disable that specific helicopter. I’ve not tested this with the latest version of the script but with a previous version this worked fine even on helicopters that don’t have a spotlight. But maybe in the new version this could cause issues if you allow helicopters that don’t have spotlights. (Not sure)…
Hope this helps. If you have any questions just reply or send me a PM.
EDIT: Btw this most likely isn’t the most perfect way to do it but since I’ve already changed my own code for this exact situation I just copied/pasted my code over here. If anyone wants to improve it, be my guest.
Thank you good sir, it’s nice to see this kind of support around here!
This works great, thank you!
Is there a way to get the speed of cars kinda like the DoJ server? If not cool if so great! Just get back to me but it would really be a killer if you could or tell me how to do it i have a little experience in the coding world.
If you are using the modified version a few posts up then you should be getting the speed of cars by default, when you use the helicam and zoom in on an individual car. If you then press SPACE you can lock on the car and continue to get its speed while in helicam and even after you exit helicam.
If you are not seeing the speed, then maybe you hit Q by accident, which toggles the vehicle display mode between speed+model+license / model+license / no display.
I have a problem, how do I change the control id to something and how do I find out what control id is what key