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.

405 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
3 years ago
ImmersionAPI.IsRetail and 'SUPER_TRACKING_CHANGED',
ImmersionAPI.IsWoW10 and 'PLAYER_INTERACTION_MANAGER_FRAME_SHOW',
4 years ago
}) 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
3 years ago
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
3 years ago
--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.
3 years ago
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