view Turok/Modules/Utilities/Toast.lua @ 11:0b1a2f3dbfc4 tip

aura duration override triggering activation twice when aura data still exists briefly after duration end
author Nenue
date Mon, 22 Feb 2016 03:11:54 -0500
parents a9b8b0866ece
children
line wrap: on
line source
--- ${PACKAGE_NAME}
-- @file-author@
-- @project-revision@ @project-hash@
-- @file-revision@ @file-hash@
-- Created: 2/2/2016 12:09 AM

local mod = Turok:NewModule("Toast", "AceTimer-3.0")
local _G = _G
local db
local T, tostring, type, max, tinsert, UIParent, loadstring = _G.Turok, tostring, type, max, table.insert, _G.UIParent, loadstring
--@debug@
local DEBUG = true
--@end-debug@
local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
local print = function(...)
  if not DEBUG then return end
  if _G.Devian and _G.DevianDB.workspace ~= 1 then
    _G.print('Toast', ...)
  end
end

-- GLOBALS:BossBanner_OnEvent, BossBanner
-- kill off the default BossBanner
local old_bb = BossBanner_OnEvent
BossBanner_OnEvent = function(self, event, ...)
  T:Print(event, ...)
end

T.defaults.toast = {
  alert_hold = 2,
  alert_fade = 1,
  alert_flash = .6,
  anchor = 'TOPLEFT',
  parent = 'UIParent',
  anchorTo = 'LEFT',
  x =200, y= 0,
  loot = {
    background_color = {},
  }
}

mod.events = {
  "ACHIEVEMENT_EARNED",
  "CRITERIA_EARNED",
  "LFG_COMPLETION_REWARD",
  "GUILD_CHALLENGE_COMPLETED",
  "CHALLENGE_MODE_COMPLETED",
  "LOOT_ITEM_ROLL_WON",
  "SHOW_LOOT_TOAST",
  "SHOW_LOOT_TOAST_UPGRADE",
  "SHOW_PVP_FACTION_LOOT_TOAST",
  "PET_BATTLE_CLOSE",
  "STORE_PRODUCT_DELIVERED",
  "GARRISON_BUILDING_ACTIVATABLE",
  "GARRISON_MISSION_FINISHED",
  "GARRISON_FOLLOWER_ADDED",
  "GARRISON_RANDOM_MISSION_ADDED",
  "BOSS_KILL",
  "ENCOUNTER_LOOT_RECEIVED",
}

mod.events_args = {
  ['ACHIEVEMENT_EARNED'] = {12, false},
  ['ACHIEVEMENT_EARNED'] = {12, true},
  ['LFG_COMPLETED_REWARD'] = {},
}
local y_factor = -1
local x_factor = 0
local tkAlerts = TkAlertContainer
local AlertFrame_FixPosition = function(alertFrame)
  print('   pos:', alertFrame.order, cKey(alertFrame.x), cWord(alertFrame.y))
  alertFrame:SetPoint('TOPLEFT', tkAlerts, 'TOPLEFT', alertFrame.x, alertFrame.y)
end

--- Goes through the alert container index and fixes the frame positions
-- used before and after an alert frame changes visibility
local AlertContainer_Update = function(self)
  local anum = 1
  local offset = 0
  for i, alert in ipairs(tkAlerts.alerts) do
    if alert:IsShown() then
      alert.x = offset * x_factor
      alert.y = offset * y_factor
      alert.drawHeight = alert.ename:GetStringHeight()+ alert.desc:GetStringHeight()
      alert.order = anum
      anum = anum + 1
      offset = offset + alert.drawHeight
      print('   draw height:', i, cText(alert.drawHeight))
      print('      position:', i, cPink(offset))
      AlertFrame_FixPosition(alert)
      print('index', i, 'shifted from position', cNum(alert.order), 'to', cNum(anum), ' at draw point', alert.y)
      print('    draw distance update ', cNum(0), 'x', cNum(offset))
    else
      print('index', i, 'is not visible')
    end
  end
  self:SetHeight(offset)
end

--- config mode blocking command
local AlertFrame_Block = function(alertFrame)
  -- only do stuff when configMode is on
  if not mod.configMode then
    return
  end
end

--- if not config mode, then fire at the end of fadeIn animation to queue fadeOut
local AlertFrame_Pin = function(alertFrame)
  if not mod.configMode then
    alertFrame.fadeOut.a1:SetStartDelay(db.alert_hold)
    alertFrame.fadeOut:Play()
  end
end

--- if not config mode, then fire at the end of fadeOut to remove the frame from view and trigger positions update
local AlertFrame_Remove = function(alertFrame)
  mod.num_events = mod.num_events - 1
  alertFrame:Hide()
  print(mod.num_events)
  AlertContainer_Update(tkAlerts)
end

--- orders all visible frames to fadeOut
-- @param stagger forces the spacing of startDelay times for each frame
local AlertContainer_Clear = function(self, stagger)
  stagger = stagger or 0.1
  for i = #self.alerts, 1, -1 do
    print('clear check', i)
    local alert = self.alerts[i]
    if alert:IsShown() and not alert.fadeOut:IsPlaying() then
      alert.fadeOut.a1:SetStartDelay((#self.alerts-i)* stagger)
      alert.fadeOut:Play()
    end
  end
end
local AlertContainer_Unlock = function()
end

--- Displays a new alert
-- @param name text naming the class of event that occurred
-- @param text alert subtext describing basic info about the event
-- @order order (optional) sets display slot of the alert
local function AlertContainer_ShowAlert(self, name, text, order)
  local db = TurokData.toast
  mod.num_events = mod.num_events + 1

  local alertFrame
  if not order then
  local i = 1
    while i <= #self.alerts and not alertFrame do
      if not self.alerts[i]:IsShown() then
        alertFrame = self.alerts[i]
        print('re-using alert frame #', i)
      end
      i = i +1
    end
  else
    alertFrame = self.alerts[order]
  end

  if not alertFrame then
    alertFrame = CreateFrame('Frame', 'TkAlertPanel'..(order or #self.alerts+1), self, 'TkAlertFrame')
    self.alerts[#self.alerts+1] = alertFrame
    print('creating new alert frame', #self.alerts)

    alertFrame.Pin = AlertFrame_Pin
    alertFrame.Remove = AlertFrame_Remove
  end

  alertFrame.ename:SetText(name)
  local height1 = alertFrame.ename:GetStringHeight()
  alertFrame.desc:SetText(text)
  local height2 = height1+ alertFrame.desc:GetStringHeight()
  alertFrame.desc:SetPoint('TOPLEFT', alertFrame, 'TOPLEFT', 0, -height1)

  alertFrame.order = order or mod.num_events
  alertFrame:SetSize(300, height2)
  alertFrame:Show()
  --alertFrame.flashIn.a1:SetDuration(db.alert_flash/2)
  --alertFrame.flashIn.a2:SetDuration(db.alert_flash/2)
  alertFrame.flashIn:Play()
  if not mod.configMode then
    AlertContainer_Update(tkAlerts)
  end
end


--- updates the completed missions index and returns info on the mission ID if passed
local completedMissions
local Garrison_UpdateCompleteMissions = function(missionID)
  completedMissions = C_Garrison.GetCompleteMissions()
  --- slide entries around for reference
  for i, set in ipairs(completedMissions) do
    if i ~= set.missionID then
      completedMissions[set.missionID] = set
      completedMissions[i] = nil
    end
  end

  if missionID and completedMissions[missionID] then
    local m = completedMissions[missionID]
    return m.name, m.location, m.locPrefix, m.isRare, m.followers, m.rewards, m.state
  else
    return false
  end
end
--- container events handler
local AlertContainer_OnEvent = function (self, event, ...)
  print(event, ...)
  tkAlerts:Show()
  if event == 'SHOW_LOOT_TOAST' then
    local typeIdentifier, itemLink, quantity, specID, sex, isPersonal, lootSource = ...;
    if typeIdentifier == "currency" then
      AlertContainer_ShowAlert(self, itemLink, 'x'..quantity)
    elseif typeIdentifier == "item" then
      local _, _, _, ilvl, _, _, _, _, equipSlot = GetItemInfo(itemLink)
      AlertContainer_ShowAlert(self, itemLink, tostring(ilvl)..' '..tostring(_G[equipSlot]))
    end
  elseif event == 'GARRISON_MISSION_FINISHED' then
    local missionID = ...
    local name, location, locPrefix, isRare, followers, rewards = Garrison_UpdateCompleteMissions(missionID)
    local mission_info = {
      (isRare and ('|cFF44BBFF') or ('|cFFFFFF00')..name.. '|r'),
    }

    if followers then
      for i, guid in ipairs(followers) do
        print(C_Garrison.GetFollowerInfo(guid))
      end
    end

      --'|T:'..icon..':0


    AlertContainer_ShowAlert(self, 'Mission Complete')
  elseif event == 'GARRISON_BUILDING_ACTIVATABLE' then
    local missionID = ...
  elseif event == 'ACHIEVEMENT_EARNED' then
  elseif event == 'LFG_COMPLETION_REWARD' then
    local name, typeID, subtypeID, textureFilename, moneyBase, moneyVar, experienceBase, experienceVar, numStrangers, numRewards = GetLFGCompletionReward()
    local _, _, _, _, hasBonusStep, isBonusStepComplete = C_Scenario.GetInfo();

  end
end

local AlertContainer_Test = function()
  if not mod.configMode then
    print('starting test mode')
    tkAlerts:Show()
    mod.configMode = true

    tkAlerts.configBG:Show()
    tkAlerts.configBG:SetTexture(0,0.5,0,0.5)
    tkAlerts:RegisterForDrag('LeftButton')
    tkAlerts:EnableMouse(true)

    for i, frame in ipairs(tkAlerts.tools) do
      frame:Show()
    end
    -- test fillers
    local _,_, offset, range = GetSpellTabInfo(2)
    print(offset, range)

    for i, event in ipairs(mod.events) do
      --print(i, event)
      local _, id = GetSpellBookItemInfo(math.random(offset, offset+range), 'spell')
      print(id)

      local name = GetSpellLink(id)
      local text = GetSpellDescription(id)
      if not tkAlerts.alerts[i] then
        print('creating alert frame #', i)
        AlertContainer_ShowAlert(tkAlerts, name, text, i)
      else
        print('updating alert frame #', i)
        local alert = tkAlerts.alerts[i]
        alert:Show()
        alert.order = i
        alert.ename:SetText(name)
        alert.desc:SetText(text)
        if alert.fadeOut:IsPlaying() then
          alert.fadeOut:Stop()
          alert.backdrop:SetAlpha(0.5)
        end

        alert.flashIn:Play()
      end
    end
    AlertContainer_Update(tkAlerts)
  else
    tkAlerts:EnableMouse(false)
    tkAlerts.configBG:Hide()
    mod.configMode = nil
    for i, frame in ipairs(tkAlerts.tools) do
      frame:Hide()
    end
    for i, alert in ipairs(tkAlerts.alerts) do
      for j, frame in ipairs(alert.tools) do
        frame:Hide()
      end
      alert.fadeOut.a1:SetStartDelay(i*0.2)
      alert.fadeOut:Play()
    end

  end
end

mod.GetToastsAnchor = function(self)

  local cX, cY = (GetScreenWidth() / 2), (GetScreenHeight() / 2)
  local min_skewness = cX
  local max_centrality, center_frame
  for n, f in ipairs({UIParent:GetChildren()}) do
    if type(f) == 'table' and f.GetObjectType and f.GetName then
      if f.IsForbidden and f:IsForbidden() then
        print(n, 'is a forbidden object')
      else
        local name = f:GetName()

        if f:IsVisible() and f:IsMouseEnabled() then
          print(name and name or tostring(f):sub(8), 'is a frame!')
          local x = f:GetCenter()
          local y = f:GetTop()    -- Y need center top position
          if (x and y) and (y <= cY) then
            x = math.abs(x - cX) -- X works in either direction

            -- distance of current - distant of record / max to get ratio of 1 where direct center results in 1
            local skewness_factor = (cX - x) / cX
            local vertness_factor = y / cY
            local centrality = skewness_factor * vertness_factor
            print('result: (', floor(x), floor(y), ') = ', skewness_factor, 'skew,', vertness_factor, 'vertness.\nTotal score:', cNum(centrality))
            if (not max_centrality) or max_centrality < centrality then
              center_frame = f
              max_centrality = centrality
              print(cWord(name and name or tostring(f):sub(8)),cPink(' is the new record!'))
            end

          end
        end
      end
    end
  end
  return center_frame
end

mod.OnEnable = function()
  db = TurokData.toast

  --- find the closest frame to the center bottom and anchor to that


  mod.num_events = 0
  tkAlerts.alerts = {}
  for i, event in ipairs(mod.events) do
    tkAlerts:RegisterEvent(event)
  end

  tkAlerts.Clear = AlertContainer_Clear
  tkAlerts.Unlock = AlertContainer_Unlock
  tkAlerts.Close = AlertContainer_Test

  tkAlerts:ClearAllPoints()
  tkAlerts:SetPoint(db.anchor, db.parent, db.anchorTo, db.x, db.y)
  tkAlerts.x = db.x
  tkAlerts.y = db.y
  tkAlerts.parent = db.parent
  tkAlerts.anchor = db.anchor
  tkAlerts.anchorTo = db.anchorTo
  tkAlerts:EnableMouse(false)

  tkAlerts:SetScript('OnEvent', AlertContainer_OnEvent)
  T:RegisterChatCommand("alert", AlertContainer_Test)
  T:RegisterChatCommand("atest", function()
    AlertContainer_OnEvent(tkAlerts, 'GARRISON_MISSION_FINISHED', 327)
  end)
end