· 5 years ago · Oct 18, 2020, 07:14 PM
1local _
2-- global functions and variebles to locals to keep LINT happy
3local assert = _G.assert
4local ARTIFACT_RELIC_TALENT_AVAILABLE = _G.ARTIFACT_RELIC_TALENT_AVAILABLE; assert(ARTIFACT_RELIC_TALENT_AVAILABLE ~= nil,'ARTIFACT_RELIC_TALENT_AVAILABLE')
5local C_Garrison = _G.C_Garrison; assert(C_Garrison ~= nil,'C_Garrison')
6local C_Reputation = _G.C_Reputation; assert(C_Reputation ~= nil,'C_Reputation')
7local CreateFrame = _G.CreateFrame; assert(CreateFrame ~= nil,'CreateFrame')
8local date = _G.date; assert(date ~= nil,'date')
9local debugprofilestop = _G.debugprofilestop; assert(debugprofilestop ~= nil,'debugprofilestop')
10local ERR_SPELL_FAILED_REAGENTS_GENERIC = _G.ERR_SPELL_FAILED_REAGENTS_GENERIC; assert(ERR_SPELL_FAILED_REAGENTS_GENERIC ~= nil,'ERR_SPELL_FAILED_REAGENTS_GENERIC')
11local ExpandAllFactionHeaders = _G.ExpandAllFactionHeaders; assert(ExpandAllFactionHeaders ~= nil,'ExpandAllFactionHeaders')
12local format = _G.format; assert(format ~= nil,'format')
13local GetArchaeologyRaceInfo = _G.GetArchaeologyRaceInfo; assert(GetArchaeologyRaceInfo ~= nil,'GetArchaeologyRaceInfo')
14local GetCVar = _G.GetCVar; assert(GetCVar ~= nil,'GetCVar')
15local GetFactionInfo = _G.GetFactionInfo; assert(GetFactionInfo ~= nil,'GetFactionInfo')
16local GetFactionInfoByID = _G.GetFactionInfoByID; assert(GetFactionInfoByID ~= nil,'GetFactionInfoByID')
17local GetChatWindowInfo = _G.GetChatWindowInfo; assert(GetChatWindowInfo ~= nil,'GetChatWindowInfo')
18local GetItemCount = _G.GetItemCount; assert(GetItemCount ~= nil,'GetItemCount')
19local GetItemInfo = _G.GetItemInfo; assert(GetItemInfo ~= nil,'GetItemInfo')
20local GetItemSpell = _G.GetItemSpell; assert(GetItemSpell ~= nil,'GetItemSpell')
21local GetMinimapZoneText = _G.GetMinimapZoneText; assert(GetMinimapZoneText ~= nil,'GetMinimapZoneText')
22local GetNumArchaeologyRaces = _G.GetNumArchaeologyRaces; assert(GetNumArchaeologyRaces ~= nil,'GetNumArchaeologyRaces')
23local GetNumFactions = _G.GetNumFactions; assert(GetNumFactions ~= nil,'GetNumFactions')
24local GetSpellCooldown = _G.GetSpellCooldown; assert(GetSpellCooldown ~= nil,'GetSpellCooldown')
25local GetSpellInfo = _G.GetSpellInfo; assert(GetSpellInfo ~= nil,'GetSpellInfo')
26local GetTime = _G.GetTime; assert(GetTime ~= nil,'GetTime')
27local gsub = _G.gsub; assert(gsub ~= nil,'gsub')
28local InCombatLockdown = _G.InCombatLockdown; assert(InCombatLockdown ~= nil,'InCombatLockdown')
29local ipairs = _G.ipairs; assert(ipairs ~= nil,'ipairs')
30local IsPlayerSpell = _G.IsPlayerSpell; assert(IsPlayerSpell ~= nil,'IsPlayerSpell')
31local ITEM_OPENABLE = _G.ITEM_OPENABLE; assert(ITEM_OPENABLE ~= nil,'ITEM_OPENABLE')
32local ITEM_SPELL_TRIGGER_ONUSE = _G.ITEM_SPELL_TRIGGER_ONUSE; assert(ITEM_SPELL_TRIGGER_ONUSE ~= nil,'ITEM_SPELL_TRIGGER_ONUSE')
33local LE_FOLLOWER_TYPE_SHIPYARD_6_2 = Enum.GarrisonFollowerType.FollowerType_6_2; assert(LE_FOLLOWER_TYPE_SHIPYARD_6_2 ~= nil,'LE_FOLLOWER_TYPE_SHIPYARD_6_2')
34local LE_GARRISON_TYPE_6_0 = Enum.GarrisonType.Type_6_0; assert(LE_GARRISON_TYPE_6_0 ~= nil,'LE_GARRISON_TYPE_6_0')
35local LE_GARRISON_TYPE_7_0 = Enum.GarrisonType.Type_7_0; assert(LE_GARRISON_TYPE_7_0 ~= nil,'LE_GARRISON_TYPE_7_0')
36local LibStub = _G.LibStub; assert(LibStub ~= nil,'LibStub')
37local math = _G.math; assert(math ~= nil,'math')
38local NUM_CHAT_WINDOWS = _G.NUM_CHAT_WINDOWS; assert(NUM_CHAT_WINDOWS ~= nil,'NUM_CHAT_WINDOWS')
39local pairs = _G.pairs; assert(pairs ~= nil,'pairs')
40local select = _G.select; assert(select ~= nil,'select')
41local string = _G.string; assert(string ~= nil,'string')
42local tonumber = _G.tonumber; assert(tonumber ~= nil,'tonumber')
43local type = _G.type; assert(type ~= nil,'type')
44local UIParent = _G.UIParent; assert(UIParent ~= nil,'UIParent')
45local UnitClass = _G.UnitClass; assert(UnitClass ~= nil,'UnitClass')
46local unpack = _G.unpack; assert(unpack ~= nil,'unpack')
47local wipe = _G.wipe; assert(wipe ~= nil,'wipe')
48local PLAYER_LIST_DELIMITER = _G.PLAYER_LIST_DELIMITER; assert(PLAYER_LIST_DELIMITER ~= nil,'PLAYER_LIST_DELIMITER')
49local table = _G.table; assert(table ~= nil,'table')
50-- local AddOn
51local ADDON, P = ...
52local NOP = LibStub("AceAddon-3.0"):GetAddon(ADDON)
53--
54local ARCHAELOGY_ANNOUNCE = P.ARCHAELOGY_ANNOUNCE; assert(ARCHAELOGY_ANNOUNCE ~= nil,'ARCHAELOGY_ANNOUNCE')
55local ARTIFACT_ANNOUNCE = P.ARTIFACT_ANNOUNCE; assert(ARTIFACT_ANNOUNCE ~= nil,'ARTIFACT_ANNOUNCE')
56local CB_CVAR = P.CB_CVAR; assert(CB_CVAR ~= nil,'CB_CVAR')
57local L = P.L
58local PRI_REP = P.PRI_REP; assert(PRI_REP ~= nil,'PRI_REP')
59local REWARD_ANNOUNCE = P.REWARD_ANNOUNCE; assert(REWARD_ANNOUNCE ~= nil,'REWARD_ANNOUNCE')
60local RGB_RED = P.RGB_RED; assert(RGB_RED ~= nil,'RGB_RED')
61local RGB_YELLOW = P.RGB_YELLOW; assert(RGB_YELLOW ~= nil,'RGB_YELLOW')
62local SHIPYARD_ANNOUNCE = P.SHIPYARD_ANNOUNCE; assert(SHIPYARD_ANNOUNCE ~= nil,'SHIPYARD_ANNOUNCE')
63local SPELL_PICKLOCK = P.SPELL_PICKLOCK; assert(SPELL_PICKLOCK ~= nil,'SPELL_PICKLOCK')
64local TALENT_ANNOUNCE = P.TALENT_ANNOUNCE; assert(TALENT_ANNOUNCE ~= nil,'TALENT_ANNOUNCE')
65local TOGO_ANNOUNCE = P.TOGO_ANNOUNCE; assert(TOGO_ANNOUNCE ~= nil,'TOGO_ANNOUNCE')
66local TOOLTIP_ITEM = P.TOOLTIP_ITEM; assert(TOOLTIP_ITEM ~= nil,'TOOLTIP_ITEM')
67local TOOLTIP_SCAN = P.TOOLTIP_SCAN; assert(TOOLTIP_SCAN ~= nil,'TOOLTIP_SCAN')
68local TOOLTIP_SPELL = P.TOOLTIP_SPELL; assert(TOOLTIP_SPELL ~= nil,'TOOLTIP_SPELL')
69local whoCalls = P.whoCalls; assert(whoCalls ~= nil,'whoCalls')
70local WORK_ANNOUNCE = P.WORK_ANNOUNCE; assert(WORK_ANNOUNCE ~= nil,'WORK_ANNOUNCE')
71local LIB_MASQUE = P.LIB_MASQUE; -- this one could not exist
72local LIB_QUESTITEM = P.LIB_QUESTITEM; assert(LIB_QUESTITEM ~= nil,'LIB_QUESTITEM')
73local print = P.print; assert(print ~= nil,'print')
74local T_BLACKLIST = P.T_BLACKLIST; assert(T_BLACKLIST ~= nil,'T_BLACKLIST')
75local T_CHECK = P.T_CHECK; assert(T_CHECK ~= nil,'T_CHECK')
76local T_OPEN = P.T_OPEN; assert(T_OPEN ~= nil,'T_OPEN')
77local T_RECIPES_FIND = P.T_RECIPES_FIND; assert(T_RECIPES_FIND ~= nil,'T_RECIPES_FIND')
78local T_REPS = P.T_REPS; assert(T_REPS ~= nil,'T_REPS')
79local T_SPELL_FIND = P.T_SPELL_FIND; assert(T_SPELL_FIND ~= nil,'T_SPELL_FIND')
80local T_USE = P.T_USE; assert(T_USE ~= nil,'T_USE')
81local VALIDATE = P.VALIDATE -- this can be null or false
82local TIMER_IDLE = P.TIMER_IDLE; assert(TIMER_IDLE ~= nil,'TIMER_IDLE')
83--
84function NOP:Verbose(...) -- if verbose then output
85 if NOP.AceDB.profile.verbose then print(...) end
86end
87function NOP:OnInitialize() -- app initialize
88 self:InitEvents() -- register events
89 self:ProfileLoad() -- initialize AceDB
90 self:OptionsLoad() -- initialize AceConfig
91 self.scanFrame = self:TooltipCreate(TOOLTIP_SCAN)
92 self.itemFrame = self:TooltipCreate(TOOLTIP_ITEM)
93 self.spellFrame = self:TooltipCreate(TOOLTIP_SPELL) -- /run NOP.spellFrame = NOP:TooltipCreate("NOP_TOOLTIP_SPELL")
94end
95function NOP:OnEnable() -- add-on enable
96 self.masque = LIB_MASQUE and LIB_MASQUE:Group(ADDON) -- when user has installed Masque addon, then skinnig is done by Masque save new group pointer
97 LIB_QUESTITEM.RegisterCallback(self, "LibQuestItem_Update","QBUpdate")
98end
99function NOP:TooltipCreate(name) -- create tooltip frame
100 local frame
101 if _G[name] and _G[name].SetOwner then -- test if frame exist, workaround for broken tooltip, no need create new tooltip frame, reuse old one
102 frame = _G[name]
103 else
104 frame = CreateFrame("GameTooltip",name,nil,"GameTooltipTemplate") -- create new frame
105 end
106 frame:SetOwner(UIParent,"ANCHOR_NONE") -- frame out of screen and start updating
107 return frame
108end
109local tItemRetry = {}
110function NOP:ItemLoad() -- load template item tooltips
111 local itemRetry = nil
112 self:Profile(true)
113 local nCB = tonumber(GetCVar(CB_CVAR)) -- if colorblind mode activated then on 2nd line there is extra info
114 for itemID, data in pairs(NOP.T_RECIPES) do
115 if not T_RECIPES_FIND[itemID] then -- need fill pattern
116 local name = GetItemInfo(itemID) -- query or fill client side cache
117 if type(name) ~= 'string' or name == '' then -- item has no info on client side yet, let wait for server
118 if VALIDATE then
119 local retry = tItemRetry[itemID] or 0
120 retry = retry + 1
121 tItemRetry[itemID] = retry
122 if retry > 1 then print("ItemLoad:GetItemInfo() empty for",itemID, retry) end
123 end
124 itemRetry = itemID
125 else
126 local c,pattern,zone,map,faction = unpack(data,1,5)
127 if (c[2] == PRI_REP) and faction then T_REPS[name] = faction end -- fill-up item name to faction table
128 self.itemFrame:ClearLines() -- clean tooltip frame
129 self.itemFrame:SetItemByID(itemID)
130 local count = self.itemFrame:NumLines()
131 if count > 1 then -- I must have at least 2 lines in tooltip
132 if type(pattern) == "number" then
133 if count >= (pattern + nCB) then
134 local i = pattern + nCB
135 local tooltipText = TOOLTIP_ITEM .. "TextLeft" .. i
136 local text = _G[tooltipText].GetText and _G[tooltipText]:GetText() or "none"
137 if text and (text ~= "none") and (text ~= "") and not string.find(text,'100') then -- bandaid for incomplete tooltip
138 T_RECIPES_FIND[itemID] = {c,text,zone,map,faction}
139 else
140 if VALIDATE then
141 local retry = tItemRetry[itemID] or 0
142 retry = retry + 1
143 tItemRetry[itemID] = retry
144 if retry > 1 then print("ItemLoad:SetItemByID()",itemID,"Line:",i,"Contains:",text,retry) end
145 end
146 itemRetry = itemID
147 end
148 else
149 if VALIDATE then
150 local retry = tItemRetry[itemID] or 0
151 retry = retry + 1
152 tItemRetry[itemID] = retry
153 if retry > 1 then print("ItemLoad:SetItemByID()",itemID,"Have lines:",count,"Looking for:",pattern,"1st line:",_G[TOOLTIP_ITEM .. "TextLeft" .. 1]:GetText(),retry) end
154 end
155 itemRetry = itemID
156 end
157 elseif type(pattern) == "string" then
158 local tooltipText = TOOLTIP_ITEM .. "TextLeft" .. 1
159 local heading = _G[tooltipText]:GetText()
160 if heading then -- look in 1st line
161 local compare = gsub(heading,pattern,"%1")
162 if compare and (compare ~= heading) and (compare ~= "") then
163 T_RECIPES_FIND[itemID] = {c,compare,zone,map,faction}
164 else
165 if VALIDATE then
166 local retry = tItemRetry[itemID] or 0
167 retry = retry + 1
168 tItemRetry[itemID] = retry
169 if retry > 1 then print("ItemLoad:SetItemByID() 1st line",itemID,"Looking for:",pattern,"Have:",heading,retry) end
170 end
171 itemRetry = itemID
172 end
173 end
174 end
175 else -- in normal case this code can't be reached if yes something is broken
176 if VALIDATE then
177 local retry = tItemRetry[itemID] or 0
178 retry = retry + 1
179 tItemRetry[itemID] = retry
180 if retry > 1 then print("ItemLoad() empty tooltip for",itemID,tItemRetry[itemID]) end
181 end
182 itemRetry = itemID
183 self.itemFrame = self:TooltipCreate(TOOLTIP_ITEM) -- empty tooltip I just throw out old one. Workaround for bad tooltip frame init damn Blizzard!
184 end
185 end
186 end
187 end
188 self:Profile(false)
189 if itemRetry then self:TimerFire("ItemLoad", P.TIMER_IDLE) end -- else if VALIDATE then for k,d in pairs(T_RECIPES_FIND) do print("ItemLoad:",k,"Pattern:",d[2]) end end end
190 self.itemLoad = true
191end
192local spellLoaded = {}
193local tSpellRetry = {}
194local T_SPELL_BY_NAME = NOP.T_SPELL_BY_NAME; assert(T_SPELL_BY_NAME ~= nil,'T_SPELL_BY_NAME')
195function NOP:SpellLoad() -- load spell patterns
196 local spellRetry = nil
197 self:Profile(true)
198 for itemID,data in pairs(T_SPELL_BY_NAME) do
199 if not spellLoaded[itemID] then -- not in local cache yet
200 local name = GetItemInfo(itemID) -- 1st fetch item into cache
201 if type(name) ~= 'string' or name == '' then
202 if VALIDATE then
203 local retry = tSpellRetry[itemID] or 0
204 retry = retry + 1
205 tSpellRetry[itemID] = retry
206 if retry > 1 then print("SpellLoad:GetItemInfo() empty for ",itemID,retry) end
207 end
208 spellRetry = itemID
209 else
210 local spell = GetItemSpell(itemID) -- now query if it has spell
211 if type(spell) == 'string' and spell ~= "" then
212 T_SPELL_FIND[spell] = data
213 spellLoaded[itemID] = spell
214 else -- in normal case this code can't be rached because tables are validated
215 if VALIDATE then print("GetItemSpell() no spell for",itemID, name, GetItemInfo(itemID)) end
216 spellRetry = itemID
217 end
218 end
219 else
220 local spell = spellLoaded[itemID]
221 T_SPELL_FIND[spell] = T_SPELL_BY_NAME[itemID]
222 end
223 end
224 self:Profile(false)
225 if spellRetry then self:TimerFire("SpellLoad", TIMER_IDLE) end -- it is even driven, but who trust Blizzard's API?
226 self.spellLoad = true
227end
228function NOP:PickLockUpdate() -- rogue picklocking
229 if IsPlayerSpell(SPELL_PICKLOCK) then -- have it in spellbook?
230 self.spellFrame:ClearLines() -- clean tooltip frame
231 self.spellFrame:SetSpellByID(SPELL_PICKLOCK) -- Fills the tooltip with information about a spell specified by ID
232 local count = self.spellFrame:NumLines()
233 if count > 3 then
234 local text = _G[P.TOOLTIP_SPELL .. "TextLeft" .. 4]:GetText() -- 4th line contains actual level of picklocking
235 if text and text ~= "" then
236 self.pickLockLevel = tonumber(string.match(text,"%d+")) -- /run local level = string.match("blabla 500.","%d+"); print(level)
237 if self.pickLockLevel then
238 self.pickLockSpell = GetSpellInfo(SPELL_PICKLOCK) -- save name for later use
239 else
240 print("Can't determine level of",GetSpellInfo(SPELL_PICKLOCK),"unexpected formating of tooltip!",text) -- diagnostic
241 end
242 end
243 else
244 self:Verbose("Tooltip has less lines than expected, has", count, "instead more than 3.") -- diagnostic
245 end
246 end
247end
248function NOP:PrintTooltip(tooltip) -- dump tooltip in chat frame
249 local name = tooltip:GetName()
250 for i=1,tooltip:NumLines() do -- scan all lines in tooltip
251 local leftText = _G[name .. "TextLeft" .. i]
252 local rightText = _G[name .. "TextRight" .. i]
253 if leftText and leftText.GetText then
254 local r,g,b,a = leftText:GetTextColor()
255 local line = leftText:GetText()
256 if line and line ~= "" then print(format("L %2d RGBA %3.3d %3.3d %3.3d %3.3d T %s",i,math.floor(r * 255 + 0.5),math.floor(g * 255 + 0.5),math.floor(b * 255 + 0.5),math.floor(a * 255 + 0.5), line)) end
257 end
258 if rightText and rightText.GetText then
259 local r,g,b,a = rightText:GetTextColor()
260 local line = rightText:GetText()
261 if line and line ~= "" then print(format("R %2d RGBA %3.3d %3.3d %3.3d %3.3d T %s",i,math.floor(r * 255 + 0.5),math.floor(g * 255 + 0.5),math.floor(b * 255 + 0.5),math.floor(a * 255 + 0.5), line)) end
262 end
263 end
264end
265function NOP:BlacklistClear() -- reset temporary blacklist
266 if not NOP.AceDB.profile.Skip and T_BLACKLIST and T_BLACKLIST[0] then -- have blacklisted items and is not session sticky, lets erase blacklist and check again
267 wipe(T_BLACKLIST) -- empty list
268 wipe(T_CHECK)
269 return true
270 end
271end
272function NOP:BlacklistReset() -- reset permanent blacklist
273 if (type(NOP.AceDB.profile.T_BLACKLIST) == "table") then
274 wipe(NOP.AceDB.profile.T_BLACKLIST)
275 else
276 NOP.AceDB.profile.T_BLACKLIST = {}
277 end
278 if (type(NOP.AceDB.profile.T_BLACKLIST_Q) == "table") then
279 wipe(NOP.AceDB.profile.T_BLACKLIST_Q)
280 else
281 NOP.AceDB.profile.T_BLACKLIST_Q = {}
282 end
283 wipe(T_CHECK)
284 self:BAG_UPDATE()
285end
286function NOP:BlacklistItem(isPermanent,itemID) -- right click will add item into blacklist
287 if itemID then
288 local name = GetItemInfo(itemID)
289 if isPermanent then
290 if not (type(NOP.AceDB.profile.T_BLACKLIST) == "table") then NOP.AceDB.profile.T_BLACKLIST = {} end
291 NOP.AceDB.profile.T_BLACKLIST[0] = true
292 NOP.AceDB.profile.T_BLACKLIST[itemID] = true
293 print(L["PERMA_BLACKLIST"],name or itemID)
294 else
295 if not (type(T_BLACKLIST) == "table") then T_BLACKLIST = {} end
296 T_BLACKLIST[0] = true -- blacklist is defined
297 T_BLACKLIST[itemID] = true
298 if NOP.AceDB.profile.Skip then
299 print(L["SESSION_BLACKLIST"],name or itemID)
300 else
301 print(L["TEMP_BLACKLIST"],name or itemID)
302 end
303 end
304 T_USE[itemID] = nil; T_CHECK[itemID] = nil
305 end
306end
307function NOP:Profile(onStart) -- time profiling
308 if not self.profileOn then return end
309 if not self.profileSession then self.profileSession = GetTime() end -- start of session
310 if onStart then
311 self.profileCount = (self.profileCount or 0) + 1
312 self.profileTP = debugprofilestop()
313 return
314 end
315 local elapsed = (debugprofilestop() - self.profileTP)
316 if self.profileMaxRun == nil or self.profileMaxRun < elapsed then self.profileMaxRun = elapsed end
317 self.profileTotal = (self.profileTotal or 0) + elapsed
318end
319function NOP:inCombat() -- combat lockdown
320 return InCombatLockdown()
321end
322function NOP:SecondsToString(s) -- return delta, time-string
323 local nH = math.floor(s/3600)
324 local nM = math.floor(s/60 - nH*60)
325 local nS = math.floor(s - nH*3600 - nM*60)
326 if nH > 0 then return 30,string.format("%d",nH) .. ":" .. string.format("%02d",nM); end
327 if nM > 0 then return 5,string.format("%d",nM) .. ":" .. string.format("%02d",nS); end
328 if s > 9.9 then return 1,string.format("%.0f",s); end
329 return 0.1, string.format("%.1f",s)
330end
331function NOP:removekey(t, key) -- remove item in hash table by key
332 if t and key and (type(t) == "table") and (t[key] ~= nil) then
333 local element = t[key]
334 t[key] = nil
335 return element
336 end
337 return nil
338end
339local HERALD_ANNOUNCED = {}
340function NOP:CheckBuilding(toCheck) -- recheck (force request landing page) and annonce
341 if not NOP.AceDB.profile.herald then return end
342 if toCheck then C_Garrison.RequestLandingPageShipmentInfo(); return; end
343 if C_Garrison.HasGarrison(LE_GARRISON_TYPE_6_0) then -- garrison shipments
344 local buildings = C_Garrison.GetBuildings(LE_GARRISON_TYPE_6_0)
345 local numBuildings = #buildings
346 if(numBuildings > 0) then
347 for i = 1, numBuildings do
348 local buildingID = buildings[i].buildingID;
349 if buildingID and not HERALD_ANNOUNCED[buildingID] then
350 local name, _, _, shipmentsReady, shipmentsTotal = C_Garrison.GetLandingPageShipmentInfo(buildingID)
351 if name and shipmentsReady and shipmentsTotal and (shipmentsReady / shipmentsTotal) > WORK_ANNOUNCE then
352 self:PrintToActive((TOGO_ANNOUNCE):format(name,shipmentsReady,shipmentsTotal-shipmentsReady))
353 HERALD_ANNOUNCED[buildingID] = true
354 end
355 end
356 end
357 end
358 end
359 if C_Garrison.HasGarrison(LE_GARRISON_TYPE_7_0) then -- Order hall
360 local followerShipments = C_Garrison.GetFollowerShipments(LE_GARRISON_TYPE_7_0) -- troops ready
361 if followerShipments then
362 for i = 1, #followerShipments do
363 if not HERALD_ANNOUNCED[followerShipments[i]] then
364 local name, _, _, shipmentsReady, shipmentsTotal = C_Garrison.GetLandingPageShipmentInfoByContainerID(followerShipments[i])
365 if name and shipmentsReady and shipmentsTotal and (shipmentsReady / shipmentsTotal) > WORK_ANNOUNCE then
366 self:PrintToActive((TOGO_ANNOUNCE):format(name,shipmentsReady,shipmentsTotal-shipmentsReady))
367 HERALD_ANNOUNCED[followerShipments[i]] = true
368 end
369 end
370 end
371 end
372 local looseShipments = C_Garrison.GetLooseShipments(LE_GARRISON_TYPE_7_0) -- research
373 if looseShipments then
374 for i = 1, #looseShipments do
375 if not HERALD_ANNOUNCED[looseShipments[i]] then
376 local name, _, _, shipmentsReady, shipmentsTotal = C_Garrison.GetLandingPageShipmentInfoByContainerID(looseShipments[i])
377 if name and shipmentsReady and shipmentsTotal and (shipmentsReady / shipmentsTotal) > WORK_ANNOUNCE then
378 self:PrintToActive((TOGO_ANNOUNCE):format(name,shipmentsReady,shipmentsTotal-shipmentsReady))
379 HERALD_ANNOUNCED[looseShipments[i]] = true
380 end
381 end
382 end
383 end
384 local talentTrees = C_Garrison.GetTalentTreeIDsByClassID(LE_GARRISON_TYPE_7_0, select(3, UnitClass("player"))) -- orderhall talents
385 if talentTrees then
386 local completeTalentID = C_Garrison.GetCompleteTalent(LE_GARRISON_TYPE_7_0)
387 if completeTalentID and not HERALD_ANNOUNCED[completeTalentID] then
388 for treeIndex, treeID in ipairs(talentTrees) do
389 local treeInfo = C_Garrison.GetTalentTreeInfo(treeID)
390 for talentIndex, talent in ipairs(treeInfo.talents) do
391 if (talent.id == completeTalentID) then
392 self:PrintToActive((TALENT_ANNOUNCE):format(talent.name))
393 HERALD_ANNOUNCED[completeTalentID] = true
394 end
395 end
396 end
397 end
398 for treeIndex, treeID in ipairs(talentTrees) do
399 local treeInfo = C_Garrison.GetTalentTreeInfo(treeID)
400 for talentIndex, talent in ipairs(treeInfo.talents) do
401 if talent.selected and not HERALD_ANNOUNCED[talent.perkSpellID] and NOP.T_INSTA_WQ[talent.perkSpellID] then
402 local ability = GetSpellInfo(talent.perkSpellID) -- spell name
403 local _, duration = GetSpellCooldown(talent.perkSpellID)
404 local count = GetItemCount(NOP.T_INSTA_WQ[talent.perkSpellID])
405 local name = GetItemInfo(NOP.T_INSTA_WQ[talent.perkSpellID])
406 if duration == 0 and name then
407 local txt = " " .. RGB_RED .. ERR_SPELL_FAILED_REAGENTS_GENERIC .. " " .. RGB_YELLOW .. name
408 self:PrintToActive((TALENT_ANNOUNCE):format(ability) .. ((count == 0) and txt or ""))
409 HERALD_ANNOUNCED[talent.perkSpellID] = true
410 end
411 end
412 end
413 end
414 end
415 end -- /run local _, _, t = C_Garrison.GetTalentTreeInfoForID(119); for a,b in ipairs(t) do print(a, b.selected, b.perkSpellID) end
416 for i = 1, GetNumArchaeologyRaces() do -- archaelogy can be completed
417 local raceName, _, _, have, required = GetArchaeologyRaceInfo(i)
418 if raceName and (required > 0) and (have >= required) and not HERALD_ANNOUNCED[raceName] then
419 self:PrintToActive((ARCHAELOGY_ANNOUNCE):format(raceName))
420 HERALD_ANNOUNCED[raceName] = true
421 end
422 end
423 if not HERALD_ANNOUNCED["shipyard"] then -- shipyard missing ships
424 local activeShips, maxShips = C_Garrison.GetNumFollowers(LE_FOLLOWER_TYPE_SHIPYARD_6_2), 0
425 local _,_,_,_,_,shipyardRank = C_Garrison.GetOwnedBuildingInfo(98)
426 if shipyardRank == 1 then
427 maxShips = 6
428 elseif shipyardRank == 2 then
429 maxShips = 8
430 elseif shipyardRank == 3 then
431 maxShips = 10
432 end
433 if maxShips > 0 then
434 if activeShips < maxShips then
435 self:PrintToActive((SHIPYARD_ANNOUNCE):format(activeShips,maxShips))
436 HERALD_ANNOUNCED["shipyard"] = true
437 end
438 end
439 end
440 ExpandAllFactionHeaders()
441 local nF = GetNumFactions()
442 local paragon = {}
443 for i=1, nF do
444 local name, _, _, _, _, value, _, _, header, _, _, _, _, id = GetFactionInfo(i)
445 if name and not header and id then
446 if C_Reputation.IsFactionParagon(id) then
447 local reward = false
448 local top
449 value, top, _, reward = C_Reputation.GetFactionParagonInfo(id)
450 while (value and top and (value > top)) do value = value - top end
451 if reward and not HERALD_ANNOUNCED[id] then
452 table.insert(paragon,name)
453 HERALD_ANNOUNCED[id] = true
454 end
455 end
456 end
457 end
458 if #paragon > 0 then self:PrintToActive((REWARD_ANNOUNCE):format(table.concat(paragon,PLAYER_LIST_DELIMITER))) end
459end
460function NOP:PrintToActive(msg) -- print to all active chat windows
461 local ElvUI = _G.ElvUI
462 if msg then
463 local txt = ("|cff7f7f7f%s|r [|cff007f7f%s|r]" .. " %s"):format(ElvUI and "" or ("[" .. date("%H:%M") .. "]"),ADDON,msg)
464 for i = 1, NUM_CHAT_WINDOWS do
465 local name, fontSize, r, g, b, alpha, shown, locked, docked, uninteractable = GetChatWindowInfo(i)
466 if shown and _G["ChatFrame"..i] then
467 _G["ChatFrame"..i]:AddMessage(txt)
468 end
469 end
470 end
471end
472function NOP:CompressText(text) -- printable
473 text = string.gsub(text, "\n", "/n") -- novy radek
474 text = string.gsub(text, "/n$", "") -- novy radek na konci zahodit
475 text = string.gsub(text, "||", "/124") -- interni formatovani WoW
476 return string.trim(text)
477end
478function NOP:GetReputation(name) -- reputation standing with paragon reward check
479 local fID = T_REPS[name]; if not fID then return end
480 local _, _, level, _, top, value = GetFactionInfoByID(fID)
481 local reward
482 if C_Reputation.IsFactionParagon(fID) then _, _, _, reward = C_Reputation.GetFactionParagonInfo(fID) end
483 return level, top, value, reward
484end
485local T_timers = {}
486function NOP:TimerFire(name,period,...) -- timer without overlap
487 if not (type(period) == 'number' and period > 0) then whoCalls('Period must be a number greater than zero ' .. period); return; end
488 if not (T_timers[name] and (self:TimeLeft(T_timers[name]) > 0)) then T_timers[name] = self:ScheduleTimer(name,period,...) end -- schedule when timer with this name is not running
489end
490function NOP:TimerCancel(name) -- cancel timer by name
491 local timer = T_timers[name]
492 if (timer and (self:TimeLeft(timer) > 0)) then self:CancelTimer(timer) end
493end