local _, addon = ... local StatCardController = addon.StatCardController; local GetCategoryNumAchievements = addon.GetCustomCategoryNumAchievements; local IsBossCard = addon.IsBossCard; local GetStatisticInfo = addon.GetStatisticInfo; local DataProvider = addon.DataProvider; local PinUtil = addon.PinUtil; --Constant local NUM_ACHIEVEMENT_CARDS = 8; local LEGACY_ID = 15234; local FEAT_OF_STRENGTH_ID = 81; --local GUILD_FEAT_OF_STRENGTH_ID = 15093; --local GUILD_CATEGORY_ID = 15076; local sin = math.sin; local cos = math.cos; local abs = math.abs; local min = math.min; local max = math.max; local sqrt = math.sqrt; local pow = math.pow; local pi = math.pi; local floor = math.floor; local ceil = math.ceil; local After = C_Timer.After; local gsub = string.gsub; local bband = bit.band; local format = string.format; local tremove = table.remove; local tinsert = table.insert; local GetAchievementNumCriteria = GetAchievementNumCriteria; local GetAchievementCriteriaInfo = GetAchievementCriteriaInfo; local GetRewardItemID = C_AchievementInfo.GetRewardItemID; local GetPreviousAchievement = GetPreviousAchievement; local GetNextAchievement = GetNextAchievement; local SetFocusedAchievement = SetFocusedAchievement; --Requset guild achievement progress from server, will fire "CRITERIA_UPDATE" after calling GetAchievementCriteriaInfo() local FadeFrame = NarciFadeUI.Fade; local GetParentAchievementID = NarciAPI.GetParentAchievementID; local L = Narci.L; local function linear(t, b, e, d) return (e - b) * t / d + b end local function outSine(t, b, e, d) return (e - b) * sin(t / d * (pi / 2)) + b end local function inOutSine(t, b, e, d) return (b - e) / 2 * (cos(pi * t / d) - 1) + b end local function outQuart(t, b, e, d) t = t / d - 1; return (b - e) * (pow(t, 4) - 1) + b end addon.outQuart = outQuart; --FormatShortDate Derivated from FormatShortDate (Util.lua) local FormatDate; if LOCALE_enGB then function FormatDate(day, month, year, twoRowMode) if (year) then if twoRowMode then return format("%1$d/%2$d\n20%3$02d", day, month, year); else return format("%1$d/%2$d/%3$02d", day, month, year); end else return format("%1$d/%2$d", day, month); end end else function FormatDate(day, month, year, twoRowMode) if (year) then if twoRowMode then return format("%2$d/%1$02d\n20%3$02d", day, month, year); else return format("%2$d/%1$02d/%3$02d", day, month, year); end else return format("%2$d/%1$02d", day, month); end end end local themeID = 0; local showNotEarnedMark = false; local isDarkTheme = true; local isGuildView = false; local texturePrefix = "Interface\\AddOns\\Narcissus_Achievements\\Art\\DarkWood\\"; local function ReskinButton(button) --if true then return end button.border:SetTexture(texturePrefix.."AchievementCardBorder"); button.background:SetTexture(texturePrefix.."AchievementCardBackground"); button.bottom:SetTexture(texturePrefix.."AchievementCardBackground"); button.lion:SetTexture(texturePrefix.."Lion"); button.mask:SetTexture(texturePrefix.."AchievementCardBorderMask"); local isDarkTheme = isDarkTheme; button.RewardFrame.background:SetShown(not isDarkTheme); button.RewardFrame.rewardNodeLeft:SetShown(isDarkTheme); button.RewardFrame.rewardNodeRight:SetShown(isDarkTheme); button.RewardFrame.rewardLineLeft:SetShown(isDarkTheme); button.RewardFrame.rewardLineRight:SetShown(isDarkTheme); button.RewardFrame.reward:SetPoint("BOTTOM", button.RewardFrame, "BOTTOM", 0, (isDarkTheme and 3) or 1); if isDarkTheme then button.description:SetFontObject(NarciAchievementText); else button.description:SetFontObject(NarciAchievementTextBlack); end button.isDarkTheme = isDarkTheme; --Reposition Elements button.icon:ClearAllPoints(); button.lion:ClearAllPoints(); button.date:ClearAllPoints(); button.NotEarned:ClearAllPoints(); if showNotEarnedMark then button.NotEarned:SetWidth(20); else button.NotEarned:SetWidth(0.1); end if themeID == 3 then button.icon:SetPoint("CENTER", button.border, "LEFT", 32, 0); button.lion:SetPoint("CENTER", button.border, "RIGHT", -28, -1); button.date:SetPoint("RIGHT", button.border, "TOPRIGHT", -54, -25); button.NotEarned:SetPoint("TOPLEFT", button.icon, "TOPRIGHT", 6, -2); else button.icon:SetPoint("CENTER", button.border, "LEFT", 27, 4); button.lion:SetPoint("CENTER", button.border, "RIGHT", -22, 3); button.date:SetPoint("RIGHT", button.border, "TOPRIGHT", -48, -25); button.NotEarned:SetPoint("TOPLEFT", button.icon, "TOPRIGHT", 7, -6.5); end button.isDark = nil; end local MainFrame, InspectionFrame, MountPreview, Tooltip, ReturnButton, SummaryButton, GoToCategoryButton, FilterButton; local CategoryContainer, AchievementContainer, DIYContainer, EditorContainer, SummaryFrame, AchievementCards, TabButtons; local UpdateSummaryFrame; local TabUtil = {}; addon.TabUtil = TabUtil; local CategoryButtons = { player = { parentButtons = {}, buttons = {}, }, guild = { parentButtons = {}, buttons = {}, }, stats = { parentButtons = {}, buttons = {}, }, }; local IS_STAT_CATEGORY = { [-2] = true, --Used to show pinned statistics }; function CategoryButtons:GetActiveParentButtons(tabID) tabID = tabID or TabUtil.tabID; if tabID == 1 then return self.player.parentButtons; elseif tabID == 2 then return self.guild.parentButtons; elseif tabID == 3 then return self.stats.parentButtons; end end local CategoryStructure = { player = {}, guild = {}, stats = {}, }; local function IsAccountWide(flags) --ACHIEVEMENT_FLAGS_ACCOUNT if flags then return bband(flags, 131072) == 131072 else return false end end addon.IsAccountWide = IsAccountWide; local ScrollUtil = {}; --DataProvider for ScrollFrame --Limit the request frequency local processor = CreateFrame("Frame"); processor:Hide(); processor:SetScript("OnUpdate", function(self, elapsed) local processComplete; self.cycle = self.cycle + 1; if self.func then self.arg2, processComplete = self.func(self.arg1, self.arg2); if processComplete then self:Hide(); self.func = nil; self.callback(); if self.cycle == 2 then ScrollUtil:UpdateScrollChild(0); end end if self.cycle == 3 then ScrollUtil:UpdateScrollChild(0); end else self:Hide(); end end) function processor:Start() local processComplete; if self.func then self.cycle = 1; self.arg2, processComplete = self.func(self.arg1, self.arg2); if processComplete then self:Hide(); self.func = nil; self.callback(); --Achievement Card Data is being constructed by OnUpdate script --The step is 4: Meaning it takes 2 frames to build the visible area --Update scrollframe on the 3rd frame ScrollUtil:UpdateScrollChild(0); else self:Show(); end else self:Hide(); end end ------------------------------------------------------------------------------------------------------ local function HideItemPreview() GameTooltip:Hide(); if MountPreview:IsShown() then MountPreview:FadeOut(); end MountPreview:ClearCallback(); end local animFlyIn = NarciAPI_CreateAnimationFrame(0.45); local animFlyOut = NarciAPI_CreateAnimationFrame(0.25); animFlyIn:SetScript("OnUpdate", function(self, elapsed) self.total = self.total + elapsed; local alpha = outQuart(self.total, self.fromAlpha, 1, self.duration); local scale = outQuart(self.total, 0.5, 1, 0.2); if self.total >= 0.2 then scale = 1; local textAlpha = outQuart(self.total - 0.2, 0, 1, 0.2); if textAlpha > 1 then textAlpha = 1; elseif textAlpha < 0 then textAlpha = 0; end self.header:SetAlpha(textAlpha); self.description:SetAlpha(textAlpha); self.date:SetAlpha(textAlpha); self.reward:SetAlpha(textAlpha); end if self.total >= self.duration then alpha = 1; --offsetX = 0; --offsetY = 0; scale = 1; self.header:SetAlpha(1); self.description:SetAlpha(1); self.date:SetAlpha(1); self.reward:SetAlpha(1); self:Hide(); end if alpha > 1 then alpha = 1; elseif alpha < 0 then alpha = 0; end self.background:SetAlpha(alpha); self.ObjectiveFrame:SetAlpha(alpha); self.ChainFrame:SetAlpha(alpha); self.Card:SetScale(scale); end) function animFlyIn:Play() self:Hide(); self.Card:SetAlpha(1); self.header:SetAlpha(0); self.description:SetAlpha(0); self.date:SetAlpha(0); self.reward:SetAlpha(0); self.fromAlpha = self.background:GetAlpha(); animFlyOut:Hide(); self:Show(); end animFlyOut:SetScript("OnUpdate", function(self, elapsed) self.total = self.total + elapsed; local offsetX = inOutSine(self.total, self.fromX, self.toX, self.duration); local offsetY = outSine(self.total, self.fromY, self.toY, self.duration); local alpha = outQuart(self.total, 1, 0, self.duration); if self.total >= self.duration then offsetX = self.toX; offsetY = self.toY; alpha = 0; InspectionFrame:Hide(); if self.button then self.button:Show(); end self.noTranslation = nil; self:Hide(); end self.background:SetAlpha(alpha); self.ObjectiveFrame:SetAlpha(alpha); self.ChainFrame:SetAlpha(alpha); if self.noTranslation then self.Card:SetAlpha(alpha); else self.Card:SetPoint("BOTTOM", InspectionFrame, "CENTER", offsetX, offsetY); end end) function animFlyOut:Play() animFlyIn:Hide(); self:Hide(); if self.button then --achievement button in the scrollframe self.button:Hide(); end local _; _, _, _, self.fromX, self.fromY = self.Card:GetPoint(); self:Show(); HideItemPreview(); InspectionFrame.isTransiting = true; InspectionFrame.HotkeyShiftClick:Hide(); InspectionFrame.HotkeyMouseWheel:Hide(); InspectionFrame.GetLink:Hide(); InspectionFrame.GoToCategoryButton:Hide(); InspectionFrame.Card.ParentAchievmentButton:Hide(); InspectionFrame.PrevButton:Hide(); InspectionFrame.NextButton:Hide(); end ------------------------------------------------------------------------------------------------------ local function DisplayProgress(id, flags) local cData, iData = {}, {}; cData.names, iData.names = {}, {}; cData.icons, iData.icons = {}, {}; cData.assetIDs, iData.assetIDs = {}, {}; cData.bars, iData.bars = {}, {}; local numCompleted, numIncomplete = 0, 0; --if ( not ( bit.band(flags, 128) == 128 ) ) then --ACHIEVEMENT_FLAGS_HAS_PROGRESS_BAR = 128!! local numCriteria = GetAchievementNumCriteria(id); if numCriteria == 0 then numCompleted = 0; numIncomplete = 0; else local criteriaString, criteriaType, completed, quantity, reqQuantity, charName, flags, assetID, quantityString; for i = 1, numCriteria do criteriaString, criteriaType, completed, quantity, reqQuantity, charName, flags, assetID, quantityString = GetAchievementCriteriaInfo(id, i); --print("criteriaType: "..criteriaType) if ( bband(flags, 1) == 1 ) then --EVALUATION_TREE_FLAG_PROGRESS_BAR = 1 if ( completed == false ) then numIncomplete = numIncomplete + 1; tinsert(iData.bars, {quantity, reqQuantity, criteriaString}); else numCompleted = numCompleted + 1; tinsert(cData.bars, {quantity, reqQuantity, criteriaString}); end else if ( completed == false ) then numIncomplete = numIncomplete + 1; criteriaString = "|CFF808080" .. criteriaString .. "|r"; tinsert(iData.names, criteriaString); if criteriaType == 8 and assetID then --CRITERIA_TYPE_ACHIEVEMENT local icon = DataProvider:GetAchievementInfo(assetID, 10); iData.icons[numIncomplete] = icon; iData.assetIDs[numIncomplete] = assetID; end else numCompleted = numCompleted + 1; criteriaString = "|CFF5fbb46" .. criteriaString .. "|r"; --00FF00 tinsert(cData.names, criteriaString); if criteriaType == 8 and assetID then --CRITERIA_TYPE_ACHIEVEMENT local icon = DataProvider:GetAchievementInfo(assetID, 10); cData.icons[numCompleted] = icon; cData.assetIDs[numCompleted] = assetID; end end end end end --end cData.count = numCompleted; iData.count = numIncomplete; InspectionFrame:DisplayCriteria(cData, iData); end local InspectCard; --function local function ShutInspection() InspectionFrame:Hide(); end local function ToggleTracking(id) if not id then return end; if DataProvider:IsTrackedAchievement(id) then RemoveTrackedAchievement(id); else local MAX_TRACKED_ACHIEVEMENTS = 10; if ( DataProvider.numTrackedAchievements >= MAX_TRACKED_ACHIEVEMENTS ) then UIErrorsFrame:AddMessage(format(ACHIEVEMENT_WATCH_TOO_MANY, MAX_TRACKED_ACHIEVEMENTS), 1.0, 0.1, 0.1, 1.0); return; end local _, _, _, completed, _, _, _, _, _, _, _, isGuild, wasEarnedByMe = DataProvider:GetAchievementInfo(id); if ( (completed and isGuild) or wasEarnedByMe ) then UIErrorsFrame:AddMessage(ERR_ACHIEVEMENT_WATCH_COMPLETED, 1.0, 0.1, 0.1, 1.0); return; end AddTrackedAchievement(id); return true end end local function ProcessModifiedClick(button) local achievementID = button.id; if not achievementID then return true end local isModifiedClick = IsModifiedClick(); if isModifiedClick then local handled = nil; if ( IsModifiedClick("CHATLINK") ) then local achievementLink = GetAchievementLink(achievementID); if ( achievementLink ) then handled = ChatEdit_InsertLink(achievementLink); if ( not handled and SocialPostFrame and Social_IsShown() ) then Social_InsertLink(achievementLink); handled = true; end end end if ( not handled and IsModifiedClick("QUESTWATCHTOGGLE") ) then local isTracking = ToggleTracking(achievementID); button.trackIcon:SetShown(isTracking); end end return isModifiedClick end local function AchievementCard_OnClick(self) if not ProcessModifiedClick(self) then InspectCard(self, true); end end local function FormatRewardText(id, rewardText) if isDarkTheme then local itemID = GetRewardItemID(id); if itemID then local itemID, itemType, itemSubType, _, icon, itemClassID, itemSubClassID = GetItemInfoInstant(itemID); if itemSubType == "Mount" then rewardText = gsub(rewardText, ".+:(.+)", "|cff808080".. "Mount:" .."|r|cff8950c6".."%1".."|r"); elseif itemSubType == "Companion Pets" then rewardText = gsub(rewardText, ".+:(.+)", "|cff808080".. "Pet:" .."|r|cfff2b344".."%1".."|r"); else rewardText = "|cffa3d39c"..rewardText.."|r"; end return rewardText, itemID else return ("|cffa3d39c"..rewardText.."|r"); --Pastel Yellow Green end else return ("|cffffd200"..rewardText.."|r"); end end local function GetProgressivePoints(achievementID, basePoints) local points; local _, progressivePoints, completed if basePoints then progressivePoints = basePoints; else _, _, progressivePoints, completed = DataProvider:GetAchievementInfo(achievementID); end achievementID = GetPreviousAchievement(achievementID); while achievementID do _, _, points, completed = DataProvider:GetAchievementInfo(achievementID); progressivePoints = progressivePoints + points; achievementID = GetPreviousAchievement(achievementID); end if ( progressivePoints ) then return progressivePoints; else return 0; end end local function FormatAchievementCard(button, id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, wasEarnedByMe) if not button then return end local headerObject, numLines, textHeight; button.id = id; button.trackIcon:SetShown( DataProvider:IsTrackedAchievement(id) ); if ( not completed or ( not isGuild and not wasEarnedByMe ) ) and (showNotEarnedMark) then button.NotEarned:Show(); else button.NotEarned:Hide(); end --for long text button.header:SetText(name); if button.header:IsTruncated() then headerObject = button.headerLong; headerObject:SetText(name); button.header:Hide(); else headerObject = button.header; button.headerLong:Hide(); end headerObject:Show(); if IsAccountWide(flags) then if completed then if isDarkTheme then headerObject:SetTextColor(0.427, 0.812, 0.965); --(0.427, 0.812, 0.965)(0.4, 0.755, 0.9) else headerObject:SetTextColor(1, 1, 1); end else if isDarkTheme then headerObject:SetTextColor(0.214, 0.406, 0.484); else headerObject:SetTextColor(0.5, 0.5, 0.5); end end else if completed then if isDarkTheme then headerObject:SetTextColor(0.9, 0.82, 0.58); --(1, 0.91, 0.647); --(0.9, 0.82, 0.58) --(0.851, 0.774, 0.55) else headerObject:SetTextColor(1, 1, 1); end else if isDarkTheme then headerObject:SetTextColor(0.5, 0.46, 0.324); else headerObject:SetTextColor(0.5, 0.5, 0.5); end end end points = GetProgressivePoints(id, points); if points == 0 then button.points:SetText(""); button.lion:Show(); else if points >= 100 then if not button.useSmallPoints then button.useSmallPoints = true; button.points:SetFontObject(NarciAchievemtPointsSmall); end else if button.useSmallPoints then button.useSmallPoints = nil; button.points:SetFontObject(NarciAchievemtPoints); end end button.points:SetText(points); button.lion:Hide(); end button.icon:SetTexture(icon); local rewardHeight; local shadowHeight = 0; if rewardText and rewardText ~= "" then local itemID; rewardHeight = 22; rewardText, itemID = FormatRewardText(id, rewardText); button.RewardFrame.reward:SetText(rewardText); button.RewardFrame.itemID = itemID; button.itemID = itemID; button.RewardFrame:Show(); else if isDarkTheme then rewardHeight = 2; else rewardHeight = 8; end button.RewardFrame:Hide(); button.RewardFrame:SetHeight(2); end button.RewardFrame:SetHeight(rewardHeight); button.description:SetHeight(0); button.description:SetText(description); textHeight = floor( button.background:GetHeight() + 0.5 ); local descriptionHeight = button.description:GetHeight(); button.description:SetHeight(descriptionHeight + 2) numLines = ceil( descriptionHeight / 14 - 0.1 ); button:SetHeight(72 + rewardHeight + 14*(numLines - 1) ); button.shadow:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 12, - 6 - numLines * 6 - shadowHeight); if IsAccountWide(flags) then --ACHIEVEMENT_FLAGS_ACCOUNT if button.accountWide ~= true then button.accountWide = true; button.border:SetTexCoord(0.05078125, 0.94921875, 0.5, 1); button.bottom:SetTexCoord(0.05078125, 0.94921875, 0.985, 1); end if textHeight <= 288 then button.background:SetTexCoord(0.05078125, 0.94921875, 0.985 - textHeight/288/2, 0.985); else button.background:SetTexCoord(0.05078125, 0.94921875, 0.5, 0.985); end else if button.accountWide then button.accountWide = nil; button.border:SetTexCoord(0.05078125, 0.94921875, 0, 0.5); button.bottom:SetTexCoord(0.05078125, 0.94921875, 0.485, 0.5); end if textHeight <= 288 then button.background:SetTexCoord(0.05078125, 0.94921875, 0.485 - textHeight/288/2, 0.485); else button.background:SetTexCoord(0.05078125, 0.94921875, 0, 0.485); end end if completed then button.date:SetText( FormatDate(day, month, year) ); button.RewardFrame.reward:SetAlpha(1); if (button.isDark == nil) or (button.isDark) then button.isDark = false; button.icon:SetDesaturated(false); button.points:SetTextColor(0.8, 0.8, 0.8); if isDarkTheme then button.description:SetTextColor(0.72, 0.72, 0.72); else button.description:SetTextColor(0, 0, 0); end button.icon:SetVertexColor(1, 1, 1); button.lion:SetVertexColor(1, 1, 1); button.border:SetVertexColor(1, 1, 1); button.background:SetVertexColor(1, 1, 1); button.bottom:SetVertexColor(1, 1, 1); button.border:SetDesaturated(false); button.background:SetDesaturated(false); button.bottom:SetDesaturated(false); end else button.date:SetText(""); button.RewardFrame.reward:SetAlpha(0.60); if (button.isDark == nil) or (not button.isDark) then button.isDark = true; button.icon:SetDesaturated(true); button.points:SetTextColor(0.6, 0.6, 0.6); if isDarkTheme then button.description:SetTextColor(0.6, 0.6, 0.6); else button.description:SetTextColor(0, 0, 0); end button.icon:SetVertexColor(0.60, 0.60, 0.60); button.lion:SetVertexColor(0.60, 0.60, 0.60); button.border:SetVertexColor(0.60, 0.60, 0.60); button.background:SetVertexColor(0.72, 0.72, 0.72); button.bottom:SetVertexColor(0.72, 0.72, 0.72); button.border:SetDesaturation(0.6); button.background:SetDesaturation(0.6); button.bottom:SetDesaturation(0.6); end end button:SetShown(id); end local function FormatAchievementCardByIndex(buttonIndex, ...) local button = AchievementCards[buttonIndex]; if button then FormatAchievementCard(button, ...); if buttonIndex > 7 or buttonIndex < 0 then button:SetAlpha(1); else button.toAlpha = 1; --for flip animation end end end local function InspectAchievement(achievementID) if not achievementID then return end; local id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, wasEarnedByMe = DataProvider:GetAchievementInfo(achievementID); local displayCard; if DataProvider:IsStatistic(achievementID) then displayCard = InspectionFrame.StatCard; InspectionFrame.Card:Hide(); displayCard:SetData(achievementID); displayCard:Show(); displayCard:SetAlpha(1); displayCard:ClearAllPoints(); displayCard:SetPoint("BOTTOM", InspectionFrame, "CENTER", 0, 36); else displayCard = InspectionFrame.Card; InspectionFrame.StatCard:Hide(); FormatAchievementCardByIndex(-1, id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, wasEarnedByMe); end animFlyOut.Card = displayCard; DisplayProgress(id, flags); InspectionFrame.currentAchievementID = id; InspectionFrame.currentAchievementName = name; InspectionFrame:Show(); InspectionFrame.HotkeyShiftClick:Show(); InspectionFrame.GetLink:Show(); local itemID = GetRewardItemID(id); if itemID then MountPreview:SetItem(itemID); else HideItemPreview(); end InspectionFrame:UpdateChain(id, completed); InspectionFrame:FindParentAchievementID(id); GoToCategoryButton:SetAchievement(id, isGuild); return completed, displayCard:GetHeight()/2 end local function FormatStatCard(buttonIndex, id) local button = StatCardController:Accquire(buttonIndex); if button then button:SetData(id); end end ScrollUtil.heightData = {}; ScrollUtil.achievementID = {}; ScrollUtil.totalHeight = 0; ScrollUtil.positionToButton = {}; ScrollUtil.formatFunc = FormatAchievementCardByIndex; function ScrollUtil:ResetHeights() self.totalHeight = 18; self.position = 1; self.lastOffset = 0; self.nextOffset = 0; wipe(self.heightData); end function ScrollUtil:GetScrollRange() local range = self.totalHeight - (AchievementContainer:GetHeight() or 0) + 18; if range < 0 then range = 0; end return range; end function ScrollUtil:SetCardData(cardIndex, achievementID, description, rewardText) local rewardHeight; if rewardText and rewardText ~= "" then rewardHeight = 22; else if isDarkTheme then rewardHeight = 2; else rewardHeight = 8; end end self.textReference:SetText(""); self.textReference:SetHeight(0); self.textReference:SetText(description); local descriptionHeight = self.textReference:GetHeight(); local numLines = ceil( descriptionHeight / 14 - 0.1 ); local buttonHeight = 72 + rewardHeight + 14*(numLines - 1) + 4; --4 is the Gap self.heightData[cardIndex] = self.totalHeight; self.achievementID[cardIndex] = achievementID; self.totalHeight = self.totalHeight + buttonHeight; return buttonHeight; end function ScrollUtil:SetStatCardData(cardIndex, achievementID) local buttonHeight; if achievementID < 0 then buttonHeight = 48; else if IsBossCard(achievementID) then --boss card buttonHeight = 94; else buttonHeight = 72; end end self.heightData[cardIndex] = self.totalHeight; self.achievementID[cardIndex] = achievementID; self.totalHeight = self.totalHeight + buttonHeight; return buttonHeight; end function ScrollUtil:GetTopButtonIndex(scrollOffset) if scrollOffset > self.nextOffset then local p = self.position + 1; if self.heightData[p] then self.position = p; self.lastOffset = self.heightData[p]; self.nextOffset = self.heightData[p + 1]; self:UpdateScrollChild(-1); end elseif scrollOffset < self.lastOffset then local p = self.position - 1; if self.heightData[p] then self.nextOffset = self.heightData[p + 1]; self.lastOffset = self.heightData[p]; if p < 1 then p = 1; end self.position = p; self:UpdateScrollChild(1); end end end function ScrollUtil:UpdateScrollChild(direction) if direction < 0 then local topButton = tremove(self.activeCards, 1); tinsert(self.activeCards, topButton); elseif direction > 0 then local bottomButton = tremove(self.activeCards); tinsert(self.activeCards, 1, bottomButton); end wipe(self.positionToButton); local p = self.position; local id; local positionIndex; local realID, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, wasEarnedByMe; local card; for i = 1, NUM_ACHIEVEMENT_CARDS do card = self.activeCards[i]; if not card then break end; positionIndex = p + i - 1; card.positionIndex = positionIndex; --card.TableIndex:SetText(i); if self.heightData[positionIndex] then card:SetPoint("TOP", AchievementContainer.ScrollChild, "TOP", 0, -self.heightData[positionIndex]); self.positionToButton[positionIndex] = card; id = self.achievementID[positionIndex]; if id then if card.id ~= id then realID, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, wasEarnedByMe = DataProvider:GetAchievementInfo(id); self.formatFunc(i, id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, wasEarnedByMe); card:Show(); else card:Show(); end else card:Hide(); end else card:Hide(); end end end function ScrollUtil:GetOffsetByPositionIndex(index) return self.heightData[index] or 0 end function ScrollUtil:GetAchievementIDByPositionIndex(index) return self.achievementID[index]; end function ScrollUtil:GetCardByPositionIndex(index) return self.positionToButton[index]; end function InspectCard(button, playAnimation) --Private local index = button.positionIndex; if not index then return end; local Card = InspectionFrame.Card; local id = button.id; Card:ClearAllPoints(); Card:SetPoint("BOTTOM", InspectionFrame, "CENTER", 0, 36); InspectionFrame.pauseScroll = nil; InspectionFrame.dataIndex = index; local numAchievements = InspectionFrame.numAchievements; if index <= 1 then index = 1; InspectionFrame.PrevButton:Disable(); InspectionFrame.NextButton:Enable(); InspectionFrame.NextButton:Show(); elseif index >= numAchievements then index = numAchievements; InspectionFrame.PrevButton:Enable(); InspectionFrame.PrevButton:Show(); InspectionFrame.NextButton:Disable(); else InspectionFrame.PrevButton:Enable(); InspectionFrame.NextButton:Enable(); InspectionFrame.PrevButton:Show(); InspectionFrame.NextButton:Show(); end if numAchievements > 0 then InspectionFrame.HotkeyMouseWheel:Show(); else InspectionFrame.HotkeyMouseWheel:Hide(); end local completed, extraY = InspectAchievement(id); --Animation local x0, y0 = InspectionFrame:GetCenter(); local x1, y1 = button:GetCenter(); local offsetX = x1 - x0; local offsetY = y1 - y0 - extraY; --animFlyIn.fromX, animFlyIn.fromY = offsetX, offsetY; animFlyOut.toX, animFlyOut.toY = offsetX, offsetY; animFlyOut.button = button; animFlyOut.duration = max(0.2, 0.2*(sqrt(offsetX^2 + (offsetY - 36)^2))/150 ); if playAnimation then InspectionFrame:SyncBlurOffset(index); animFlyIn:Play(); if DataProvider:IsStatistic(id) then InspectionFrame.StatCard:SetAlpha(0); FadeFrame(InspectionFrame.StatCard, 0.2, 1); end end end local function Slice_UpdateAchievementCards(categoryID, startIndex) --from 1st complete achievement to bottom local slice = 4; local id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, wasEarnedByMe; local processComplete = false; local numProcessed = 0; --print("process: "..startIndex); for i = startIndex, startIndex + slice do id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, wasEarnedByMe = DataProvider:GetAchievementInfoByOrder(categoryID, i); if i > 0 and id then if i <= NUM_ACHIEVEMENT_CARDS then FormatAchievementCardByIndex(i, id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, wasEarnedByMe); end ScrollUtil:SetCardData(i, id, description, rewardText); numProcessed = i; else processComplete = true; break; end end return numProcessed + 1, processComplete end local function Slice_ReverselyUpdateAchievementCards_Callback(categoryID, startIndex) --from 1st complete achievement to 1st incomplete local slice = 4; local id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, wasEarnedByMe; local processComplete = false; local numProcessed = 0; local numAchievements, numCompleted, numIncomplete = GetCategoryNumAchievements(categoryID, false); local index; for i = startIndex, startIndex + slice do id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, wasEarnedByMe = DataProvider:GetAchievementInfoByOrder(categoryID, i); if i <= numCompleted then index = i + numIncomplete; if i <= NUM_ACHIEVEMENT_CARDS then FormatAchievementCardByIndex(index, id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, wasEarnedByMe); end ScrollUtil:SetCardData(index, id, description, rewardText); numProcessed = i; else processComplete = true; break; end end return numProcessed + 1, processComplete end local function Slice_ReverselyUpdateAchievementCards(categoryID, startIndex) --from 1st incomplete achievement to the bottom local slice = 4; local id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, wasEarnedByMe; local processComplete = false; local numProcessed = 0; local numAchievements, numCompleted = GetCategoryNumAchievements(categoryID, false); --print("reverse process: "..startIndex); for i = startIndex, startIndex + slice do id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, wasEarnedByMe = DataProvider:GetAchievementInfoByOrder(categoryID, numCompleted + i); if id then --print("id #"..id) if not completed then if i <= NUM_ACHIEVEMENT_CARDS then FormatAchievementCardByIndex(i, id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, wasEarnedByMe); end ScrollUtil:SetCardData(i, id, description, rewardText); numProcessed = i; end else --print("Break, begin forward") processor:Hide(); processor.arg1 = categoryID; processor.arg2 = 1; --startIndex processor.func = Slice_ReverselyUpdateAchievementCards_Callback --Slice_UpdateAchievementCards; processor:Show(); return 1, false end end return numProcessed + 1, processComplete end local function Slice_UpdateStatCards(categoryID, startIndex) --from 1st complete achievement to bottom local slice = 4; local id; local processComplete = false; local numProcessed = 0; for i = startIndex, startIndex + slice do id = GetStatisticInfo(categoryID, i); if i > 0 and id then ScrollUtil:SetStatCardData(i, id); numProcessed = i; else processComplete = true; break; end end return numProcessed + 1, processComplete end local function UpdateAchievementScrollRange() local scrollBar = AchievementContainer.scrollBar; local range = ScrollUtil:GetScrollRange(); scrollBar:SetMinMaxValues(0, range); AchievementContainer.range = range; scrollBar:SetShown(range ~= 0); end processor.func = Slice_ReverselyUpdateAchievementCards --Slice_UpdateAchievementCards; processor.callback = UpdateAchievementScrollRange; local animFlip = CreateFrame("Frame"); function animFlip:Play(groupIndex) self:Hide(); self.t = 0; local objects; if groupIndex == 1 then objects = AchievementCards; else return end local numObjects = #objects; numObjects = min(numObjects, 6); for i = 1, numObjects do objects[i]:SetAlpha(0); end local oT, card; local scale, offset1, offset2, alpha; local fullDuration = numObjects * 0.05 + 0.4; self:SetScript("OnUpdate", function(f, elapsed) self.t = self.t + elapsed; for i = 1, numObjects do oT = self.t - 0.05 * (i - 1); if oT > 0 then if oT < 0.4 then scale = outQuart(oT, 1.25, 1, 0.4); offset1 = outQuart(oT, 24, 0, 0.4); offset2 = outQuart(oT, -72, -48, 0.4); alpha = min(1, linear(oT, 0, 1, 0.25)); else scale = 1; offset1 = 0; offset2 = -48; alpha = 1; end card = objects[i]; card:SetAlpha(alpha); card.border:SetPoint("TOP", 0, offset1); card.description:SetPoint("TOP", 0, offset2); card.points:SetScale(scale); card.icon:SetScale(scale); card.lion:SetScale(scale); card.border:SetScale(scale); end end if self.t > fullDuration then self:Hide(); end end); self:Show(); end local SORT_FUNC = Slice_ReverselyUpdateAchievementCards; local function UpdateAchievementCardsBySlice(categoryID) ScrollUtil:ResetHeights(); processor:Hide(); AchievementContainer.scrollBar:SetValue(0); for i = 1, NUM_ACHIEVEMENT_CARDS do AchievementCards[i]:Hide(); end local numAchievements, numCompleted, numIncomplete = GetCategoryNumAchievements(categoryID, false); DataProvider.numAchievements = numAchievements; processor.arg1 = categoryID; processor.arg2 = 1; --fromIndex processor.func = SORT_FUNC; processor:Start(); --animation if numAchievements ~= 0 then animFlip:Play(1); end end local function SwitchToSortMethod(index) if index == 2 then SORT_FUNC = Slice_UpdateAchievementCards; else SORT_FUNC = Slice_ReverselyUpdateAchievementCards; end local categoryID = DataProvider.currentCategory; if categoryID and categoryID > 0 then UpdateAchievementCardsBySlice(categoryID); end end local function UpdateStatCardsBySlice(categoryID) ScrollUtil:ResetHeights(); processor:Hide(); AchievementContainer.scrollBar:SetValue(0); local card; local numAchievements = GetCategoryNumAchievements(categoryID, false); local numCards = math.min(numAchievements, NUM_ACHIEVEMENT_CARDS); for i = 1, numCards do card = StatCardController:Accquire(i); card:Show(); --card:SetStat(categoryID, i); end StatCardController:HideRest(numCards + 1); DataProvider.numAchievements = numAchievements; processor.arg1 = categoryID; processor.arg2 = 1; --fromIndex processor.func = Slice_UpdateStatCards; processor:Start(); StatCardController:PlayAnimation(); end --------------------------------------------------------------------------------------------------- local function UpdateCategoryScrollRange() local button, buttons; local totalHeight = 0; local parentButtons = CategoryButtons:GetActiveParentButtons(); if not parentButtons then return end; for i = 1, #parentButtons do button = parentButtons[i]; if button.expanded then totalHeight = totalHeight + ( button.expandedHeight or 32); else totalHeight = totalHeight + 32; end totalHeight = totalHeight + 4; end local scrollBar = CategoryContainer.scrollBar; local newRange = max(0, totalHeight - CategoryContainer:GetHeight() + 52); local _, oldRange = scrollBar:GetMinMaxValues(); CategoryContainer.positionFunc = nil; if (newRange < oldRange) and (scrollBar:GetValue() > newRange) then CategoryContainer.positionFunc = function(endValue, delta, scrollBar) if scrollBar:GetValue() <= newRange then CategoryContainer.positionFunc = nil; scrollBar:SetShown(newRange ~= 0); scrollBar:SetMinMaxValues(0, newRange); CategoryContainer.range = newRange; end end else scrollBar:SetShown(newRange ~= 0); scrollBar:SetMinMaxValues(0, newRange); CategoryContainer.range = newRange; end end local animExpand = NarciAPI_CreateAnimationFrame(0.25); animExpand:SetScript("OnUpdate", function(self, elapsed) self.total = self.total + elapsed; local height = outSine(self.total, self.fromHeight, self.toHeight, self.duration); if self.total >= self.duration then self:Hide() height = self.toHeight; end animExpand.object:SetHeight(height); end) function animExpand:Set(object, toHeight) if self:IsShown() then if object == self.object then self:Hide(); else --Snap to destination self.object:SetHeight(self.toHeight); self:Hide(); end end local fromHeight = object:GetHeight(); self.object = object; self.fromHeight = fromHeight; self.toHeight = toHeight; local duration = sqrt(abs(fromHeight - toHeight)/32)*0.085; if duration > 0.01 then self.duration = duration; self:Show(); end end function animExpand:CollapseAll() self:Hide(); local button; local parentButtons = CategoryButtons:GetActiveParentButtons(); for i = 1, #parentButtons do button = parentButtons[i]; button.box:SetHeight(32); button.drawer:Hide(); button.drawer:SetAlpha(0); button.expanded = nil; if not button.isStats then button.progress:Hide(); button.percentSign:Show(); button.value:Show(); end end local lastButton = DataProvider:GetCategoryButtonByID(DataProvider.currentCategory); if lastButton then lastButton.label:SetTextColor(0.8, 0.8, 0.8); end CategoryContainer.scrollBar:SetValue(0); UpdateCategoryScrollRange(); end local function SetCategoryButtonProgress(button, numAchievements, numCompleted, isFeatsOfStrength) if numAchievements == 0 or numCompleted == 0 then button.fill:Hide(); button.fillEnd:Hide(); button.progress:SetText(0 .."/".. numAchievements); else if isFeatsOfStrength then button.fill:Hide(); button.fillEnd:Hide(); button.progress:SetText(numCompleted); else local percentage = numCompleted / numAchievements; if percentage == 1 then button.fill:Hide(); button.fillEnd:Hide(); button.progress:SetText(numAchievements); button.label:SetPoint("LEFT", 27, 0); button.greenCheck:Show(); else button.fill:Show(); button.fillEnd:Show(); button.fill:SetWidth(button.fillWidth * percentage); button.fill:SetTexCoord(0, percentage * 0.75, 0, 1); button.progress:SetText(numCompleted .."/".. numAchievements); button.label:SetPoint("LEFT", 10, 0); button.greenCheck:Hide(); end end end button.progress:Show(); button.percentSign:Hide(); button.value:Hide(); button.numAchievements, button.numCompleted = numAchievements, numCompleted; end local function UpdateCategoryButtonProgress(button) local categoryID = button.id; local totalAchievements, totalCompleted = GetCategoryNumAchievements(categoryID, true); --ACHIEVEMENT_COMPARISON_SUMMARY_ID button.numAchievements, button.numCompleted = totalAchievements, totalCompleted; if button.isStats then button.progress:SetText(totalAchievements); button.progress:Show(); button.percentSign:Hide(); button.value:Hide(); if button.subCategories then local numAchievements; for i = 1, #button.subCategories do categoryID = button.subCategories[i]; numAchievements = GetCategoryNumAchievements(categoryID, true); totalAchievements = totalAchievements + numAchievements; local childButton = DataProvider:GetCategoryButtonByID(categoryID); if childButton then childButton.progress:SetText(numAchievements); childButton.progress:Show(); childButton.percentSign:Hide(); childButton.value:Hide(); end end end return end local noPercent = button.noPercent; if noPercent then button.progress:SetText(totalCompleted); else button.progress:SetText(totalCompleted .."/".. totalAchievements); end if button.expanded then button.progress:Show(); button.percentSign:Hide(); button.value:Hide(); else button.progress:Hide(); button.percentSign:Show(); button.value:Show(); end if button.subCategories then local numAchievements, numCompleted; for i = 1, #button.subCategories do categoryID = button.subCategories[i]; numAchievements, numCompleted = GetCategoryNumAchievements(categoryID, true); totalAchievements = totalAchievements + numAchievements; totalCompleted = totalCompleted + numCompleted; local childButton = DataProvider:GetCategoryButtonByID(categoryID); if childButton then SetCategoryButtonProgress(childButton, numAchievements, numCompleted, noPercent); end end end button.totalAchievements, button.totalCompleted = totalAchievements, totalCompleted; if totalAchievements == 0 or totalCompleted == 0 then button.fill:Hide(); button.fillEnd:Hide(); button.value:SetText("0"); else if noPercent then button.fill:Hide(); button.fillEnd:Hide(); button.value:SetText(totalCompleted); else button.fill:Show(); button.fillEnd:Show(); local percentage = totalCompleted / totalAchievements; button.fill:SetWidth(button.fillWidth * percentage); button.fill:SetTexCoord(0, percentage * 0.75, 0, 1); if percentage == 1 then button.value:SetText("100"); else button.value:SetText( floor(100 * percentage) ); end end end end local function UpdateCategoryButtonProgressByCategoryID(categoryID) local button = DataProvider:GetCategoryButtonByID(categoryID); if button then UpdateCategoryButtonProgress(button) end end local function ReleaseAchievementCard() for _, card in pairs(AchievementCards) do --card:ClearAllPoints(); card:Hide(); end end local function SelectCategory(categoryID) if IS_STAT_CATEGORY[categoryID] then --statistics AchievementContainer:Show(); ScrollUtil.formatFunc = FormatStatCard; ScrollUtil.activeCards = StatCardController:GetTable(); UpdateStatCardsBySlice(categoryID); ReleaseAchievementCard(); SummaryFrame:Hide(); InspectionFrame.numAchievements = GetCategoryNumAchievements(categoryID, false); else AchievementContainer:Show(); ScrollUtil.formatFunc = FormatAchievementCardByIndex; ScrollUtil.activeCards = AchievementCards; StatCardController:ReleaseAll(); if categoryID == -1 then UpdateSummaryFrame(); SummaryFrame:Show(); else UpdateAchievementCardsBySlice(categoryID); SummaryFrame:Hide(); InspectionFrame.numAchievements = GetCategoryNumAchievements(categoryID, false); end end end local function ToggleFeatOfStrenghtText(button) if button.isFoS then local _, totalCompleted = GetCategoryNumAchievements(button.id, false); if totalCompleted == 0 then if isGuildView then MainFrame.FeatOfStrengthText:SetText(GUILD_FEAT_OF_STRENGTH_DESCRIPTION); else MainFrame.FeatOfStrengthText:SetText(FEAT_OF_STRENGTH_DESCRIPTION); end MainFrame.FeatOfStrengthText:Show(); else MainFrame.FeatOfStrengthText:Hide(); end else MainFrame.FeatOfStrengthText:Hide(); end end local function SubCategoryButton_OnClick(button) local categoryID = button.id; if categoryID ~= DataProvider.currentCategory then --print(categoryID); local lastButton = DataProvider:GetCategoryButtonByID(DataProvider.currentCategory); DataProvider.currentCategory = categoryID; if lastButton then lastButton.label:SetTextColor(0.8, 0.8, 0.8); end button.label:SetTextColor(1, 0.91, 0.647); SelectCategory(categoryID); else --print("old") end ToggleFeatOfStrenghtText(button); end local function CategoryButton_OnClick(button, mouse) SummaryButton:Show(); local expandedHeight = button.expandedHeight; local isExpanded = not button.expanded; if expandedHeight ~= 32 then if (mouse == "RightButton" or DataProvider.currentCategory == button.id) and (not isExpanded) then FadeFrame(button.drawer, 0.15, 0); animExpand:Set(button.box, 32); button.expanded = nil; else FadeFrame(button.drawer, 0.2, 1); animExpand:Set(button.box, expandedHeight); button.expanded = true; if mouse ~= "RightButton" then SubCategoryButton_OnClick(button); end end else button.expanded = isExpanded; SubCategoryButton_OnClick(button); end if button.isStats then else if button.expanded then button.progress:Show(); button.percentSign:Hide(); button.value:Hide(); else button.progress:Hide(); button.percentSign:Show(); button.value:Show(); end end UpdateCategoryScrollRange(); ---- ToggleFeatOfStrenghtText(button); end local function ExpandCategoryButtonNoAnimation(button) if not button then return end; if not button.expanded then button.progress:Show(); button.percentSign:Hide(); button.value:Hide(); local expandedHeight = button.expandedHeight; if expandedHeight ~= 32 then button.box:SetHeight(expandedHeight); button.drawer:Show(); button.drawer:SetAlpha(1); end button.expanded = true; UpdateCategoryScrollRange(); ToggleFeatOfStrenghtText(button); end end local function BuildCategoryStructure(tabID) local GUILD_FEAT_OF_STRENGTH_ID = 15093; local GUILD_CATEGORY_ID = 15076; local categories, structure, feats, legacys; if tabID == 2 then categories = GetGuildCategoryList(); structure = CategoryStructure.guild; feats = { FEAT_OF_STRENGTH_ID }; legacys = {}; elseif tabID == 1 then categories = GetCategoryList(); structure = CategoryStructure.player; feats = { GUILD_FEAT_OF_STRENGTH_ID }; legacys = { LEGACY_ID }; elseif tabID == 3 then categories = GetStatisticsCategoryList(); structure = CategoryStructure.stats; feats = {}; legacys = {}; tinsert(categories, 12080000); --reserved for Narcissus Stat for k, id in pairs(categories) do IS_STAT_CATEGORY[id] = true; end end local id; local name, parentID; local id2Order = {}; local subCategories = {}; local numParent = 0; for i = 1, #categories do id = categories[i]; name, parentID = DataProvider:GetCategoryInfo(id); --print(name, parentID) if (parentID == -1 or parentID == 15076) then if not id2Order[id] then numParent = numParent + 1; structure[ numParent ] = { ["id"] = id, ["name"] = name, ["children"] = {} }; id2Order[ id ] = numParent; end else tinsert(subCategories, id); end if parentID == LEGACY_ID then tinsert(legacys, id); elseif parentID == FEAT_OF_STRENGTH_ID then tinsert(feats, id); end end local order; for i = 1, #subCategories do id = subCategories[i]; name, parentID = DataProvider:GetCategoryInfo(id); order = id2Order[parentID]; tinsert( structure[ order ].children, id); end structure.numCategories = #categories; end local function CreateCategoryButtons(tabID) local frame; local button, parentButton, parentData, id; local structure; local numButtons = 0; local parentButtons, buttons; if not CategoryButtons.buttons then CategoryButtons.buttons = {}; end local isStats; local parentButtons = CategoryButtons:GetActiveParentButtons(tabID); if tabID == 1 then buttons = CategoryButtons.player.buttons; structure = CategoryStructure.player; frame = CategoryContainer.ScrollChild.PlayerCategory; --CategoryContainer.ScrollChild.GuildCategory:Hide(); elseif tabID == 2 then buttons = CategoryButtons.guild.buttons; structure = CategoryStructure.guild; frame = CategoryContainer.ScrollChild.GuildCategory; --CategoryContainer.ScrollChild.PlayerCategory:Hide(); elseif tabID == 3 then buttons = CategoryButtons.stats.buttons; structure = CategoryStructure.stats; frame = CategoryContainer.ScrollChild.StatsCategory; isStats = true; --CategoryContainer.ScrollChild.GuildCategory:Hide(); end --frame:Show(); for i = 1, #structure do numButtons = numButtons + 1; parentButton = buttons[numButtons]; parentData = structure[i]; id = parentData.id; if not parentButton then parentButton = CreateFrame("Button", nil, frame, "NarciAchievementCategoryButtonTemplate"); tinsert(buttons, parentButton); tinsert(parentButtons, parentButton); parentButton.isParentButton = true; parentButton:SetScript("OnClick", CategoryButton_OnClick); end DataProvider.id2Button[id] = parentButton; parentButton:SetParent(frame); parentButton:ClearAllPoints(); if i == 1 then parentButton:SetPoint("TOP", frame, "TOP", 2 , -24); else parentButton:SetPoint("TOP", parentButtons[i - 1].box, "BOTTOM", 0, -2); end if id == LEGACY_ID or id == FEAT_OF_STRENGTH_ID or id == 15093 then parentButton.noPercent = true; parentButton.percentSign:SetText(""); parentButton.value:SetPoint("RIGHT", parentButton, "RIGHT", -10, 0); if id == FEAT_OF_STRENGTH_ID or id == 15093 then parentButton.isFoS = true; end else parentButton.noPercent = nil; parentButton.percentSign:SetText("%"); parentButton.value:SetPoint("RIGHT", parentButton, "RIGHT", -16, 0); end parentButton.id = id; parentButton.label:SetText(parentData.name); parentButton.subCategories = parentData.children; local numChildren = #parentData.children; parentButton.expandedHeight = numChildren * 32 + 32; if isStats then parentButton.isStats = true; end for j = 1, numChildren do button = buttons[numButtons + 1]; id = parentData.children[j]; if not button then button = CreateFrame("Button", nil, parentButton.drawer, "NarciAchievementSubCategoryButtonTemplate"); button.label:SetWidth(130); tinsert(buttons, button); button:SetScript("OnClick", SubCategoryButton_OnClick); end DataProvider.id2Button[id] = button; button:SetParent(parentButton.drawer); button:ClearAllPoints(); if j == 1 then button:SetPoint("TOPRIGHT", parentButton.drawer, "BOTTOMRIGHT", 0, 0); else button:SetPoint("TOPRIGHT", buttons[numButtons], "BOTTOMRIGHT", 0, 0); end numButtons = numButtons + 1; button.id = id; button.label:SetText( DataProvider:GetCategoryInfo(id, 1) ); button.noPercent = nil; if isStats then button.isStats = true; end end UpdateCategoryButtonProgress(parentButton); end end local function CreateAchievementButtons(frame) local button; local buttons = {}; local numButtons = 0; for i = 1, NUM_ACHIEVEMENT_CARDS do button = CreateFrame("Button", nil, frame, "NarciAchievementLargeCardTemplate"); button:SetScript("OnClick", AchievementCard_OnClick); button.index = i; --button.AbsoluteIndex:SetText(i) tinsert(buttons, button); if i == 1 then button:SetPoint("TOP", frame, "TOP", 0, -18); else button:SetPoint("TOP", buttons[i - 1], "BOTTOM", 0, -4); end ReskinButton(button); end frame.buttons = buttons; AchievementCards = buttons; --Pop-up Card : Achievement details local Card = InspectionFrame.Card Card:SetScript("OnClick", AchievementCard_OnClick); AchievementCards[-1] = Card; animFlyIn.Card = Card; animFlyOut.Card = Card; animFlyIn.header = Card.header; animFlyIn.description = Card.description; animFlyIn.date = Card.date; animFlyIn.reward = Card.RewardFrame; end ------------------------------------------------ NarciAchievementInspectionFrameMixin = {}; function NarciAchievementInspectionFrameMixin:OnLoad() if not self.isLoaded then self.isLoaded = true; else return end InspectionFrame = self; local CompleteFrame = self.CriteriaFrame.LeftInset; CompleteFrame.header:SetText("COMPLETED"); CompleteFrame.header:SetTextColor(0.216, 0.502, 0.2); CompleteFrame.count:SetTextColor(0.216, 0.502, 0.2); self.numCompleted = CompleteFrame.count; local IncompleteFrame = self.CriteriaFrame.RightInset; IncompleteFrame.header:SetText("INCOMPLETED"); IncompleteFrame.header:SetTextColor(0.502, 0.2, 0.2); IncompleteFrame.count:SetTextColor(0.502, 0.2, 0.2); self.numIncomplete = IncompleteFrame.count; animFlyIn.background = self.blur; animFlyIn.ObjectiveFrame = self.CriteriaFrame; animFlyIn.ChainFrame = self.ChainFrame; animFlyOut.background = self.blur; animFlyOut.ObjectiveFrame = self.CriteriaFrame; animFlyOut.ChainFrame = self.ChainFrame; self.dataIndex = 1; self.TextContainerLeft = self.CriteriaFrame.LeftInset.TextFrame; self.TextContainerRight = self.CriteriaFrame.RightInset.TextFrame; self.MetaContainerLeft = self.CriteriaFrame.LeftInset.MetaFrame; self.MetaContainerRight = self.CriteriaFrame.RightInset.MetaFrame; self.BarContainerLeft = self.CriteriaFrame.LeftInset.BarFrame; self.BarContainerRight = self.CriteriaFrame.RightInset.BarFrame; --Achievement Container Blur local BlurFrame = self.blur; local BlurAnchor = BlurFrame.ScrollChild; local blur1 = BlurFrame.ScrollChild.blur1; local blur2 = BlurFrame.ScrollChild.blur2; local deltaRatio = 1; local speedRatio = 0.24; local blurHeight = 77; local range = 20000; local RepositionBlur = function(value, delta) local index; if delta < 0 then index = ceil( (value - 200) /940); else index = ceil( (value - 200) /940) - 1; end if index < 0 then index = 1; end if index ~= self.blurOffset then self.blurOffset = index; if index % 2 == 1 then blur2:SetPoint("TOPLEFT", BlurAnchor, "TOPLEFT", 0, -index * 940); blur2:SetPoint("TOPRIGHT", BlurAnchor, "TOPRIGHT", 0, -index * 940); else blur1:SetPoint("TOPLEFT", BlurAnchor, "TOPLEFT", 0, -index * 940); blur1:SetPoint("TOPRIGHT", BlurAnchor, "TOPRIGHT", 0, -index * 940); end end end NarciAPI_ApplySmoothScrollToScrollFrame(BlurFrame, deltaRatio, speedRatio, RepositionBlur, blurHeight, range); local Blur_OnMouseWheel = BlurFrame:GetScript("OnMouseWheel"); function self:ScrollBlur(delta) if delta ~= 0 then Blur_OnMouseWheel(BlurFrame, delta); ReturnButton:Hide(); end end function self:SyncBlurOffset(buttonIndex) self.blurOffset = -1; local value = (buttonIndex - 1) * blurHeight; BlurFrame.scrollBar:SetValue(value); local index = ceil( (value - 200) /940); if index < 0 then index = 1; end if index % 2 == 1 then blur1:SetPoint("TOPLEFT", BlurAnchor, "TOPLEFT", 0, (1 - index) * 940); blur1:SetPoint("TOPRIGHT", BlurAnchor, "TOPRIGHT", 0, (1 - index) * 940); blur2:SetPoint("TOPLEFT", BlurAnchor, "TOPLEFT", 0, -index * 940); blur2:SetPoint("TOPRIGHT", BlurAnchor, "TOPRIGHT", 0, -index * 940); else blur1:SetPoint("TOPLEFT", BlurAnchor, "TOPLEFT", 0, -index * 940); blur1:SetPoint("TOPRIGHT", BlurAnchor, "TOPRIGHT", 0, -index * 940); blur2:SetPoint("TOPLEFT", BlurAnchor, "TOPLEFT", 0, (1- index) * 940); blur2:SetPoint("TOPRIGHT", BlurAnchor, "TOPRIGHT", 0, (1 -index) * 940); end end --ScrollFrames: Criteria: Text, Meta local function UpdateScrollFrameDivider(value, delta, scrollBar) local minVal, maxVal = scrollBar:GetMinMaxValues(); if value >= maxVal - 0.1 then scrollBar.divLeft:Hide(); scrollBar.divCenter:Hide(); scrollBar.divRight:Hide(); elseif maxVal ~= 0 then scrollBar.divLeft:Show(); scrollBar.divCenter:Show(); scrollBar.divRight:Show(); end end local positionFunc = UpdateScrollFrameDivider; local parentScrollFunc = function(delta) InspectionFrame:OnMouseWheel(delta); end local numLines = 30; local deltaRatio = 2; local speedRatio = 0.24; local buttonHeight = 26; local range = numLines * buttonHeight - IncompleteFrame:GetHeight(); NarciAPI_ApplySmoothScrollToScrollFrame(IncompleteFrame.TextFrame, deltaRatio, speedRatio, positionFunc, buttonHeight, range, parentScrollFunc); NarciAPI_ApplySmoothScrollToScrollFrame(CompleteFrame.TextFrame, deltaRatio, speedRatio, positionFunc, buttonHeight, range, parentScrollFunc); local buttonHeight = 36; local deltaRatio = 2; NarciAPI_ApplySmoothScrollToScrollFrame(IncompleteFrame.MetaFrame, deltaRatio, speedRatio, positionFunc, buttonHeight, range, parentScrollFunc); NarciAPI_ApplySmoothScrollToScrollFrame(CompleteFrame.MetaFrame, deltaRatio, speedRatio, positionFunc, buttonHeight, range, parentScrollFunc); self:SetScript("OnLoad", nil); self.OnLoad = nil; end function NarciAchievementInspectionFrameMixin:OnShow() self.isTransiting = nil; end function NarciAchievementInspectionFrameMixin:OnMouseDown() if not self.NextButton:IsMouseOver() and not self.PrevButton:IsMouseOver() then animFlyOut:Play(); end end function NarciAchievementInspectionFrameMixin:ScrollToCategoryButton(button) if button then local topButton = CategoryButtons.player.buttons[1]; local offset = max(0, topButton:GetTop() - button:GetTop() - (CategoryContainer:GetHeight()/2 or 32) +32); --Attempt to position it to the vertical center CategoryContainer.scrollBar:SetValue(offset); end end function NarciAchievementInspectionFrameMixin:ScrollToPosition(positionIndex) local offset = ScrollUtil:GetOffsetByPositionIndex(positionIndex) - 18; AchievementContainer.scrollBar:SetValue(offset); end function NarciAchievementInspectionFrameMixin:OnMouseWheel(delta, stopLooping) if self.isTransiting or self.pauseScroll then return end; local index = self.dataIndex; if delta > 0 then index = index - 1; else index = index + 1; end if index < 1 or index > self.numAchievements then return else self.dataIndex = index; end self.dataIndex = index; self:ScrollBlur(delta); self:ScrollToPosition(index); local newButton = ScrollUtil:GetCardByPositionIndex(index); if newButton then if newButton.isHeader and not stopLooping then --skip statistic header self:OnMouseWheel(delta, true); return end InspectCard(newButton); end end local function FormatTextButtons(container, data, count, completed) if not container.buttons then container.buttons = {}; end local buttons = container.buttons; local button, numLines; for i = 1, count do button = buttons[i]; if not button then button = CreateFrame("Button", nil, container.ScrollChild, "NarciAchievementObjectiveTextButton"); tinsert(buttons, button); if i == 1 then button:SetPoint("TOPLEFT", container.ScrollChild, "TOPLEFT", 0, 0); button:SetPoint("TOPRIGHT", container.ScrollChild, "TOPRIGHT", 0, 0); elseif i % 5 == 1 then --bigger distance for every 5 entries button:SetPoint("TOPLEFT", buttons[i - 1], "BOTTOMLEFT", 0, -14); button:SetPoint("TOPRIGHT", buttons[i - 1], "TOPRIGHT", 0, -14); else button:SetPoint("TOPLEFT", buttons[i - 1], "BOTTOMLEFT", 0, 0); button:SetPoint("TOPRIGHT", buttons[i - 1], "TOPRIGHT", 0, 0); end if not completed then button.dash:SetTextColor(0.6, 0.6, 0.6); button.icon:SetDesaturated(true); else button.dash:SetText("|CFF5fbb46- |r"); button.icon:SetDesaturated(false); end end button.name:SetText(data.names[i]); numLines = ceil( button.name:GetHeight() / 12 - 0.1 ); button.icon:SetTexture(nil); button:SetHeight(18 + (numLines - 1)*12 ); button:Show(); end for i = count + 1, #buttons do buttons[i]:Hide(); end --Update Scroll Range local scrollBar = container.scrollBar; local range; if count == 0 then range = 0; else range = max(0, buttons[1]:GetTop() - buttons[count]:GetBottom() - container:GetHeight() + 4); range = floor(range + 0.2); end scrollBar:SetValue(0); scrollBar:SetMinMaxValues(0, range); scrollBar:SetShown(range ~= 0); container.positionFunc(0, 1, scrollBar); container.range = range; end local function FormatMetaButtons(container, data, count, completed) if not container.buttons then container.buttons = {}; end local buttons = container.buttons; local button, icon; for i = 1, count do button = buttons[i]; if not button then button = CreateFrame("Button", nil, container.ScrollChild, "NarciAchievementObjectiveMetaAchievementButton"); tinsert(buttons, button); if i == 1 then local buttonWidth = button:GetWidth(); button:SetPoint("TOP", container.ScrollChild, "TOP", -(buttonWidth + 1) * 3, 0); elseif i % 7 == 1 then button:SetPoint("TOPLEFT", buttons[i - 7], "BOTTOMLEFT", 0, -1); else button:SetPoint("TOPLEFT", buttons[i - 1], "TOPRIGHT", 1, 0); end if not completed then button.icon:SetDesaturated(true); end end icon = data.icons[i]; button.icon:SetTexture(icon); local id = data.assetIDs[i]; button.id = id; if id then button.textMode = nil; button.criteriaString = nil; else button.textMode = true; button.criteriaString = data.names[i]; if completed then button.icon:SetTexture(461267); --ThumbsUp else button.icon:SetTexture(456031); --Thumbsdown end end button.trackIcon:SetShown( DataProvider:IsTrackedAchievement(id) ); button:Show(); end for i = count + 1, #buttons do buttons[i]:Hide(); end local header = container.name; local numRow = ceil( count / 7); header:ClearAllPoints(); if numRow < 3 then header:SetPoint("TOPLEFT", buttons[1 + (numRow - 1)*7], "BOTTOMLEFT", 3, -4); else header:SetPoint("TOPLEFT", container, "BOTTOMLEFT", 3, -16); end if completed then header:SetText(""); container.description:SetText(""); container.points:SetText(""); container.shield:Hide(); else buttons[1]:SetAchievement(); end --Update Scroll Range local scrollBar = container.scrollBar; local range; if count == 0 then range = 0; else range = max(0, buttons[1]:GetTop() - buttons[count]:GetBottom() - container:GetHeight()); end scrollBar:SetValue(0); scrollBar:SetMinMaxValues(0, range); scrollBar:SetShown(range ~= 0); container.positionFunc(0, 1, scrollBar); container.range = range; end local function FormatStatusBars(container, data, count, completed) if not container.bars then container.bars = {}; end local bars = container.bars; local numBars = #data.bars; local bar, barData; for i = 1, numBars do bar = bars[i]; if not bar then bar = CreateFrame("Button", nil, container, "NarciAchievementObjectiveStatusBar"); tinsert(bars, bar); if i == 1 then bar:SetPoint("TOP", container, "TOP", 0, -2); else bar:SetPoint("TOP", bars[i - 1], "BOTTOM", 0, -16); end if completed then bar.label:SetTextColor(0.8, 0.8, 0.8); else bar.label:SetTextColor(0.6, 0.6, 0.6); end end barData = data.bars[i]; bar:SetMinMaxValues(0, barData[2]); bar:SetValue(barData[1], true); bar.label:SetText(barData[3]); bar:SetHeight(bar.label:GetHeight() + 22); bar:Show(); end for i = numBars + 1, #bars do bars[i]:Hide(); end end function NarciAchievementInspectionFrameMixin:FindParentAchievementID(achievementID) local parentAchievementID = GetParentAchievementID(achievementID); local button = AchievementCards[-1].ParentAchievmentButton; if parentAchievementID then local _, name, _, completed, month, day, year, _, _, icon = DataProvider:GetAchievementInfo(parentAchievementID); if completed then button:SetAlpha(1); else button:SetAlpha(0.60); end button.name = name; button.id = parentAchievementID; button.icon:SetTexture(icon); button.icon:SetDesaturated(not completed); button:Show(); if not button.hasInitialized then button.hasInitialized = true; button.border:SetTexture("Interface\\AddOns\\Narcissus_Achievements\\Art\\Shared\\IconBorderMiniPointRight"); end else button:Hide(); end end function NarciAchievementInspectionFrameMixin:UpdateChain(achievementID, currentIsCompleted) local ChainFrame = self.ChainFrame; if not ChainFrame.buttons then ChainFrame.buttons = {}; end local buttons = ChainFrame.buttons; local button; local achievements = { achievementID }; local currentAchievementID = achievementID; local currentPosition = 1; achievementID = GetPreviousAchievement(currentAchievementID); while achievementID do tinsert(achievements, 1, achievementID); achievementID = GetPreviousAchievement(achievementID); currentPosition = currentPosition + 1; end achievementID = GetNextAchievement(currentAchievementID); while achievementID do tinsert(achievements, achievementID); achievementID = GetNextAchievement(achievementID); end local numAchievements = #achievements; if numAchievements > 1 then local gap = 1; local extraHeight = 0; local buttonWidth; local id, completed, month, day, year, icon, _; local numCompleted = 0; for i = 1, numAchievements do button = buttons[i]; if not button then button = CreateFrame("Button", nil, ChainFrame, "NarciAchievementChainButton"); button.date.flyIn.hold1:SetDuration( (i - 1)*0.025 ); button.date.flyIn.hold2:SetDuration( (i - 1)*0.025 ); tinsert(buttons, button); end id = achievements[i]; _, _, _, completed, month, day, year, _, _, icon = DataProvider:GetAchievementInfo(id); if completed then numCompleted = numCompleted + 1; end button:ClearAllPoints(); if i == 1 then buttonWidth = button:GetWidth(); local numButtonsFirstRow = min(15, numAchievements); button:SetPoint("CENTER", ChainFrame.reference, "TOP", (buttonWidth + gap)*(1 - numButtonsFirstRow)/2 , -buttonWidth/2); buttonWidth = buttonWidth + gap; elseif i % 15 == 1 then --15 buttons per row local numButtonsThisRow = mod(numAchievements, 15); extraHeight = extraHeight + buttonWidth; local offsetY; if ChainFrame.showDates and completed then offsetY = -78; else offsetY = -1.5 * buttonWidth; end button:SetPoint("CENTER", ChainFrame.reference, "TOP", (buttonWidth + gap)*(1 - numButtonsThisRow)/2, offsetY); else button:SetPoint("CENTER", buttons[i - 1], "CENTER", buttonWidth, 0); end if i == currentPosition then button.border:SetTexture("Interface\\AddOns\\Narcissus_Achievements\\Art\\Shared\\IconBorderPointyMini"); button:SetAlpha(1); else button.border:SetTexture("Interface\\AddOns\\Narcissus_Achievements\\Art\\Shared\\IconBorderMini"); if completed then button:SetAlpha(0.60); else button:SetAlpha(0.33); end end if completed then button.date:SetText( FormatDate(day, month, year, true) ); else button.date:SetText(""); end button.icon:SetDesaturated(not completed); button.id = id; button.icon:SetTexture(icon); button:Show(); end for i = numAchievements + 1, #buttons do buttons[i]:Hide(); end ChainFrame.count:SetText( numCompleted .."/".. numAchievements); ChainFrame.reference:SetHeight(34 + extraHeight); --base height 34 ChainFrame:Show(); return true else ChainFrame:Hide(); return false end end function NarciAchievementInspectionFrameMixin:DisplayCriteria(cData, iData) local numCompleted = cData.count; local numIncomplete = iData.count; self.numCompleted:SetText(numCompleted); self.numIncomplete:SetText(numIncomplete); local TextContainer = self.TextContainerLeft; local MetaContainer = self.MetaContainerLeft; local BarContainer = self.BarContainerLeft; local icon = cData.icons[1]; local numBars = #cData.bars; local type = 1; if numBars ~= 0 then type = 3 elseif icon then type = 2; end if numCompleted == 0 then TextContainer:Hide(); MetaContainer:Hide(); BarContainer:Hide(); else if type == 2 then TextContainer:Hide(); MetaContainer:Show(); BarContainer:Hide(); FormatMetaButtons(MetaContainer, cData, numCompleted, true); elseif type == 3 then TextContainer:Hide(); MetaContainer:Hide(); BarContainer:Show(); FormatStatusBars(BarContainer, cData, numCompleted, true); else TextContainer:Show(); MetaContainer:Hide(); BarContainer:Hide(); FormatTextButtons(TextContainer, cData, numCompleted, true); end end -- local TextContainer = self.TextContainerRight; local MetaContainer = self.MetaContainerRight; local BarContainer = self.BarContainerRight; local icon = iData.icons[1]; local numBars = #iData.bars; local type = 1; if numBars ~= 0 then type = 3 elseif icon then type = 2; end if numIncomplete == 0 then TextContainer:Hide(); MetaContainer:Hide(); BarContainer:Hide(); else if type == 2 then TextContainer:Hide(); MetaContainer:Show(); BarContainer:Hide(); FormatMetaButtons(MetaContainer, iData, numIncomplete); elseif type == 3 then TextContainer:Hide(); MetaContainer:Hide(); BarContainer:Show(); FormatStatusBars(BarContainer, iData, numIncomplete); else TextContainer:Show(); MetaContainer:Hide(); BarContainer:Hide(); FormatTextButtons(TextContainer, iData, numIncomplete); end end end function NarciAchievementInspectionFrameMixin:ShowOrHideChainDates() local ChainFrame = self.ChainFrame; local showDates = ChainFrame.showDates; local buttons = ChainFrame.buttons; local button; local offsetY; if showDates then offsetY = -78; else offsetY = -52.5; end for i = 1, #buttons do button = buttons[i]; button.date:SetShown(showDates); if showDates then button.date.flyIn:Play(); else button.date.flyIn:Stop(); end if i ~= 1 and i % 15 == 1 then local date = button.date:GetText(); if date and date ~= "" then local point, relativeTo, relativePoint, xOfs, yOfs = button:GetPoint(); button:SetPoint(point, relativeTo, relativePoint, xOfs, offsetY); end end end --Update toggle visual if showDates then ChainFrame.DateToggle:SetLabelText(L["Hide Dates"]); else ChainFrame.DateToggle:SetLabelText(L["Show Dates"]); end ChainFrame.count:SetShown(not showDates); ChainFrame.header:SetShown(not showDates); ChainFrame.divLeft:SetShown(not showDates); ChainFrame.divRight:SetShown(not showDates); end ------------------------------------------------------------------------------ NarciAchievementGoToCategoryButtonMixin = {}; function NarciAchievementGoToCategoryButtonMixin:OnLoad() GoToCategoryButton = self; self:OnLeave(); end function NarciAchievementGoToCategoryButtonMixin:OnEnter() self.Label:SetTextColor(0.8, 0.8, 0.8); self.Icon:SetTexCoord(0.5, 1, 0, 1); self.Icon:SetAlpha(0.6); end function NarciAchievementGoToCategoryButtonMixin:OnLeave() self.Label:SetTextColor(0.6, 0.6, 0.6); self.Icon:SetTexCoord(0, 0.5, 0, 1); self.Icon:SetAlpha(0.4); end function NarciAchievementGoToCategoryButtonMixin:OnClick() if self.categoryID then local categoryButton = DataProvider:GetCategoryButtonByID(self.categoryID, self.isGuild); if categoryButton and (self.categoryID ~= DataProvider.currentCategory) then if not categoryButton.isParentButton then local parentCategoryButton = DataProvider:GetCategoryButtonByID(self.parentCategoryID, self.isGuild); ExpandCategoryButtonNoAnimation(parentCategoryButton); end categoryButton:Click(); animFlyOut.noTranslation = true; animFlyOut:Play(); InspectionFrame:ScrollToCategoryButton(categoryButton); AchievementContainer:Show(); SummaryButton:Show(); else animFlyOut:Play(); end end end function NarciAchievementGoToCategoryButtonMixin:SetAchievement(achievementID, isGuild) local categoryID = DataProvider:GetAchievementCategory(achievementID); local name, parentCategoryID = DataProvider:GetCategoryInfo(categoryID); if categoryID then self.categoryID = categoryID; self.parentCategoryID = parentCategoryID; self.isGuild = isGuild; self.Label:SetText(name); self:Show(); self:SetWidth(max(self.Label:GetWidth() + 60, 96)); end end NarciMetaAchievementButtonMixin = {}; function NarciMetaAchievementButtonMixin:SetAchievement() local id = self.id; if id then local id, name, points, completed, month, day, year, description, flags, icon, rewardText = DataProvider:GetAchievementInfo(id); local parent = self:GetParent():GetParent(); parent.name:SetText(name); parent.description:SetText(description); parent.points:SetText(points); parent.shield:SetShown(points ~= 0); if completed then if IsAccountWide(flags) then parent.name:SetTextColor(0.427, 0.812, 0.965); else parent.name:SetTextColor(1, 0.91, 0.647); end else parent.name:SetTextColor(0.8, 0.8, 0.8); end self.name = name; end end function NarciMetaAchievementButtonMixin:OnEnter() if self.textMode then local parent = self:GetParent():GetParent(); parent.name:SetText(self.criteriaString); parent.description:SetText(nil); parent.points:SetText(nil); parent.shield:Hide(); else self:SetAchievement(); end self.icon.animIn:Play(); self.border.animIn:Play(); end function NarciMetaAchievementButtonMixin:OnLeave() self.icon.animIn:Stop(); self.border.animIn:Stop(); self.icon:SetScale(1); self.border:SetScale(1); end function NarciMetaAchievementButtonMixin:OnClick() if ProcessModifiedClick(self) then return end; local id = InspectionFrame.currentAchievementID; if id and id ~= self.id then ReturnButton:AddToQueue(id, InspectionFrame.currentAchievementName); InspectAchievement(self.id); end end NarciAchievementChainButtonMixin = {}; function NarciAchievementChainButtonMixin:OnLoad() end function NarciAchievementChainButtonMixin:OnEnter() self:SetAchievement(); self.icon.animIn:Play(); self.border.animIn:Play(); end function NarciAchievementChainButtonMixin:OnLeave() self.icon.animIn:Stop(); self.border.animIn:Stop(); self.icon:SetScale(1); self.border:SetScale(1); Tooltip:FadeOut(); local ChainFrame = self:GetParent(); if ChainFrame.DateToggle and not ChainFrame:IsMouseOver() then ChainFrame.DateToggle:FadeOut(); end end function NarciAchievementChainButtonMixin:SetAchievement() local id = self.id; if id then Tooltip:ClearAllPoints(); Tooltip:SetPoint("TOPLEFT", self, "BOTTOMLEFT", 1, -2); Tooltip:SetAchievement(id); end end function NarciAchievementChainButtonMixin:OnMouseDown() Tooltip:FadeOut(); end NarciAchievementTooltipMixin = {}; function NarciAchievementTooltipMixin:OnLoad() NarciAPI.NineSliceUtil.SetUpBorder(self.FrameBorder, "whiteBorder", -12, 0.67, 0.67, 0.67); local animFade = NarciAPI_CreateAnimationFrame(0.25); self.animFade = animFade; animFade:SetScript("OnUpdate", function(frame, elapsed) frame.total = frame.total + elapsed; local alpha = outQuart(frame.total, frame.fromAlpha, frame.toAlpha, frame.duration); if frame.total >= frame.duration then frame:Hide(); alpha = frame.toAlpha; if alpha == 0 then self:Hide(); end end self:SetAlpha(alpha); end) function self:FadeIn() if InspectionFrame.isTransiting then return end; self:Show(); animFade:Hide(); animFade.fromAlpha = self:GetAlpha(); animFade.toAlpha = 1; animFade:Show(); end function self:FadeOut() animFade.toAlpha = 0; if not self:IsShown() then return end; animFade:Hide(); animFade.fromAlpha = self:GetAlpha(); animFade:Show(); end local showDelay = NarciAPI_CreateAnimationFrame(0.12); self.showDelay = showDelay; showDelay:SetScript("OnUpdate", function(frame, elapsed) frame.total = frame.total + elapsed; if frame.total >= frame.duration then frame:Hide(); if animFade.toAlpha == 1 then self:FadeIn(); end end end) end function NarciAchievementTooltipMixin:OnHide() self:Hide(); self:SetAlpha(0); end function NarciAchievementTooltipMixin:ResizeAndShow() self:SetHeight( self.name:GetHeight() + self.description:GetHeight() + 4 + 24 ); self:SetWidth( max(self.name:GetWrappedWidth() + (self.points:IsShown() and 48 or 0), self.description:GetWrappedWidth() + (self.date:IsShown() and 88 or 0) ) + 24); if not self:IsShown() then self.animFade.toAlpha = 1; self.showDelay:Show(); elseif self.animFade.toAlpha == 0 then self:FadeIn(); end end function NarciAchievementTooltipMixin:SetAchievement(id) local _, name, points, completed, month, day, year, description, flags, icon = DataProvider:GetAchievementInfo(id); self.name:SetText(name); self.description:SetText(description); if completed then if IsAccountWide(flags) then self.name:SetTextColor(0.427, 0.812, 0.965); else self.name:SetTextColor(1, 0.91, 0.647); end self.date:SetText( FormatDate(day, month, year) ); self.date:Show(); else self.name:SetTextColor(0.8, 0.8, 0.8); self.date:SetText(""); self.date:Hide(); end if points == 0 then self.shield:Hide(); self.points:Hide(); else self.shield:Show(); self.points:SetText(points); self.points:Show(); end self:ResizeAndShow(); end ---------------------------------------------------------------------------------- NarciAchievementReturnButtonMixin = {}; function NarciAchievementReturnButtonMixin:OnLoad() ReturnButton = self; self.structure = {}; end function NarciAchievementReturnButtonMixin:PlayFlyIn() self.flyIn:Play(); end function NarciAchievementReturnButtonMixin:OnEnter() self.label:SetTextColor(0.88, 0.88, 0.88); end function NarciAchievementReturnButtonMixin:OnLeave() self.label:SetTextColor(0.6, 0.6, 0.6); end function NarciAchievementReturnButtonMixin:OnHide() self:Hide(); self:StopAnimating(); wipe(self.structure); end function NarciAchievementReturnButtonMixin:SetLabelText(text) self.label:SetText(""); self.label:SetWidth(0); self.label:SetText(text); local textWidth = min(140, self.label:GetWidth()); self.label:SetWidth(textWidth) self:SetWidth(textWidth + 24); end function NarciAchievementReturnButtonMixin:AddToQueue(achievementID, achievementName) for i = 1, #self.structure + 1 do if self.structure[i] == nil then self.structure[i] = {id = achievementID, name = achievementName}; break; end end self:SetLabelText(achievementName); self:Show(); self:PlayFlyIn(); end function NarciAchievementReturnButtonMixin:OnClick() for i = #self.structure, 1, -1 do local info = self.structure[i]; if info and info.id then InspectAchievement(info.id); if i == 1 then self:Hide(); else self:SetLabelText(self.structure[i - 1].name) end self.structure[i] = nil; break; end end end NarciAchievementSearchBoxMixin = {}; local function InspectResult(button) local playAnimation = not InspectionFrame:IsShown(); local id = button.id; local Card = InspectionFrame.Card; Card:ClearAllPoints(); Card:SetPoint("BOTTOM", InspectionFrame, "CENTER", 0, 36); InspectionFrame.PrevButton:Disable(); InspectionFrame.NextButton:Disable(); InspectionFrame.pauseScroll = true; InspectAchievement(id); animFlyOut.button = nil; animFlyOut.fromX, animFlyOut.toX, animFlyOut.fromY, animFlyOut.toY = 0, 0, 0, 0; animFlyOut.noTranslation = true; if playAnimation then animFlyIn:Play(); if DataProvider:IsStatistic(id) then InspectionFrame.StatCard:SetAlpha(0); FadeFrame(InspectionFrame.StatCard, 0.25, 1); end end end addon.InspectResult = InspectResult; -------------------------------------------------------------- local DateUtil = {}; function DateUtil:GetToday() self.today = time(); end function DateUtil:GetDifference(day, month, year) year = 2000 + year; local past = time( {day = day, month = month, year = year} ) if not self.today then self.today = time(); end return (self.today - past) end function DateUtil:GetPastDays(day, month, year) local diff = self:GetDifference(day, month, year); local d = floor(diff / 86400); if d <= 0 then return L["Today"] elseif d == 1 then return L["Yesterday"] elseif d < 31 then return format(L["Format Days Ago"], d); else local m = floor(d / 30.5); if m <= 1 then return L["A Month Ago"] elseif m < 12 then return format(L["Format Months Ago"], m); else local y = floor(m / 12 + 0.15); if y == 1 then return L["A Year Ago"] else return format(L["Format Years Ago"], y); end end end end local UpdateHeaderFrame; function UpdateSummaryFrame(breakLoop) local recentAchievements = { GetLatestCompletedAchievements(isGuildView) }; local numAchievements = #recentAchievements; if (numAchievements < 5) and (not breakLoop) then After(0.05, function() UpdateSummaryFrame(true); end); return end ScrollUtil:ResetHeights(); processor:Hide(); AchievementContainer.scrollBar:SetValue(0); for i = 1, NUM_ACHIEVEMENT_CARDS do AchievementCards[i]:Hide(); end local id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild; for i = 1, #recentAchievements do id = recentAchievements[i]; if id then id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild = DataProvider:GetAchievementInfo(id); if i <= NUM_ACHIEVEMENT_CARDS then FormatAchievementCardByIndex(i, id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, true); AchievementCards[i].date:SetText( DateUtil:GetPastDays(day, month, year) ); end ScrollUtil:SetCardData(i, id, description, rewardText); else break; end end UpdateAchievementScrollRange(); ScrollUtil:UpdateScrollChild(0); UpdateHeaderFrame(isGuildView); if numAchievements ~= 0 then animFlip:Play(1); end InspectionFrame.numAchievements = numAchievements; end ---------------------------------------------------------------------------------- function TabUtil:SaveOffset() --currentTab before switching local button = DataProvider:GetCategoryButtonByID(DataProvider.currentCategory); local offset = CategoryContainer.scrollBar:GetValue(); if self.tabID == 1 then --achievement self.lastPlayerButton = button; self.lastPlayerScrollValue = offset; elseif self.tabID == 2 then --guild self.lastGuildButton = button; self.lastGuildScrollValue = offset; elseif self.tabID == 3 then --statistics self.lastStatButton = button; self.lastStatScrollValue = offset; end ShutInspection(); end function TabUtil:ResumeOffset() local lastButton, lastOffset; if self.tabID == 1 then lastButton = self.lastPlayerButton; lastOffset = self.lastPlayerScrollValue; elseif self.tabID == 2 then lastButton = self.lastGuildButton; lastOffset = self.lastGuildScrollValue; elseif self.tabID == 3 then lastButton = self.lastStatButton; lastOffset = self.lastStatScrollValue; end if lastButton then AchievementContainer:Show(); SummaryButton:Show(); SubCategoryButton_OnClick(lastButton); ToggleFeatOfStrenghtText(lastButton); else if self.tabID == 4 then else SummaryButton:Click(); end end AchievementContainer.scrollBar:SetValue(0); CategoryContainer.scrollBar:SetValue(lastOffset or 0); end function TabUtil:ToggleAchievement(tabID) if tabID ~= self.tabID then self:SaveOffset(); self.tabID = tabID; local isGuild = (tabID == 2); isGuildView = isGuild; CategoryContainer.ScrollChild.PlayerCategory:SetShown(not isGuild); CategoryContainer.ScrollChild.GuildCategory:SetShown(isGuild); CategoryContainer.ScrollChild.StatsCategory:Hide(); DIYContainer:Hide(); EditorContainer:Hide(); CategoryContainer:Show(); UpdateCategoryScrollRange(); MainFrame.HeaderFrame.points:SetText( BreakUpLargeNumbers(GetTotalAchievementPoints(isGuild)) ); FilterButton:Enable(); SummaryButton:SetMode(1); self:ResumeOffset(); self:EnableSearchBox(true); end end function TabUtil:ToggleStats() if self.tabID == 3 then return end; self:SaveOffset(); self.tabID = 3; CategoryContainer.ScrollChild.StatsCategory:Show(); CategoryContainer.ScrollChild.PlayerCategory:Hide(); CategoryContainer.ScrollChild.GuildCategory:Hide(); MainFrame.FeatOfStrengthText:Hide(); MainFrame:UpdatePinCount(); DIYContainer:Hide(); EditorContainer:Hide(); CategoryContainer:Show(); UpdateCategoryScrollRange(); FilterButton:Disable(); SummaryButton:SetMode(2); self:ResumeOffset(); self:EnableSearchBox(true); end function TabUtil:ToggleDIY() if self.tabID == 4 then return end; self:SaveOffset(); self.tabID = 4; DataProvider.currentCategory = nil; DIYContainer:Show(); if not TabUtil.isDIYLoaded then TabUtil.isDIYLoaded = true; DIYContainer:Refresh(); DIYContainer.scrollBar:SetValue(0); end EditorContainer:Show(); CategoryContainer:Hide(); AchievementContainer:Hide(); FilterButton:Disable(); SummaryButton:SetMode(3); MainFrame.FeatOfStrengthText:Hide(); self:EnableSearchBox(false); end function TabUtil:GetTabID() return self.tabID or 1; end -------------------------------------------------------------- NarciAchievementFilterButtonMixin = {}; function NarciAchievementFilterButtonMixin:OnLoad() self:OnLeave(); FilterButton = self; self.OnLoad = nil; self:SetScript("OnLoad", nil); end function NarciAchievementFilterButtonMixin:OnClick() local state = not NarciAchievementOptions.IncompleteFirst; NarciAchievementOptions.IncompleteFirst = state; self:UpdateFilter(); end function NarciAchievementFilterButtonMixin:OnEnable() self.label:SetTextColor(0.8, 0.8, 0.8); self:EnableMouse(true); end function NarciAchievementFilterButtonMixin:OnDisable() self.label:SetTextColor(0.5, 0.5, 0.5); self:EnableMouse(false); end function NarciAchievementFilterButtonMixin:UpdateFilter() if NarciAchievementOptions.IncompleteFirst then self.label:SetText(L["Incomplete First"]); SwitchToSortMethod(1); else self.label:SetText(L["Earned First"]); SwitchToSortMethod(2); end end function NarciAchievementFilterButtonMixin:OnMouseDown() self.texture:SetTexCoord(0, 1, 0.5, 1); end function NarciAchievementFilterButtonMixin:OnMouseUp() self.texture:SetTexCoord(0, 1, 0, 0.5); end function NarciAchievementFilterButtonMixin:OnEnter() self.texture:SetVertexColor(1, 1, 1); end function NarciAchievementFilterButtonMixin:OnLeave() self.texture:SetVertexColor(0.66, 0.66, 0.66); end ------------------------------------------------------------------ NarciAchievementSummaryButtonMixin = {}; function NarciAchievementSummaryButtonMixin:OnClick() self:Hide(); local categoryID; if self.modeID == 2 then AchievementContainer:Show(); SelectCategory(-2); animExpand:CollapseAll(); categoryID = - 2; else categoryID = -1; SelectCategory(-1); end if DataProvider.currentCategory ~= categoryID then animExpand:CollapseAll(); DataProvider.currentCategory = categoryID; end MainFrame.FeatOfStrengthText:Hide(); end function NarciAchievementSummaryButtonMixin:OnMouseDown() self.texture:SetTexCoord(0, 1, 0.5, 0.8125); self.label:SetTextColor(0.6, 0.6, 0.6); end function NarciAchievementSummaryButtonMixin:OnMouseUp() self.texture:SetTexCoord(0, 1, 0, 0.3125); self.label:SetTextColor(0.8, 0.8, 0.8); end function NarciAchievementSummaryButtonMixin:SetMode(modeID) self.modeID = modeID; if modeID == 3 then self:Show(); self:Disable(); self.label:SetText("DIY"); else if modeID == 1 then self.label:SetText(ACHIEVEMENT_SUMMARY_CATEGORY); else self.label:SetText(L["Pinned Entries"]); end self:Enable(); end end function NarciAchievementSummaryButtonMixin:OnEnable() self:EnableMouse(true); self:OnMouseUp(); end function NarciAchievementSummaryButtonMixin:OnDisable() self:EnableMouse(); self:OnMouseDown(); end ------------------------------------------------------------------ local function CreateTabButtons() local tabNames = {ACHIEVEMENTS, ACHIEVEMENTS_GUILD_TAB, STATISTICS, "DIY", L["Settings"]}; local frame = Narci_AchievementFrame; local buttons = {}; local function DeselectRest(button) for i = 1, #buttons do if buttons[i] ~= button then buttons[i]:Deselect(); end end end local funcs = { function(self) TabUtil:ToggleAchievement(1); DeselectRest(self); self:Select(); end, function(self) TabUtil:ToggleAchievement(2); DeselectRest(self); self:Select(); end, function(self) TabUtil:ToggleStats(); DeselectRest(self); self:Select(); end, function(self) TabUtil:ToggleDIY(); DeselectRest(self); self:Select(); end, function(self) MainFrame.Settings:Toggle(); end, } local numTabs = #tabNames; for i = 1, numTabs do local button = CreateFrame("Button", nil, frame, "NarciAchievementTabButtonTemplate"); tinsert(buttons, button); button:SetLabel(tabNames[i]); button.id = i; if i == 1 then button:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", 23, 0); button:Select(); elseif i == numTabs then --settings button:SetPoint("TOPRIGHT", frame, "BOTTOMRIGHT", -23, 0); MainFrame.Settings.linkedTabButton = button; else button:SetPoint("TOPLEFT", buttons[i - 1], "TOPRIGHT", 14, 0); end button:SetScript("OnClick", funcs[i]); end TabButtons = buttons; TabUtil:ToggleAchievement(1); end ---------------------------------------------------------------------------------- local function GetInspectedCardLink() local achievementID = InspectionFrame.currentAchievementID; if not achievementID then return end; local URL = NarciLanguageUtil:GetWowheadLink(); URL = URL .. "achievement=" .. tostring(achievementID); return URL end NarciAchievementGetLinkButtonMixin = {}; function NarciAchievementGetLinkButtonMixin:OnLoad() self.Label:SetText(L["External Link"]); local textWidth = max(40, floor( self.Label:GetWidth() + 0.5 ) ); self:SetWidth(textWidth + 40 + 16); local range = textWidth + 24; self.Icon:SetPoint("RIGHT", self, "RIGHT", -18, 0); local animShow = NarciAPI_CreateAnimationFrame(0.25); self.animShow = animShow; animShow:SetScript("OnUpdate", function(frame, elapsed) frame.total = frame.total + elapsed; local offsetX = inOutSine(frame.total, frame.fromX, frame.toX, frame.duration); local textOffset = inOutSine(frame.total, frame.fromTextX, frame.toTextX, frame.duration); local alpha = outQuart(frame.total, frame.fromAlpha, frame.toAlpha, frame.duration); if frame.total >= frame.duration then offsetX = frame.toX; textOffset = frame.toTextX; alpha = frame.toAlpha; frame:Hide(); if alpha == 0 then self.Clipboard:Hide(); end end self.Icon:SetPoint("RIGHT", self, "RIGHT", offsetX, 0); self.Label:SetPoint("RIGHT", self, textOffset, 0); self.Label:SetAlpha(alpha); self.Clipboard:SetAlpha(alpha); end); function self:AnimShow() animShow:Hide(); local offsetX, _; _, _, _, offsetX = self.Icon:GetPoint(); _, _, _, animShow.fromTextX = self.Label:GetPoint(); animShow.duration = ( sqrt(1 - abs(offsetX)/ range) * 0.4); animShow.fromX = offsetX; animShow.toX = -range; --animShow.fromTextX = -28; --keep label still animShow.toTextX = -28; animShow.fromAlpha = self.Label:GetAlpha(); animShow.toAlpha = 1; animShow:Show(); end function self:AnimHide() animShow:Hide(); local offsetX, _; _, _, _, offsetX = self.Icon:GetPoint(); _, _, _, animShow.fromTextX = self.Label:GetPoint(); animShow.duration = ( sqrt(abs(offsetX)/ range) * 0.5); animShow.fromX = offsetX; animShow.toX = -20; animShow.toTextX = -10; animShow.fromAlpha = self.Label:GetAlpha(); animShow.toAlpha = 0; animShow:Show(); end self.Clipboard:SetScript("OnLeave", function() self:OnLeave(); end); self.Clipboard.EditBox:SetScript("OnLeave", function() self:OnLeave(); end); self.Clipboard:ReAnchorTooltipToObject(self.Label); self.Clipboard.EditBox.onQuitFunc = function() self:OnLeave(); end end function NarciAchievementGetLinkButtonMixin:OnClick() self.Clipboard:ShowClipboard(); self.Clipboard:SetText( GetInspectedCardLink() ); self.Clipboard:SetFocus(); self.Label:Hide(); end function NarciAchievementGetLinkButtonMixin:OnEnter() --self.Label:SetTextColor(1, 1, 1); self.Icon:SetTexCoord(0.5, 1, 0, 1); self.Icon:SetAlpha(0.6); self:AnimShow(); self.Label:Show(); end function NarciAchievementGetLinkButtonMixin:OnLeave() if not self:IsMouseOver() and not self.Clipboard:HasFocus() then self.Label:SetTextColor(0.8, 0.8, 0.8); self.Icon:SetTexCoord(0, 0.5, 0, 1); self.Icon:SetAlpha(0.4); self:AnimHide(); self.Clipboard.EditBox:HighlightText(0, 0); end end -------------------------------------------------------- local function InitializeFrame(frame) --Category CategoryContainer = frame.CategoryFrame; local isGuild = true; CreateCategoryButtons(2); isGuild = not isGuild; CreateCategoryButtons(1); CreateCategoryButtons(3); local numCategories = CategoryStructure.player.numCategories; local deltaRatio = 2; local speedRatio = 0.15; local buttonHeight = 32; local range = numCategories * (buttonHeight + 4) - CategoryContainer:GetHeight(); local positionFunc; NarciAPI_ApplySmoothScrollToScrollFrame(CategoryContainer, deltaRatio, speedRatio, positionFunc, buttonHeight, range); UpdateCategoryScrollRange(); --Sort Method FilterButton:UpdateFilter(); showNotEarnedMark = NarciAchievementOptions.ShowRedMark; --Achievement AchievementContainer = frame.AchievementCardFrame; CreateAchievementButtons(AchievementContainer.ScrollChild); local numCards = #AchievementCards; local deltaRatio = 1; local speedRatio = 0.24; local buttonHeight = 64; local range = numCards * (buttonHeight + 4) - AchievementContainer:GetHeight(); local positionFunc; NarciAPI_ApplySmoothScrollToScrollFrame(AchievementContainer, deltaRatio, speedRatio, positionFunc, buttonHeight, range); local scrollBar = AchievementContainer.scrollBar; scrollBar:SetScript("OnValueChanged", function(bar, value) AchievementContainer:SetVerticalScroll(value); ScrollUtil:GetTopButtonIndex(value); end); UpdateAchievementScrollRange(); --Model Preview MountPreview = CreateFrame("ModelScene", nil, frame,"NarciAchievementRewardModelTemplate"); MountPreview:ClearAllPoints(); MountPreview:SetPoint("LEFT", frame, "RIGHT", 4, 0); Tooltip = frame.Tooltip; --Header Filter, Total Points, Search, Close local HeaderFrame = frame.HeaderFrame; SummaryButton = frame.SummaryButton; function UpdateHeaderFrame(isGuild) --Private local total, completed = GetNumCompletedAchievements(isGuild); HeaderFrame.totalAchievements:SetText(completed.."/"..total); HeaderFrame.points:SetText( BreakUpLargeNumbers(GetTotalAchievementPoints(isGuild)) ); if completed > 0 then HeaderFrame.fill:Show(); local percentage = completed / total; HeaderFrame.fill:SetWidth(198 * percentage); HeaderFrame.fill:SetTexCoord(0, percentage * 0.75, 0, 1); if percentage == 1 then HeaderFrame.value:SetText("100"); HeaderFrame.fillEnd:Hide(); else HeaderFrame.value:SetText( floor(100 * percentage) ); HeaderFrame.fillEnd:Show(); end end HeaderFrame.value:Show(); HeaderFrame.percentSign:Show(); HeaderFrame.progress:Hide(); end --SummaryFrame SummaryFrame = frame.SummaryFrame; --DIY Achievements DIYContainer = frame.DIYContainer; EditorContainer = frame.EditorContainer; --Tabs CreateTabButtons(); NarciAchievement_SelectTheme(NarciAchievementOptions.Theme or 1); frame:Show(); UpdateSummaryFrame(); --Reclaim Temp wipe(CategoryStructure); CategoryStructure = nil; CreateCategoryButtons = nil; CreateAchievementButtons = nil; CreateTabButtons = nil; InitializeFrame = nil; end ----------------------------------------- local FloatingCard = addon.FloatingCard; local function RefreshInspection(achievementID) --Refresh inspection card if InspectionFrame:IsShown() then if achievementID then if (achievementID == InspectionFrame.Card.id) then InspectAchievement(achievementID); end else if InspectionFrame.Card.id then InspectAchievement(InspectionFrame.Card.id); end end end end -------------------------------------------------------------------- --Public NarciAchievementFrameMixin = {}; function NarciAchievementFrameMixin:OnLoad() MainFrame = self; ScrollUtil.textReference = self.TextHeightRetriever; self:RegisterForDrag("LeftButton"); self:SetAttribute("nodeignore", true); --ConsolePort: Ignore this frame end local function AchievementFrame_OnKeyDown(self, key) if key == "ESCAPE" then self:SetPropagateKeyboardInput(false); self:Hide(); else self:SetPropagateKeyboardInput(true); end end function NarciAchievementFrameMixin:OnShow() if self.pendingCategoryID then SelectCategory(self.pendingCategoryID); self.pendingCategoryID = nil; elseif self.pendingUpdate then self.pendingUpdate = nil; UpdateSummaryFrame(); end self:RegisterDynamicEvent(true); RefreshInspection(); StatCardController:UpdateList(); self:SetScript("OnKeyDown", AchievementFrame_OnKeyDown); end function NarciAchievementFrameMixin:OnHide() self:RegisterDynamicEvent(false); self:SetScript("OnKeyDown", nil); end function NarciAchievementFrameMixin:RegisterDynamicEvent(state) if state then self:RegisterEvent("CRITERIA_UPDATE"); self:RegisterEvent("CRITERIA_COMPLETE"); else self:UnregisterEvent("CRITERIA_UPDATE"); self:UnregisterEvent("CRITERIA_COMPLETE"); end end function NarciAchievementFrameMixin:OnEvent(event, ...) RefreshInspection(...); if event == "CRITERIA_UPDATE" then StatCardController:UpdateList(); end end function NarciAchievementFrameMixin:ShowRedMark(visible) showNotEarnedMark = visible; for i = 1, #AchievementCards do if visible then AchievementCards[i].NotEarned:SetWidth(20); else AchievementCards[i].NotEarned:SetWidth(0.1); end end if MainFrame:IsShown() then local categoryID = DataProvider.currentCategory; if categoryID and categoryID ~= 0 then SelectCategory(categoryID); end end end function NarciAchievementFrameMixin:LocateAchievement(achievementID, clickAgainToClose) local Card = InspectionFrame.Card; if (not achievementID) or ( clickAgainToClose and (Card.id == achievementID) and MainFrame:IsShown() and InspectionFrame:IsShown() ) then MainFrame:Hide(); return end MainFrame:Show(); local playAnimation = not InspectionFrame:IsShown(); Card:ClearAllPoints(); Card:SetPoint("BOTTOM", InspectionFrame, "CENTER", 0, 36); InspectionFrame.PrevButton:Disable(); InspectionFrame.NextButton:Disable(); InspectionFrame.pauseScroll = true; InspectAchievement(achievementID); animFlyOut.button = nil; animFlyOut.fromX, animFlyOut.toX, animFlyOut.fromY, animFlyOut.toY = 0, 0, 0, 0; animFlyOut.noTranslation = true; if playAnimation then animFlyIn:Play(); end end function NarciAchievementFrameMixin:OnDragStart() self:StartMoving(); --Anchor settings frame to self local f = self.Settings; f:ClearAllPoints(); f:SetPoint("TOPLEFT", self, "TOPRIGHT", 8, -4); end function NarciAchievementFrameMixin:OnDragStop() self:StopMovingOrSizing(); self.Settings.AnchorToUIParent(); end function NarciAchievementFrameMixin:UpdatePinCount() local HeaderFrame = self.HeaderFrame; local total, cap = PinUtil:GetTotal(); HeaderFrame.totalAchievements:SetText(L["Pinned Entries"]); HeaderFrame.progress:SetText(string.format("%d/%d", total, cap)); HeaderFrame.progress:Show(); HeaderFrame.value:Hide(); HeaderFrame.percentSign:Hide(); if total > 0 then HeaderFrame.fill:Show(); local percentage = total / cap; HeaderFrame.fill:SetWidth(198 * percentage); HeaderFrame.fill:SetTexCoord(0, percentage * 0.75, 0, 1); if percentage == 1 then HeaderFrame.fillEnd:Hide(); else HeaderFrame.fillEnd:Show(); end else HeaderFrame.fill:Hide(); HeaderFrame.fillEnd:Hide(); end end --[[ function NarciAchievement_FormatAlertCard(card) local achievementID = card.id; local uiScale = MainFrame:GetEffectiveScale(); card.uiScale = uiScale; local id, name, points, completed, month, day, year, description, flags, icon, rewardText = DataProvider:GetAchievementInfo(achievementID); FormatFloatingCard(card, id, name, points, completed, month, day, year, description, flags, icon, rewardText); ReskinButton(card); end function NarciAchievement_ReskinButton(card) ReskinButton(card); card.isDarkTheme = isDarkTheme; end --]] NarciAchievementLargeCardMixin = {}; function NarciAchievementLargeCardMixin:OnDragStart() --Create Floating Card if not self.id then return end; self.AnimPushed:Stop(); self:Hide(); local card = FloatingCard:CreateFromCard(self, 1); end function NarciAchievementLargeCardMixin:OnLoad() self:RegisterForDrag("LeftButton"); self:SetScript("OnLoad", nil); self.OnLoad = nil; end function NarciAchievementLargeCardMixin:OnMouseDown() self.AnimPushed:Stop(); self.AnimPushed.hold:SetDuration(20); self.AnimPushed:Play(); end function NarciAchievementLargeCardMixin:OnMouseUp() self.AnimPushed.hold:SetDuration(0); end function NarciAchievementLargeCardMixin:UpdateTheme() ReskinButton(self); if self.id then local visibility = self:IsShown(); local id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, earnedByMe = DataProvider:GetAchievementInfo(self.id); FormatAchievementCard(self, id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuild, earnedByMe); self:SetShown(visibility); end end function NarciAchievement_SelectTheme(index) if not index or index > 3 or index < 1 then index = 1; end if index == themeID then return end; themeID = index; NarciAchievementOptions.Theme = index; if index == 3 then isDarkTheme = true; texturePrefix = "Interface\\AddOns\\Narcissus_Achievements\\Art\\Flat\\"; elseif index == 2 then isDarkTheme = false; texturePrefix = "Interface\\AddOns\\Narcissus_Achievements\\Art\\Classic\\"; else isDarkTheme = true; texturePrefix = "Interface\\AddOns\\Narcissus_Achievements\\Art\\DarkWood\\"; end --Statistics StatCardController:SetTheme(index); InspectionFrame.StatCard:UpdateTheme(); for i = 1, #AchievementCards do ReskinButton(AchievementCards[i]); end ReskinButton(AchievementCards[-1]); --DIY Cards if DIYContainer.cards then for i = 1, #DIYContainer.cards do ReskinButton(DIYContainer.cards[i]); DIYContainer.cards[i].isDarkTheme = isDarkTheme; end end DIYContainer:RefreshTheme(); DIYContainer.NewEntry.background:SetTexture(texturePrefix.."NewEntry"); if index == 1 then EditorContainer.notes:SetFontObject(NarciAchievementText); EditorContainer.notes:SetTextColor(0.68, 0.58, 0.51); elseif index == 2 then EditorContainer.notes:SetFontObject(NarciAchievementTextBlack); EditorContainer.notes:SetTextColor(0, 0, 0); else EditorContainer.notes:SetFontObject(NarciAchievementText); EditorContainer.notes:SetTextColor(0.5, 0.5, 0.5); end local inspectedAchievementID = AchievementCards[-1].id; if inspectedAchievementID then local id, name, points, completed, month, day, year, description, flags, icon, rewardText = DataProvider:GetAchievementInfo(inspectedAchievementID); FormatAchievementCardByIndex(-1, id, name, points, completed, month, day, year, description, flags, icon, rewardText); end if SummaryFrame:IsVisible() then UpdateSummaryFrame(); else if not DIYContainer:IsShown() then local categoryID = DataProvider.currentCategory; SelectCategory(categoryID); end end --Search Results: local ResultFrame = MainFrame.HeaderFrame.SearchBox.ClipFrame.ResultFrame; local resultButtonGap; if index == 3 then resultButtonGap = -1; else resultButtonGap = -2 end ResultFrame.background:SetTexture(texturePrefix.."SearchResultFrame"); for i = 1, #ResultFrame.buttons do ResultFrame.buttons[i].background:SetTexture(texturePrefix.."ResultButton"); ResultFrame.buttons[i].mask:SetTexture(texturePrefix.."ResultButtonMask"); if i ~= 1 then ResultFrame.buttons[i]:SetPoint("TOP", ResultFrame.buttons[i - 1], "BOTTOM", 0, resultButtonGap); end end --Border Skin local HeaderFrame = MainFrame.HeaderFrame; HeaderFrame.background:SetTexture(texturePrefix.."BoxHeaderBorder"); HeaderFrame.mask:SetTexture(texturePrefix.."BoxHeaderBorderMask"); MainFrame.background:SetTexture(texturePrefix.."BoxRight"); MainFrame.categoryBackground:SetTexture(texturePrefix.."BoxLeft"); AchievementContainer.OverlayFrame.top:SetTexture(texturePrefix.."BoxRight"); AchievementContainer.OverlayFrame.bottom:SetTexture(texturePrefix.."BoxRight"); AchievementContainer.scrollBar.Thumb:SetTexture(texturePrefix.."SliderThumb"); CategoryContainer.OverlayFrame.top:SetTexture(texturePrefix.."BoxLeft"); CategoryContainer.OverlayFrame.bottom:SetTexture(texturePrefix.."BoxLeft"); CategoryContainer.scrollBar.Thumb:SetTexture(texturePrefix.."SliderThumb"); DIYContainer.OverlayFrame.top:SetTexture(texturePrefix.."BoxRight"); DIYContainer.OverlayFrame.bottom:SetTexture(texturePrefix.."BoxRight"); DIYContainer.scrollBar.Thumb:SetTexture(texturePrefix.."SliderThumb"); EditorContainer.OverlayFrame.top:SetTexture(texturePrefix.."BoxLeft"); EditorContainer.OverlayFrame.bottom:SetTexture(texturePrefix.."BoxLeft"); EditorContainer.scrollBar.Thumb:SetTexture(texturePrefix.."SliderThumb"); --Scroll frame inner Shadow local showShadow = index == 1; AchievementContainer.OverlayFrame.topShadow:SetShown(showShadow); AchievementContainer.OverlayFrame.bottomShadow:SetShown(showShadow); DIYContainer.OverlayFrame.topShadow:SetShown(showShadow); DIYContainer.OverlayFrame.bottomShadow:SetShown(showShadow); CategoryContainer.OverlayFrame.topShadow:SetShown(showShadow); CategoryContainer.OverlayFrame.bottomShadow:SetShown(showShadow); EditorContainer.OverlayFrame.topShadow:SetShown(showShadow); EditorContainer.OverlayFrame.bottomShadow:SetShown(showShadow); --Category Buttons local cateButtons = {CategoryButtons.player.buttons, CategoryButtons.guild.buttons, CategoryButtons.stats.buttons}; for _, buttons in pairs(cateButtons) do for i = 1, #buttons do if buttons[i].isParentButton then buttons[i].background:SetTexture(texturePrefix.."CategoryButton"); else buttons[i].background:SetTexture(texturePrefix.."SubCategoryButton"); end buttons[i].fill:SetTexture(texturePrefix.."CategoryButtonBar"); buttons[i].fillEnd:SetTexture(texturePrefix.."CategoryButtonBar"); end end HeaderFrame.fill:SetTexture(texturePrefix.."CategoryButtonBar"); HeaderFrame.fillEnd:SetTexture(texturePrefix.."CategoryButtonBar"); --Header Reposition local offsetY = 0; if index == 2 then offsetY = -6; end local FilterButton = MainFrame.FilterButton; FilterButton:ClearAllPoints(); FilterButton:SetPoint("TOPRIGHT", MainFrame, "TOP", -2, -12 + offsetY); FilterButton.texture:SetTexture(texturePrefix.."DropDownButton"); local CloseButton = MainFrame.CloseButton; CloseButton:ClearAllPoints(); CloseButton:SetPoint("TOPRIGHT", MainFrame, "TOPRIGHT", -11, -11 + offsetY); CloseButton.texture:SetTexture(texturePrefix.."CloseButton"); if index == 2 then CloseButton:SetSize(39, 26); else CloseButton:SetSize(36, 26); end local SearchBox = HeaderFrame.SearchBox SearchBox:ClearAllPoints(); SearchBox:SetPoint("TOPRIGHT", HeaderFrame, "TOPRIGHT", -67, -5 + offsetY); local points = HeaderFrame.points; points:ClearAllPoints(); points:SetPoint("TOPRIGHT", HeaderFrame, "TOP", 137, -17 + offsetY); local reference = HeaderFrame.reference; --Summary All Points/Achv if index == 2 then reference:SetHeight(35); else reference:SetHeight(32); end SummaryButton:ClearAllPoints(); SummaryButton:SetPoint("TOPLEFT", MainFrame, "TOPLEFT", 32, -8 + offsetY); SummaryButton.texture:SetTexture(texturePrefix.."SummaryButton"); --Tab buttons for i = 1, #TabButtons do TabButtons[i]:SetButtonTexture(texturePrefix.."TabButton"); if index == 1 then TabButtons[i]:SetTextOffset(28); else TabButtons[i]:SetTextOffset(20); end end --AlertSystem --Floating Cards FloatingCard:SetTheme(index); end -------------------------------------------------------------------- local function UpdateTrackAchievements() local changedAchievementID = DataProvider:GetTrackedAchievements(); if not changedAchievementID then return end local currentCategory = DataProvider.currentCategory; local categoryID = DataProvider:GetAchievementCategory(changedAchievementID); local shouldUpdate; if categoryID == currentCategory then shouldUpdate = true; end local isTracked = DataProvider:IsTrackedAchievement(changedAchievementID); if shouldUpdate then local numAchievements = DataProvider.numAchievements or 0; local card; for i = 1, numAchievements do card = AchievementCards[i]; if card then if card.id == changedAchievementID then card.trackIcon:SetShown(isTracked); end else break end end end if (InspectionFrame:IsShown()) and (changedAchievementID == InspectionFrame.Card.id) then InspectionFrame.Card.trackIcon:SetShown(isTracked); end end ----------------------------------------------------------------------------- function NarciAchievementFrameMixin:Init() DataProvider:GetTrackedAchievements(); PinUtil:Load(); addon.LoadDIY(); self:RegisterEvent("TRACKED_ACHIEVEMENT_LIST_CHANGED"); BuildCategoryStructure(2); BuildCategoryStructure(1); BuildCategoryStructure(3); InitializeFrame(Narci_AchievementFrame); self:RegisterEvent("ACHIEVEMENT_EARNED"); self:RegisterEvent("CRITERIA_EARNED"); self.Init = nil; end function NarciAchievementFrameMixin:InspectCard(card, playAnimation) InspectCard(card, playAnimation); end local function OnAchivementEarned(achievementID) DataProvider:UpdateAchievementCache(achievementID); RefreshInspection(achievementID); local categoryID = DataProvider:GetAchievementCategory(achievementID); if categoryID then if DataProvider.achievementOrderCache[categoryID] then wipe(DataProvider.achievementOrderCache[categoryID]); end UpdateCategoryButtonProgressByCategoryID(categoryID); if categoryID == DataProvider.currentCategory then if MainFrame:IsShown() then SelectCategory(categoryID); else MainFrame.pendingCategoryID = categoryID; end return; end end if SummaryFrame:IsShown() then MainFrame.pendingUpdate = true; end end local EventListener = CreateFrame("Frame"); EventListener:RegisterEvent("ACHIEVEMENT_EARNED"); EventListener:RegisterEvent("TRACKED_ACHIEVEMENT_LIST_CHANGED"); EventListener:SetScript("OnEvent", function(self, event, ...) if event == "ACHIEVEMENT_EARNED" then local achievementID = ...; OnAchivementEarned(achievementID); if not self.pauseUpdate then self.pauseUpdate = true; After(0, function() UpdateHeaderFrame(isGuildView); self.pauseUpdate = nil; end); end elseif event == "TRACKED_ACHIEVEMENT_LIST_CHANGED" then UpdateTrackAchievements(); end end) addon.ReskinButton = ReskinButton; addon.FormatAchievementCard = FormatAchievementCard; local function IsDarkTheme() return isDarkTheme; end addon.IsDarkTheme = IsDarkTheme;