Files
wk_wars2x/cl_radar.lua

935 lines
28 KiB
Lua

--[[------------------------------------------------------------------------
Wraith ARS 2X - v1.0.0
Created by WolfKnight
------------------------------------------------------------------------]]--
local next = next
local dot = dot
local table = table
local type = type
local tostring = tostring
local math = math
local pairs = pairs
--[[------------------------------------------------------------------------
Resource Rename Fix - for those muppets who rename the resource and
complain that the NUI aspect doesn't work!
------------------------------------------------------------------------]]--
Citizen.SetTimeout( 1000, function()
-- Get the name of the resource, for example the default name is 'wk_wrs2'
local name = GetCurrentResourceName()
print( "WK_WARS2X: Sending resource name (" .. name .. ") to JavaScript side." )
-- Send a message through the NUI system to the JavaScript file to give the name of the resource
SendNUIMessage( { _type = "updatePathName", pathName = name } )
end )
--[[------------------------------------------------------------------------
Player info variables
------------------------------------------------------------------------]]--
PLY = {}
PLY.ped = PlayerPedId()
PLY.veh = nil
PLY.inDriverSeat = false
--[[------------------------------------------------------------------------
Radar variables
------------------------------------------------------------------------]]--
RADAR.vars =
{
-- The radar's power
power = false,
poweringUp = false,
-- These are the settings that are used in the operator menu
settings = {
menuActive = false,
fastDisplay = true,
oppSensitivity = 5,
sameSensitivity = 5,
alert = true
},
-- Player's vehicle speed, this is used to update the patrol vehicle speed on the radar
patrolSpeed = 0,
patrolLocked = false,
psBlank = false,
-- The speed type, this is used when converting speeds to a readable format
-- Either "mph" or "kmh", can be toggle in-game
speedType = "mph",
-- Antennas, this table contains all of the data needed for operation of the front and rear antennas
antennas = {
-- Variables for the front antenna
[ "front" ] = {
xmit = false, -- Whether the antenna is on or off
mode = 0, -- Current antenna mode, 0 = none, 1 = same, 2 = opp, 3 = same and opp
speed = 0, -- Speed of the vehicle caught by the front antenna
dir = nil, -- Direction the caught vehicle is going, 0 = towards, 1 = away
fastMode = 1, -- Current fast mode, 1 = polling, 2 = lock on at first fast vehicle
fastSpeed = 0, -- Speed of the fastest vehicle caught by the front antenna
fastDir = nil, -- Direction the fastest vehicle is going, 0 = towards, 1 = away
fastLocked = false -- Whether the fast speed is locked or not
},
[ "rear" ] = {
xmit = false, -- Whether the antenna is on or off
mode = 0, -- Current antenna mode, 0 = none, 1 = same, 2 = opp, 3 = same and opp
speed = 0, -- Speed of the vehicle caught by the front antenna
dir = nil, -- Direction the caught vehicle is going, 0 = towards, 1 = away
fastMode = 1, -- Current fast mode, 1 = polling, 2 = lock on at first fast vehicle
fastSpeed = 0, -- Speed of the fastest vehicle caught by the front antenna
fastDir = nil, -- Direction the fastest vehicle is going, 0 = towards, 1 = away
fastLocked = false -- Whether the fast speed is locked or not
}
},
-- The maximum distance that the radar system's ray traces can go
maxCheckDist = 300.0,
-- Cached dynamic vehicle sphere sizes, automatically populated when the system is running
sphereSizes = {},
-- Vehicle pool, automatically populated when the system is running, holds all of the current
-- vehicle IDs for the player using entity enumeration (see cl_utils.lua)
vehiclePool = {},
-- Radar stage, this is used to tell the system what it should currently be doing, the stages are:
-- - 0 = Gathering vehicles hit by the rays
-- - 1 = Filtering the vehicles caught (removing duplicates, etc) and calculating what needs to be shown
-- to the user based on modes and settings
-- - 2 = Sending all required data across to the NUI system for display
radarStage = 0,
-- Ray trace state, this is used so the radar stage doesn't progress to the next stage unless
-- all of the ray trace threads have completed
rayTraceState = 0,
-- Number of ray traces, automatically cached when the system first runs
numberOfRays = 0
}
-- Table to store tables for hit entities of captured vehicles
RADAR.capturedVehicles = {}
-- Table for temp id storage to stop unnecessary trace checks
RADAR.tempVehicleIDs = {}
-- The current vehicle data for display
RADAR.activeVehicles = {}
-- These vectors are used in the custom ray tracing system
RADAR.rayTraces = {
{ startVec = { x = 0.0 }, endVec = { x = 0.0, y = 200.0 }, rayType = "same" },
{ startVec = { x = -5.0 }, endVec = { x = -5.0, y = 200.0 }, rayType = "same" },
{ startVec = { x = 5.0 }, endVec = { x = 5.0, y = 200.0 }, rayType = "same" },
-- { startVec = { x = 3.0 }, endVec = { x = 3.0, y = 200.0 }, rayType = "same" },
-- { startVec = { x = -3.0 }, endVec = { x = -3.0, y = 200.0 }, rayType = "same" },
{ startVec = { x = -10.0 }, endVec = { x = -10.0, y = 200.0 }, rayType = "opp" },
{ startVec = { x = -16.0 }, endVec = { x = -16.0, y = 200.0 }, rayType = "opp" },
-- ultimate lag test
--[[{ startVec = { x = -6.0 }, endVec = { x = -6.0, y = 200.0 }, rayType = "same" },
{ startVec = { x = 6.0 }, endVec = { x = 6.0, y = 200.0 }, rayType = "same" },
{ startVec = { x = -7.0 }, endVec = { x = -7.0, y = 200.0 }, rayType = "same" },
{ startVec = { x = 7.0 }, endVec = { x = 7.0, y = 200.0 }, rayType = "same" },
{ startVec = { x = -8.0 }, endVec = { x = -8.0, y = 200.0 }, rayType = "same" },
{ startVec = { x = 8.0 }, endVec = { x = 8.0, y = 200.0 }, rayType = "same" }]]
}
-- Each of these are used for sorting the captured vehicle data, the 'strongest' filter is used for the main
-- target window of each antenna, whereas the 'fastest' filter is used for the fast target window of each antenna
RADAR.sorting = {
strongest = function( a, b ) return a.size > b.size end,
fastest = function( a, b ) return a.speed > b.speed end
}
--[[------------------------------------------------------------------------
Radar essentials functions
------------------------------------------------------------------------]]--
function RADAR:IsPowerOn()
return self.vars.power
end
function RADAR:IsPoweringUp()
return self.vars.poweringUp
end
function RADAR:SetPoweringUpState( state )
self.vars.poweringUp = state
end
function RADAR:TogglePower()
self.vars.power = not self.vars.power
SendNUIMessage( { _type = "radarPower", state = self:IsPowerOn() } )
-- Power is now turned on
if ( self:IsPowerOn() ) then
self:SetPoweringUpState( true )
Citizen.SetTimeout( 2000, function()
self:SetPoweringUpState( false )
SendNUIMessage( { _type = "poweredUp" } )
end )
else
self:ResetAntenna( "front" )
self:ResetAntenna( "rear" )
end
end
function RADAR:IsFastDisplayEnabled()
return self.vars.settings.fastDisplay
end
function RADAR:ToggleFastDisplay()
self.vars.settings.fastDisplay = not self.vars.settings.fastDisplay
end
function RADAR:IsEitherAntennaOn()
return self:IsAntennaTransmitting( "front" ) or self:IsAntennaTransmitting( "rear" )
end
--[[------------------------------------------------------------------------
Radar basics functions
------------------------------------------------------------------------]]--
function RADAR:GetPatrolSpeed()
return self.vars.patrolSpeed
end
function RADAR:GetVehiclePool()
return self.vars.vehiclePool
end
function RADAR:GetMaxCheckDist()
return self.vars.maxCheckDist
end
function RADAR:GetActiveVehicles()
return self.activeVehicles
end
function RADAR:GetStrongestSortFunc()
return self.sorting.strongest
end
function RADAR:GetFastestSortFunc()
return self.sorting.fastest
end
function RADAR:SetPatrolSpeed( speed )
if ( type( speed ) == "number" ) then
self.vars.patrolSpeed = self:GetVehSpeedFormatted( speed )
end
end
function RADAR:SetVehiclePool( pool )
if ( type( pool ) == "table" ) then
self.vars.vehiclePool = pool
end
end
function RADAR:SetActiveVehicles( vehs )
if ( type( vehs ) == "table" ) then
self.activeVehicles = vehs
end
end
--[[------------------------------------------------------------------------
Radar ray trace functions
------------------------------------------------------------------------]]--
function RADAR:GetRayTraceState()
return self.vars.rayTraceState
end
function RADAR:CacheNumRays()
self.vars.numberOfRays = #self.rayTraces
end
function RADAR:GetNumOfRays()
return self.vars.numberOfRays
end
function RADAR:IncreaseRayTraceState()
self.vars.rayTraceState = self.vars.rayTraceState + 1
end
function RADAR:ResetRayTraceState()
self.vars.rayTraceState = 0
end
function RADAR:GetIntersectedVehIsFrontOrRear( t )
if ( t > 10.0 ) then
return 1 -- vehicle is in front
elseif ( t < -10.0 ) then
return -1 -- vehicle is behind
end
return 0 -- vehicle is next to self
end
function RADAR:GetLineHitsSphereAndDir( centre, radius, rayStart, rayEnd )
-- First we get the normalised ray, this way we then know the direction the ray is going
local rayNorm = norm( rayEnd - rayStart )
-- Then we calculate the ray from the start point to the centre position of the sphere
local rayToCentre = centre - rayStart
-- Now that we have the ray to the centre of the sphere, and the normalised ray direction, we
-- can calculate the shortest point from the centre of the sphere onto the ray itself. This
-- would then give us the opposite side of the right angled triangle. All of the resulting
-- values are also in squared form, as performing square root functions is slower.
local tProj = dot( rayToCentre, rayNorm )
local oppLenSqr = dot( rayToCentre, rayToCentre ) - ( tProj * tProj )
-- Square the radius
local radiusSqr = radius * radius
local rayDist = #( rayEnd - rayStart )
local distToCentre = #( rayStart - centre ) - ( radius * 2 )
-- Now all we have to do is compare the squared opposite length and the radius squared, this
-- will then tell us if the ray intersects with the sphere.
-- if ( oppLenSqr < radiusSqr and ( t0 < ( dist + radius ) ) ) then
if ( oppLenSqr < radiusSqr and not ( distToCentre > rayDist ) ) then
return true, self:GetIntersectedVehIsFrontOrRear( tProj )
end
return false, nil
end
function RADAR:ShootCustomRay( plyVeh, veh, s, e )
local pos = GetEntityCoords( veh )
local dist = #( pos - s )
local key = tostring( veh )
if ( DoesEntityExist( veh ) and veh ~= plyVeh and dist < self:GetMaxCheckDist() and not self:HasVehicleAlreadyBeenHit( key ) ) then
local entSpeed = GetEntitySpeed( veh )
local visible = HasEntityClearLosToEntity( plyVeh, veh, 15 ) -- 13 seems okay, 15 too (doesn't grab ents through ents)
if ( entSpeed > 0.1 and visible ) then
local radius, size = self:GetDynamicRadius( veh )
local hit, relPos = self:GetLineHitsSphereAndDir( pos, radius, s, e )
if ( hit ) then
-- UTIL:DrawDebugSphere( pos.x, pos.y, pos.z, radius, { 255, 0, 0, 40 } )
self:SetVehicleHasBeenHit( key )
return true, relPos, dist, entSpeed, size
end
end
end
return false, nil, nil, nil, nil
end
function RADAR:GetVehsHitByRay( ownVeh, vehs, s, e )
local t = {}
local hasData = false
for _, veh in pairs( vehs ) do
local hit, relativePos, distance, speed, size = self:ShootCustomRay( ownVeh, veh, s, e )
if ( hit ) then
local d = {}
d.veh = veh
d.relPos = relativePos
-- d.dist = UTIL:Round( distance, 2 ) -- Possibly remove
-- d.speed = UTIL:Round( speed, 3 )
d.dist = distance
d.speed = speed
d.size = size
table.insert( t, d )
hasData = true
end
end
if ( hasData ) then return t end
end
function RADAR:CreateRayThread( vehs, from, startX, endX, endY, rayType )
local startPoint = GetOffsetFromEntityInWorldCoords( from, startX, 0.0, 0.0 )
local endPoint = GetOffsetFromEntityInWorldCoords( from, endX, endY, 0.0 )
local hitVehs = self:GetVehsHitByRay( from, vehs, startPoint, endPoint )
self:InsertCapturedVehicleData( hitVehs, rayType )
self:IncreaseRayTraceState()
end
function RADAR:CreateRayThreads( ownVeh, vehicles )
for _, v in pairs( self.rayTraces ) do
self:CreateRayThread( vehicles, ownVeh, v.startVec.x, v.endVec.x, v.endVec.y, v.rayType )
end
end
--[[------------------------------------------------------------------------
Radar stage functions
------------------------------------------------------------------------]]--
function RADAR:GetRadarStage()
return self.vars.radarStage
end
function RADAR:IncreaseRadarStage()
self.vars.radarStage = self.vars.radarStage + 1
end
function RADAR:ResetRadarStage()
self.vars.radarStage = 0
end
--[[------------------------------------------------------------------------
Radar antenna functions
------------------------------------------------------------------------]]--
function RADAR:ToggleAntenna( ant, cb )
if ( self:IsPowerOn() ) then
self.vars.antennas[ant].xmit = not self.vars.antennas[ant].xmit
if ( cb ) then cb() end
end
end
function RADAR:IsAntennaTransmitting( ant )
return self.vars.antennas[ant].xmit
end
function RADAR:GetAntennaTextFromNum( relPos )
if ( relPos == 1 ) then
return "front"
elseif ( relPos == -1 ) then
return "rear"
end
end
function RADAR:GetAntennaMode( ant )
return self.vars.antennas[ant].mode
end
function RADAR:SetAntennaMode( ant, mode, cb )
if ( type( mode ) == "number" ) then
if ( mode >= 0 and mode <= 3 and self:IsPowerOn() ) then
self.vars.antennas[ant].mode = mode
if ( cb ) then cb() end
end
end
end
function RADAR:SetAntennaSpeed( ant, speed )
if ( type( speed ) == "number" ) then
if ( speed >= 0 and speed <= 999 ) then
self.vars.antennas[ant].speed = speed
end
end
end
function RADAR:SetAntennaDir( ant, dir )
if ( type( dir ) == "number" ) then
if ( dir == 0 or dir == 1 ) then
self.vars.antennas[ant].dir = dir
end
end
end
function RADAR:SetAntennaFastMode( ant, mode )
if ( type( mode ) == "number" ) then
if ( mode == 1 or mode == 2 ) then
self.vars.antennas[ant].fastMode = mode
end
end
end
function RADAR:SetAntennaFastSpeed( ant, speed )
if ( type( speed ) == "number" ) then
if ( speed >= 0 and speed <= 999 ) then
self.vars.antennas[ant].fastSpeed = speed
end
end
end
function RADAR:SetAntennaFastDir( ant, dir )
if ( type( dir ) == "number" ) then
if ( dir == 0 or dir == 1 ) then
self.vars.antennas[ant].fastDir = dir
end
end
end
function RADAR:SetAntennaFastLock( ant, state )
if ( type( state ) == "boolean" ) then
self.vars.antennas[ant].fastLocked = state
end
end
function RADAR:ResetAntenna( ant )
-- Overwrite default behaviour, this is because when the system is turned off, the temporary memory is
-- technically reset, as the setter functions require either the radar power to be on or the antenna to
-- be transmitting, this is the only way to reset the values
self.vars.antennas[ant].xmit = false
self.vars.antennas[ant].mode = 0
end
--[[------------------------------------------------------------------------
Radar captured vehicle functions
------------------------------------------------------------------------]]--
function RADAR:GetCapturedVehicles()
return self.capturedVehicles
end
function RADAR:ResetCapturedVehicles()
self.capturedVehicles = {}
end
function RADAR:InsertCapturedVehicleData( t, rt )
if ( type( t ) == "table" and not UTIL:IsTableEmpty( t ) ) then
for _, v in pairs( t ) do
v.rayType = rt
table.insert( self.capturedVehicles, v )
end
end
end
function RADAR:HasVehicleAlreadyBeenHit( key )
return self.tempVehicleIDs[key] == true
end
function RADAR:SetVehicleHasBeenHit( key )
self.tempVehicleIDs[key] = true
end
function RADAR:ResetTempVehicleIDs()
self.tempVehicleIDs = {}
end
--[[------------------------------------------------------------------------
Radar dynamic sphere radius functions
------------------------------------------------------------------------]]--
function RADAR:GetDynamicDataValue( key )
return self.vars.sphereSizes[key]
end
function RADAR:DoesDynamicRadiusDataExist( key )
return self:GetDynamicDataValue( key ) ~= nil
end
function RADAR:SetDynamicRadiusKey( key, t )
self.vars.sphereSizes[key] = t
end
function RADAR:InsertDynamicRadiusData( key, radius, actualSize )
if ( self:GetDynamicDataValue( key ) == nil ) then
local t = {}
t.radius = radius
t.actualSize = actualSize
self:SetDynamicRadiusKey( key, t )
end
end
function RADAR:GetRadiusData( key )
return self.vars.sphereSizes[key].radius or 5.0, self.vars.sphereSizes[key].actualSize
end
function RADAR:GetDynamicRadius( veh )
local mdl = GetEntityModel( veh )
local key = tostring( mdl )
local dataExists = self:DoesDynamicRadiusDataExist( key )
if ( not dataExists ) then
local min, max = GetModelDimensions( mdl )
local size = max - min
local numericSize = size.x + size.y + size.z
local dynamicRadius = UTIL:Clamp( ( numericSize * numericSize ) / 12, 4.0, 10.0 )
self:InsertDynamicRadiusData( key, dynamicRadius, numericSize )
return dynamicRadius, numericSize
end
return self:GetRadiusData( key )
end
--[[------------------------------------------------------------------------
Radar functions
------------------------------------------------------------------------]]--
function RADAR:GetVehSpeedFormatted( speed )
if ( self.vars.speedType == "mph" ) then
return UTIL:Round( math.ceil( speed * 2.236936 ), 0 )
else
return UTIL:Round( math.ceil( speed * 3.6 ), 0 )
end
end
function RADAR:GetAllVehicles()
local t = {}
for v in UTIL:EnumerateVehicles() do
table.insert( t, v )
end
return t
end
function RADAR:CheckVehicleDataFitsMode( ant, rt )
local mode = self:GetAntennaMode( ant )
if ( ( mode == 3 ) or ( mode == 1 and rt == "same" ) or ( mode == 2 and rt == "opp" ) ) then return true end
return false
end
function RADAR:GetVehiclesForAntenna()
local vehs = { ["front"] = {}, ["rear"] = {} }
local results = { ["front"] = { nil, nil }, ["rear"] = { nil, nil } }
-- Loop through and split up the vehicles based on front and rear, this is simply because the actual system
-- that gets all of the vehicles hit by the radar only has a relative position of either 1 or -1, which we
-- then convert below into an antenna string!
for ant in UTIL:Values( { "front", "rear" } ) do
if ( self:IsAntennaTransmitting( ant ) ) then
for k, v in pairs( self:GetCapturedVehicles() ) do
local antText = self:GetAntennaTextFromNum( v.relPos )
if ( ant == antText ) then
table.insert( vehs[ant], v )
end
end
-- As the radar is based on how the real Stalker DSR 2X works, we now sort the dataset by
-- the 'strongest' (largest) target, this way the first result for the front and rear data
-- will be the one that gets displayed in the target boxes.
table.sort( vehs[ant], self:GetStrongestSortFunc() )
end
end
for ant in UTIL:Values( { "front", "rear" } ) do
if ( not UTIL:IsTableEmpty( vehs[ant] ) ) then
-- Get the 'strongest' vehicle for the antenna
for k, v in pairs( vehs[ant] ) do
if ( self:CheckVehicleDataFitsMode( ant, v.rayType ) ) then
results[ant][1] = v
break
end
end
if ( self:IsFastDisplayEnabled() ) then
-- Get the 'fastest' vehicle for the antenna
table.sort( vehs[ant], self:GetFastestSortFunc() )
local temp = results[ant][1]
for k, v in pairs( vehs[ant] ) do
if ( self:CheckVehicleDataFitsMode( ant, v.rayType ) and v.veh ~= temp.veh and v.size < temp.size and v.speed > temp.speed ) then
results[ant][2] = v
break
end
end
end
end
end
return { ["front"] = { results["front"][1], results["front"][2] }, ["rear"] = { results["rear"][1], results["rear"][2] } }
end
-- Num4 = 108 - INPUT_VEH_FLY_ROLL_LEFT_ONLY
-- Num5 = 112 - INPUT_VEH_FLY_PITCH_DOWN_ONLY
-- Num6 = 109 - INPUT_VEH_FLY_ROLL_RIGHT_ONLY
-- Num7 = 117 - INPUT_VEH_FLY_SELECT_TARGET_LEFT
-- Num8 = 111 - INPUT_VEH_FLY_PITCH_UP_ONLY
-- Num9 = 118 - INPUT_VEH_FLY_SELECT_TARGET_RIGHT
-- F5 = 166 - INPUT_SELECT_CHARACTER_MICHAEL
function RADAR:RunControlManager()
-- 'Z' key, toggles debug mode
if ( IsDisabledControlJustPressed( 1, 20 ) ) then
self.config.debug_mode = not self.config.debug_mode
end
if ( IsDisabledControlJustPressed( 1, 166 ) ) then
SendNUIMessage( { _type = "openRemote" } )
SetNuiFocus( true, true )
end
if ( IsDisabledControlJustPressed( 1, 117 ) ) then
self:TogglePower()
UTIL:Notify( "Radar power toggled." )
end
if ( IsDisabledControlJustPressed( 1, 118 ) ) then
self:ToggleFastDisplay()
UTIL:Notify( "Fast display toggled." )
end
-- 'Num8' key, toggles front antenna
--[[ if ( IsDisabledControlJustPressed( 1, 111 ) ) then
self:ToggleAntenna( "front" )
UTIL:Notify( "Front antenna toggled." )
end
-- 'Num5' key, toggles rear antenna
if ( IsDisabledControlJustPressed( 1, 112 ) ) then
self:ToggleAntenna( "rear" )
UTIL:Notify( "Rear antenna toggled." )
end ]]
end
--[[------------------------------------------------------------------------
NUI callback
------------------------------------------------------------------------]]--
RegisterNUICallback( "togglePower", function()
RADAR:TogglePower()
end )
RegisterNUICallback( "closeRemote", function()
SetNuiFocus( false, false )
end )
RegisterNUICallback( "setAntennaMode", function( data )
RADAR:SetAntennaMode( data.value, tonumber( data.mode ), function()
SendNUIMessage( { _type = "antennaMode", ant = data.value, mode = tonumber( data.mode ) } )
end )
end )
RegisterNUICallback( "toggleAntenna", function( data )
RADAR:ToggleAntenna( data.value, function()
SendNUIMessage( { _type = "antennaXmit", ant = data.value, on = RADAR:IsAntennaTransmitting( data.value ) } )
end )
end )
--[[------------------------------------------------------------------------
Main function
------------------------------------------------------------------------]]--
function RADAR:Main()
-- Check to make sure the player is in the driver's seat, and also that the vehicle has a class of VC_EMERGENCY (18)
if ( DoesEntityExist( PLY.veh ) and PLY.inDriverSeat and GetVehicleClass( PLY.veh ) == 18 and self:IsPowerOn() and not self:IsPoweringUp() ) then
local plyVehPos = GetEntityCoords( PLY.veh )
-- First stage of the radar - get all of the vehicles hit by the radar
if ( self:GetRadarStage() == 0 ) then
if ( self:GetRayTraceState() == 0 ) then
local vehs = self:GetVehiclePool()
self:ResetCapturedVehicles()
self:ResetRayTraceState()
self:CreateRayThreads( PLY.veh, vehs )
elseif ( self:GetRayTraceState() == self:GetNumOfRays() ) then
self:IncreaseRadarStage()
end
elseif ( self:GetRadarStage() == 1 ) then
local data = {}
-- Get the player's vehicle speed
local entSpeed = GetEntitySpeed( PLY.veh )
if ( entSpeed == 0 ) then
data.patrolSpeed = "¦[]"
else
local speed = self:GetVehSpeedFormatted( entSpeed )
data.patrolSpeed = UTIL:FormatSpeed( speed )
end
-- Only grab data to send if there have actually been vehicles captured by the radar
if ( not UTIL:IsTableEmpty( self:GetCapturedVehicles() ) ) then
local vehsForDisplay = self:GetVehiclesForAntenna()
self:SetActiveVehicles( vehsForDisplay ) -- not really any point in setting this
else
self:SetActiveVehicles( { ["front"] = { nil, nil }, ["rear"] = { nil, nil } } )
end
-- Work out what has to be sent
local av = self:GetActiveVehicles()
data.antennas = { ["front"] = nil, ["rear"] = nil }
for ant in UTIL:Values( { "front", "rear" } ) do
if ( self:IsAntennaTransmitting( ant ) ) then
data.antennas[ant] = {}
for i = 1, 2 do
data.antennas[ant][i] = { speed = "¦¦¦", dir = 0 }
-- The vehicle data exists for this slot
if ( av[ant][i] ~= nil ) then
-- We already have the vehicle speed as we needed it earlier on for filtering
data.antennas[ant][i].speed = UTIL:FormatSpeed( self:GetVehSpeedFormatted( av[ant][i].speed ) )
-- Work out if the vehicle is closing or away
local ownH = GetEntityHeading( PLY.veh )
local tarH = GetEntityHeading( av[ant][i].veh )
data.antennas[ant][i].dir = UTIL:GetEntityRelativeDirection( ownH, tarH, 120 )
end
end
end
end
-- Send the update to the NUI side
SendNUIMessage( { _type = "update", speed = data.patrolSpeed, antennas = data.antennas } )
self:ResetTempVehicleIDs()
self:ResetRadarStage()
self:ResetRayTraceState()
end
end
end
-- Updates the local player information
Citizen.CreateThread( function()
while ( true ) do
PLY.ped = PlayerPedId()
PLY.veh = GetVehiclePedIsIn( PLY.ped, false )
PLY.inDriverSeat = GetPedInVehicleSeat( PLY.veh, -1 ) == PLY.ped
Citizen.Wait( 250 )
end
end )
-- Update the vehicle pool every 3 seconds
Citizen.CreateThread( function()
while ( true ) do
local vehs = RADAR:GetAllVehicles()
RADAR:SetVehiclePool( vehs )
Citizen.Wait( 3000 )
end
end )
-- Main thread
Citizen.CreateThread( function()
SetNuiFocus( false, false )
RADAR:CacheNumRays()
while ( true ) do
RADAR:Main()
Citizen.Wait( 50 )
end
end )
-- Control manager
Citizen.CreateThread( function()
while ( true ) do
RADAR:RunControlManager()
Citizen.Wait( 0 )
end
end )
------------------------------ DEBUG ------------------------------
Citizen.CreateThread( function()
while ( true ) do
-- Ray line drawing
-- local veh = GetVehiclePedIsIn( PlayerPedId(), false )
for k, v in pairs( RADAR.rayTraces ) do
local startP = GetOffsetFromEntityInWorldCoords( PLY.veh, v.startVec.x, 0.0, 0.0 )
local endP = GetOffsetFromEntityInWorldCoords( PLY.veh, v.endVec.x, v.endVec.y, 0.0 )
UTIL:DrawDebugLine( startP, endP )
end
local av = RADAR:GetActiveVehicles()
for ant in UTIL:Values( { "front", "rear" } ) do
for i = 1, 2, 1 do
if ( av[ant] ~= nil and av[ant][i] ~= nil ) then
local pos = GetEntityCoords( av[ant][i].veh )
local r = RADAR:GetDynamicRadius( av[ant][i].veh )
if ( i == 1 ) then
UTIL:DrawDebugSphere( pos.x, pos.y, pos.z, r, { 255, 127, 0, 100 } )
else
UTIL:DrawDebugSphere( pos.x, pos.y, pos.z, r, { 255, 0, 0, 100 } )
end
end
end
end
Citizen.Wait( 0 )
end
end )
--[[ local types = { "FRONT", "FRONT FAST", "REAR", "REAR FAST" }
Citizen.CreateThread( function()
while ( true ) do
-- Caught veh debug printing
local av = RADAR:GetActiveVehicles()
DrawRect( 0.500, 0.850, 0.400, 0.220, 0, 0, 0, 150 )
for i = 1, 4, 1 do
UTIL:DrawDebugText( 0.250 + ( 0.100 * i ), 0.750, 0.60, true, types[i] )
if ( av[i] ~= nil ) then
local pos = GetEntityCoords( av[i].veh )
local speed = RADAR:GetVehSpeedFormatted( GetEntitySpeed( av[i].veh ) )
local veh = av[i].veh
local rt = av[i].rayType
local dir = UTIL:GetEntityRelativeDirection( GetEntityHeading( GetVehiclePedIsIn( PlayerPedId(), false ) ), GetEntityHeading( veh ), 100 )
if ( dir == 1 ) then dir = "/\\" elseif ( dir == 2 ) then dir = "\\/" else dir = "none" end
DrawMarker( 2, pos.x, pos.y, pos.z + 3, 0.0, 0.0, 0.0, 0.0, 180.0, 0.0, 1.0, 1.0, 1.0, 255, 255, 0, 70, false, true, 2, nil, nil, false )
if ( i % 2 == 0 ) then
UTIL:DrawDebugText( 0.250 + ( 0.100 * i ), 0.800, 0.60, true, "Ent: " .. tostring( veh ) .. "\nSpeed: ~r~" .. tostring( speed ) .. "~s~mph" .. "\nRay type: " .. tostring( rt ) .. "\nDir: " .. tostring( dir ) )
else
UTIL:DrawDebugText( 0.250 + ( 0.100 * i ), 0.800, 0.60, true, "Ent: " .. tostring( veh ) .. "\nSpeed: " .. tostring( speed ) .. "mph" .. "\nRay type: " .. tostring( rt ) .. "\nDir: " .. tostring( dir ) )
end
else
UTIL:DrawDebugText( 0.250 + ( 0.100 * i ), 0.800, 0.60, true, "Ent: nil" .. "\nSpeed: nil" .. "\nRay type: nil" .. "\nDir: nil" )
end
end
-- Ray line drawing
local veh = GetVehiclePedIsIn( PlayerPedId(), false )
for k, v in pairs( RADAR.rayTraces ) do
local startP = GetOffsetFromEntityInWorldCoords( veh, v.startVec.x, 0.0, 0.0 )
local endP = GetOffsetFromEntityInWorldCoords( veh, v.endVec.x, v.endVec.y, 0.0 )
UTIL:DrawDebugLine( startP, endP )
end
Citizen.Wait( 0 )
end
end ) ]]
-- Commands for debugging
RegisterCommand( "rdr", function( src, args, raw )
if ( args[1] == "setlimit" ) then
RADAR:SetFastLimit( tonumber( args[2] ) )
elseif ( args[1] == "setmode" ) then
if ( args[2] == "front" or args[2] == "rear" ) then
RADAR:SetAntennaMode( args[2], tonumber( args[3] ) )
end
end
end, false )