Ripped a part utility folder and shoved it into a lib. Changed a lot of Utility prefixes to the modules name. This contains breaking changes. Scripts that use 0.2.0 will need to update to match the new format
458 lines
15 KiB
Lua
458 lines
15 KiB
Lua
-- Credit: https://github.com/citizenfx/lua/blob/luaglm-dev/cfx/libs/scripts/examples/dataview.lua
|
|
local dataView = setmetatable({
|
|
EndBig = ">",
|
|
EndLittle = "<",
|
|
Types = {
|
|
Int8 = { code = "i1" },
|
|
Uint8 = { code = "I1" },
|
|
Int16 = { code = "i2" },
|
|
Uint16 = { code = "I2" },
|
|
Int32 = { code = "i4" },
|
|
Uint32 = { code = "I4" },
|
|
Int64 = { code = "i8" },
|
|
Uint64 = { code = "I8" },
|
|
Float32 = { code = "f", size = 4 }, -- a float (native size)
|
|
Float64 = { code = "d", size = 8 }, -- a double (native size)
|
|
|
|
LuaInt = { code = "j" }, -- a lua_Integer
|
|
UluaInt = { code = "J" }, -- a lua_Unsigned
|
|
LuaNum = { code = "n" }, -- a lua_Number
|
|
String = { code = "z", size = -1, }, -- zero terminated string
|
|
},
|
|
|
|
FixedTypes = {
|
|
String = { code = "c" }, -- a fixed-sized string with n bytes
|
|
Int = { code = "i" }, -- a signed int with n bytes
|
|
Uint = { code = "I" }, -- an unsigned int with n bytes
|
|
},
|
|
}, {
|
|
__call = function(_, length)
|
|
return dataView.ArrayBuffer(length)
|
|
end
|
|
})
|
|
dataView.__index = dataView
|
|
|
|
--[[ Create an ArrayBuffer with a size in bytes --]]
|
|
function dataView.ArrayBuffer(length)
|
|
return setmetatable({
|
|
blob = string.blob(length),
|
|
length = length,
|
|
offset = 1,
|
|
cangrow = true,
|
|
}, dataView)
|
|
end
|
|
|
|
--[[ Wrap a non-internalized string --]]
|
|
function dataView.Wrap(blob)
|
|
return setmetatable({
|
|
blob = blob,
|
|
length = blob:len(),
|
|
offset = 1,
|
|
cangrow = true,
|
|
}, dataView)
|
|
end
|
|
|
|
--[[ Return the underlying bytebuffer --]]
|
|
function dataView:Buffer() return self.blob end
|
|
function dataView:ByteLength() return self.length end
|
|
function dataView:ByteOffset() return self.offset end
|
|
function dataView:SubView(offset, length)
|
|
return setmetatable({
|
|
blob = self.blob,
|
|
length = length or self.length,
|
|
offset = 1 + offset,
|
|
cangrow = false,
|
|
}, dataView)
|
|
end
|
|
|
|
--[[ Return the Endianness format character --]]
|
|
local function ef(big) return (big and dataView.EndBig) or dataView.EndLittle end
|
|
|
|
--[[ Helper function for setting fixed datatypes within a buffer --]]
|
|
local function packblob(self, offset, value, code)
|
|
-- If cangrow is false the dataview represents a subview, i.e., a subset
|
|
-- of some other string view. Ensure the references are the same before
|
|
-- updating the subview
|
|
local packed = self.blob:blob_pack(offset, code, value)
|
|
if self.cangrow or packed == self.blob then
|
|
self.blob = packed
|
|
self.length = packed:len()
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Create the API by using dataView.Types
|
|
--]]
|
|
for label,datatype in pairs(dataView.Types) do
|
|
if not datatype.size then -- cache fixed encoding size
|
|
datatype.size = string.packsize(datatype.code)
|
|
elseif datatype.size >= 0 and string.packsize(datatype.code) ~= datatype.size then
|
|
local msg = "Pack size of %s (%d) does not match cached length: (%d)"
|
|
error(msg:format(label, string.packsize(datatype.code), datatype.size))
|
|
return nil
|
|
end
|
|
|
|
dataView["Get" .. label] = function(self, offset, endian)
|
|
offset = offset or 0
|
|
if offset >= 0 then
|
|
local o = self.offset + offset
|
|
local v,_ = self.blob:blob_unpack(o, ef(endian) .. datatype.code)
|
|
return v
|
|
end
|
|
return nil
|
|
end
|
|
|
|
dataView["Set" .. label] = function(self, offset, value, endian)
|
|
if offset >= 0 and value then
|
|
local o = self.offset + offset
|
|
local v_size = (datatype.size < 0 and value:len()) or datatype.size
|
|
if self.cangrow or ((o + (v_size - 1)) <= self.length) then
|
|
if not packblob(self, o, value, ef(endian) .. datatype.code) then
|
|
error("cannot grow subview")
|
|
end
|
|
else
|
|
error("cannot grow dataview")
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
end
|
|
|
|
for label,datatype in pairs(dataView.FixedTypes) do
|
|
datatype.size = -1 -- Ensure cached encoding size is invalidated
|
|
|
|
dataView["GetFixed" .. label] = function(self, offset, typelen, endian)
|
|
if offset >= 0 then
|
|
local o = self.offset + offset
|
|
if (o + (typelen - 1)) <= self.length then
|
|
local code = ef(endian) .. "c" .. tostring(typelen)
|
|
local v,_ = self.blob:blob_unpack(o, code)
|
|
return v
|
|
end
|
|
end
|
|
return nil -- Out of bounds
|
|
end
|
|
|
|
dataView["SetFixed" .. label] = function(self, offset, typelen, value, endian)
|
|
if offset >= 0 and value then
|
|
local o = self.offset + offset
|
|
if self.cangrow or ((o + (typelen - 1)) <= self.length) then
|
|
local code = ef(endian) .. "c" .. tostring(typelen)
|
|
if not packblob(self, o, value, code) then
|
|
error("cannot grow subview")
|
|
end
|
|
else
|
|
error("cannot grow dataview")
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
end
|
|
|
|
|
|
-- CREDITS
|
|
-- Andyyy7666: https://github.com/overextended/ox_lib/pull/453
|
|
-- AvarianKnight: https://forum.cfx.re/t/allow-drawgizmo-to-be-used-outside-of-fxdk/5091845/8?u=demi-automatic
|
|
local dataview = dataView
|
|
local enableScale = false -- allow scaling mode. doesnt scale collisions and resets when physics are applied it seems
|
|
local gizmoEnabled = false
|
|
local currentMode = 'translate'
|
|
local isRelative = false
|
|
local currentEntity
|
|
|
|
-- FUNCTIONS
|
|
|
|
local function normalize(x, y, z)
|
|
local length = math.sqrt(x * x + y * y + z * z)
|
|
if length == 0 then
|
|
return 0, 0, 0
|
|
end
|
|
return x / length, y / length, z / length
|
|
end
|
|
|
|
local identityMatrix = {
|
|
vector3(1, 0, 0),
|
|
vector3(0, 1, 0),
|
|
vector3(0, 0, 1)
|
|
}
|
|
local function makeEntityMatrix(entity)
|
|
local f, r, u, a = GetEntityMatrix(entity)
|
|
local view = dataview.ArrayBuffer(60)
|
|
|
|
view:SetFloat32(0, r[1])
|
|
:SetFloat32(4, r[2])
|
|
:SetFloat32(8, r[3])
|
|
:SetFloat32(12, 0)
|
|
:SetFloat32(16, f[1])
|
|
:SetFloat32(20, f[2])
|
|
:SetFloat32(24, f[3])
|
|
:SetFloat32(28, 0)
|
|
:SetFloat32(32, u[1])
|
|
:SetFloat32(36, u[2])
|
|
:SetFloat32(40, u[3])
|
|
:SetFloat32(44, 0)
|
|
:SetFloat32(48, a[1])
|
|
:SetFloat32(52, a[2])
|
|
:SetFloat32(56, a[3])
|
|
:SetFloat32(60, 1)
|
|
|
|
return view
|
|
end
|
|
|
|
local function applyEntityMatrix(entity, view)
|
|
local x1, y1, z1 = view:GetFloat32(16), view:GetFloat32(20), view:GetFloat32(24)
|
|
local x2, y2, z2 = view:GetFloat32(0), view:GetFloat32(4), view:GetFloat32(8)
|
|
local x3, y3, z3 = view:GetFloat32(32), view:GetFloat32(36), view:GetFloat32(40)
|
|
local tx, ty, tz = view:GetFloat32(48), view:GetFloat32(52), view:GetFloat32(56)
|
|
|
|
if not enableScale then
|
|
x1, y1, z1 = normalize(x1, y1, z1)
|
|
x2, y2, z2 = normalize(x2, y2, z2)
|
|
x3, y3, z3 = normalize(x3, y3, z3)
|
|
end
|
|
|
|
SetEntityMatrix(entity,
|
|
x1, y1, z1,
|
|
x2, y2, z2,
|
|
x3, y3, z3,
|
|
tx, ty, tz
|
|
)
|
|
end
|
|
|
|
-- LOOPS
|
|
|
|
function RegisterButtonHandler(button, onPressed, onReleased)
|
|
onPressed = onPressed and type(onPressed) == 'function' and onPressed
|
|
onReleased = onReleased and type(onReleased) == 'function' and onReleased
|
|
DisableControlAction(0, button, true)
|
|
local pressed = IsDisabledControlJustPressed(0, button)
|
|
local released = IsDisabledControlJustReleased(0, button)
|
|
return (pressed and onPressed and onPressed()) or (released and onReleased and onReleased())
|
|
end
|
|
|
|
|
|
local function gizmoLoop(entity, onConfirm, onUpdate, onCancel)
|
|
entity = tonumber(entity)
|
|
EnterCursorMode()
|
|
|
|
if IsEntityAPed(entity) then
|
|
SetEntityAlpha(entity, 200)
|
|
else
|
|
SetEntityDrawOutline(entity, true)
|
|
end
|
|
|
|
while gizmoEnabled and DoesEntityExist(entity) do
|
|
Wait(0)
|
|
|
|
DisableControlAction(0, 24, true) -- lmb
|
|
DisableControlAction(0, 25, true) -- rmb
|
|
DisableControlAction(0, 140, true) -- r
|
|
DisableControlAction(0, 245, true) --t
|
|
DisablePlayerFiring(PlayerId(), true)
|
|
SetEntityCollision(entity, false, false)
|
|
FreezeEntityPosition(entity, false)
|
|
local matrixBuffer = makeEntityMatrix(entity)
|
|
local changed = DrawGizmo(matrixBuffer:Buffer(), 'Editor1')
|
|
if changed then
|
|
print(GetEntityCoords(entity))
|
|
applyEntityMatrix(entity, matrixBuffer)
|
|
SetEntityCoords(entity, GetEntityCoords(entity))
|
|
end
|
|
|
|
if onUpdate and type(onUpdate) == 'function' then onUpdate(entity, GetEntityCoords(entity), GetEntityRotation(entity)) end
|
|
--controles
|
|
--frontend confirm 18 [ENTER]
|
|
RegisterButtonHandler(191, function()
|
|
if not gizmoEnabled then return end
|
|
gizmoEnabled = false
|
|
if onConfirm and type(onConfirm) == 'function' then onConfirm(entity, GetEntityCoords(entity), GetEntityRotation(entity)) end
|
|
end)
|
|
--frontend cancel 202 [BACKSPACE]
|
|
RegisterButtonHandler(202, function()
|
|
if not gizmoEnabled then return end
|
|
gizmoEnabled = false
|
|
if onCancel and type(onCancel) == 'function' then onCancel(entity, GetEntityCoords(entity), GetEntityRotation(entity)) end
|
|
CreateThread(function()
|
|
local timeout = 100
|
|
while timeout > 0 do
|
|
DisableFrontendThisFrame()
|
|
timeout = timeout - 1
|
|
Wait(1)
|
|
end
|
|
end)
|
|
end)
|
|
--register e
|
|
RegisterButtonHandler(38, function()
|
|
if not gizmoEnabled then return end
|
|
gizmoEnabled = false
|
|
local playerPos = GetEntityCoords(PlayerPedId())
|
|
local entityPos = GetFinalRenderedCamCoord()
|
|
local depth = #(playerPos - entityPos) -- Vector3 distance
|
|
PlaceableObject.Create({spawned = entity}, nil, onConfirm, onUpdate, onCancel, {depth = depth})
|
|
end)
|
|
|
|
--ctrl
|
|
RegisterButtonHandler(36, function()
|
|
if not gizmoEnabled then return end
|
|
LeaveCursorMode()
|
|
end, function()
|
|
EnterCursorMode()
|
|
end)
|
|
|
|
RegisterButtonHandler(19, function()
|
|
if not gizmoEnabled then return end
|
|
PlaceObjectOnGroundProperly_2(currentEntity)
|
|
end)
|
|
FreezeEntityPosition(entity, false)
|
|
end
|
|
|
|
LeaveCursorMode()
|
|
|
|
if DoesEntityExist(entity) then
|
|
if IsEntityAPed(entity) then SetEntityAlpha(entity, 255) end
|
|
SetEntityDrawOutline(entity, false)
|
|
end
|
|
|
|
gizmoEnabled = false
|
|
currentEntity = nil
|
|
return true
|
|
end
|
|
|
|
-- local function textUILoop()
|
|
-- CreateThread(function()
|
|
-- while gizmoEnabled do
|
|
-- Wait(100)
|
|
-- local scaleText = (enableScale and '[S] - Scale Mode \n') or ''
|
|
-- lib.showTextUI(
|
|
-- ('Current Mode: %s | %s \n'):format(currentMode, (isRelative and 'Relative') or 'World') ..
|
|
-- '[W] - Translate Mode \n' ..
|
|
-- '[R] - Rotate Mode \n' ..
|
|
-- scaleText ..
|
|
-- '[Q] - Relative/World \n' ..
|
|
-- '[LALT] - Snap To Ground \n' ..
|
|
-- '[ENTER] - Done Editing \n'
|
|
-- )
|
|
-- end
|
|
-- lib.hideTextUI()
|
|
-- end)
|
|
-- end
|
|
|
|
-- EXPORTS
|
|
|
|
|
|
local function useGizmo(entity, onConfirm, onUpdate, onCancel)
|
|
gizmoEnabled = true
|
|
currentEntity = entity
|
|
-- textUILoop()
|
|
gizmoLoop(entity, onConfirm, onUpdate, onCancel)
|
|
|
|
return {
|
|
handle = entity,
|
|
position = GetEntityCoords(entity),
|
|
rotation = GetEntityRotation(entity)
|
|
}
|
|
end
|
|
|
|
exports("useGizmo", useGizmo)
|
|
|
|
Gizmo = {}
|
|
local running = false
|
|
function Gizmo.UseGizmo(obj, onConfirm, onUpdate, onCancel)
|
|
if running then return end
|
|
running = true
|
|
|
|
--instructional buttons
|
|
local scaleform = Scaleform.SetupInstructionalButtons({
|
|
{type = "CLEAR_ALL"},
|
|
{type = "SET_CLEAR_SPACE", int = 200},
|
|
{type = "SET_DATA_SLOT", name = 'Translate:', keyIndex = {245}, int = 1},
|
|
{type = "SET_DATA_SLOT", name = 'Scale:', keyIndex = {33}, int = 2},
|
|
{type = "SET_DATA_SLOT", name = 'Rotate:', keyIndex = {45}, int = 3},
|
|
{type = "SET_DATA_SLOT", name = 'Snap to Ground:', keyIndex = {19}, int = 4},
|
|
{type = "SET_DATA_SLOT", name = 'Toggle Camera:', keyIndex = {36}, int = 5},
|
|
{type = "SET_DATA_SLOT", name = 'Cancel Placement:', keyIndex = {202}, int = 6},
|
|
{type = "SET_DATA_SLOT", name = 'Toggle Gizmo:', keyIndex = {38}, int = 7},
|
|
{type = "DRAW_INSTRUCTIONAL_BUTTONS"},
|
|
{type = "SET_BACKGROUND_COLOUR"},
|
|
})
|
|
local quit = false
|
|
CreateThread(function()
|
|
while not quit do
|
|
DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255, 0)
|
|
Wait(0)
|
|
end
|
|
running = false
|
|
end)
|
|
|
|
quit = useGizmo(obj, onConfirm, onUpdate, onCancel)
|
|
running = false
|
|
end
|
|
exports("UseGizmo", Gizmo.UseGizmo)
|
|
|
|
|
|
--got rid of the cancer
|
|
-- lib.addKeybind({
|
|
-- name = '_gizmoLocal',
|
|
-- description = 'toggle gizmo to be local to the entity instead of world',
|
|
-- defaultKey = 'Q',
|
|
-- onPressed = function(self)
|
|
-- if not gizmoEnabled then return end
|
|
-- isRelative = not isRelative
|
|
-- ExecuteCommand('+gizmoLocal')
|
|
-- end,
|
|
-- onReleased = function (self)
|
|
-- ExecuteCommand('-gizmoLocal')
|
|
-- end
|
|
-- })
|
|
|
|
-- lib.addKeybind({
|
|
-- name = 'gizmoclose',
|
|
-- description = 'close gizmo',
|
|
-- defaultKey = 'RETURN',
|
|
-- onPressed = function(self)
|
|
-- if not gizmoEnabled then return end
|
|
-- gizmoEnabled = false
|
|
-- end,
|
|
-- })
|
|
|
|
-- lib.addKeybind({
|
|
-- name = 'gizmoSnapToGround',
|
|
-- description = 'snap current gizmo object to floor/surface',
|
|
-- defaultKey = 'LMENU',
|
|
-- onPressed = function(self)
|
|
-- if not gizmoEnabled then return end
|
|
-- PlaceObjectOnGroundProperly_2(currentEntity)
|
|
-- end,
|
|
-- })
|
|
|
|
-- if enableScale then
|
|
-- lib.addKeybind({
|
|
-- name = '_gizmoScale',
|
|
-- description = 'Sets mode for the gizmo to scale',
|
|
-- defaultKey = 'S',
|
|
-- onPressed = function(self)
|
|
-- if not gizmoEnabled then return end
|
|
-- currentMode = 'Scale'
|
|
-- ExecuteCommand('+gizmoScale')
|
|
-- end,
|
|
-- onReleased = function (self)
|
|
-- ExecuteCommand('-gizmoScale')
|
|
-- end
|
|
-- })
|
|
-- end
|
|
|
|
|
|
--onreousrcestop
|
|
|
|
AddEventHandler('onResourceStop', function(resource)
|
|
if resource == GetCurrentResourceName() then
|
|
if not gizmoEnabled then return end
|
|
gizmoEnabled = false
|
|
LeaveCursorMode()
|
|
end
|
|
end)
|
|
|
|
return Gizmo |