Search code examples
lualua-table

Creating a dynamic array in Lua


I'm new to programming with Lua so please forgive me my stupidity. I'm trying to create a dynamic array using Lua for QBcore (FiveM), but I keep ending up with index (?) when I create the array.

Code with index

This makes the menu within the game not working (I think..).

The code I have is:

local mdtMenu = nil
RegisterNetEvent('qb-radialmenu:client:onRadialmenuOpen', function()
    if mdtMenu ~= nil then
        exports['qb-radialmenu']:RemoveOption(mdtMenu)
        mdtMenu = nil
    end
    
    local myData = QBCore.Functions.GetPlayerData()
    local myJob = myData.job
    local menuItems = createRadialJobExtrasMenu(myJob.onduty)
    
    if myJob.name == "ems" or myJob.name == "police" then
        mdtMenu = exports['qb-radialmenu']:AddOption({
            menuItems
        }, mdtMenu)
    end
    
    print(QBCore.Debug(mdtMenu))
    --print(mdtMenu)
end)

The function used in above code:

function createRadialJobExtrasMenu(onduty)
    local extra_menuItems = {}
    local mdtMenu = {
        id = 'job_extras',
        title = 'Job Extras',
        icon = "square-plus",
        items = {
            open_mdt = {
                title = 'Open MDT menu',
                icon = "tablet-screen-button",
                type = 'command',
                event = "mdt",
                shouldClose = true
            },
            my_data = {
                title = 'My Data',
                icon = "person",
                type = 'command',
                event = "myaddons-myinfo",
                shouldClose = true
            },
            mdt_signonoff = {},
        }
    }
    
    if onduty then
        mdt_signonoff = {
            title = 'Sign Off (Duty)',
            icon = "circle-xmark",
            type = 'command',
            event = 'myaddons-toggleduty',
            shouldClose = true
        }
    else
        mdt_signonoff = {
            title = 'Sign On (Duty)',
            icon = "check-to-slot",
            type = 'command',
            event = 'myaddons-toggleduty',
            shouldClose = true
        }
    end
    
    table.insert(mdtMenu.items.mdt_signonoff, mdt_signonoff)
    
    return mdtMenu
end

The menu I expect when on-duty (note the third entry in items:):

local mdtMenu = nil
RegisterNetEvent('qb-radialmenu:client:onRadialmenuOpen', function()
    if mdtMenu ~= nil then
        exports['qb-radialmenu']:RemoveOption(mdtMenu)
        mdtMenu = nil
    end
    
    local myData = QBCore.Functions.GetPlayerData()
    local myJob = myData.job
    
    if myJob.name == "ems" or myJob.name == "police" then
            mdtMenu = exports['qb-radialmenu']:AddOption({
                id = 'job_extras',
                title = 'Job Extras',
                icon = "square-plus",
                items = {
                    {
                        id = 'open_mdt',
                        title = 'Open MDT menu',
                        icon = "tablet-screen-button",
                        type = 'command',
                        event = "mdt",
                        shouldClose = true
                    },
                    {
                        id = 'my_data',
                        title = 'My Data',
                        icon = "person",
                        type = 'command',
                        event = "myaddons-myinfo",
                        shouldClose = true
                    },
                    {
                        id = 'mdt_signoff',
                        title = 'Sign Off (Duty)',
                        icon = "circle-xmark",
                        type = 'command',
                        event = 'myaddons-toggleduty',
                        shouldClose = true
                    },
                }
            }, mdtMenu)
    end
end)

And when off-duty (note the third entry in items:):

local mdtMenu = nil
RegisterNetEvent('qb-radialmenu:client:onRadialmenuOpen', function()
    if mdtMenu ~= nil then
        exports['qb-radialmenu']:RemoveOption(mdtMenu)
        mdtMenu = nil
    end
    
    local myData = QBCore.Functions.GetPlayerData()
    local myJob = myData.job
    
    if myJob.name == "ems" or myJob.name == "police" then
            mdtMenu = exports['qb-radialmenu']:AddOption({
                id = 'job_extras',
                title = 'Job Extras',
                icon = "square-plus",
                items = {
                    {
                        id = 'open_mdt',
                        title = 'Open MDT menu',
                        icon = "tablet-screen-button",
                        type = 'command',
                        event = "mdt",
                        shouldClose = true
                    },
                    {
                        id = 'my_data',
                        title = 'My Data',
                        icon = "person",
                        type = 'command',
                        event = "myaddons-myinfo",
                        shouldClose = true
                    },
                    {
                        id = 'mdt_signon',
                        title = 'Sign On (Duty)',
                        icon = "check-to-slot",
                        type = 'command',
                        event = 'myaddons-toggleduty',
                        shouldClose = true
                    },
                }
            }, mdtMenu)
    end
end)

I hope my question makes any sence.

Edit: Changed the code as I assume that @Vlad means, but this still leaves me with 1 index (?) number.

Edit2: The only correct structure is like the code below:

local mdtMenu = nil
RegisterNetEvent('qb-radialmenu:client:onRadialmenuOpen', function()
    if mdtMenu ~= nil then
        exports['qb-radialmenu']:RemoveOption(mdtMenu)
        mdtMenu = nil
    end
    
    local myData = QBCore.Functions.GetPlayerData()
    local myJob = myData.job
    
    if myJob.name == "ems" or myJob.name == "police" then
        mdtMenu = exports['qb-radialmenu']:AddOption({
            id = 'job_extras',
            title = 'Job Extras',
            icon = "square-plus",
            items = {
                {
                    id = 'open_mdt',
                    title = 'Open MDT menu',
                    icon = "tablet-screen-button",
                    type = 'command',
                    event = "mdt",
                    shouldClose = true
                },
                {
                    id = 'my_data',
                    title = 'My Data',
                    icon = "person",
                    type = 'command',
                    event = "myaddons-myinfo",
                    shouldClose = true
                },
            }
        }, mdtMenu)
    end
end)

But when I dynamically try to add a menu item with code below, I get an error.

if myJob.onduty then
        table.insert(mdtMenu.items, {id = 'mdt_signoff', title = 'Sign Off (Duty)', icon = "circle-xmark", type = 'command', event = 'myaddons-toggleduty', shouldClose = true})
    else
        table.insert(mdtMenu.items, {id = 'mdt_signon', title = 'Sign On (Duty)', icon = "check-to-slot", type = 'command', event = 'myaddons-toggleduty', shouldClose = true})
    end

The complete codeblock:

local mdtMenu = nil
RegisterNetEvent('qb-radialmenu:client:onRadialmenuOpen', function()
    if mdtMenu ~= nil then
        exports['qb-radialmenu']:RemoveOption(mdtMenu)
        mdtMenu = nil
    end
    
    local myData = QBCore.Functions.GetPlayerData()
    local myJob = myData.job
    
    if myJob.name == "ems" or myJob.name == "police" then
        mdtMenu = exports['qb-radialmenu']:AddOption({
            id = 'job_extras',
            title = 'Job Extras',
            icon = "square-plus",
            items = {
                {
                    id = 'open_mdt',
                    title = 'Open MDT menu',
                    icon = "tablet-screen-button",
                    type = 'command',
                    event = "mdt",
                    shouldClose = true
                },
                {
                    id = 'my_data',
                    title = 'My Data',
                    icon = "person",
                    type = 'command',
                    event = "myaddons-myinfo",
                    shouldClose = true
                },
            }
        }, mdtMenu)
    end
    
    if myJob.onduty then
        table.insert(mdtMenu.items, {id = 'mdt_signoff', title = 'Sign Off (Duty)', icon = "circle-xmark", type = 'command', event = 'myaddons-toggleduty', shouldClose = true})
    else
        table.insert(mdtMenu.items, {id = 'mdt_signon', title = 'Sign On (Duty)', icon = "check-to-slot", type = 'command', event = 'myaddons-toggleduty', shouldClose = true})
    end
end)

The error I'm getting:

Error

It IS working with below code, but that means that I have to re-create the complete menu for each dynamic menu item, making the menu not-so-dynamic:

local mdtMenu = nil
RegisterNetEvent('qb-radialmenu:client:onRadialmenuOpen', function()
    if mdtMenu ~= nil then
        exports['qb-radialmenu']:RemoveOption(mdtMenu)
        mdtMenu = nil
    end
    
    local myData = QBCore.Functions.GetPlayerData()
    local myJob = myData.job
    
    if myJob.name == "ems" or myJob.name == "police" then
        if myJob.onduty then
            mdtMenu = exports['qb-radialmenu']:AddOption({
                id = 'job_extras',
                title = 'Job Extras',
                icon = "square-plus",
                items = {
                    {
                        id = 'open_mdt',
                        title = 'Open MDT menu',
                        icon = "tablet-screen-button",
                        type = 'command',
                        event = "mdt",
                        shouldClose = true
                    },
                    {
                        id = 'my_data',
                        title = 'My Data',
                        icon = "person",
                        type = 'command',
                        event = "myaddons-myinfo",
                        shouldClose = true
                    },
                    {
                        id = 'mdt_signoff',
                        title = 'Sign Off (Duty)',
                        icon = "circle-xmark",
                        type = 'command',
                        event = 'myaddons-toggleduty',
                        shouldClose = true
                    },
                }
            }, mdtMenu)
        else
            mdtMenu = exports['qb-radialmenu']:AddOption({
                id = 'job_extras',
                title = 'Job Extras',
                icon = "square-plus",
                items = {
                    {
                        id = 'open_mdt',
                        title = 'Open MDT menu',
                        icon = "tablet-screen-button",
                        type = 'command',
                        event = "mdt",
                        shouldClose = true
                    },
                    {
                        id = 'my_data',
                        title = 'My Data',
                        icon = "person",
                        type = 'command',
                        event = "myaddons-myinfo",
                        shouldClose = true
                    },
                    {
                        id = 'mdt_signon',
                        title = 'Sign On (Duty)',
                        icon = "check-to-slot",
                        type = 'command',
                        event = 'myaddons-toggleduty',
                        shouldClose = true
                    },
                }
            }, mdtMenu)
        end
    end
end)

Edit3: Below code seems to be working (thanks @dt192!!):

local mdtMenu = nil
RegisterNetEvent('qb-radialmenu:client:onRadialmenuOpen', function()
    if mdtMenu ~= nil then
        exports['qb-radialmenu']:RemoveOption(mdtMenu)
        mdtMenu = nil
    end
    
    local myData = QBCore.Functions.GetPlayerData()
    local myJob = myData.job
    
    if myJob.name == "ems" or myJob.name == "police" then
        local myTable = createRadialJobExtrasMenu(myJob.onduty)
        
        mdtMenu = exports['qb-radialmenu']:AddOption(myTable, mdtMenu)
    end
end)

AddEventHandler('onResourceStop', function(resource)
    if mdtMenu ~= nil then
        exports['qb-radialmenu']:RemoveOption(mdtMenu)
        mdtMenu = nil
    end
end)

With the function:

function createRadialJobExtrasMenu(onduty)
    local tbl = {
        id = 'job_extras',
        title = 'Job Extras',
        icon = "square-plus",
        items = {
            {
                id = 'open_mdt',
                title = 'Open MDT menu',
                icon = "tablet-screen-button",
                type = 'command',
                event = "mdt",
                shouldClose = true
            },
            {
                id = 'my_data',
                title = 'My Data',
                icon = "person",
                type = 'command',
                event = "myaddons-myinfo",
                shouldClose = true
            },
            {
                id = 'mdt_signon',
                title = 'Sign On (Duty)',
                icon = "check-to-slot",
                type = 'command',
                event = 'myaddons-toggleduty',
                shouldClose = true
            },
        }
    }

    if onduty then
        tbl.items[3].id = 'mdt_signoff'
        tbl.items[3].title = 'Sign Off (Duty)'
        tbl.items[3].icon = 'circle-xmark'
    end
    
    return tbl
end

Solution

  • You could probably do something like this

        local tbl = {
            id = 'job_extras',
            title = 'Job Extras',
            icon = "square-plus",
            items = {
                {
                    id = 'open_mdt',
                    title = 'Open MDT menu',
                    icon = "tablet-screen-button",
                    type = 'command',
                    event = "mdt",
                    shouldClose = true
                },
                {
                    id = 'my_data',
                    title = 'My Data',
                    icon = "person",
                    type = 'command',
                    event = "myaddons-myinfo",
                    shouldClose = true
                },
                {
                    id = 'mdt_signon',
                    title = 'Sign On (Duty)',
                    icon = "check-to-slot",
                    type = 'command',
                    event = 'myaddons-toggleduty',
                    shouldClose = true
                },
            }
        }
    
        if myJob.onduty then
            tbl.items[3].id = 'mdt_signoff'
            tbl.items[3].title = 'Sign Off (Duty)'
            tbl.items[3].icon = 'circle-xmark'
        end
    
        mdtMenu = exports['qb-radialmenu']:AddOption(tbl, mdtMenu)
    

    You could also just inline the checks like

        mdtMenu = exports['qb-radialmenu']:AddOption({
            id = 'job_extras',
            title = 'Job Extras',
            icon = "square-plus",
            items = {
                {
                    id = 'open_mdt',
                    title = 'Open MDT menu',
                    icon = "tablet-screen-button",
                    type = 'command',
                    event = "mdt",
                    shouldClose = true
                },
                {
                    id = 'my_data',
                    title = 'My Data',
                    icon = "person",
                    type = 'command',
                    event = "myaddons-myinfo",
                    shouldClose = true
                },
                {
                    id = myJob.onduty and 'mdt_signoff' or 'mdt_signon',
                    title = myJob.onduty and 'Sign Off (Duty)' or 'Sign On (Duty)',
                    icon = myJob.onduty and 'circle-xmark' or 'check-to-slot',
                    type = 'command',
                    event = 'myaddons-toggleduty',
                    shouldClose = true
                },
            }
        }, mdtMenu)
    

    or (not sure which version you are using at this point)

            mdtMenu.item.mdt_signonoff = {
                title = myJob.onduty and 'Sign Off (Duty)' or 'Sign On (Duty)',
                icon = myJob.onduty and 'circle-xmark' or 'check-to-slot',
                type = 'command',
                event = 'myaddons-toggleduty',
                shouldClose = true
            }