setfenv(1, VoiceOver) Options = { } local AceGUI = LibStub("AceGUI-3.0") local AceConfigDialog = LibStub("AceConfigDialog-3.0") local AceDBOptions = LibStub("AceDBOptions-3.0") ------------------------------------------------------------ -- Construction of the options table for AceConfigDialog -- -- Needed to preserve order (modern AceGUI has support for custom sorting of dropdown items, but old versions don't) local FRAME_STRATAS = { "BACKGROUND", "LOW", "MEDIUM", "HIGH", "DIALOG", } local slashCommandsHandler = {} function slashCommandsHandler:values(info) if not self.indexToName then self.indexToName = { "Nothing" } self.indexToCommand = { "" } self.commandToIndex = { [""] = 1 } for command, handler in Utils:Ordered(Options.table.args.SlashCommands.args, function(a, b) return (a.order or 100) < (b.order or 100) end) do if not handler.dropdownHidden then table.insert(self.indexToName, handler.name) table.insert(self.indexToCommand, command) self.commandToIndex[command] = #self.indexToCommand end end end return self.indexToName end function slashCommandsHandler:get(info) local config, key = info.arg() return self.commandToIndex[config[key]] end function slashCommandsHandler:set(info, value) local config, key = info.arg() config[key] = self.indexToCommand[value] end -- General Tab local GeneralTab = { name = "General", type = "group", order = 10, args = { MinimapButton = { type = "group", order = 2, inline = true, name = "Minimap Button", args = { MinimapButtonShow = { type = "toggle", order = 1, name = "Show Minimap Button", get = function(info) return not Addon.db.profile.MinimapButton.LibDBIcon.hide end, set = function(info, value) Addon.db.profile.MinimapButton.LibDBIcon.hide = not value if value then LibStub("LibDBIcon-1.0"):Show("VoiceOver") else LibStub("LibDBIcon-1.0"):Hide("VoiceOver") end end, }, MinimapButtonLock = { type = "toggle", order = 2, name = "Lock Position", get = function(info) return Addon.db.profile.MinimapButton.LibDBIcon.lock end, set = function(info, value) if value then LibStub("LibDBIcon-1.0"):Lock("VoiceOver") else LibStub("LibDBIcon-1.0"):Unlock("VoiceOver") end end, }, LineBreak1 = { type = "description", name = "", order = 3 }, MinimapButtons = { type = "group", inline = true, name = "", handler = slashCommandsHandler, args = { MinimapButtonLeftClick = { type = "select", order = 4, name = "Left Click", desc = "Action performed by left-clicking the minimap button.", values = "values", get = "get", set = "set", arg = function(value) return Addon.db.profile.MinimapButton.Commands, "LeftButton" end, }, MinimapButtonMiddleClick = { type = "select", order = 4, name = "Middle Click", desc = "Action performed by middle-clicking the minimap button.", values = "values", get = "get", set = "set", arg = function(value) return Addon.db.profile.MinimapButton.Commands, "MiddleButton" end, }, MinimapButtonRightClick = { type = "select", order = 4, name = "Right Click", desc = "Action performed by right-clicking the minimap button.", values = "values", get = "get", set = "set", arg = function(value) return Addon.db.profile.MinimapButton.Commands, "RightButton" end, } } } } }, Frame = { type = "group", order = 3, inline = true, name = "Frame", disabled = function(info) return Addon.db.profile.SoundQueueUI.HideFrame end, args = { LockFrame = { type = "toggle", order = 1, name = "Lock Frame", desc = "Prevent the frame from being moved or resized.", get = function(info) return Addon.db.profile.SoundQueueUI.LockFrame end, set = function(info, value) Addon.db.profile.SoundQueueUI.LockFrame = value Addon.soundQueue.ui:RefreshConfig() end, }, ResetFrame = { type = "execute", order = 2, name = "Reset Frame", desc = "Resets frame position and size back to default.", func = function(info) Addon.soundQueue.ui.frame:Reset() end, }, LineBreak1 = { type = "description", name = "", order = 3 }, FrameStrata = { type = "select", order = 5, name = "Frame Strata", desc = "Changes the \"depth\" of the frame, determining which other frames will it overlap or fall behind.", values = FRAME_STRATAS, get = function(info) for k, v in ipairs(FRAME_STRATAS) do if v == Addon.db.profile.SoundQueueUI.FrameStrata then return k; end end end, set = function(info, value) Addon.db.profile.SoundQueueUI.FrameStrata = FRAME_STRATAS[value] Addon.soundQueue.ui.frame:SetFrameStrata(Addon.db.profile.SoundQueueUI.FrameStrata) end, }, FrameScale = { type = "range", order = 4, name = "Frame Scale", desc = "Automatically hides the takling frame when no voice over is playing.", softMin = 0.5, softMax = 2, bigStep = 0.05, isPercent = true, get = function(info) return Addon.db.profile.SoundQueueUI.FrameScale end, set = function(info, value) Addon.db.profile.SoundQueueUI.FrameScale = value Addon.soundQueue.ui:RefreshConfig() end, }, LineBreak2 = { type = "description", name = "", order = 6 }, HidePortrait = { type = "toggle", order = 7, name = "Hide NPC Portrait", desc = "Talking NPC portrait will not appear when voice over audio is played.\n\n" .. Utils:ColorizeText("This might be useful when using other addons that replace the dialog experience, such as " .. Utils:ColorizeText("Immersion", NORMAL_FONT_COLOR_CODE) .. ".", GRAY_FONT_COLOR_CODE), get = function(info) return Addon.db.profile.SoundQueueUI.HidePortrait end, set = function(info, value) Addon.db.profile.SoundQueueUI.HidePortrait = value Addon.soundQueue.ui:RefreshConfig() end, }, HideFrame = { type = "toggle", order = 8, name = "Hide Entirely", desc = "Play voiceovers without ever displaying the frame.", disabled = false, get = function(info) return Addon.db.profile.SoundQueueUI.HideFrame end, set = function(info, value) Addon.db.profile.SoundQueueUI.HideFrame = value Addon.soundQueue.ui:RefreshConfig() end, }, }, }, Audio = { type = "group", order = 4, inline = true, name = "Audio", args = { SoundChannel = Version:IsRetailOrAboveLegacyVersion(40000) and { type = "select", width = 0.75, order = 1, name = "Sound Channel", desc = "Controls which sound channel VoiceOver will play in.", values = Enums.SoundChannel:GetValueToNameMap(), get = function(info) return Addon.db.profile.Audio.SoundChannel end, set = function(info, value) Addon.db.profile.Audio.SoundChannel = value Addon.soundQueue.ui:RefreshConfig() end, }, LineBreak = { type = "description", name = "", order = 2 }, GossipFrequency = { type = "select", width = 1.1, order = 3, name = "NPC Greeting Playback Frequency", desc = "Controls how often VoiceOver will play NPC greeting dialog.", values = { [Enums.GossipFrequency.Always] = "Always", [Enums.GossipFrequency.OncePerQuestNPC] = "Play Once for Quest NPCs", [Enums.GossipFrequency.OncePerNPC] = "Play Once for All NPCs", [Enums.GossipFrequency.Never] = "Never", }, get = function(info) return Addon.db.profile.Audio.GossipFrequency end, set = function(info, value) Addon.db.profile.Audio.GossipFrequency = value Addon.soundQueue.ui:RefreshConfig() end, }, AutoToggleDialog = Version:IsRetailOrAboveLegacyVersion(60100) and { type = "toggle", width = 2.25, order = 4, name = "Mute Vocal NPCs Greetings While VoiceOver is Playing", desc = "While VoiceOver is playing, the Dialog channel will be muted.", get = function(info) return Addon.db.profile.Audio.AutoToggleDialog end, set = function(info, value) Addon.db.profile.Audio.AutoToggleDialog = value Addon.soundQueue.ui:RefreshConfig() if Addon.db.profile.Audio.AutoToggleDialog then SetCVar("Sound_EnableDialog", 1) end end, }, } }, Debug = { type = "group", order = 5, inline = true, name = "Debugging Tools", args = { DebugEnabled = { type = "toggle", order = 1, width = 1.25, name = "Enable Debug Messages", desc = "Enables printing of some \"useful\" debug messages to the chat window.", get = function(info) return Addon.db.profile.DebugEnabled end, set = function(info, value) Addon.db.profile.DebugEnabled = value end, }, } } } } local LegacyWrathTab = Version.IsLegacyWrath and { type = "group", name = "3.3.5 Backport", order = 19, args = { PlayOnMusicChannel = { type = "group", order = 100, name = "Play Voiceovers on Music Channel", inline = true, args = { Description = { type = "description", order = 100, name = "3.3.5 client lacks the ability to stop addon sounds at will. As a workaround, you can play the voiceovers on the music channel instead, which, unlike sounds, can be stopped. Regular background music will not be playing throughout the duration of voiceovers.|n|nIf you normally play with music disabled - it will be temporarily enabled during voiceovers, but no actual background music will be played.", }, Enabled = { type = "toggle", order = 200, name = "Enable", disabled = false, get = function(info) return Addon.db.profile.LegacyWrath.PlayOnMusicChannel.Enabled end, set = function(info, value) Addon.db.profile.LegacyWrath.PlayOnMusicChannel.Enabled = value end, }, Disabled = { type = "description", order = 300, name = format("With this option disabled you %swill not be able to pause|r voiceovers after they start playing. Attempting to pause will instead %1$spause the voiceover queue|r once the current sound has finished playing.", RED_FONT_COLOR_CODE), hidden = function(info) return Addon.db.profile.LegacyWrath.PlayOnMusicChannel.Enabled end, }, Settings = { type = "group", order = 400, name = "", inline = true, hidden = function(info) return not Addon.db.profile.LegacyWrath.PlayOnMusicChannel.Enabled end, args = { FadeOutMusic = { type = "range", order = 100, name = "Music Fade Out (secs)", desc = "Background music will fade out over this number of seconds before playing voiceovers. Has no effect if in-game music is disabled or muted.", min = 0, softMax = 2, bigStep = 0.05, get = function(info) return Addon.db.profile.LegacyWrath.PlayOnMusicChannel.FadeOutMusic end, set = function(info, value) Addon.db.profile.LegacyWrath.PlayOnMusicChannel.FadeOutMusic = value end, }, Volume = { type = "range", order = 200, name = "Voiceover Volume", desc = "Music channel volume will be temporarily adjusted to this value while the voiceovers are playing.", min = 0, max = 1, bigStep = 0.01, isPercent = true, get = function(info) return Addon.db.profile.LegacyWrath.PlayOnMusicChannel.Volume end, set = function(info, value) Addon.db.profile.LegacyWrath.PlayOnMusicChannel.Volume = value end, }, } }, } }, } } local DataModulesTab = { name = "Data Modules", type = "group", childGroups = "tree", order = 20, args = {} } local SlashCommands = { type = "group", name = "Commands", order = 110, inline = true, dialogHidden = true, args = { PlayPause = { type = "execute", order = 1, name = "Play/Pause Audio", desc = "Play/Pause voiceovers", hidden = true, func = function(info) Addon.soundQueue:TogglePauseQueue() end }, Play = { type = "execute", order = 2, name = "Play Audio", desc = "Resume the playback of voiceovers", func = function(info) Addon.soundQueue:ResumeQueue() end }, Pause = { type = "execute", order = 3, name = "Pause Audio", desc = "Pause the playback of voiceovers", func = function(info) Addon.soundQueue:PauseQueue() end }, Skip = { type = "execute", order = 4, name = "Skip Line", desc = "Skip the currently played voiceover", func = function(info) local soundData = Addon.soundQueue.sounds[1] if soundData then Addon.soundQueue:RemoveSoundFromQueue(soundData) end end }, Clear = { type = "execute", order = 5, name = "Clear Queue", desc = "Stop the playback and clears the voiceovers queue", func = function(info) Addon.soundQueue:RemoveAllSoundsFromQueue() end }, Options = { type = "execute", order = 100, name = "Open Options", desc = "Open the options panel", func = function(info) Options:OpenConfigWindow() end }, } } Options.table = { name = "Voice Over", type = "group", childGroups = "tab", args = { General = GeneralTab, LegacyWrath = LegacyWrathTab, DataModules = DataModulesTab, Profiles = nil, -- Filled in Options:OnInitialize, order is implicity 100 SlashCommands = SlashCommands, } } ------------------------------------------------------------ function Options:AddDataModule(module, order) local descriptionOrder = 0 local function GetNextOrder() descriptionOrder = descriptionOrder + 1 return descriptionOrder end local function MakeDescription(header, text) return { type = "description", order = GetNextOrder(), name = function() return format("%s%s: |r%s", NORMAL_FONT_COLOR_CODE, header, type(text) == "function" and text() or text) end } end local name, title, notes, loadable, reason = GetAddOnInfo(module.AddonName) if reason == "DEMAND_LOADED" or reason == "INTERFACE_VERSION" then reason = nil end DataModulesTab.args[module.AddonName] = { name = function() local isLoaded = DataModules:GetModule(module.AddonName) return format("%d. %s%s%s|r", order, reason and RED_FONT_COLOR_CODE or isLoaded and HIGHLIGHT_FONT_COLOR_CODE or GRAY_FONT_COLOR_CODE, module.Title:gsub("VoiceOver Data %- ", ""), isLoaded and "" or " (not loaded)") end, type = "group", order = order, args = { AddonName = MakeDescription("Addon Name", module.AddonName), Title = MakeDescription("Title", module.Title), ModuleVersion = MakeDescription("Module Data Format Version", module.ModuleVersion), ModulePriority = MakeDescription("Module Priority", module.ModulePriority), ContentVersion = MakeDescription("Content Version", module.ContentVersion), LoadOnDemand = MakeDescription("Load on Demand", module.LoadOnDemand and "Yes" or "No"), Loaded = MakeDescription("Is Loaded", function() return DataModules:GetModule(module.AddonName) and "Yes" or "No" end), NotLoadableReason = { type = "description", order = GetNextOrder(), name = format("%sReason: |r%s%s|r", NORMAL_FONT_COLOR_CODE, RED_FONT_COLOR_CODE, reason and _G["ADDON_"..reason] or ""), hidden = not reason, }, Load = { type = "execute", order = GetNextOrder(), name = "Load", hidden = function() return reason or not module.LoadOnDemand or DataModules:GetModule(module.AddonName) end, func = function() DataModules:LoadModule(module) end, }, } } end ---Initialization of opens panel function Options:Initialize() self.table.args.Profiles = AceDBOptions:GetOptionsTable(Addon.db) -- Create options table Debug:Print("Registering options table...", "Options") LibStub("AceConfig-3.0"):RegisterOptionsTable("VoiceOver", self.table, "vo") AceConfigDialog:AddToBlizOptions("VoiceOver", "VoiceOver") Debug:Print("Done!", "Options") -- Create the option frame ---@type AceGUIFrame self.frame = AceGUI:Create("Frame") --AceConfigDialog:SetDefaultSize("VoiceOver", 640, 780) -- Let it be auto-sized AceConfigDialog:Open("VoiceOver", self.frame) self.frame:SetLayout("Fill") self.frame:Hide() -- Enable the frame to be closed with Escape key _G["VoiceOverOptions"] = self.frame.frame tinsert(UISpecialFrames, "VoiceOverOptions") end function Options:OpenConfigWindow() if self.frame:IsShown() then PlaySound(SOUNDKIT.ESCAPE_SCREEN_CLOSE) self.frame:Hide() else PlaySound(SOUNDKIT.ESCAPE_SCREEN_OPEN) self.frame:Show() AceConfigDialog:Open("VoiceOver", self.frame) end end