Nenue@40: local _, db = ... Nenue@40: Nenue@40: local wipe, tinsert, ipairs, select = table.wipe, table.insert, ipairs, select Nenue@40: local UnitClass = UnitClass Nenue@40: local time, max, min = time, max, min Nenue@40: local print = DEVIAN_WORKSPACE and function(...) print('ClassPlan', ...) end or nop Nenue@40: Nenue@40: Nenue@40: local CG_GetBuildings = C_Garrison.GetBuildings Nenue@40: local CG_GetFollowerShipments = C_Garrison.GetFollowerShipments Nenue@40: local CG_GetLooseShipments = C_Garrison.GetLooseShipments Nenue@40: local CG_GetTalentTrees = C_Garrison.GetTalentTrees Nenue@40: local CG_GetCompleteTalent = C_Garrison.GetCompleteTalent Nenue@40: local CG_GetLandingPageShipmentInfo = C_Garrison.GetLandingPageShipmentInfo Nenue@40: local CG_GetLandingPageShipmentInfoByContainerID = C_Garrison.GetLandingPageShipmentInfoByContainerID Nenue@40: Nenue@40: local AK_NOTES, RECRUIT_MAJOR, RECRUIT_MINOR, OH_TALENT, NOMI = 2, 4, 8, 16, 32 Nenue@40: Nenue@40: local FollowerTypes = { Nenue@40: ['Pathfinders'] = true, Nenue@40: ['Acolytes'] = true, Nenue@40: } Nenue@40: local ShipmentOrder = { Nenue@40: [AK_NOTES] = 2, Nenue@40: [RECRUIT_MAJOR] = 3, Nenue@40: [RECRUIT_MINOR] = 4, Nenue@40: [NOMI] = 5, Nenue@40: [OH_TALENT] = 6, Nenue@40: } Nenue@40: local SortKey = 'shipmentType' Nenue@40: local SortTable = ShipmentOrder Nenue@40: Nenue@40: local ShipmentList = { Nenue@40: templateName = 'ClassPlanShipmentEntry', Nenue@40: listKey = {'shipments'}, Nenue@40: listTitle = {'Work Orders'}, Nenue@40: events = { Nenue@40: 'GARRISON_MISSION_LIST_UPDATE', Nenue@40: 'GARRISON_LANDINGPAGE_SHIPMENTS', Nenue@40: 'GARRISON_TALENT_UPDATE', Nenue@40: "GARRISON_TALENT_COMPLETE", Nenue@40: "GARRISON_SHIPMENT_RECEIVED", Nenue@40: 'GARRISON_FOLLOWER_LIST_UPDATE', Nenue@40: 'SHIPMENT_CRAFTER_INFO', Nenue@40: 'ITEM_PUSH'}, Nenue@40: } Nenue@40: Nenue@40: local ShipmentEntry = {} Nenue@40: Nenue@40: function ShipmentList:Reanchor() Nenue@40: print('|cFF00FFFF'..self:GetName()..':Reanchor|r') Nenue@40: self:SetPoint('TOPLEFT', ClassOrderPlan.BackgroundInset, 'TOPLEFT') Nenue@40: self:SetPoint('BOTTOMRIGHT', -ClassOrderPlan.BackgroundInset:GetWidth()/2, 0) Nenue@40: end Nenue@40: Nenue@40: do Nenue@40: local ShipmentsInfo = {} Nenue@40: local AddShipmentInfo = function(shipmentType, name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID, followerID) Nenue@40: -- early login queries may return empty tables, causing the sorter to compare nil Nenue@40: if not creationTime then Nenue@40: return Nenue@40: end Nenue@40: --print(shipmentType, name, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString) Nenue@40: tinsert(ShipmentsInfo, Nenue@40: { Nenue@40: shipmentType = shipmentType, Nenue@40: name = name, Nenue@40: icon = texture, Nenue@40: shipmentCapacity = shipmentCapacity, Nenue@40: shipmentsReady = shipmentsReady, Nenue@40: shipmentsTotal = shipmentsTotal, Nenue@40: creationTime = creationTime, Nenue@40: duration = duration, Nenue@40: timeleftString = timeleftString, Nenue@40: itemName = itemName, Nenue@40: itemIcon = itemIcon, Nenue@40: itemQuality = itemQuality, Nenue@40: itemID = itemID, Nenue@40: followerID = followerID, Nenue@40: }) Nenue@40: print(' ', name, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration) Nenue@40: end Nenue@40: function ShipmentList:GetPlayerData () Nenue@40: local profileList = self:GetParent().profile.shipments Nenue@40: wipe(ShipmentsInfo) Nenue@40: Nenue@40: local garrisonType = LE_GARRISON_TYPE_7_0 Nenue@40: local buildings = CG_GetBuildings(garrisonType); Nenue@40: local shipmentIndex = 0 Nenue@40: --print('Buildings:') Nenue@40: for i = 1, #buildings do Nenue@40: local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID = CG_GetLandingPageShipmentInfo(i); Nenue@40: AddShipmentInfo(RECRUIT_MAJOR, name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID) Nenue@40: end Nenue@40: Nenue@40: --print('Follower:') Nenue@40: local followerShipments = CG_GetFollowerShipments(garrisonType); Nenue@40: for i = 1, #followerShipments do Nenue@40: local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, _, _, _, _, followerID = CG_GetLandingPageShipmentInfoByContainerID(followerShipments[i]); Nenue@40: AddShipmentInfo(RECRUIT_MINOR, name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, nil, nil, nil, nil, followerID) Nenue@40: end Nenue@40: Nenue@40: --print('Loose:') Nenue@40: local looseShipments = CG_GetLooseShipments(garrisonType) Nenue@40: for i = 1, #looseShipments do Nenue@40: local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString = CG_GetLandingPageShipmentInfoByContainerID(looseShipments[i]); Nenue@40: AddShipmentInfo(AK_NOTES, name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString) Nenue@40: end Nenue@40: Nenue@109: Nenue@109: local talentTrees = C_Garrison.GetTalentTreeIDsByClassID(garrisonType, select(3, UnitClass("player"))); Nenue@40: -- this is a talent that has completed, but has not been seen in the talent UI yet. Nenue@40: local completeTalentID = CG_GetCompleteTalent(garrisonType); Nenue@109: print('Talents:') Nenue@40: if (talentTrees) then Nenue@109: for treeIndex, treeID in ipairs(talentTrees) do Nenue@109: local _, _, tree = C_Garrison.GetTalentTreeInfoForID(garrisonType, treeID); Nenue@40: for talentIndex, talent in ipairs(tree) do Nenue@40: local showTalent = false; Nenue@40: if (talent.isBeingResearched) or (talent.id == completeTalentID) then Nenue@40: AddShipmentInfo(OH_TALENT, talent.name, talent.icon, 1, (talent.isBeingResearched and 0 or 1), 1, talent.researchStartTime, talent.researchDuration, talent.timeleftString) Nenue@40: end Nenue@40: end Nenue@40: end Nenue@40: end Nenue@40: Nenue@40: wipe(profileList) Nenue@40: for index, data in ipairs(ShipmentsInfo) do Nenue@40: --DEFAULT_CHAT_FRAME:AddMessage(data.shipmentType ..' '.. tostring(data.name) ..' '.. tostring(data.creationTime) ..' '.. tostring(data.duration)) Nenue@40: tinsert(profileList, data) Nenue@40: end Nenue@40: self.isStale = true Nenue@40: return true Nenue@40: end Nenue@40: end Nenue@40: -- Update shipment flags data Nenue@40: local SetActualShipmentTime = function(self) Nenue@40: Nenue@40: if self.isComplete then Nenue@40: return nil, nil Nenue@40: end Nenue@40: Nenue@40: local timestamp = time() Nenue@40: local timeLeft = self.creationTime + self.duration - timestamp Nenue@40: local duration = self.duration * (self.fullDuration and (self.shipmentsTotal - self.shipmentsReady) or 1) Nenue@40: local justFinished = false Nenue@40: while (self.shipmentsReady < self.shipmentsTotal) and (timeLeft <= 0) do Nenue@40: if not self.originalReady then Nenue@40: self.originalReady = self.shipmentsReady Nenue@40: self.originalCreationTime = self.creationTime Nenue@40: end Nenue@40: Nenue@40: self.shipmentsReady = self.shipmentsReady + 1 Nenue@40: self.creationTime = self.creationTime + self.duration Nenue@40: timeLeft = timeLeft + self.duration Nenue@40: print('|cFF00FF88 pre-parse "'..self.name..'"|r', 'timeLeft:', timeLeft, 'shipments:', self.shipmentsReady, self.shipmentsTotal) Nenue@40: end Nenue@40: Nenue@40: if (timeLeft <= 0) and (not self.isBeingResearched) then Nenue@40: self.isComplete = true Nenue@40: self.isStale = true Nenue@40: end Nenue@40: Nenue@40: local expires = (self.originalCreationTime or self.creationTime) + duration Nenue@40: return expires, duration Nenue@40: end Nenue@40: Nenue@40: function ShipmentList:OnEvent(event, ...) Nenue@40: if (event == 'SHIPMENT_CRAFTER_INFO' or event == 'ITEM_PUSH' or event == 'GARRISON_FOLLOWER_LIST_UPDATE') then Nenue@40: C_Garrison.RequestLandingPageShipmentInfo() Nenue@40: elseif event == 'SHIPMENT_UPDATE' then Nenue@40: local shipmentStarted = ... Nenue@40: if shipmentStarted then Nenue@40: C_Garrison.RequestLandingPageShipmentInfo() Nenue@40: end Nenue@40: elseif event == 'GARRISON_LANDINGPAGE_SHIPMENTS' then Nenue@40: --WorldPlan:print("New shipments data received.") Nenue@40: self:RefreshData() Nenue@40: else Nenue@40: ClassPlanHandlerBase.OnEvent(self, event, ...) Nenue@40: end Nenue@40: end Nenue@40: Nenue@40: Nenue@40: function ShipmentList:OnGetItem (data) Nenue@40: if data.shipmentsTotal then Nenue@40: local expires = SetActualShipmentTime(data) Nenue@40: if expires and (expires > time()) then Nenue@40: self:ScheduleUpdate(expires) Nenue@40: end Nenue@40: end Nenue@40: Nenue@40: if data.shipmentType == 255 or type(data.shipmentType == 'string') then Nenue@40: if data.name == 'Artifact Research Notes' then Nenue@40: data.shipmentType = AK_NOTES Nenue@40: elseif string.match(data.name, 'Recipes') then Nenue@40: data.shipmentType = NOMI Nenue@40: elseif FollowerTypes[data.name] then Nenue@40: data.shipmentType = RECRUIT_MINOR Nenue@40: else Nenue@40: data.shipmentType = OH_TALENT Nenue@40: end Nenue@40: end Nenue@40: Nenue@40: Nenue@40: end Nenue@40: Nenue@40: function ShipmentList:OnHeaderClick() Nenue@40: -- flip sort table and key, push a sort and refresh Nenue@40: end Nenue@40: Nenue@40: Nenue@40: ShipmentList.SortHandler = function(a, b) Nenue@95: local status = false Nenue@107: if b.isComplete ~= a.isComplete then Nenue@107: if a.isComplete then Nenue@107: status = true Nenue@107: end Nenue@107: else Nenue@107: if a[SortKey] then Nenue@107: if b[SortKey] then Nenue@107: status = (SortTable[a[SortKey]] < SortTable[b[SortKey]]) Nenue@107: else Nenue@95: status = true Nenue@95: end Nenue@40: else Nenue@40: if a.profileKey ~= b.profileKey then Nenue@95: status = (a.profileKey < b.profileKey) Nenue@95: Nenue@40: else Nenue@40: if a.shipmentsReady and b.shipmentsReady then Nenue@95: status = (a.shipmentsReady) > (b.shipmentsReady) Nenue@40: elseif a.shipmentsReady or b.shipmentsReady then Nenue@95: status = (a.shipmentsReady) or true or false Nenue@40: else Nenue@40: Nenue@40: if (a.creationTime ~= b.creationTime) then Nenue@95: status = (a.creationTime) < (b.creationTime) Nenue@40: else Nenue@95: status = (a.name) < (b.name) Nenue@40: end Nenue@40: end Nenue@40: Nenue@40: end Nenue@40: end Nenue@40: end Nenue@95: return status Nenue@40: end Nenue@40: Nenue@40: function ShipmentList:OnShow() Nenue@40: print('|cFF00FF88'..self:GetName()..':OnShow()|r') Nenue@40: end Nenue@40: Nenue@40: function ShipmentEntry:OnLoad() Nenue@40: ClassPlanMissionEntryMixin.OnLoad(self) Nenue@40: end Nenue@40: Nenue@40: Nenue@40: function ShipmentEntry:Update() Nenue@40: --print(' |cFF00FF88"'.. self.name..'":Update()|r') Nenue@40: self.Icon:SetTexture(self.icon) Nenue@40: self.Count:SetText(self.shipmentsReady ..'/'.. self.shipmentsTotal) Nenue@40: Nenue@40: -- flag as complete Nenue@40: local itemType = db.ClassPlanTypes.inProgress Nenue@40: if ( self.shipmentsReady >= self.shipmentsTotal ) and (not self.isBeingResearched) then Nenue@40: self.Swipe:SetCooldownUNIX(0, 0); Nenue@40: itemType = db.ClassPlanTypes.complete Nenue@40: else Nenue@40: if (self.shipmentsReady >= 1) and (self.shipmentsReady < self.shipmentsTotal) then Nenue@40: itemType = db.ClassPlanTypes.shipmentsReady Nenue@40: end Nenue@40: self.Swipe:SetCooldownUNIX(self.creationTime or 0 , self.duration or 0); Nenue@40: end Nenue@40: self.Background:SetColorTexture(unpack(itemType.backgroundColor)) Nenue@40: Nenue@40: SetActualShipmentTime(self) Nenue@40: self.throttle = 2 Nenue@40: end Nenue@40: Nenue@40: Nenue@40: Nenue@40: function ShipmentEntry:OnUpdate(sinceLast) Nenue@40: self.throttle = (self.throttle or 1) + sinceLast Nenue@40: if self.throttle >= 1 then Nenue@40: self.throttle = self.throttle - 1 Nenue@40: else Nenue@40: return Nenue@40: end Nenue@40: Nenue@40: if (self.shipmentsReady and self.shipmentsTotal) and (self.shipmentsReady < self.shipmentsTotal) then Nenue@40: local expires, duration = SetActualShipmentTime(self) Nenue@40: if self.isComplete then Nenue@40: self.TimeLeft:SetText('Complete!') Nenue@40: self.TimeLeft:SetTextColor(0,1,1) Nenue@40: elseif self.shipmentsReady >= 1 then Nenue@40: self:SetTimeLeft(expires, duration) Nenue@40: self.TimeLeft:SetTextColor(1,1,0) Nenue@40: else Nenue@40: self:SetTimeLeft(expires, duration) Nenue@40: self.TimeLeft:SetTextColor(1,1,1) Nenue@40: end Nenue@40: elseif self.isBeingResearched then Nenue@40: self:SetTimeLeft(self.researchStartTime + self.researchDuration - time(), self.researchDuration) Nenue@40: self.TimeLeft:SetTextColor(1,1,1) Nenue@40: else Nenue@40: self.TimeLeft:SetText('Complete!') Nenue@40: self.TimeLeft:SetTextColor(0,1,0) Nenue@40: end Nenue@40: end Nenue@40: Nenue@40: function ShipmentEntry:OnEnter() Nenue@40: if ( self.shipmentsReady and self.shipmentsTotal ) then Nenue@40: GameTooltip:SetOwner(self, 'ANCHOR_LEFT') Nenue@40: GameTooltip:AddLine(self.Owner:GetText(), self.Owner:GetTextColor()) Nenue@40: GameTooltip:AddLine(self.shipmentsReady .. ' of '.. self.shipmentsTotal) Nenue@40: GameTooltip:Show() Nenue@40: end Nenue@40: end Nenue@40: Nenue@40: Nenue@40: function ShipmentEntry:OnLeave() Nenue@40: if GameTooltip:IsOwned(self) then Nenue@40: GameTooltip:Hide() Nenue@40: end Nenue@40: end Nenue@40: Nenue@40: function ShipmentEntry:OnClick(button) Nenue@40: self.fullDuration = not self.fullDuration Nenue@40: self:Update() Nenue@40: end Nenue@40: Nenue@40: ClassPlanShipmentHandler = CreateFromMixins(ClassPlanHandlerBase, ShipmentList) Nenue@40: ClassPlanShipmentEntryMixin = CreateFromMixins(ClassPlanEntryBase, ShipmentEntry)