Created
January 29, 2023 05:53
-
-
Save nolvuscodes/4733439df0ceec9ca0827209c342be1d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Who objects to the ObjectiveTracker...? | |
OBJECTIVE_TRACKER_ITEM_WIDTH = 33; | |
OBJECTIVE_TRACKER_HEADER_HEIGHT = 25; | |
OBJECTIVE_TRACKER_LINE_WIDTH = 248; | |
OBJECTIVE_TRACKER_HEADER_OFFSET_X = -10; | |
-- calculated values | |
OBJECTIVE_TRACKER_DASH_WIDTH = 0; | |
OBJECTIVE_TRACKER_TEXT_WIDTH = 0; | |
OBJECTIVE_TRACKER_COLOR = { | |
["Normal"] = { r = 0.8, g = 0.8, b = 0.8 }, | |
["NormalHighlight"] = { r = HIGHLIGHT_FONT_COLOR.r, g = HIGHLIGHT_FONT_COLOR.g, b = HIGHLIGHT_FONT_COLOR.b }, | |
["Failed"] = { r = DIM_RED_FONT_COLOR.r, g = DIM_RED_FONT_COLOR.g, b = DIM_RED_FONT_COLOR.b }, | |
["FailedHighlight"] = { r = RED_FONT_COLOR.r, g = RED_FONT_COLOR.g, b = RED_FONT_COLOR.b }, | |
["Header"] = { r = 0.75, g = 0.61, b = 0 }, | |
["HeaderHighlight"] = { r = NORMAL_FONT_COLOR.r, g = NORMAL_FONT_COLOR.g, b = NORMAL_FONT_COLOR.b }, | |
["Complete"] = { r = 0.6, g = 0.6, b = 0.6 }, | |
["TimeLeft"] = { r = DIM_RED_FONT_COLOR.r, g = DIM_RED_FONT_COLOR.g, b = DIM_RED_FONT_COLOR.b }, | |
["TimeLeftHighlight"] = { r = RED_FONT_COLOR.r, g = RED_FONT_COLOR.g, b = RED_FONT_COLOR.b }, | |
}; | |
OBJECTIVE_TRACKER_COLOR["Normal"].reverse = OBJECTIVE_TRACKER_COLOR["NormalHighlight"]; | |
OBJECTIVE_TRACKER_COLOR["NormalHighlight"].reverse = OBJECTIVE_TRACKER_COLOR["Normal"]; | |
OBJECTIVE_TRACKER_COLOR["Failed"].reverse = OBJECTIVE_TRACKER_COLOR["FailedHighlight"]; | |
OBJECTIVE_TRACKER_COLOR["FailedHighlight"].reverse = OBJECTIVE_TRACKER_COLOR["Failed"]; | |
OBJECTIVE_TRACKER_COLOR["Header"].reverse = OBJECTIVE_TRACKER_COLOR["HeaderHighlight"]; | |
OBJECTIVE_TRACKER_COLOR["HeaderHighlight"].reverse = OBJECTIVE_TRACKER_COLOR["Header"]; | |
OBJECTIVE_TRACKER_COLOR["TimeLeft"].reverse = OBJECTIVE_TRACKER_COLOR["TimeLeftHighlight"]; | |
OBJECTIVE_TRACKER_COLOR["TimeLeftHighlight"].reverse = OBJECTIVE_TRACKER_COLOR["TimeLeft"]; | |
-- these are generally from events | |
OBJECTIVE_TRACKER_UPDATE_QUEST = 0x00001; | |
OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED = 0x00002; | |
OBJECTIVE_TRACKER_UPDATE_TASK_ADDED = 0x00004; | |
OBJECTIVE_TRACKER_UPDATE_WORLD_QUEST_ADDED = 0x00008; | |
OBJECTIVE_TRACKER_UPDATE_SCENARIO = 0x00010; | |
OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE = 0x00020; | |
OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT = 0x00040; | |
OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED = 0x00080; | |
OBJECTIVE_TRACKER_UPDATE_SCENARIO_BONUS_DELAYED = 0x00100; | |
OBJECTIVE_TRACKER_UPDATE_SUPER_TRACK_CHANGED = 0x00200; | |
OBJECTIVE_TRACKER_UPDATE_MOVED = 0x80000; | |
-- these are for the specific module ONLY! | |
OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST = 0x00400; | |
OBJECTIVE_TRACKER_UPDATE_MODULE_AUTO_QUEST_POPUP = 0x00800; | |
OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE = 0x01000; | |
OBJECTIVE_TRACKER_UPDATE_MODULE_WORLD_QUEST = 0x02000; | |
OBJECTIVE_TRACKER_UPDATE_MODULE_SCENARIO = 0x04000; | |
OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT = 0x08000; | |
OBJECTIVE_TRACKER_UPDATE_SCENARIO_SPELLS = 0x10000; | |
OBJECTIVE_TRACKER_UPDATE_MODULE_UI_WIDGETS = 0x20000; | |
OBJECTIVE_TRACKER_UPDATE_MODULE_PROFESSION_RECIPE = 0x40000; | |
OBJECTIVE_TRACKER_UPDATE_MODULE_MONTHLY_ACTIVITIES = 0x80000; | |
-- special updates | |
OBJECTIVE_TRACKER_UPDATE_STATIC = 0x0000; | |
OBJECTIVE_TRACKER_UPDATE_ALL = 0xFFFFFFFF; | |
OBJECTIVE_TRACKER_UPDATE_REASON = OBJECTIVE_TRACKER_UPDATE_ALL; -- default | |
OBJECTIVE_TRACKER_UPDATE_ID = 0; | |
-- speed optimizations | |
local floor = math.floor; | |
local min = min; | |
local band = bit.band; | |
-- ***************************************************************************************************** | |
-- ***** MODULE STUFF | |
-- ***************************************************************************************************** | |
--[[ | |
blockTemplate: template for the blocks - a quest would be a single block | |
blockType: type of object | |
lineTemplate: template for the lines - a quest objective would be a single line (even if it wordwraps); only FRAME supported | |
lineSpacing: spacing between lines; for the first line it'll be the distance from the top of its block | |
blockOffsetX: offset from the left edge of the blocksframe \__These are both added to a table indexed by blockTemplate. | |
blockOffsetY: offset from the block above / | |
fromHeaderOffsetY: offset from the header for the first block, if there's a header; used instead of blockOffsetY | |
fromModuleOffsetY: offset from the previous module | |
poolCollection: pool of (potentially) multiple frame types for use in a module | |
usedBlocks: table of used blocks; a module should always have its own, this table uses template type so that GetBlock(id) ALWAYS returns | |
the correct frame that already exists (for animation purposes) | |
freelines: table of free lines; a module needs it own if not using default line template | |
there's no table of used lines, that's per block | |
updateReasonModule: the update for this module alone | |
updateReasonEvents: the events which should update the module | |
=== modules do not need to change these, they're keyed by block & line === | |
usedTimerBars: table of used timer bars | |
freeTimerBars: table of free timer bars | |
=== modules should NOT change these === | |
contentsHeight: the current combined height of all the blocks in the module | |
contentsAnimHeight: the current combined animation height of all the blocks in the module | |
oldContentsHeight: the previous height on the last update | |
hasSkippedBlocks: if the module couldn't display all its blocks because of not enough space | |
--]] | |
DEFAULT_OBJECTIVE_TRACKER_MODULE = {}; | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:OnLoad(friendlyName, defaultTemplate) | |
self.friendlyName = friendlyName or "UnnamedTrackerModule"; | |
self.blockTemplate = defaultTemplate or "ObjectiveTrackerBlockTemplate"; | |
self.blockType = "Frame"; | |
self.lineTemplate = "ObjectiveTrackerLineTemplate"; | |
self.lineSpacing = 2; | |
self.lineWidth = OBJECTIVE_TRACKER_TEXT_WIDTH; | |
self.poolCollection = CreateFramePoolCollection(); | |
self.usedBlocks = { }; | |
self.freeLines = { }; | |
self.fromHeaderOffsetY = -10; | |
self.fromModuleOffsetY = -10; | |
self.contentsHeight = 0; | |
self.contentsAnimHeight = 0; | |
self.oldContentsHeight = 0; | |
self.hasSkippedBlocks = false; | |
self.usedTimerBars = { }; | |
self.freeTimerBars = { }; | |
self.usedProgressBars = { }; | |
self.freeProgressBars = { }; | |
self.updateReasonModule = 0; | |
self.updateReasonEvents = 0; | |
self.BlocksFrame = ObjectiveTrackerFrame.BlocksFrame; | |
DEFAULT_OBJECTIVE_TRACKER_MODULE.AddBlockOffset(self, self.blockTemplate, 0, -6); | |
end | |
function ObjectiveTracker_GetModuleInfoTable(friendlyName, baseModule, defaultTemplate) | |
local info = CreateFromMixins(baseModule or DEFAULT_OBJECTIVE_TRACKER_MODULE); | |
info:OnLoad(friendlyName, defaultTemplate); | |
return info; | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:BeginLayout(isStaticReanchor) | |
self.topBlock = nil; -- this is the header or the first block for header-less modules | |
self.firstBlock = nil; -- this is the first non-header block | |
self.lastBlock = nil; | |
self.oldContentsHeight = self.contentsHeight; | |
self.contentsHeight = 0; | |
self.contentsAnimHeight = 0; | |
self.potentialBlocksAddedThisLayout = 0; -- this isn't a ref count, this is the total number of blocks that the module tried to add. | |
-- if it's not a static reanchor, reset whether we've skipped blocks | |
if ( not isStaticReanchor ) then | |
self.hasSkippedBlocks = false; | |
if not self:UsesSharedHeader() and self.Header then | |
self.Header:Hide(); | |
end | |
end | |
self:MarkBlocksUnused(); | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:EndLayout(isStaticReanchor) | |
-- isStaticReanchor not used yet | |
self.lastBlock = self.BlocksFrame.currentBlock; | |
self:FreeUnusedBlocks(); | |
end | |
-- ***** BLOCKS | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:SetHeader(block, text, animateReason) | |
block.module = self; | |
block.isHeader = true; | |
block.Text:SetText(text); | |
block.animateReason = animateReason or 0; | |
self.Header = block; | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:SetSharedHeader(block) | |
self.Header = block; | |
self.usesSharedHeader = true; | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:UsesSharedHeader() | |
return self.usesSharedHeader; | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:GetBlock(id, overrideType, overrideTemplate) | |
local blockType = overrideType or self.blockType; | |
local blockTemplate = overrideTemplate or self.blockTemplate; | |
if not self.usedBlocks[blockTemplate] then | |
self.usedBlocks[blockTemplate] = {}; | |
end | |
-- first try to return existing block | |
local block = self.usedBlocks[blockTemplate][id]; | |
if not block then | |
local pool = self.poolCollection:GetOrCreatePool(blockType, self.BlocksFrame or ObjectiveTrackerFrame.BlocksFrame, blockTemplate); | |
local isNewBlock = nil; | |
block, isNewBlock = pool:Acquire(blockTemplate); | |
if isNewBlock then | |
block.blockTemplate = blockTemplate; -- stored so we can use it to free from the lookup later | |
block.lines = {}; | |
end | |
self.usedBlocks[blockTemplate][id] = block; | |
block.id = id; | |
block.module = self; | |
end | |
block.used = true; | |
block.height = 0; | |
block.currentLine = nil; | |
-- prep lines | |
if block.lines then | |
for objectiveKey, line in pairs(block.lines) do | |
line.used = nil; | |
end | |
end | |
return block; | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:GetExistingBlock(id, overrideTemplate) | |
local template = overrideTemplate or self.blockTemplate; | |
assert(template); | |
assert(self.usedBlocks) | |
local blocks = self.usedBlocks[template]; | |
if blocks then | |
return blocks[id]; | |
end | |
return nil; | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:MarkBlocksUnused() | |
for blockTemplate, blockTable in pairs(self.usedBlocks) do | |
for blockID, block in pairs(blockTable) do | |
block.used = nil; | |
end | |
end | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:FreeBlock(block) | |
-- free all the lines | |
for _, line in pairs(block.lines) do | |
self:FreeLine(block, line); | |
end | |
block.lines = { }; | |
-- free the block | |
self.usedBlocks[block.blockTemplate][block.id] = nil; | |
self.poolCollection:Release(block); | |
-- callback | |
if ( self.OnFreeBlock ) then | |
self:OnFreeBlock(block); | |
end | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:FreeUnusedBlocks() | |
for blockTemplate, blockTable in pairs(self.usedBlocks) do | |
for blockID, block in pairs(blockTable) do | |
if not block.used then | |
self:FreeBlock(block); | |
end | |
end | |
end | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:GetBlockCount() | |
local count = 0; | |
local modules = self:GetRelatedModules(); | |
for index, module in ipairs(modules) do | |
count = count + (module.potentialBlocksAddedThisLayout or 0); | |
end | |
return count; | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:GetActiveBlocks(blockTemplate) | |
-- By default use the module's preferred block type. | |
blockTemplate = blockTemplate or self.blockTemplate; | |
if not self.usedBlocks[blockTemplate] then | |
self.usedBlocks[blockTemplate] = {}; | |
end | |
return self.usedBlocks[blockTemplate]; | |
end | |
-- ***** LINES | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:FreeLine(block, line) | |
block.lines[line.objectiveKey] = nil; | |
-- if the line has a type, the freeLines will be the cache for that type of line, otherwise use the module's default | |
local freeLines = (line.type and line.type.freeLines) or self.freeLines; | |
tinsert(freeLines, line); | |
-- remove timer bar | |
if ( line.TimerBar ) then | |
self:FreeTimerBar(block, line); | |
end | |
if ( line.ProgressBar ) then | |
self:FreeProgressBar(block, line); | |
end | |
if ( line.type and self.OnFreeTypedLine ) then | |
self:OnFreeTypedLine(line); | |
elseif ( self.OnFreeLine ) then | |
self:OnFreeLine(line); | |
end | |
line:Hide(); | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:FreeUnusedLines(block) | |
for objectiveKey, line in pairs(block.lines) do | |
if ( not line.used ) then | |
self:FreeLine(block, line); | |
end | |
end | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:GetLine(block, objectiveKey, lineType) | |
-- first look for existing line | |
local line = block.lines[objectiveKey]; | |
-- if existing line is not of the same type, discard it | |
if ( line and line.type ~= lineType ) then | |
self:FreeLine(block, line); | |
line = nil; | |
end | |
if ( line ) then | |
line.used = true; | |
return line; | |
end | |
local freeLines = (lineType and lineType.freeLines) or self.freeLines; | |
local numFreeLines = #freeLines; | |
local parent = block.ScrollContents or block; | |
if ( numFreeLines > 0 ) then | |
-- get a free line | |
line = freeLines[numFreeLines]; | |
tremove(freeLines, numFreeLines); | |
line:SetParent(parent); | |
line:Show(); | |
else | |
-- create a new line | |
line = CreateFrame("Frame", nil, parent, (lineType and lineType.template) or self.lineTemplate); | |
line.type = lineType; | |
end | |
block.lines[objectiveKey] = line; | |
line.objectiveKey = objectiveKey; | |
line.used = true; | |
return line; | |
end | |
-- ***** OBJECTIVES | |
OBJECTIVE_DASH_STYLE_SHOW = 1; | |
OBJECTIVE_DASH_STYLE_HIDE = 2; | |
OBJECTIVE_DASH_STYLE_HIDE_AND_COLLAPSE = 3; | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:AddObjective(block, objectiveKey, text, lineType, useFullHeight, dashStyle, colorStyle, adjustForNoText, overrideHeight) | |
local line = self:GetLine(block, objectiveKey, lineType); | |
-- width | |
if ( block.lineWidth ~= line.width ) then | |
line.Text:SetWidth(block.lineWidth or self.lineWidth); | |
line.width = block.lineWidth; -- default should be nil | |
end | |
-- dash | |
if ( line.Dash ) then | |
if ( not dashStyle ) then | |
dashStyle = OBJECTIVE_DASH_STYLE_SHOW; | |
end | |
if ( line.dashStyle ~= dashStyle ) then | |
if ( dashStyle == OBJECTIVE_DASH_STYLE_SHOW ) then | |
line.Dash:Show(); | |
line.Dash:SetText(QUEST_DASH); | |
elseif ( dashStyle == OBJECTIVE_DASH_STYLE_HIDE ) then | |
line.Dash:Hide(); | |
line.Dash:SetText(QUEST_DASH); | |
elseif ( dashStyle == OBJECTIVE_DASH_STYLE_HIDE_AND_COLLAPSE ) then | |
line.Dash:Hide(); | |
line.Dash:SetText(nil); | |
else | |
error("Invalid dash style: " .. tostring(dashStyle)); | |
end | |
line.dashStyle = dashStyle; | |
end | |
end | |
-- set the text | |
local textHeight = self:SetStringText(line.Text, text, useFullHeight, colorStyle, block.isHighlighted); | |
local height = overrideHeight or textHeight; | |
line:SetHeight(height); | |
local yOffset; | |
if ( adjustForNoText and text == "" ) then | |
-- don't change the height | |
-- move the line up so the next object ends up in the same position as if there had been no line | |
yOffset = height; | |
else | |
block.height = block.height + height + block.module.lineSpacing; | |
yOffset = -block.module.lineSpacing; | |
end | |
-- anchor the line | |
local anchor = block.currentLine or block.HeaderText; | |
if ( anchor ) then | |
line:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, yOffset); | |
else | |
line:SetPoint("TOPLEFT", 0, yOffset); | |
end | |
block.currentLine = line; | |
return line; | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:SetStringText(fontString, text, useFullHeight, colorStyle, useHighlight) | |
if useFullHeight then | |
fontString:SetMaxLines(0); | |
else | |
fontString:SetMaxLines(2); | |
end | |
fontString:SetText(text); | |
local stringHeight = fontString:GetHeight(); | |
colorStyle = colorStyle or OBJECTIVE_TRACKER_COLOR["Normal"]; | |
if ( useHighlight and colorStyle.reverse ) then | |
colorStyle = colorStyle.reverse; | |
end | |
if ( fontString.colorStyle ~= colorStyle ) then | |
fontString:SetTextColor(colorStyle.r, colorStyle.g, colorStyle.b); | |
fontString.colorStyle = colorStyle; | |
end | |
return stringHeight; | |
end | |
-- ***** BLOCK HEADER | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:SetBlockHeader(block, text) | |
local height = self:SetStringText(block.HeaderText, text, nil, OBJECTIVE_TRACKER_COLOR["Header"], block.isHighlighted); | |
block.height = height; | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:OnBlockHeaderClick(block, mouseButton) | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:OnBlockHeaderEnter(block) | |
block.isHighlighted = true; | |
if ( block.HeaderText ) then | |
local headerColorStyle = OBJECTIVE_TRACKER_COLOR["HeaderHighlight"]; | |
block.HeaderText:SetTextColor(headerColorStyle.r, headerColorStyle.g, headerColorStyle.b); | |
block.HeaderText.colorStyle = headerColorStyle; | |
end | |
for objectiveKey, line in pairs(block.lines) do | |
local colorStyle = line.Text.colorStyle.reverse; | |
if ( colorStyle ) then | |
line.Text:SetTextColor(colorStyle.r, colorStyle.g, colorStyle.b); | |
line.Text.colorStyle = colorStyle; | |
if ( line.Dash ) then | |
line.Dash:SetTextColor(OBJECTIVE_TRACKER_COLOR["NormalHighlight"].r, OBJECTIVE_TRACKER_COLOR["NormalHighlight"].g, OBJECTIVE_TRACKER_COLOR["NormalHighlight"].b); | |
end | |
end | |
end | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:OnBlockHeaderLeave(block) | |
block.isHighlighted = nil; | |
if ( block.HeaderText ) then | |
local headerColorStyle = OBJECTIVE_TRACKER_COLOR["Header"]; | |
block.HeaderText:SetTextColor(headerColorStyle.r, headerColorStyle.g, headerColorStyle.b); | |
block.HeaderText.colorStyle = headerColorStyle; | |
end | |
for objectiveKey, line in pairs(block.lines) do | |
local colorStyle = line.Text.colorStyle.reverse; | |
if ( colorStyle ) then | |
line.Text:SetTextColor(colorStyle.r, colorStyle.g, colorStyle.b); | |
line.Text.colorStyle = colorStyle; | |
if ( line.Dash ) then | |
line.Dash:SetTextColor(OBJECTIVE_TRACKER_COLOR["Normal"].r, OBJECTIVE_TRACKER_COLOR["Normal"].g, OBJECTIVE_TRACKER_COLOR["Normal"].b); | |
end | |
end | |
end | |
end | |
-- ***** TIMER BAR | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:AddTimerBar(block, line, duration, startTime) | |
local timerBar = self.usedTimerBars[block] and self.usedTimerBars[block][line]; | |
if ( not timerBar ) then | |
local numFreeTimerBars = #self.freeTimerBars; | |
local parent = block.ScrollContents or block; | |
if ( numFreeTimerBars > 0 ) then | |
timerBar = self.freeTimerBars[numFreeTimerBars]; | |
tremove(self.freeTimerBars, numFreeTimerBars); | |
timerBar:SetParent(parent); | |
timerBar:Show(); | |
else | |
timerBar = CreateFrame("Frame", nil, parent, "ObjectiveTrackerTimerBarTemplate"); | |
timerBar.Label:SetPoint("LEFT", OBJECTIVE_TRACKER_DASH_WIDTH, 0); | |
timerBar.height = timerBar:GetHeight(); | |
end | |
if ( not self.usedTimerBars[block] ) then | |
self.usedTimerBars[block] = { }; | |
end | |
self.usedTimerBars[block][line] = timerBar; | |
timerBar:Show(); | |
end | |
-- anchor the status bar | |
local anchor = block.currentLine or block.HeaderText; | |
if ( anchor ) then | |
timerBar:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, -block.module.lineSpacing); | |
else | |
timerBar:SetPoint("TOPLEFT", 0, -block.module.lineSpacing); | |
end | |
timerBar.Bar:SetMinMaxValues(0, duration); | |
timerBar.duration = duration; | |
timerBar.startTime = startTime; | |
timerBar.block = block; | |
line.TimerBar = timerBar; | |
block.height = block.height + timerBar.height + block.module.lineSpacing; | |
block.currentLine = timerBar; | |
return timerBar; | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:FreeTimerBar(block, line) | |
local timerBar = line.TimerBar; | |
if ( timerBar ) then | |
self.usedTimerBars[block][line] = nil; | |
tinsert(self.freeTimerBars, timerBar); | |
timerBar:Hide(); | |
line.TimerBar = nil; | |
end | |
end | |
-- ***************************************************************************************************** | |
-- ***** PROGRESS BAR | |
-- ***************************************************************************************************** | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:AddProgressBar(block, line, questID) | |
local progressBar = self.usedProgressBars[block] and self.usedProgressBars[block][line]; | |
if ( not progressBar ) then | |
local numFreeProgressBars = #self.freeProgressBars; | |
local parent = block.ScrollContents or block; | |
if ( numFreeProgressBars > 0 ) then | |
progressBar = self.freeProgressBars[numFreeProgressBars]; | |
tremove(self.freeProgressBars, numFreeProgressBars); | |
progressBar:SetParent(parent); | |
progressBar:Show(); | |
else | |
progressBar = CreateFrame("Frame", nil, parent, "ObjectiveTrackerProgressBarTemplate"); | |
progressBar.height = progressBar:GetHeight(); | |
end | |
if ( not self.usedProgressBars[block] ) then | |
self.usedProgressBars[block] = { }; | |
end | |
self.usedProgressBars[block][line] = progressBar; | |
progressBar:RegisterEvent("QUEST_LOG_UPDATE"); | |
progressBar:Show(); | |
-- initialize to the right values | |
progressBar.questID = questID; | |
ObjectiveTrackerProgressBar_SetValue(progressBar, GetQuestProgressBarPercent(questID)); | |
end | |
-- anchor the status bar | |
local anchor = block.currentLine or block.HeaderText; | |
if ( anchor ) then | |
progressBar:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, -block.module.lineSpacing); | |
else | |
progressBar:SetPoint("TOPLEFT", 0, -block.module.lineSpacing); | |
end | |
progressBar.block = block; | |
progressBar.questID = questID; | |
line.ProgressBar = progressBar; | |
block.height = block.height + progressBar.height + block.module.lineSpacing; | |
block.currentLine = progressBar; | |
return progressBar; | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:FreeProgressBar(block, line) | |
local progressBar = line.ProgressBar; | |
if ( progressBar ) then | |
self.usedProgressBars[block][line] = nil; | |
tinsert(self.freeProgressBars, progressBar); | |
progressBar:Hide(); | |
line.ProgressBar = nil; | |
progressBar:UnregisterEvent("QUEST_LOG_UPDATE"); | |
end | |
end | |
local function ObjectiveTracker_SetModulesCollapsed(collapsed, modules) | |
for index, module in ipairs(modules) do | |
module.collapsed = collapsed; | |
end | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:GetRelatedModules() | |
-- Default implementation, most single/shared modules can be found this way | |
-- NOTE: This actually inserts self as well, since the header matches, that's fine. | |
local modules = {}; | |
local header = self.Header; | |
for index, module in ipairs(ObjectiveTrackerFrame.MODULES) do | |
if module.Header == header then | |
table.insert(modules, module); | |
end | |
end | |
return modules; | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:SetCollapsed(collapsed) | |
ObjectiveTracker_SetModulesCollapsed(collapsed, self:GetRelatedModules()); | |
if self.Header and self.Header.MinimizeButton then | |
self.Header.MinimizeButton:SetCollapsed(collapsed); | |
end | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:IsCollapsed() | |
return self.collapsed; | |
end | |
-- ***************************************************************************************************** | |
-- ***** MODULE/BLOCK CUSTOMIZATION | |
-- ***************************************************************************************************** | |
local function ObjectiveTracker_AddCustomizationData(module, customizationKey, template, data) | |
if not module[customizationKey] then | |
module[customizationKey] = {}; | |
end | |
module[customizationKey][template] = data; | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:AddButtonOffsets(template, offsets) | |
ObjectiveTracker_AddCustomizationData(self, "buttonOffsets", template, offsets); | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:AddBlockOffset(template, x, y) | |
ObjectiveTracker_AddCustomizationData(self, "blockOffset", template, { x or 0, y or 0 }); | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:AddPaddingBetweenButtons(template, padding) | |
ObjectiveTracker_AddCustomizationData(self, "paddingBetweenButtons", template, padding); | |
end | |
local function GetBlockTemplate(block) | |
return block.blockTemplate or block.module.blockTemplate; | |
end | |
function ObjectiveTracker_GetButtonOffsets(block, offsetTag) | |
local offsets = block.module.buttonOffsets; | |
if offsets then | |
return unpack(offsets[GetBlockTemplate(block)][offsetTag]); | |
end | |
return 0, 0; | |
end | |
function ObjectiveTracker_GetBlockOffset(block) | |
local offset = block.module.blockOffset; | |
if offset then | |
return unpack(offset[GetBlockTemplate(block)]); | |
end | |
return 0, 0; | |
end | |
function ObjectiveTracker_GetPaddingBetweenButtons(block) | |
local padding = block.module.paddingBetweenButtons; | |
if padding then | |
return padding[GetBlockTemplate(block)]; | |
end | |
return 0; | |
end | |
-- ***************************************************************************************************** | |
-- ***** BLOCK HEADER HANDLERS | |
-- ***************************************************************************************************** | |
function ObjectiveTrackerBlockHeader_OnLoad(self) | |
self:RegisterForClicks("LeftButtonUp", "RightButtonUp"); | |
self.GetDebugReportInfo = ObjectiveTrackerBlockHeader_GetDebugReportInfo; | |
end | |
function ObjectiveTrackerBlockHeader_OnClick(self, mouseButton) | |
local block = self:GetParent(); | |
block.module:OnBlockHeaderClick(block, mouseButton); | |
end | |
function ObjectiveTrackerBlockHeader_OnEnter(self) | |
local block = self:GetParent(); | |
block.module:OnBlockHeaderEnter(block); | |
end | |
function ObjectiveTrackerBlockHeader_OnLeave(self) | |
local block = self:GetParent(); | |
block.module:OnBlockHeaderLeave(block); | |
end | |
function ObjectiveTrackerBlockHeader_GetDebugReportInfo(self) | |
local block = self:GetParent(); | |
if block.module.GetDebugReportInfo then | |
return block.module:GetDebugReportInfo(block); | |
end | |
return nil; | |
end | |
-- ***************************************************************************************************** | |
-- ***** OBJECTIVE TRACKER LINES | |
-- ***************************************************************************************************** | |
function ObjectiveTrackerCheckLine_OnHide(self) | |
self.Glow.Anim:Stop(); | |
self.Sheen.Anim:Stop(); | |
end | |
-- ***************************************************************************************************** | |
-- ***** TIMER BARS | |
-- ***************************************************************************************************** | |
function ObjectiveTrackerTimerBar_OnUpdate(self, elapsed) | |
local timeNow = GetTime(); | |
local timeRemaining = self.duration - (timeNow - self.startTime); | |
self.Bar:SetValue(timeRemaining); | |
if ( timeRemaining < 0 ) then | |
-- hold at 0 for a moment | |
if ( timeRemaining > -1 ) then | |
timeRemaining = 0; | |
else | |
ObjectiveTracker_Update(self.block.module.updateReasonModule); | |
return; | |
end | |
end | |
self.Label:SetText(SecondsToClock(timeRemaining)); | |
self.Label:SetTextColor(ObjectiveTrackerTimerBar_GetTextColor(self.duration, self.duration - timeRemaining)); | |
end | |
function ObjectiveTrackerTimerBar_GetTextColor(duration, elapsed) | |
local START_PERCENTAGE_YELLOW = .66 | |
local START_PERCENTAGE_RED = .33 | |
local percentageLeft = 1 - ( elapsed / duration ) | |
if ( percentageLeft > START_PERCENTAGE_YELLOW ) then | |
return 1, 1, 1; | |
elseif ( percentageLeft > START_PERCENTAGE_RED ) then -- Start fading to yellow by eliminating blue | |
local blueOffset = (percentageLeft - START_PERCENTAGE_RED) / (START_PERCENTAGE_YELLOW - START_PERCENTAGE_RED); | |
return 1, 1, blueOffset; | |
else | |
local greenOffset = percentageLeft / START_PERCENTAGE_RED; -- Fade to red by eliminating green | |
return 1, greenOffset, 0; | |
end | |
end | |
-- ***************************************************************************************************** | |
-- ***** PROGRESS BARS | |
-- ***************************************************************************************************** | |
function ObjectiveTrackerProgressBar_SetValue(self, percent) | |
self.Bar:SetValue(percent); | |
self.Bar.Label:SetFormattedText(PERCENTAGE_STRING, percent); | |
end | |
function ObjectiveTrackerProgressBar_OnEvent(self) | |
ObjectiveTrackerProgressBar_SetValue(self, GetQuestProgressBarPercent(self.questID)); | |
end | |
-- ***************************************************************************************************** | |
-- ***** FRAME HANDLERS | |
-- ***************************************************************************************************** | |
function ObjectiveTracker_OnLoad(self) | |
DEFAULT_OBJECTIVE_TRACKER_MODULE.OnLoad(self, "DEFAULT_OBJECTIVE_TRACKER_MODULE"); | |
-- create a line so we can get some measurements | |
local line = CreateFrame("Frame", nil, self, self.lineTemplate); | |
line.Text:SetText("Double line|ntest"); | |
-- reuse it | |
tinsert(self.freeLines, line); | |
-- get measurements | |
OBJECTIVE_TRACKER_DASH_WIDTH = line.Dash:GetWidth(); | |
OBJECTIVE_TRACKER_TEXT_WIDTH = OBJECTIVE_TRACKER_LINE_WIDTH - OBJECTIVE_TRACKER_DASH_WIDTH - 12; | |
line.Text:SetWidth(OBJECTIVE_TRACKER_TEXT_WIDTH); | |
local frameLevel = self.BlocksFrame:GetFrameLevel(); | |
self.HeaderMenu:SetFrameLevel(frameLevel + 2); | |
self:RegisterEvent("PLAYER_ENTERING_WORLD"); | |
UIDropDownMenu_Initialize(self.BlockDropDown, nil, "MENU"); | |
QuestPOI_Initialize(self.BlocksFrame, function(self) self:SetScale(0.9); self:RegisterForClicks("LeftButtonUp", "RightButtonUp"); end ); | |
end | |
function ObjectiveTracker_UpdateHeight() | |
local self = ObjectiveTrackerFrame; | |
if not self:IsInDefaultPosition() then | |
-- Assure we can fit the scenario block since it has gameplay implications | |
local isScenarioBlockShowing = ScenarioBlocksFrame and ScenarioBlocksFrame:IsShown(); | |
local scenarioBlockHeight = isScenarioBlockShowing and (ScenarioBlocksFrame:GetHeight() + ObjectiveTrackerBlocksFrame.ScenarioHeader:GetHeight() + 10) or 0; | |
local newHeight = math.max(self.editModeHeight or 800, scenarioBlockHeight); | |
self:SetHeight(newHeight); | |
return; | |
end | |
local point, relativeTo, relativePoint, offsetX, offsetY = self:GetPoint(1); | |
if offsetY then | |
local parentHeight = self:GetParent():GetHeight(); | |
local setHeight = parentHeight + offsetY; | |
setHeight = math.max(setHeight, 20); | |
self:SetHeight(setHeight); | |
end | |
end | |
function ObjectiveTracker_OnShow(self) | |
UIParentManagedFrameMixin.OnShow(self); | |
ObjectiveTracker_UpdateHeight(); | |
end | |
function ObjectiveTracker_Initialize(self) | |
self.MODULES = { SCENARIO_CONTENT_TRACKER_MODULE, | |
UI_WIDGET_TRACKER_MODULE, | |
BONUS_OBJECTIVE_TRACKER_MODULE, | |
WORLD_QUEST_TRACKER_MODULE, | |
CAMPAIGN_QUEST_TRACKER_MODULE, | |
QUEST_TRACKER_MODULE, | |
ACHIEVEMENT_TRACKER_MODULE, | |
PROFESSION_RECIPE_TRACKER_MODULE, | |
MONTHLY_ACTIVITIES_TRACKER_MODULE, | |
}; | |
self.MODULES_UI_ORDER = { SCENARIO_CONTENT_TRACKER_MODULE, | |
UI_WIDGET_TRACKER_MODULE, | |
CAMPAIGN_QUEST_TRACKER_MODULE, | |
QUEST_TRACKER_MODULE, | |
BONUS_OBJECTIVE_TRACKER_MODULE, | |
WORLD_QUEST_TRACKER_MODULE, | |
ACHIEVEMENT_TRACKER_MODULE, | |
PROFESSION_RECIPE_TRACKER_MODULE, | |
MONTHLY_ACTIVITIES_TRACKER_MODULE, | |
}; | |
self:RegisterEvent("QUEST_LOG_UPDATE"); | |
self:RegisterEvent("TRACKED_ACHIEVEMENT_LIST_CHANGED"); | |
self:RegisterEvent("PERKS_ACTIVITIES_TRACKED_UPDATED"); | |
self:RegisterEvent("PERKS_ACTIVITY_COMPLETED"); | |
self:RegisterEvent("QUEST_WATCH_LIST_CHANGED"); | |
self:RegisterEvent("QUEST_AUTOCOMPLETE"); | |
self:RegisterEvent("QUEST_ACCEPTED"); | |
self:RegisterEvent("SUPER_TRACKING_CHANGED"); | |
self:RegisterEvent("SCENARIO_UPDATE"); | |
self:RegisterEvent("SCENARIO_CRITERIA_UPDATE"); | |
self:RegisterEvent("SCENARIO_SPELL_UPDATE"); | |
self:RegisterEvent("SCENARIO_BONUS_VISIBILITY_UPDATE"); | |
self:RegisterEvent("TRACKED_ACHIEVEMENT_UPDATE"); | |
self:RegisterEvent("ZONE_CHANGED_NEW_AREA"); | |
self:RegisterEvent("ZONE_CHANGED"); | |
self:RegisterEvent("QUEST_POI_UPDATE"); | |
self:RegisterEvent("VARIABLES_LOADED"); | |
self:RegisterEvent("QUEST_TURNED_IN"); | |
self:RegisterEvent("PLAYER_MONEY"); | |
self:RegisterEvent("CVAR_UPDATE"); | |
self:RegisterEvent("WAYPOINT_UPDATE"); | |
self.watchMoneyReasons = 0; | |
WorldMapFrame:RegisterCallback("SetFocusedQuestID", ObjectiveTracker_OnFocusedQuestChanged, self); | |
WorldMapFrame:RegisterCallback("ClearFocusedQuestID", ObjectiveTracker_OnFocusedQuestChanged, self); | |
ProfessionsRecipeTracking_Initialize(); | |
self.initialized = true; | |
end | |
function ObjectiveTracker_OnFocusedQuestChanged(self) | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST); | |
end | |
function ObjectiveTracker_OnEvent(self, event, ...) | |
if ( event == "QUEST_LOG_UPDATE" ) then | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_QUEST); | |
elseif ( event == "TRACKED_ACHIEVEMENT_UPDATE" ) then | |
AchievementObjectiveTracker_OnAchievementUpdate(...); | |
elseif ( event == "PERKS_ACTIVITIES_TRACKED_UPDATED" ) then | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_MODULE_MONTHLY_ACTIVITIES); | |
elseif ( event == "PERKS_ACTIVITY_COMPLETED" ) then | |
MonthlyActivitiesObjectiveTracker_OnActivityCompleted(...); | |
elseif ( event == "QUEST_ACCEPTED" ) then | |
local questID = ...; | |
if ( not C_QuestLog.IsQuestBounty(questID) ) then | |
if ( C_QuestLog.IsQuestTask(questID) ) then | |
if ( QuestUtils_IsQuestWorldQuest(questID) ) then | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_WORLD_QUEST_ADDED, questID); | |
else | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_TASK_ADDED, questID); | |
end | |
else | |
if ( GetCVarBool("autoQuestWatch") and C_QuestLog.GetNumQuestWatches() < Constants.QuestWatchConsts.MAX_QUEST_WATCHES ) then | |
C_QuestLog.AddQuestWatch(questID, Enum.QuestWatchType.Automatic); | |
QuestSuperTracking_OnQuestTracked(questID); | |
end | |
end | |
end | |
elseif ( event == "TRACKED_ACHIEVEMENT_LIST_CHANGED" ) then | |
local achievementID, added = ...; | |
if ( added ) then | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED, achievementID); | |
else | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT); | |
end | |
elseif ( event == "QUEST_WATCH_LIST_CHANGED" ) then | |
local questID, added = ...; | |
if ( added ) then | |
if ( not C_QuestLog.IsQuestBounty(questID) or C_QuestLog.IsComplete(questID) ) then | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED, questID); | |
end | |
else | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_QUEST); | |
end | |
elseif ( event == "QUEST_POI_UPDATE" ) then | |
QuestPOIUpdateIcons(); | |
if ( GetCVar("trackQuestSorting") == "proximity" ) then | |
C_QuestLog.SortQuestWatches(); | |
end | |
-- C_QuestLog.SortQuestWatches might not trigger a QUEST_WATCH_LIST_CHANGED due to unique signals, so force an update | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST); | |
QuestSuperTracking_OnPOIUpdate(); | |
elseif ( event == "SCENARIO_CRITERIA_UPDATE" ) then | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_SCENARIO); | |
elseif ( event == "SCENARIO_SPELL_UPDATE" ) then | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_SCENARIO_SPELLS); | |
elseif ( event == "SCENARIO_BONUS_VISIBILITY_UPDATE") then | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE); | |
elseif ( event == "SUPER_TRACKING_CHANGED" ) then | |
ObjectiveTracker_UpdateSuperTrackedQuest(self); | |
elseif ( event == "ZONE_CHANGED" ) then | |
local lastMapID = C_Map.GetBestMapForUnit("player"); | |
if ( lastMapID ~= self.lastMapID ) then | |
C_QuestLog.SortQuestWatches(); | |
self.lastMapID = lastMapID; | |
end | |
elseif ( event == "QUEST_AUTOCOMPLETE" ) then | |
local questId = ...; | |
AutoQuestPopupTracker_AddPopUp(questId, "COMPLETE"); | |
elseif ( event == "SCENARIO_UPDATE" ) then | |
local newStage = ...; | |
if ( newStage ) then | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE); | |
else | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_SCENARIO); | |
end | |
elseif ( event == "ZONE_CHANGED_NEW_AREA" ) then | |
C_QuestLog.SortQuestWatches(); | |
elseif ( event == "QUEST_TURNED_IN" ) then | |
local questID, xp, money = ...; | |
if ( C_QuestLog.IsQuestTask(questID) and not C_QuestLog.IsQuestBounty(questID) ) then | |
BonusObjectiveTracker_OnTaskCompleted(...); | |
end | |
elseif ( event == "PLAYER_MONEY" and self.watchMoneyReasons > 0 ) then | |
ObjectiveTracker_Update(self.watchMoneyReasons); | |
elseif ( event == "PLAYER_ENTERING_WORLD" ) then | |
if ( not self.initialized ) then | |
ObjectiveTracker_Initialize(self); | |
end | |
ObjectiveTracker_Update(); | |
if not QuestSuperTracking_IsSuperTrackedQuestValid() then | |
QuestSuperTracking_ChooseClosestQuest(); | |
end | |
self.lastMapID = C_Map.GetBestMapForUnit("player"); | |
elseif ( event == "CVAR_UPDATE" ) then | |
local arg1 =...; | |
if ( arg1 == "questPOI" ) then | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST); | |
end | |
elseif ( event == "VARIABLES_LOADED" ) then | |
ObjectiveTracker_Update(); | |
elseif ( event == "WAYPOINT_UPDATE" ) then | |
ObjectiveTracker_Update(); | |
end | |
end | |
function ObjectiveTracker_OnSizeChanged(self) | |
ObjectiveTracker_Update(); | |
end | |
function ObjectiveTracker_OnUpdate(self) | |
if self.isUpdateDirty then | |
ObjectiveTracker_Update(); | |
end | |
end | |
function ObjectiveTrackerHeader_OnAnimFinished(self) | |
local header = self:GetParent(); | |
header.animating = nil; | |
end | |
ObjectiveTrackerHeaderMixin = {}; | |
function ObjectiveTrackerHeaderMixin:OnLoad() | |
self.height = OBJECTIVE_TRACKER_HEADER_HEIGHT; | |
end | |
function ObjectiveTrackerHeaderMixin:PlayAddAnimation() | |
self.animating = true; | |
self.HeaderOpenAnim:Restart(); | |
end | |
-- ***************************************************************************************************** | |
-- ***** BUTTONS | |
-- ***************************************************************************************************** | |
ObjectiveTrackerMinimizeButtonMixin = {}; | |
function ObjectiveTrackerMinimizeButtonMixin:OnLoad() | |
local collapsed = false; | |
self:SetAtlases(collapsed); | |
end | |
function ObjectiveTrackerMinimizeButtonMixin:SetAtlases(collapsed) | |
local normalTexture = self:GetNormalTexture(); | |
local pushedTexture = self:GetPushedTexture(); | |
if self.buttonType == "module" then | |
if collapsed then | |
normalTexture:SetAtlas("UI-QuestTrackerButton-Expand-Section", true); | |
pushedTexture:SetAtlas("UI-QuestTrackerButton-Expand-Section-Pressed", true); | |
else | |
normalTexture:SetAtlas("UI-QuestTrackerButton-Collapse-Section", true); | |
pushedTexture:SetAtlas("UI-QuestTrackerButton-Collapse-Section-Pressed", true); | |
end | |
else | |
if collapsed then | |
normalTexture:SetAtlas("UI-QuestTrackerButton-Expand-All", true); | |
pushedTexture:SetAtlas("UI-QuestTrackerButton-Expand-All-Pressed", true); | |
else | |
normalTexture:SetAtlas("UI-QuestTrackerButton-Collapse-All", true); | |
pushedTexture:SetAtlas("UI-QuestTrackerButton-Collapse-All-Pressed", true); | |
end | |
end | |
end | |
function ObjectiveTrackerMinimizeButtonMixin:SetCollapsed(collapsed) | |
self:SetAtlases(collapsed); | |
end | |
function ObjectiveTracker_MinimizeButton_OnClick(self) | |
PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON); | |
if ( ObjectiveTrackerFrame.collapsed ) then | |
ObjectiveTracker_Expand(); | |
else | |
ObjectiveTracker_Collapse(); | |
end | |
ObjectiveTracker_Update(); | |
end | |
function ObjectiveTracker_MinimizeModuleButton_OnClick(self) | |
PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON); | |
local module = self:GetParent().module; | |
module:SetCollapsed(not module:IsCollapsed()); | |
ObjectiveTracker_Update(0, nil, module); | |
end | |
function ObjectiveTracker_Collapse() | |
ObjectiveTrackerFrame.collapsed = true; | |
ObjectiveTrackerFrame.BlocksFrame:Hide(); | |
ObjectiveTrackerFrame.HeaderMenu.MinimizeButton:SetCollapsed(true); | |
ObjectiveTrackerFrame.HeaderMenu.Title:Show(); | |
end | |
function ObjectiveTracker_Expand() | |
ObjectiveTrackerFrame.collapsed = nil; | |
ObjectiveTrackerFrame.BlocksFrame:Show(); | |
ObjectiveTrackerFrame.HeaderMenu.MinimizeButton:SetCollapsed(false); | |
ObjectiveTrackerFrame.HeaderMenu.Title:Hide(); | |
end | |
function ObjectiveTracker_ToggleDropDown(frame, handlerFunc) | |
local dropDown = ObjectiveTrackerBlockDropDown; | |
if ( dropDown.activeFrame ~= frame ) then | |
CloseDropDownMenus(); | |
end | |
dropDown.activeFrame = frame; | |
dropDown.initialize = handlerFunc; | |
ToggleDropDownMenu(1, nil, dropDown, "cursor", 3, -3); | |
PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON); | |
end | |
-- ***************************************************************************************************** | |
-- ***** BLOCK CONTROL | |
-- ***************************************************************************************************** | |
local function AnchorBlock(block, anchorBlock, checkFit) | |
local module = block.module; | |
local blocksFrame = module.BlocksFrame; | |
local offsetX, offsetY = ObjectiveTracker_GetBlockOffset(block); | |
block:ClearAllPoints(); | |
if ( anchorBlock ) then | |
if ( anchorBlock.isHeader ) then | |
offsetY = module.fromHeaderOffsetY; | |
end | |
-- check if the block can fit | |
if ( checkFit and (blocksFrame.contentsHeight + block.height - offsetY > blocksFrame.maxHeight) ) then | |
return; | |
end | |
if ( block.isHeader ) then | |
offsetY = offsetY + anchorBlock.module.fromModuleOffsetY; | |
block:SetPoint("LEFT", OBJECTIVE_TRACKER_HEADER_OFFSET_X, 0); | |
else | |
block:SetPoint("LEFT", offsetX, 0); | |
end | |
block:SetPoint("TOP", anchorBlock, "BOTTOM", 0, offsetY); | |
else | |
offsetY = 0; | |
-- check if the block can fit | |
if ( checkFit and (blocksFrame.contentsHeight + block.height > blocksFrame.maxHeight) ) then | |
return; | |
end | |
-- if the blocks frame is a scrollframe, attach to its scrollchild | |
if ( block.isHeader ) then | |
block:SetPoint("TOPLEFT", blocksFrame.ScrollContents or blocksFrame, "TOPLEFT", OBJECTIVE_TRACKER_HEADER_OFFSET_X, offsetY); | |
else | |
block:SetPoint("TOPLEFT", blocksFrame.ScrollContents or blocksFrame, "TOPLEFT", offsetX, offsetY); | |
end | |
end | |
return offsetY; | |
end | |
local function InternalAddBlock(block) | |
local module = block.module or DEFAULT_OBJECTIVE_TRACKER_MODULE; | |
local blocksFrame = module.BlocksFrame; | |
block.nextBlock = nil; | |
-- This doesn't take fit into account, it just assumes that there's content to be added, so the potential count | |
-- should increase (this is related to showing the collapse buttons on the headers, see Reorder) | |
-- NOTE: Never count headers as added blocks | |
if not block.isHeader then | |
module.potentialBlocksAddedThisLayout = (module.potentialBlocksAddedThisLayout or 0) + 1; | |
end | |
-- Only allow headers to be added if the module is collapsed. | |
if not block.isHeader and module:IsCollapsed() then | |
return false; | |
end | |
local offsetY = AnchorBlock(block, blocksFrame.currentBlock, not module.ignoreFit); | |
if ( not offsetY ) then | |
return false; | |
end | |
if ( not module.topBlock ) then | |
module.topBlock = block; | |
end | |
if ( not module.firstBlock and not block.isHeader ) then | |
module.firstBlock = block; | |
end | |
if ( blocksFrame.currentBlock ) then | |
blocksFrame.currentBlock.nextBlock = block; | |
end | |
blocksFrame.currentBlock = block; | |
blocksFrame.contentsHeight = blocksFrame.contentsHeight + block.height - offsetY; | |
module.contentsAnimHeight = module.contentsAnimHeight + block.height; | |
module.contentsHeight = module.contentsHeight + block.height - offsetY; | |
return true; | |
end | |
function ObjectiveTracker_AddHeader(header, isStaticReanchor) | |
if InternalAddBlock(header) then | |
header.added = true; | |
header:Show(); | |
return true; | |
end | |
return false; | |
end | |
function ObjectiveTracker_AddBlock(block) | |
local header = block.module.Header; | |
local blockAdded = false; | |
-- if there's no header or it's been added, just add the block... | |
if not header or header.added then | |
blockAdded = InternalAddBlock(block); | |
elseif ObjectiveTracker_CanFitBlock(block, header) then | |
-- try to add header and maybe block | |
if ObjectiveTracker_AddHeader(header) then | |
blockAdded = InternalAddBlock(block); | |
end | |
end | |
if not blockAdded then | |
block.module.hasSkippedBlocks = true; | |
end | |
return blockAdded; | |
end | |
function ObjectiveTracker_CanFitBlock(block, header) | |
local module = block.module; | |
local blocksFrame = module.BlocksFrame; | |
local offsetY; | |
if ( not blocksFrame.currentBlock ) then | |
offsetY = 0; | |
elseif ( blocksFrame.currentBlock.isHeader ) then | |
offsetY = module.fromHeaderOffsetY; | |
else | |
offsetY = select(2, ObjectiveTracker_GetBlockOffset(block)); | |
end | |
local totalHeight; | |
if header then | |
totalHeight = header.height - offsetY + block.height - module.fromHeaderOffsetY; | |
else | |
totalHeight = block.height - offsetY; | |
end | |
return (blocksFrame.contentsHeight + totalHeight) <= blocksFrame.maxHeight; | |
end | |
-- ***** SLIDING | |
function ObjectiveTracker_SlideBlock(block, slideData) | |
block.slideData = slideData; | |
if ( slideData.startDelay ) then | |
block.slideTime = -slideData.startDelay; | |
else | |
block.slideTime = 0; | |
end | |
block.slideHeight = slideData.startHeight; | |
block:SetHeight(slideData.startHeight); | |
block:SetScript("OnUpdate", ObjectiveTracker_OnSlideBlockUpdate); | |
end | |
function ObjectiveTracker_EndSlideBlock(block) | |
block:SetScript("OnUpdate", nil); | |
if ( block.slideData ) then | |
block:SetHeight(block.slideData.endHeight); | |
if ( block.slideData.scroll ) then | |
block:SetVerticalScroll(0); | |
end | |
end | |
block.slidingAction = nil; | |
end | |
function ObjectiveTracker_OnSlideBlockUpdate(block, elapsed) | |
local slideData = block.slideData; | |
block.slideTime = block.slideTime + elapsed; | |
if ( block.slideTime <= 0 ) then | |
return; | |
end | |
local height = floor(slideData.startHeight + (slideData.endHeight - slideData.startHeight) * (min(block.slideTime, slideData.duration) / slideData.duration)); | |
if ( height ~= block.slideHeight ) then | |
block.slideHeight = height; | |
block:SetHeight(height); | |
if ( slideData.scroll ) then | |
block:UpdateScrollChildRect(); | |
-- scrolling means the bottom of the content comes in first or leaves last | |
if (slideData.expanding) then | |
block:SetVerticalScroll(0); | |
else | |
block:SetVerticalScroll(max(slideData.endHeight, slideData.startHeight) - height); | |
end | |
end | |
end | |
if ( block.slideTime >= slideData.duration + (slideData.endDelay or 0) ) then | |
block:SetScript("OnUpdate", nil); | |
if ( slideData.onFinishFunc ) then | |
slideData.onFinishFunc(block); | |
end | |
end | |
end | |
function ObjectiveTracker_CancelSlideBlock(block) | |
block:SetScript("OnUpdate", nil); | |
local slideData = block.slideData; | |
if( slideData ) then | |
block:SetHeight(slideData.startHeight); | |
if ( slideData.scroll ) then | |
block:UpdateScrollChildRect(); | |
-- scrolling means the bottom of the content comes in first or leaves last | |
block:SetVerticalScroll(0); | |
end | |
end | |
end | |
-- ***** UPDATE | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:StaticReanchorCheckAddHeaderOnly() | |
if self:IsCollapsed() and not self.Header.added and self:GetBlockCount() > 0 then | |
ObjectiveTracker_AddHeader(self.Header, true); -- the header was marked as not being added, make sure to add it again... | |
return true; | |
end | |
return false; | |
end | |
function DEFAULT_OBJECTIVE_TRACKER_MODULE:StaticReanchor() | |
-- If this module is collapsed, don't process anything, it will result in the entire module being hidden, since just the header | |
-- is showing, there's nothing to update. | |
if self:StaticReanchorCheckAddHeaderOnly() then | |
return; | |
end | |
local block = self.firstBlock; | |
self:BeginLayout(true); | |
while ( block ) do | |
if ( block.module == self ) then | |
local nextBlock = block.nextBlock; | |
if ( ObjectiveTracker_AddBlock(block) ) then | |
block.used = true; | |
block:Show(); | |
block = nextBlock; | |
else | |
-- a prior module reduced the previously available space | |
block.used = false; | |
block:Hide(); | |
break; | |
end | |
else | |
break; | |
end | |
end | |
self:EndLayout(true); | |
end | |
local function GetRelatedModulesForUpdate(module) | |
if module then | |
return tInvert(module:GetRelatedModules()) | |
end | |
return nil; | |
end | |
local function IsRelatedModuleForUpdate(module, moduleLookup) | |
if moduleLookup then | |
return moduleLookup[module] ~= nil; | |
end | |
return false; | |
end | |
local function ObjectiveTracker_GetVisibleHeaders() | |
local headers = {}; | |
for index, module in ipairs(ObjectiveTrackerFrame.MODULES) do | |
local header = module.Header; | |
if header.added and header:IsVisible() then | |
headers[header] = true; | |
end | |
end | |
return headers; | |
end | |
local function ObjectiveTracker_AnimateHeaders(previouslyVisibleHeaders) | |
local currentHeaders = ObjectiveTracker_GetVisibleHeaders(); | |
for header, isVisible in pairs(currentHeaders) do | |
if isVisible and not previouslyVisibleHeaders[header] then | |
header:PlayAddAnimation(); | |
end | |
end | |
end | |
function ObjectiveTracker_UpdateSuperTrackedQuest(self) | |
local questID = C_SuperTrack.GetSuperTrackedQuestID(); | |
ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_SUPER_TRACK_CHANGED, questID); | |
QuestPOI_SelectButtonByQuestID(self.BlocksFrame, questID); | |
end | |
function ObjectiveTracker_Update(reason, id, moduleWhoseCollapseChanged) | |
local tracker = ObjectiveTrackerFrame; | |
if not reason or reason == OBJECTIVE_TRACKER_UPDATE_ALL or reason == OBJECTIVE_TRACKER_UPDATE_MOVED then | |
local trackerCenterX = tracker:GetCenter(); | |
if not trackerCenterX then | |
return; | |
end | |
local halfScreenWidth = GetScreenWidth() / 2; | |
tracker.isOnLeftSideOfScreen = trackerCenterX < halfScreenWidth; | |
tracker.HeaderMenu.MinimizeButton:ClearAllPoints(); | |
tracker.HeaderMenu.Title:ClearAllPoints(); | |
if tracker.isOnLeftSideOfScreen then | |
tracker.HeaderMenu.MinimizeButton:SetPoint("LEFT", tracker.HeaderMenu, "LEFT", 0, 0); | |
tracker.HeaderMenu.Title:SetPoint("LEFT", tracker.HeaderMenu.MinimizeButton, "RIGHT", 3, 0); | |
else | |
tracker.HeaderMenu.MinimizeButton:SetPoint("RIGHT", tracker.HeaderMenu, "RIGHT", 0, 0); | |
tracker.HeaderMenu.Title:SetPoint("RIGHT", tracker.HeaderMenu.MinimizeButton, "LEFT", -3, 0); | |
end | |
end | |
if tracker.isUpdating then | |
-- Trying to update while we're already updating, try again next frame | |
tracker.isUpdateDirty = true; | |
return; | |
end | |
tracker.isUpdating = true; | |
if ( not tracker.initialized ) then | |
tracker.isUpdating = false; | |
return; | |
end | |
tracker.BlocksFrame.maxHeight = ObjectiveTrackerFrame.BlocksFrame:GetHeight(); | |
if ( tracker.BlocksFrame.maxHeight == 0 ) then | |
tracker.isUpdating = false; | |
return; | |
end | |
tracker.isUpdateDirty = false; | |
OBJECTIVE_TRACKER_UPDATE_REASON = reason or OBJECTIVE_TRACKER_UPDATE_ALL; | |
OBJECTIVE_TRACKER_UPDATE_ID = id; | |
tracker.BlocksFrame.currentBlock = nil; | |
tracker.BlocksFrame.contentsHeight = 0; | |
-- Gather existing headers, only newly added ones will animate | |
local currentHeaders = ObjectiveTracker_GetVisibleHeaders(); | |
-- mark headers unused | |
for index, module in ipairs(tracker.MODULES) do | |
if module.Header then | |
module.Header.added = nil; | |
end | |
end | |
-- These can be nil, it's fine, trust the API. | |
local relatedModules = GetRelatedModulesForUpdate(moduleWhoseCollapseChanged); | |
-- run module updates | |
local gotMoreRoomThisPass = false; | |
for i = 1, #tracker.MODULES do | |
local module = tracker.MODULES[i]; | |
if IsRelatedModuleForUpdate(moduleWhoseCollapseChanged, relatedModules) or (band(OBJECTIVE_TRACKER_UPDATE_REASON, module.updateReasonModule + module.updateReasonEvents) > 0) then | |
-- run a full update on this module | |
module:Update(); | |
-- check if it's now taking up less space, using subtraction because of floats | |
if ( module.oldContentsHeight - module.contentsHeight >= 1 ) then | |
-- it is taking up less space, might have freed room for other modules | |
gotMoreRoomThisPass = true; | |
end | |
else | |
-- this module's contents have not have changed | |
-- but if we got more room and this module has unshown content, do a full update | |
-- also do a full update if the header is animating since the module does not technically have any blocks at that point | |
if ( (module.hasSkippedBlocks and gotMoreRoomThisPass) or (module.Header and module.Header.animating) ) then | |
module:Update(); | |
else | |
module:StaticReanchor(); | |
end | |
end | |
end | |
ObjectiveTracker_ReorderModules(); | |
ObjectiveTracker_UpdatePOIs(); | |
ObjectiveTracker_AnimateHeaders(currentHeaders); | |
-- hide unused headers | |
for i = 1, #tracker.MODULES do | |
ObjectiveTracker_CheckAndHideHeader(tracker.MODULES[i].Header); | |
end | |
if ( tracker.BlocksFrame.currentBlock ) then | |
tracker.HeaderMenu:Show(); | |
else | |
tracker.HeaderMenu:Hide(); | |
end | |
tracker.BlocksFrame.currentBlock = nil; | |
tracker.isUpdating = false; | |
if tracker:IsInDefaultPosition() then | |
UIParent_ManageFramePositions(); | |
end | |
end | |
function ObjectiveTracker_CheckAndHideHeader(moduleHeader) | |
if ( moduleHeader and not moduleHeader.added and moduleHeader:IsShown() ) then | |
moduleHeader:Hide(); | |
if ( moduleHeader.animating ) then | |
moduleHeader.animating = nil; | |
moduleHeader.HeaderOpenAnim:Stop(); | |
end | |
end | |
end | |
function ObjectiveTracker_WatchMoney(watchMoney, reason) | |
if ( watchMoney ) then | |
if ( band(ObjectiveTrackerFrame.watchMoneyReasons, reason) == 0 ) then | |
ObjectiveTrackerFrame.watchMoneyReasons = ObjectiveTrackerFrame.watchMoneyReasons + reason; | |
end | |
else | |
if ( band(ObjectiveTrackerFrame.watchMoneyReasons, reason) > 0 ) then | |
ObjectiveTrackerFrame.watchMoneyReasons = ObjectiveTrackerFrame.watchMoneyReasons - reason; | |
end | |
end | |
end | |
local function ObjectiveTracker_CountVisibleModules() | |
local count = 0; | |
local seen = {}; | |
for index, module in ipairs(ObjectiveTrackerFrame.MODULES) do | |
local header = module.Header; | |
if header and not seen[header] then | |
seen[header] = true; | |
if header:IsVisible() and module:GetBlockCount() > 0 then -- testing out the whole active block count concept.... | |
count = count + 1; | |
end | |
end | |
end | |
return count; | |
end | |
function ObjectiveTracker_ReorderModules() | |
local visibleCount = ObjectiveTracker_CountVisibleModules(); | |
local showAllModuleMinimizeButtons = visibleCount > 1; | |
local detachIndex = nil; | |
local anchorBlock = nil; | |
local header = ObjectiveTrackerFrame.HeaderMenu; | |
header:ClearAllPoints(); | |
for index, module in ipairs(ObjectiveTrackerFrame.MODULES_UI_ORDER) do | |
local topBlock = module.topBlock; | |
if topBlock then | |
if module:UsesSharedHeader() then | |
AnchorBlock(topBlock, module.Header); | |
local containingModule = module.Header.module; | |
if containingModule and containingModule.firstBlock then | |
containingModule.firstBlock:ClearAllPoints(); | |
AnchorBlock(containingModule.firstBlock, module.lastBlock); | |
end | |
else | |
AnchorBlock(topBlock, anchorBlock); | |
anchorBlock = module.lastBlock; | |
end | |
local headerPoint = ObjectiveTrackerFrame.isOnLeftSideOfScreen and "LEFT" or "RIGHT"; | |
if header then | |
header:ClearAllPoints(); | |
header:SetPoint(headerPoint, module.Header, headerPoint, ObjectiveTrackerFrame.isOnLeftSideOfScreen and -10 or 0, 0); | |
header = nil; | |
end | |
module.Header.Text:ClearAllPoints(); | |
module.Header.Text:SetPoint("LEFT", module.Header, "LEFT", ObjectiveTrackerFrame.isOnLeftSideOfScreen and 30 or 4, -1); | |
-- Side-step annoying "uncollapse" issue by allowing a collapsed module to continue showing its minimize button even if | |
-- it's the only remaining visible module | |
local shouldShowThisModuleMinimizeButton = showAllModuleMinimizeButtons or module:IsCollapsed(); | |
module.Header.MinimizeButton:SetShown(shouldShowThisModuleMinimizeButton); | |
if shouldShowThisModuleMinimizeButton then | |
module.Header.MinimizeButton:ClearAllPoints(); | |
module.Header.MinimizeButton:SetPoint(headerPoint, module.Header, headerPoint, ObjectiveTrackerFrame.isOnLeftSideOfScreen and 9 or -20, 0); | |
end | |
end | |
end | |
end | |
function ObjectiveTracker_UpdatePOIs() | |
if not ObjectiveTrackerFrame.MODULES then | |
return; | |
end | |
local blocksFrame = ObjectiveTrackerFrame.BlocksFrame; | |
QuestPOI_ResetUsage(blocksFrame); | |
local showPOIs = GetCVarBool("questPOI"); | |
if ( not showPOIs ) then | |
QuestPOI_HideUnusedButtons(blocksFrame); | |
return; | |
end | |
local numPOINumeric = 0; -- This is tied to the QuestPOI system, it must be maintained across tracker instances. | |
for i, module in ipairs(ObjectiveTrackerFrame.MODULES) do | |
if module.UpdatePOIs then | |
numPOINumeric = module:UpdatePOIs(numPOINumeric); | |
end | |
end | |
QuestPOI_SelectButtonByQuestID(blocksFrame, C_SuperTrack.GetSuperTrackedQuestID()); | |
QuestPOI_HideUnusedButtons(blocksFrame); | |
end | |
QuestHeaderMixin = {}; | |
function QuestHeaderMixin:OnShow() | |
self:RegisterEvent("QUEST_SESSION_JOINED"); | |
self:RegisterEvent("QUEST_SESSION_LEFT"); | |
self:UpdateHeader(); | |
end | |
function QuestHeaderMixin:OnHide() | |
self:UnregisterEvent("QUEST_SESSION_JOINED"); | |
self:UnregisterEvent("QUEST_SESSION_LEFT"); | |
end | |
function QuestHeaderMixin:OnEvent() | |
self:UpdateHeader(); | |
end | |
function QuestHeaderMixin:UpdateHeader() | |
if C_QuestSession.HasJoined() then | |
self.Text:SetText(TRACKER_HEADER_PARTY_QUESTS); | |
else | |
self.Text:SetText(TRACKER_HEADER_QUESTS); | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment