annotate Core.lua @ 63:f1d5827dbde0 v24

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