yellowfive@57
|
1 AskMrRobot = LibStub("AceAddon-3.0"):NewAddon("AskMrRobot", "AceEvent-3.0", "AceComm-3.0", "AceConsole-3.0", "AceSerializer-3.0")
|
yellowfive@57
|
2 local Amr = AskMrRobot
|
yellowfive@57
|
3 Amr.Serializer = LibStub("AskMrRobot-Serializer")
|
yellowfive@57
|
4
|
yellowfive@57
|
5 Amr.ADDON_NAME = "AskMrRobot"
|
yellowfive@57
|
6
|
yellowfive@57
|
7 -- types of inter-addon messages that we receive, used to parcel them out to the proper handlers
|
yellowfive@57
|
8 Amr.MessageTypes = {
|
yellowfive@57
|
9 Version = "_V",
|
yellowfive@124
|
10 VersionRequest = "_VR"
|
yellowfive@57
|
11 }
|
yellowfive@57
|
12
|
yellowfive@57
|
13 local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
|
yellowfive@57
|
14 local AceGUI = LibStub("AceGUI-3.0")
|
yellowfive@57
|
15
|
yellowfive@57
|
16 -- minimap icon and LDB support
|
yellowfive@57
|
17 local _amrLDB = LibStub("LibDataBroker-1.1"):NewDataObject(Amr.ADDON_NAME, {
|
yellowfive@57
|
18 type = "launcher",
|
yellowfive@57
|
19 text = "Ask Mr. Robot",
|
yellowfive@57
|
20 icon = "Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\icon",
|
yellowfive@57
|
21 OnClick = function(self, button, down)
|
yellowfive@57
|
22 if button == "LeftButton" then
|
yellowfive@124
|
23 --if IsControlKeyDown() then
|
yellowfive@124
|
24 -- Amr:Wipe()
|
yellowfive@124
|
25 --else
|
yellowfive@57
|
26 Amr:Toggle()
|
yellowfive@124
|
27 --end
|
yellowfive@57
|
28 elseif button == "RightButton" then
|
yellowfive@57
|
29 Amr:EquipGearSet()
|
yellowfive@57
|
30 end
|
yellowfive@57
|
31 end,
|
yellowfive@57
|
32 OnTooltipShow = function(tt)
|
yellowfive@57
|
33 tt:AddLine("Ask Mr. Robot", 1, 1, 1);
|
yellowfive@57
|
34 tt:AddLine(" ");
|
yellowfive@57
|
35 tt:AddLine(L.MinimapTooltip)
|
yellowfive@57
|
36 end
|
yellowfive@57
|
37 })
|
yellowfive@57
|
38 local _icon = LibStub("LibDBIcon-1.0")
|
yellowfive@57
|
39
|
yellowfive@57
|
40
|
yellowfive@57
|
41 -- initialize the database
|
yellowfive@57
|
42 local function initializeDb()
|
yellowfive@57
|
43
|
yellowfive@57
|
44 local defaults = {
|
yellowfive@139
|
45 char = {
|
yellowfive@139
|
46 LastVersion = 0, -- used to clean out old stuff
|
yellowfive@129
|
47 FirstUse = true, -- true if this is first time use, gets cleared after seeing the export help splash window
|
yellowfive@129
|
48 Talents = {}, -- for each spec, selected talents
|
yellowfive@185
|
49 Soulbinds = {}, -- selected nodes in each soulbind tree for this character
|
yellowfive@185
|
50 ActiveSoulbinds = {}, -- for each spec, active soulbind
|
yellowfive@185
|
51 UnlockedConduits = {}, -- unlocked conduits for this character
|
yellowfive@129
|
52 Equipped = {}, -- for each spec, slot id to item info
|
yellowfive@129
|
53 BagItems = {}, -- list of item info for bags
|
yellowfive@129
|
54 BankItems = {}, -- list of item info for bank
|
yellowfive@129
|
55 BagItemsAndCounts = {}, -- used mainly for the shopping list
|
Yellowfive@197
|
56 BankItemsAndCounts = {}, -- used mainly for the shopping list
|
Yellowfive@197
|
57 GreatVaultItems = {}, -- available weekly rewards from the great vault
|
yellowfive@139
|
58 GearSetups = {}, -- imported gear sets
|
yellowfive@161
|
59 JunkData = {}, -- imported data about items that can be vendored/scrapped/disenchanted
|
yellowfive@129
|
60 ExtraEnchantData = {}, -- enchant id to enchant display information and material information
|
yellowfive@129
|
61 Logging = { -- character logging settings
|
yellowfive@129
|
62 Enabled = false, -- whether logging is currently on or not
|
yellowfive@129
|
63 LastZone = nil, -- last zone the player was in
|
yellowfive@129
|
64 LastDiff = nil, -- last difficulty for the last zone the player was in
|
yellowfive@129
|
65 LastWipe = nil -- last time a wipe was called by this player
|
yellowfive@129
|
66 }
|
yellowfive@129
|
67 },
|
yellowfive@57
|
68 profile = {
|
yellowfive@57
|
69 minimap = { -- minimap hide/show and position settings
|
yellowfive@57
|
70 hide = false
|
yellowfive@57
|
71 },
|
yellowfive@57
|
72 window = {}, -- main window position settings
|
yellowfive@57
|
73 shopWindow = {}, -- shopping list window position settings
|
yellowfive@161
|
74 junkWindow = {}, -- junk list window position settings
|
yellowfive@57
|
75 options = {
|
yellowfive@57
|
76 autoGear = false, -- auto-equip saved gear sets when changing specs
|
yellowfive@161
|
77 junkVendor = false, -- auto-show junk list at vendor/scrapper
|
yellowfive@61
|
78 shopAh = false, -- auto-show shopping list at AH
|
yellowfive@91
|
79 disableEm = false, -- disable auto-creation of equipment manager sets
|
yellowfive@61
|
80 uiScale = 1 -- custom scale for AMR UI
|
yellowfive@57
|
81 },
|
yellowfive@57
|
82 Logging = { -- global logging settings
|
yellowfive@57
|
83 Auto = {} -- for each instanceId, for each difficultyId, true if auto-logging enabled
|
yellowfive@57
|
84 }
|
yellowfive@57
|
85 },
|
yellowfive@57
|
86 global = {
|
yellowfive@57
|
87 Region = nil, -- region that this user is in, all characters on the same account should be the same region
|
yellowfive@139
|
88 Shopping2 = {}, -- shopping list data stored globally for access on any character
|
yellowfive@57
|
89 Logging = { -- a lot of log data is stored globally for simplicity, can only be raiding with one character at a time
|
yellowfive@57
|
90 Wipes = {}, -- times that a wipe was called
|
yellowfive@57
|
91 PlayerData = {}, -- player data gathered at fight start
|
yellowfive@57
|
92 PlayerExtras = {} -- player extra data like auras, gathered at fight start
|
yellowfive@57
|
93 }
|
yellowfive@57
|
94 }
|
yellowfive@57
|
95 }
|
yellowfive@57
|
96
|
yellowfive@124
|
97 Amr.db = LibStub("AceDB-3.0"):New("AskMrRobotDb4", defaults)
|
yellowfive@110
|
98
|
yellowfive@110
|
99 -- set defaults for auto logging; if a new zone is added and some other stuff was turned on, turn on the new zone too
|
yellowfive@110
|
100 local hasSomeLogging = false
|
yellowfive@110
|
101 local addedLogging = {}
|
yellowfive@57
|
102 for i, instanceId in ipairs(Amr.InstanceIdsOrdered) do
|
yellowfive@110
|
103 local byDiff = Amr.db.profile.Logging.Auto[instanceId]
|
yellowfive@57
|
104 if not byDiff then
|
yellowfive@57
|
105 byDiff = {}
|
yellowfive@110
|
106 Amr.db.profile.Logging.Auto[instanceId] = byDiff
|
yellowfive@110
|
107 addedLogging[instanceId] = byDiff
|
yellowfive@57
|
108 end
|
yellowfive@57
|
109
|
yellowfive@57
|
110 for k, difficultyId in pairs(Amr.Difficulties) do
|
yellowfive@110
|
111 if not byDiff[difficultyId] then
|
yellowfive@57
|
112 byDiff[difficultyId] = false
|
yellowfive@110
|
113 else
|
yellowfive@110
|
114 hasSomeLogging = true
|
yellowfive@57
|
115 end
|
yellowfive@57
|
116 end
|
yellowfive@137
|
117 end
|
yellowfive@137
|
118
|
yellowfive@137
|
119 for k,v in pairs(Amr.db.profile.Logging.Auto) do
|
yellowfive@137
|
120 if not Amr.IsSupportedInstanceId(k) then
|
yellowfive@137
|
121 Amr.db.profile.Logging.Auto[k] = nil
|
yellowfive@137
|
122 end
|
yellowfive@57
|
123 end
|
yellowfive@57
|
124
|
yellowfive@110
|
125 if hasSomeLogging then
|
yellowfive@110
|
126 for instanceId, byDiff in pairs(addedLogging) do
|
yellowfive@110
|
127 for k, difficultyId in pairs(Amr.Difficulties) do
|
yellowfive@110
|
128 byDiff[difficultyId] = true
|
yellowfive@110
|
129 end
|
yellowfive@110
|
130 end
|
yellowfive@110
|
131 end
|
yellowfive@110
|
132
|
yellowfive@139
|
133 -- upgrade old gear set info to new format
|
yellowfive@139
|
134 if Amr.db.char.GearSets then
|
yellowfive@139
|
135 Amr.db.char.GearSets = nil
|
yellowfive@139
|
136 end
|
yellowfive@139
|
137
|
yellowfive@139
|
138 if not Amr.db.char.GearSetups then
|
yellowfive@139
|
139 Amr.db.char.GearSetups = {}
|
yellowfive@139
|
140 end
|
yellowfive@139
|
141
|
yellowfive@139
|
142 if Amr.db.global.Shopping then
|
yellowfive@139
|
143 Amr.db.global.Shopping = nil
|
yellowfive@139
|
144 end
|
yellowfive@57
|
145
|
yellowfive@57
|
146 Amr.db.RegisterCallback(Amr, "OnProfileChanged", "RefreshConfig")
|
yellowfive@57
|
147 Amr.db.RegisterCallback(Amr, "OnProfileCopied", "RefreshConfig")
|
yellowfive@57
|
148 Amr.db.RegisterCallback(Amr, "OnProfileReset", "RefreshConfig")
|
yellowfive@57
|
149 end
|
yellowfive@57
|
150
|
yellowfive@57
|
151 function Amr:OnInitialize()
|
yellowfive@57
|
152
|
yellowfive@57
|
153 initializeDb()
|
yellowfive@57
|
154
|
yellowfive@57
|
155 Amr:RegisterChatCommand("amr", "SlashCommand")
|
yellowfive@57
|
156
|
yellowfive@57
|
157 _icon:Register(Amr.ADDON_NAME, _amrLDB, self.db.profile.minimap)
|
yellowfive@57
|
158
|
yellowfive@57
|
159 -- listen for inter-addon communication
|
yellowfive@139
|
160 self:RegisterComm(Amr.ChatPrefix, "OnCommReceived")
|
yellowfive@57
|
161 end
|
yellowfive@57
|
162
|
yellowfive@57
|
163 local _enteredWorld = false
|
yellowfive@57
|
164 local _pendingInit = false
|
yellowfive@57
|
165
|
yellowfive@139
|
166 -- upgrade some stuff from old to new formats
|
yellowfive@139
|
167 local function upgradeFromOld()
|
yellowfive@139
|
168
|
yellowfive@139
|
169 local currentVersion = tonumber(GetAddOnMetadata(Amr.ADDON_NAME, "Version"))
|
yellowfive@139
|
170 if Amr.db.char.LastVersion < 65 then
|
yellowfive@143
|
171
|
yellowfive@143
|
172 if not Amr.db.profile.options.disableEm then
|
yellowfive@143
|
173 for i = 1,GetNumSpecializations() do
|
yellowfive@143
|
174 local _, specName = GetSpecializationInfo(i)
|
yellowfive@143
|
175 if specName then
|
yellowfive@143
|
176 local setid = C_EquipmentSet.GetEquipmentSetID("AMR " .. specName)
|
yellowfive@143
|
177 if setid then
|
yellowfive@143
|
178 C_EquipmentSet.DeleteEquipmentSet(setid)
|
yellowfive@143
|
179 end
|
yellowfive@139
|
180 end
|
yellowfive@139
|
181 end
|
yellowfive@139
|
182 end
|
yellowfive@143
|
183
|
yellowfive@139
|
184 end
|
yellowfive@139
|
185 Amr.db.char.LastVersion = currentVersion
|
yellowfive@139
|
186
|
yellowfive@139
|
187 end
|
yellowfive@139
|
188
|
yellowfive@133
|
189 local function finishInitialize()
|
yellowfive@57
|
190
|
yellowfive@57
|
191 -- record region, the only thing that we still can't get from the log file
|
yellowfive@57
|
192 Amr.db.global.Region = Amr.RegionNames[GetCurrentRegion()]
|
yellowfive@57
|
193
|
yellowfive@57
|
194 -- make sure that some initialization is deferred until after PLAYER_ENTERING_WORLD event so that data we need is available;
|
yellowfive@57
|
195 -- also delay this initialization for a few extra seconds to deal with some event spam that is otherwise hard to identify and ignore when a player logs in
|
yellowfive@57
|
196 Amr.Wait(5, function()
|
yellowfive@145
|
197 --Amr:InitializeVersions()
|
yellowfive@57
|
198 Amr:InitializeGear()
|
yellowfive@57
|
199 Amr:InitializeExport()
|
yellowfive@57
|
200 Amr:InitializeCombatLog()
|
yellowfive@139
|
201
|
yellowfive@139
|
202 upgradeFromOld()
|
yellowfive@57
|
203 end)
|
yellowfive@57
|
204 end
|
yellowfive@57
|
205
|
yellowfive@133
|
206 local function onPlayerEnteringWorld()
|
yellowfive@57
|
207
|
yellowfive@57
|
208 _enteredWorld = true
|
yellowfive@57
|
209
|
yellowfive@57
|
210 if _pendingInit then
|
yellowfive@57
|
211 finishInitialize()
|
yellowfive@57
|
212 _pendingInit = false
|
yellowfive@57
|
213 end
|
yellowfive@57
|
214 end
|
yellowfive@57
|
215
|
yellowfive@57
|
216 function Amr:OnEnable()
|
yellowfive@57
|
217
|
yellowfive@81
|
218 --[[
|
yellowfive@57
|
219 -- listen for changes to the snapshot enable state, and always make sure it is enabled if using the core AskMrRobot addon
|
yellowfive@57
|
220 self:RegisterMessage("AMR_SNAPSHOT_STATE_CHANGED", function(eventName, isEnabled)
|
yellowfive@57
|
221 if not isEnabled then
|
yellowfive@57
|
222 -- immediately re-enable on any attempt to disable
|
yellowfive@57
|
223 Amr.Serializer:EnableSnapshots()
|
yellowfive@57
|
224 end
|
yellowfive@57
|
225 end)
|
yellowfive@57
|
226 self.Serializer:EnableSnapshots()
|
yellowfive@81
|
227 ]]
|
yellowfive@57
|
228
|
yellowfive@57
|
229 -- update based on current configuration whenever enabled
|
yellowfive@57
|
230 self:RefreshConfig()
|
yellowfive@57
|
231
|
yellowfive@57
|
232 -- if we have fully entered the world, do initialization; otherwise wait for PLAYER_ENTERING_WORLD to continue
|
yellowfive@57
|
233 if not _enteredWorld then
|
yellowfive@57
|
234 _pendingInit = true
|
yellowfive@57
|
235 else
|
yellowfive@57
|
236 _pendingInit = false
|
yellowfive@57
|
237 finishInitialize()
|
yellowfive@57
|
238 end
|
yellowfive@57
|
239 end
|
yellowfive@57
|
240
|
yellowfive@57
|
241 function Amr:OnDisable()
|
yellowfive@57
|
242 -- disabling is not supported
|
yellowfive@57
|
243 end
|
yellowfive@57
|
244
|
yellowfive@179
|
245 local function onEnterCombat()
|
yellowfive@179
|
246 Amr:Hide()
|
yellowfive@179
|
247 Amr:HideShopWindow()
|
yellowfive@179
|
248 Amr:HideJunkWindow()
|
yellowfive@179
|
249 end
|
yellowfive@179
|
250
|
yellowfive@57
|
251
|
yellowfive@57
|
252 ----------------------------------------------------------------------------------------
|
yellowfive@57
|
253 -- Slash Commands
|
yellowfive@57
|
254 ----------------------------------------------------------------------------------------
|
yellowfive@57
|
255 local _slashMethods = {
|
yellowfive@57
|
256 hide = "Hide",
|
yellowfive@57
|
257 show = "Show",
|
yellowfive@57
|
258 toggle = "Toggle",
|
yellowfive@124
|
259 equip = "EquipGearSet",
|
yellowfive@57
|
260 version = "PrintVersions",
|
yellowfive@161
|
261 junk = "ShowJunkWindow",
|
yellowfive@124
|
262 --wipe = "Wipe",
|
yellowfive@124
|
263 --undowipe = "UndoWipe",
|
yellowfive@61
|
264 reset = "Reset",
|
yellowfive@57
|
265 test = "Test"
|
yellowfive@57
|
266 }
|
yellowfive@57
|
267
|
yellowfive@57
|
268 function Amr:SlashCommand(input)
|
yellowfive@57
|
269 input = string.lower(input)
|
yellowfive@57
|
270 local parts = {}
|
yellowfive@57
|
271 for w in input:gmatch("%S+") do
|
yellowfive@57
|
272 table.insert(parts, w)
|
yellowfive@57
|
273 end
|
yellowfive@57
|
274
|
yellowfive@57
|
275 if #parts == 0 then return end
|
yellowfive@57
|
276
|
yellowfive@57
|
277 local func = _slashMethods[parts[1]]
|
yellowfive@57
|
278 if not func then return end
|
yellowfive@57
|
279
|
yellowfive@57
|
280 local funcArgs = {}
|
yellowfive@57
|
281 for i = 2, #parts do
|
yellowfive@57
|
282 table.insert(funcArgs, parts[i])
|
yellowfive@57
|
283 end
|
yellowfive@57
|
284
|
yellowfive@57
|
285 Amr[func](Amr, unpack(funcArgs))
|
yellowfive@57
|
286 end
|
yellowfive@57
|
287
|
yellowfive@57
|
288
|
yellowfive@57
|
289 ----------------------------------------------------------------------------------------
|
yellowfive@57
|
290 -- Configuration
|
yellowfive@57
|
291 ----------------------------------------------------------------------------------------
|
yellowfive@57
|
292
|
yellowfive@57
|
293 -- refresh all state based on the current values of configuration options
|
yellowfive@57
|
294 function Amr:RefreshConfig()
|
yellowfive@57
|
295
|
yellowfive@57
|
296 self:UpdateMinimap()
|
yellowfive@57
|
297 self:RefreshOptionsUi()
|
yellowfive@57
|
298 self:RefreshLogUi()
|
yellowfive@57
|
299 end
|
yellowfive@57
|
300
|
yellowfive@57
|
301 function Amr:UpdateMinimap()
|
yellowfive@57
|
302
|
yellowfive@57
|
303 if self.db.profile.minimap.hide or not Amr:IsEnabled() then
|
yellowfive@57
|
304 _icon:Hide(Amr.ADDON_NAME)
|
yellowfive@57
|
305 else
|
yellowfive@57
|
306 -- change icon color if logging
|
yellowfive@57
|
307 if Amr:IsLogging() then
|
yellowfive@57
|
308 _amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon_green'
|
yellowfive@57
|
309 else
|
yellowfive@57
|
310 _amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon'
|
yellowfive@57
|
311 end
|
yellowfive@57
|
312
|
yellowfive@57
|
313 _icon:Show(Amr.ADDON_NAME)
|
yellowfive@57
|
314 end
|
yellowfive@57
|
315 end
|
yellowfive@57
|
316
|
yellowfive@57
|
317
|
yellowfive@57
|
318 ----------------------------------------------------------------------------------------
|
yellowfive@57
|
319 -- Version Checking
|
yellowfive@57
|
320 ----------------------------------------------------------------------------------------
|
yellowfive@57
|
321
|
yellowfive@57
|
322 -- version of addon being run by each person in the player's raid or group
|
yellowfive@57
|
323 Amr.GroupVersions = {}
|
yellowfive@57
|
324
|
yellowfive@57
|
325 local function toGroupVersionKey(realm, name)
|
yellowfive@57
|
326 realm = string.gsub(realm, "%s+", "")
|
yellowfive@57
|
327 return name .. "-" .. realm
|
yellowfive@57
|
328 end
|
yellowfive@57
|
329
|
yellowfive@57
|
330 -- prune out version information for players no longer in the current raid group
|
yellowfive@57
|
331 local function pruneVersionInfo()
|
yellowfive@57
|
332
|
yellowfive@57
|
333 local newVersions = {}
|
yellowfive@57
|
334 local units = Amr:GetGroupUnitIdentifiers()
|
yellowfive@57
|
335
|
yellowfive@57
|
336 for i, unitId in ipairs(units) do
|
yellowfive@57
|
337 local realm, name = Amr:GetRealmAndName(unitId)
|
yellowfive@57
|
338 if realm then
|
yellowfive@57
|
339 local key = toGroupVersionKey(realm, name)
|
yellowfive@57
|
340 newVersions[key] = Amr.GroupVersions[key]
|
yellowfive@57
|
341 end
|
yellowfive@57
|
342 end
|
yellowfive@57
|
343
|
yellowfive@57
|
344 Amr.GroupVersions = newVersions
|
yellowfive@57
|
345 end
|
yellowfive@57
|
346
|
yellowfive@57
|
347 -- send version information to other people in the same raid group
|
yellowfive@57
|
348 local function sendVersionInfo()
|
yellowfive@57
|
349
|
yellowfive@57
|
350 local realm = GetRealmName()
|
yellowfive@57
|
351 local name = UnitName("player")
|
yellowfive@57
|
352 local ver = GetAddOnMetadata(Amr.ADDON_NAME, "Version")
|
yellowfive@57
|
353
|
yellowfive@57
|
354 local msg = string.format("%s\n%s\n%s\n%s", Amr.MessageTypes.Version, realm, name, ver)
|
yellowfive@57
|
355 Amr:SendAmrCommMessage(msg)
|
yellowfive@57
|
356 end
|
yellowfive@57
|
357
|
yellowfive@57
|
358 local function onVersionInfoReceived(message)
|
yellowfive@57
|
359
|
yellowfive@57
|
360 -- message will be of format: realm\nname\nversion
|
yellowfive@57
|
361 local parts = {}
|
yellowfive@57
|
362 for part in string.gmatch(message, "([^\n]+)") do
|
yellowfive@57
|
363 table.insert(parts, part)
|
yellowfive@57
|
364 end
|
yellowfive@57
|
365
|
yellowfive@57
|
366 local key = toGroupVersionKey(parts[2], parts[3])
|
yellowfive@57
|
367 local ver = parts[4]
|
yellowfive@57
|
368
|
yellowfive@57
|
369 Amr.GroupVersions[key] = tonumber(ver)
|
yellowfive@57
|
370
|
yellowfive@57
|
371 -- make sure that versions are properly pruned in case this message arrived late and the player has since been removed from the group
|
yellowfive@57
|
372 pruneVersionInfo()
|
yellowfive@57
|
373 end
|
yellowfive@57
|
374
|
yellowfive@57
|
375 -- get the addon version another person in the player's raid/group is running, or 0 if they are not running the addon
|
yellowfive@57
|
376 function Amr:GetAddonVersion(realm, name)
|
yellowfive@57
|
377 local ver = Amr.GroupVersions[toGroupVersionKey(realm, name)]
|
yellowfive@57
|
378 return ver or 0
|
yellowfive@57
|
379 end
|
yellowfive@57
|
380
|
yellowfive@57
|
381 function Amr:PrintVersions()
|
yellowfive@57
|
382
|
yellowfive@57
|
383 if not IsInGroup() and not IsInRaid() then
|
yellowfive@57
|
384 self:Print(L.VersionChatNotGrouped)
|
yellowfive@57
|
385 return
|
yellowfive@57
|
386 end
|
yellowfive@57
|
387
|
yellowfive@57
|
388 local units = self:GetGroupUnitIdentifiers()
|
yellowfive@57
|
389
|
yellowfive@57
|
390 local msg = {}
|
yellowfive@57
|
391 table.insert(msg, L.VersionChatTitle)
|
yellowfive@57
|
392
|
yellowfive@57
|
393 for i, unitId in ipairs(units) do
|
yellowfive@57
|
394 local realm, name = self:GetRealmAndName(unitId)
|
yellowfive@57
|
395 if realm then
|
yellowfive@57
|
396 local key = toGroupVersionKey(realm, name)
|
yellowfive@57
|
397 local ver = Amr.GroupVersions[key]
|
yellowfive@57
|
398 if not ver then
|
yellowfive@57
|
399 table.insert(msg, key .. " |cFFFF0000" .. L.VersionChatNotInstalled .. "|r")
|
yellowfive@57
|
400 else
|
yellowfive@57
|
401 table.insert(msg, key .. " v" .. ver)
|
yellowfive@57
|
402 end
|
yellowfive@57
|
403 end
|
yellowfive@57
|
404 end
|
yellowfive@57
|
405
|
yellowfive@57
|
406 msg = table.concat(msg, "\n")
|
yellowfive@57
|
407 print(msg)
|
yellowfive@57
|
408 end
|
yellowfive@57
|
409
|
yellowfive@57
|
410 function Amr:InitializeVersions()
|
yellowfive@57
|
411 Amr:AddEventHandler("GROUP_ROSTER_UPDATE", pruneVersionInfo)
|
yellowfive@57
|
412 Amr:AddEventHandler("GROUP_ROSTER_UPDATE", sendVersionInfo)
|
yellowfive@57
|
413
|
yellowfive@57
|
414 -- request version information from anyone in my group upon initialization
|
yellowfive@57
|
415 if IsInGroup() or IsInRaid() then
|
yellowfive@57
|
416 Amr:SendAmrCommMessage(Amr.MessageTypes.VersionRequest)
|
yellowfive@57
|
417 end
|
yellowfive@57
|
418 end
|
yellowfive@57
|
419
|
yellowfive@57
|
420
|
yellowfive@57
|
421 ----------------------------------------------------------------------------------------
|
yellowfive@57
|
422 -- Generic Helpers
|
yellowfive@57
|
423 ----------------------------------------------------------------------------------------
|
yellowfive@57
|
424
|
yellowfive@57
|
425 local _waitTable = {}
|
yellowfive@57
|
426 local _waitFrame = nil
|
yellowfive@57
|
427
|
yellowfive@57
|
428 -- execute the specified function after the specified delay (in seconds)
|
yellowfive@57
|
429 function Amr.Wait(delay, func, ...)
|
yellowfive@57
|
430 if not _waitFrame then
|
yellowfive@57
|
431 _waitFrame = CreateFrame("Frame", "AmrWaitFrame", UIParent)
|
yellowfive@57
|
432 _waitFrame:SetScript("OnUpdate", function (self, elapse)
|
yellowfive@57
|
433 local count = #_waitTable
|
yellowfive@57
|
434 local i = 1
|
yellowfive@57
|
435 while(i <= count) do
|
yellowfive@57
|
436 local waitRecord = table.remove(_waitTable, i)
|
yellowfive@57
|
437 local d = table.remove(waitRecord, 1)
|
yellowfive@57
|
438 local f = table.remove(waitRecord, 1)
|
yellowfive@57
|
439 local p = table.remove(waitRecord, 1)
|
yellowfive@57
|
440 if d > elapse then
|
yellowfive@57
|
441 table.insert(_waitTable, i, { d-elapse, f, p })
|
yellowfive@57
|
442 i = i + 1
|
yellowfive@57
|
443 else
|
yellowfive@57
|
444 count = count - 1
|
yellowfive@57
|
445 f(unpack(p))
|
yellowfive@57
|
446 end
|
yellowfive@57
|
447 end
|
yellowfive@57
|
448 end)
|
yellowfive@57
|
449 end
|
yellowfive@57
|
450 table.insert(_waitTable, { delay, func, {...} })
|
yellowfive@57
|
451 return true
|
yellowfive@57
|
452 end
|
yellowfive@57
|
453
|
yellowfive@57
|
454 -- helper to iterate over a table in order by its keys
|
yellowfive@57
|
455 function Amr.spairs(t, order)
|
yellowfive@57
|
456 -- collect the keys
|
yellowfive@57
|
457 local keys = {}
|
yellowfive@57
|
458 for k in pairs(t) do keys[#keys+1] = k end
|
yellowfive@57
|
459
|
yellowfive@57
|
460 -- if order function given, sort by it by passing the table and keys a, b,
|
yellowfive@57
|
461 -- otherwise just sort the keys
|
yellowfive@57
|
462 if order then
|
yellowfive@57
|
463 table.sort(keys, function(a,b) return order(t, a, b) end)
|
yellowfive@57
|
464 else
|
yellowfive@57
|
465 table.sort(keys)
|
yellowfive@57
|
466 end
|
yellowfive@57
|
467
|
yellowfive@57
|
468 -- return the iterator function
|
yellowfive@57
|
469 local i = 0
|
yellowfive@57
|
470 return function()
|
yellowfive@57
|
471 i = i + 1
|
yellowfive@57
|
472 if keys[i] then
|
yellowfive@57
|
473 return keys[i], t[keys[i]]
|
yellowfive@57
|
474 end
|
yellowfive@57
|
475 end
|
yellowfive@57
|
476 end
|
yellowfive@57
|
477
|
yellowfive@57
|
478 function Amr.StartsWith(str, prefix)
|
yellowfive@57
|
479 if string.len(str) < string.len(prefix) then return false end
|
yellowfive@57
|
480 return string.sub(str, 1, string.len(prefix)) == prefix
|
yellowfive@57
|
481 end
|
yellowfive@57
|
482
|
yellowfive@124
|
483 function Amr.IsEmpty(table)
|
yellowfive@124
|
484 return next(table) == nil
|
yellowfive@124
|
485 end
|
yellowfive@124
|
486
|
yellowfive@124
|
487 function Amr.Contains(table, value)
|
yellowfive@124
|
488 if not table then return false end
|
yellowfive@124
|
489 for k,v in pairs(table) do
|
yellowfive@124
|
490 if v == value then
|
yellowfive@124
|
491 return true
|
yellowfive@124
|
492 end
|
yellowfive@124
|
493 end
|
yellowfive@124
|
494 return false
|
yellowfive@124
|
495 end
|
yellowfive@124
|
496
|
yellowfive@57
|
497 -- helper to get the unit identifiers (e.g. to pass to GetUnitName) for all members of the player's current group/raid
|
yellowfive@57
|
498 function Amr:GetGroupUnitIdentifiers()
|
yellowfive@57
|
499
|
yellowfive@57
|
500 local units = {}
|
yellowfive@57
|
501 if IsInRaid() then
|
yellowfive@57
|
502 for i = 1,40 do
|
yellowfive@57
|
503 table.insert(units, "raid" .. i)
|
yellowfive@57
|
504 end
|
yellowfive@57
|
505 elseif IsInGroup() then
|
yellowfive@57
|
506 table.insert(units, "player")
|
yellowfive@57
|
507 for i = 1,4 do
|
yellowfive@57
|
508 table.insert(units, "party" .. i)
|
yellowfive@57
|
509 end
|
yellowfive@57
|
510 else
|
yellowfive@57
|
511 table.insert(units, "player")
|
yellowfive@57
|
512 end
|
yellowfive@57
|
513
|
yellowfive@57
|
514 return units
|
yellowfive@57
|
515 end
|
yellowfive@57
|
516
|
yellowfive@57
|
517 -- helper to get the realm and name from a unitId (e.g. "player" or "raid1")
|
yellowfive@57
|
518 function Amr:GetRealmAndName(unitId)
|
yellowfive@57
|
519
|
yellowfive@57
|
520 local name = GetUnitName(unitId, true)
|
yellowfive@57
|
521 if not name then return end
|
yellowfive@57
|
522
|
yellowfive@57
|
523 local realm = GetRealmName()
|
yellowfive@57
|
524 local splitPos = string.find(name, "-")
|
yellowfive@57
|
525 if splitPos ~= nil then
|
yellowfive@57
|
526 realm = string.sub(name, splitPos + 1)
|
yellowfive@57
|
527 name = string.sub(name, 1, splitPos - 1)
|
yellowfive@57
|
528 end
|
yellowfive@57
|
529
|
yellowfive@57
|
530 return realm, name
|
yellowfive@57
|
531 end
|
yellowfive@57
|
532
|
yellowfive@57
|
533 -- find the unitid of a player given the name and realm... this comes from the server so the realm will be in english...
|
yellowfive@57
|
534 -- TODO: more robust handling of players with same name but different realms in the same group on non-english clients
|
yellowfive@57
|
535 function Amr:GetUnitId(unitRealm, unitName)
|
yellowfive@57
|
536
|
yellowfive@57
|
537 local nameMatches = {}
|
yellowfive@57
|
538
|
yellowfive@57
|
539 local units = Amr:GetGroupUnitIdentifiers()
|
yellowfive@57
|
540 for i, unitId in ipairs(units) do
|
yellowfive@57
|
541 local realm, name = Amr:GetRealmAndName(unitId)
|
yellowfive@57
|
542 if realm then
|
yellowfive@57
|
543 -- remove spaces to ensure proper matches
|
yellowfive@57
|
544 realm = string.gsub(realm, "%s+", "")
|
yellowfive@57
|
545 unitRealm = string.gsub(unitRealm, "%s+", "")
|
yellowfive@57
|
546
|
yellowfive@57
|
547 if unitRealm == realm and unitName == name then return unitId end
|
yellowfive@57
|
548 if unitName == name then
|
yellowfive@57
|
549 table.insert(nameMatches, unitId)
|
yellowfive@57
|
550 end
|
yellowfive@57
|
551 end
|
yellowfive@57
|
552 end
|
yellowfive@57
|
553
|
yellowfive@57
|
554 -- only one player with same name, must be the player of interest
|
yellowfive@57
|
555 if #nameMatches == 1 then return nameMatches[1] end
|
yellowfive@57
|
556
|
yellowfive@57
|
557 -- could not find or ambiguous
|
yellowfive@57
|
558 return nil
|
yellowfive@57
|
559 end
|
yellowfive@57
|
560
|
yellowfive@133
|
561 --[[
|
yellowfive@57
|
562 -- search the tooltip for txt, returns true if it is encountered on any line
|
yellowfive@57
|
563 function Amr:IsTextInTooltip(tt, txt)
|
yellowfive@57
|
564 local regions = { tt:GetRegions() }
|
yellowfive@57
|
565 for i, region in ipairs(regions) do
|
yellowfive@57
|
566 if region and region:GetObjectType() == "FontString" then
|
yellowfive@57
|
567 if region:GetText() == txt then
|
yellowfive@57
|
568 return true
|
yellowfive@57
|
569 end
|
yellowfive@57
|
570 end
|
yellowfive@57
|
571 end
|
yellowfive@57
|
572 return false
|
yellowfive@57
|
573 end
|
yellowfive@133
|
574 ]]
|
yellowfive@57
|
575
|
yellowfive@124
|
576 -- helper to determine if we can equip an item (it is soulbound)
|
yellowfive@59
|
577 function Amr:CanEquip(bagId, slotId)
|
yellowfive@124
|
578 local item = Item:CreateFromBagAndSlot(bagId, slotId)
|
yellowfive@124
|
579 if item then
|
yellowfive@124
|
580 local loc = item:GetItemLocation()
|
yellowfive@124
|
581 return C_Item.IsBound(loc)
|
yellowfive@124
|
582 else
|
yellowfive@124
|
583 -- for now just return true if we can't find the item... will get an error trying to equip if it isn't bound
|
yellowfive@124
|
584 return true
|
yellowfive@124
|
585 end
|
yellowfive@124
|
586
|
yellowfive@124
|
587 --local tt = Amr.GetItemTooltip(bagId, slotId)
|
yellowfive@124
|
588 --if self:IsTextInTooltip(tt, ITEM_SOULBOUND) then return true end
|
yellowfive@124
|
589 --if self:IsTextInTooltip(tt, ITEM_BNETACCOUNTBOUND) then return true end
|
yellowfive@124
|
590 --if self:IsTextInTooltip(tt, ITEM_ACCOUNTBOUND) then return true end
|
yellowfive@57
|
591 end
|
yellowfive@57
|
592
|
yellowfive@57
|
593 -- helper to determine if an item has a unique constraint
|
yellowfive@133
|
594 --[[
|
yellowfive@57
|
595 function Amr:IsUnique(bagId, slotId)
|
yellowfive@81
|
596 local tt = Amr.GetItemTooltip(bagId, slotId)
|
yellowfive@57
|
597 if self:IsTextInTooltip(tt, ITEM_UNIQUE_EQUIPPABLE) then return true end
|
yellowfive@57
|
598 if self:IsTextInTooltip(tt, ITEM_UNIQUE) then return true end
|
yellowfive@57
|
599 return false
|
yellowfive@57
|
600 end
|
yellowfive@133
|
601 ]]
|
yellowfive@57
|
602
|
yellowfive@57
|
603
|
yellowfive@57
|
604 ----------------------------------------------------------------------------------------
|
yellowfive@57
|
605 -- Inter-Addon Communication
|
yellowfive@57
|
606 ----------------------------------------------------------------------------------------
|
yellowfive@57
|
607 function Amr:SendAmrCommMessage(message, channel)
|
yellowfive@57
|
608 -- prepend version to all messages
|
yellowfive@57
|
609 local v = GetAddOnMetadata(Amr.ADDON_NAME, "Version")
|
yellowfive@57
|
610 message = v .. "\r" .. message
|
yellowfive@57
|
611
|
yellowfive@79
|
612 Amr:SendCommMessage(Amr.ChatPrefix, message, channel or (IsInGroup(LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT" or "RAID"))
|
yellowfive@57
|
613 end
|
yellowfive@57
|
614
|
yellowfive@57
|
615 function Amr:OnCommReceived(prefix, message, distribution, sender)
|
yellowfive@57
|
616
|
yellowfive@57
|
617 local parts = {}
|
yellowfive@57
|
618 for part in string.gmatch(message, "([^\r]+)") do
|
yellowfive@57
|
619 table.insert(parts, part)
|
yellowfive@57
|
620 end
|
yellowfive@57
|
621
|
yellowfive@57
|
622 local ver = parts[1]
|
yellowfive@57
|
623 if ver then ver = tonumber(ver) end
|
yellowfive@57
|
624 if ver then
|
yellowfive@57
|
625 -- newest versions of the addon start all messages with a version number
|
yellowfive@57
|
626 message = parts[2]
|
yellowfive@57
|
627 end
|
yellowfive@57
|
628
|
yellowfive@57
|
629 -- we always allow version checks, even from old versions of the addon that aren't otherwise compatible
|
yellowfive@57
|
630 if Amr.StartsWith(message, Amr.MessageTypes.Version) then
|
yellowfive@57
|
631 -- version checking between group members
|
yellowfive@57
|
632 if Amr.StartsWith(message, Amr.MessageTypes.VersionRequest) then
|
yellowfive@57
|
633 sendVersionInfo()
|
yellowfive@57
|
634 else
|
yellowfive@57
|
635 onVersionInfoReceived(message)
|
yellowfive@57
|
636 end
|
yellowfive@57
|
637
|
yellowfive@57
|
638 return
|
yellowfive@57
|
639 end
|
yellowfive@57
|
640
|
yellowfive@57
|
641 -- any other kind of message is ignored if the version is too old
|
yellowfive@57
|
642 if not ver or ver < Amr.MIN_ADDON_VERSION then return end
|
yellowfive@57
|
643
|
yellowfive@124
|
644 --[[
|
yellowfive@57
|
645 if Amr.StartsWith(message, Amr.MessageTypes.Team) then
|
yellowfive@57
|
646 -- if fully initialized, process team optimizer messages
|
yellowfive@57
|
647 if Amr["ProcessTeamMessage"] then
|
yellowfive@57
|
648 Amr:ProcessTeamMessage(message)
|
yellowfive@57
|
649 end
|
yellowfive@57
|
650 else
|
yellowfive@57
|
651 -- if we are fully loaded, process a player snapshot when it is received (combat logging)
|
yellowfive@57
|
652 if Amr["ProcessPlayerSnapshot"] then
|
yellowfive@57
|
653 self:ProcessPlayerSnapshot(message)
|
yellowfive@57
|
654 end
|
yellowfive@57
|
655 end
|
yellowfive@124
|
656 ]]
|
yellowfive@57
|
657 end
|
yellowfive@57
|
658
|
yellowfive@57
|
659
|
yellowfive@57
|
660 ----------------------------------------------------------------------------------------
|
yellowfive@57
|
661 -- Events
|
yellowfive@57
|
662 ----------------------------------------------------------------------------------------
|
yellowfive@57
|
663 local _eventHandlers = {}
|
yellowfive@57
|
664
|
yellowfive@57
|
665 local function handleEvent(eventName, ...)
|
yellowfive@57
|
666 local list = _eventHandlers[eventName]
|
yellowfive@57
|
667 if list then
|
yellowfive@57
|
668 --print(eventName .. " handled")
|
yellowfive@57
|
669 for i, handler in ipairs(list) do
|
yellowfive@57
|
670 if type(handler) == "function" then
|
yellowfive@57
|
671 handler(select(1, ...))
|
yellowfive@57
|
672 else
|
yellowfive@57
|
673 Amr[handler](Amr, select(1, ...))
|
yellowfive@57
|
674 end
|
yellowfive@57
|
675 end
|
yellowfive@57
|
676 end
|
yellowfive@57
|
677 end
|
yellowfive@57
|
678
|
yellowfive@57
|
679 -- WoW and Ace seem to work on a "one handler" kind of approach to events (as far as I can tell from the sparse documentation of both).
|
yellowfive@57
|
680 -- This is a simple wrapper to allow adding multiple handlers to the same event, thus allowing better encapsulation of code from file to file.
|
yellowfive@57
|
681 function Amr:AddEventHandler(eventName, methodOrName)
|
yellowfive@57
|
682 local list = _eventHandlers[eventName]
|
yellowfive@57
|
683 if not list then
|
yellowfive@57
|
684 list = {}
|
yellowfive@57
|
685 _eventHandlers[eventName] = list
|
yellowfive@57
|
686 Amr:RegisterEvent(eventName, handleEvent)
|
yellowfive@57
|
687 end
|
yellowfive@57
|
688 table.insert(list, methodOrName)
|
yellowfive@57
|
689 end
|
yellowfive@57
|
690
|
yellowfive@57
|
691 Amr:AddEventHandler("PLAYER_ENTERING_WORLD", onPlayerEnteringWorld)
|
yellowfive@57
|
692
|
yellowfive@179
|
693 Amr:AddEventHandler("PLAYER_REGEN_DISABLED", onEnterCombat)
|
yellowfive@57
|
694
|
yellowfive@57
|
695 ----------------------------------------------------------------------------------------
|
yellowfive@57
|
696 -- Debugging
|
yellowfive@57
|
697 ----------------------------------------------------------------------------------------
|
yellowfive@124
|
698 function Amr:dump(o)
|
yellowfive@124
|
699 if type(o) == 'table' then
|
yellowfive@124
|
700 local s = '{ '
|
yellowfive@124
|
701 for k,v in pairs(o) do
|
yellowfive@124
|
702 if type(k) ~= 'number' then k = '"'..k..'"' end
|
yellowfive@124
|
703 s = s .. '['..k..'] = ' .. Amr:dump(v) .. ','
|
yellowfive@124
|
704 end
|
yellowfive@124
|
705 return s .. '} '
|
yellowfive@124
|
706 else
|
yellowfive@124
|
707 return tostring(o)
|
yellowfive@124
|
708 end
|
yellowfive@124
|
709 end
|
yellowfive@124
|
710
|
yellowfive@69
|
711 function Amr:Test()
|
yellowfive@124
|
712
|
yellowfive@185
|
713 --local item = Item:CreateFromItemID(171416)
|
yellowfive@185
|
714 --local blah = C_LegendaryCrafting.GetRuneforgePowers(item:GetItemLocation())
|
yellowfive@185
|
715
|
yellowfive@185
|
716 --local info = C_LegendaryCrafting.GetRuneforgePowerInfo(30)
|
yellowfive@185
|
717
|
yellowfive@185
|
718 --print(Amr:dump(info))
|
Yellowfive@197
|
719
|
yellowfive@69
|
720 end
|