2022-09-14 00:05:08 +10:00
local creatorActive = false
local controlsActive = false
local zoneType , step , xCoord , yCoord , zCoord , heading , height , width , length
local steps = { { 0.05 , 0.1 , 0.25 , 0.5 , 1 , 2.5 , 5 , 10 , 25 , 50 , 100 } , { 0.25 , 0.5 , 1 , 2.5 , 5 , 15 , 30 , 45 , 60 , 90 , 180 } }
local points = { }
2022-09-15 00:00:53 +10:00
local format = ' array '
2022-09-15 21:19:10 +10:00
local displayModes = { ' basic ' , ' walls ' , ' axes ' , ' both ' }
local displayMode = 1
2022-10-02 11:43:10 +10:00
local minCheck = steps [ 1 ] [ 1 ] / 2
2023-01-07 00:27:12 -05:00
local lastZone = { }
local alignMovementWithCamera = false
local useLastZoneFalsyInputs = { [ ' 0 ' ] = true , [ ' ' ] = true , [ ' false ' ] = true , [ ' nil ' ] = true }
2022-09-14 00:05:08 +10:00
local function firstToUpper ( str )
return ( str : gsub ( " ^%l " , string.upper ) )
end
local function updateText ( )
local text = {
( ' ------ Creating %s Zone ------ \n ' ) : format ( firstToUpper ( zoneType ) ) ,
( ' Step size [Scroll]: %sm/%s° \n ' ) : format ( steps [ 1 ] [ step ] , steps [ 2 ] [ step ] ) ,
( ' X coord [A/D]: %s \n ' ) : format ( xCoord ) ,
( ' Y coord [W/S]: %s \n ' ) : format ( yCoord ) ,
( ' Z coord [R/F]: %s \n ' ) : format ( zCoord ) ,
}
if zoneType == ' poly ' then
text [ # text + 1 ] = ( ' Height [Shift + Scroll]: %s \n ' ) : format ( height )
2022-09-15 21:19:10 +10:00
text [ # text + 1 ] = ( ' Cycle display mode [G]: %s \n ' ) : format ( firstToUpper ( displayModes [ displayMode ] ) )
2022-09-14 00:05:08 +10:00
text [ # text + 1 ] = ' Create new point - [Space] \n '
elseif zoneType == ' box ' then
text [ # text + 1 ] = ( ' Heading [Q/E]: %s° \n ' ) : format ( heading )
text [ # text + 1 ] = ( ' Height [Shift + Scroll]: %s \n ' ) : format ( height )
text [ # text + 1 ] = ( ' Width [Ctrl + Scroll]: %s \n ' ) : format ( width )
text [ # text + 1 ] = ( ' Length [Alt + Scroll]: %s \n ' ) : format ( length )
2022-09-15 21:19:10 +10:00
text [ # text + 1 ] = ( ' Cycle display mode [G]: %s \n ' ) : format ( firstToUpper ( displayModes [ displayMode ] ) )
2022-09-14 00:05:08 +10:00
text [ # text + 1 ] = ' Recenter - [Space] \n '
elseif zoneType == ' sphere ' then
text [ # text + 1 ] = ( ' Size [Shift + Scroll]: %s \n ' ) : format ( height )
text [ # text + 1 ] = ' Recenter - [Space] \n '
end
text [ # text + 1 ] = ' Toggle controls - [X] \n '
2023-01-07 00:27:12 -05:00
text [ # text + 1 ] = ' Toggle movement mode - [M] \n '
2022-09-14 00:05:08 +10:00
text [ # text + 1 ] = ' Save - [Enter] \n '
text [ # text + 1 ] = ' Cancel - [Esc] '
lib.showTextUI ( table.concat ( text ) )
end
local function round ( number )
return number >= 0 and math.floor ( number + 0.5 ) or math.ceil ( number - 0.5 )
end
local function closeCreator ( cancel )
if not cancel then
if zoneType == ' poly ' then
points [ # points + 1 ] = vec ( xCoord , yCoord )
end
2022-09-15 00:00:53 +10:00
local input = lib.inputDialog ( ( ' Name your %s Zone ' ) : format ( firstToUpper ( zoneType ) ) , {
{ type = ' input ' , label = ' Name ' , placeholder = ' none ' } ,
{ type = ' select ' , label = ' Format ' , default = format , options = {
{ value = ' function ' , label = ' Function ' } ,
{ value = ' array ' , label = ' Array ' } ,
2022-09-15 00:01:29 +10:00
{ value = ' target ' , label = ' Target ' } ,
2022-09-15 00:00:53 +10:00
} }
} ) or { }
format = input [ 2 ]
2022-09-14 00:05:08 +10:00
TriggerServerEvent ( ' ox_lib:saveZone ' , {
zoneType = zoneType ,
2022-09-15 00:00:53 +10:00
name = input [ 1 ] or ' none ' ,
format = format ,
2022-09-14 00:05:08 +10:00
xCoord = xCoord ,
yCoord = yCoord ,
zCoord = zCoord ,
heading = heading ,
height = height ,
width = width ,
length = length ,
points = points
} )
2023-01-07 00:27:12 -05:00
lastZone [ zoneType ] = {
zoneType = zoneType ,
heading = heading ,
height = height ,
width = width ,
length = length ,
}
2022-09-14 00:05:08 +10:00
end
creatorActive = false
controlsActive = false
2022-09-14 00:56:56 +10:00
lib.hideTextUI ( )
2022-09-14 00:05:08 +10:00
zoneType = nil
end
2022-09-15 21:19:10 +10:00
local function drawRectangle ( rec )
DrawPoly ( rec [ 1 ] . x , rec [ 1 ] . y , rec [ 1 ] . z , rec [ 2 ] . x , rec [ 2 ] . y , rec [ 2 ] . z , rec [ 3 ] . x , rec [ 3 ] . y , rec [ 3 ] . z , 255 , 42 , 24 , 100 )
DrawPoly ( rec [ 2 ] . x , rec [ 2 ] . y , rec [ 2 ] . z , rec [ 1 ] . x , rec [ 1 ] . y , rec [ 1 ] . z , rec [ 3 ] . x , rec [ 3 ] . y , rec [ 3 ] . z , 255 , 42 , 24 , 100 )
DrawPoly ( rec [ 1 ] . x , rec [ 1 ] . y , rec [ 1 ] . z , rec [ 4 ] . x , rec [ 4 ] . y , rec [ 4 ] . z , rec [ 3 ] . x , rec [ 3 ] . y , rec [ 3 ] . z , 255 , 42 , 24 , 100 )
DrawPoly ( rec [ 4 ] . x , rec [ 4 ] . y , rec [ 4 ] . z , rec [ 1 ] . x , rec [ 1 ] . y , rec [ 1 ] . z , rec [ 3 ] . x , rec [ 3 ] . y , rec [ 3 ] . z , 255 , 42 , 24 , 100 )
end
2022-09-14 00:05:08 +10:00
local function drawLines ( )
local thickness = vec ( 0 , 0 , height / 2 )
2022-09-15 21:19:10 +10:00
local activeA , activeB = vec ( xCoord , yCoord , zCoord ) + thickness , vec ( xCoord , yCoord , zCoord ) - thickness
if zoneType == ' poly ' then
DrawLine ( activeA.x , activeA.y , activeA.z , activeB.x , activeB.y , activeB.z , 255 , 42 , 24 , 225 )
end
2022-09-14 00:05:08 +10:00
for i = 1 , # points do
points [ i ] = vec ( points [ i ] . x , points [ i ] . y , zCoord )
local a = points [ i ] + thickness
local b = points [ i ] - thickness
local c = ( points [ i + 1 ] and vec ( points [ i + 1 ] . x , points [ i + 1 ] . y , zCoord ) or points [ 1 ] ) + thickness
local d = ( points [ i + 1 ] and vec ( points [ i + 1 ] . x , points [ i + 1 ] . y , zCoord ) or points [ 1 ] ) - thickness
local e = points [ i ]
local f = ( points [ i + 1 ] and vec ( points [ i + 1 ] . x , points [ i + 1 ] . y , zCoord ) or points [ 1 ] )
2022-09-15 21:19:10 +10:00
if i == # points and zoneType == ' poly ' then
DrawLine ( a.x , a.y , a.z , b.x , b.y , b.z , 255 , 42 , 24 , 225 )
DrawLine ( activeA.x , activeA.y , activeA.z , c.x , c.y , c.z , 255 , 42 , 24 , 225 )
DrawLine ( activeB.x , activeB.y , activeB.z , d.x , d.y , d.z , 255 , 42 , 24 , 225 )
DrawLine ( a.x , a.y , a.z , activeA.x , activeA.y , activeA.z , 255 , 42 , 24 , 225 )
DrawLine ( b.x , b.y , b.z , activeB.x , activeB.y , activeB.z , 255 , 42 , 24 , 225 )
DrawLine ( xCoord , yCoord , zCoord , f.x , f.y , f.z , 255 , 42 , 24 , 225 )
DrawLine ( e.x , e.y , e.z , xCoord , yCoord , zCoord , 255 , 42 , 24 , 225 )
else
DrawLine ( a.x , a.y , a.z , b.x , b.y , b.z , 255 , 42 , 24 , 225 )
DrawLine ( a.x , a.y , a.z , c.x , c.y , c.z , 255 , 42 , 24 , 225 )
DrawLine ( b.x , b.y , b.z , d.x , d.y , d.z , 255 , 42 , 24 , 225 )
DrawLine ( e.x , e.y , e.z , f.x , f.y , f.z , 255 , 42 , 24 , 225 )
end
if displayMode == 2 or displayMode == 4 then
if i == # points and zoneType == ' poly ' then
drawRectangle ( { a , b , activeB , activeA } )
drawRectangle ( { activeA , activeB , d , c } )
else
drawRectangle ( { a , b , d , c } )
end
end
2022-09-14 00:05:08 +10:00
end
end
2023-01-07 00:27:12 -05:00
local function getRelativePos ( origin , point , theta )
if theta == 0.0 then return point end
local p = point - origin
local pX , pY = p.x , p.y
theta = math.rad ( theta )
local cosTheta = math.cos ( theta )
local sinTheta = math.sin ( theta )
local x = math.floor ( ( ( pX * cosTheta - pY * sinTheta ) + origin.x ) * 100 + 0.0 ) / 100
local y = math.floor ( ( ( pX * sinTheta + pY * cosTheta ) + origin.y ) * 100 + 0.0 ) / 100
return x , y
end
2022-11-25 18:05:55 -08:00
local isFivem = cache.game == ' fivem '
local controls = {
[ ' INPUT_LOOK_LR ' ] = isFivem and 1 or 0xA987235F ,
[ ' INPUT_LOOK_UD ' ] = isFivem and 2 or 0xD2047988 ,
[ ' INPUT_MP_TEXT_CHAT_ALL ' ] = isFivem and 245 or 0x9720FCEE
}
2023-01-07 00:27:12 -05:00
local function startCreator ( arg , useLast )
2022-09-14 15:15:36 +10:00
creatorActive = true
2022-09-15 00:08:59 +10:00
controlsActive = true
2022-09-14 15:15:36 +10:00
zoneType = arg
step = 5
local coords = GetEntityCoords ( cache.ped )
xCoord = round ( coords.x ) + 0.0
yCoord = round ( coords.y ) + 0.0
zCoord = round ( coords.z ) + 0.0
2023-01-07 00:27:12 -05:00
heading = useLast and lastZone [ zoneType ] . heading or 0.0
height = useLast and lastZone [ zoneType ] . height or 4.0
width = useLast and lastZone [ zoneType ] . width or 4.0
length = useLast and lastZone [ zoneType ] . length or 4.0
2022-09-14 15:15:36 +10:00
points = { }
updateText ( )
while creatorActive do
Wait ( 0 )
2022-09-15 20:56:10 +10:00
if IsDisabledControlJustReleased ( 0 , 73 ) then -- x
2022-09-14 15:15:36 +10:00
if creatorActive then
controlsActive = not controlsActive
end
end
2023-01-07 00:27:12 -05:00
if IsDisabledControlJustReleased ( 0 , 301 ) then -- m
if creatorActive then
alignMovementWithCamera = not alignMovementWithCamera
end
end
2022-09-14 15:15:36 +10:00
2022-09-15 21:19:10 +10:00
if displayMode == 3 or displayMode == 4 then
DrawLine ( xCoord , yCoord , zCoord , xCoord + 2 , yCoord , zCoord , 0 , 0 , 255 , 225 )
DrawLine ( xCoord , yCoord , zCoord , xCoord , yCoord + 2 , zCoord , 0 , 0 , 255 , 225 )
DrawLine ( xCoord , yCoord , zCoord , xCoord , yCoord , zCoord + 2 , 0 , 0 , 255 , 225 )
end
2022-09-14 15:15:36 +10:00
2022-09-15 21:19:10 +10:00
if zoneType == ' poly ' then
2022-09-14 15:15:36 +10:00
drawLines ( )
elseif zoneType == ' box ' then
2022-10-01 15:24:51 +10:00
local rad = math.rad ( - heading )
2022-09-15 20:57:10 +10:00
local sinH = math.sin ( rad )
local cosH = math.cos ( rad )
2022-12-11 23:46:55 +11:00
local center = vec2 ( xCoord , yCoord )
2022-09-14 15:15:36 +10:00
---@type vector2[]
points = {
2022-12-11 23:46:55 +11:00
center + vec2 ( ( width * cosH + length * sinH ) , ( length * cosH - width * sinH ) ) / 2 ,
center + vec2 ( - ( width * cosH - length * sinH ) , ( length * cosH + width * sinH ) ) / 2 ,
center + vec2 ( - ( width * cosH + length * sinH ) , - ( length * cosH - width * sinH ) ) / 2 ,
center + vec2 ( ( width * cosH - length * sinH ) , - ( length * cosH + width * sinH ) ) / 2 ,
2022-09-14 15:15:36 +10:00
}
drawLines ( )
elseif zoneType == ' sphere ' then
2022-12-17 06:21:22 +11:00
DrawMarker ( 28 , xCoord , yCoord , zCoord , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , height , height , height , 255 , 42 , 24 , 100 , false , false , 0 , false , false , false , false )
2022-09-14 15:15:36 +10:00
end
if controlsActive then
DisableAllControlActions ( )
2022-11-25 18:05:55 -08:00
EnableControlAction ( 0 , controls [ ' INPUT_LOOK_LR ' ] , true )
EnableControlAction ( 0 , controls [ ' INPUT_LOOK_UD ' ] , true )
EnableControlAction ( 0 , controls [ ' INPUT_MP_TEXT_CHAT_ALL ' ] , true )
2022-09-14 15:15:36 +10:00
local change = false
2022-10-02 11:43:10 +10:00
local lStep = steps [ 1 ] [ step ]
local rStep = steps [ 2 ] [ step ]
2022-09-14 15:15:36 +10:00
2022-09-15 20:56:10 +10:00
if IsDisabledControlJustReleased ( 0 , 17 ) then -- scroll up
2022-09-14 15:15:36 +10:00
if IsDisabledControlPressed ( 0 , 21 ) then -- shift held down
change = true
2022-10-02 11:43:10 +10:00
height += lStep
2022-09-14 15:15:36 +10:00
elseif IsDisabledControlPressed ( 0 , 36 ) then -- ctrl held down
change = true
2022-10-02 11:43:10 +10:00
width += lStep
2022-09-14 15:15:36 +10:00
elseif IsDisabledControlPressed ( 0 , 19 ) then -- alt held down
change = true
2022-10-02 11:43:10 +10:00
length += lStep
2022-09-14 15:15:36 +10:00
elseif step < 11 then
change = true
step += 1
end
2022-09-15 20:56:10 +10:00
elseif IsDisabledControlJustReleased ( 0 , 16 ) then -- scroll down
2022-09-14 15:15:36 +10:00
if IsDisabledControlPressed ( 0 , 21 ) then -- shift held down
change = true
2022-10-02 11:43:10 +10:00
if height - lStep > lStep then
height -= lStep
elseif height - lStep > 0 then
height = lStep
2022-09-14 15:15:36 +10:00
end
elseif IsDisabledControlPressed ( 0 , 36 ) then -- ctrl held down
change = true
2022-10-02 11:43:10 +10:00
if width - lStep > lStep then
width -= lStep
elseif width - lStep > 0 then
width = lStep
2022-09-14 15:15:36 +10:00
end
elseif IsDisabledControlPressed ( 0 , 19 ) then -- alt held down
change = true
2022-10-02 11:43:10 +10:00
if length - lStep > lStep then
length -= lStep
elseif length - lStep > 0 then
length = lStep
2022-09-14 15:15:36 +10:00
end
elseif step > 1 then
change = true
step -= 1
end
2022-09-15 20:56:10 +10:00
elseif IsDisabledControlJustReleased ( 0 , 32 ) then -- w
2022-09-14 15:15:36 +10:00
change = true
2023-01-07 00:27:12 -05:00
if alignMovementWithCamera then
local newX , newY = getRelativePos ( vec2 ( xCoord , yCoord ) , vec2 ( xCoord , yCoord + lStep ) , GetGameplayCamRot ( 2 ) . z )
if math.abs ( newX ) < minCheck then
newX = 0.0
end
if math.abs ( newY ) < minCheck then
newY = 0.0
end
xCoord = newX
yCoord = newY
else
local newValue = yCoord + lStep
if math.abs ( newValue ) < minCheck then
newValue = 0.0
end
yCoord = newValue
2022-10-02 11:43:10 +10:00
end
2022-09-15 20:56:10 +10:00
elseif IsDisabledControlJustReleased ( 0 , 33 ) then -- s
2022-09-14 15:15:36 +10:00
change = true
2023-01-07 00:27:12 -05:00
if alignMovementWithCamera then
local newX , newY = getRelativePos ( vec2 ( xCoord , yCoord ) , vec2 ( xCoord , yCoord - lStep ) , GetGameplayCamRot ( 2 ) . z )
if math.abs ( newX ) < minCheck then
newX = 0.0
end
if math.abs ( newY ) < minCheck then
newY = 0.0
end
xCoord = newX
yCoord = newY
else
local newValue = yCoord - lStep
if math.abs ( newValue ) < minCheck then
newValue = 0.0
end
yCoord = newValue
2022-10-02 11:43:10 +10:00
end
2022-09-15 20:56:10 +10:00
elseif IsDisabledControlJustReleased ( 0 , 35 ) then -- d
2022-09-14 15:15:36 +10:00
change = true
2023-01-07 00:27:12 -05:00
if alignMovementWithCamera then
local newX , newY = getRelativePos ( vec2 ( xCoord , yCoord ) , vec2 ( xCoord + lStep , yCoord ) , GetGameplayCamRot ( 2 ) . z )
if math.abs ( newX ) < minCheck then
newX = 0.0
end
if math.abs ( newY ) < minCheck then
newY = 0.0
end
xCoord = newX
yCoord = newY
else
local newValue = xCoord + lStep
if math.abs ( newValue ) < minCheck then
newValue = 0.0
end
xCoord = newValue
2022-10-02 11:43:10 +10:00
end
2022-09-15 20:56:10 +10:00
elseif IsDisabledControlJustReleased ( 0 , 34 ) then -- a
2022-09-14 15:15:36 +10:00
change = true
2023-01-07 00:27:12 -05:00
if alignMovementWithCamera then
local newX , newY = getRelativePos ( vec2 ( xCoord , yCoord ) , vec2 ( xCoord - lStep , yCoord ) , GetGameplayCamRot ( 2 ) . z )
if math.abs ( newX ) < minCheck then
newX = 0.0
end
if math.abs ( newY ) < minCheck then
newY = 0.0
end
xCoord = newX
yCoord = newY
else
local newValue = xCoord - lStep
if math.abs ( newValue ) < minCheck then
newValue = 0.0
end
xCoord = newValue
2022-10-02 11:43:10 +10:00
end
2022-09-15 20:56:10 +10:00
elseif IsDisabledControlJustReleased ( 0 , 45 ) then -- r
2022-09-14 15:15:36 +10:00
change = true
2022-10-02 11:43:10 +10:00
local newValue = zCoord + lStep
if math.abs ( newValue ) < minCheck then
newValue = 0.0
end
zCoord = newValue
2022-09-15 20:56:10 +10:00
elseif IsDisabledControlJustReleased ( 0 , 23 ) then -- f
2022-09-14 15:15:36 +10:00
change = true
2022-10-02 11:43:10 +10:00
local newValue = zCoord - lStep
if math.abs ( newValue ) < minCheck then
newValue = 0.0
end
zCoord = newValue
2022-09-15 20:56:10 +10:00
elseif IsDisabledControlJustReleased ( 0 , 38 ) then -- e
2022-09-14 15:15:36 +10:00
change = true
2022-10-02 11:43:10 +10:00
heading -= rStep
2022-09-14 15:15:36 +10:00
if heading < 0 then
heading += 360
end
2022-10-01 15:24:51 +10:00
elseif IsDisabledControlJustReleased ( 0 , 44 ) then -- q
change = true
2022-10-02 11:43:10 +10:00
heading += rStep
2022-10-01 15:24:51 +10:00
if heading >= 360 then
heading -= 360
end
2022-09-15 21:19:10 +10:00
elseif IsDisabledControlJustReleased ( 0 , 47 ) then -- g
change = true
if displayMode == # displayModes then
displayMode = 1
else
displayMode += 1
end
2022-09-15 20:56:10 +10:00
elseif IsDisabledControlJustReleased ( 0 , 22 ) then -- space
2022-09-14 15:15:36 +10:00
change = true
if zoneType == ' poly ' then
points [ # points + 1 ] = vec2 ( xCoord , yCoord )
end
2022-09-15 20:56:10 +10:00
coords = GetEntityCoords ( cache.ped )
2022-09-14 15:15:36 +10:00
xCoord = round ( coords.x )
yCoord = round ( coords.y )
2022-09-15 20:56:10 +10:00
elseif IsDisabledControlJustReleased ( 0 , 201 ) then -- enter
2022-09-14 15:15:36 +10:00
closeCreator ( )
2022-09-15 20:56:10 +10:00
elseif IsDisabledControlJustReleased ( 0 , 200 ) then -- esc
SetPauseMenuActive ( false )
2022-09-14 15:15:36 +10:00
closeCreator ( true )
end
if change then
updateText ( )
end
end
end
end
2022-09-14 00:05:08 +10:00
2022-09-14 15:15:36 +10:00
RegisterCommand ( ' zone ' , function ( source , args , rawCommand )
2023-01-07 00:27:12 -05:00
if args [ 1 ] ~= ' poly ' and args [ 1 ] ~= ' box ' and args [ 1 ] ~= ' sphere ' then
lib.notify ( { title = ' Invalid zone type ' , type = ' error ' } )
return
end
if creatorActive then
lib.notify ( { title = ' Already creating a zone ' , type = ' error ' } )
return
end
local useLast = args [ 2 ] and not useLastZoneFalsyInputs [ args [ 2 ] ]
if useLast then
if not lastZone [ args [ 1 ] ] then
lib.notify ( { title = ' No last zone to duplicate ' , type = ' error ' } )
return
end
if args [ 1 ] == ' poly ' then
lib.notify ( { title = ' Cannot duplicate a poly zone ' , type = ' error ' } )
return
end
end
CreateThread ( function ( )
startCreator ( args [ 1 ] , useLast )
end )
2022-09-14 15:15:36 +10:00
end , true )
2022-09-15 00:21:16 +10:00
TriggerEvent ( ' chat:addSuggestion ' , ' /zone ' , ' Starts creation of the specified zone ' , {
2023-01-07 00:27:12 -05:00
{ name = ' zoneType ' , help = ' poly, box, sphere ' } , { name = ' useLast ' , help = ' duplicates the last created zone of the specified type (box and sphere only, optional) ' }
2022-09-15 00:21:16 +10:00
} )