Advertisement
abitoftaste

MenuDialog.lua

Dec 10th, 2024
35
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 7.83 KB | None | 0 0
  1. local common = require("UI Expansion.common")
  2.  
  3. local GUI_ID_MenuDialog = tes3ui.registerID("MenuDialog")
  4. local GUI_ID_MenuDialog_a_topic = tes3ui.registerID("MenuDialog_a_topic")
  5. local GUI_ID_MenuDialog_answer_block = tes3ui.registerID("MenuDialog_answer_block")
  6. local GUI_ID_MenuDialog_hyper = tes3ui.registerID("MenuDialog_hyper")
  7. local GUI_ID_MenuDialog_scroll_pane = tes3ui.registerID("MenuDialog_scroll_pane")
  8. local GUI_ID_MenuDialog_topics_pane = tes3ui.registerID("MenuDialog_topics_pane")
  9.  
  10. local GUI_ID_PartScrollPane_pane = tes3ui.registerID("PartScrollPane_pane")
  11.  
  12. local GUI_PID_ChoiceNumbered = tes3ui.registerProperty("UIEx:ChoiceRegistered")
  13.  
  14. local GUI_Palette_TopicSeen = common.getColor(common.config.dialogueTopicSeenColor)
  15. local GUI_Palette_TopicUnique = common.getColor(common.config.dialogueTopicUniqueColor)
  16.  
  17. ----------------------------------------------------------------------------------------------------
  18. -- Dialogue: Adds colorization to show what topics provide new and unique responses.
  19. ----------------------------------------------------------------------------------------------------
  20.  
  21. --- Check to see if the player has used a number key to select a response.
  22. --- @param e keyDownEventData
  23. local function checkForAnswerHotkey(e)
  24.     -- Make sure we're in the dialogue menu.
  25.     local topMenu = tes3.getTopMenu()
  26.     if not topMenu then
  27.         return
  28.     end
  29.     if not (topMenu.id == GUI_ID_MenuDialog) then
  30.         return
  31.     end
  32.  
  33.     -- Make sure we're not doing text input.
  34.     if (common.isTextInputActive()) then
  35.         return
  36.     end
  37.  
  38.     local key = tes3.scanCodeToNumber[e.keyCode]
  39.     if not key then
  40.         return
  41.     end
  42.  
  43.     -- Do we have any answers?
  44.     local firstAnswer = topMenu:findChild("MenuDialog_answer_block")
  45.     if not firstAnswer then
  46.         return
  47.     end
  48.  
  49.     -- Get a lsit of answers.
  50.     local answers = {}
  51.     for _, child in ipairs(firstAnswer.parent.children) do
  52.         if (child.id == firstAnswer.id) then
  53.             table.insert(answers, child)
  54.         end
  55.     end
  56.  
  57.     local answer = answers[key]
  58.     if answer then
  59.         answer:triggerEvent("mouseClick")
  60.     end
  61. end
  62. event.register("keyDown", checkForAnswerHotkey)
  63.  
  64. --- Updates colors on topic lists, and registers to let us know when they are needed.
  65. --- @param e tes3uiEventData
  66. local function updateTopicsList(e)
  67.     -- If the function lacks context to the dialogue menu, look it up.
  68.     local menuDialogue = tes3ui.findMenu(GUI_ID_MenuDialog)
  69.     local textPane = menuDialogue:findChild(GUI_ID_MenuDialog_scroll_pane):findChild(GUI_ID_PartScrollPane_pane)
  70.     local topicsPane = menuDialogue:findChild(GUI_ID_MenuDialog_topics_pane):findChild(GUI_ID_PartScrollPane_pane)
  71.  
  72.     e.info = e.source:getPropertyObject("MenuDialog_UIEXP_info")
  73.     e.actor = e.source:getPropertyObject("MenuDialog_UIEXP_actor")
  74.  
  75.     -- Forward along click events to trigger dialogue as usual.
  76.     if (e.source) then
  77.         -- Were we forced out of dialogue?
  78.         if (tes3ui.findMenu(GUI_ID_MenuDialog) == nil) then
  79.             return
  80.         end
  81.  
  82.         -- Make sure that the node heard from field is always used.
  83.         if e.info then
  84.             if e.actor then
  85.                 if (e.info.firstHeardFrom == nil) then
  86.                     local el = textPane:findChild(GUI_ID_MenuDialog_answer_block)
  87.                     if el then
  88.                         if string.match(el.text, "[cC]ontinue%p*$") then
  89.                             -- a standard continue choice for long text
  90.                             -- (e.g. LGNPC background), topic should be grayed /abot
  91.                             e.info.firstHeardFrom = e.actor
  92.                         end
  93.                     else           
  94.                         e.info.firstHeardFrom = e.actor
  95.                     end
  96.                 end
  97.             end
  98.         end
  99.     end
  100.  
  101.     -- Catch events from hyperlinks.
  102.     for _, element in pairs(textPane.children) do
  103.         if (element.id == GUI_ID_MenuDialog_hyper) then
  104.             element:registerAfter("mouseClick", updateTopicsList)
  105.         end
  106.     end
  107.  
  108.     -- Get the actor that we're talking with.
  109.     local mobileActor = menuDialogue:getPropertyObject("PartHyperText_actor") --- @type tes3mobileActor
  110.     local actor = mobileActor.reference.object.baseObject --- @type tes3actor
  111.  
  112.     -- Go through and update all the topics.
  113.     for _, element in pairs(topicsPane.children) do
  114.         -- We only care about topics in this list.
  115.         if (element.id == GUI_ID_MenuDialog_a_topic) then
  116.             element.widget.idleDisabled = GUI_Palette_TopicSeen
  117.  
  118.             -- Get the info associated with this topic.
  119.             local dialogue = element:getPropertyObject("PartHyperText_dialog") --- @type tes3dialogue
  120.             local info = element:getPropertyObject("") or dialogue:getInfo({ actor = mobileActor })
  121.  
  122.             -- Update color scheme on the topic.
  123.             if (info == nil or info.firstHeardFrom) then
  124.                 element.widget.state = 2
  125.             elseif (info.actor == actor) then
  126.                 -- Topic has actor-unique dialogue, set new state.
  127.                 element.widget.state = 4
  128.                 element.widget.idleActive = GUI_Palette_TopicUnique
  129.             else
  130.                 element.widget.state = 1
  131.             end
  132.             element:triggerEvent("mouseLeave")
  133.  
  134.             -- Store objects on the element for quick reference later.
  135.             element:setPropertyObject("MenuDialog_UIEXP_info", info)
  136.             element:setPropertyObject("MenuDialog_UIEXP_actor", actor)
  137.  
  138.             -- Register an event so that we update when any topic is clicked.
  139.             element:registerAfter("mouseClick", updateTopicsList)
  140.         end
  141.     end
  142. end
  143.  
  144. local function stripPrefixNumbers(s)
  145.     local r = string.gsub(s, "^%d+[%.:]%s+", '')
  146.     if r == s then
  147.         return r
  148.     end
  149.     return stripPrefixNumbers(r)
  150. end
  151.  
  152. --- Add numbers to the dialog choices.
  153. --- @param e tes3uiEventData
  154. local function updateAnswerText(e)
  155.     local menuDialog = e.source
  156.  
  157.     local scrollPane = menuDialog:findChild(GUI_ID_MenuDialog_scroll_pane)
  158.     if (not scrollPane) then
  159.         return
  160.     end
  161.  
  162.     local answerCount = 0
  163.     local updated = false
  164.    
  165.     for _, child in ipairs(scrollPane:getContentElement().children) do
  166.         if (child.id == GUI_ID_MenuDialog_answer_block) then
  167.             answerCount = answerCount + 1
  168.  
  169.             local didBefore = child:getPropertyBool(GUI_PID_ChoiceNumbered)
  170.             if (not didBefore) then
  171.                 child:setPropertyBool(GUI_PID_ChoiceNumbered, true)
  172.  
  173.                 child:registerAfter("mouseClick", updateTopicsList)
  174.                
  175.                 -- strip numeric prefixes if already present in .es[mp] choice command text /abot
  176.                 -- bad behavior example:
  177.                 -- https://www.nexusmods.com/morrowind/mods/55445?tab=posts&jump_to_comment=147290882&BH=0
  178.                 child.text = stripPrefixNumbers(child.text)
  179.                
  180.                 child.text = string.format("%d. %s", answerCount, child.text)
  181.                 updated = true
  182.             end
  183.         end
  184.     end
  185.     if updated then
  186.         menuDialog:updateLayout() -- fixed? /abot
  187.     end
  188. end
  189.  
  190. --- Create our changes for MenuDialog.
  191. --- @param e uiActivatedEventData
  192. local function onDialogueMenuActivated(e)
  193.     -- We only care if this is the node time it was activated.
  194.     if (not e.newlyCreated) then
  195.         return
  196.     end
  197.  
  198.     -- Set the pre-update event to update the topic list.
  199.     -- We only want this event to fire once. We'll manually track changes above to be more efficient.
  200.     local function firstPreUpdate(preUpdateEventData)
  201.         assert(e.element:unregisterAfter("preUpdate", firstPreUpdate))
  202.         updateTopicsList(preUpdateEventData)
  203.     end
  204.     e.element:registerAfter("preUpdate", firstPreUpdate)
  205.     e.element:registerAfter("update", updateAnswerText)
  206. end
  207. event.register("uiActivated", onDialogueMenuActivated, { filter = "MenuDialog" })
  208.  
  209. local function displayPlayerChoices()
  210.     if (not common.config.displayPlayerDialogueChoices) then
  211.         return
  212.     end
  213.  
  214.     local menu = tes3ui.findMenu("MenuDialog")
  215.     if not menu then
  216.         return
  217.     end
  218.  
  219.     local block = menu:findChild("MenuDialog_answer_block")
  220.     if not block then
  221.         return
  222.     end
  223.  
  224.     for _, child in pairs(block.parent.children) do
  225.         if child.name == "MenuDialog_answer_block" then
  226.             child:registerBefore("mouseClick", function(e)
  227.                 tes3.messageBox(child.text)
  228.  
  229.                 -- Find our newly created element and recolor it.
  230.                 local dialogueElements = block.parent.children
  231.                 local createdElement = dialogueElements[#dialogueElements]
  232.                 createdElement.color = tes3ui.getPalette("journal_finished_quest_over_color")
  233.             end)
  234.         end
  235.     end
  236. end
  237. event.register("postInfoResponse", displayPlayerChoices)
  238.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement