You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
404 lines
14 KiB
404 lines
14 KiB
local _, L = ...
|
|
local frame = _G[ _ .. 'Frame' ]
|
|
local talkbox = frame.TalkBox
|
|
local titles = frame.TitleButtons
|
|
local inspector = frame.Inspector
|
|
local elements = talkbox.Elements
|
|
L.frame = frame
|
|
|
|
----------------------------------
|
|
-- Prepare propagation, so that we
|
|
-- can catch certain key strokes
|
|
-- but propagate the event otherwise.
|
|
----------------------------------
|
|
frame:SetPropagateKeyboardInput(true)
|
|
|
|
----------------------------------
|
|
-- In the case of hide UI option,
|
|
-- frames needs to ignore the
|
|
-- alpha change of UIParent.
|
|
----------------------------------
|
|
frame:SetIgnoreParentAlpha(true)
|
|
inspector:SetIgnoreParentAlpha(true)
|
|
|
|
----------------------------------
|
|
-- Register events for main frame
|
|
----------------------------------
|
|
for _, event in pairs({
|
|
'ADDON_LOADED',
|
|
-- 'ITEM_TEXT_BEGIN', -- Starting to read a book
|
|
-- 'ITEM_TEXT_READY', -- New book text is ready
|
|
-- 'ITEM_TEXT_CLOSED', -- Stop reading a book
|
|
'GOSSIP_CLOSED', -- Close gossip frame
|
|
'GOSSIP_SHOW', -- Show gossip options, can be a mix of gossip/quests
|
|
'QUEST_ACCEPTED', -- Use this event for on-the-fly quest text tracking.
|
|
'QUEST_COMPLETE', -- Quest completed
|
|
'QUEST_DETAIL', -- Quest details/objectives/accept frame
|
|
'QUEST_FINISHED', -- Fires when quest frame is closed
|
|
'QUEST_GREETING', -- Multiple quests to choose from, but no gossip options
|
|
-- 'QUEST_IGNORED', -- Ignore the currently shown quest
|
|
'QUEST_PROGRESS', -- Fires when you click on a quest you're currently on
|
|
'QUEST_ITEM_UPDATE', -- Item update while in convo, refresh frames.
|
|
-- 'MERCHANT_SHOW', -- Force close gossip on merchant interaction.
|
|
'NAME_PLATE_UNIT_ADDED', -- For nameplate mode
|
|
'NAME_PLATE_UNIT_REMOVED', -- For nameplate mode
|
|
ImmersionAPI.IsRetail and 'SUPER_TRACKING_CHANGED',
|
|
ImmersionAPI.IsWoW10 and 'PLAYER_INTERACTION_MANAGER_FRAME_SHOW',
|
|
}) do if event then
|
|
frame:RegisterEvent(event)
|
|
end
|
|
end
|
|
|
|
|
|
frame.IgnoreResetEvent = {
|
|
QUEST_ACCEPTED = true,
|
|
NAME_PLATE_UNIT_ADDED = true,
|
|
NAME_PLATE_UNIT_REMOVED = true,
|
|
SUPER_TRACKING_CHANGED = true,
|
|
}
|
|
|
|
frame.IgnoreGossipEvent = {
|
|
GOSSIP_SHOW = true,
|
|
GOSSIP_CLOSED = true,
|
|
QUEST_ACCEPTED = true,
|
|
NAME_PLATE_UNIT_ADDED = true,
|
|
NAME_PLATE_UNIT_REMOVED = true,
|
|
SUPER_TRACKING_CHANGED = true,
|
|
}
|
|
|
|
----------------------------------
|
|
-- Register events for titlebuttons
|
|
----------------------------------
|
|
for _, event in pairs({
|
|
'GOSSIP_CLOSED', -- Hide buttons
|
|
'GOSSIP_SHOW', -- Show gossip options, can be a mix of gossip/quests
|
|
'QUEST_COMPLETE', -- Hide when going from gossip -> complete
|
|
'QUEST_DETAIL', -- Hide when going from gossip -> detail
|
|
'QUEST_FINISHED', -- Hide when going from gossip -> finished
|
|
'QUEST_GREETING', -- Show quest options, why is this a thing again?
|
|
-- 'QUEST_IGNORED', -- Hide when using ignore binding?
|
|
'QUEST_PROGRESS', -- Hide when going from gossip -> active quest
|
|
-- 'QUEST_LOG_UPDATE', -- If quest changes while interacting
|
|
}) do titles:RegisterEvent(event) end
|
|
|
|
titles:RegisterUnitEvent('UNIT_QUEST_LOG_CHANGED', 'player')
|
|
|
|
----------------------------------
|
|
-- Load SavedVaribles, config and compat
|
|
----------------------------------
|
|
function frame:ADDON_LOADED(name)
|
|
if name == _ then
|
|
local svref = _ .. 'Setup'
|
|
L.cfg = _G[svref] or L.GetDefaultConfig()
|
|
_G[svref] = L.cfg
|
|
|
|
-- Set module scales
|
|
talkbox:SetScale(L('boxscale'))
|
|
titles:SetScale(L('titlescale'))
|
|
elements:SetScale(L('elementscale'))
|
|
self:SetScale(L('scale'))
|
|
|
|
-- Set the module points
|
|
talkbox:SetPoint(L('boxpoint'), UIParent, L('boxoffsetX'), L('boxoffsetY'))
|
|
titles:SetPoint('CENTER', UIParent, 'CENTER', L('titleoffset'), L('titleoffsetY'))
|
|
|
|
self:SetFrameStrata(L('strata'))
|
|
talkbox:SetFrameStrata(L('strata'))
|
|
|
|
-- If previous version and flyins were disabled, set anidivisor to instant
|
|
if L.cfg.disableflyin then
|
|
L.cfg.disableflyin = nil
|
|
L.cfg.anidivisor = 1
|
|
end
|
|
|
|
-- Hide portrait
|
|
talkbox.PortraitFrame:SetShown(not L('disableportrait'))
|
|
talkbox.MainFrame.Model.PortraitBG:SetShown(not L('disableportrait'))
|
|
|
|
-- Show solid background
|
|
talkbox.BackgroundFrame.SolidBackground:SetShown(L('solidbackground'))
|
|
L.SetBackdrop(elements, L('solidbackground') and L.Backdrops.TALKBOX_SOLID or L.Backdrops.TALKBOX)
|
|
|
|
-- Set frame ignore for hideUI features on load.
|
|
L.ToggleIgnoreFrame(Minimap, not L('hideminimap'))
|
|
L.ToggleIgnoreFrame(MinimapCluster, not L('hideminimap'))
|
|
L.ToggleIgnoreFrame(ObjectiveTrackerFrame, not L('hidetracker'))
|
|
|
|
-- Register options table
|
|
LibStub('AceConfigRegistry-3.0'):RegisterOptionsTable(_, L.options)
|
|
L.config = LibStub('AceConfigDialog-3.0'):AddToBlizOptions(_)
|
|
|
|
-- Slash handler
|
|
_G['SLASH_' .. _:upper() .. '1'] = '/' .. _:lower()
|
|
SlashCmdList[_:upper()] = function() LibStub('AceConfigDialog-3.0'):Open(_) end
|
|
|
|
-- Add some sexiness to the config frame.
|
|
local logo = CreateFrame('Frame', nil, L.config)
|
|
logo:SetFrameLevel(4)
|
|
logo:SetSize(64, 64)
|
|
logo:SetPoint('TOPRIGHT', 8, 24)
|
|
L.SetBackdrop(logo, {bgFile = ('Interface\\AddOns\\%s\\Textures\\Logo'):format(_)})
|
|
L.config.logo = logo
|
|
|
|
-- Run functions for compatibility with other addons on load.
|
|
-- If the addon in question is already loaded, run the function and remove from list.
|
|
for addOn, func in pairs(L.compat) do
|
|
-- denotes if existing and loadable at any point
|
|
if select(4, GetAddOnInfo(addOn)) then
|
|
-- is it actually loaded at this point?
|
|
if IsAddOnLoaded(addOn) then
|
|
func(self)
|
|
L.compat[addOn] = nil
|
|
end
|
|
else -- the addon is not going to load, remove it from table.
|
|
L.compat[addOn] = nil
|
|
end
|
|
end
|
|
-- If the compatibile addon loads after Immersion, run the function and remove from list.
|
|
elseif L.compat and L.compat[name] then
|
|
L.compat[name](self)
|
|
L.compat[name] = nil
|
|
end
|
|
|
|
-- The L.compat table is empty -> all addons are loaded, disabled or missing.
|
|
-- Garbage collect the table.
|
|
if not next(L.compat) then
|
|
L.compat = nil
|
|
end
|
|
|
|
-- Immersion is loaded, no more addons to track. Garbage collect this function.
|
|
if not L.compat and IsAddOnLoaded(_) then
|
|
self:UnregisterEvent('ADDON_LOADED')
|
|
self.ADDON_LOADED = nil
|
|
end
|
|
end
|
|
|
|
-- Set backdrops on elements
|
|
L.SetBackdrop(talkbox.Hilite, L.Backdrops.TALKBOX_HILITE)
|
|
|
|
-- Initiate titlebuttons
|
|
L.Mixin(titles, L.TitlesMixin)
|
|
|
|
-- Initiate elements
|
|
L.Mixin(elements, L.ElementsMixin)
|
|
|
|
-- Initiate reputation bar
|
|
L.Mixin(talkbox.ReputationBar, NPCFriendshipStatusBarMixin or {})
|
|
talkbox.ReputationBar.Update = talkbox.ReputationBar.Update or nop;
|
|
if talkbox.ReputationBar.SetColorFill then
|
|
talkbox.ReputationBar:SetColorFill(1, 1, 1)
|
|
end
|
|
|
|
----------------------------------
|
|
-- Set up dynamically sized frames
|
|
----------------------------------
|
|
do
|
|
local AdjustToChildren = L.AdjustToChildren
|
|
L.Mixin(elements, AdjustToChildren)
|
|
L.Mixin(elements.Content, AdjustToChildren)
|
|
L.Mixin(elements.Progress, AdjustToChildren)
|
|
L.Mixin(elements.Content.RewardsFrame, AdjustToChildren)
|
|
L.Mixin(inspector, AdjustToChildren)
|
|
L.Mixin(inspector.Extras, AdjustToChildren)
|
|
L.Mixin(inspector.Choices, AdjustToChildren)
|
|
end
|
|
|
|
-- Set point since the relative region didn't exist on load.
|
|
local name = talkbox.NameFrame.Name
|
|
name:SetPoint('TOPLEFT', talkbox.PortraitFrame.Portrait, 'TOPRIGHT', 2, -19)
|
|
|
|
-- Model script, light
|
|
local model = talkbox.MainFrame.Model
|
|
--L.SetLight(model, true, L.ModelMixin.LightValues)
|
|
L.Mixin(model, L.ModelMixin)
|
|
|
|
----------------------------------
|
|
-- Main text things
|
|
----------------------------------
|
|
local text = talkbox.TextFrame.Text
|
|
Mixin(text, L.TextMixin) -- see Mixins\Text.lua
|
|
-- Set array of fonts so the fontstring can be as big as possible without truncating the text
|
|
text:SetFontObjectsToTry(SystemFont_Shadow_Large, SystemFont_Shadow_Med2, SystemFont_Shadow_Med1)
|
|
|
|
-- Run a 'talk' animation on the portrait model whenever a new text is set
|
|
function text:OnDisplayLineCallback(text)
|
|
local counter = talkbox.TextFrame.SpeechProgress
|
|
talkbox.TextFrame.FadeIn:Stop()
|
|
talkbox.TextFrame.FadeIn:Play()
|
|
if text then
|
|
local isEmote = text:match('%b<>')
|
|
self:SetVertexColor(1, isEmote and 0.5 or 1, isEmote and 0 or 1)
|
|
model:PrepareAnimation(text, isEmote)
|
|
model:RunSequence(self:GetModifiedTime(), self:IsSequence())
|
|
end
|
|
|
|
counter:Hide()
|
|
if self:IsSequence() then
|
|
if not self:IsFinished() then
|
|
counter:Show()
|
|
counter:SetText(self:GetProgress())
|
|
end
|
|
frame:AddHint('SQUARE', self:GetNumRemaining() <= 1 and RESET or NEXT)
|
|
else
|
|
frame:RemoveHint('SQUARE')
|
|
end
|
|
|
|
if self:IsVisible() then
|
|
if L('disableprogression') then
|
|
self:PauseTimer()
|
|
end
|
|
|
|
if ( text and L('ttsenabled') ) then
|
|
C_VoiceChat.StopSpeakingText()
|
|
C_Timer.After(0, function()
|
|
C_VoiceChat.SpeakText(
|
|
(L('ttsvoice') or 1) -1,
|
|
text,
|
|
Enum.VoiceTtsDestination.LocalPlayback,
|
|
L('ttsrate'),
|
|
L('ttsvolume'))
|
|
end)
|
|
end
|
|
end
|
|
end
|
|
|
|
function text:OnFinishedCallback()
|
|
-- remove the last playback line, because the text played until completion.
|
|
if ( L('onthefly') or L('supertracked') ) and not self:IsForceFinishedFlagged() then
|
|
frame:RemoveToastByText(self.storedText)
|
|
end
|
|
end
|
|
|
|
----------------------------------
|
|
-- Misc fixes
|
|
----------------------------------
|
|
talkbox:RegisterForClicks('LeftButtonUp', 'RightButtonUp')
|
|
talkbox:RegisterForDrag('LeftButton')
|
|
talkbox.TextFrame.SpeechProgress:SetFont('Fonts\\MORPHEUS.ttf', 16, '')
|
|
talkbox.TextFrame.SpeechProgress:SetShadowColor(0, 0, 0, 1)
|
|
talkbox.TextFrame.SpeechProgress:SetShadowOffset(0, -1)
|
|
|
|
----------------------------------
|
|
-- Set movable frames
|
|
----------------------------------
|
|
talkbox:SetMovable(true)
|
|
talkbox:SetUserPlaced(false)
|
|
talkbox:SetClampedToScreen(true)
|
|
|
|
titles:SetMovable(true)
|
|
titles:SetUserPlaced(false)
|
|
|
|
--------------------------------
|
|
-- Hooks and hacks
|
|
--------------------------------
|
|
|
|
-- Hide regular frames
|
|
L.HideFrame(GossipFrame)
|
|
L.HideFrame(QuestFrame)
|
|
--L.HideFrame(ItemTextFrame)
|
|
|
|
-- Handle custom gossip events (new in Shadowlands)
|
|
frame.gossipHandlers = CustomGossipFrameManager and CustomGossipFrameManager.handlers or {};
|
|
|
|
-- Anchor the real talking head to the fake talking head,
|
|
-- make it appear IN PLACE of the fake one if the fake one isn't shown.
|
|
do
|
|
local function HookTalkingHead()
|
|
-- use this as assertion. if something else beat Immersion to it and manipulated the frame,
|
|
-- it shouldn't be moved, even if enabled by user. in essence, dummy protection.
|
|
if UIPARENT_MANAGED_FRAME_POSITIONS and UIPARENT_MANAGED_FRAME_POSITIONS.TalkingHeadFrame then
|
|
local managedFramePos = UIPARENT_MANAGED_FRAME_POSITIONS.TalkingHeadFrame
|
|
local alertFrameIndex, alertFrameSettings
|
|
local isTalkingHeadMoved, isDragging
|
|
|
|
local function Drag(self)
|
|
if isTalkingHeadMoved and L('movetalkinghead') then
|
|
isDragging = true
|
|
talkbox:OnDragStart()
|
|
end
|
|
end
|
|
|
|
local function Drop(self)
|
|
if isDragging then
|
|
isDragging = nil
|
|
talkbox:OnDragStop()
|
|
end
|
|
end
|
|
|
|
local function Move(self)
|
|
-- if the move is allowed
|
|
if L('movetalkinghead') then
|
|
self:ClearAllPoints(self)
|
|
self:RegisterForDrag('LeftButton')
|
|
|
|
if not isTalkingHeadMoved then
|
|
-- Need this to keep it from resetting position
|
|
self.ignoreFramePositionManager = true
|
|
UIPARENT_MANAGED_FRAME_POSITIONS.TalkingHeadFrame = nil
|
|
|
|
-- Flag for reset
|
|
isTalkingHeadMoved = true
|
|
|
|
-- Remove from alert system, preventing other frames from anchoring to it.
|
|
for index, alertFrameSubSystem in ipairs(AlertFrame.alertFrameSubSystems) do
|
|
if alertFrameSubSystem.anchorFrame and alertFrameSubSystem.anchorFrame == TalkingHeadFrame then
|
|
alertFrameIndex = index
|
|
alertFrameSettings = table.remove(AlertFrame.alertFrameSubSystems, index)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Move the frame
|
|
if not talkbox:IsVisible() then
|
|
talkbox:SetOffset() -- force update; (1) reset the offset calculation
|
|
talkbox:SetExtraOffset(0) -- (2) make sure it isn't offset by elements from prior quest
|
|
self:SetPoint('BOTTOM', talkbox, 'BOTTOM', 0, 0)
|
|
else
|
|
self:SetPoint('BOTTOM', talkbox, 'TOP', 0, 0)
|
|
end
|
|
|
|
-- if the setting is toggled off and the frame was manipulated, reset.
|
|
elseif isTalkingHeadMoved then
|
|
UIPARENT_MANAGED_FRAME_POSITIONS.TalkingHeadFrame = managedFramePos
|
|
self.ignoreFramePositionManager = nil
|
|
self:RegisterForDrag(nil)
|
|
isTalkingHeadMoved = nil
|
|
-- Reinsert the table into the alert system, if it existed.
|
|
if alertFrameIndex and alertFrameSettings then
|
|
table.insert(AlertFrame.alertFrameSubSystems, alertFrameIndex, alertFrameSettings)
|
|
alertFrameIndex = nil
|
|
alertFrameSettings = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
TalkingHeadFrame:HookScript('OnShow', Move)
|
|
TalkingHeadFrame:HookScript('OnHide', Drop)
|
|
TalkingHeadFrame:HookScript('OnDragStart', Drag)
|
|
TalkingHeadFrame:HookScript('OnDragStop', Drop)
|
|
talkbox:HookScript('OnShow', function() Move(TalkingHeadFrame) end)
|
|
talkbox:HookScript('OnHide', function() Move(TalkingHeadFrame) end)
|
|
end
|
|
end
|
|
|
|
-- Run the init if the frame already exists (force loaded)
|
|
if TalkingHeadFrame then
|
|
HookTalkingHead()
|
|
elseif TalkingHead_LoadUI then -- Hook to the loading function.
|
|
hooksecurefunc('TalkingHead_LoadUI', HookTalkingHead)
|
|
end
|
|
end
|
|
|
|
do -- Azerite Empowered Item UI
|
|
local loaded = false
|
|
local function ignoreAzeriteItemUI()
|
|
if not loaded and IsAddOnLoaded('Blizzard_AzeriteUI') then
|
|
loaded = true
|
|
L.ToggleIgnoreFrame(AzeriteEmpoweredItemUI, true)
|
|
end
|
|
end
|
|
if OpenAzeriteEmpoweredItemUIFromItemLocation and OpenAzeriteEmpoweredItemUIFromLink then
|
|
hooksecurefunc('OpenAzeriteEmpoweredItemUIFromItemLocation', ignoreAzeriteItemUI)
|
|
hooksecurefunc('OpenAzeriteEmpoweredItemUIFromLink', ignoreAzeriteItemUI)
|
|
end
|
|
end
|
|
|