view ClassPlan.lua @ 28:3f4368c4a49b

Added tag r27-beta for changeset 4a7e89bffbcb
author Nenue
date Thu, 27 Oct 2016 06:19:05 -0400
parents 4a7e89bffbcb
children e8679ecb48d8
line wrap: on
line source
local wipe, tinsert, sort = table.wipe, tinsert, table.sort
local pairs, ipairs = pairs, ipairs
local floor, mod, time = floor, mod, time
local GetTime = GetTime
local GI_currentTime = time()

local BOUND_FRAMES = {}
local blockTemplate = {
  point = 'TOPLEFT',
  relativePoint ='TOPLEFT',
}

SLASH_CLASSPLAN1 = "/classplan"
SLASH_CLASSPLAN2 = "/cp"
SlashCmdList.CLASSPLAN = function(args)
  ClassOrderPlan:Toggle()
end

ClassOrderPlanCore = {
  events = {},
  freeBlocks = {},
  blocks = {},
  sortedItems = {},
  timers = {},
  shipments = {},
  playerFirst = false,
  prototypes = {},
  Queued = {}
}

ClassPlanMissionMixin = {
  templateName = 'ClassPlanMissionEntry',
  events = {
    'GARRISON_MISSION_LIST_UPDATE',
    'GARRISON_MISSION_STARTED',
    'GARRISON_MISSION_FINISHED',
    'GARRISON_LANDINGPAGE_SHIPMENTS'},}
ClassPlanShipmentMixin = {
  templateName = 'ClassPlanShipmentEntry',
  parent = false,
  point = 'TOPRIGHT',
  relativePoint ='TOPRIGHT',
  events = {
    'GARRISON_LANDINGPAGE_SHIPMENTS',
    'GARRISON_TALENT_UPDATE',
    "GARRISON_TALENT_COMPLETE",
    "GARRISON_SHIPMENT_RECEIVED",
    'GARRISON_FOLLOWER_LIST_UPDATE',
    'GARRISON_SHOW_LANDING_PAGE'},
}
setmetatable(ClassPlanShipmentMixin, {__index = ClassPlanMissionMixin})
local core, MissionsHandler, ShipmentsHandler = ClassOrderPlanCore, ClassPlanMissionMixin, ClassPlanShipmentMixin
local print = DEVIAN_WORKSPACE and function(...) print('ClassPlan', ...) end or nop

local GetTimeLeftString = function(timeLeft)
  local days = floor(timeLeft/(24*3600))
  local hours = floor(mod(timeLeft, (24*3600)) / 3600)
  local minutes = floor(mod(timeLeft, 3600) / 60)
  local seconds = mod(timeLeft, 60)
  if days >= 1 then
    return (days .. 'd' .. ' ') .. ((hours > 0) and (hours .. 'h') or '')
  else
    return ((hours > 0) and (hours .. 'h') or '') .. ((minutes > 0) and (' ' ..minutes .. ' min') or '')
  end
end



MissionsHandler.GetPlayerData = function(self)
  if not self.profile then
    return
  end
  local items = C_Garrison.GetLandingPageItems(LE_GARRISON_TYPE_7_0)
  wipe(self.profile.missions)
  for index, data in ipairs(items) do
    print(' -',data.name, '|cFF00FF00'.. data.timeLeft .. '|r', date("%A %I:%m %p", data.missionEndTime))
    tinsert(self.profile.missions, data)
  end
  ClassOrderPlan.isStale = true
end

MissionsHandler.OnGetItem = function(data)
  if data.missionEndTime < GI_currentTime then
    data.isComplete = true
  end
end

MissionsHandler.FreeBlock = function(self, block)
end

MissionsHandler.SortHandler = function (a,b)
  local result = false
  --if not a or not b then
  --  return true
  --else
    --if (a.isMine ~= b.isMine) then
    --  result = a.isMine
    --else
      --if (not b.missionEndTime) or (not a.missionEndTime) then
      --  print('missing article', b.missionEndTime, a.missionEndTime)
      --end
      return ( b.missionEndTime > a.missionEndTime)
    --end
  --end
end

ShipmentsHandler.OnGetItem = function(data)
  if data.shipmentsTotal then
    local timeLeft = data.creationTime + data.duration - GI_currentTime
    if (timeLeft <= 0) and (data.shipmentsReady < data.shipmentsTotal) then
      local numOrders = min(-1*floor(timeLeft/data.duration), (data.shipmentsTotal - data.shipmentsReady))

      if not data.originalCreationTime then
        data.originalCreationTime = data.creationTime
        data.originalShipmentsReady = data.shipmentsReady
      end

      data.creationTime = data.creationTime + numOrders*data.duration
      data.shipmentsReady = data.shipmentsReady + numOrders
      print(data.profileKey, 'shipment "'.. data.name..'" reconciling', numOrders, 'lapsed orders. -->', data.creationTime, data.shipmentsReady)
    end
  end
end

ShipmentsHandler.SortHandler = function(a, b)
  if b.isComplete ~= a.isComplete then
    return a.isComplete and true or false
  elseif a.shipmentsReady or b.shipmentsReady then
    return (a.shipmentsReady or 0) > (b.shipmentsReady or 0)
  else
    return (a.creationTime) < (b.creationTime)
  end
end

local ShipmentsInfo = {}
local AddShipmentInfo = function(shipmentType, name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID, followerID)
  -- early login queries may return empty tables, causing the sorter to compare nil
  if not creationTime then
    return
  end
  --print(shipmentType, name, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString)
  tinsert(ShipmentsInfo,
    {
      shipmentType = shipmentType,
      name = name,
      icon = texture,
      shipmentCapacity = shipmentCapacity,
      shipmentsReady = shipmentsReady,
      shipmentsTotal = shipmentsTotal,
      creationTime = creationTime,
      duration = duration,
      timeleftString = timeleftString,
      itemName = itemName,
      itemIcon = itemIcon,
      itemQuality = itemQuality,
      itemID = itemID,
      followerID = followerID,
    })
end
ShipmentsHandler.GetPlayerData = function (self)
  if not self.profile then
    return
  end
  wipe(ShipmentsInfo)

  local garrisonType = LE_GARRISON_TYPE_7_0
  local buildings = C_Garrison.GetBuildings(garrisonType);
  local shipmentIndex = 0
  --print('Buildings:')
  for i = 1, #buildings do
    local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID = C_Garrison.GetLandingPageShipmentInfo(buildingID);
    AddShipmentInfo('Building', name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID)
  end

  --print('Follower:')
  local followerShipments = C_Garrison.GetFollowerShipments(garrisonType);
  for i = 1, #followerShipments do
    local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, _, _, _, _, followerID = C_Garrison.GetLandingPageShipmentInfoByContainerID(followerShipments[i]);
    AddShipmentInfo('Follower', name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, nil, nil, nil, nil, followerID)
  end

  --print('Loose:')
  local looseShipments = C_Garrison.GetLooseShipments(garrisonType)
  for i = 1, #looseShipments do
    local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString = C_Garrison.GetLandingPageShipmentInfoByContainerID(looseShipments[i]);
    AddShipmentInfo('Misc', name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString)
  end

  local talentTrees = C_Garrison.GetTalentTrees(garrisonType, select(3, UnitClass("player")));
  -- this is a talent that has completed, but has not been seen in the talent UI yet.
  local completeTalentID = C_Garrison.GetCompleteTalent(garrisonType);
  --print('Talents:')
  if (talentTrees) then
    for treeIndex, tree in ipairs(talentTrees) do
      for talentIndex, talent in ipairs(tree) do
        local showTalent = false;
        if (talent.isBeingResearched) or (talent.id == completeTalentID) then
          AddShipmentInfo('Talent', talent.name, talent.icon, 1, (talent.isBeingResearched and 0 or 1), 1, talent.researchStartTime, talent.researchDuration, talent.timeleftString)
        end
      end
    end
  end

  self.profile.shipments = self.profile.shipments or {}
  wipe(self.profile.shipments)
  for index, data in ipairs(ShipmentsInfo) do
    --DEFAULT_CHAT_FRAME:AddMessage(data.shipmentType ..' '.. tostring(data.name) ..' '.. tostring(data.creationTime) ..' '.. tostring(data.duration))
    tinsert(self.profile.shipments, data)
  end
  ClassOrderPlan.isStale = true
end

function core:OnLoad ()
  self:RegisterEvent('PLAYER_LOGIN')
  self:RegisterEvent('ADDON_LOADED')
  self:RegisterEvent('PLAYER_REGEN_ENABLED')
  self:RegisterEvent('PLAYER_REGEN_DISABLED')
  -- Blizzard_GarrisonUI already fires a shipment data request for GARRISON_SHIPMENT_RECEIVED; this is unlikely to
  self:AddHandler('missions', MissionsHandler)
  self:AddHandler('shipments', ShipmentsHandler)
  self:Reanchor()
  C_Garrison.RequestLandingPageShipmentInfo();
end

local parentFrames = {'VeneerWorldState', 'OrderHallCommandBar'}
function core:Reanchor()
  self:ClearAllPoints()
  self.anchorParent = UIParent
  local anchorTo = 'TOP'
  for i, name in ipairs(parentFrames) do
    local frame = _G[name]
    if frame then
      if not BOUND_FRAMES[frame] then
        BOUND_FRAMES[frame] = {visible = (frame:IsVisible() and frame:IsShown())}
        hooksecurefunc(frame, 'Show', function()
          BOUND_FRAMES[frame].visible = true
          print(frame:GetName(), 'Show', 'reanchor trigger')
          self:Reanchor()
        end)
        hooksecurefunc(frame, 'Hide', function()
          BOUND_FRAMES[frame].visible = nil
          print(frame:GetName(), 'Hide', 'reanchor trigger')
          self:Reanchor()
        end)
      end
      print('f:', frame:GetName(), BOUND_FRAMES[frame].visible)
      if BOUND_FRAMES[frame].visible then
        self.anchorParent = frame
        anchorTo = 'BOTTOM'
        break
      end
    end
  end
  print('|cFFFF8800Using ' .. tostring(self.anchorParent:GetName()) .. '-'..anchorTo..' as anchor point')

  if self:IsShown() then
    ClassPlanButton.Background:Show()
    ClassPlanButton:SetWidth(600)
  else
    ClassPlanButton.Background:Hide()
    ClassPlanButton:SetWidth(200)
  end

  self:SetPoint('TOP', ClassPlanButton, 'BOTTOM', 0, 0)
  ClassPlanButton:ClearAllPoints()
  ClassPlanButton:SetPoint('TOP', self.anchorParent, anchorTo, 0, 0)

  print(ClassPlanButton:GetPoint(1))
end

function core:AddHandler(name, prototype)
  self.prototypes[name] = setmetatable(prototype, {
    __index = blockTemplate,
    __tostring = function() return name end
    })

  for i, event in ipairs(prototype.events) do
    if not self.events[event] then
      self:RegisterEvent(event)
      self.events[event] = {}
      print('|cFF00FF00registering', event)
    end

    prototype.numBlocks = 0
    if not self.events[event][name] then
      print('adding', name, 'to', event)
      self.events[event][name] = prototype.GetPlayerData
    end
  end
  self.sortedItems[name] = {}
end

function core:Setup()
  if IsLoggedIn() then
    WorldPlanData.OrderHall = WorldPlanData.OrderHall or {}
    self.data = WorldPlanData.OrderHall
    self.data.characters = self.data.characters or {}

    local name, realm = UnitName('player')
    realm  = realm or GetRealmName()
    self.profileKey = name .. '-' .. realm
    if not self.data.characters[self.profileKey] then
      self.data.characters[self.profileKey] = {}
    end
    self.profile = self.data.characters[self.profileKey]

    self.profile.shipments = self.profile.shipments or {}
    self.profile.missions = self.profile.missions or {}
    self.profile.classColor = RAID_CLASS_COLORS[select(2, UnitClass('player'))]

    if self.data.IsShown then
      self:Show()
    end
    self.initialized = true
    self.isStale = true
  end
end

local last_invoc = {}
function core:OnEvent (event, ...)
  print(event)
  if event == 'PLAYER_REGEN_DISABLED' then
    if self:IsVisible() then
      self.combatHide = true
      self:SetShown(false)
    end

  elseif event == 'PLAYER_REGEN_ENABLED' then
    if self.combatHide == true then
      self.combatHide = nil
      self:SetShown(true)
    end
  elseif event == 'PLAYER_LOGIN' then
    if not self.initialized then
      self:Setup()
    end
  elseif self.initialized and self.events[event] then
    self.isStale = true
    if self:IsVisible() then

    else
      for handler, func in pairs(self.events[event]) do
        if not self.Queued[handler] then
          print('scheduling update for handler', handler)
          self.Queued[handler] = C_Timer.NewTimer(0.25, function()
            func(handler)
            self.Queued[handler] = nil
          end)
        end
      end
    end
  end
end

function core:UpdateNotifications()
end


local SetOwnerData = function(self, data)
  local name, realm = string.match(data.profileKey, "(.+)%-(.+)")
  local ownerText = '|c'.. data.classColor.colorStr .. name .. '|r'
  --if realm ~= GI_currentRealm then
    --ownerText = ownerText .. ' (' .. realm .. ')'
  --end
  self.Owner:SetText(ownerText)
  self.Name:SetTextColor(data.classColor.r, data.classColor.g, data.classColor.b)
end

function core:RefreshItems(configKey, prototype)
  local sortedItems = self.sortedItems[configKey]

  self.blocks[configKey] = self.blocks[configKey] or {}
  local blocks = self.blocks[configKey]

  local lastProfile
  local numItems = #sortedItems
  local totalHeight = 0
  for i, data in ipairs(sortedItems) do
    local block = blocks[i]

    if not block then
      block = CreateFrame('Button', nil, self, prototype.templateName)
      block:SetID(i)
      block.handler = prototype
      prototype.numBlocks = prototype.numBlocks + 1

      if prototype.lastBlock then
        block:SetPoint('TOPLEFT', prototype.lastBlock, 'BOTTOMLEFT', 0, 0)
      else
        block:SetPoint(prototype.point, self[prototype.parent] or self, prototype.relativePoint, 0, 0)
      end
      prototype.lastBlock = block
      blocks[i] = block
    end

    totalHeight = totalHeight + block:GetHeight()
    block.lastProfile = lastProfile
    for k,v in pairs(data) do
      if type(block[k]) ~= 'function' then
        block[k] = v
      end
    end
    block:Refresh(data)
    print(block.isComplete, block.missionEndTime, block.name)
    SetOwnerData(block, data)

    block:Show()
    lastProfile = data.profileKey
  end

  for i = numItems + 1, prototype.numBlocks do
    if blocks[i] then
      blocks[i]:Hide()
    end
  end

  return totalHeight
end


local GI_profileKey, GI_profile, GI_isMine
local defaultClassColor = {r = 0.7, g = 0.7, b =0.7, colorStr = "ffffffff"}
local DoItemList = function (source, dest, onGetItem)
  local numItems = 0
  for index, data in ipairs(source) do
    data.classColor = GI_profile.classColor or defaultClassColor
    data.profileKey = GI_profileKey
    data.isMine = GI_isMine
    if onGetItem then
      onGetItem(data)
    end
    numItems = numItems + 1
    tinsert(dest, data)
  end
  return numItems
end

local max = math.max
function core:Refresh()
  print('|cFF00FFFFRefresh()|r')
  self.currentHeight = 0
  for name, ptype in pairs(self.prototypes) do
    print(' |cFF00FF00'..name..'|r')
    if self.isStale then
      ptype.GetPlayerData(self)
      self.sortedItems[name] = self.sortedItems[name] or {}

      wipe(self.sortedItems[name])
        --print(  'object:', ptype)
      for key, profile in pairs(self.data.characters) do
        if profile[name] and #profile[name] >= 1 then
          print(' ', #profile[name], key)
          GI_profileKey = key
          GI_profile = profile
          GI_isMine = (profile == self.profile)
          local results = DoItemList(profile[name], self.sortedItems[name], ptype.OnGetItem)
        end

      end

      if ptype.SortHandler then
        sort(self.sortedItems[name], ptype.SortHandler)
      end
    end

    local itemsHeight = self:RefreshItems(name, ptype)
    self.currentHeight = max(itemsHeight, self.currentHeight)
  end

  self:Reanchor()
  self:SetHeight(self.currentHeight)
  self.isStale = nil
end

function core:Toggle()
  if self:IsShown() then
    self:Hide()
  else
    self:Show()
  end

  if self.data then
    self.data.IsShown = self:IsShown()
  end
end

function core:OnUpdate()
  if self.isStale then
    print('update requested internally')
    self:Refresh()
  end
end

function core:OnShow()
  print('|cFF00FFFFShow()')
  if self.isStale then
    print('on-show update')
    self:Refresh()
  end
  ClassPlanButton.Background:Show()
  ClassPlanButton.Grip:SetShown(true)
end
function core:OnHide()
  print('|cFF00FFFFHide()')
  ClassPlanButton.Background:Hide()
  ClassPlanButton.Grip:SetShown(false)
end


function core:SortItems(ptype)
  print('|cFF0088FFSortItems('..tostring(ptype)..')|r')
  GI_currentTime = time()

end

function MissionsHandler:OnComplete()
  print('flagging complete', self.name)
  self:Refresh()
end

function MissionsHandler:OnUpdate(sinceLast)
  if self.isComplete then
    return
  end
  self.throttle = (self.throttle or .5) + sinceLast
  if self.throttle >= .5 then
    self.throttle = self.throttle - .5
  else
    return
  end

  if self.missionEndTime then
    local timeLeft = self.missionEndTime - time()
    if timeLeft < 0 then
      self:OnComplete()
    else
      self.TimeLeft:SetText(GetTimeLeftString(timeLeft))
      if timeLeft > 3600 then
        self.TimeLeft:SetTextColor(1,1,1)
      else
        self.TimeLeft:SetTextColor(1,1,0)
      end
    end

    if not self.isComplete then
      local progress = (time() - (self.missionEndTime - self.durationSeconds)) / self.durationSeconds
      local w = self.ProgressBG:GetWidth()
      if progress >= 1 then
        self.ProgressBar:SetWidth(w)
      else
        self.ProgressBar:SetWidth(progress * w)
        local r, g = 1, 0
        if progress >= 0.5 then
          g = 1
          r = 1-((progress-0.5)*2)
        else
          g = min(progress * 2, 1)
          r = 1
        end
        self.ProgressBar:SetColorTexture(r,g,0,1)
        self.ProgressBG:SetColorTexture(r,g,0,0.125)
      end
      self.ProgressBG:Show()
      self.ProgressBar:Show()
    else
      self.ProgressBG:Hide()
      self.ProgressBar:Hide()
    end
  else
    self.TimeLeft:SetText(self.missionEndTime)
  end
end

function MissionsHandler:Refresh()
  local r,g,b = 1, 1, 1
  if self.isRare then
    r,g,b = 0.1, 0.4, 1
    self.IconBorder:SetVertexColor(r, g, b, 1)
  end

  --self.missionData = data
  self.Name:SetText(self.name)
  if #self.rewards >= 1 then
    self.Icon:SetTexture(self.rewards[1].icon or GetItemIcon(self.rewards[1].itemID))
    self.rewardInfo = self.rewards[1]
  else
    self.Icon:SetAtlas(self.typeAtlas, false)
  end

  if self.isComplete then
    self.Done:Show()
  else
    self.Done:Hide()
  end

  if self.isComplete then
    self.TimeLeft:SetText('Complete!')
    self.Background:SetColorTexture(.25,.25,.25,1)
  else
    self.Background:SetColorTexture(0,0,0,0.5)
  end
end

function MissionsHandler:OnEnter()
  if self.rewardInfo and self.rewardInfo.itemID then
    GameTooltip:SetOwner(self, 'ANCHOR_LEFT')
    GameTooltip:SetItemByID(self.rewardInfo.itemID)
    GameTooltip:Show()
  end
end

function MissionsHandler:OnLeave()
  if GameTooltip:IsOwned(self) then
    GameTooltip:Hide()
  end
end

function ShipmentsHandler:Refresh()
  --[[
  self.icon = data.icon
  self.shipmentCapacity = data.shipmentCapacity
  self.shipmentsReady = data.shipmentsReady
  self.shipmentsTotal = data.shipmentsTotal
  self.creationTime = data.creationTime
  self.duration = data.duration
  self.itemID = data.itemID
  self.itemQuality = data.itemQuality
      icon = texture,
      shipmentCapacity = shipmentCapacity,
      shipmentsReady = shipmentsReady,
      shipmentsTotal = shipmentsTotal,
      creationTime = creationTime,
      duration = duration,
      timeleftString = timeleftString,
      itemName = itemName,
      itemIcon = itemIcon,
      itemQuality = itemQuality,
      itemID = itemID

  --]]
  self.Icon:SetTexture(self.icon)
  self.Name:SetText(self.name)
  self.Count:SetText(self.shipmentsReady)
  self.Done:SetShown(self.shipmentsReady and (self.shipmentsReady >= 1))

  -- flag as complete
  if ( self.shipmentsReady >= self.shipmentsTotal ) and (not self.isBeingResearched) then
    self.Swipe:SetCooldownUNIX(0, 0);
    self.Done:Show();
    self.isComplete = true
  else
    self.Swipe:SetCooldownUNIX(self.creationTime or 0 , self.duration or 0);
  end

  if self.isComplete then
    self.TimeLeft:SetText('Complete!')
    self.Background:SetColorTexture(0.5,0.5,0.5)
  elseif (self.shipmentsReady and (self.shipmentsReady > 0)) then
    self.Background:SetColorTexture(0.5,0.5,0.5,.5)
  else
    self.Background:SetColorTexture(0,0,0,0.5)
  end
end

local time = time
function ShipmentsHandler:OnUpdate(sinceLast)
  self.throttle = (self.throttle or 1) + sinceLast
  if self.throttle >= 1 then
    self.throttle = self.throttle - 1
  else
    return
  end

  if (self.shipmentsReady and self.shipmentsTotal) and (self.shipmentsReady < self.shipmentsTotal) then
    local timeLeft = self.creationTime + self.duration - time()
    if self.shipmentsReady >= 1 then
      self.TimeLeft:SetText(GetTimeLeftString(timeLeft))
      self.TimeLeft:SetTextColor(0,1,0)
    else
      self.TimeLeft:SetText(GetTimeLeftString(timeLeft))
      self.TimeLeft:SetTextColor(1,1,1)
    end
    if (timeLeft < 0) then
      if self.shipmentsReady < self.shipmentsTotal then
        self.shipmentsReady = self.shipmentsReady + 1
        self.creationTime = self.creationTime + self.duration
        -- text will be set on next update
      end
    end
  elseif self.isBeingResearched then
    self.TimeLeft:SetText(GetTimeLeftString(self.researchStartTime + self.researchDuration - time()))
    self.TimeLeft:SetTextColor(1,1,0)
  else
    self.TimeLeft:SetText('Complete!')
    self.TimeLeft:SetTextColor(0,1,0)
  end

  if not self.isComplete then
    local progress =  ((time() - self.creationTime) / self.duration)
    local w = self.ProgressBG:GetWidth()
    if progress >= 1 then
      self.ProgressBar:SetWidth(w)
    else
      self.ProgressBar:SetWidth(progress * w)
    end

    local r, g = 1, 0
    if progress >= 0.5 then
      g = 1
      r = 1-((progress-0.5)*2)
    else
      g = min(progress * 2, 1)
      r = 1
    end
    self.ProgressBar:SetColorTexture(r, g, 0, 1)
    self.ProgressBG:SetColorTexture(r, g, 0, .125)
    self.ProgressBG:Show()
    self.ProgressBar:Show()
  else
    self.ProgressBG:Hide()
    self.ProgressBar:Hide()
  end
end

function ShipmentsHandler:OnEnter()
  if ( self.shipmentsReady and self.shipmentsTotal ) then
    GameTooltip:SetOwner(self, 'ANCHOR_LEFT')
    GameTooltip:AddLine(self.Owner:GetText(), self.Owner:GetTextColor())
    GameTooltip:AddLine(self.shipmentType)
    GameTooltip:AddLine(self.shipmentsReady .. ' of '.. self.shipmentsTotal)
    GameTooltip:Show()
  end
end

function ShipmentsHandler:OnLeave()
  if GameTooltip:IsOwned(self) then
    GameTooltip:Hide()
  end
end

function ShipmentsHandler:OnClick(button)
  if button == 'RightButton' then
    self.handler:FreeBlock(self)
  end
end