diff --git a/README.md b/README.md index 32b9c4d..01198b4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # `polygun` -Easily create polyzones around objects and save for configuring +Easily create polyzones around objects and save for configuring ![image](https://github.com/BryceRussell/polygun/assets/19967622/26c96993-aba7-4089-8cde-1d1956919651) @@ -19,9 +19,23 @@ Easily create polyzones around objects and save for configuring 2) Equip a gun and aim it at an object to select it 3) (**Optional**) Tweak size - - **Scroll**: Scale XYZ + - **Scroll**: Scale XYZ - **Up/Down Arrows**: Scale X - **Right/Left Arrows**: Scale Y - **Page Up/Down**: Scale Z -4) Press `E` (or use `/polygunsave`) to save it to the `zones.txt` file inside the resource +# Controls + +4) `X` to save it to the `zones.txt` file inside the resource + +5) `N` copy Vector2 + +6) `E` copy Vector3 + +7) `Q` Copy vectory4 + +8) `F` copy only coordinates (x, y, z) format + +9) `R` copy cords in json format + +10) `G` Add zone at laser/line pointed location \ No newline at end of file diff --git a/client/client.lua b/client/client.lua index 8a277c1..4aaa88f 100644 --- a/client/client.lua +++ b/client/client.lua @@ -1,13 +1,46 @@ -local RESULT, ENTITY, NAME, COORDS, PREVIOUS, ZONE, PREVIOUSZONE, SCALEX, SCALEY, SCALEZ - +local RESULT, HEADING, ENTITY, NAME, COORDS, PREVIOUS, ZONE, PREVIOUSZONE, SCALEX, SCALEY, SCALEZ +local laserEnabled = false +local laserEndPoint = nil local hashes_file = LoadResourceFile(GetCurrentResourceName(), "hashes.json") local hashes = json.decode(hashes_file) - +lib.hideTextUI() local function round(num, numDecimalPlaces) local mult = 10 ^ (numDecimalPlaces or 0) return math.floor(num * mult + 0.5) / mult end +local RotationToDirection = function(rotation) + local adjustedRotation = { + x = (math.pi / 180) * rotation.x, + y = (math.pi / 180) * rotation.y, + z = (math.pi / 180) * rotation.z + } + local direction = { + x = -math.sin(adjustedRotation.z) * math.abs(math.cos(adjustedRotation.x)), + y = math.cos(adjustedRotation.z) * math.abs(math.cos(adjustedRotation.x)), + z = math.sin(adjustedRotation.x) + } + return direction +end +local RayCastGamePlayCamera = function(distance) + -- Checks to see if the Gameplay Cam is Rendering or another is rendering (no clip functionality) + local currentRenderingCam = false + if not IsGameplayCamRendering() then + currentRenderingCam = GetRenderingCam() + end + + local cameraRotation = not currentRenderingCam and GetGameplayCamRot() or GetCamRot(currentRenderingCam, 2) + local cameraCoord = not currentRenderingCam and GetGameplayCamCoord() or GetCamCoord(currentRenderingCam) + local direction = RotationToDirection(cameraRotation) + local destination = { + x = cameraCoord.x + direction.x * distance, + y = cameraCoord.y + direction.y * distance, + z = cameraCoord.z + direction.z * distance + } + local _, b, c, _, e = GetShapeTestResult(StartShapeTestRay(cameraCoord.x, cameraCoord.y, cameraCoord.z, destination.x, destination.y, destination.z, -1, PlayerPedId(), 0)) + return b, c, e +end + local function parseBox(zone) return "{\n" @@ -16,10 +49,10 @@ local function parseBox(zone) "vector3(" .. tostring(round(zone.center.x, 2)) .. ", " .. tostring(round(zone.center.y, 2)) .. ", " .. tostring(round(zone.center.z, 2)) .. "),\n" - .. "\tlength = " .. tostring(zone.length) .. ",\n" - .. "\twidth = " .. tostring(zone.width) .. ",\n" + .. "\tlength = " .. tostring(SCALEX) .. ",\n" + .. "\twidth = " .. tostring(SCALEY) .. ",\n" .. "\tname = \"" .. zone.name .. "\",\n" - .. "\theading = " .. zone.offsetRot .. ",\n" + .. "\theading = " .. tostring(HEADING) .. ",\n" .. "\tminZ = " .. tostring(round(zone.minZ, 2)) .. ",\n" .. "\tmaxZ = " .. tostring(round(zone.maxZ, 2)) .. ",\n" .. "}\n" @@ -31,110 +64,281 @@ end local function drawEntityZone() destoryZone() - ZONE = EntityZone:Create(ENTITY, { - name = NAME, - debugPoly = true, - useZ = true, - scale = scale or { SCALEX, SCALEY, SCALEZ } + if DoesEntityExist(ENTITY) then + ZONE = EntityZone:Create(ENTITY, { + name = NAME, + debugPoly = true, + useZ = true, + scale = scale or { SCALEX, SCALEY, SCALEZ } + }) + end +end + +function createEntity() + local input = lib.inputDialog('Create Zone', { + {type = 'input', label = 'Zone Name', description = 'Enter Zone name', required = true, min = 4, max = 16, default = "boxzone_"..tostring(math.random(1111,9999))}, + {type = 'number', label = 'Width X', description = 'Enter width size', required = true, icon = 'hashtag', default = 2}, + {type = 'number', label = 'Height Y', description = 'Enter height size', required = true, icon = 'hashtag', default = 2}, + {type = 'number', label = 'Height Z', description = 'Enter height for Z', required = true, icon = 'hashtag', default = 2}, }) + if not input then return end + local modelName = "prop_parking_sign_1" + NAME = input[1] + SCALEX = input[2] + SCALEY = input[3] + SCALEZ = input[4] + if DoesEntityExist(ENTITY) then + if GetEntityModel(entity) == GetHashKey("prop_parking_sign_1") then + DeleteEntity(ENTITY) + end + end + ENTITY = nil + RequestModel(modelName) + while not HasModelLoaded(modelName) do + Wait(500) + end + ENTITY = CreateObject(modelName, laserEndPoint.x, laserEndPoint.y, laserEndPoint.z, true, true, true) + SetEntityHeading(ENTITY, HEADING) + SetEntityAlpha(ENTITY, 0, false) + FreezeEntityPosition(ENTITY, true) + SetEntityCompletelyDisableCollision(ENTITY, false) + Wait(100) + drawEntityZone() end -RegisterCommand('polygunsave', function() +function saveZone() if not Config.loopOn or not ZONE or ZONE == PREVIOUSZONE then return end - local text = parseBox(ZONE) - if Config.clipboard then lib.setClipboard(text) end - TriggerServerEvent('polygun:save', ZONE, text) - lib.notify({ - title = 'Box Zone Saved', - duration = 7500, - description = 'Check zones.txt inside the polygun resource', - type = 'success' - }) PREVIOUSZONE = ZONE -end) - -RegisterKeyMapping('polygunsave', 'Save selected polyzone', 'keyboard', Config.defaultSaveKey) + if ZONE then + local text = parseBox(ZONE) + if Config.clipboard then lib.setClipboard(text) end + TriggerServerEvent('polygun:save', ZONE, text) + lib.notify({ + title = 'Box Zone Saved', + duration = 7500, + description = 'Check zones.txt inside the polygun resource', + type = 'success' + }) + if laserEnabled then + if DoesEntityExist(ENTITY) then + DeleteEntity(ENTITY) + end + end + end + ENTITY = nil + destoryZone() + Wait(300) + RESULT = nil + laserEnabled = false +end --- Thread that makes everything happen. -Citizen.CreateThread(function() -- Create the thread. +CreateThread(function() -- Create the thread. while true do -- Loop it infinitely. - local pause = 250 -- If infos are off, set loop to every 250ms. Eats less resources. + local pause = 250 + local player = PlayerId() + local playerPed = PlayerPedId() + -- If infos are off, set loop to every 250ms. Eats less resources. if Config.loopOn then -- If the info is on then... pause = 5 -- Only loop every 5ms (equivalent of 200fps). - local player = PlayerId() if IsPlayerFreeAiming(player) then -- If the player is free-aiming (update texts)... + if RESULT then + RESULT = nil + end local start = GetPedBoneCoords(PlayerPedId(), 57005, 0.0, 0.0, 0.0) local result, entity = GetEntityPlayerIsFreeAimingAt(player) -- Get what the player is aiming at. This isn't actually the function, that's below the thread. if result then RESULT = result ENTITY = entity COORDS = GetEntityCoords(ENTITY) - if Config.debugAimLine then - DrawLine(start.x, start.y, start.z, COORDS.x, COORDS.y, COORDS.z, 0, 255, 0, 255) - end - elseif Config.debugAimLine then - local finish = GetWorldCoordFromScreenCoord(0.5, 0.5) - DrawLine(start.x, start.y, start.z, finish.x, finish.y, finish.z, 0, 255, 0, 255) end end - if RESULT then - local heading = GetEntityHeading(ENTITY) - local model = GetEntityModel(ENTITY) - NAME = hashes[tostring(model)] or 'unknown' - if Config.debugText then - DrawInfos("Coordinates: " .. COORDS, "Heading: " .. heading, "Hash: " .. model, "Name: " .. NAME) - end - if ENTITY ~= PREVIOUS then - SCALEX = 1.0 - SCALEY = 1.0 - SCALEZ = 1.0 - drawEntityZone() - PREVIOUS = ENTITY - end - if IsControlJustPressed(0, 241) or IsDisabledControlPressed(1, 241) then -- Scroll Up - SCALEX = SCALEX + Config.addX - SCALEY = SCALEY + Config.addY - SCALEZ = SCALEZ + Config.addZ - drawEntityZone() - end - if IsControlJustPressed(0, 242) or IsDisabledControlPressed(1, 242) then -- Scroll down - SCALEX = SCALEX - Config.subX - SCALEY = SCALEY - Config.subY - SCALEZ = SCALEZ - Config.subZ - drawEntityZone() - end + if RESULT then + local heading = GetEntityHeading(ENTITY) + local model = GetEntityModel(ENTITY) + NAME = hashes[tostring(model)] or 'unknown' + if Config.debugText then + DrawInfos("Coordinates: " .. COORDS, "Heading: " .. heading, "Hash: " .. model, "Name: " .. NAME) + end + HEADING =heading + if ENTITY ~= PREVIOUS then + SCALEX = 1.0 + SCALEY = 1.0 + SCALEZ = 1.0 + drawEntityZone() + PREVIOUS = ENTITY + end + if IsControlJustPressed(0, 241) or IsDisabledControlPressed(1, 241) then -- Scroll Up + SCALEX = SCALEX + Config.addX + SCALEY = SCALEY + Config.addY + SCALEZ = SCALEZ + Config.addZ + drawEntityZone() + end + if IsControlJustPressed(0, 242) or IsDisabledControlPressed(1, 242) then -- Scroll down + SCALEX = SCALEX - Config.subX + SCALEY = SCALEY - Config.subY + SCALEZ = SCALEZ - Config.subZ + drawEntityZone() + end - if IsControlPressed(1, Config.addXControl) or IsDisabledControlPressed(1, Config.addXControl) then -- Up Arrow - SCALEX = SCALEX + Config.addX - drawEntityZone() - end - if IsControlPressed(1, Config.subXControl) or IsDisabledControlPressed(1, Config.subXControl) then -- Down Arrow - SCALEX = SCALEX - Config.subZ - drawEntityZone() - end + if IsControlPressed(1, Config.addXControl) or IsDisabledControlPressed(1, Config.addXControl) then -- Up Arrow + SCALEX = SCALEX + Config.addX + drawEntityZone() + end + if IsControlPressed(1, Config.subXControl) or IsDisabledControlPressed(1, Config.subXControl) then -- Down Arrow + SCALEX = SCALEX - Config.subZ + drawEntityZone() + end - if IsControlPressed(1, Config.subYControl) or IsDisabledControlPressed(1, Config.subYControl) then -- Left Arrow - SCALEY = SCALEY - Config.subY - drawEntityZone() - end - if IsControlPressed(1, Config.addYControl) or IsDisabledControlPressed(1, Config.addYControl) then -- Right Arrow - SCALEY = SCALEY + Config.addY - drawEntityZone() - end + if IsControlPressed(1, Config.subYControl) or IsDisabledControlPressed(1, Config.subYControl) then -- Left Arrow + SCALEY = SCALEY - Config.subY + drawEntityZone() + end + if IsControlPressed(1, Config.addYControl) or IsDisabledControlPressed(1, Config.addYControl) then -- Right Arrow + SCALEY = SCALEY + Config.addY + drawEntityZone() + end + + if IsControlPressed(1, Config.addZControl) or IsDisabledControlPressed(1, Config.addZControl) then -- Page Up + SCALEZ = SCALEZ + Config.addZ + drawEntityZone() + end + if IsControlPressed(1, Config.subZControl) or IsDisabledControlPressed(1, Config.subZControl) then -- Page Down + SCALEZ = SCALEZ - Config.subZ + drawEntityZone() + end + else + local color = { r = 0, g = 255, b = 0, a = 200 } + local position = GetEntityCoords(playerPed) + local hit, coords, entity = RayCastGamePlayCamera(1000.0) + local heading = GetEntityHeading(GetPlayerPed(-1)) + local x = round(coords.x, 2) + local y = round(coords.y, 2) + local z = round(coords.z, 2) + local w = round(heading, 2) + HEADING = w + if IsControlJustReleased(0, 58) then -- G add zone + laserEndPoint = coords + createEntity() + Wait(1000) + end + if IsControlJustReleased(0, 51) then -- Copy Vector3 Coords + laserEndPoint = string.format('vector3(%s, %s, %s)', x, y, z) + lib.setClipboard(laserEndPoint) + lib.notify({ + title = 'Vector 3 Copy', + duration = 7500, + description = laserEndPoint, + type = 'success' + }) + end + if IsControlJustReleased(0, 52) then -- Copy Vector4 Coords + laserEndPoint = string.format('vector4(%s, %s, %s, %s)', x, y, z, w) + lib.setClipboard(laserEndPoint) + lib.notify({ + title = 'Vector 4 Copy', + duration = 7500, + description = laserEndPoint, + type = 'success' + }) + end + + if IsControlJustReleased(0, 306) then --N Copy Coords + + laserEndPoint = string.format('vector2(%s, %s)', x, y) + lib.setClipboard(laserEndPoint) + lib.notify({ + title = 'Vector 2 Copy', + duration = 7500, + description = laserEndPoint, + type = 'success' + }) + end + if IsControlJustReleased(0, 45) then -- Copy Coords In json + + laserEndPoint = string.format('{x = %s, y = %s, z = %s, w = %s)', x, y, z, w) + lib.setClipboard(laserEndPoint) + lib.notify({ + title = 'Json Copy', + duration = 7500, + description = laserEndPoint, + type = 'success' + }) + end + if IsControlJustReleased(0, 74) then -- Copy Heading + laserEndPoint = string.format('%s', w) + lib.setClipboard(laserEndPoint) + lib.notify({ + title = 'Heading Copy', + duration = 7500, + description = laserEndPoint, + type = 'success' + }) + end + + if IsControlJustReleased(0, 75) then -- Copy Coords without vetor + local x = round(coords.x, 2) + local y = round(coords.y, 2) + local z = round(coords.z, 2) + laserEndPoint = string.format('%s, %s, %s', x, y, z) + lib.setClipboard(laserEndPoint) + lib.notify({ + title = 'Coordinate Copy', + duration = 7500, + description = laserEndPoint, + type = 'success' + }) + end + if IsControlJustPressed(0, 241) or IsDisabledControlPressed(1, 241) then -- Scroll Up + SCALEX = SCALEX + Config.addX + SCALEY = SCALEY + Config.addY + SCALEZ = SCALEZ + Config.addZ + drawEntityZone() + end + if IsControlJustPressed(0, 242) or IsDisabledControlPressed(1, 242) then -- Scroll down + SCALEX = SCALEX - Config.subX + SCALEY = SCALEY - Config.subY + SCALEZ = SCALEZ - Config.subZ + drawEntityZone() + end + + if IsControlPressed(1, Config.addXControl) or IsDisabledControlPressed(1, Config.addXControl) then -- Up Arrow + SCALEX = SCALEX + Config.addX + drawEntityZone() + end + if IsControlPressed(1, Config.subXControl) or IsDisabledControlPressed(1, Config.subXControl) then -- Down Arrow + SCALEX = SCALEX - Config.subZ + drawEntityZone() + end + + if IsControlPressed(1, Config.subYControl) or IsDisabledControlPressed(1, Config.subYControl) then -- Left Arrow + SCALEY = SCALEY - Config.subY + drawEntityZone() + end + if IsControlPressed(1, Config.addYControl) or IsDisabledControlPressed(1, Config.addYControl) then -- Right Arrow + SCALEY = SCALEY + Config.addY + drawEntityZone() + end - if IsControlPressed(1, Config.addZControl) or IsDisabledControlPressed(1, Config.addZControl) then -- Page Up - SCALEZ = SCALEZ + Config.addZ - drawEntityZone() + if IsControlPressed(1, Config.addZControl) or IsDisabledControlPressed(1, Config.addZControl) then -- Page Up + SCALEZ = SCALEZ + Config.addZ + drawEntityZone() + end + if IsControlPressed(1, Config.subZControl) or IsDisabledControlPressed(1, Config.subZControl) then -- Page Down + SCALEZ = SCALEZ - Config.subZ + drawEntityZone() + end + DrawLine(position.x, position.y, position.z, coords.x, coords.y, coords.z, color.r, color.g, color.b, color.a) + DrawMarker(28, coords.x, coords.y, coords.z, 0.0, 0.0, 0.0, 0.0, 180.0, 0.0, 0.1, 0.1, 0.1, color.r, color.g, color.b, color.a, false, true, 2, nil, nil, false, false) end - if IsControlPressed(1, Config.subZControl) or IsDisabledControlPressed(1, Config.subZControl) then -- Page Down - SCALEZ = SCALEZ - Config.subZ - drawEntityZone() + if IsControlJustReleased(0, 73) then -- Xsave zone + saveZone() + Wait(1000) end - end - end -- Info is off, don't need to do anything. - Citizen.Wait(pause) -- Now wait the specified time. - end -- End (stop looping). -end) -- Endind the entire thread here. + + end + Wait(pause) + end +end) -- Ends the function. -- Function to draw the text. @@ -161,10 +365,29 @@ end -- Creating the function to toggle the info. ToggleInfos = function() -- "ToggleInfos" is a function Config.loopOn = not Config.loopOn -- Switch them around + if laserEnabled then + laserEndPoint = nil + if DoesEntityExist(ENTITY) then + DeleteEntity(ENTITY) + end + laserEnabled = false + destoryZone() + end end -- Ending the function here. --- Creating the command. -RegisterCommand("polygun", function() -- Listen for this command. +RegisterNetEvent('polygun:runpolygun', function() destoryZone() - ToggleInfos() -- Heard it! Let's toggle the function above. -end) -- Ending the function here. + ToggleInfos() + if Config.loopOn then + lib.notify({ + title = 'Info', + duration = 7500, + description = 'Get Gun In hand and aim to entity', + type = 'success' + }) + RESULT = nil + lib.showTextUI("CONTROLS : [N] vector2 | [E] vector3 | [Q] vector4 | [F] coords | [R] Json | [H] Heading | [G] Add Zone | [X] Save Zone", {position = 'top-center'}) + else + lib.hideTextUI() + end +end) \ No newline at end of file diff --git a/fxmanifest.lua b/fxmanifest.lua index 1cc85b6..1bebc54 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -20,7 +20,7 @@ client_scripts { '@PolyZone/client.lua', '@PolyZone/BoxZone.lua', '@PolyZone/EntityZone.lua', - 'client/client.lua' + 'client/*.lua' } server_scripts { diff --git a/server/server.lua b/server/server.lua index 1f4353f..c26bdea 100644 --- a/server/server.lua +++ b/server/server.lua @@ -5,3 +5,17 @@ AddEventHandler("polygun:save", function(zone, text) io.write("--Name: " .. zone.name .. " | " .. os.date("!%Y-%m-%dT%H:%M:%SZ\n") .. text) io.close(file) end) + +lib.addCommand('polygun', { + help = 'Create Polyzon at entity', + restricted = 'group.admin' +}, function(source, args, raw) + TriggerClientEvent("polygun:runpolygun", source) +end) + +lib.addCommand('plaser', { + help = 'Copy coordinates at laser point', + restricted = 'group.admin' +}, function(source, args, raw) + TriggerClientEvent("polygun:runlaser", source) +end) diff --git a/shared/config.lua b/shared/config.lua index 9deeec8..a60d2bf 100644 --- a/shared/config.lua +++ b/shared/config.lua @@ -2,7 +2,7 @@ Config = {} Config.loopOn = false -- true: enabled by default, false: disabled by default (use /polygun to toggle) -Config.defaultSaveKey = 'E' -- Keybind to save selected zone to `zones.txt` file +Config.defaultSaveKey = 'X' -- Keybind to save selected zone to `zones.txt` file Config.clipboard = true -- Copy to clipboard on save diff --git a/zones.txt b/zones.txt new file mode 100644 index 0000000..e69de29