annotate Core.lua @ 186:3c2626ed5f82

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