Sample provided in Discord's Cyberpunk 2077 Modding Community within the channel "-cet-snippets" by keanuWheeze.

Main file:

Spoiler:  
Show

local interactionUI = require("interactionUI")
registerForEvent("onInit", function()
    interactionUI.init()
end)
registerForEvent("onUpdate", function()
    interactionUI.update()
end)
registerHotkey("customDialog", "Show Dialog", function()
    -- Setup choices
    local choice1 = interactionUI.createChoice("Choice 1", TweakDBInterface.GetChoiceCaptionIconPartRecord("ChoiceCaptionParts.ShowerIcon"), gameinteractionsChoiceType.QuestImportant) -- Icon and choiceType are optional
    local choice2 = interactionUI.createChoice("Choice 2", TweakDBInterface.GetChoiceCaptionIconPartRecord("ChoiceCaptionParts.SitIcon"), gameinteractionsChoiceType.AlreadyRead)
    local choice3 = interactionUI.createChoice("Choice 3", TweakDBInterface.GetChoiceCaptionIconPartRecord("ChoiceCaptionParts.WaitIcon"), gameinteractionsChoiceType.Selected)
    -- Setup, set and show hub
    local hub = interactionUI.createHub("Hub Name", {choice1, choice2, choice3}) -- Create hub and give it the list of choices
    interactionUI.setupHub(hub) -- Set the hub
    interactionUI.showHub() -- Show the previously set hub
    -- Setup callbacks
    interactionUI.callbacks[1] = function()
        print("Choice 1 used")
    end
    interactionUI.callbacks[2] = function()
        print("Choice 2 used")
    end
    interactionUI.callbacks[3] = function()
        print("Choice 3 used")
        interactionUI.hideHub() -- Hide the dialog
    end
end)


File with functions:
Spoiler:  
Show

local ui =  {
    baseControler = nil,
    hub = nil,
    callbacks = {},
    hubShown = false,
    selectedIndex = 0,
    input = false
}
---@param localizedName string
---@param icon gamedataChoiceCaptionIconPart_Record
---@param choiceType gameinteractionsChoiceType
---@return gameinteractionsvisListChoiceData
function ui.createChoice(localizedName, icon, choiceType) -- Creates and returns a choice
    local choice = gameinteractionsvisListChoiceData.new()
    choice.localizedName = localizedName or "Choice"
    choice.inputActionName = "None"
    if icon then
        local part = gameinteractionsChoiceCaption.new()
        part:AddPartFromRecord(icon)
        choice.captionParts = part
    end
    if choiceType then
        local choiceT = gameinteractionsChoiceTypeWrapper.new()
        choiceT:SetType(choiceType)
        choice.type = choiceT
    end
    return choice
end
---@param title string
---@param choices table
---@param activityState gameinteractionsvisEVisualizerActivityState
---@return gameinteractionsvisListChoiceHubData
function ui.createHub(title, choices, activityState) -- Creates and returns a hub
    local hub = gameinteractionsvisListChoiceHubData.new()
    hub.title = title or "Title"
    hub.choices = choices or {}
    hub.activityState = activityState or gameinteractionsvisEVisualizerActivityState.Active
    hub.hubPriority = 1
    hub.id = 69420 + math.random(99999)
    return hub
end
---@param hub gameinteractionsvisListChoiceHubData
function ui.setupHub(hub) -- Setup interaction hub
    ui.hub = hub
end
function ui.showHub() -- Shows the hub previously set using setupHub()
    if not ui.hub or not ui.baseControler then return end
    local data = DialogChoiceHubs.new()
    data.choiceHubs = {ui.hub}
    ui.baseControler.AreDialogsOpen = #data.choiceHubs > 0
    ui.baseControler.dialogIsScrollable = #data.choiceHubs > 1
    ui.baseControler:OnDialogsSelectIndex(0)
    ui.baseControler:UpdateDialogsData(data)
    ui.baseControler:OnInteractionsChanged()
    ui.baseControler:UpdateListBlackboard()
    ui.baseControler:OnDialogsActivateHub(ui.hub.id)
    ui.hubShown = true
    ui.selectedIndex = 0
end
function ui.hideHub() -- Hides the hub
    if not ui.hub or not ui.baseControler then return end
    local data = DialogChoiceHubs.new()
    ui.baseControler:UpdateDialogsData(data)
    ui.baseControler:OnInteractionsChanged()
    ui.baseControler:UpdateListBlackboard()
    ui.hubShown = false
end
function ui.registerChoiceCallback(choiceIndex, callback) -- Register a callback for a choice via index, starting at 1
    ui.callbacks[choiceIndex] = callback
end
function ui.clearCallbacks() -- Remove all callbacks
    ui.callbacks = {}
end
function ui.init() -- Register needed observers
    Observe("InteractionUIBase", "OnDialogsData", function(this)
        ui.baseControler = this
    end)
    Observe("InteractionUIBase", "OnInitialize", function(this)
        ui.baseControler = this
    end)
    Observe('PlayerPuppet', 'OnAction', function(_, action)
        if ui.input or not ui.hubShown then return end
        local actionName = Game.NameToString(action:GetName(action))
        local actionType = action:GetType(action).value
        if actionName == 'ChoiceScrollUp' then
            if actionType == 'BUTTON_PRESSED'then
                ui.selectedIndex = ui.selectedIndex - 1
                if ui.selectedIndex < 0 then
                    ui.selectedIndex = #ui.hub.choices - 1
                end
                ui.input = true
            end
        elseif actionName == 'ChoiceScrollDown' then
            if actionType == 'BUTTON_PRESSED'then
                ui.selectedIndex = ui.selectedIndex + 1
                if ui.selectedIndex > #ui.hub.choices - 1 then
                    ui.selectedIndex = 0
                end
                ui.input = true
            end
        elseif actionName == 'ChoiceApply' then
            if actionType == 'BUTTON_PRESSED'then
                if ui.callbacks[ui.selectedIndex + 1] then
                    ui.callbacks[ui.selectedIndex + 1]()
                end
                ui.input = true
            end
        end
    end)
    Override("InteractionUIBase", "OnDialogsSelectIndex", function(_, idx, wrapped) -- Avoid index getting set by game
        if ui.hubShown then
            if idx ~= ui.selectedIndex then
                return
            end
        end
        wrapped(idx)
    end)
    Override("InteractionUIBase", "OnDialogsData", function(_, value, wrapped) -- Avoid interaction getting overriden by game
        if ui.hubShown then return end
        wrapped(value)
    end)
    Override("dialogWidgetGameController", "OnDialogsActivateHub", function(_, id, wrapped) -- Avoid interaction getting overriden by game
        if ui.hubShown and id ~= ui.hub.id then return end
        wrapped(id)
    end)
end
function ui.update() -- Run ever frame to avoid unwanted changes
    if ui.hubShown then
        Game.GetBlackboardSystem():Get(GetAllBlackboardDefs().UIInteractions):SetInt(GetAllBlackboardDefs().UIInteractions.SelectedIndex, ui.selectedIndex)
    end
    ui.input = false -- Avoid double input
end
return ui

Article information

Added on

Edited on

Written by

esuriddick