From c85098a207aa5a7e957a98f0fae23d2669dceff7 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 7 Nov 2019 18:44:24 +0000 Subject: [PATCH] Init --- __resource.lua | 24 ++ bounding box vector points.txt | 13 + cl_radar - sphere test.lua | 633 +++++++++++++++++++++++++++++++++ cl_radar.lua | 553 ++++++++++++++++++++++++++++ cl_utils.lua | 324 +++++++++++++++++ config.lua | 26 ++ nui/digital-7.regular.ttf | Bin 0 -> 34360 bytes nui/radar.css | 302 ++++++++++++++++ nui/radar.html | 158 ++++++++ nui/radar.js | 197 ++++++++++ old_cl_radar.lua | 619 ++++++++++++++++++++++++++++++++ radar operation.txt | 17 + 12 files changed, 2866 insertions(+) create mode 100644 __resource.lua create mode 100644 bounding box vector points.txt create mode 100644 cl_radar - sphere test.lua create mode 100644 cl_radar.lua create mode 100644 cl_utils.lua create mode 100644 config.lua create mode 100644 nui/digital-7.regular.ttf create mode 100644 nui/radar.css create mode 100644 nui/radar.html create mode 100644 nui/radar.js create mode 100644 old_cl_radar.lua create mode 100644 radar operation.txt diff --git a/__resource.lua b/__resource.lua new file mode 100644 index 0000000..0a0ede2 --- /dev/null +++ b/__resource.lua @@ -0,0 +1,24 @@ +--[[----------------------------------------------------------------------- + + Wraith Radar System - v2.0.0 + Created by WolfKnight + +-----------------------------------------------------------------------]]-- + +resource_manifest_version '44febabe-d386-4d18-afbe-5e627f4af937' + +name 'WraithRS 2' +description 'An advanced radar system for FiveM' +author 'WolfKnight' +version '2.0.0' + +ui_page "nui/radar.html" + +files { + "nui/*" +} + +client_script 'config.lua' +client_script 'cl_utils.lua' +client_script 'cl_radar.lua' +-- client_script 'cl_radar - sphere test.lua' \ No newline at end of file diff --git a/bounding box vector points.txt b/bounding box vector points.txt new file mode 100644 index 0000000..82a1b15 --- /dev/null +++ b/bounding box vector points.txt @@ -0,0 +1,13 @@ +Vector points for bounding box: +All points are assuming the view is facing the rear of the vehicle +All points have been manually tested + +Bottom back left: min.x min.y min.z +Bottom back right: max.x min.y min.z +Bottom front left: min.x max.y min.z +Bottom front right: max.x max.y min.z + +Top back left: min.x min.y max.z +Top back right: max.x min.y max.z +Top front left: min.x max.y max.z +Top front right: max.x max.y max.z \ No newline at end of file diff --git a/cl_radar - sphere test.lua b/cl_radar - sphere test.lua new file mode 100644 index 0000000..83fcb8f --- /dev/null +++ b/cl_radar - sphere test.lua @@ -0,0 +1,633 @@ +--[[------------------------------------------------------------------------ + + Wraith Radar System - v1.0.3 + Created by WolfKnight + +------------------------------------------------------------------------]]-- + +--[[------------------------------------------------------------------------ + Resource Rename Fix +------------------------------------------------------------------------]]-- +Citizen.CreateThread( function() + -- Wait for a short period of time to give the resource time to load + Citizen.Wait( 5000 ) + + -- Get the name of the resource, for example the default name is 'wk_wrs' + local resourceName = GetCurrentResourceName() + + -- Send a message through the NUI system to the JavaScript file to + -- give the name of the resource + SendNUIMessage( { resourcename = resourceName } ) +end ) + +--[[------------------------------------------------------------------------ + Radar variables +------------------------------------------------------------------------]]-- +RADAR.vars = +{ + -- Player's vehicle speed + patrolSpeed = 0, + + -- The speed type + speedType = "mph", + + -- Table to store entity IDs of captured vehicles + capturedVehicles = {}, + + captureOffsets = {}, + + -- Antennas + antennas = { + front = { + + }, + + back = { + + } + } +} + +--[[------------------------------------------------------------------------ + Radar setters and getters +------------------------------------------------------------------------]]-- +function RADAR:SetPatrolSpeed( speed ) + if ( type( speed ) == "number" ) then + self.vars.patrolSpeed = speed + end +end + +function RADAR:GetPatrolSpeed() + return self.vars.patrolSpeed +end + +--[[------------------------------------------------------------------------ + Radar functions +------------------------------------------------------------------------]]-- +function RADAR:GetVehSpeed( veh ) + if ( self.vars.speedType == "mph" ) then + return GetEntitySpeed( veh ) * 2.236936 + else + return GetEntitySpeed( veh ) * 3.6 + end +end + +function RADAR:ResetCapturedVehicles() + self.vars.capturedVehicles = {} +end + + +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 EnumeratePeds() + return EnumerateEntities(FindFirstPed, FindNextPed, EndFindPed) +end + +function EnumerateVehicles() + return EnumerateEntities(FindFirstVehicle, FindNextVehicle, EndFindVehicle) +end + + +local traces = +{ + { startVec = { x = 0.0, y = 5.0 }, endVec = { x = 0.0, y = 150.0 } }, + + -- "Forward cone" + { startVec = { x = -5.0, y = 15.0 }, endVec = { x = -5.0, y = 150.0 } }, + { startVec = { x = 5.0, y = 15.0 }, endVec = { x = 5.0, y = 150.0 } }, + { startVec = { x = -10.0, y = 25.0 }, endVec = { x = -10.0, y = 150.0 } }, + { startVec = { x = 10.0, y = 25.0 }, endVec = { x = 10.0, y = 150.0 } }, + { startVec = { x = -15.0, y = 35.0 }, endVec = { x = -15.0, y = 150.0 } }, + { startVec = { x = 15.0, y = 35.0 }, endVec = { x = 15.0, y = 150.0 } }, + + -- "Rear cone" + --[[{ startVec = { x = -2.5, y = 135.0 }, endVec = { x = -2.5, y = 15.0 } }, + { startVec = { x = 2.5, y = 135.0 }, endVec = { x = 2.5, y = 15.0 } }, + { startVec = { x = -7.5, y = 125.0 }, endVec = { x = -7.5, y = 25.0 } }, + { startVec = { x = 7.5, y = 125.0 }, endVec = { x = 7.5, y = 25.0 } }, + { startVec = { x = -12.5, y = 115.0 }, endVec = { x = -12.5, y = 35.0 } }, + { startVec = { x = 12.5, y = 115.0 }, endVec = { x = 12.5, y = 35.0 } }]] +} + +function RADAR:RayTraceFromVeh( playerVeh ) + local playerVehPos = GetEntityCoords( playerVeh, true ) + local forwardVector = GetEntityForwardVector( playerVeh ) + + -- Vector3 source = playerVehPos + forwardVector * Start.Y (15.0) + Game.Player.Character.CurrentVehicle.RightVector * Start.X (-5.0) + --local rayStart1 = playerVehPos + ( forwardVector * vector3( -5.0, 15.0, 0.0 ) ) + GetEntityRotation( playerVeh, false ) + --local rayEnd1 = playerVehPos + ( forwardVector * vector3( -5.0, 300.0, 0.0 ) ) + GetEntityRotation( playerVeh, false ) + + local offset = GetOffsetFromEntityInWorldCoords( playerVeh, 0.0, 60.0, 0.0 ) + + -- for veh in EnumerateVehicles() do + -- local pos = GetEntityCoords( veh, true ) + + -- local dist = GetDistanceBetweenCoords( pos.x, pos.y, pos.z, offset.x, offset.y, offset.z, true ) + + -- UTIL:DrawDebugText( 0.50, 0.50, 0.60, true, "Dist: " .. tostring( dist ), 255, 255, 255, 255 ) + -- end + + for k, v in pairs( traces ) do + local rayStart1 = GetOffsetFromEntityInWorldCoords( playerVeh, v.startVec.x, v.startVec.y, 0.0 ) + local rayEnd1 = GetOffsetFromEntityInWorldCoords( playerVeh, v.endVec.x, v.endVec.y, 0.0 ) + + DrawLine( rayStart1.x, rayStart1.y, rayStart1.z, rayEnd1.x, rayEnd1.y, rayEnd1.z, 255, 255, 255, 255 ) + + local rayHandle = StartShapeTestCapsule( rayStart1.x, rayStart1.y, rayStart1.z, rayEnd1.x, rayEnd1.y, rayEnd1.z, 20.0, 10, playerVeh ) + local _, hitEntity, endCoords, surfaceNormal, vehicle = GetShapeTestResult( rayHandle ) + + -- SetEntityAsMissionEntity( vehicle ) + + UTIL:DrawDebugText( 0.50, ( k * 0.040 ) + 0.6, 0.60, true, "Found entity: " .. tostring( vehicle ), 255, 255, 255, 255 ) + + local hitEntPos = GetEntityCoords( vehicle, true ) + DrawMarker( 28, hitEntPos.x, hitEntPos.y, hitEntPos.z + 6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 2.0, 0.5, 0, 250, 0, 200, false, true, 2, false, false, false, false ) + end +end + +function RADAR:DoesLineIntersectSphere( centre, radiusSqr, 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 to the centre position of the sphere + local rayToCentre = vehPos - startPoint + + -- 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 sqrt functions is slower. + local tProj = dot( rayToCentre, rayNorm ) + local oppLenSqr = dot( rayToCentre, rayToCentre ) - ( tProj * tProj ) + + -- 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 ) then + return true + end + + return false +end + +--[[------------------------------------------------------------------------ + Test time +------------------------------------------------------------------------]]-- +function GetBoundingBox( ent ) + local mdl = GetEntityModel( ent ) + local min, max = GetModelDimensions( mdl ) + -- local radius = 5.0 + + -- min = min + -radius + -- max = max + radius + + local points = { + vector3( min.x, min.y, min.z ), -- Bottom back left + vector3( max.x, min.y, min.z ), -- Bottom back right + vector3( min.x, max.y, min.z ), -- Bottom front left + vector3( max.x, max.y, min.z ), -- Bottom front right + vector3( min.x, min.y, max.z ), -- Top back left + vector3( max.x, min.y, max.z ), -- Top back right + vector3( min.x, max.y, max.z ), -- Top front left + vector3( max.x, max.y, max.z ) -- Top front right + } + + return points +end + +function GetDrawableFromBoundingBox( box ) + local points = { + { box[1], box[3] }, + { box[3], box[4] }, + { box[4], box[2] }, + { box[2], box[1] }, + + { box[1], box[5] }, + { box[3], box[7] }, + { box[4], box[8] }, + { box[2], box[6] }, + + { box[5], box[7] }, + { box[7], box[8] }, + { box[8], box[6] }, + { box[6], box[5] } + } + + return points +end + +function DrawBoundingBox( ent, box ) + for _, v in pairs( box ) do + local a = GetOffsetFromEntityInWorldCoords( ent, v[1] ) + local b = GetOffsetFromEntityInWorldCoords( ent, v[2] ) + + DrawLine( a.x, a.y, a.z, b.x, b.y, b.z, 255, 255, 255, 255 ) + end +end + +function GetAllVehicleEnts() + local ents = {} + + for v in EnumerateVehicles() do + table.insert( ents, v ) + end + + return ents +end + +function CustomShapeTest( startPos, endPos, entToIgnore ) + local flags = 1 | 2 | 4 | 16 | 256 + local trace = StartShapeTestRay( startPos, endPos, flags, entToIgnore, 7 ) + local _, didHit, hitPos, _, hitEnt = GetShapeTestResult( trace ) + + UTIL:DrawDebugText( 0.500, 0.850, 0.55, true, "Hit?: " .. tostring( didHit ) .. "\nHit pos: " .. tostring( hitPos ) .. "\nHit ent: " .. tostring( hitEnt ) ) + + if ( hitPos ) then + DrawMarker( 28, hitPos.x, hitPos.y, hitPos.z + 5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.25, 0, 250, 120, 200, false, true, 2, false, false, false, false ) + end +end + +function values(xs) + local i = 0 + return function() + i = i + 1; + return xs[i] + end +end + +function map(xs, fn) + local t = {} + for i,v in ipairs(xs) do + local r = fn(v, i, xs) + table.insert(t, r) + end + return t +end + +function filter(xs, fn) + local t = {} + for i,v in ipairs(xs) do + if fn(v, i, xs) then + table.insert(t, v) + end + end + return t +end + + +function DoesLineIntersectAABB( p1, p2, min, max ) + -- I know this probably isn't the best way to check if a line with a 'radius' intersects with + -- a bounding box, but it's a lot less resource intensive so... + --local min = min + -3.0 + --local max = max + 3.0 + + -- This checks to make sure the origin of the line doesn't start inside the box + -- Also known as "IsPointInsideAABB" + if ( ( p1.x > min.x and p1.x < max.x and p1.y > min.y and p1.y < max.y and p1.z > min.z and p1.z < max.z ) or + ( p2.x > min.x and p2.x < max.x and p2.y > min.y and p2.y < max.y and p2.z > min.z and p2.z < max.z ) ) then + return true + end + + -- This checks to make sure the line itself is not even near the box + for a in values( { 'x', 'y', 'z' } ) do + -- The first half of this statement checks to see if the axis origin and end line points + -- are less than the minimum of the bounding box, the second half checks the start and + -- end line points are beyond the maximum of the bounding box + if ( ( p1[a] < min[a] and p2[a] < min[a] ) or ( p1[a] > max[a] and p2[a] > max[a] ) ) then + return false + end + end + + -- Still trying to work out how this one works + for p in values( { min, max } ) do + for a, o in pairs( { x = { 'y', 'z' }, y = { 'x', 'z' }, z = { 'x', 'y' } } ) do + + -- p = min or max as vector + -- a = x, y or z + -- o = 'y''z', 'x''z' or 'x''y' respectively + -- eg h = l1 + ( l1[x] - min[x] ) / ( l1[x] - l2[x] ) * ( l2 - l1 ) + -- eg o1, o2 = [o1], o[2] = 'y', 'z' + + -- The below variable h appears to calculate the values that allow for x1, x2, y1, and y2 + -- This way the intersection point can be found + local h = p1 + ( p1[a] - p[a] ) / ( p1[a] - p2[a] ) * ( p2 - p1 ) + local o1, o2 = o[1], o[2] + + if ( h[o1] >= min[o1] and h[o1] <= max[o1] and h[o2] >= min[o2] and h[o2] <= max[o2] ) then + return true + end + end + end + + return false +end + +function DoesLineIntersectSphere() +end + +function DoesLineIntersectEntityBoundingBox(p1, p2, entity) + local model = GetEntityModel(entity) + local min, max = GetModelDimensions(model) + + local l1 = GetOffsetFromEntityGivenWorldCoords( entity, p1 ) + local l2 = GetOffsetFromEntityGivenWorldCoords( entity, p2 ) + + -- Citizen.Trace( "\np1: " .. tostring( p1 ) .. "\tp2: " .. tostring( p2 ) .. "\tl1: " .. tostring( l1 ) .. "\tl2: " .. tostring( l2 ) ) + + return DoesLineIntersectAABB(l1, l2, min, max) +end + + +function RaytraceBoundingBox(p1, p2, ignoredEntity) + local entities = GetAllVehicleEnts() + local matches = filter(entities, function (entity) + if entity == ignoredEntity then return false end + if not IsEntityOnScreen(entity) then return false end + -- if not IsEntityTargetable(entity) then return false end + return DoesLineIntersectEntityBoundingBox(p1, p2, entity) + end) + + table.sort(matches, function (a, b) + local h1 = GetEntityCoords(a) + local h2 = GetEntityCoords(b) + return #(p1 - h1) < #(p1 - h2) + end) + + if matches[1] then + local pos = GetEntityCoords(matches[1]) + return pos, matches[1] + end + + return nil, nil +end + + +function RADAR:Test() + -- Get the player's ped + local ped = GetPlayerPed( -1 ) + + -- Make sure the player is sitting in a vehicle + if ( IsPedSittingInAnyVehicle( ped ) ) then + -- Get the vehicle the player is in + local plyVeh = GetVehiclePedIsIn( ped, false ) + local plyVehPos = GetEntityCoords( plyVeh ) + + -- Check the vehicle actually exists and that the player is in the driver's seat + if ( DoesEntityExist( plyVeh ) and GetPedInVehicleSeat( plyVeh, -1 ) ) then + local startPoint = GetOffsetFromEntityInWorldCoords( plyVeh, 0.0, 5.0, 0.0 ) + local endPoint = GetOffsetFromEntityInWorldCoords( plyVeh, 0.0, 50.0, 1.0 ) + DrawLine( startPoint, endPoint, 0, 255, 0, 255 ) + + --for veh in EnumerateVehicles() do + + local veh = UTIL:GetVehicleInDirection( plyVeh, startPoint, endPoint ) + + if ( DoesEntityExist( veh ) ) then + local vehPos = GetEntityCoords( veh ) + local vehPosRel = GetOffsetFromEntityGivenWorldCoords( plyVeh, vehPos ) + + --[[local vehBox = GetBoundingBox( veh ) + local drawableBox = GetDrawableFromBoundingBox( vehBox ) + DrawBoundingBox( veh, drawableBox )]] + + if ( veh ~= plyVeh ) then + DrawMarker( 28, vehPos.x, vehPos.y, vehPos.z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 10.0, 10.0, 10.0, 255, 120, 80, 40, false, true, 2, false, false, false, false ) + + -- First attempt, didn't really work + -- local r = 5.0 + -- local ro = startPoint + -- local s = vehPos + -- local rd = norm( startPoint ) + -- local t = dot( s - ro, rd ) + -- local p = ro + ( rd * t ) + + -- UTIL:DrawDebugSphere( ro.x, ro.y, ro.z, 0.5 ) + -- UTIL:DrawDebugSphere( s.x, s.y, s.z, 0.5 ) + -- UTIL:DrawDebugSphere( p.x, p.y, p.z, 0.5 ) + + -- local y = #( s - p ) + + -- if ( y < r ) then + -- local x = math.sqrt( ( r * r ) - ( y * y ) ) + -- local t1 = t - x + -- local t2 = t + x + + -- UTIL:DrawDebugText( 0.500, 0.700, 0.50, true, "t1: " .. tostring( t1 ) .. "\nt2: " .. tostring( t2 ) ) + -- end + + -- Second attempt + -- local ra = 20.0 + -- local ce = vehPos + -- local ro = startPoint + -- local rd = norm( startPoint ) + + -- local oc = ro - ce + -- local b = dot( oc, rd ) + -- local c = dot( oc, oc ) - ( ra * ra ) + -- local h = ( b * b ) - c + + -- if ( h > 0.0 ) then + -- h = math.sqrt( h ) + -- local result = vector2( -b-h, -b+h ) + + -- UTIL:DrawDebugText( 0.500, 0.700, 0.50, true, "Result: " .. tostring( result ) ) + -- end + + -- Third bloody attempt + -- local center = vehPos + -- local radius = 5.0 + -- local ro = startPoint + -- local rd = norm( startPoint ) + + -- local oc = startPoint - center + -- local a = dot( rd, rd ) + -- local b = 2.0 * dot( oc, rd ) + -- local c = dot( oc, oc ) - radius * radius + -- local dis = b * b - 4 * a * c + + -- if ( dis < 0 ) then + -- UTIL:DrawDebugText( 0.500, 0.650, 0.50, true, "-1" ) + -- else + -- local val = ( -b - math.sqrt( dis ) ) / ( 2.0 * a ) + -- UTIL:DrawDebugText( 0.500, 0.650, 0.50, true, "yes" ) + -- end + + -- Fourth attempt + local radius = 10.0 + local radiusSqr = radius * radius + + local rayNorm = norm( endPoint - startPoint ) + UTIL:DrawDebugText( 0.500, 0.600, 0.50, true, "rayNorm: " .. tostring( rayNorm ) ) + + local rayToCenter = vehPos - startPoint + DrawLine( startPoint, vehPos, 255, 255, 255, 255 ) + + local tProj = dot( rayToCenter, rayNorm ) + UTIL:DrawDebugText( 0.500, 0.625, 0.50, true, "tProj: " .. tostring( tProj ) ) + + UTIL:DrawDebugText( 0.500, 0.100, 0.60, true, "Veh in front? = " .. tostring( ( tProj > 0 ) ) ) + + local iPos = ( rayNorm * tProj ) + startPoint + DrawLine( vehPos, iPos, 255, 255, 255, 255 ) -- draw the smallest point + DrawLine( startPoint, iPos, 255, 255, 255, 255 ) -- draw a line on the opposite + + local oppLenSqr = dot( rayToCenter, rayToCenter ) - ( tProj * tProj ) + UTIL:DrawDebugText( 0.500, 0.650, 0.50, true, "radiusSqr: " .. tostring( radiusSqr ) ) + UTIL:DrawDebugText( 0.500, 0.675, 0.50, true, "oppLenSqr: " .. tostring( oppLenSqr ) ) + + if ( oppLenSqr > radiusSqr ) then + UTIL:DrawDebugText( 0.500, 0.800, 0.50, true, "Projection point outside radius" ) + elseif ( oppLenSqr == radiusSqr ) then + UTIL:DrawDebugText( 0.500, 0.825, 0.50, true, "Single point intersection" ) + end + + local oLen = math.sqrt( radiusSqr - oppLenSqr ) + UTIL:DrawDebugText( 0.500, 0.700, 0.50, true, "oLen: " .. tostring( oLen ) ) + + local t0 = tProj - oLen + local t1 = tProj + oLen + + if ( t1 < t0 ) then + local tmp = t0 + t0 = t1 + t1 = tmp + end + + UTIL:DrawDebugText( 0.500, 0.725, 0.50, true, "t0: " .. tostring( t0 ) .. "\nt1: " .. tostring( t1 ) ) + + local t0p = ( rayNorm * t0 ) + startPoint + local t1p = ( rayNorm * t1 ) + startPoint + + UTIL:DrawDebugSphere( t0p.x, t0p.y, t0p.z, 0.25 ) + UTIL:DrawDebugSphere( t1p.x, t1p.y, t1p.z, 0.25 ) + + DrawLine( vehPos, t0p, 255, 255, 0, 255 ) + DrawLine( vehPos, t1p, 255, 255, 0, 255 ) + + if ( oppLenSqr < radiusSqr ) then + UTIL:DrawDebugText( 0.500, 0.875, 0.55, true, "Intersects sphere" ) + else + UTIL:DrawDebugText( 0.500, 0.925, 0.55, true, "Doesn't intersect sphere" ) + end + end + end + end + end +end + +function RADAR:Main() + -- Get the local player's ped and store it in a variable + local ped = GetPlayerPed( -1 ) + + -- As we only want the radar to work when a player is sitting in a + -- vehicle, we run that check first + if ( IsPedSittingInAnyVehicle( ped ) ) then + -- Get the vehicle the player is sitting in + local vehicle = GetVehiclePedIsIn( ped, false ) + + UTIL:DrawDebugText( 0.50, 0.020, 0.60, true, "Found player vehicle: " .. tostring( vehicle ), 255, 255, 255, 255 ) + + -- 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 ( GetPedInVehicleSeat( vehicle, -1 ) == ped --[[ and GetVehicleClass( vehicle ) == 18 ]] ) then + local vehicleSpeed = UTIL:Round( self:GetVehSpeed( vehicle ), 0 ) + local vehicleHeading = UTIL:Round( GetEntityHeading( vehicle ), 0 ) + + UTIL:DrawDebugText( 0.50, 0.060, 0.60, true, "Self speed: " .. tostring( vehicleSpeed ), 255, 255, 255, 255 ) + + local vehiclePos = GetEntityCoords( vehicle, true ) + -- local edge = GetOffsetFromEntityInWorldCoords( vehicle, 20.0, 50.0, 0.0 ) + + local newX = UTIL:Round( vehiclePos.x, 0 ) + local newY = UTIL:Round( vehiclePos.y, 0 ) + local newZ = UTIL:Round( vehiclePos.z, 0 ) + + UTIL:DrawDebugText( 0.50, 0.100, 0.60, true, "Vehicle X: " .. tostring( newX ) .. " Vehicle Y: " .. tostring( newY ) .. " Vehicle Z: " .. tostring( newZ ), 255, 255, 255, 255 ) + + local model = GetEntityModel( vehicle ) + local min, max = GetModelDimensions( model ) + local format = "Min X: " .. tostring( min.x ) .. " Min Y: " .. tostring( min.y ) .. " Min Z: " .. tostring( min.z ) + local format2 = "Max X: " .. tostring( max.x ) .. " Max Y: " .. tostring( max.y ) .. " Max Z: " .. tostring( max.z ) + + UTIL:DrawDebugText( 0.50, 0.180, 0.60, true, "Model: " .. tostring( model ), 255, 255, 255, 255 ) + UTIL:DrawDebugText( 0.50, 0.220, 0.60, true, format, 255, 255, 255, 255 ) + UTIL:DrawDebugText( 0.50, 0.260, 0.60, true, format2, 255, 255, 255, 255 ) + + for veh in EnumerateVehicles() do + if ( GetEntitySpeed( veh ) > 1.0 and veh ~= vehicle ) then + local aiVehHeading = UTIL:Round( GetEntityHeading( veh ), 0 ) + local sameHeading = UTIL:IsEntityInMyHeading( vehicleHeading, aiVehHeading, 45 ) + + if ( sameHeading ) then + local mdl = GetEntityModel( veh ) + + if ( IsThisModelACar( mdl ) or IsThisModelABike( mdl ) or IsThisModelAQuadbike( mdl ) ) then + local bounds = getBoundingBox( veh ) + local boundsDrawable = getBoundingBoxDrawable( bounds ) + drawBoundingBox( veh, boundsDrawable ) + end + end + end + end + + -- self:RayTraceFromVeh( vehicle ) + + -- local newPos = GetOffsetFromEntityInWorldCoords( vehicle, 0.0, 10.0, 0.0 ) + + -- UTIL:DrawSphere( newPos.x, newPos.y, newPos.z, 6.0, 128, 255, 0, 0.15 ) + + -- local ranVeh = GetRandomVehicleInSphere( newPos.x, newPos.y, newPos.z, 6.0, 0, 23 ) + + -- UTIL:DrawDebugText( 0.50, 0.140, 0.60, true, "Found random vehicle: " .. tostring( ranVeh ), 255, 255, 255, 255 ) + + -- if ( DoesEntityExist( ranVeh ) and IsEntityAVehicle( ranVeh ) ) then + -- local targetPos = GetEntityCoords( ranVeh, true ) + -- DrawMarker(2, targetPos.x, targetPos.y, targetPos.z + 6, 0.0, 0.0, 0.0, 0.0, 180.0, 0.0, 2.0, 2.0, 2.0, 255, 128, 0, 50, false, true, 2, nil, nil, false) + -- end + + -- begin bubble test + -- for i = 1, 5 do + -- local newPos = GetOffsetFromEntityInWorldCoords( vehicle, 0.0, i * 10.0, 0.0 ) + -- -- UTIL:DrawSphere( newPos1.x, newPos1.y, newPos1.z, 2.0, 128, 255, 20, 0.4 ) + -- DrawMarker( 28, newPos.x, newPos.y, newPos.z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, i * 2.0, i * 2.0, 0.5, 0, 250, 0, 200, false, true, 2, false, false, false, false ) + -- end + end + end +end + +Citizen.CreateThread( function() + while ( true ) do + -- RADAR:Main() + RADAR:Test() + + Citizen.Wait( 0 ) + end +end ) \ No newline at end of file diff --git a/cl_radar.lua b/cl_radar.lua new file mode 100644 index 0000000..0c1c67f --- /dev/null +++ b/cl_radar.lua @@ -0,0 +1,553 @@ +--[[------------------------------------------------------------------------ + + Wraith Radar System - v2.0.0 + Created by WolfKnight + +------------------------------------------------------------------------]]-- + +local next = next +local dot = dot +local table = table +local type = type + +--[[------------------------------------------------------------------------ + Resource Rename Fix +------------------------------------------------------------------------]]-- +Citizen.CreateThread( function() + -- Wait for a short period of time to give the resource time to load + Citizen.Wait( 5000 ) + + -- Get the name of the resource, for example the default name is 'wk_wrs2' + local resourceName = GetCurrentResourceName() + + -- Send a message through the NUI system to the JavaScript file to + -- give the name of the resource + SendNUIMessage( { resourcename = resourceName } ) +end ) + +--[[------------------------------------------------------------------------ + Radar variables +------------------------------------------------------------------------]]-- +RADAR.vars = +{ + -- Player's vehicle speed + patrolSpeed = 0, + + -- The speed type + speedType = "mph", + + -- Antennas + antennas = { + front = { + + }, + + back = { + + } + }, + + sortMode = 1, + + maxCheckDist = 300.0, + + -- Cached dynamic sphere sizes + sphereSizes = {}, + + -- Vehicle pool + 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 radar + -- - 1 = Filtering the vehicles caught + -- - 3 = Calculating what vehicle speed to show based on modes + radarStage = 0, + + -- Ray stage + rayTraceState = 0, + + -- Number of rays + numberOfRays = 0 +} + +-- Table to store entity IDs of captured vehicles +RADAR.capturedVehicles = {} +RADAR.caughtEnt = 0 + +RADAR.rayTraces = { + { startVec = { x = 0.0, y = 5.0 }, endVec = { x = 0.0, y = 150.0 } }, + { startVec = { x = -5.0, y = 15.0 }, endVec = { x = -5.0, y = 150.0 } }, + { startVec = { x = 5.0, y = 15.0 }, endVec = { x = 5.0, y = 150.0 } }, + --{ startVec = { x = -12.0, y = 25.0 }, endVec = { x = -12.0, y = 150.0 } }, + --{ startVec = { x = 12.0, y = 25.0 }, endVec = { x = 12.0, y = 150.0 } } +} + +RADAR.sorting = { + [1] = { name = "CLOSEST", func = function( a, b ) return a.dist < b.dist end }, + [2] = { name = "FASTEST", func = function( a, b ) return a.speed > b.speed end }, + --[3] = { name = "FASTEST & CLOSEST", func = function( a, b ) return math.ceil( a.speed ) > math.ceil( b.speed ) and a.dist < b.dist end }, + [3] = { name = "LARGEST", func = function( a, b ) return a.size > b.size end } +} + +--[[------------------------------------------------------------------------ + Radar variable functions +------------------------------------------------------------------------]]-- +function RADAR:SetPatrolSpeed( speed ) + if ( type( speed ) == "number" ) then + self.vars.patrolSpeed = speed + end +end + +function RADAR:GetPatrolSpeed() + return self.vars.patrolSpeed +end + +function RADAR:SetVehiclePool( pool ) + if ( type( pool ) == "table" ) then + self.vars.vehiclePool = pool + end +end + +function RADAR:GetVehiclePool() + return self.vars.vehiclePool +end + +function RADAR:GetMaxCheckDist() + return self.vars.maxCheckDist +end + +function RADAR:GetRayTraceState() + return self.vars.rayTraceState +end + +function RADAR:IncreaseRayTraceState() + self.vars.rayTraceState = self.vars.rayTraceState + 1 +end + +function RADAR:ResetRayTraceState() + self.vars.rayTraceState = 0 +end + +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 + +function RADAR:GetNumOfRays() + return self.vars.numberOfRays +end + +function RADAR:CacheNumOfRays() + self.vars.numberOfRays = #self.rayTraces +end + +function RADAR:ToggleSortMode() + if ( self.vars.sortMode < #self.sorting ) then + self.vars.sortMode = self.vars.sortMode + 1 + else + self.vars.sortMode = 1 + end + + UTIL:Notify( "Radar mode set to " .. self.sorting[self.vars.sortMode].name ) +end + +--[[------------------------------------------------------------------------ + Radar functions +------------------------------------------------------------------------]]-- +function RADAR:GetVehSpeedFormatted( speed ) + if ( self.vars.speedType == "mph" ) then + -- return GetEntitySpeed( veh ) * 2.236936 + return math.ceil( speed * 2.236936 ) + else + return GetEntitySpeed( veh ) * 3.6 + end +end + +function RADAR:ResetCapturedVehicles() + self.capturedVehicles = {} +end + +function RADAR:InsertCapturedVehicleData( t ) + if ( type( t ) == "table" and not UTIL:IsTableEmpty( t ) ) then + for _, v in pairs( t ) do + table.insert( self.capturedVehicles, v ) + end + end +end + +function RADAR:GetCapturedVehicles() + return self.capturedVehicles +end + +function RADAR:FilterCapturedVehicles() + for k, vehTable in pairs( self.capturedVehicles ) do + local veh = vehTable.veh + + for b, v in pairs( self.capturedVehicles ) do + if ( v.veh == veh and k ~= b ) then table.remove( self.capturedVehicles, b ) end + end + end +end + +function RADAR:GetAllVehicles() + local t = {} + + for v in UTIL:EnumerateVehicles() do + table.insert( t, v ) + end + + return t +end + +function RADAR:DoesDynamicRadiusDataExist( key ) + return self.vars.sphereSizes[key] ~= nil +end + +function RADAR:InsertDynamicRadiusData( key, radius, actualSize ) + if ( self.vars.sphereSizes[key] == nil ) then + self.vars.sphereSizes[key] = {} + + self.vars.sphereSizes[key].radius = radius + self.vars.sphereSizes[key].actualSize = actualSize + 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 ) / 10, 4.0, 10.0 ) + + self:InsertDynamicRadiusData( key, dynamicRadius, numericSize ) + + return dynamicRadius, numericSize + end + + return self:GetRadiusData( key ) +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( localVeh, veh, s, e ) + local pos = GetEntityCoords( veh ) + local dist = #( pos - s ) + + if ( DoesEntityExist( veh ) and veh ~= localVeh and dist < self:GetMaxCheckDist() ) then + local entSpeed = GetEntitySpeed( veh ) + local visible = HasEntityClearLosToEntity( localVeh, 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 } ) + + 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 ) + d.speed = UTIL:Round( speed, 3 ) + d.size = math.ceil( size ) + + table.insert( t, d ) + + hasData = true + end + end + + if ( hasData ) then return t end +end + +function RADAR:CreateRayThread( vehs, from, startX, endX, endY ) + --Citizen.CreateThread( function() + local startP = GetOffsetFromEntityInWorldCoords( from, startX, 0.0, 0.0 ) + local endP = GetOffsetFromEntityInWorldCoords( from, endX, endY, 0.0 ) + + UTIL:DrawDebugLine( startP, endP ) + + local hitVehs = self:GetVehsHitByRay( from, vehs, startP, endP ) + + self:InsertCapturedVehicleData( hitVehs ) + + print( "Ray thread: increasing ray state from " .. tostring( self:GetRayTraceState() ) .. " to " .. tostring( self:GetRayTraceState() + 1 ) ) + self:IncreaseRayTraceState() + --end ) +end + +function RADAR:RunControlManager() + -- 'Z' key, toggles debug mode + if ( IsDisabledControlJustPressed( 1, 20 ) ) then + self.config.debug_mode = not self.config.debug_mode + end + + -- Change the sort mode + if ( IsDisabledControlJustPressed( 1, 105 ) ) then + self:ToggleSortMode() + end +end + +--[[------------------------------------------------------------------------ + Test time +------------------------------------------------------------------------]]-- +function RADAR:Main() + -- Get the local player's ped and store it in a variable + local ped = PlayerPedId() + + -- Get the vehicle the player is sitting in + local plyVeh = GetVehiclePedIsIn( ped, false ) + + -- 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( plyVeh ) and GetPedInVehicleSeat( plyVeh, -1 ) == ped and GetVehicleClass( plyVeh ) == 18 ) then + local plyVehPos = GetEntityCoords( plyVeh ) + + -- First stage of the radar - get all of the vehicles hit by the radar + if ( self:GetRadarStage() == 0 ) then + if ( self:GetRayTraceState() == 0 ) then + print( "Radar stage at 0, starting ray trace." ) + local vehs = self:GetVehiclePool() + + print( "Resetting captured vehicles and ray trace state." ) + self:ResetCapturedVehicles() + self:ResetRayTraceState() + + print( "Creating ray threads." ) + for _, v in pairs( self.rayTraces ) do + self:CreateRayThread( vehs, plyVeh, v.startVec.x, v.endVec.x, v.endVec.y ) + end + + print( "Reached end of stage 0." ) + print( "Stage = " .. tostring( self:GetRadarStage() ) .. "\tTrace state = " .. tostring( self:GetRayTraceState() ) ) + elseif ( self:GetRayTraceState() == self:GetNumOfRays() ) then + print( "Ray traces finished, increasing radar stage." ) + self:IncreaseRadarStage() + end + elseif ( self:GetRadarStage() == 1 ) then + print( "Radar stage now 1." ) + + self:FilterCapturedVehicles() + local caughtVehs = self:GetCapturedVehicles() + + if ( not UTIL:IsTableEmpty( caughtVehs ) ) then + -- table.sort( caughtVehs, RADAR.sorting.fastest ) + table.sort( caughtVehs, self.sorting[self.vars.sortMode].func ) + + print( "Printing table for sort mode " .. self.sorting[self.vars.sortMode].name ) + for k, v in pairs( caughtVehs ) do + print( tostring( k ) .. " - " .. tostring( v.veh ) .. " - " .. tostring( v.relPos ) .. " - " .. tostring( v.dist ) .. " - " .. tostring( v.speed ) .. " - " .. tostring( v.size ) ) + end + + self.caughtEnt = caughtVehs[1].veh + else + self.caughtEnt = 0 + end + + self:ResetRadarStage() + self:ResetRayTraceState() + end + + --Wait( 1000 ) + end +end + +function RADAR:Mainold() + -- Get the local player's ped and store it in a variable + local ped = GetPlayerPed( -1 ) + + -- As we only want the radar to work when a player is sitting in a + -- vehicle, we run that check first + if ( IsPedSittingInAnyVehicle( ped ) ) then + -- Get the vehicle the player is sitting in + local vehicle = GetVehiclePedIsIn( ped, false ) + + UTIL:DrawDebugText( 0.50, 0.020, 0.60, true, "Found player vehicle: " .. tostring( vehicle ), 255, 255, 255, 255 ) + + -- 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 ( GetPedInVehicleSeat( vehicle, -1 ) == ped --[[ and GetVehicleClass( vehicle ) == 18 ]] ) then + local vehicleSpeed = UTIL:Round( self:GetVehSpeed( vehicle ), 0 ) + local vehicleHeading = UTIL:Round( GetEntityHeading( vehicle ), 0 ) + + UTIL:DrawDebugText( 0.50, 0.060, 0.60, true, "Self speed: " .. tostring( vehicleSpeed ), 255, 255, 255, 255 ) + + local vehiclePos = GetEntityCoords( vehicle, true ) + -- local edge = GetOffsetFromEntityInWorldCoords( vehicle, 20.0, 50.0, 0.0 ) + + local newX = UTIL:Round( vehiclePos.x, 0 ) + local newY = UTIL:Round( vehiclePos.y, 0 ) + local newZ = UTIL:Round( vehiclePos.z, 0 ) + + UTIL:DrawDebugText( 0.50, 0.100, 0.60, true, "Vehicle X: " .. tostring( newX ) .. " Vehicle Y: " .. tostring( newY ) .. " Vehicle Z: " .. tostring( newZ ), 255, 255, 255, 255 ) + + local model = GetEntityModel( vehicle ) + local min, max = GetModelDimensions( model ) + local format = "Min X: " .. tostring( min.x ) .. " Min Y: " .. tostring( min.y ) .. " Min Z: " .. tostring( min.z ) + local format2 = "Max X: " .. tostring( max.x ) .. " Max Y: " .. tostring( max.y ) .. " Max Z: " .. tostring( max.z ) + + UTIL:DrawDebugText( 0.50, 0.180, 0.60, true, "Model: " .. tostring( model ), 255, 255, 255, 255 ) + UTIL:DrawDebugText( 0.50, 0.220, 0.60, true, format, 255, 255, 255, 255 ) + UTIL:DrawDebugText( 0.50, 0.260, 0.60, true, format2, 255, 255, 255, 255 ) + + for veh in EnumerateVehicles() do + if ( GetEntitySpeed( veh ) > 1.0 and veh ~= vehicle ) then + local aiVehHeading = UTIL:Round( GetEntityHeading( veh ), 0 ) + local sameHeading = UTIL:IsEntityInMyHeading( vehicleHeading, aiVehHeading, 45 ) + + if ( sameHeading ) then + local mdl = GetEntityModel( veh ) + + if ( IsThisModelACar( mdl ) or IsThisModelABike( mdl ) or IsThisModelAQuadbike( mdl ) ) then + local bounds = getBoundingBox( veh ) + local boundsDrawable = getBoundingBoxDrawable( bounds ) + drawBoundingBox( veh, boundsDrawable ) + end + end + end + end + + -- self:RayTraceFromVeh( vehicle ) + + -- local newPos = GetOffsetFromEntityInWorldCoords( vehicle, 0.0, 10.0, 0.0 ) + + -- UTIL:DrawSphere( newPos.x, newPos.y, newPos.z, 6.0, 128, 255, 0, 0.15 ) + + -- local ranVeh = GetRandomVehicleInSphere( newPos.x, newPos.y, newPos.z, 6.0, 0, 23 ) + + -- UTIL:DrawDebugText( 0.50, 0.140, 0.60, true, "Found random vehicle: " .. tostring( ranVeh ), 255, 255, 255, 255 ) + + -- if ( DoesEntityExist( ranVeh ) and IsEntityAVehicle( ranVeh ) ) then + -- local targetPos = GetEntityCoords( ranVeh, true ) + -- DrawMarker(2, targetPos.x, targetPos.y, targetPos.z + 6, 0.0, 0.0, 0.0, 0.0, 180.0, 0.0, 2.0, 2.0, 2.0, 255, 128, 0, 50, false, true, 2, nil, nil, false) + -- end + + -- begin bubble test + -- for i = 1, 5 do + -- local newPos = GetOffsetFromEntityInWorldCoords( vehicle, 0.0, i * 10.0, 0.0 ) + -- -- UTIL:DrawSphere( newPos1.x, newPos1.y, newPos1.z, 2.0, 128, 255, 20, 0.4 ) + -- DrawMarker( 28, newPos.x, newPos.y, newPos.z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, i * 2.0, i * 2.0, 0.5, 0, 250, 0, 200, false, true, 2, false, false, false, false ) + -- end + end + 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 ) + +-- Citizen.SetTimeout( 3000, function() +-- local vehs = RADAR:GetAllVehicles() + +-- RADAR:SetVehiclePool( vehs ) +-- end) + +-- Main thread +Citizen.CreateThread( function() + RADAR:CacheNumOfRays() + + while ( true ) do + RADAR:Main() + -- RADAR:Test() + + Citizen.Wait( 50 ) + end +end ) + +-- Control manager +Citizen.CreateThread( function() + while ( true ) do + RADAR:RunControlManager() + + Citizen.Wait( 0 ) + end +end ) + +Citizen.CreateThread( function() + while ( true ) do + local pos = GetEntityCoords( RADAR.caughtEnt ) + local speed = GetEntitySpeed( RADAR.caughtEnt ) + + DrawMarker( 28, pos.x, pos.y, pos.z + 6, 0.0, 0.0, 0.0, 0.0, 180.0, 0.0, 0.5, 0.5, 3.0, 255, 255, 255, 255, false, true, 2, nil, nil, false ) + + UTIL:DrawDebugText( 0.500, 0.700, 0.80, true, "Ent: " .. tostring( RADAR.caughtEnt ) .. "\nSpeed: " .. RADAR:GetVehSpeedFormatted( speed ) .. "mph" ) + + Citizen.Wait( 0 ) + end +end) \ No newline at end of file diff --git a/cl_utils.lua b/cl_utils.lua new file mode 100644 index 0000000..c52b0fe --- /dev/null +++ b/cl_utils.lua @@ -0,0 +1,324 @@ +--[[----------------------------------------------------------------------- + + Wraith Radar System - v2.0.0 + Created by WolfKnight + +-----------------------------------------------------------------------]]-- +UTIL = {} + +function UTIL:Round( num, numDecimalPlaces ) + -- return tonumber( string.format( "%.0f", num ) ) + return tonumber( string.format( "%." .. ( numDecimalPlaces or 0 ) .. "f", num ) ) +end + +function UTIL:OppositeAngle( ang ) + return ( ang + 180 ) % 360 +end + +function UTIL:FormatSpeed( speed ) + return string.format( "%03d", speed ) +end + +function UTIL:Clamp( val, min, max ) + if ( val < min ) then + return min + elseif ( val > max ) then + return max + end + + return val +end + +function UTIL:IsTableEmpty( t ) + local c = 0 + + for _ in pairs( t ) do c = c + 1 end + + return c == 0 +end + +function UTIL:GetVehicleInDirection( entFrom, coordFrom, coordTo ) + local rayHandle = StartShapeTestCapsule( coordFrom.x, coordFrom.y, coordFrom.z, coordTo.x, coordTo.y, coordTo.z, 20.0, 10, entFrom, 7 ) + local _, hitEntity, endCoords, surfaceNormal, vehicle = GetShapeTestResult( rayHandle ) + return vehicle +end + +function UTIL:IsEntityInMyHeading( myAng, tarAng, angToCheck ) + local rangeStartFront = myAng - ( angToCheck / 2 ) + local rangeEndFront = myAng + ( angToCheck / 2 ) + + local opp = self:OppositeAngle( myAng ) + + local rangeStartBack = opp - ( angToCheck / 2 ) + local rangeEndBack = opp + ( angToCheck / 2 ) + + if ( ( tarAng > rangeStartFront ) and ( tarAng < rangeEndFront ) ) then + return true + elseif ( ( tarAng > rangeStartBack ) and ( tarAng < rangeEndBack ) ) then + return false + else + return nil + end +end + +function UTIL:Notify( text ) + SetNotificationTextEntry( "STRING" ) + AddTextComponentSubstringPlayerName( text ) + DrawNotification( false, true ) +end + +function UTIL:DrawDebugText( x, y, scale, centre, text ) + SetTextFont( 4 ) + SetTextProportional( 0 ) + SetTextScale( scale, scale ) + SetTextColour( 255, 255, 255, 255 ) + SetTextDropShadow( 0, 0, 0, 0, 255 ) + SetTextEdge( 2, 0, 0, 0, 255 ) + SetTextCentre( centre ) + SetTextDropShadow() + SetTextOutline() + SetTextEntry( "STRING" ) + AddTextComponentString( text ) + DrawText( x, y ) +end + +function UTIL:DrawDebugSphere( x, y, z, r, col ) + if ( RADAR.config.debug_mode ) then + local col = col or { 255, 255, 255, 255 } + + DrawMarker( 28, x, y, z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, r, r, r, col[1], col[2], col[3], col[4], false, true, 2, false, false, false, false ) + end +end + +function UTIL:DrawDebugLine( startP, endP, col ) + if ( RADAR.config.debug_mode ) then + local col = col or { 255, 255, 255, 255 } + + DrawLine( startP, endP, col[1], col[2], col[3], col[4] ) + end +end + +UTIL.car_colours = { + [ "0" ] = "Metallic Black", + [ "1" ] = "Metallic Graphite Black", + [ "2" ] = "Metallic Black Steal", + [ "3" ] = "Metallic Dark Silver", + [ "4" ] = "Metallic Silver", + [ "5" ] = "Metallic Blue Silver", + [ "6" ] = "Metallic Steel Gray", + [ "7" ] = "Metallic Shadow Silver", + [ "8" ] = "Metallic Stone Silver", + [ "9" ] = "Metallic Midnight Silver", + [ "10" ] = "Metallic Gun Metal", + [ "11" ] = "Metallic Anthracite Grey", + [ "12" ] = "Matte Black", + [ "13" ] = "Matte Gray", + [ "14" ] = "Matte Light Grey", + [ "15" ] = "Util Black", + [ "16" ] = "Util Black Poly", + [ "17" ] = "Util Dark Silver", + [ "18" ] = "Util Silver", + [ "19" ] = "Util Gun Metal", + [ "20" ] = "Util Shadow Silver", + [ "21" ] = "Worn Black", + [ "22" ] = "Worn Graphite", + [ "23" ] = "Worn Silver Grey", + [ "24" ] = "Worn Silver", + [ "25" ] = "Worn Blue Silver", + [ "26" ] = "Worn Shadow Silver", + [ "27" ] = "Metallic Red", + [ "28" ] = "Metallic Torino Red", + [ "29" ] = "Metallic Formula Red", + [ "30" ] = "Metallic Blaze Red", + [ "31" ] = "Metallic Graceful Red", + [ "32" ] = "Metallic Garnet Red", + [ "33" ] = "Metallic Desert Red", + [ "34" ] = "Metallic Cabernet Red", + [ "35" ] = "Metallic Candy Red", + [ "36" ] = "Metallic Sunrise Orange", + [ "37" ] = "Metallic Classic Gold", + [ "38" ] = "Metallic Orange", + [ "39" ] = "Matte Red", + [ "40" ] = "Matte Dark Red", + [ "41" ] = "Matte Orange", + [ "42" ] = "Matte Yellow", + [ "43" ] = "Util Red", + [ "44" ] = "Util Bright Red", + [ "45" ] = "Util Garnet Red", + [ "46" ] = "Worn Red", + [ "47" ] = "Worn Golden Red", + [ "48" ] = "Worn Dark Red", + [ "49" ] = "Metallic Dark Green", + [ "50" ] = "Metallic Racing Green", + [ "51" ] = "Metallic Sea Green", + [ "52" ] = "Metallic Olive Green", + [ "53" ] = "Metallic Green", + [ "54" ] = "Metallic Gasoline Blue Green", + [ "55" ] = "Matte Lime Green", + [ "56" ] = "Util Dark Green", + [ "57" ] = "Util Green", + [ "58" ] = "Worn Dark Green", + [ "59" ] = "Worn Green", + [ "60" ] = "Worn Sea Wash", + [ "61" ] = "Metallic Midnight Blue", + [ "62" ] = "Metallic Dark Blue", + [ "63" ] = "Metallic Saxony Blue", + [ "64" ] = "Metallic Blue", + [ "65" ] = "Metallic Mariner Blue", + [ "66" ] = "Metallic Harbor Blue", + [ "67" ] = "Metallic Diamond Blue", + [ "68" ] = "Metallic Surf Blue", + [ "69" ] = "Metallic Nautical Blue", + [ "70" ] = "Metallic Bright Blue", + [ "71" ] = "Metallic Purple Blue", + [ "72" ] = "Metallic Spinnaker Blue", + [ "73" ] = "Metallic Ultra Blue", + [ "74" ] = "Metallic Bright Blue", + [ "75" ] = "Util Dark Blue", + [ "76" ] = "Util Midnight Blue", + [ "77" ] = "Util Blue", + [ "78" ] = "Util Sea Foam Blue", + [ "79" ] = "Uil Lightning Blue", + [ "80" ] = "Util Maui Blue Poly", + [ "81" ] = "Util Bright Blue", + [ "82" ] = "Matte Dark Blue", + [ "83" ] = "Matte Blue", + [ "84" ] = "Matte Midnight Blue", + [ "85" ] = "Worn Dark Blue", + [ "86" ] = "Worn Blue", + [ "87" ] = "Worn Light Blue", + [ "88" ] = "Metallic Taxi Yellow", + [ "89" ] = "Metallic Race Yellow", + [ "90" ] = "Metallic Bronze", + [ "91" ] = "Metallic Yellow Bird", + [ "92" ] = "Metallic Lime", + [ "93" ] = "Metallic Champagne", + [ "94" ] = "Metallic Pueblo Beige", + [ "95" ] = "Metallic Dark Ivory", + [ "96" ] = "Metallic Choco Brown", + [ "97" ] = "Metallic Golden Brown", + [ "98" ] = "Metallic Light Brown", + [ "99" ] = "Metallic Straw Beige", + [ "100" ] = "Metallic Moss Brown", + [ "101" ] = "Metallic Biston Brown", + [ "102" ] = "Metallic Beechwood", + [ "103" ] = "Metallic Dark Beechwood", + [ "104" ] = "Metallic Choco Orange", + [ "105" ] = "Metallic Beach Sand", + [ "106" ] = "Metallic Sun Bleeched Sand", + [ "107" ] = "Metallic Cream", + [ "108" ] = "Util Brown", + [ "109" ] = "Util Medium Brown", + [ "110" ] = "Util Light Brown", + [ "111" ] = "Metallic White", + [ "112" ] = "Metallic Frost White", + [ "113" ] = "Worn Honey Beige", + [ "114" ] = "Worn Brown", + [ "115" ] = "Worn Dark Brown", + [ "116" ] = "Worn Straw Beige", + [ "117" ] = "Brushed Steel", + [ "118" ] = "Brushed Black Steel", + [ "119" ] = "Brushed Aluminium", + [ "120" ] = "Chrome", + [ "121" ] = "Worn Off White", + [ "122" ] = "Util Off White", + [ "123" ] = "Worn Orange", + [ "124" ] = "Worn Light Orange", + [ "125" ] = "Metallic Securicor Green", + [ "126" ] = "Worn Taxi Yellow", + [ "127" ] = "Police Car Blue", + [ "128" ] = "Matte Green", + [ "129" ] = "Matte Brown", + [ "130" ] = "Worn Orange", + [ "131" ] = "Matte White", + [ "132" ] = "Worn White", + [ "133" ] = "Worn Olive Army Green", + [ "134" ] = "Pure White", + [ "135" ] = "Hot Pink", + [ "136" ] = "Salmon Pink", + [ "137" ] = "Metallic Vermillion Pink", + [ "138" ] = "Orange", + [ "139" ] = "Green", + [ "140" ] = "Blue", + [ "141" ] = "Mettalic Black Blue", + [ "142" ] = "Metallic Black Purple", + [ "143" ] = "Metallic Black Red", + [ "144" ] = "Hunter Green", + [ "145" ] = "Metallic Purple", + [ "146" ] = "Metaillic V Dark Blue", + [ "147" ] = "MODSHOP BLACK1", + [ "148" ] = "Matte Purple", + [ "149" ] = "Matte Dark Purple", + [ "150" ] = "Metallic Lava Red", + [ "151" ] = "Matte Forest Green", + [ "152" ] = "Matte Olive Drab", + [ "153" ] = "Matte Desert Brown", + [ "154" ] = "Matte Desert Tan", + [ "155" ] = "Matte Foilage Green", + [ "156" ] = "DEFAULT ALLOY COLOR", + [ "157" ] = "Epsilon Blue" +} + +--[[The MIT License (MIT) + + Copyright (c) 2017 IllidanS4 + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + The below code can be found at: 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 UTIL:EnumerateVehicles() + return EnumerateEntities(FindFirstVehicle, FindNextVehicle, EndFindVehicle) +end \ No newline at end of file diff --git a/config.lua b/config.lua new file mode 100644 index 0000000..af4f24e --- /dev/null +++ b/config.lua @@ -0,0 +1,26 @@ +--[[------------------------------------------------------------------------ + + Wraith Radar System - v2.0.0 + Created by WolfKnight + +------------------------------------------------------------------------]]-- + +-- Do not touch this +RADAR = {} +RADAR.config = {} + +-- Radar Control Panel key +-- The default key to open the radar control panel is 166 (F5 - INPUT_SELECT_CHARACTER_MICHAEL) +RADAR.config.control_panel_key = 166 + +-- Radar Unlock/Reset Key +-- The default key to unlock/reset the radar is 244 (M - INPUT_INTERACTION_MENU) +RADAR.config.reset_key = 244 + +-- Fast Lock Blip +-- true = vehicles that go over the fast limit will have a blip added to the minimap for a short period of time +-- false = no blips +RADAR.config.fast_lock_blips = true + +-- Debug mode +RADAR.config.debug_mode = true \ No newline at end of file diff --git a/nui/digital-7.regular.ttf b/nui/digital-7.regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..5dbe6f908239d00a9f88c335df3efb9261ecc3cc GIT binary patch literal 34360 zcmeHw3!GdOP2V|7F8%I9uPza}2bVJM@`V>JyW#qcZrnUY#C>?>y8aChT=~+Vh}j_G=v#U7 zrgY+_5bmG89p$gPZo{f|a~Ia!EmE^qr0TR){Tnw(Qv4!!A4Ps_)!JLGd*v6m$58$^ zBA4u2J+N-e<8yPhA^{h^snvu114rk*b_($6VEe0);J(GV7uN&0p0aw~rY+BGHT%%E z4{4uYyMAT=MN_W}iQLZq{BT|WmJQBG)%THiFRs%={p$uFTh(@+$Q{7XvvR}wjhn7I z|JpescM+dAHw+JMxcv3&?i88E{{OR(R`~vW;m#jl9nJq%Vy-1fk#7bsWO@1a<5$g; zT=8DVR43|`pg5R{7w&PUWk2v*COOGDrmC8Bo`yH5lpEwyiKqmJ_+)_qD+lPLQk=#M z#sdOcJDzrIL&^tqXna$0hLh#&*PW_8`%2_23@hqNr#m$c&b#Dm$Yozx*V72nh%H1t zpt|~YAni$9Uj|$o@b%#f%pIM;p6M&B?_&seTWMCP%A!2W+3|@&mQnQ$>v{}?qstnH zbs9rE&N6RVf1<1|Z&{puDf=4paP2|e3|S6eBQ;XSZu9?I`=Fts0YXLVx9Mw@b}4Yd z7#R0hc^D&(6GrET#fD)l{@;0_IdBWwGYkpI?|nt^rngwYy!8PF#19x$FeHKEn-Z2F zU_?TIQ3(UaBmx*$Fd@<6AEZiRfTqL&t0e(gqhM015KoC&{BNn1YQQ?F0j!rKV1t5< zl0v*mYKw13v(y2mr5>3Z*U(zPcfKw$6*sfq!S`eQmt$@=dQ~bTm zkSTy23eJ=^#AnG=z}eCc*eTiKf65%02AET@OQs_}S7rcqOGoi{GEZg#_9!@CW+8rx z%mzGFIss3UImO>f9@62ra=LF#pEWkcF8}MQ|2k;VE2)GpR zKOk)u0bVA(fS1d;fNz)c0GBDaT+S~Zl`G@|z$;}j;5%dq;8k)V;0gt=mbVpOlWXK6 zz<%iiyjCs-T&duIT!Q$ZEG@n&*U6=TtK>4k)p9xD8U?SHw-^6iZjfbwYh^j$I=KRH zNWt}TCE^?89mQYCjdB&>u&e;wC|3h+Qt&3Z2JxGvzxZ#mS*``#A}axJQSeq7K>VFD z2zZ-ZSNx^iE~^0Vkkx?iQt(b$gZR7Udcdu6L-7~#9$5=`mx6c8I>hghA;9;_dcb>S zL-FVGKDiNan}YYrFyh-~BjEdG6X5-FQ}JKr0}4JMHzWRMvKjD$vIX$Zow$i0A{k@o>UD%$`*EB68JlR06wMQSLC7MVc9Jo0{p5x47f)= z4EQw#pO%jx{&o4w;y=k=`76L@!td=BvY3LcctBmVdD z1;7{OvEq;AC3zh1W!VMz0|kF5Uqt*LHfQJGan;-o$ zkb^|~RZ6!Lkcn)EeK|ItRk=|Ms&+$>k$@X7r^Cs>!S)<32V%VB%i%zUjJSYSSvr%{ z%jrfoJ`?4mK2-~wV|zi5$7;{xQD}~C52#yJBPiifO0wJ231jowo(EW?J&yRuTr|5;C126kA98K zM}1x&YH_>K0NRWB*`Cww^Z9)kcH-*v`S8&%rxWcl>P4}#bSvBEbK~uEO`MPVe16p8 z_TUz@7x(*dlWNb8RIk_Rt!xkD`lqyq`t0`nFoL8i5b*omPLJOo2w)hz=$g~-$49@M zKo5C93TTz3TiLi3Z+<4qM|=JNYVjZlpuOr~Fc9!Ky@5b5=+q>0qUC_~asoZ%0V$wW zmTu<(zf4~)R1%vqLm&@e~_!tcUt&#MQ zm5p2R4tgfe_q+U|unP=@AdL3vqmfX^>+*%e(TL0MXO*sS7$5y|x%@#tqoHsJH~PqJci1~|KI#icfVD3Wj?iptj74#i+aHOgW({fk!S=r zl+vwi+=_S9H*r4di^YL;AQ+3s(Oyf!YA=?EyMsa1UM%L0SufQdGs-8Gae6kf{D0U+A8+ zFTdYRnvB-g*Vc}tH(1&A^)bBbqZ8+&zJ_LCov3bTZbp0Ov}Kx_62a=0bX#k%y1K4D zSRYKM@zJkfFj=2uv@zY-Se9;Or_)t)Wzfw1*dV zmiWc)lG-nV1@2_b*d-q0LFA>Oojb92=oXK7u`l4qen1er1Yy|!QP}-)Nx;@OVdK|8 z$4tSduY)b$AdRr!n{juGw8B=O0vmlQZ1XH^^69X}J7gy8@7b`s=fK|Xf}PzB`?^Qw z!=^qJwsamg^ck?77rjs0 z-65Y%o9SAxUu(LvSVT@5DOKrIx1C`aGk=$(jRgkhFQ2(v0v(HYOR#U*7Y*b7<-3jI-MeLB<5$6z&Z}1- zJBSJwty#FsxCR%Y4kS!(K`h*n?nPa_mn_RHPj5?aJAYtXx;MSLe_)rZO@+u9+_rpn zdY3F+wg&LhWi7iFEUzy`2bV8D74<||4{ky6ZOc)^^;QiCRnqJ?Q7GE67>G~pTXylX zU3V_5-?d=j^7@vR^rBt+`j+k5x3IotIXe<7VG4|IUz4(Mjsxe|bmS*=ElZc}T2Q}B zmT%j}I+rfXwCuWb+qU{`=!bQ^TlS4gFl2PX0xMy+unrE-ZsX2ARE8kaQm@7fas_xU zUx>D=IuNb-gg zNb<%Ck~bZjDoj{T=uORsq$0qrerH%mWcL4n>70ItUP9$G38IoT;DUx4P!nA_q z{lNJelKk2eNb;2xBp*07$p=dv0oK<6{Xs?YRmX|st0zP9HIpLw^(9OzNPYuwzMdps zdjd(mu7c!4$0qsuQb&OG2B5!Qk^IKvMDpRukbL8$NWQ6rX$8q|0?wOA@|#Z}$v0P! ze9N&(eoLt%!1`98e~TjdJC75|Z<`FsZ=V#&?s!gd^B1 zGoEl_*L21cE`!jmQamcft5SSXru$VqpwdGs9#$nIDjrj1;tD2IdX<8v%B@zgM&%|| zJf&bQ%K(q^5B41$Uo+k#iygfVIJ2D>8{aEFV0;v34#iO|5#yBN_Tr-AXK>EsM&l); zuXuyul2>u!W-;C$!+D+0H+v7Nu}iy<8bxXhVeKKD zf2l%Mt7fo>i4YAS!-WDSLhpe+*egd6u&tY%(U{~L%5OM+$HxP0l^jl#FA=ASLz2**E1WfdfU6Zk_20`*}hNU>*&m8cYGXQv=c%2Z&Vq;FUv=B(p}GXV=b$31o;aQw+S#0 zaSzfxNS}|mX0P4C^iHIATIo5&bBME^JmPt)o-+_X19UQy-b{N>vq9M219xWonFio& zV_ZY3&k=FpDJ1Rlx%495StX6>rg`ca?ILtuZmsbfI9Li>K zsXv8ZWV5HM3qXu2Dk2z(3HSyltBsd&vf7PtPGD@C;Wx^{S2Rbyq4_xu#|Mc#1&$7e zUN9bsO}-AQiRKWmn$dBjRDo2SqcsTA2-^@&LD+6xPqVIPSm8`7ox?yL?IM4+g6^|1 zzF~N**d$J(s;~nX@;U{-7DI=cJ$rz!mIX=hWUE3&vyK|E7E(QqnciL1g6ZGo#IVJa z6ah74c?_4cyNlmO2QH0!vf08PK&b^LW`3aXBIf(iqj~dU3WhUr0gCtGg%S^tm^l;U z5$HQuc!h$ZVQ=0vTmdaMhH}U*?0fP_ez)@ihqIK7oNCF)(R1k+dLEJOI`FkwIS;x- zK%WTGS}~6_EAQdaU#q*EA*6UK!a9seV=1QtDOt$f*%;ESA`MgKqD(i+=#(r{<|E~_ zQW>V4PANMJ_>$5wOUiO+jRTk^2q|TgNY~<=lehsX87pNEBt%YTp-DESWBR^R4`5t+ za!F?F0lu0T6=YCLD^=EQfn!Nb74B};@zAh zyzb=O=)k;a#AIY+af|=C7Vx?X*Ksw-oN~1ojaHOwM9RoasLogup(WWySyg4zx4qk~ z2=1-I1Sy{fxu$`_t>AHELB258%{I*7h!vj-10<&NMqi_&o_@aWNa_%OAJ2iINV;X_z$g-TyBlv+S- zgyOVw4v+yBSnq*^Q(Yt}i@aBigASw(_*P*<`BcJ&=Ld4K)40ts6`Z+JW1>3@We6B> z6&8e1uAXGb2)IXpgG0?rS1INz#X_YReqUgfGp!gj1>)F~j2c)375%sbV*PtOo9*q1 zE?N}r=`F>K$AEWVHrkWT_CygW#eo6w-fP_B!0#61%&~ZLfhOKT;!Tyt0leMTP`i;q z(a0$gf-F%uk(wkW2KLEp_6qxBJWz@vqS`Iq3!SzPBRLW8l@uYMyhGz^EHLg-zY~fQ zS8$hRSbJ6(E38H10St~hT1>&~hb zJsZ5#JIbV`5iOH`Wl{}4wJO!&p&S55G(glm&bqJ$gNSYt5>mzsQj!7m%53Z4LCC5Y z$jX_-Rh$|-b#W7M6XZ{p?qUh*am=kp$N_0YNLkflg>76t^oa4+CN()&P^%0;K_SqD zlq$%n29!uza;j{tc9$eniyNaGR}w1jHJ&S5oD2KR{L$CAJkKb+j^+1KN+qbvW$SW4 z$sgt#8PJ!r%($$OJ1(SWd-BNIe5l#Vsfr5M^PNC@e)m9%-5;7XnJLW44> zI<=Gyhm&gsM#5H?Itov5iHH}DN8NWd~J_8HK!^`jirdT464+|fD$w%zAhj0royi&X3j6X2x3&wM{EBa zbS+=XWU*~mQEz-kQz(OBvMH1S=C-u`<0w>vev_7@1G$JBAhfFMK&i$GO3}tfW4{C%;D;Fg?XAC7e>Bmsb9G5CqrQ`!i<-?j3r#@}* zit4d4RK{6|%n4T?V?~K2N=puw-;uRm+uYiw_FAv;5vBu`iMWHaKU*Ogv|Q9S z9XaFJ93qyT!jk12!g@0YP&dLEHm6XI(&}JXDMx8_Xim}U#Q0XnXpT`GHSP>$2KpGo{Neq<4`*T2GR3ye2FD8Qlea9 zu|86r^M7hNmAU$jr82}D3Yy@`M&wT@!SuRSp(>3>)-n7#mZECUbCj~AMm~q%MQ+9Z zu@ig@+HF+~VaVNBSlK5R`eYz<(*~oadp8Prp>5wyn+Fxcx)M@c#C~&i4IQeXl|PKMXMv)Y?4y=E2^; zh~_l1!YXaawg+66U2vFof!cSQ5IS4JCJZ_M0)_2lO)4T|h8L+=!#2s(rt? zza7<8=#~NKmPzcEeORwF4Q1R--Oq#3;U?&4-I5w)2zlJG^&n2|g8T9ufg0$XF*H-7 zIGrg}o_I$JAxo35YY;Y)kL#`VBxdc^*(-1Y1!%2lebP%iQVRiF%ffitt8QPV4q4d8 z?Rb*#=uR`g)i_+PTw#S3w3LoY8q1J9?)ycX{1O94X_z)1#zpWOlXo;DWNEHxjR@PRDRWPu zMcrDf#=z!MsuK~#0JV~^sLdH|Y0hIc01$mD_WW(m)!W8n6oSG&u!sKQ6grf9W>hmY~lYaqn^_;-p~jid&7qBaNfY(VX8aSXxh7}_6`So#NCZLY8E5=7je}Sn+NUv3$8|@{y z?WxwQWZQ^cg-!ViIa6*;DIc-5K$h%{!o#*O87qfrZ#=Gft=tx+H8j3OKH5q>wq;Rn zrI%Y4f2!??z1?PGv?VcWFtN+~(5TJ9afZ|0*BF0UEAghiUsHu0q!wDA+)3r^v}G@6 zDAyUDaHzIsXf@I(dpYY8NGspDVH)Lc3L*C`Se7!D$`NL!UYEEGa#_<{Ve_wEfvmko zYt`v)k7HS{u3UV$(&+VubkJ;J_t)N$v=aH&OloSkFi0(;|Y^uAXueADmJJ)sZJ-qmRV zu9R0vq){4kO{VlEUsTRAeND;D*+J_wiIBVMEKl8q>8<1qsy*B+rtZS+u<`h#yB)3q zBcq(D@t$${gWhcJ^OZ)c!uZ>J6z=b>mgq2T_JCDl?^$sCs^oO`nZ}e$iQf=Hnksrt zWp8K$CdeysV9F<4Q5Z7OM~{sbW>s|dwm3BtZzVo3330&X#hl`zRtt;5>&P;OOSp{4 zGoHJFlvnor*YqgRp4a<@pzNWuocJ}WLyW5s*{33ymlzL@L4djY!1XHk8Wqv6g+ zMn~^M`5k>^05U}3t_oIQ-WIEaib-)8Y8<|xoo=74P#Og^SRi3rz8qI-wVZ8J5LO|i zw=0R>m9JMEVdcka#4oI=FID=Im3d`pv2E}qLlBqym4|T@=TO#!HyJ8yVIKfwEz^$L z4(2zMQ(0@SePeyi<+8^18QrbNXtd9WJ)y?P)tj=0yhm9>ZIGHSui0y^Kh#1Pa=l`@ zttpa+$-CG`=Z5kgFuL{;X*ol!*dqbWuk;C>36FZELO;qD8l@cSHOW>}Dz$feJ)+%H z{GD+a>x8WrbLDN9&*^^C?&0Az2XM~> zQD&7yD)kuVatFhSkTjrdpxk8$dj~9_XrZ?Xs^O_5Cj`+XX`x+eam?mA%WW|T{z~~4s*OOpccrTZ{XjvBEssw`F`a#opuGi*i^DFgmu3 zCONbpZHleuO|;J$wm62qTw7~tLYlqa^W+NiIOFMFB1dWYI-c*1B`E_~Dub{Ip^|}I zvnW1OpcCn*c}g3V+|`_d^zvQJWS8N|%!Z>%DZS<1xHdYkY>fwQ1)MZFs@5{2qs%8) z*k`+F25_3|XwF(n)X|@kU+xWMKbB-6&#mhH_-1)R^IMv{hOeM6Y4pXOXQOwA$t&Y) z&T-^WzK|m1HeTBrBh$!v)PXn?DSK@0<8{f4Ns8I~YBuG_w&XiL|Dk?lR@yri8#PDP zkyr5LZj1rkxozyhd?mkxuu~hQvZVwQ*JJW4S2}VaOK=v`hOPnQq)H{3QqO*Ksi(FQ z#W%$jTXzT>H1PFD4IGl}Nw#2}b9VQtWmzfA`}cDe=FR=aYHKOBl;xvan2fE&eBlpU zD&*4ydYhKf=#$pl@N`tIw=se_h+eW!ZmqYuZM}^VYIEApr-ic8RnS*YRb(Rq8Fwr@XI0NSVcaC9_E8R&YXtrM+?kotwi6T=M`%=bW|-2%-)v#=TnLt9_w1 zZF00(?9c)K1P>^hxx!0e?bizQhH7{FK9X_ZNH#lQ(X4Pk67)Xn8 zj+UP+&CzQ_m?qEC-ED8|SIS08oU*;!GKpv(BeFMt%ANeMj;td@6?T!9hNq4?22q-) z2cTB?)jl3D9cMiWUZBtam~4#Z&tO*3#5Subo=9EYTnXJL)Upb#AF{TAh6 zcxY?hd?+8-j?MTy4nl&33a{Ykc;SVI$~0+Um;9z3XtoCK0_VA z*y)uRr&__y)bF%T13z6!ZNafRuD3WaNrwV<{CXDwwfjt9cF?>J+`Oo?)kpJ9EvRp9 zBUf`QD8pU^Zq!GjiY{8l1Z*ChZD_s=D=st3c+L#9E zgg5(+nE6B6SA+BRc+rnxJ+}aOdq^B~lfk*dbda(c2?NkqpX@{qePkt!% z_Ms95_^c&#e1N@m+)T$#fHZjXXY|M#w^<#>co`Sq8CMO;SEbL6spnEC52HRhWP2Cs z!SlcX<8d-781_$2nQl9vBKnt3z{#sHYg4dYLH+zF>lsj6h=Bcor~T?eL;ZXw^}r?t z^31~%QS>-Pa89b|Xs~>1_F1>`XL4OAOO0vTSJq(tR1Hr6QUng-T0cR+2bA^iopcHx z--CxgDWcD|VtC{XO7V$77nJV109`*79{00GQc$KBWzMxy_TbM#E|61ElfM5l+yBM` z4E*uWEPNN>yO1Gno=^8aDv0C%M`+E^Q)$N(OL`r3ET^h4KJalPzxP9|V$@TL z3b&4v*rW>ik@J2PV|7LbRXl@VWvMwn_W3*N*(BOwub#idlMP#OaKYh&;F#bZK4bv% zgWk>Yp4kJRD4tK(I`;W=>KrD9z&^jilN~&zGUoYoJU&2)gWq~$huKDH2hs8atxqV_ z=zINNc@7?B<00cxga6ALwfwB?icluDPVJsMuPawmZF;_uzwEL+zK0Y1IKdbN4e8MFUu!iMEgteGJ4Egjb zhW1k#d6dF_CKb>1MlnemagTn)7fY?6%VtI|5QfK<&^b&fr*~a2-$AW%Xm`Fwqrf9@gY8KYVVHH#DK^j_X&=BS>h1N z8mTuBMGv-E@7?k%<42AUI>@_buw+dXYRnd+FjV(L@vFSfs$ z_2B`48?yIjAI&~D?HcqejsLo~Uw7FNtn~@}7q5QtpV1$(;!dQ0%!<1Z-(|(!c;4c3 zR-Dg-{Jj;&KT#ReQJa3f!#Kl=8{#)UV#V>3AmdY394BRromL#DC5$JmxLfLtr>(dL z=|8aIK4~%zTXDa{G5>U1A*prnxEo(#Im?l?;#CrGtiibn!^OXU-0j$C#nE5q3@h%C zi1TzSj{Z6qTXFQ)d6gAMf1T^CxCiNc$ zeO%wh|7i{5|EBu!@5+Pt$Eh^_(fJm@rT7=BTahw^|7u>3>$C80&4b{=exwW_HjG~| zd zem~0d?hDcC5WZt`#-?13I_IDk;*rMxfUQJ5X~c6Xy$k;#mcwt9@z2~U-^Q2NY?2K~ zpN;os^_~U1>^=}f;x~kvR|1oDxO>e1lbtg>*uQCTAbssE>7{FK9UQu0efq4mgIoHC z1_p;`r9Yd_b#~5AFWq#@+QFGUvyinmJ-lYs>P;Kd!-E?Khi@Jnn3Z0=Y14*NXV2ce zdGoA|EVyy-=E0#^E7z}ExMtOwP5oZY~*!=>v| + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/nui/radar.js b/nui/radar.js new file mode 100644 index 0000000..17bf4c4 --- /dev/null +++ b/nui/radar.js @@ -0,0 +1,197 @@ +/*------------------------------------------------------------------------- + + Wraith Radar System - v1.0.3 + Created by WolfKnight + +-------------------------------------------------------------------------*/ + +var resourceName = ""; +var radarEnabled = false; +var targets = []; + +$( function() { + radarInit(); + + var radarContainer = $( "#policeradar" ); + + var fwdArrowFront = radarContainer.find( ".fwdarrowfront" ); + var fwdArrowBack = radarContainer.find( ".fwdarrowback" ); + var bwdArrowFront = radarContainer.find( ".bwdarrowfront" ); + var bwdArrowBack = radarContainer.find( ".bwdarrowback" ); + + var fwdSame = radarContainer.find( ".fwdsame" ); + var fwdOpp = radarContainer.find( ".fwdopp" ); + var fwdXmit = radarContainer.find( ".fwdxmit" ); + + var bwdSame = radarContainer.find( ".bwdsame" ); + var bwdOpp = radarContainer.find( ".bwdopp" ); + var bwdXmit = radarContainer.find( ".bwdxmit" ); + + var radarRCContainer = $( "#policeradarrc" ); + + window.addEventListener( 'message', function( event ) { + var item = event.data; + + // change this to a switch/case instead of multiple if statements + if ( item.resourcename ) { + resourceName = item.resourcename; + } + + if ( item.toggleradar ) { + radarEnabled = !radarEnabled; + radarContainer.fadeToggle(); + } + + if ( item.hideradar ) { + radarContainer.fadeOut(); + } else if ( item.hideradar == false ) { + radarContainer.fadeIn(); + } + + if ( item.patrolspeed ) { + updateSpeed( "patrolspeed", item.patrolspeed ); + } + + if ( item.fwdspeed ) { + updateSpeed( "fwdspeed", item.fwdspeed ); + } + + if ( item.fwdfast ) { + updateSpeed( "fwdfast", item.fwdfast ); + } + + if ( item.lockfwdfast == true || item.lockfwdfast == false ) { + lockSpeed( "fwdfast", item.lockfwdfast ) + } + + if ( item.bwdspeed ) { + updateSpeed( "bwdspeed", item.bwdspeed ); + } + + if ( item.bwdfast ) { + updateSpeed( "bwdfast", item.bwdfast ); + } + + if ( item.lockbwdfast == true || item.lockbwdfast == false ) { + lockSpeed( "bwdfast", item.lockbwdfast ) + } + + if ( item.fwddir || item.fwddir == false || item.fwddir == null ) { + updateArrowDir( fwdArrowFront, fwdArrowBack, item.fwddir ) + } + + if ( item.bwddir || item.bwddir == false || item.bwddir == null ) { + updateArrowDir( bwdArrowFront, bwdArrowBack, item.bwddir ) + } + + if ( item.fwdxmit ) { + fwdXmit.addClass( "active" ); + } else if ( item.fwdxmit == false ) { + fwdXmit.removeClass( "active" ); + } + + if ( item.bwdxmit ) { + bwdXmit.addClass( "active" ); + } else if ( item.bwdxmit == false ) { + bwdXmit.removeClass( "active" ); + } + + if ( item.fwdmode ) { + modeSwitch( fwdSame, fwdOpp, item.fwdmode ); + } + + if ( item.bwdmode ) { + modeSwitch( bwdSame, bwdOpp, item.bwdmode ); + } + + if ( item.toggleradarrc ) { + radarRCContainer.toggle(); + } + } ); +} ) + +function radarInit() { + $( '.container' ).each( function( i, obj ) { + $( this ).find( '[data-target]' ).each( function( subi, subobj ) { + targets[ $( this ).attr( "data-target" ) ] = $( this ) + } ) + } ); + + $( "#policeradarrc" ).find( "button" ).each( function( i, obj ) { + if ( $( this ).attr( "data-action" ) ) { + $( this ).click( function() { + var data = $( this ).data( "action" ); + + sendData( "RadarRC", data ); + } ) + } + } ); + + /* With the new scaling option in the css file, it can cause the radar to go off the screen if + the transform-origin property is not set according to where the radar is positioned. The below + code is a simple workaround that sets the origin depending on what the user has configured for + the position of the radar from the two available options in the css file. */ + var left = $( "#policeradar" ).css( "left" ); + + if ( left == "auto" ) { + $( "#policeradar" ).css( "transform-origin", "bottom right" ); + } else { + $( "#policeradar" ).css( "transform-origin", "bottom center" ); + } +} + +// function dbgFunc() +// { +// var left = $( "#policeradar" ).css( "left" ); +// sendData( "debug", "Left: " + left ); +// } + +function updateSpeed( attr, data ) { + targets[ attr ].find( ".speednumber" ).each( function( i, obj ) { + $( obj ).html( data[i] ); + } ); +} + +function lockSpeed( attr, state ) { + targets[ attr ].find( ".speednumber" ).each( function( i, obj ) { + if ( state == true ) { + $( obj ).addClass( "locked" ); + } else { + $( obj ).removeClass( "locked" ); + } + } ); +} + +function modeSwitch( sameEle, oppEle, state ) { + if ( state == "same" ) { + sameEle.addClass( "active" ); + oppEle.removeClass( "active" ); + } else if ( state == "opp" ) { + oppEle.addClass( "active" ); + sameEle.removeClass( "active" ); + } else if ( state == "none" ) { + oppEle.removeClass( "active" ); + sameEle.removeClass( "active" ); + } +} + +function updateArrowDir( fwdEle, bwdEle, state ) { + if ( state == true ) { + fwdEle.addClass( "active" ); + bwdEle.removeClass( "active" ); + } else if ( state == false ) { + bwdEle.addClass( "active" ); + fwdEle.removeClass( "active" ); + } else if ( state == null ) { + fwdEle.removeClass( "active" ); + bwdEle.removeClass( "active" ); + } +} + +function sendData( name, data ) { + $.post( "http://" + resourceName + "/" + name, JSON.stringify( data ), function( datab ) { + if ( datab != "ok" ) { + console.log( datab ); + } + } ); +} \ No newline at end of file diff --git a/old_cl_radar.lua b/old_cl_radar.lua new file mode 100644 index 0000000..32edc85 --- /dev/null +++ b/old_cl_radar.lua @@ -0,0 +1,619 @@ +--[[------------------------------------------------------------------------ + + Wraith Radar System - v1.0.3 + Created by WolfKnight + +------------------------------------------------------------------------]]-- + +--[[------------------------------------------------------------------------ + Resource Rename Fix +------------------------------------------------------------------------]]-- +Citizen.CreateThread( function() + Citizen.Wait( 1000 ) + local resourceName = GetCurrentResourceName() + SendNUIMessage( { resourcename = resourceName } ) +end ) + +--[[----------------------------------------------------------------------- + Test +-----------------------------------------------------------------------]]-- +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 EnumerateObjects() + return EnumerateEntities(FindFirstObject, FindNextObject, EndFindObject) +end + +function EnumeratePeds() + return EnumerateEntities(FindFirstPed, FindNextPed, EndFindPed) +end + +function EnumerateVehicles() + return EnumerateEntities(FindFirstVehicle, FindNextVehicle, EndFindVehicle) +end + +function EnumeratePickups() + return EnumerateEntities(FindFirstPickup, FindNextPickup, EndFindPickup) +end + +--[[------------------------------------------------------------------------ + Utils +------------------------------------------------------------------------]]-- +function round( num ) + return tonumber( string.format( "%.0f", num ) ) +end + +function oppang( ang ) + return ( ang + 180 ) % 360 +end + +function FormatSpeed( speed ) + return string.format( "%03d", speed ) +end + +function GetVehicleInDirectionSphere( entFrom, coordFrom, coordTo ) + local rayHandle = StartShapeTestCapsule( coordFrom.x, coordFrom.y, coordFrom.z, coordTo.x, coordTo.y, coordTo.z, 2.0, 10, entFrom, 7 ) + local _, _, _, _, vehicle = GetShapeTestResult( rayHandle ) + return vehicle +end + +function IsEntityInMyHeading( myAng, tarAng, range ) + local rangeStartFront = myAng - ( range / 2 ) + local rangeEndFront = myAng + ( range / 2 ) + + local opp = oppang( myAng ) + + local rangeStartBack = opp - ( range / 2 ) + local rangeEndBack = opp + ( range / 2 ) + + if ( ( tarAng > rangeStartFront ) and ( tarAng < rangeEndFront ) ) then + return true + elseif ( ( tarAng > rangeStartBack ) and ( tarAng < rangeEndBack ) ) then + return false + else + return nil + end +end + + +--[[------------------------------------------------------------------------ + Police Vehicle Radar +------------------------------------------------------------------------]]-- +local radarEnabled = false +local hidden = false +local radarInfo = +{ + patrolSpeed = "000", + + speedType = "mph", + + fwdPrevVeh = 0, + fwdXmit = true, + fwdMode = "same", + fwdSpeed = "000", + fwdFast = "000", + fwdFastLocked = false, + fwdDir = nil, + fwdFastSpeed = 0, + + bwdPrevVeh = 0, + bwdXmit = false, + bwdMode = "none", + bwdSpeed = "OFF", + bwdFast = "OFF", + bwdFastLocked = false, + bwdDir = nil, + bwdFastSpeed = 0, + + fastResetLimit = 150, + fastLimit = 60, + + angles = { [ "same" ] = { x = 0.0, y = 70.0, z = 0.0 }, [ "opp" ] = { x = -10.0, y = 50.0, z = 0.0 } }, + + lockBeep = true +} + +RegisterNetEvent( 'wk:toggleRadar' ) +AddEventHandler( 'wk:toggleRadar', function() + local ped = GetPlayerPed( -1 ) + + if ( IsPedSittingInAnyVehicle( ped ) ) then + local vehicle = GetVehiclePedIsIn( ped, false ) + + if ( GetVehicleClass( vehicle ) == 18 ) then + radarEnabled = not radarEnabled + + if ( radarEnabled ) then + Notify( "~b~Radar enabled." ) + else + Notify( "~b~Radar disabled." ) + end + + ResetFrontAntenna() + ResetRearAntenna() + + SendNUIMessage({ + toggleradar = true, + fwdxmit = radarInfo.fwdXmit, + fwdmode = radarInfo.fwdMode, + bwdxmit = radarInfo.bwdXmit, + bwdmode = radarInfo.bwdMode + }) + else + Notify( "~r~You must be in a police vehicle." ) + end + else + Notify( "~r~You must be in a vehicle." ) + end +end ) + +RegisterNetEvent( 'wk:changeRadarLimit' ) +AddEventHandler( 'wk:changeRadarLimit', function( speed ) + radarInfo.fastLimit = speed +end ) + +function Radar_SetLimit() + Citizen.CreateThread( function() + DisplayOnscreenKeyboard( false, "", "", "", "", "", "", 4 ) + + while true do + if ( UpdateOnscreenKeyboard() == 1 ) then + local speedStr = GetOnscreenKeyboardResult() + + if ( string.len( speedStr ) > 0 ) then + local speed = tonumber( speedStr ) + + if ( speed < 999 and speed > 1 ) then + TriggerEvent( 'wk:changeRadarLimit', speed ) + end + + break + else + DisplayOnscreenKeyboard( false, "", "", "", "", "", "", 4 ) + end + elseif ( UpdateOnscreenKeyboard() == 2 ) then + break + end + + Citizen.Wait( 0 ) + end + end ) +end + +function ResetFrontAntenna() + if ( radarInfo.fwdXmit ) then + radarInfo.fwdSpeed = "000" + radarInfo.fwdFast = "000" + else + radarInfo.fwdSpeed = "OFF" + radarInfo.fwdFast = " " + end + + radarInfo.fwdDir = nil + radarInfo.fwdFastSpeed = 0 + radarInfo.fwdFastLocked = false +end + +function ResetRearAntenna() + if ( radarInfo.bwdXmit ) then + radarInfo.bwdSpeed = "000" + radarInfo.bwdFast = "000" + else + radarInfo.bwdSpeed = "OFF" + radarInfo.bwdFast = " " + end + + radarInfo.bwdDir = nil + radarInfo.bwdFastSpeed = 0 + radarInfo.bwdFastLocked = false +end + +function ResetFrontFast() + if ( radarInfo.fwdXmit ) then + radarInfo.fwdFast = "000" + radarInfo.fwdFastSpeed = 0 + radarInfo.fwdFastLocked = false + + SendNUIMessage( { lockfwdfast = false } ) + end +end + +function ResetRearFast() + if ( radarInfo.bwdXmit ) then + radarInfo.bwdFast = "000" + radarInfo.bwdFastSpeed = 0 + radarInfo.bwdFastLocked = false + + SendNUIMessage( { lockbwdfast = false } ) + end +end + +function CloseRadarRC() + SendNUIMessage({ + toggleradarrc = true + }) + + TriggerEvent( 'wk:toggleMenuControlLock', false ) + + SetNuiFocus( false ) +end + +function ToggleSpeedType() + if ( radarInfo.speedType == "mph" ) then + radarInfo.speedType = "kmh" + Notify( "~b~Speed type set to Km/h." ) + else + radarInfo.speedType = "mph" + Notify( "~b~Speed type set to MPH." ) + end +end + +function ToggleLockBeep() + if ( radarInfo.lockBeep ) then + radarInfo.lockBeep = false + Notify( "~b~Radar fast lock beep disabled." ) + else + radarInfo.lockBeep = true + Notify( "~b~Radar fast lock beep enabled." ) + end +end + +function GetVehSpeed( veh ) + if ( radarInfo.speedType == "mph" ) then + return GetEntitySpeed( veh ) * 2.236936 + else + return GetEntitySpeed( veh ) * 3.6 + end +end + +function ManageVehicleRadar() + if ( radarEnabled ) then + local ped = GetPlayerPed( -1 ) + + if ( IsPedSittingInAnyVehicle( ped ) ) then + local vehicle = GetVehiclePedIsIn( ped, false ) + + if ( GetPedInVehicleSeat( vehicle, -1 ) == ped and GetVehicleClass( vehicle ) == 18 ) then + -- Patrol speed + local vehicleSpeed = round( GetVehSpeed( vehicle ), 0 ) + + radarInfo.patrolSpeed = FormatSpeed( vehicleSpeed ) + + -- Rest of the radar options + local vehiclePos = GetEntityCoords( vehicle, true ) + local h = round( GetEntityHeading( vehicle ), 0 ) + + -- Front Antenna + if ( radarInfo.fwdXmit ) then + local newPos = GetOffsetFromEntityInWorldCoords( vehicle, 0.0, 5.0, 0.0 ) + local forwardPosition = GetOffsetFromEntityInWorldCoords( vehicle, radarInfo.angles[ radarInfo.fwdMode ].x, radarInfo.angles[ radarInfo.fwdMode ].y, radarInfo.angles[ radarInfo.fwdMode ].z ) + local fwdPos = { x = forwardPosition.x, y = forwardPosition.y, z = forwardPosition.z } + local _, fwdZ = GetGroundZFor_3dCoord( fwdPos.x, fwdPos.y, fwdPos.z + 500.0 ) + + if ( fwdPos.z < fwdZ and not ( fwdZ > vehiclePos.z + 1.0 ) ) then + fwdPos.z = fwdZ + 0.5 + end + + -- Draw line for debug + --DrawLine( newPos.x, newPos.y, newPos.z, fwdPos.x, fwdPos.y, fwdPos.z, 255, 255, 255, 255 ) + --DrawDText( 0.015, 0.25, 0.65, false, "Vehicle X: " .. vehiclePos.x .. "\nVehicle Y: " .. vehiclePos.y .. "\nVehicle Z: " .. vehiclePos.z, 255, 255, 255, 255 ) + --DrawDText( 0.2, 0.25, 0.65, false, "New X: " .. newPos.x .. "\nNew Y: " .. newPos.y .. "\nNew Z: " .. newPos.z, 255, 255, 255, 255 ) + --DrawDText( 0.015, 0.42, 0.65, false, "End X: " .. fwdPos.x .. "\nEnd Y: " .. fwdPos.y .. "\nEnd Z: " .. fwdPos.z, 255, 255, 255, 255 ) + + local packedFwdPos = vector3( fwdPos.x, fwdPos.y, fwdPos.z ) + local newVehiclePos = vector3( newPos.x, newPos.y, newPos.z ) + + local fwdVeh = GetVehicleInDirectionSphere( vehicle, newVehiclePos, packedFwdPos ) + --DrawDText( 0.015, 0.65, 0.65, false, "Ray entity: " .. tostring( fwdVeh ) .. "\nType: " .. GetEntityType( fwdVeh ), 255, 255, 255, 255 ) + + if ( DoesEntityExist( fwdVeh ) and IsEntityAVehicle( fwdVeh ) ) then + local fwdVehSpeed = round( GetVehSpeed( fwdVeh ), 0 ) + + local fwdVehHeading = round( GetEntityHeading( fwdVeh ), 0 ) + local dir = IsEntityInMyHeading( h, fwdVehHeading, 100 ) + + -- Draw line for debug to car + local pos = GetEntityCoords( fwdVeh, true ) + -- DrawLine( vehiclePos.x, vehiclePos.y, vehiclePos.z, pos.x, pos.y, pos.z, 255, 255, 255, 255 ) + DrawMarker(2, pos.x, pos.y, pos.z + 6, 0.0, 0.0, 0.0, 0.0, 180.0, 0.0, 2.0, 2.0, 2.0, 255, 128, 0, 50, false, true, 2, nil, nil, false) + + radarInfo.fwdSpeed = FormatSpeed( fwdVehSpeed ) + radarInfo.fwdDir = dir + + if ( fwdVehSpeed > radarInfo.fastLimit and not radarInfo.fwdFastLocked ) then + if ( radarInfo.lockBeep ) then + PlaySoundFrontend( -1, "Beep_Red", "DLC_HEIST_HACKING_SNAKE_SOUNDS", 1 ) + end + + radarInfo.fwdFastSpeed = fwdVehSpeed + radarInfo.fwdFastLocked = true + + SendNUIMessage( { lockfwdfast = true } ) + end + + radarInfo.fwdFast = FormatSpeed( radarInfo.fwdFastSpeed ) + + radarInfo.fwdPrevVeh = fwdVeh + + --[[ Test + local plate = GetVehicleNumberPlateText( fwdVeh ) + local colourPrimary, colourSecondary = GetVehicleColours( fwdVeh ) + local name = GetLabelText( GetDisplayNameFromVehicleModel( GetEntityModel( fwdVeh ) ) ) + local class = GetLabelText( "VEH_CLASS_" .. tostring( GetVehicleClass( fwdVeh ) ) ) + + Notify( "Name: " .. name .. "\nClass: " .. class .. "\nPlate: " .. plate .. "\nColour: " .. CAR_COLOURS[ tostring( colourPrimary ) ] .. " / " .. CAR_COLOURS[ tostring( colourSecondary ) ] ) + ]] + end + end + + -- Rear Antenna + if ( radarInfo.bwdXmit ) then + local backwardPosition = GetOffsetFromEntityInWorldCoords( vehicle, radarInfo.angles[ radarInfo.bwdMode ].x, -radarInfo.angles[ radarInfo.bwdMode ].y, radarInfo.angles[ radarInfo.bwdMode ].z ) + local bwdPos = { x = backwardPosition.x, y = backwardPosition.y, z = backwardPosition.z } + local _, bwdZ = GetGroundZFor_3dCoord( bwdPos.x, bwdPos.y, bwdPos.z + 500.0 ) + + if ( bwdPos.z < bwdZ and not ( bwdZ > vehiclePos.z + 1.0 ) ) then + bwdPos.z = bwdZ + 0.5 + end + + local packedBwdPos = vector3( bwdPos.x, bwdPos.y, bwdPos.z ) + local bwdVeh = GetVehicleInDirectionSphere( vehicle, vehiclePos, packedBwdPos ) + + if ( DoesEntityExist( bwdVeh ) and IsEntityAVehicle( bwdVeh ) ) then + local bwdVehSpeed = round( GetVehSpeed( bwdVeh ), 0 ) + + local bwdVehHeading = round( GetEntityHeading( bwdVeh ), 0 ) + local dir = IsEntityInMyHeading( h, bwdVehHeading, 100 ) + + radarInfo.bwdSpeed = FormatSpeed( bwdVehSpeed ) + radarInfo.bwdDir = dir + + if ( bwdVehSpeed > radarInfo.fastLimit and not radarInfo.bwdFastLocked ) then + if ( radarInfo.lockBeep ) then + PlaySoundFrontend( -1, "Beep_Red", "DLC_HEIST_HACKING_SNAKE_SOUNDS", 1 ) + end + + radarInfo.bwdFastSpeed = bwdVehSpeed + radarInfo.bwdFastLocked = true + + SendNUIMessage( { lockbwdfast = true } ) + end + + radarInfo.bwdFast = FormatSpeed( radarInfo.bwdFastSpeed ) + + radarInfo.bwdPrevVeh = bwdVeh + end + end + + SendNUIMessage({ + patrolspeed = radarInfo.patrolSpeed, + fwdspeed = radarInfo.fwdSpeed, + fwdfast = radarInfo.fwdFast, + fwddir = radarInfo.fwdDir, + bwdspeed = radarInfo.bwdSpeed, + bwdfast = radarInfo.bwdFast, + bwddir = radarInfo.bwdDir + }) + end + end + end +end + +RegisterNetEvent( 'wk:radarRC' ) +AddEventHandler( 'wk:radarRC', function() + Citizen.Wait( 10 ) + + TriggerEvent( 'wk:toggleMenuControlLock', true ) + + SendNUIMessage({ + toggleradarrc = true + }) + + SetNuiFocus( true, true ) +end ) + +RegisterNUICallback( "debug", function( data, cb ) + TriggerEvent( "chat:addMessage", { + color = { 255, 255, 255 }, + multiline = true, + args = { "WraithRS DEBUG", data } + }) +end ) + +RegisterNUICallback( "RadarRC", function( data, cb ) + -- Toggle Radar + if ( data == "radar_toggle" ) then + TriggerEvent( 'wk:toggleRadar' ) + + -- Front Antenna + elseif ( data == "radar_frontopp" and radarInfo.fwdXmit ) then + radarInfo.fwdMode = "opp" + SendNUIMessage( { fwdmode = radarInfo.fwdMode } ) + elseif ( data == "radar_frontxmit" ) then + radarInfo.fwdXmit = not radarInfo.fwdXmit + ResetFrontAntenna() + SendNUIMessage( { fwdxmit = radarInfo.fwdXmit } ) + + if ( radarInfo.fwdXmit == false ) then + radarInfo.fwdMode = "none" + else + radarInfo.fwdMode = "same" + end + + SendNUIMessage( { fwdmode = radarInfo.fwdMode } ) + elseif ( data == "radar_frontsame" and radarInfo.fwdXmit ) then + radarInfo.fwdMode = "same" + SendNUIMessage( { fwdmode = radarInfo.fwdMode } ) + + -- Rear Antenna + elseif ( data == "radar_rearopp" and radarInfo.bwdXmit ) then + radarInfo.bwdMode = "opp" + SendNUIMessage( { bwdmode = radarInfo.bwdMode } ) + elseif ( data == "radar_rearxmit" ) then + radarInfo.bwdXmit = not radarInfo.bwdXmit + ResetRearAntenna() + SendNUIMessage( { bwdxmit = radarInfo.bwdXmit } ) + + if ( radarInfo.bwdXmit == false ) then + radarInfo.bwdMode = "none" + else + radarInfo.bwdMode = "same" + end + + SendNUIMessage( { bwdmode = radarInfo.bwdMode } ) + elseif ( data == "radar_rearsame" and radarInfo.bwdXmit ) then + radarInfo.bwdMode = "same" + SendNUIMessage( { bwdmode = radarInfo.bwdMode } ) + + -- Set Fast Limit + elseif ( data == "radar_setlimit" ) then + CloseRadarRC() + Radar_SetLimit() + + -- Speed Type + elseif ( data == "radar_speedtype" ) then + ToggleSpeedType() + + elseif ( data == "radar_lockbeep" ) then + ToggleLockBeep() + + -- Close + elseif ( data == "close" ) then + CloseRadarRC() + end + + if ( cb ) then cb( 'ok' ) end +end ) + +Citizen.CreateThread( function() + SetNuiFocus( false ) + + while true do + ManageVehicleRadar() + + -- Only run 10 times a second, more realistic, also prevents spam + Citizen.Wait( 100 ) + -- Citizen.Wait( 0 ) + end +end ) + +Citizen.CreateThread( function() + while true do + local ped = GetPlayerPed( -1 ) + + -- These control pressed natives must be the disabled check ones. + + -- LCtrl is pressed and M has just been pressed + if ( IsDisabledControlPressed( 1, 36 ) and IsDisabledControlJustPressed( 1, 244 ) and IsPedSittingInAnyVehicle( ped ) ) then + TriggerEvent( 'wk:radarRC' ) + end + + -- LCtrl is not being pressed and M has just been pressed + if ( not IsDisabledControlPressed( 1, 36 ) and IsDisabledControlJustPressed( 1, 244 ) ) then + ResetFrontFast() + ResetRearFast() + end + + local inVeh = IsPedSittingInAnyVehicle( ped ) + local veh = nil + + if ( inVeh ) then + veh = GetVehiclePedIsIn( ped, false ) + end + + if ( ( (not inVeh or (inVeh and GetVehicleClass( veh ) ~= 18)) and radarEnabled and not hidden) or IsPauseMenuActive() and radarEnabled ) then + hidden = true + SendNUIMessage( { hideradar = true } ) + elseif ( inVeh and GetVehicleClass( veh ) == 18 and radarEnabled and hidden ) then + hidden = false + SendNUIMessage( { hideradar = false } ) + end + + Citizen.Wait( 0 ) + end +end ) + + +--[[------------------------------------------------------------------------ + Menu Control Lock - Prevents certain actions + Thanks to the authors of the ES Banking script. +------------------------------------------------------------------------]]-- +local locked = false + +RegisterNetEvent( 'wk:toggleMenuControlLock' ) +AddEventHandler( 'wk:toggleMenuControlLock', function( lock ) + locked = lock +end ) + +Citizen.CreateThread( function() + while true do + if ( locked ) then + local ped = GetPlayerPed( -1 ) + + DisableControlAction( 0, 1, true ) -- LookLeftRight + DisableControlAction( 0, 2, true ) -- LookUpDown + DisableControlAction( 0, 24, true ) -- Attack + DisablePlayerFiring( ped, true ) -- Disable weapon firing + DisableControlAction( 0, 142, true ) -- MeleeAttackAlternate + DisableControlAction( 0, 106, true ) -- VehicleMouseControlOverride + + SetPauseMenuActive( false ) + end + + Citizen.Wait( 0 ) + end +end ) + + +--[[------------------------------------------------------------------------ + Notify +------------------------------------------------------------------------]]-- +function Notify( text ) + SetNotificationTextEntry( "STRING" ) + AddTextComponentSubstringPlayerName( text ) + DrawNotification( false, true ) +end + +function DrawDText( x, y, scale, centre, text, r, g, b, a ) + SetTextFont(4) + SetTextProportional(0) + SetTextScale(scale, scale) + SetTextColour(r, g, b, a) + SetTextDropShadow(0, 0, 0, 0,255) + SetTextEdge(2, 0, 0, 0, 255) + SetTextCentre( centre ) + SetTextDropShadow() + SetTextOutline() + SetTextEntry("STRING") + AddTextComponentString(text) + DrawText( x, y ) +end \ No newline at end of file diff --git a/radar operation.txt b/radar operation.txt new file mode 100644 index 0000000..5710065 --- /dev/null +++ b/radar operation.txt @@ -0,0 +1,17 @@ +Process: + - Run the custom ray trace function to check bounding boxes + - Collect all of the hit vehicles in a table (RADAR.vars.capturedVehicles) + - Priority goes to the vehicle which fits the set direction mode (fwd/bwd) and the set radar mode (fastest/biggest) + - Loop through the vehicle table and grab the one that fits the above mentioned modes + - Display the chosen vehicle's speed in the radar + +Checks: + - Vehicle must be moving faster than 1.0 (units) + - Vehicle is not the player's vehicle + - Vehicle is a car, motorcycle, or a quadbike + +Possible TODO: + - Create a system that stores bounding box data of models in a table so that the system doesn't have to create new data for models that have been previously caught by the radar + + Development notes: + - Set the radiusSqr to be based on vehicle class \ No newline at end of file