Nenue@6: --- ${PACKAGE_NAME} Nenue@6: -- @file-author@ Nenue@6: -- @project-revision@ @project-hash@ Nenue@6: -- @file-revision@ @file-hash@ Nenue@6: -- Created: 2/2/2016 12:09 AM Nenue@6: Nenue@6: local mod = Turok:NewModule("Toast", "AceTimer-3.0") Nenue@6: local _G = _G Nenue@6: local db Nenue@6: local T, tostring, type, max, tinsert, UIParent, loadstring = _G.Turok, tostring, type, max, table.insert, _G.UIParent, loadstring Nenue@6: --@debug@ Nenue@6: local DEBUG = true Nenue@6: --@end-debug@ Nenue@6: local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool Nenue@6: local print = function(...) Nenue@6: if not DEBUG then return end Nenue@6: if _G.Devian and _G.DevianDB.workspace ~= 1 then Nenue@6: _G.print('Toast', ...) Nenue@6: end Nenue@6: end Nenue@6: Nenue@6: -- GLOBALS:BossBanner_OnEvent, BossBanner Nenue@6: -- kill off the default BossBanner Nenue@6: local old_bb = BossBanner_OnEvent Nenue@6: BossBanner_OnEvent = function(self, event, ...) Nenue@6: T:Print(event, ...) Nenue@6: end Nenue@6: Nenue@6: T.defaults.toast = { Nenue@6: alert_hold = 2, Nenue@6: alert_fade = 1, Nenue@6: alert_flash = .6, Nenue@11: anchor = 'TOPLEFT', Nenue@6: parent = 'UIParent', Nenue@11: anchorTo = 'LEFT', Nenue@11: x =200, y= 0, Nenue@6: loot = { Nenue@6: background_color = {}, Nenue@6: } Nenue@6: } Nenue@6: Nenue@6: mod.events = { Nenue@6: "ACHIEVEMENT_EARNED", Nenue@6: "CRITERIA_EARNED", Nenue@6: "LFG_COMPLETION_REWARD", Nenue@6: "GUILD_CHALLENGE_COMPLETED", Nenue@6: "CHALLENGE_MODE_COMPLETED", Nenue@6: "LOOT_ITEM_ROLL_WON", Nenue@6: "SHOW_LOOT_TOAST", Nenue@6: "SHOW_LOOT_TOAST_UPGRADE", Nenue@6: "SHOW_PVP_FACTION_LOOT_TOAST", Nenue@6: "PET_BATTLE_CLOSE", Nenue@6: "STORE_PRODUCT_DELIVERED", Nenue@6: "GARRISON_BUILDING_ACTIVATABLE", Nenue@6: "GARRISON_MISSION_FINISHED", Nenue@6: "GARRISON_FOLLOWER_ADDED", Nenue@6: "GARRISON_RANDOM_MISSION_ADDED", Nenue@6: "BOSS_KILL", Nenue@6: "ENCOUNTER_LOOT_RECEIVED", Nenue@6: } Nenue@6: Nenue@6: mod.events_args = { Nenue@6: ['ACHIEVEMENT_EARNED'] = {12, false}, Nenue@6: ['ACHIEVEMENT_EARNED'] = {12, true}, Nenue@6: ['LFG_COMPLETED_REWARD'] = {}, Nenue@6: } Nenue@6: local y_factor = -1 Nenue@6: local x_factor = 0 Nenue@6: local tkAlerts = TkAlertContainer Nenue@6: local AlertFrame_FixPosition = function(alertFrame) Nenue@6: print(' pos:', alertFrame.order, cKey(alertFrame.x), cWord(alertFrame.y)) Nenue@6: alertFrame:SetPoint('TOPLEFT', tkAlerts, 'TOPLEFT', alertFrame.x, alertFrame.y) Nenue@6: end Nenue@6: Nenue@6: --- Goes through the alert container index and fixes the frame positions Nenue@6: -- used before and after an alert frame changes visibility Nenue@6: local AlertContainer_Update = function(self) Nenue@6: local anum = 1 Nenue@6: local offset = 0 Nenue@6: for i, alert in ipairs(tkAlerts.alerts) do Nenue@6: if alert:IsShown() then Nenue@6: alert.x = offset * x_factor Nenue@6: alert.y = offset * y_factor Nenue@6: alert.drawHeight = alert.ename:GetStringHeight()+ alert.desc:GetStringHeight() Nenue@6: alert.order = anum Nenue@6: anum = anum + 1 Nenue@6: offset = offset + alert.drawHeight Nenue@6: print(' draw height:', i, cText(alert.drawHeight)) Nenue@6: print(' position:', i, cPink(offset)) Nenue@6: AlertFrame_FixPosition(alert) Nenue@6: print('index', i, 'shifted from position', cNum(alert.order), 'to', cNum(anum), ' at draw point', alert.y) Nenue@6: print(' draw distance update ', cNum(0), 'x', cNum(offset)) Nenue@6: else Nenue@6: print('index', i, 'is not visible') Nenue@6: end Nenue@6: end Nenue@6: self:SetHeight(offset) Nenue@6: end Nenue@6: Nenue@6: --- config mode blocking command Nenue@6: local AlertFrame_Block = function(alertFrame) Nenue@6: -- only do stuff when configMode is on Nenue@6: if not mod.configMode then Nenue@6: return Nenue@6: end Nenue@6: end Nenue@6: Nenue@6: --- if not config mode, then fire at the end of fadeIn animation to queue fadeOut Nenue@6: local AlertFrame_Pin = function(alertFrame) Nenue@6: if not mod.configMode then Nenue@6: alertFrame.fadeOut.a1:SetStartDelay(db.alert_hold) Nenue@6: alertFrame.fadeOut:Play() Nenue@6: end Nenue@6: end Nenue@6: Nenue@6: --- if not config mode, then fire at the end of fadeOut to remove the frame from view and trigger positions update Nenue@6: local AlertFrame_Remove = function(alertFrame) Nenue@6: mod.num_events = mod.num_events - 1 Nenue@6: alertFrame:Hide() Nenue@6: print(mod.num_events) Nenue@6: AlertContainer_Update(tkAlerts) Nenue@6: end Nenue@6: Nenue@6: --- orders all visible frames to fadeOut Nenue@6: -- @param stagger forces the spacing of startDelay times for each frame Nenue@6: local AlertContainer_Clear = function(self, stagger) Nenue@6: stagger = stagger or 0.1 Nenue@6: for i = #self.alerts, 1, -1 do Nenue@6: print('clear check', i) Nenue@6: local alert = self.alerts[i] Nenue@6: if alert:IsShown() and not alert.fadeOut:IsPlaying() then Nenue@6: alert.fadeOut.a1:SetStartDelay((#self.alerts-i)* stagger) Nenue@6: alert.fadeOut:Play() Nenue@6: end Nenue@6: end Nenue@6: end Nenue@6: local AlertContainer_Unlock = function() Nenue@6: end Nenue@6: Nenue@6: --- Displays a new alert Nenue@6: -- @param name text naming the class of event that occurred Nenue@6: -- @param text alert subtext describing basic info about the event Nenue@6: -- @order order (optional) sets display slot of the alert Nenue@6: local function AlertContainer_ShowAlert(self, name, text, order) Nenue@6: local db = TurokData.toast Nenue@6: mod.num_events = mod.num_events + 1 Nenue@6: Nenue@6: local alertFrame Nenue@6: if not order then Nenue@6: local i = 1 Nenue@6: while i <= #self.alerts and not alertFrame do Nenue@6: if not self.alerts[i]:IsShown() then Nenue@6: alertFrame = self.alerts[i] Nenue@6: print('re-using alert frame #', i) Nenue@6: end Nenue@6: i = i +1 Nenue@6: end Nenue@6: else Nenue@6: alertFrame = self.alerts[order] Nenue@6: end Nenue@6: Nenue@6: if not alertFrame then Nenue@6: alertFrame = CreateFrame('Frame', 'TkAlertPanel'..(order or #self.alerts+1), self, 'TkAlertFrame') Nenue@6: self.alerts[#self.alerts+1] = alertFrame Nenue@6: print('creating new alert frame', #self.alerts) Nenue@6: Nenue@6: alertFrame.Pin = AlertFrame_Pin Nenue@6: alertFrame.Remove = AlertFrame_Remove Nenue@6: end Nenue@6: Nenue@6: alertFrame.ename:SetText(name) Nenue@6: local height1 = alertFrame.ename:GetStringHeight() Nenue@6: alertFrame.desc:SetText(text) Nenue@6: local height2 = height1+ alertFrame.desc:GetStringHeight() Nenue@6: alertFrame.desc:SetPoint('TOPLEFT', alertFrame, 'TOPLEFT', 0, -height1) Nenue@6: Nenue@6: alertFrame.order = order or mod.num_events Nenue@6: alertFrame:SetSize(300, height2) Nenue@6: alertFrame:Show() Nenue@6: --alertFrame.flashIn.a1:SetDuration(db.alert_flash/2) Nenue@6: --alertFrame.flashIn.a2:SetDuration(db.alert_flash/2) Nenue@6: alertFrame.flashIn:Play() Nenue@6: if not mod.configMode then Nenue@6: AlertContainer_Update(tkAlerts) Nenue@6: end Nenue@6: end Nenue@6: Nenue@6: Nenue@6: --- updates the completed missions index and returns info on the mission ID if passed Nenue@6: local completedMissions Nenue@6: local Garrison_UpdateCompleteMissions = function(missionID) Nenue@6: completedMissions = C_Garrison.GetCompleteMissions() Nenue@6: --- slide entries around for reference Nenue@6: for i, set in ipairs(completedMissions) do Nenue@6: if i ~= set.missionID then Nenue@6: completedMissions[set.missionID] = set Nenue@6: completedMissions[i] = nil Nenue@6: end Nenue@6: end Nenue@6: Nenue@6: if missionID and completedMissions[missionID] then Nenue@6: local m = completedMissions[missionID] Nenue@6: return m.name, m.location, m.locPrefix, m.isRare, m.followers, m.rewards, m.state Nenue@6: else Nenue@6: return false Nenue@6: end Nenue@6: end Nenue@6: --- container events handler Nenue@6: local AlertContainer_OnEvent = function (self, event, ...) Nenue@6: print(event, ...) Nenue@6: tkAlerts:Show() Nenue@6: if event == 'SHOW_LOOT_TOAST' then Nenue@6: local typeIdentifier, itemLink, quantity, specID, sex, isPersonal, lootSource = ...; Nenue@6: if typeIdentifier == "currency" then Nenue@6: AlertContainer_ShowAlert(self, itemLink, 'x'..quantity) Nenue@6: elseif typeIdentifier == "item" then Nenue@6: local _, _, _, ilvl, _, _, _, _, equipSlot = GetItemInfo(itemLink) Nenue@6: AlertContainer_ShowAlert(self, itemLink, tostring(ilvl)..' '..tostring(_G[equipSlot])) Nenue@6: end Nenue@6: elseif event == 'GARRISON_MISSION_FINISHED' then Nenue@6: local missionID = ... Nenue@6: local name, location, locPrefix, isRare, followers, rewards = Garrison_UpdateCompleteMissions(missionID) Nenue@6: local mission_info = { Nenue@6: (isRare and ('|cFF44BBFF') or ('|cFFFFFF00')..name.. '|r'), Nenue@6: } Nenue@6: Nenue@6: if followers then Nenue@6: for i, guid in ipairs(followers) do Nenue@6: print(C_Garrison.GetFollowerInfo(guid)) Nenue@6: end Nenue@6: end Nenue@6: Nenue@6: --'|T:'..icon..':0 Nenue@6: Nenue@6: Nenue@6: AlertContainer_ShowAlert(self, 'Mission Complete') Nenue@6: elseif event == 'GARRISON_BUILDING_ACTIVATABLE' then Nenue@6: local missionID = ... Nenue@6: elseif event == 'ACHIEVEMENT_EARNED' then Nenue@6: elseif event == 'LFG_COMPLETION_REWARD' then Nenue@6: local name, typeID, subtypeID, textureFilename, moneyBase, moneyVar, experienceBase, experienceVar, numStrangers, numRewards = GetLFGCompletionReward() Nenue@6: local _, _, _, _, hasBonusStep, isBonusStepComplete = C_Scenario.GetInfo(); Nenue@6: Nenue@6: end Nenue@6: end Nenue@6: Nenue@6: local AlertContainer_Test = function() Nenue@6: if not mod.configMode then Nenue@6: print('starting test mode') Nenue@6: tkAlerts:Show() Nenue@6: mod.configMode = true Nenue@6: Nenue@6: tkAlerts.configBG:Show() Nenue@6: tkAlerts.configBG:SetTexture(0,0.5,0,0.5) Nenue@6: tkAlerts:RegisterForDrag('LeftButton') Nenue@6: tkAlerts:EnableMouse(true) Nenue@6: Nenue@6: for i, frame in ipairs(tkAlerts.tools) do Nenue@6: frame:Show() Nenue@6: end Nenue@6: -- test fillers Nenue@6: local _,_, offset, range = GetSpellTabInfo(2) Nenue@6: print(offset, range) Nenue@6: Nenue@6: for i, event in ipairs(mod.events) do Nenue@6: --print(i, event) Nenue@6: local _, id = GetSpellBookItemInfo(math.random(offset, offset+range), 'spell') Nenue@6: print(id) Nenue@6: Nenue@6: local name = GetSpellLink(id) Nenue@6: local text = GetSpellDescription(id) Nenue@6: if not tkAlerts.alerts[i] then Nenue@6: print('creating alert frame #', i) Nenue@6: AlertContainer_ShowAlert(tkAlerts, name, text, i) Nenue@6: else Nenue@6: print('updating alert frame #', i) Nenue@6: local alert = tkAlerts.alerts[i] Nenue@6: alert:Show() Nenue@6: alert.order = i Nenue@6: alert.ename:SetText(name) Nenue@6: alert.desc:SetText(text) Nenue@6: if alert.fadeOut:IsPlaying() then Nenue@6: alert.fadeOut:Stop() Nenue@6: alert.backdrop:SetAlpha(0.5) Nenue@6: end Nenue@6: Nenue@6: alert.flashIn:Play() Nenue@6: end Nenue@6: end Nenue@6: AlertContainer_Update(tkAlerts) Nenue@6: else Nenue@6: tkAlerts:EnableMouse(false) Nenue@6: tkAlerts.configBG:Hide() Nenue@6: mod.configMode = nil Nenue@6: for i, frame in ipairs(tkAlerts.tools) do Nenue@6: frame:Hide() Nenue@6: end Nenue@6: for i, alert in ipairs(tkAlerts.alerts) do Nenue@6: for j, frame in ipairs(alert.tools) do Nenue@6: frame:Hide() Nenue@6: end Nenue@6: alert.fadeOut.a1:SetStartDelay(i*0.2) Nenue@6: alert.fadeOut:Play() Nenue@6: end Nenue@6: Nenue@6: end Nenue@6: end Nenue@6: Nenue@11: mod.GetToastsAnchor = function(self) Nenue@6: Nenue@6: local cX, cY = (GetScreenWidth() / 2), (GetScreenHeight() / 2) Nenue@6: local min_skewness = cX Nenue@6: local max_centrality, center_frame Nenue@6: for n, f in ipairs({UIParent:GetChildren()}) do Nenue@6: if type(f) == 'table' and f.GetObjectType and f.GetName then Nenue@6: if f.IsForbidden and f:IsForbidden() then Nenue@6: print(n, 'is a forbidden object') Nenue@6: else Nenue@6: local name = f:GetName() Nenue@6: Nenue@6: if f:IsVisible() and f:IsMouseEnabled() then Nenue@6: print(name and name or tostring(f):sub(8), 'is a frame!') Nenue@6: local x = f:GetCenter() Nenue@6: local y = f:GetTop() -- Y need center top position Nenue@6: if (x and y) and (y <= cY) then Nenue@6: x = math.abs(x - cX) -- X works in either direction Nenue@6: Nenue@6: -- distance of current - distant of record / max to get ratio of 1 where direct center results in 1 Nenue@6: local skewness_factor = (cX - x) / cX Nenue@6: local vertness_factor = y / cY Nenue@6: local centrality = skewness_factor * vertness_factor Nenue@6: print('result: (', floor(x), floor(y), ') = ', skewness_factor, 'skew,', vertness_factor, 'vertness.\nTotal score:', cNum(centrality)) Nenue@6: if (not max_centrality) or max_centrality < centrality then Nenue@6: center_frame = f Nenue@6: max_centrality = centrality Nenue@6: print(cWord(name and name or tostring(f):sub(8)),cPink(' is the new record!')) Nenue@6: end Nenue@6: Nenue@6: end Nenue@6: end Nenue@6: end Nenue@6: end Nenue@6: end Nenue@11: return center_frame Nenue@11: end Nenue@11: Nenue@11: mod.OnEnable = function() Nenue@11: db = TurokData.toast Nenue@11: Nenue@11: --- find the closest frame to the center bottom and anchor to that Nenue@11: Nenue@6: Nenue@6: mod.num_events = 0 Nenue@6: tkAlerts.alerts = {} Nenue@6: for i, event in ipairs(mod.events) do Nenue@6: tkAlerts:RegisterEvent(event) Nenue@6: end Nenue@6: Nenue@6: tkAlerts.Clear = AlertContainer_Clear Nenue@6: tkAlerts.Unlock = AlertContainer_Unlock Nenue@6: tkAlerts.Close = AlertContainer_Test Nenue@6: Nenue@6: tkAlerts:ClearAllPoints() Nenue@6: tkAlerts:SetPoint(db.anchor, db.parent, db.anchorTo, db.x, db.y) Nenue@6: tkAlerts.x = db.x Nenue@6: tkAlerts.y = db.y Nenue@6: tkAlerts.parent = db.parent Nenue@6: tkAlerts.anchor = db.anchor Nenue@6: tkAlerts.anchorTo = db.anchorTo Nenue@6: tkAlerts:EnableMouse(false) Nenue@6: Nenue@6: tkAlerts:SetScript('OnEvent', AlertContainer_OnEvent) Nenue@6: T:RegisterChatCommand("alert", AlertContainer_Test) Nenue@6: T:RegisterChatCommand("atest", function() Nenue@6: AlertContainer_OnEvent(tkAlerts, 'GARRISON_MISSION_FINISHED', 327) Nenue@6: end) Nenue@6: end