Last active
August 21, 2020 13:40
-
-
Save itsJarrett/3fd68a9db8ed67e78033462cc051d72f to your computer and use it in GitHub Desktop.
Title says all. Here is a screenshot >> https://i.imgur.com/H1zqgGf.jpg
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- 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-- FiveM Heli Cam by mraes, version 1.3 (2017-06-12)-- 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 veh_hash = {"polmav", "mammatus"} | |
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) | |
for _, v in pairs(veh_hash) do | |
if IsVehicleModel(vehicle, GetHashKey(v)) then | |
return true | |
end | |
end | |
return false | |
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 directions = { [0] = 'N', [45] = 'NW', [90] = 'W', [135] = 'SW', [180] = 'S', [225] = 'SE', [270] = 'E', [315] = 'NE', [360] = 'N', } | |
for k,v in pairs(directions)do | |
direction = GetEntityHeading(vehicle) | |
if (math.abs(direction - k) < 22.5) then | |
direction = v | |
break; | |
end | |
end | |
local x,y,z = table.unpack(GetEntityCoords(vehicle, false)) | |
local streetName, crossing = GetStreetNameAtCoord(x, y, z) | |
streetName, crossing = GetStreetNameFromHashKey(streetName), GetStreetNameFromHashKey(crossing) | |
location = direction .. " " .. streetName .. " / " .. crossing | |
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.35) | |
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 .. "\nLocation: " .. location) | |
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 | |
-- 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) | |
local directions = { [0] = 'N', [45] = 'NW', [90] = 'W', [135] = 'SW', [180] = 'S', [225] = 'SE', [270] = 'E', [315] = 'NE', [360] = 'N', } | |
for k,v in pairs(directions)do | |
direction = GetEntityHeading(vehicle) | |
if (math.abs(direction - k) < 22.5) then | |
direction = v | |
break; | |
end | |
end | |
local x,y,z = table.unpack(GetEntityCoords(vehicle, false)) | |
local streetName, crossing = GetStreetNameAtCoord(x, y, z) | |
streetName, crossing = GetStreetNameFromHashKey(streetName), GetStreetNameFromHashKey(crossing) | |
location = direction .. " " .. streetName .. " / " .. crossing | |
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.35) | |
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 .. "\nLocation: " .. location) | |
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 | |
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-- 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) | |
local directions = { [0] = 'N', [45] = 'NW', [90] = 'W', [135] = 'SW', [180] = 'S', [225] = 'SE', [270] = 'E', [315] = 'NE', [360] = 'N', } | |
for k,v in pairs(directions)do | |
direction = GetEntityHeading(vehicle) | |
if (math.abs(direction - k) < 22.5) then | |
direction = v | |
break; | |
end | |
end | |
local x,y,z = table.unpack(GetEntityCoords(vehicle, false)) | |
local streetName, crossing = GetStreetNameAtCoord(x, y, z) | |
streetName, crossing = GetStreetNameFromHashKey(streetName), GetStreetNameFromHashKey(crossing) | |
location = direction .. " " .. streetName .. " / " .. crossing | |
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.35) | |
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 .. "\nLocation: " .. location) | |
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 | |
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) | |
local x,y,z = table.unpack(GetEntityCoords(ped, false)) | |
local streetName, crossing = GetStreetNameAtCoord(x, y, z) | |
streetName = GetStreetNameFromHashKey(streetName) | |
local location = streetname | |
if crossing ~= nil then location = streetname .. " / " .. crossing end | |
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 .. "\nLocation: " .. location) | |
elseif vehicle_display == 1 then | |
AddTextComponentString("Model: " .. vehname .. "\nPlate: " .. licenseplate .. "\nLocation: " .. location) | |
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment