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.
721 lines
23 KiB
721 lines
23 KiB
|
2 years ago
|
-- TalkingHeadFrame Revamp: Less intrudingIsWorldQuest
|
||
|
|
|
||
|
|
local _, addon = ...
|
||
|
|
local API = addon.API;
|
||
|
|
local L = addon.L;
|
||
|
|
|
||
|
|
local FADE_OUT_AFTER_FINISH = 0.5;
|
||
|
|
local QUEST_DESCRIPTION_GRADIENT_LENGTH = 30;
|
||
|
|
local TEXT_WIDTH = 512;
|
||
|
|
local LINE_SPACING = 2;
|
||
|
|
local DEFAULT_POSITION_Y = 336;
|
||
|
|
|
||
|
|
local SPEECH_FORMAT_GENRIC = "|cffffd100%s: |r%s";
|
||
|
|
local SPEECH_FORMAT_PVP = "|cffff2020%s: |r%s";
|
||
|
|
|
||
|
|
local FadeFrame = API.UIFrameFade;
|
||
|
|
local Lerp = API.Lerp;
|
||
|
|
local Round = API.Round;
|
||
|
|
local strlenutf8 = strlenutf8;
|
||
|
|
local ceil = math.ceil;
|
||
|
|
local max = math.max;
|
||
|
|
local format = string.format;
|
||
|
|
local PlaySound = PlaySound;
|
||
|
|
local StopSound = StopSound;
|
||
|
|
local GetCurrentLineInfo = C_TalkingHead.GetCurrentLineInfo;
|
||
|
|
local IsWorldQuest = C_QuestLog.IsWorldQuest;
|
||
|
|
local IsQuestTask = C_QuestLog.IsQuestTask;
|
||
|
|
local IsInInstance = IsInInstance;
|
||
|
|
--local SetPortraitTextureFromCreatureDisplayID = SetPortraitTextureFromCreatureDisplayID;
|
||
|
|
|
||
|
|
local DB;
|
||
|
|
|
||
|
|
local PVP_CREATURE_DISPLAYID = {
|
||
|
|
[108418] = true,
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
local NewTalkingHead = CreateFrame("Frame", nil, UIParent);
|
||
|
|
NewTalkingHead:Hide();
|
||
|
|
NewTalkingHead:SetFrameStrata("FULLSCREEN"); --LOW
|
||
|
|
NewTalkingHead:SetFrameLevel(980);
|
||
|
|
NewTalkingHead:SetSize(TEXT_WIDTH, 32);
|
||
|
|
|
||
|
|
--[[cause taint "UIParentBottomManagedFrameTemplate"
|
||
|
|
NewTalkingHead.layoutIndex = 1208;
|
||
|
|
NewTalkingHead.hideWhenActionBarIsOverriden = false;
|
||
|
|
NewTalkingHead.align = "center";
|
||
|
|
--]]
|
||
|
|
|
||
|
|
addon.TalkingHead = NewTalkingHead;
|
||
|
|
|
||
|
|
|
||
|
|
local function SetFontShadow(fontString)
|
||
|
|
--Game Bug: Shadow is removed upon SetAlphaGradient
|
||
|
|
fontString:SetShadowColor(0, 0, 0, 1);
|
||
|
|
fontString:SetShadowOffset(1, -1);
|
||
|
|
end
|
||
|
|
|
||
|
|
local function SetupFontString(fontString)
|
||
|
|
fontString:SetJustifyH("MIDDLE");
|
||
|
|
fontString:SetJustifyV("TOP");
|
||
|
|
fontString:SetSpacing(LINE_SPACING);
|
||
|
|
SetFontShadow(fontString);
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
local function FadeOutAfter_OnUpdate(self, elapsed)
|
||
|
|
self.t = self.t + elapsed;
|
||
|
|
if self.t >= self.fadeOutDelay then
|
||
|
|
self:FadeOutText(true);
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function FadeIn_TypeWriter_OnUpdate(self, elapsed)
|
||
|
|
self.t = self.t + elapsed;
|
||
|
|
self.fadingProgress = self.fadingProgress + (elapsed * self.gradientCPS);
|
||
|
|
|
||
|
|
self.gradientLength = self.gradientLength + 10 * elapsed;
|
||
|
|
if self.gradientLength > QUEST_DESCRIPTION_GRADIENT_LENGTH then
|
||
|
|
self.gradientLength = QUEST_DESCRIPTION_GRADIENT_LENGTH;
|
||
|
|
end
|
||
|
|
|
||
|
|
if not self.LineText:SetAlphaGradient(self.fadingProgress, self.gradientLength) then
|
||
|
|
self:SetScript("OnUpdate", FadeOutAfter_OnUpdate);
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
|
||
|
|
if self.t < self.backgroundFadeDuration then
|
||
|
|
self.Background:SetAlpha(Lerp(0, 1, self.t / self.backgroundFadeDuration));
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function FadeIn_InstantText_OnUpdate(self, elapsed)
|
||
|
|
self.t = self.t + elapsed;
|
||
|
|
self.fadingProgress = self.fadingProgress + 4*elapsed;
|
||
|
|
|
||
|
|
if self.fadingProgress >= 1 then
|
||
|
|
self:SetScript("OnUpdate", FadeOutAfter_OnUpdate);
|
||
|
|
self.fadingProgress = 1;
|
||
|
|
self.LineText:SetAlpha(1);
|
||
|
|
self.Background:SetAlpha(1);
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
|
||
|
|
self.LineText:SetAlpha(self.fadingProgress);
|
||
|
|
self.Background:SetAlpha(self.fadingProgress);
|
||
|
|
end
|
||
|
|
|
||
|
|
local function FadeOut_OnUpdate(self, elapsed)
|
||
|
|
self.t = self.t + elapsed;
|
||
|
|
if self.t >= 0 then
|
||
|
|
self.textAlpha = self.textAlpha - 2 * elapsed;
|
||
|
|
if self.textAlpha <= 0 then
|
||
|
|
self.textAlpha = 0;
|
||
|
|
self:Hide();
|
||
|
|
self.isFadingOut = false;
|
||
|
|
self:SetScript("OnUpdate", nil);
|
||
|
|
end
|
||
|
|
self.LineText:SetAlpha(self.textAlpha);
|
||
|
|
self.Background:SetAlpha(self.textAlpha);
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:SetText(text)
|
||
|
|
self.LineText:SetText(text);
|
||
|
|
self.LineText:Show();
|
||
|
|
|
||
|
|
local textWidth = self.LineText:GetWrappedWidth();
|
||
|
|
local textHeight = self.LineText:GetHeight() - (self.fontHeight + LINE_SPACING);
|
||
|
|
local numLines = self.LineText:GetNumLines() + 1;
|
||
|
|
|
||
|
|
self.Background:ClearAllPoints();
|
||
|
|
self.Background:SetPoint("CENTER", self, "TOP", 0, -0.5*( numLines*(self.fontHeight + LINE_SPACING) - LINE_SPACING));
|
||
|
|
self.Background:SetSize(textWidth + 32, textHeight + 32);
|
||
|
|
|
||
|
|
self:UpdateFrameSize();
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:FadeInText(speakerName, text, voiceoverDuration, hideName, isFinalLine, displayInfo)
|
||
|
|
if not (text and speakerName and voiceoverDuration) then return end;
|
||
|
|
|
||
|
|
if not hideName then
|
||
|
|
local speechFormat;
|
||
|
|
if displayInfo and PVP_CREATURE_DISPLAYID[displayInfo] then
|
||
|
|
speechFormat = SPEECH_FORMAT_PVP;
|
||
|
|
else
|
||
|
|
speechFormat = SPEECH_FORMAT_GENRIC;
|
||
|
|
end
|
||
|
|
text = format(speechFormat, speakerName, text);
|
||
|
|
end
|
||
|
|
|
||
|
|
local numChars = strlenutf8(text);
|
||
|
|
if numChars == 0 or voiceoverDuration == 0 then return end;
|
||
|
|
|
||
|
|
numChars = numChars + 15;
|
||
|
|
text = " \n"..text;
|
||
|
|
|
||
|
|
self:SetText(text);
|
||
|
|
|
||
|
|
self.gradientCPS = ceil(numChars / voiceoverDuration) + 10;
|
||
|
|
self.fadingProgress = 0;
|
||
|
|
self.backgroundFadeDuration = max(voiceoverDuration - 1, 1);
|
||
|
|
|
||
|
|
self.t = 0;
|
||
|
|
self.gradientLength = QUEST_DESCRIPTION_GRADIENT_LENGTH;
|
||
|
|
|
||
|
|
if isFinalLine then
|
||
|
|
self.fadeOutDelay = voiceoverDuration + 1;
|
||
|
|
else
|
||
|
|
self.fadeOutDelay = voiceoverDuration;
|
||
|
|
end
|
||
|
|
|
||
|
|
self.isFadingOut = false;
|
||
|
|
|
||
|
|
if self.instantText then
|
||
|
|
self.LineText:SetAlpha(0);
|
||
|
|
self.Background:SetAlpha(0);
|
||
|
|
self:SetScript("OnUpdate", FadeIn_InstantText_OnUpdate);
|
||
|
|
else
|
||
|
|
self.LineText:SetAlpha(1);
|
||
|
|
self.LineText:SetAlphaGradient(0, QUEST_DESCRIPTION_GRADIENT_LENGTH);
|
||
|
|
self.Background:SetAlpha(0);
|
||
|
|
self:SetScript("OnUpdate", FadeIn_TypeWriter_OnUpdate);
|
||
|
|
end
|
||
|
|
|
||
|
|
FadeFrame(self, 0, 1);
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:FadeOutText(noDelay)
|
||
|
|
if self:IsShown() then
|
||
|
|
if not self.isFadingOut then
|
||
|
|
self.textAlpha = self.LineText:GetAlpha();
|
||
|
|
self.t = (noDelay and 0) or -1;
|
||
|
|
self:SetScript("OnUpdate", FadeOut_OnUpdate);
|
||
|
|
end
|
||
|
|
else
|
||
|
|
self.isFadingOut = false;
|
||
|
|
self.LineText:Hide();
|
||
|
|
self.LineText:SetAlpha(0);
|
||
|
|
self.textAlpha = 0;
|
||
|
|
self:SetScript("OnUpdate", nil);
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:SetFontHeightByPercentage(percentage)
|
||
|
|
if not percentage then
|
||
|
|
percentage = 100;
|
||
|
|
end
|
||
|
|
|
||
|
|
if percentage < 100 then
|
||
|
|
percentage = 100;
|
||
|
|
elseif percentage > 120 then
|
||
|
|
percentage = 120;
|
||
|
|
end
|
||
|
|
|
||
|
|
local font, baseFontHeight = QuestFont:GetFont();
|
||
|
|
local fontHeight = Round(percentage * 0.01 * baseFontHeight);
|
||
|
|
|
||
|
|
self.fontHeight = fontHeight;
|
||
|
|
|
||
|
|
local style, gray;
|
||
|
|
if DB.TalkingHead_TextOutline then
|
||
|
|
style = "OUTLINE";
|
||
|
|
gray = 0.898; --VERY_LIGHT_GRAY_COLOR
|
||
|
|
else
|
||
|
|
style = "";
|
||
|
|
gray = 1;
|
||
|
|
end
|
||
|
|
|
||
|
|
self.LineText:SetFont(font, fontHeight, style);
|
||
|
|
self.LineText:SetTextColor(gray, gray, gray);
|
||
|
|
SetupFontString(self.LineText);
|
||
|
|
|
||
|
|
DB.TalkingHead_FontSize = percentage;
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:LoadPosition()
|
||
|
|
self:ClearAllPoints();
|
||
|
|
if DB.TalkingHead_PositionX and DB.TalkingHead_PositionY then
|
||
|
|
if DB.TalkingHead_PositionX > 0 then
|
||
|
|
self:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", DB.TalkingHead_PositionX, DB.TalkingHead_PositionY);
|
||
|
|
else
|
||
|
|
self:SetPoint("TOP", UIParent, "BOTTOM", 0, DB.TalkingHead_PositionY);
|
||
|
|
end
|
||
|
|
else
|
||
|
|
self:SetPoint("TOP", UIParent, "BOTTOM", 0, DEFAULT_POSITION_Y);
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:Init()
|
||
|
|
DB = PlumberDB;
|
||
|
|
|
||
|
|
self:LoadPosition();
|
||
|
|
|
||
|
|
local _, fontHeight = QuestFont:GetFont();
|
||
|
|
self.baseFontHeight = Round(fontHeight + 0.5);
|
||
|
|
self.fontHeight = self.baseFontHeight;
|
||
|
|
|
||
|
|
self.LineText = NewTalkingHead:CreateFontString(nil, "OVERLAY", "QuestFont");
|
||
|
|
self.LineText:SetPoint("TOP", self, "TOP", 0, 0);
|
||
|
|
self.LineText:SetWidth(TEXT_WIDTH);
|
||
|
|
self.LineText:SetAlpha(0);
|
||
|
|
self:SetFontHeightByPercentage(DB.TalkingHead_FontSize);
|
||
|
|
|
||
|
|
self.Background = self:CreateTexture(nil, "BACKGROUND");
|
||
|
|
self.Background:SetTexture("Interface/AddOns/Plumber/Art/Frame/SubtitleShadow_NineSlice");
|
||
|
|
self.Background:SetTextureSliceMargins(30, 30, 30, 30);
|
||
|
|
self.Background:SetTextureSliceMode(0);
|
||
|
|
self.Background:SetSize(128, 32);
|
||
|
|
self.Background:SetPoint("CENTER", self, "TOP", 0, 0);
|
||
|
|
|
||
|
|
--[[
|
||
|
|
self.Portrait = self:CreateTexture(nil, "BACKGROUND", nil, -1);
|
||
|
|
self.Portrait:SetSize(115, 115);
|
||
|
|
self.Portrait:SetPoint("CENTER", self, "CENTER", 0, -6);
|
||
|
|
--]]
|
||
|
|
|
||
|
|
self:OnSettingsChanged();
|
||
|
|
|
||
|
|
self.Init = function() end;
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:EnableTalkingHead()
|
||
|
|
if self.enabled then return end;
|
||
|
|
|
||
|
|
if self.blizzardTalkingHeadEnabled == nil then
|
||
|
|
self.blizzardTalkingHeadEnabled = TalkingHeadFrame:IsEventRegistered("TALKINGHEAD_REQUESTED");
|
||
|
|
end
|
||
|
|
TalkingHeadFrame:UnregisterEvent("TALKINGHEAD_REQUESTED");
|
||
|
|
|
||
|
|
self.enabled = true;
|
||
|
|
self:Init();
|
||
|
|
|
||
|
|
self:RegisterEvent("TALKINGHEAD_REQUESTED");
|
||
|
|
self:RegisterEvent("TALKINGHEAD_CLOSE");
|
||
|
|
self:RegisterEvent("LOADING_SCREEN_ENABLED");
|
||
|
|
self:RegisterEvent("PLAYER_ENTERING_WORLD");
|
||
|
|
|
||
|
|
self:SetScript("OnEvent", self.OnEvent);
|
||
|
|
|
||
|
|
if not self.editmodeHooked then
|
||
|
|
self.editmodeHooked = true;
|
||
|
|
EventRegistry:RegisterCallback("EditMode.Enter", self.EnterEditMode, self);
|
||
|
|
EventRegistry:RegisterCallback("EditMode.Exit", self.ExitEditMode, self);
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:DisableTalkingHead()
|
||
|
|
if self.enabled then
|
||
|
|
if self.blizzardTalkingHeadEnabled then
|
||
|
|
TalkingHeadFrame:RegisterEvent("TALKINGHEAD_REQUESTED");
|
||
|
|
end
|
||
|
|
self.blizzardTalkingHeadEnabled = nil;
|
||
|
|
self.enabled = false;
|
||
|
|
|
||
|
|
self:UnregisterEvent("TALKINGHEAD_REQUESTED");
|
||
|
|
self:UnregisterEvent("TALKINGHEAD_CLOSE");
|
||
|
|
self:UnregisterEvent("SOUNDKIT_FINISHED");
|
||
|
|
self:UnregisterEvent("LOADING_SCREEN_ENABLED");
|
||
|
|
self:UnregisterEvent("PLAYER_ENTERING_WORLD");
|
||
|
|
self:UnregisterEvent("QUEST_ACCEPTED");
|
||
|
|
self:UnregisterEvent("QUEST_TURNED_IN");
|
||
|
|
|
||
|
|
self:OnTalkingHeadClose();
|
||
|
|
self:SetScript("OnEvent", nil);
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:TryDisable()
|
||
|
|
--Disable if not used in any other modules
|
||
|
|
if not DB then DB = PlumberDB end;
|
||
|
|
if DB.TalkingHead_MasterSwitch then return end;
|
||
|
|
|
||
|
|
self:DisableTalkingHead();
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:OnTalkingHeadRequested()
|
||
|
|
if self.voHandle then
|
||
|
|
StopSound(self.voHandle);
|
||
|
|
self.voHandle = nil;
|
||
|
|
end
|
||
|
|
|
||
|
|
local displayInfo, cameraID, vo, duration, lineNumber, numLines, name, text, isNewTalkingHead, textureKit = GetCurrentLineInfo();
|
||
|
|
|
||
|
|
--print(name, displayInfo, vo, duration) --Debug
|
||
|
|
|
||
|
|
if displayInfo and displayInfo ~= 0 and duration then
|
||
|
|
local success, voHandle = PlaySound(vo, "Talking Head", true, true);
|
||
|
|
if success then
|
||
|
|
self:RegisterEvent("SOUNDKIT_FINISHED");
|
||
|
|
self.voHandle = voHandle;
|
||
|
|
end
|
||
|
|
|
||
|
|
--lineNumber start from 0
|
||
|
|
local isFinalLine = lineNumber and numLines and (lineNumber + 1 == numLines);
|
||
|
|
|
||
|
|
if self.shouldMuteLine then
|
||
|
|
if isFinalLine then
|
||
|
|
self.shouldMuteLine = nil;
|
||
|
|
else
|
||
|
|
self:MuteNextLine(duration);
|
||
|
|
end
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
if self.hideInInstance and self.inInstance then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
duration = duration - FADE_OUT_AFTER_FINISH; --reserved for fade-out between line
|
||
|
|
local hideName = (lineNumber ~= 0) and (name and name == self.lastName);
|
||
|
|
self.lastName = name;
|
||
|
|
|
||
|
|
self:FadeInText(name, text, duration, hideName, isFinalLine, displayInfo);
|
||
|
|
--SetPortraitTextureFromCreatureDisplayID(self.Portrait, displayInfo);
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
function NewTalkingHead:FadeOutFrame()
|
||
|
|
FadeFrame(self, 0.5, 0);
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:OnTalkingHeadClose()
|
||
|
|
self:FadeOutFrame();
|
||
|
|
self.lastName = nil;
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:CloseImmediately()
|
||
|
|
if self.voHandle then
|
||
|
|
StopSound(self.voHandle);
|
||
|
|
end
|
||
|
|
FadeFrame(self, 0, 0);
|
||
|
|
self.lastName = nil;
|
||
|
|
end
|
||
|
|
|
||
|
|
local function ShouldMuteText(questID)
|
||
|
|
--Sometimes TALKINGHEAD_REQUESTED fires before QUEST_ACCEPTED, and we won't be able to mute it
|
||
|
|
--Sira Moonwarden's WQ returns wrong type?
|
||
|
|
return questID and (IsWorldQuest(questID) or IsQuestTask(questID))
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:OnEvent(event, ...)
|
||
|
|
if self.isEditing then return end;
|
||
|
|
|
||
|
|
if event == "TALKINGHEAD_REQUESTED" then
|
||
|
|
self:OnTalkingHeadRequested();
|
||
|
|
elseif event == "TALKINGHEAD_CLOSE" then
|
||
|
|
--This event will triggered automatically, not closed by the user
|
||
|
|
self:OnTalkingHeadClose();
|
||
|
|
elseif event == "SOUNDKIT_FINISHED" then
|
||
|
|
self:UnregisterEvent(event);
|
||
|
|
local voHandle = ...
|
||
|
|
if self.voHandle == voHandle then
|
||
|
|
self.voHandle = nil;
|
||
|
|
end
|
||
|
|
elseif event == "LOADING_SCREEN_ENABLED" then
|
||
|
|
self:CloseImmediately();
|
||
|
|
elseif event == "PLAYER_ENTERING_WORLD" then
|
||
|
|
self.inInstance = IsInInstance();
|
||
|
|
elseif event == "QUEST_ACCEPTED" then
|
||
|
|
local questID = ...
|
||
|
|
if ShouldMuteText(questID) then
|
||
|
|
self:MuteNextLine();
|
||
|
|
end
|
||
|
|
elseif event == "QUEST_TURNED_IN" then
|
||
|
|
local questID = ...
|
||
|
|
if ShouldMuteText(questID) then
|
||
|
|
self:MuteNextLine();
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:OnSettingsChanged()
|
||
|
|
if not DB then DB = PlumberDB; end;
|
||
|
|
if not DB then return end;
|
||
|
|
|
||
|
|
if DB.TalkingHead_InstantText then
|
||
|
|
self.instantText = true;
|
||
|
|
else
|
||
|
|
self.instantText = false;
|
||
|
|
end
|
||
|
|
|
||
|
|
if DB.TalkingHead_HideInInstance then
|
||
|
|
self.hideInInstance = true;
|
||
|
|
else
|
||
|
|
self.hideInInstance = false;
|
||
|
|
end
|
||
|
|
|
||
|
|
if DB.TalkingHead_HideWorldQuest then
|
||
|
|
self:RegisterEvent("QUEST_ACCEPTED");
|
||
|
|
self:RegisterEvent("QUEST_TURNED_IN");
|
||
|
|
else
|
||
|
|
self:UnregisterEvent("QUEST_ACCEPTED");
|
||
|
|
self:UnregisterEvent("QUEST_TURNED_IN");
|
||
|
|
self.shouldMuteLine = nil;
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
---- Edit Mode
|
||
|
|
function NewTalkingHead:EnterEditMode()
|
||
|
|
if not self.enabled then return end;
|
||
|
|
|
||
|
|
self:Init();
|
||
|
|
|
||
|
|
if not self.Selection then
|
||
|
|
local uiName = "Simple Talking Head";
|
||
|
|
local hideLabel = true;
|
||
|
|
self.Selection = addon.CreateEditModeSelection(self, uiName, hideLabel);
|
||
|
|
end
|
||
|
|
|
||
|
|
self.isEditing = true;
|
||
|
|
self:SetScript("OnUpdate", nil);
|
||
|
|
FadeFrame(self, 0, 1);
|
||
|
|
self.Selection:ShowHighlighted();
|
||
|
|
|
||
|
|
self:ShowExampleText();
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:ExitEditMode()
|
||
|
|
if self.Selection then
|
||
|
|
self.Selection:Hide();
|
||
|
|
end
|
||
|
|
self:ShowOptions(false);
|
||
|
|
self.isEditing = false;
|
||
|
|
self:CloseImmediately();
|
||
|
|
end
|
||
|
|
|
||
|
|
local function FadeIn_TypeWriter_NoAutoHide_OnUpdate(self, elapsed)
|
||
|
|
if FadeIn_TypeWriter_OnUpdate(self, elapsed) then
|
||
|
|
self:SetScript("OnUpdate", nil);
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function FadeIn_InstantText_NoAutoHide_OnUpdate(self, elapsed)
|
||
|
|
if FadeIn_InstantText_OnUpdate(self, elapsed) then
|
||
|
|
self:SetScript("OnUpdate", nil);
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:ShowExampleText(animate)
|
||
|
|
local exampleText = format(SPEECH_FORMAT_GENRIC, "The Speaker", "Time is an illusion that helps things make sense. So we are always living in the present tense.");
|
||
|
|
self:SetText(" \n"..exampleText);
|
||
|
|
self.LineText:SetAlpha(1);
|
||
|
|
|
||
|
|
if animate then
|
||
|
|
self.fadingProgress = 0;
|
||
|
|
self.t = 0;
|
||
|
|
if self.instantText then
|
||
|
|
self:SetScript("OnUpdate", FadeIn_InstantText_NoAutoHide_OnUpdate);
|
||
|
|
else
|
||
|
|
local numChars = strlenutf8(exampleText);
|
||
|
|
local voiceoverDuration = 3;
|
||
|
|
self.gradientCPS = ceil(numChars / voiceoverDuration) + 10;
|
||
|
|
self.fadingProgress = 0;
|
||
|
|
self.backgroundFadeDuration = max(voiceoverDuration - 1, 1);
|
||
|
|
self.gradientLength = QUEST_DESCRIPTION_GRADIENT_LENGTH;
|
||
|
|
self:SetScript("OnUpdate", FadeIn_TypeWriter_NoAutoHide_OnUpdate);
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:IsFocused()
|
||
|
|
return (self:IsShown() and self:IsMouseOver()) or (self.OptionFrame and self.OptionFrame:IsShown() and self.OptionFrame:IsMouseOver())
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
local function Options_FontSizeSlider_OnValueChanged(value)
|
||
|
|
NewTalkingHead:SetFontHeightByPercentage(value);
|
||
|
|
NewTalkingHead:UpdateFrameSize();
|
||
|
|
end
|
||
|
|
|
||
|
|
local function Options_FontSizeSlider_FormatValue(value)
|
||
|
|
return format("%.0f%%", value);
|
||
|
|
end
|
||
|
|
|
||
|
|
local function Options_InstantText_OnClick(self, state)
|
||
|
|
NewTalkingHead:OnSettingsChanged();
|
||
|
|
NewTalkingHead:ShowExampleText(true);
|
||
|
|
end
|
||
|
|
|
||
|
|
local function Options_TextOutline_OnClick(self, state)
|
||
|
|
NewTalkingHead:SetFontHeightByPercentage(DB.TalkingHead_FontSize);
|
||
|
|
end
|
||
|
|
|
||
|
|
local function Options_HideInInstance_OnClick(self, state)
|
||
|
|
NewTalkingHead:OnSettingsChanged();
|
||
|
|
end
|
||
|
|
|
||
|
|
local function Options_HideWorldQuest_OnClick(self, state)
|
||
|
|
NewTalkingHead:OnSettingsChanged();
|
||
|
|
end
|
||
|
|
|
||
|
|
local function Options_ResetPosition_ShouldEnable(self)
|
||
|
|
if DB.TalkingHead_PositionX and DB.TalkingHead_PositionY then
|
||
|
|
return true
|
||
|
|
else
|
||
|
|
return false
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function Options_ResetPosition_OnClick(self)
|
||
|
|
self:Disable();
|
||
|
|
DB.TalkingHead_PositionX = nil;
|
||
|
|
DB.TalkingHead_PositionY = nil;
|
||
|
|
NewTalkingHead:LoadPosition();
|
||
|
|
end
|
||
|
|
|
||
|
|
local OPTIONS_SCHEMATIC = {
|
||
|
|
title = L["EditMode TalkingHead"],
|
||
|
|
widgets = {
|
||
|
|
{type = "Checkbox", label = L["TalkingHead Option InstantText"], onClickFunc = Options_InstantText_OnClick, dbKey = "TalkingHead_InstantText"},
|
||
|
|
{type = "Checkbox", label = L["TalkingHead Option TextOutline"], onClickFunc = Options_TextOutline_OnClick, dbKey = "TalkingHead_TextOutline"},
|
||
|
|
{type = "Slider", label = L["Font Size"], minValue = 100, maxValue = 120, valueStep = 10, onValueChangedFunc = Options_FontSizeSlider_OnValueChanged, formatValueFunc = Options_FontSizeSlider_FormatValue, dbKey = "TalkingHead_FontSize"},
|
||
|
|
|
||
|
|
{type = "Divider"},
|
||
|
|
{type = "Header", label = L["TalkingHead Option Condition Header"]};
|
||
|
|
{type = "Checkbox", label = L["TalkingHead Option Condition Instance"] , onClickFunc = Options_HideInInstance_OnClick, dbKey = "TalkingHead_HideInInstance", tooltip = L["TalkingHead Option Condition Instance Tooltip"]},
|
||
|
|
{type = "Checkbox", label = L["TalkingHead Option Condition WorldQuest"], onClickFunc = Options_HideWorldQuest_OnClick, dbKey = "TalkingHead_HideWorldQuest", tooltip = L["TalkingHead Option Condition WorldQuest Tooltip"]},
|
||
|
|
|
||
|
|
{type = "Divider"},
|
||
|
|
{type = "UIPanelButton", label = L["Reset To Default Position"], onClickFunc = Options_ResetPosition_OnClick, stateCheckFunc = Options_ResetPosition_ShouldEnable, widgetKey = "ResetButton"},
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
function NewTalkingHead:CreateOptions()
|
||
|
|
self.OptionFrame = addon.SetupSettingsDialog(self, OPTIONS_SCHEMATIC);
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:ShowOptions(state)
|
||
|
|
if state then
|
||
|
|
self:CreateOptions();
|
||
|
|
self.OptionFrame:Show();
|
||
|
|
if self.OptionFrame.requireResetPosition then
|
||
|
|
self.OptionFrame.requireResetPosition = false;
|
||
|
|
self.OptionFrame:ClearAllPoints();
|
||
|
|
self.OptionFrame:SetPoint("LEFT", UIParent, "CENTER", TEXT_WIDTH * 0.5, 0);
|
||
|
|
end
|
||
|
|
else
|
||
|
|
if self.OptionFrame then
|
||
|
|
self.OptionFrame:Hide();
|
||
|
|
end
|
||
|
|
if not API.IsInEditMode() then
|
||
|
|
self:CloseImmediately();
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:ToggleOptions()
|
||
|
|
self:ShowOptions( self.OptionFrame and self.OptionFrame:IsShown() );
|
||
|
|
NewTalkingHead:EnterEditMode()
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:OnDragStart()
|
||
|
|
self:SetMovable(true);
|
||
|
|
self:SetDontSavePosition(true);
|
||
|
|
self:SetClampedToScreen(true);
|
||
|
|
self:StartMoving();
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:OnDragStop()
|
||
|
|
self:StopMovingOrSizing();
|
||
|
|
|
||
|
|
local centerX = self:GetCenter();
|
||
|
|
local uiCenter = UIParent:GetCenter();
|
||
|
|
local left = self:GetLeft();
|
||
|
|
local top = self:GetTop();
|
||
|
|
|
||
|
|
left = Round(left);
|
||
|
|
top = Round(top);
|
||
|
|
|
||
|
|
self:ClearAllPoints();
|
||
|
|
|
||
|
|
--Convert anchor and save position
|
||
|
|
if math.abs(uiCenter - centerX) <= 48 then
|
||
|
|
--Snap to centeral line
|
||
|
|
self:SetPoint("TOP", UIParent, "BOTTOM", 0, top);
|
||
|
|
DB.TalkingHead_PositionX = -1;
|
||
|
|
|
||
|
|
else
|
||
|
|
self:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", left, top);
|
||
|
|
DB.TalkingHead_PositionX = left;
|
||
|
|
end
|
||
|
|
DB.TalkingHead_PositionY = top;
|
||
|
|
|
||
|
|
if self.OptionFrame then
|
||
|
|
local button = self.OptionFrame:FindWidget("ResetButton");
|
||
|
|
if button then
|
||
|
|
button:Enable();
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:UpdateFrameSize()
|
||
|
|
local height = (self.LineText:GetHeight() or 0) + self.fontHeight;
|
||
|
|
self:SetHeight(height);
|
||
|
|
end
|
||
|
|
|
||
|
|
local MuteDelay;
|
||
|
|
local function MuteDelay_OnUpdate(self, elapsed)
|
||
|
|
self.t = self.t + elapsed;
|
||
|
|
if self.t > self.duration then
|
||
|
|
self.t = nil;
|
||
|
|
self:SetScript("OnUpdate", nil);
|
||
|
|
NewTalkingHead.shouldMuteLine = nil;
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function NewTalkingHead:MuteNextLine(duration)
|
||
|
|
--Sound is still played, just no text
|
||
|
|
--Doesn't always work when you accept multiple WQ at the same location
|
||
|
|
if not MuteDelay then
|
||
|
|
MuteDelay = CreateFrame("Frame");
|
||
|
|
end
|
||
|
|
if (not duration) or duration <= 0.5 then
|
||
|
|
duration = 0.5;
|
||
|
|
end
|
||
|
|
if MuteDelay.t then
|
||
|
|
MuteDelay.duration = (MuteDelay.duration or 0) + duration + 0.5;
|
||
|
|
else
|
||
|
|
MuteDelay.t = 0;
|
||
|
|
MuteDelay.duration = duration;
|
||
|
|
end
|
||
|
|
MuteDelay:SetScript("OnUpdate", MuteDelay_OnUpdate);
|
||
|
|
self.shouldMuteLine = true;
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
do
|
||
|
|
local function EnableModule(state)
|
||
|
|
if state then
|
||
|
|
NewTalkingHead:EnableTalkingHead();
|
||
|
|
else
|
||
|
|
NewTalkingHead:DisableTalkingHead();
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function OptionToggle_OnClick(self, button)
|
||
|
|
if NewTalkingHead.OptionFrame and NewTalkingHead.OptionFrame:IsShown() then
|
||
|
|
NewTalkingHead:ShowOptions(false);
|
||
|
|
NewTalkingHead:ExitEditMode();
|
||
|
|
else
|
||
|
|
NewTalkingHead:EnterEditMode();
|
||
|
|
NewTalkingHead:ShowOptions(true);
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local moduleData = {
|
||
|
|
name = addon.L["ModuleName TalkingHead"],
|
||
|
|
dbKey = "TalkingHead_MasterSwitch",
|
||
|
|
description = addon.L["ModuleDescription TalkingHead"],
|
||
|
|
toggleFunc = EnableModule,
|
||
|
|
categoryID = 2,
|
||
|
|
uiOrder = 3,
|
||
|
|
moduleAddedTime = 1704423300,
|
||
|
|
optionToggleFunc = OptionToggle_OnClick,
|
||
|
|
};
|
||
|
|
|
||
|
|
addon.ControlCenter:AddModule(moduleData);
|
||
|
|
end
|