Mercurial > wow > askmrrobot
comparison Core.lua @ 57:01b63b8ed811 v21
total rewrite to version 21
| author | yellowfive | 
|---|---|
| date | Fri, 05 Jun 2015 11:05:15 -0700 | 
| parents | |
| children | ee701ce45354 | 
   comparison
  equal
  deleted
  inserted
  replaced
| 56:75431c084aa0 | 57:01b63b8ed811 | 
|---|---|
| 1 -- AskMrRobot | |
| 2 -- Does cool stuff associated with askmrrobot.com: | |
| 3 -- Import/Export gear and optimization solutions from/to the website | |
| 4 -- Improve the combat logging experience and augment it with extra data not available directly in the log file | |
| 5 -- Team Optimizer convenience functionality | |
| 6 | |
| 7 AskMrRobot = LibStub("AceAddon-3.0"):NewAddon("AskMrRobot", "AceEvent-3.0", "AceComm-3.0", "AceConsole-3.0", "AceSerializer-3.0") | |
| 8 local Amr = AskMrRobot | |
| 9 Amr.Serializer = LibStub("AskMrRobot-Serializer") | |
| 10 | |
| 11 Amr.ADDON_NAME = "AskMrRobot" | |
| 12 | |
| 13 -- types of inter-addon messages that we receive, used to parcel them out to the proper handlers | |
| 14 Amr.MessageTypes = { | |
| 15 Version = "_V", | |
| 16 VersionRequest = "_VR", | |
| 17 Team = "_T" | |
| 18 } | |
| 19 | |
| 20 local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true) | |
| 21 local AceGUI = LibStub("AceGUI-3.0") | |
| 22 | |
| 23 -- minimap icon and LDB support | |
| 24 local _amrLDB = LibStub("LibDataBroker-1.1"):NewDataObject(Amr.ADDON_NAME, { | |
| 25 type = "launcher", | |
| 26 text = "Ask Mr. Robot", | |
| 27 icon = "Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\icon", | |
| 28 OnClick = function(self, button, down) | |
| 29 if button == "LeftButton" then | |
| 30 if IsControlKeyDown() then | |
| 31 Amr:Wipe() | |
| 32 else | |
| 33 Amr:Toggle() | |
| 34 end | |
| 35 elseif button == "RightButton" then | |
| 36 Amr:EquipGearSet() | |
| 37 end | |
| 38 end, | |
| 39 OnTooltipShow = function(tt) | |
| 40 tt:AddLine("Ask Mr. Robot", 1, 1, 1); | |
| 41 tt:AddLine(" "); | |
| 42 tt:AddLine(L.MinimapTooltip) | |
| 43 end | |
| 44 }) | |
| 45 local _icon = LibStub("LibDBIcon-1.0") | |
| 46 | |
| 47 | |
| 48 -- initialize the database | |
| 49 local function initializeDb() | |
| 50 | |
| 51 local defaults = { | |
| 52 char = { | |
| 53 FirstUse = true, -- true if this is first time use, gets cleared after seeing the export help splash window | |
| 54 SubSpecs = {}, -- last seen subspecs for this character, used to deal with some ambiguous specs | |
| 55 Equipped = {}, -- for each spec group (1 or 2), slot id to item link | |
| 56 BagItems = {}, -- list of item links for bag | |
| 57 BankItems = {}, -- list of item links for bank | |
| 58 VoidItems = {}, -- list of item links for void storage | |
| 59 BagItemsAndCounts = {}, -- used mainly for the shopping list | |
| 60 BankItemsAndCounts = {}, -- used mainly for the shopping list | |
| 61 GearSets = {}, -- imported gear sets, key by spec group (1 or 2), slot id to item object | |
| 62 ExtraItemData = {}, -- for each spec group (1 or 2): mainly for legacy support, item id to object with socketColor and duplicateId information | |
| 63 ExtraGemData = {}, -- for each spec group (1 or 2): gem enchant id to gem display information, and data used to detect identical gems (mainly for legacy support) | |
| 64 ExtraEnchantData = {}, -- for each spec group (1 or 2): enchant id to enchant display information and material information | |
| 65 Logging = { -- character logging settings | |
| 66 Enabled = false, -- whether logging is currently on or not | |
| 67 LastZone = nil, -- last zone the player was in | |
| 68 LastDiff = nil, -- last difficulty for the last zone the player was in | |
| 69 LastWipe = nil -- last time a wipe was called by this player | |
| 70 }, | |
| 71 TeamOpt = { | |
| 72 AllItems = {}, -- all equippable items no matter where it is, list of item unique ids, used to determine when a player gains a new equippable item | |
| 73 History = {}, -- history of drops since joining the current group | |
| 74 Rolls = {}, -- current loot choices for a loot distribution in progress | |
| 75 Role = nil, -- Leader or Member, changes UI to the mode most appropriate for this user | |
| 76 Loot = {}, -- the last loot seen by the master looter | |
| 77 LootGuid = nil, -- guid of the last unit looted by the master looter, will be "container" if there is no target | |
| 78 LootInProgress = false -- true if looting is currently in progress | |
| 79 } | |
| 80 }, | |
| 81 profile = { | |
| 82 minimap = { -- minimap hide/show and position settings | |
| 83 hide = false | |
| 84 }, | |
| 85 window = {}, -- main window position settings | |
| 86 lootWindow = {}, -- loot window position settings | |
| 87 shopWindow = {}, -- shopping list window position settings | |
| 88 options = { | |
| 89 autoGear = false, -- auto-equip saved gear sets when changing specs | |
| 90 shopAh = false -- auto-show shopping list at AH | |
| 91 }, | |
| 92 Logging = { -- global logging settings | |
| 93 Auto = {} -- for each instanceId, for each difficultyId, true if auto-logging enabled | |
| 94 } | |
| 95 }, | |
| 96 global = { | |
| 97 Region = nil, -- region that this user is in, all characters on the same account should be the same region | |
| 98 Shopping = {}, -- shopping list data stored globally for access on any character | |
| 99 Logging = { -- a lot of log data is stored globally for simplicity, can only be raiding with one character at a time | |
| 100 Wipes = {}, -- times that a wipe was called | |
| 101 PlayerData = {}, -- player data gathered at fight start | |
| 102 PlayerExtras = {} -- player extra data like auras, gathered at fight start | |
| 103 }, | |
| 104 TeamOpt = { -- this stuff is stored globally in case a player e.g. switches to an alt in a raid group | |
| 105 LootGear = {}, -- gear info that needs to be transmitted with the next loot | |
| 106 Rankings = {}, -- last rankings imported by the loot ranker | |
| 107 RankingString = nil -- last ranking string imported, kept around for efficient serialization | |
| 108 } | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 -- set defaults for auto-logging | |
| 113 for i, instanceId in ipairs(Amr.InstanceIdsOrdered) do | |
| 114 local byDiff = defaults.profile.Logging.Auto[instanceId] | |
| 115 if not byDiff then | |
| 116 byDiff = {} | |
| 117 defaults.profile.Logging.Auto[instanceId] = byDiff | |
| 118 end | |
| 119 | |
| 120 for k, difficultyId in pairs(Amr.Difficulties) do | |
| 121 if byDiff[difficultyId] == nil then | |
| 122 byDiff[difficultyId] = false | |
| 123 end | |
| 124 end | |
| 125 end | |
| 126 | |
| 127 Amr.db = LibStub("AceDB-3.0"):New("AskMrRobotDb2", defaults) | |
| 128 | |
| 129 Amr.db.RegisterCallback(Amr, "OnProfileChanged", "RefreshConfig") | |
| 130 Amr.db.RegisterCallback(Amr, "OnProfileCopied", "RefreshConfig") | |
| 131 Amr.db.RegisterCallback(Amr, "OnProfileReset", "RefreshConfig") | |
| 132 end | |
| 133 | |
| 134 function Amr:OnInitialize() | |
| 135 | |
| 136 initializeDb() | |
| 137 | |
| 138 Amr:RegisterChatCommand("amr", "SlashCommand") | |
| 139 | |
| 140 _icon:Register(Amr.ADDON_NAME, _amrLDB, self.db.profile.minimap) | |
| 141 | |
| 142 -- listen for inter-addon communication | |
| 143 self:RegisterComm(Amr.ChatPrefix, "OnCommReceived") | |
| 144 end | |
| 145 | |
| 146 local _enteredWorld = false | |
| 147 local _pendingInit = false | |
| 148 | |
| 149 function finishInitialize() | |
| 150 | |
| 151 -- record region, the only thing that we still can't get from the log file | |
| 152 Amr.db.global.Region = Amr.RegionNames[GetCurrentRegion()] | |
| 153 | |
| 154 -- make sure that some initialization is deferred until after PLAYER_ENTERING_WORLD event so that data we need is available; | |
| 155 -- 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 | |
| 156 Amr.Wait(5, function() | |
| 157 Amr:InitializeVersions() | |
| 158 Amr:InitializeGear() | |
| 159 Amr:InitializeExport() | |
| 160 Amr:InitializeCombatLog() | |
| 161 Amr:InitializeTeamOpt() | |
| 162 end) | |
| 163 end | |
| 164 | |
| 165 function onPlayerEnteringWorld() | |
| 166 | |
| 167 _enteredWorld = true | |
| 168 | |
| 169 if _pendingInit then | |
| 170 finishInitialize() | |
| 171 _pendingInit = false | |
| 172 end | |
| 173 end | |
| 174 | |
| 175 function Amr:OnEnable() | |
| 176 | |
| 177 -- listen for changes to the snapshot enable state, and always make sure it is enabled if using the core AskMrRobot addon | |
| 178 self:RegisterMessage("AMR_SNAPSHOT_STATE_CHANGED", function(eventName, isEnabled) | |
| 179 if not isEnabled then | |
| 180 -- immediately re-enable on any attempt to disable | |
| 181 Amr.Serializer:EnableSnapshots() | |
| 182 end | |
| 183 end) | |
| 184 self.Serializer:EnableSnapshots() | |
| 185 | |
| 186 -- update based on current configuration whenever enabled | |
| 187 self:RefreshConfig() | |
| 188 | |
| 189 -- if we have fully entered the world, do initialization; otherwise wait for PLAYER_ENTERING_WORLD to continue | |
| 190 if not _enteredWorld then | |
| 191 _pendingInit = true | |
| 192 else | |
| 193 _pendingInit = false | |
| 194 finishInitialize() | |
| 195 end | |
| 196 end | |
| 197 | |
| 198 function Amr:OnDisable() | |
| 199 -- disabling is not supported | |
| 200 end | |
| 201 | |
| 202 | |
| 203 ---------------------------------------------------------------------------------------- | |
| 204 -- Slash Commands | |
| 205 ---------------------------------------------------------------------------------------- | |
| 206 local _slashMethods = { | |
| 207 hide = "Hide", | |
| 208 show = "Show", | |
| 209 toggle = "Toggle", | |
| 210 equip = "EquipGearSet", -- parameter is "primary" or "secondary", or no parameter to toggle | |
| 211 version = "PrintVersions", | |
| 212 wipe = "Wipe", | |
| 213 undowipe = "UndoWipe", | |
| 214 test = "Test" | |
| 215 } | |
| 216 | |
| 217 function Amr:SlashCommand(input) | |
| 218 input = string.lower(input) | |
| 219 local parts = {} | |
| 220 for w in input:gmatch("%S+") do | |
| 221 table.insert(parts, w) | |
| 222 end | |
| 223 | |
| 224 if #parts == 0 then return end | |
| 225 | |
| 226 local func = _slashMethods[parts[1]] | |
| 227 if not func then return end | |
| 228 | |
| 229 local funcArgs = {} | |
| 230 for i = 2, #parts do | |
| 231 table.insert(funcArgs, parts[i]) | |
| 232 end | |
| 233 | |
| 234 Amr[func](Amr, unpack(funcArgs)) | |
| 235 end | |
| 236 | |
| 237 | |
| 238 ---------------------------------------------------------------------------------------- | |
| 239 -- Configuration | |
| 240 ---------------------------------------------------------------------------------------- | |
| 241 | |
| 242 -- refresh all state based on the current values of configuration options | |
| 243 function Amr:RefreshConfig() | |
| 244 | |
| 245 self:UpdateMinimap() | |
| 246 self:RefreshOptionsUi() | |
| 247 self:RefreshLogUi() | |
| 248 end | |
| 249 | |
| 250 function Amr:UpdateMinimap() | |
| 251 | |
| 252 if self.db.profile.minimap.hide or not Amr:IsEnabled() then | |
| 253 _icon:Hide(Amr.ADDON_NAME) | |
| 254 else | |
| 255 -- change icon color if logging | |
| 256 if Amr:IsLogging() then | |
| 257 _amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon_green' | |
| 258 else | |
| 259 _amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon' | |
| 260 end | |
| 261 | |
| 262 _icon:Show(Amr.ADDON_NAME) | |
| 263 end | |
| 264 end | |
| 265 | |
| 266 | |
| 267 ---------------------------------------------------------------------------------------- | |
| 268 -- Version Checking | |
| 269 ---------------------------------------------------------------------------------------- | |
| 270 | |
| 271 -- version of addon being run by each person in the player's raid or group | |
| 272 Amr.GroupVersions = {} | |
| 273 | |
| 274 local function toGroupVersionKey(realm, name) | |
| 275 realm = string.gsub(realm, "%s+", "") | |
| 276 return name .. "-" .. realm | |
| 277 end | |
| 278 | |
| 279 -- prune out version information for players no longer in the current raid group | |
| 280 local function pruneVersionInfo() | |
| 281 | |
| 282 local newVersions = {} | |
| 283 local units = Amr:GetGroupUnitIdentifiers() | |
| 284 | |
| 285 for i, unitId in ipairs(units) do | |
| 286 local realm, name = Amr:GetRealmAndName(unitId) | |
| 287 if realm then | |
| 288 local key = toGroupVersionKey(realm, name) | |
| 289 newVersions[key] = Amr.GroupVersions[key] | |
| 290 end | |
| 291 end | |
| 292 | |
| 293 Amr.GroupVersions = newVersions | |
| 294 end | |
| 295 | |
| 296 -- send version information to other people in the same raid group | |
| 297 local function sendVersionInfo() | |
| 298 | |
| 299 local realm = GetRealmName() | |
| 300 local name = UnitName("player") | |
| 301 local ver = GetAddOnMetadata(Amr.ADDON_NAME, "Version") | |
| 302 | |
| 303 local msg = string.format("%s\n%s\n%s\n%s", Amr.MessageTypes.Version, realm, name, ver) | |
| 304 Amr:SendAmrCommMessage(msg) | |
| 305 end | |
| 306 | |
| 307 local function onVersionInfoReceived(message) | |
| 308 | |
| 309 -- message will be of format: realm\nname\nversion | |
| 310 local parts = {} | |
| 311 for part in string.gmatch(message, "([^\n]+)") do | |
| 312 table.insert(parts, part) | |
| 313 end | |
| 314 | |
| 315 local key = toGroupVersionKey(parts[2], parts[3]) | |
| 316 local ver = parts[4] | |
| 317 | |
| 318 Amr.GroupVersions[key] = tonumber(ver) | |
| 319 | |
| 320 -- make sure that versions are properly pruned in case this message arrived late and the player has since been removed from the group | |
| 321 pruneVersionInfo() | |
| 322 end | |
| 323 | |
| 324 -- get the addon version another person in the player's raid/group is running, or 0 if they are not running the addon | |
| 325 function Amr:GetAddonVersion(realm, name) | |
| 326 local ver = Amr.GroupVersions[toGroupVersionKey(realm, name)] | |
| 327 return ver or 0 | |
| 328 end | |
| 329 | |
| 330 function Amr:PrintVersions() | |
| 331 | |
| 332 if not IsInGroup() and not IsInRaid() then | |
| 333 self:Print(L.VersionChatNotGrouped) | |
| 334 return | |
| 335 end | |
| 336 | |
| 337 local units = self:GetGroupUnitIdentifiers() | |
| 338 | |
| 339 local msg = {} | |
| 340 table.insert(msg, L.VersionChatTitle) | |
| 341 | |
| 342 for i, unitId in ipairs(units) do | |
| 343 local realm, name = self:GetRealmAndName(unitId) | |
| 344 if realm then | |
| 345 local key = toGroupVersionKey(realm, name) | |
| 346 local ver = Amr.GroupVersions[key] | |
| 347 if not ver then | |
| 348 table.insert(msg, key .. " |cFFFF0000" .. L.VersionChatNotInstalled .. "|r") | |
| 349 else | |
| 350 table.insert(msg, key .. " v" .. ver) | |
| 351 end | |
| 352 end | |
| 353 end | |
| 354 | |
| 355 msg = table.concat(msg, "\n") | |
| 356 print(msg) | |
| 357 end | |
| 358 | |
| 359 function Amr:InitializeVersions() | |
| 360 Amr:AddEventHandler("GROUP_ROSTER_UPDATE", pruneVersionInfo) | |
| 361 Amr:AddEventHandler("GROUP_ROSTER_UPDATE", sendVersionInfo) | |
| 362 | |
| 363 -- request version information from anyone in my group upon initialization | |
| 364 if IsInGroup() or IsInRaid() then | |
| 365 Amr:SendAmrCommMessage(Amr.MessageTypes.VersionRequest) | |
| 366 end | |
| 367 end | |
| 368 | |
| 369 | |
| 370 ---------------------------------------------------------------------------------------- | |
| 371 -- Generic Helpers | |
| 372 ---------------------------------------------------------------------------------------- | |
| 373 | |
| 374 local _waitTable = {} | |
| 375 local _waitFrame = nil | |
| 376 | |
| 377 -- execute the specified function after the specified delay (in seconds) | |
| 378 function Amr.Wait(delay, func, ...) | |
| 379 if not _waitFrame then | |
| 380 _waitFrame = CreateFrame("Frame", "AmrWaitFrame", UIParent) | |
| 381 _waitFrame:SetScript("OnUpdate", function (self, elapse) | |
| 382 local count = #_waitTable | |
| 383 local i = 1 | |
| 384 while(i <= count) do | |
| 385 local waitRecord = table.remove(_waitTable, i) | |
| 386 local d = table.remove(waitRecord, 1) | |
| 387 local f = table.remove(waitRecord, 1) | |
| 388 local p = table.remove(waitRecord, 1) | |
| 389 if d > elapse then | |
| 390 table.insert(_waitTable, i, { d-elapse, f, p }) | |
| 391 i = i + 1 | |
| 392 else | |
| 393 count = count - 1 | |
| 394 f(unpack(p)) | |
| 395 end | |
| 396 end | |
| 397 end) | |
| 398 end | |
| 399 table.insert(_waitTable, { delay, func, {...} }) | |
| 400 return true | |
| 401 end | |
| 402 | |
| 403 -- helper to iterate over a table in order by its keys | |
| 404 function Amr.spairs(t, order) | |
| 405 -- collect the keys | |
| 406 local keys = {} | |
| 407 for k in pairs(t) do keys[#keys+1] = k end | |
| 408 | |
| 409 -- if order function given, sort by it by passing the table and keys a, b, | |
| 410 -- otherwise just sort the keys | |
| 411 if order then | |
| 412 table.sort(keys, function(a,b) return order(t, a, b) end) | |
| 413 else | |
| 414 table.sort(keys) | |
| 415 end | |
| 416 | |
| 417 -- return the iterator function | |
| 418 local i = 0 | |
| 419 return function() | |
| 420 i = i + 1 | |
| 421 if keys[i] then | |
| 422 return keys[i], t[keys[i]] | |
| 423 end | |
| 424 end | |
| 425 end | |
| 426 | |
| 427 function Amr.StartsWith(str, prefix) | |
| 428 if string.len(str) < string.len(prefix) then return false end | |
| 429 return string.sub(str, 1, string.len(prefix)) == prefix | |
| 430 end | |
| 431 | |
| 432 -- helper to get the unit identifiers (e.g. to pass to GetUnitName) for all members of the player's current group/raid | |
| 433 function Amr:GetGroupUnitIdentifiers() | |
| 434 | |
| 435 local units = {} | |
| 436 if IsInRaid() then | |
| 437 for i = 1,40 do | |
| 438 table.insert(units, "raid" .. i) | |
| 439 end | |
| 440 elseif IsInGroup() then | |
| 441 table.insert(units, "player") | |
| 442 for i = 1,4 do | |
| 443 table.insert(units, "party" .. i) | |
| 444 end | |
| 445 else | |
| 446 table.insert(units, "player") | |
| 447 end | |
| 448 | |
| 449 return units | |
| 450 end | |
| 451 | |
| 452 -- helper to get the realm and name from a unitId (e.g. "player" or "raid1") | |
| 453 function Amr:GetRealmAndName(unitId) | |
| 454 | |
| 455 local name = GetUnitName(unitId, true) | |
| 456 if not name then return end | |
| 457 | |
| 458 local realm = GetRealmName() | |
| 459 local splitPos = string.find(name, "-") | |
| 460 if splitPos ~= nil then | |
| 461 realm = string.sub(name, splitPos + 1) | |
| 462 name = string.sub(name, 1, splitPos - 1) | |
| 463 end | |
| 464 | |
| 465 return realm, name | |
| 466 end | |
| 467 | |
| 468 -- find the unitid of a player given the name and realm... this comes from the server so the realm will be in english... | |
| 469 -- TODO: more robust handling of players with same name but different realms in the same group on non-english clients | |
| 470 function Amr:GetUnitId(unitRealm, unitName) | |
| 471 | |
| 472 local nameMatches = {} | |
| 473 | |
| 474 local units = Amr:GetGroupUnitIdentifiers() | |
| 475 for i, unitId in ipairs(units) do | |
| 476 local realm, name = Amr:GetRealmAndName(unitId) | |
| 477 if realm then | |
| 478 -- remove spaces to ensure proper matches | |
| 479 realm = string.gsub(realm, "%s+", "") | |
| 480 unitRealm = string.gsub(unitRealm, "%s+", "") | |
| 481 | |
| 482 if unitRealm == realm and unitName == name then return unitId end | |
| 483 if unitName == name then | |
| 484 table.insert(nameMatches, unitId) | |
| 485 end | |
| 486 end | |
| 487 end | |
| 488 | |
| 489 -- only one player with same name, must be the player of interest | |
| 490 if #nameMatches == 1 then return nameMatches[1] end | |
| 491 | |
| 492 -- could not find or ambiguous | |
| 493 return nil | |
| 494 end | |
| 495 | |
| 496 | |
| 497 -- scanning tooltip b/c for some odd reason the api has no way to get basic item properties... | |
| 498 -- so you have to generate a fake item tooltip and search for pre-defined strings in the display text | |
| 499 local _scanTt | |
| 500 function Amr:GetScanningTooltip() | |
| 501 if not _scanTt then | |
| 502 _scanTt = CreateFrame("GameTooltip", "AmrUiScanTooltip", nil, "GameTooltipTemplate") | |
| 503 _scanTt:SetOwner(UIParent, "ANCHOR_NONE") | |
| 504 end | |
| 505 return _scanTt | |
| 506 end | |
| 507 | |
| 508 local function scanTooltipHelper(txt, ...) | |
| 509 for i = 1, select("#", ...) do | |
| 510 local region = select(i, ...) | |
| 511 if region and region:GetObjectType() == "FontString" then | |
| 512 local text = region:GetText() -- string or nil | |
| 513 print(text) | |
| 514 end | |
| 515 end | |
| 516 end | |
| 517 | |
| 518 -- search the tooltip for txt, returns true if it is encountered on any line | |
| 519 function Amr:IsTextInTooltip(tt, txt) | |
| 520 local regions = { tt:GetRegions() } | |
| 521 for i, region in ipairs(regions) do | |
| 522 if region and region:GetObjectType() == "FontString" then | |
| 523 if region:GetText() == txt then | |
| 524 return true | |
| 525 end | |
| 526 end | |
| 527 end | |
| 528 return false | |
| 529 end | |
| 530 | |
| 531 -- helper to determine if an item in the player's bag is soulbound | |
| 532 function Amr:IsSoulbound(bagId, slotId) | |
| 533 local tt = self:GetScanningTooltip() | |
| 534 tt:ClearLines() | |
| 535 if bagId then | |
| 536 tt:SetBagItem(bagId, slotId) | |
| 537 else | |
| 538 tt:SetInventoryItem("player", slotId) | |
| 539 end | |
| 540 return self:IsTextInTooltip(tt, ITEM_SOULBOUND) | |
| 541 end | |
| 542 | |
| 543 -- helper to determine if an item has a unique constraint | |
| 544 function Amr:IsUnique(bagId, slotId) | |
| 545 local tt = self:GetScanningTooltip() | |
| 546 tt:ClearLines() | |
| 547 if bagId then | |
| 548 tt:SetBagItem(bagId, slotId) | |
| 549 else | |
| 550 tt:SetInventoryItem("player", slotId) | |
| 551 end | |
| 552 if self:IsTextInTooltip(tt, ITEM_UNIQUE_EQUIPPABLE) then return true end | |
| 553 if self:IsTextInTooltip(tt, ITEM_UNIQUE) then return true end | |
| 554 return false | |
| 555 end | |
| 556 | |
| 557 | |
| 558 ---------------------------------------------------------------------------------------- | |
| 559 -- Inter-Addon Communication | |
| 560 ---------------------------------------------------------------------------------------- | |
| 561 function Amr:SendAmrCommMessage(message, channel) | |
| 562 -- prepend version to all messages | |
| 563 local v = GetAddOnMetadata(Amr.ADDON_NAME, "Version") | |
| 564 message = v .. "\r" .. message | |
| 565 | |
| 566 Amr:SendCommMessage(Amr.ChatPrefix, message, channel or "RAID") | |
| 567 end | |
| 568 | |
| 569 function Amr:OnCommReceived(prefix, message, distribution, sender) | |
| 570 | |
| 571 local parts = {} | |
| 572 for part in string.gmatch(message, "([^\r]+)") do | |
| 573 table.insert(parts, part) | |
| 574 end | |
| 575 | |
| 576 local ver = parts[1] | |
| 577 if ver then ver = tonumber(ver) end | |
| 578 if ver then | |
| 579 -- newest versions of the addon start all messages with a version number | |
| 580 message = parts[2] | |
| 581 end | |
| 582 | |
| 583 -- we always allow version checks, even from old versions of the addon that aren't otherwise compatible | |
| 584 if Amr.StartsWith(message, Amr.MessageTypes.Version) then | |
| 585 -- version checking between group members | |
| 586 if Amr.StartsWith(message, Amr.MessageTypes.VersionRequest) then | |
| 587 sendVersionInfo() | |
| 588 else | |
| 589 onVersionInfoReceived(message) | |
| 590 end | |
| 591 | |
| 592 return | |
| 593 end | |
| 594 | |
| 595 -- any other kind of message is ignored if the version is too old | |
| 596 if not ver or ver < Amr.MIN_ADDON_VERSION then return end | |
| 597 | |
| 598 if Amr.StartsWith(message, Amr.MessageTypes.Team) then | |
| 599 -- if fully initialized, process team optimizer messages | |
| 600 if Amr["ProcessTeamMessage"] then | |
| 601 Amr:ProcessTeamMessage(message) | |
| 602 end | |
| 603 else | |
| 604 -- if we are fully loaded, process a player snapshot when it is received (combat logging) | |
| 605 if Amr["ProcessPlayerSnapshot"] then | |
| 606 self:ProcessPlayerSnapshot(message) | |
| 607 end | |
| 608 end | |
| 609 end | |
| 610 | |
| 611 | |
| 612 ---------------------------------------------------------------------------------------- | |
| 613 -- Events | |
| 614 ---------------------------------------------------------------------------------------- | |
| 615 local _eventHandlers = {} | |
| 616 | |
| 617 local function handleEvent(eventName, ...) | |
| 618 local list = _eventHandlers[eventName] | |
| 619 if list then | |
| 620 --print(eventName .. " handled") | |
| 621 for i, handler in ipairs(list) do | |
| 622 if type(handler) == "function" then | |
| 623 handler(select(1, ...)) | |
| 624 else | |
| 625 Amr[handler](Amr, select(1, ...)) | |
| 626 end | |
| 627 end | |
| 628 end | |
| 629 end | |
| 630 | |
| 631 -- 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). | |
| 632 -- This is a simple wrapper to allow adding multiple handlers to the same event, thus allowing better encapsulation of code from file to file. | |
| 633 function Amr:AddEventHandler(eventName, methodOrName) | |
| 634 local list = _eventHandlers[eventName] | |
| 635 if not list then | |
| 636 list = {} | |
| 637 _eventHandlers[eventName] = list | |
| 638 Amr:RegisterEvent(eventName, handleEvent) | |
| 639 end | |
| 640 table.insert(list, methodOrName) | |
| 641 end | |
| 642 | |
| 643 Amr:AddEventHandler("PLAYER_ENTERING_WORLD", onPlayerEnteringWorld) | |
| 644 | |
| 645 | |
| 646 ---------------------------------------------------------------------------------------- | |
| 647 -- Debugging | |
| 648 ---------------------------------------------------------------------------------------- | |
| 649 --[[ | |
| 650 function Amr:Test(val1, val2, val3) | |
| 651 | |
| 652 local link = GetLootSlotLink(tonumber(val1)) | |
| 653 local index = Amr:TestLootIndex(link) | |
| 654 print("loot index: " .. index) | |
| 655 | |
| 656 if val2 then | |
| 657 local candidate = Amr:TestLootCandidate(link, val2, val3) | |
| 658 print("loot candidate: " .. candidate) | |
| 659 | |
| 660 GiveMasterLoot(index, candidate) | |
| 661 end | |
| 662 end | |
| 663 ]] | 
