That is just it, they dont sync it because its not needed to sync. The spotlight is just for singleplayer, and it would be easy to sync if it was stationary.
But vehicle position is synced across clients is what I am saying. I think you missed what I meant. My thought is that vehicle position is synced from client to the server and back to other clients. Can we reverse engineer this method and apply it to the spotlight angle?
I have been working on enhancing the spotlight side of things since mraes released this amazing mod, but I’ve had lots of trouble, and rjross2013’s great additions needed to be made compatible too. I will post the script in the next message which you can jump to if you want but please note this is to some extent a work in progress. In single player everything works for me at least but I have tested very little of it for syncing across players. Here are the functions I was going for:
(1) three switchable spotlight modes: (a) the ‘forward spotlight’, the only proper though static cone of light, which mraes originally included with syncing across clients and which I have basically left as is; (b) the ‘manual spotlight’ (with auto-tracking option, all via helicam) introduced by rjross2013. I have tried to also make this sync across clients; © a new ‘tracking spotlight’ (simple circle of light) which potentially follows a target vehicle when the pilot is not using helicam. I have also tried to sync this across clients.
The spotlight modes are all toggled using mraes’ configurable input, by default ‘G’. While using the tracking spotlight this key will turn on and off the tracking spotlight (pause it) until the target is lost or released by the pilot, so to switch from say tracking spotlight to forward spotlight, you first release the target vehicle being tracked and then press G to engage the forward spotlight.
(2) vehicle target lock obviously must persist now after exiting helicam, and is terminated when target distance exceeds a configurable max target distance or when the heli pilot releases the target lock with the configurable key (default space bar). If a new target vehicle is acquired while the tracking spotlight is active, the spotlight should switch targets and track the new one.
(3) vehicle information display also persists now after exiting the helicam view when a target vehicle has been acquired. It now also by default includes vehicle speed. The display modes can be toggled between radar+model+license / model+license / no display using a configurable key (by default ‘Q’).
As a technical aside on syncing, I first tried to do all of this using client/server events but lost most of my hair from ripping it out. In the end, the first version was not reliable for syncing the tracking spotlight but probably could have been fixed. The manual spotlight, as mraes and rjross2013 were discussing, suffered the consequences of inefficient client-server communications. I got it synced but upon testing with another person, the light would flicker significantly.
This new version relies mostly on ped decorators to pass some variables from heli pilot to other players. I’m no programmer but this method was way easier to work with. The manual spotlight seems more efficient (client->client) since the spotlight quality appears to be fairly stable. Once again, all of the syncing needs full testing in a multiplayer environment.
Seems like fine work!
I hadn’t thought about using decorators for this.
client:
-- FiveM Heli Cam by mraes
-- Version 1.3 2017-06-12
-- Modified version incorporating spotlight and other tweaks by Loque and by rjross2013. Credits for tips gleaned from these mods: Guadmaz's Simple Police Searchlight, devilkkw's Speed Camera and nynjardin's Simple Outlaw Alert.
-- config
local brightness = 1.0
local fov_max = 80.0
local fov_min = 10.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 maxtargetdistance = 700 -- max distance at which target lock is maintained
-- Script starts here
local target_vehicle = nil
local new_target = nil
local manual_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("EnableTrackingSpotlight", 3)
DecorRegister("EnableManualSpotlight", 3)
DecorRegister("DeleteTarget", 3)
DecorRegister("PauseTrackingSpotlight", 3)
DecorRegister("xSpotvector", 3)
DecorRegister("ySpotvector", 3)
DecorRegister("zSpotvector", 3)
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 DecorGetInt(lPed, "EnableTrackingSpotlight") == 2 then
if DecorGetInt(lPed, "PauseTrackingSpotlight") == 1 then
DecorSetInt(lPed, "PauseTrackingSpotlight", 2)
else
DecorSetInt(lPed, "PauseTrackingSpotlight", 1)
end
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
else
spotlight_state = false
TriggerServerEvent("heli:forward.spotlight", spotlight_state)
DecorSetInt(lPed, "EnableTrackingSpotlight", 2)
DecorSetInt(lPed, "PauseTrackingSpotlight", 1)
TriggerServerEvent("heli:tracking.spotlight", target_vehicle)
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
end
else
DecorSetInt(lPed, "EnableTrackingSpotlight", 1)
DecorSetInt(lPed, "PauseTrackingSpotlight", 1)
spotlight_state = not spotlight_state
TriggerServerEvent("heli:forward.spotlight", spotlight_state)
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
end
end
if IsControlJustPressed(0, toggle_lock_on) and GetPedInVehicleSeat(heli, -1) == lPed then -- Delete target
DecorSetInt(lPed, "DeleteTarget", 2)
DecorSetInt(lPed, "EnableTrackingSpotlight", 1)
DecorSetInt(lPed, "PauseTrackingSpotlight", 1)
target_vehicle = nil
new_target = nil
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
end
if DecorGetInt(lPed, "DeleteTarget") == 2 then
target_vehicle = nil
new_target = nil
Citizen.Wait(5)
DecorSetInt(lPed, "DeleteTarget", 1)
end
if IsControlJustPressed(0, toggle_display) and GetPedInVehicleSeat(heli, -1) == lPed then
ChangeDisplay()
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(1) -- 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
DecorSetInt(lPed, "EnableTrackingSpotlight", 2)
DecorSetInt(lPed, "PauseTrackingSpotlight", 1)
TriggerServerEvent("heli:tracking.spotlight", target_vehicle)
end
manual_spotlight = false
DecorSetInt(lPed, "EnableManualSpotlight", 1)
helicam = false
--brightness = 2
end
if IsControlJustPressed(0, toggle_vision) then
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
ChangeVision()
end
if IsControlJustPressed(0, toggle_spotlight) then
if DecorGetInt(lPed, "EnableTrackingSpotlight") == 2 then -- Pause tracking spotlight
DecorSetInt(lPed, "PauseTrackingSpotlight", 2)
manual_spotlight = not manual_spotlight
if manual_spotlight then
DecorSetInt(lPed, "EnableManualSpotlight", 2)
DecorSetInt(lPed, "xSpotvector", xSpotvector)
DecorSetInt(lPed, "ySpotvector", ySpotvector)
DecorSetInt(lPed, "zSpotvector", zSpotvector)
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
TriggerServerEvent("heli:manual.spotlight")
else
DecorSetInt(lPed, "EnableManualSpotlight", 1)
end
elseif spotlight_state then -- Turn off forward spotlight
spotlight_state = false
TriggerServerEvent("heli:forward.spotlight", spotlight_state)
manual_spotlight = not manual_spotlight
if manual_spotlight then
DecorSetInt(lPed, "EnableManualSpotlight", 2)
DecorSetInt(lPed, "xSpotvector", xSpotvector)
DecorSetInt(lPed, "ySpotvector", ySpotvector)
DecorSetInt(lPed, "zSpotvector", zSpotvector)
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
TriggerServerEvent("heli:manual.spotlight")
else
DecorSetInt(lPed, "EnableManualSpotlight", 1)
end
else
manual_spotlight = not manual_spotlight
if manual_spotlight then
DecorSetInt(lPed, "EnableManualSpotlight", 2)
DecorSetInt(lPed, "xSpotvector", xSpotvector)
DecorSetInt(lPed, "ySpotvector", ySpotvector)
DecorSetInt(lPed, "zSpotvector", zSpotvector)
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
TriggerServerEvent("heli:manual.spotlight")
else
DecorSetInt(lPed, "EnableManualSpotlight", 1)
end
end
end
if IsControlJustPressed(0, 246) then
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
if manual_spotlight then
lightUp()
end
end
if IsControlJustPressed(0, 173) then
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
if manual_spotlight then
lightDown()
end
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
PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
locked_on_vehicle = nil
target_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)
DecorSetInt(lPed, "DeleteTarget", 1)
locked_on_vehicle = vehicle_detected
if target_vehicle then
new_target = vehicle_detected
target_vehicle = nil
toggle = false
TriggerServerEvent("heli:target.change", new_target)
else
target_vehicle = vehicle_detected
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
local helicoords = GetEntityCoords(heli)
local rotation = GetCamRot(cam, 2)
local forward_vector = RotAnglesToVec(rotation)
local xSpotvector, ySpotvector, zSpotvector = table.unpack(forward_vector)
local camcoords = GetCamCoord(cam)
DecorSetInt(lPed, "EnableManualSpotlight", 2)
DecorSetInt(lPed, "xSpotvector", xSpotvector)
DecorSetInt(lPed, "ySpotvector", ySpotvector)
DecorSetInt(lPed, "zSpotvector", zSpotvector)
else
DecorSetInt(lPed, "EnableManualSpotlight", 1)
end
end
DecorSetInt(lPed, "EnableManualSpotlight", 1)
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 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:tracking.spotlight')
AddEventHandler('heli:tracking.spotlight', function(serverID, target_vehicle)
local heli = GetVehiclePedIsIn(GetPlayerPed(GetPlayerFromServerId(serverID)), false)
heliPed = GetPlayerPed(GetPlayerFromServerId(serverID))
while not IsEntityDead(heliPed) and (GetVehiclePedIsIn(heliPed) == heli) and target_vehicle and (DecorGetInt(heliPed, "EnableTrackingSpotlight") == 2) do
Citizen.Wait(1)
if new_target then -- Target change
target_vehicle = new_target
new_target = nil
end
local helicoords = GetEntityCoords(heli)
local targetcoords = GetEntityCoords(target_vehicle)
local spotVector = targetcoords - helicoords
local target_distance = (Vdist(targetcoords, helicoords) + 20)
if DecorGetInt(heliPed, "DeleteTarget") == 2 or target_distance > maxtargetdistance then -- Target loss or deletion
DecorSetInt(heliPed, "DeleteTarget", 2)
new_target = nil
target_vehicle = nil
Citizen.Wait(10)
DecorSetInt(heliPed, "DeleteTarget", 1)
DecorSetInt(heliPed, "PauseTrackingSpotlight", 1)
DecorSetInt(heliPed, "EnableTrackingSpotlight", 1)
break
end
if DecorGetInt(heliPed, "PauseTrackingSpotlight") ~= 2 then
DrawSpotLight(helicoords['x'], helicoords['y'], helicoords['z'], spotVector['x'], spotVector['y'], spotVector['z'], 255, 255, 255, target_distance, 10.0, brightness, 4.0, 1.0, 0.0)
end
end
Citizen.Wait(5)
new_target = nil
target_vehicle = nil
DecorSetInt(heliPed, "EnableTrackingSpotlight", 1)
DecorSetInt(heliPed, "DeleteTarget", 1)
DecorSetInt(heliPed, "PauseTrackingSpotlight", 1)
end)
RegisterNetEvent('heli:manual.spotlight')
AddEventHandler('heli:manual.spotlight', function(serverID)
--if GetPlayerServerId(PlayerId()) ~= serverID then -- If working, this could skip event for the source, allowing an improved client-side spotlight for heli pilot
local heli = GetVehiclePedIsIn(GetPlayerPed(GetPlayerFromServerId(serverID)), false)
heliPed = GetPlayerPed(GetPlayerFromServerId(serverID))
while not IsEntityDead(heliPed) and (GetVehiclePedIsIn(heliPed) == heli) and (DecorGetInt(heliPed, "EnableManualSpotlight") == 2) do
Citizen.Wait(0)
local helicoords = GetEntityCoords(heli)
spotoffset = helicoords + vector3(0.0, 0.0, -1.5)
xSpotvector = DecorGetInt(heliPed, "xSpotvector")
ySpotvector = DecorGetInt(heliPed, "ySpotvector")
zSpotvector = DecorGetInt(heliPed, "zSpotvector")
DrawSpotLight(spotoffset['x'], spotoffset['y'], spotoffset['z'], xSpotvector, ySpotvector, zSpotvector, 255, 255, 255, 800.0, 4.0, brightness, 6.0, 1.0, 1.0)
end
DecorSetInt(heliPed, "EnableManualSpotlight", 1)
--end
end)
RegisterNetEvent('heli:target.change')
AddEventHandler('heli:target.change', function(serverID, new_target)
if new_target then
target_vehicle = new_target
new_target = nil
end
end)
function lightUp()
brightness = brightness + 1.0
end
function lightDown()
brightness = brightness - 1.0
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)
local vehspeed = GetEntitySpeed(vehicle)*3.6
--local vehspeed = GetEntitySpeed(vehicle)*2.236936 -- to change to Mph, use this line instead of previous, and update relevant text in AddTextComponentString line below
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).." Km/h\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
server:
-- FiveM Heli Cam by mraes
-- Version 1.3 2017-06-12
-- Modified version incorporating spotlight and other tweaks by Loque and by rjross2013.
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_vehicle)
local serverID = source
TriggerClientEvent('heli:tracking.spotlight', -1, serverID, target_vehicle)
end)
RegisterServerEvent('heli:target.change')
AddEventHandler('heli:target.change', function(new_target)
local serverID = source
TriggerClientEvent('heli:target.change', -1, serverID, new_target)
end)
RegisterServerEvent('heli:manual.spotlight')
AddEventHandler('heli:manual.spotlight', function()
local serverID = source
TriggerClientEvent('heli:manual.spotlight', -1, serverID)
end)
Sounds awesome. Excited to test this tomorrow with multiple players
FYI I edited the post above with new client code, so if you grabbed it before this message is posted, grab it again.
I fixed a minor glitch and also realized that manual spotlight was not transitioning to tracking spotlight. Now it should. So when using the manual spotlight within helicam to auto-track a vehicle, you can exit helicam and the tracking spotlight will take over and continue to track the vehicle.
Just tried your script with a few people and it’s a definite improvement. We found though that when we turned the spotlight on, it wouldn’t come on for others unless the camera operator kept turning the light off and on.
did you wait for it to sync sometimes takes time to sync between everyone present …sometimes
WolfKnight, which spotlight modes did or didn’t do this? The tracking spotlight outside of helicam view or the manual spotlight via helicam view?
Also, it would help if you describe in more detail what did happen: e.g. pilot turned on manual spotlight (pressed G in helicam view), spotlight came on for him properly but did not for others (at all or just a flicker?), unless pilot kept flicking on and off continuously? Weird.
That’s disappointing if confirmed by others.
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?