Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scaleform class #698

Merged
merged 5 commits into from
Jan 30, 2025
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 231 additions & 0 deletions imports/scaleform/client.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
---@class renderTargetTable
---@field name string
---@field model string|number

---@class detailsTable
---@field name string
---@field fullScreen? boolean
---@field x? number
---@field y? number
---@field width? number
---@field height? number
---@field renderTarget? renderTargetTable

---@class Scaleform : OxClass
---@field scaleform number
---@field draw boolean
---@field target number
---@field targetName string
---@field handle number
---@field fullScreen boolean
lib.scaleform = lib.class('Scaleform')

--- Converts the arguments into data types usable by scaleform
---@param argsTable table
local function convertArgs(argsTable)
for i = 1, #argsTable do
local arg = argsTable[i]
local argType = type(arg)

if argType == 'string' then
ScaleformMovieMethodAddParamPlayerNameString(arg)
elseif argType == 'number' then
if math.type(arg) == 'integer' then
ScaleformMovieMethodAddParamInt(arg)
else
ScaleformMovieMethodAddParamFloat(arg)
end
elseif argType == 'boolean' then
ScaleformMovieMethodAddParamBool(arg)
else
error(('Unsupported Parameter type [%s]'):format(argType))
end
end
end

---@param expectedType 'boolean' | 'integer' | 'string'
---@return boolean | integer | string
---@description Awaits the return value, and converts it to a usable data type
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for @description here and in other places.

local function retrieveReturnValue(expectedType)
local result = EndScaleformMovieMethodReturnValue()

lib.waitFor(function()
if IsScaleformMovieMethodReturnValueReady(result) then
return true
end
end, "Failed to retrieve return value", 1000)

if expectedType == "integer" then
return GetScaleformMovieMethodReturnValueInt(result)
elseif expectedType == "boolean" then
return GetScaleformMovieMethodReturnValueBool(result)
else
return GetScaleformMovieMethodReturnValueString(result)
end
end

---@param details detailsTable | string
---@return nil
---@description Create a new scaleform class
function lib.scaleform:constructor(details)
details = type(details) == "table" and details or { name = details }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've mixed ' and " for strings in a number of places. We generally use '.


local scaleform = lib.requestScaleformMovie(details.name)
if not scaleform then
return error(('Failed to request scaleform movie - [%s]'):format(details.name))
end

self.handle = scaleform
self.draw = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use more descriptive property names like isDrawing or such.
You could also use private properties or otherwise mark the property as private for LLS.


self.fullScreen = details.fullScreen ~= nil and details.fullScreen or true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will always be true.

self.x = details.x or 0
self.y = details.y or 0
self.width = details.width or 0
self.height = details.height or 0

if details.renderTarget then
self:setRenderTarget(details.renderTarget.name, details.renderTarget.model)
end
end

---@param name string
---@param args? table
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not be marked as optional, as your type check ensures it is a table. You should also clarify that it's an array-like table and which types are valid - (number | string | boolean)[]

---@param returnValue? string
---@return any
---@description Call a scaleform function, with optional args or return value.
function lib.scaleform:callMethod(name, args, returnValue)
if not self.handle then
return error('Scaleform handle is nil')
end

if type(args) ~= 'table' then
return error('Args must be a table')
end
Copy link
Member

@thelindat thelindat Jan 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These error messages (and elsewhere) could be formatted a little better. I've used a typeError function in some other parts of ox_lib.


BeginScaleformMovieMethod(self.handle, name)

if args then
convertArgs(args)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

args cannot be falsey, so this if statement is redundant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I corrected the code because args can be false. Here is a modified code from qbx_police where I tested the scaleform class.

local function setupIntructionalScaleform()
    if not currentScaleform then
        return
    end

    currentScaleform:callMethod('CLEAR_ALL')
    currentScaleform:callMethod('SET_CLEAR_SPACE', { 200 })
    currentScaleform:callMethod('SET_DATA_SLOT', { 1, GetControlInstructionalButton(1, 177, true), 'Fermer la caméra' })
    currentScaleform:callMethod('DRAW_INSTRUCTIONAL_BUTTONS')
    currentScaleform:callMethod('SET_BACKGROUND_COLOUR', { 0, 0, 0, 80 })
end

end

if returnValue then
return retrieveReturnValue(returnValue)
end

EndScaleformMovieMethod()
end

---@param isFullscreen boolean
---@return nil
---@description Set the scaleform to render in full screen
function lib.scaleform:setFullScreen(isFullscreen)
self.fullScreen = isFullscreen
end

---@param x number
---@param y number
---@param width number
---@param height number
---@return nil
---@description Set the properties of the scaleform (Requires SetFullScreen to be false)
function lib.scaleform:setProperties(x, y, width, height)
if self.fullScreen then
return error('Cannot set properties when full screen is enabled')
end

self.x = x
self.y = y
self.width = width
self.height = height
end

---@param name string
---@param model string|number
---@return nil
---@description Create a render target for the scaleform - optional , only if you want to render the scaleform in 3D
function lib.scaleform:setRenderTarget(name, model)
if self.target then
ReleaseNamedRendertarget(self.targetName)
end

if type(model) == 'string' then
model = joaat(model)
end

if not IsNamedRendertargetRegistered(name) then
RegisterNamedRendertarget(name, false)

if not IsNamedRendertargetLinked(model) then
LinkNamedRendertarget(model)
end

self.target = GetNamedRendertargetRenderId(name)
self.targetName = name
end
end

---@return nil
---@description Set The Scaleform to draw
function lib.scaleform:startDrawing()
if self.draw then
return error("Scaleform Already Drawing")
end

self.draw = true
CreateThread(function()
while self.draw do
if self.target then
SetTextRenderId(self.target)
SetScriptGfxDrawOrder(4)
SetScriptGfxDrawBehindPausemenu(true)
SetScaleformFitRendertarget(self.handle, true)
end

if self.fullScreen then
DrawScaleformMovieFullscreen(self.handle, 255, 255, 255, 255, 0)
else
if not self.x or not self.y or not self.width or not self.height then
error('Properties not set for scaleform')
DrawScaleformMovieFullscreen(self.handle, 255, 255, 255, 255, 0)
else
DrawScaleformMovie(self.handle, self.x, self.y, self.width, self.height, 255, 255, 255, 255, 0)
end
end

if self.target then
SetTextRenderId(1)
end

Wait(0)
end
end)
end

---@return nil
---@description stop the scaleform from drawing, use this to only temporarily disable it, use Dispose otherwise.
function lib.scaleform:stopDrawing()
if not self.draw then
return
end
self.draw = false
end

---@return nil
---@description Disposes of the scaleform and reset the values
function lib.scaleform:dispose()
if self.handle then
SetScaleformMovieAsNoLongerNeeded(self.handle)
end

if self.target then
ReleaseNamedRendertarget(self.targetName)
end

self.handle = nil
self.target = nil
self.draw = false
end

---@return Scaleform
return lib.scaleform