yellowfive@57: AskMrRobot = LibStub("AceAddon-3.0"):NewAddon("AskMrRobot", "AceEvent-3.0", "AceComm-3.0", "AceConsole-3.0", "AceSerializer-3.0") yellowfive@57: local Amr = AskMrRobot yellowfive@57: Amr.Serializer = LibStub("AskMrRobot-Serializer") yellowfive@57: yellowfive@57: Amr.ADDON_NAME = "AskMrRobot" yellowfive@57: yellowfive@57: -- types of inter-addon messages that we receive, used to parcel them out to the proper handlers yellowfive@57: Amr.MessageTypes = { yellowfive@57: Version = "_V", yellowfive@124: VersionRequest = "_VR" yellowfive@57: } yellowfive@57: yellowfive@57: local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true) yellowfive@57: local AceGUI = LibStub("AceGUI-3.0") yellowfive@57: yellowfive@57: -- minimap icon and LDB support yellowfive@57: local _amrLDB = LibStub("LibDataBroker-1.1"):NewDataObject(Amr.ADDON_NAME, { yellowfive@57: type = "launcher", yellowfive@57: text = "Ask Mr. Robot", yellowfive@57: icon = "Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\icon", yellowfive@57: OnClick = function(self, button, down) yellowfive@57: if button == "LeftButton" then yellowfive@124: --if IsControlKeyDown() then yellowfive@124: -- Amr:Wipe() yellowfive@124: --else yellowfive@57: Amr:Toggle() yellowfive@124: --end yellowfive@57: elseif button == "RightButton" then yellowfive@57: Amr:EquipGearSet() yellowfive@57: end yellowfive@57: end, yellowfive@57: OnTooltipShow = function(tt) yellowfive@57: tt:AddLine("Ask Mr. Robot", 1, 1, 1); yellowfive@57: tt:AddLine(" "); yellowfive@57: tt:AddLine(L.MinimapTooltip) yellowfive@57: end yellowfive@57: }) yellowfive@57: local _icon = LibStub("LibDBIcon-1.0") yellowfive@57: yellowfive@57: yellowfive@57: -- initialize the database yellowfive@57: local function initializeDb() yellowfive@57: yellowfive@57: local defaults = { yellowfive@139: char = { yellowfive@139: LastVersion = 0, -- used to clean out old stuff yellowfive@129: FirstUse = true, -- true if this is first time use, gets cleared after seeing the export help splash window yellowfive@129: Talents = {}, -- for each spec, selected talents yellowfive@165: Essences = {}, -- for each spec, selected essences yellowfive@165: UnlockedEssences = {}, -- unlocked essences for this character yellowfive@129: Equipped = {}, -- for each spec, slot id to item info yellowfive@129: BagItems = {}, -- list of item info for bags yellowfive@129: BankItems = {}, -- list of item info for bank yellowfive@129: BagItemsAndCounts = {}, -- used mainly for the shopping list yellowfive@129: BankItemsAndCounts = {}, -- used mainly for the shopping list yellowfive@139: GearSetups = {}, -- imported gear sets yellowfive@161: JunkData = {}, -- imported data about items that can be vendored/scrapped/disenchanted yellowfive@129: ExtraEnchantData = {}, -- enchant id to enchant display information and material information yellowfive@129: Logging = { -- character logging settings yellowfive@129: Enabled = false, -- whether logging is currently on or not yellowfive@129: LastZone = nil, -- last zone the player was in yellowfive@129: LastDiff = nil, -- last difficulty for the last zone the player was in yellowfive@129: LastWipe = nil -- last time a wipe was called by this player yellowfive@129: } yellowfive@129: }, yellowfive@57: profile = { yellowfive@57: minimap = { -- minimap hide/show and position settings yellowfive@57: hide = false yellowfive@57: }, yellowfive@57: window = {}, -- main window position settings yellowfive@57: shopWindow = {}, -- shopping list window position settings yellowfive@161: junkWindow = {}, -- junk list window position settings yellowfive@57: options = { yellowfive@57: autoGear = false, -- auto-equip saved gear sets when changing specs yellowfive@161: junkVendor = false, -- auto-show junk list at vendor/scrapper yellowfive@61: shopAh = false, -- auto-show shopping list at AH yellowfive@91: disableEm = false, -- disable auto-creation of equipment manager sets yellowfive@61: uiScale = 1 -- custom scale for AMR UI yellowfive@57: }, yellowfive@57: Logging = { -- global logging settings yellowfive@57: Auto = {} -- for each instanceId, for each difficultyId, true if auto-logging enabled yellowfive@57: } yellowfive@57: }, yellowfive@57: global = { yellowfive@57: Region = nil, -- region that this user is in, all characters on the same account should be the same region yellowfive@139: Shopping2 = {}, -- shopping list data stored globally for access on any character yellowfive@57: Logging = { -- a lot of log data is stored globally for simplicity, can only be raiding with one character at a time yellowfive@57: Wipes = {}, -- times that a wipe was called yellowfive@57: PlayerData = {}, -- player data gathered at fight start yellowfive@57: PlayerExtras = {} -- player extra data like auras, gathered at fight start yellowfive@57: } yellowfive@57: } yellowfive@57: } yellowfive@57: yellowfive@124: Amr.db = LibStub("AceDB-3.0"):New("AskMrRobotDb4", defaults) yellowfive@110: yellowfive@110: -- 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: local hasSomeLogging = false yellowfive@110: local addedLogging = {} yellowfive@57: for i, instanceId in ipairs(Amr.InstanceIdsOrdered) do yellowfive@110: local byDiff = Amr.db.profile.Logging.Auto[instanceId] yellowfive@57: if not byDiff then yellowfive@57: byDiff = {} yellowfive@110: Amr.db.profile.Logging.Auto[instanceId] = byDiff yellowfive@110: addedLogging[instanceId] = byDiff yellowfive@57: end yellowfive@57: yellowfive@57: for k, difficultyId in pairs(Amr.Difficulties) do yellowfive@110: if not byDiff[difficultyId] then yellowfive@57: byDiff[difficultyId] = false yellowfive@110: else yellowfive@110: hasSomeLogging = true yellowfive@57: end yellowfive@57: end yellowfive@137: end yellowfive@137: yellowfive@137: for k,v in pairs(Amr.db.profile.Logging.Auto) do yellowfive@137: if not Amr.IsSupportedInstanceId(k) then yellowfive@137: Amr.db.profile.Logging.Auto[k] = nil yellowfive@137: end yellowfive@57: end yellowfive@57: yellowfive@110: if hasSomeLogging then yellowfive@110: for instanceId, byDiff in pairs(addedLogging) do yellowfive@110: for k, difficultyId in pairs(Amr.Difficulties) do yellowfive@110: byDiff[difficultyId] = true yellowfive@110: end yellowfive@110: end yellowfive@110: end yellowfive@110: yellowfive@139: -- upgrade old gear set info to new format yellowfive@139: if Amr.db.char.GearSets then yellowfive@139: Amr.db.char.GearSets = nil yellowfive@139: end yellowfive@139: yellowfive@139: if not Amr.db.char.GearSetups then yellowfive@139: Amr.db.char.GearSetups = {} yellowfive@139: end yellowfive@139: yellowfive@139: if Amr.db.global.Shopping then yellowfive@139: Amr.db.global.Shopping = nil yellowfive@139: end yellowfive@57: yellowfive@57: Amr.db.RegisterCallback(Amr, "OnProfileChanged", "RefreshConfig") yellowfive@57: Amr.db.RegisterCallback(Amr, "OnProfileCopied", "RefreshConfig") yellowfive@57: Amr.db.RegisterCallback(Amr, "OnProfileReset", "RefreshConfig") yellowfive@57: end yellowfive@57: yellowfive@57: function Amr:OnInitialize() yellowfive@57: yellowfive@57: initializeDb() yellowfive@57: yellowfive@57: Amr:RegisterChatCommand("amr", "SlashCommand") yellowfive@57: yellowfive@57: _icon:Register(Amr.ADDON_NAME, _amrLDB, self.db.profile.minimap) yellowfive@57: yellowfive@57: -- listen for inter-addon communication yellowfive@139: self:RegisterComm(Amr.ChatPrefix, "OnCommReceived") yellowfive@57: end yellowfive@57: yellowfive@57: local _enteredWorld = false yellowfive@57: local _pendingInit = false yellowfive@57: yellowfive@139: -- upgrade some stuff from old to new formats yellowfive@139: local function upgradeFromOld() yellowfive@139: yellowfive@139: local currentVersion = tonumber(GetAddOnMetadata(Amr.ADDON_NAME, "Version")) yellowfive@139: if Amr.db.char.LastVersion < 65 then yellowfive@143: yellowfive@143: if not Amr.db.profile.options.disableEm then yellowfive@143: for i = 1,GetNumSpecializations() do yellowfive@143: local _, specName = GetSpecializationInfo(i) yellowfive@143: if specName then yellowfive@143: local setid = C_EquipmentSet.GetEquipmentSetID("AMR " .. specName) yellowfive@143: if setid then yellowfive@143: C_EquipmentSet.DeleteEquipmentSet(setid) yellowfive@143: end yellowfive@139: end yellowfive@139: end yellowfive@139: end yellowfive@143: yellowfive@139: end yellowfive@139: Amr.db.char.LastVersion = currentVersion yellowfive@139: yellowfive@139: end yellowfive@139: yellowfive@133: local function finishInitialize() yellowfive@57: yellowfive@57: -- record region, the only thing that we still can't get from the log file yellowfive@57: Amr.db.global.Region = Amr.RegionNames[GetCurrentRegion()] yellowfive@57: yellowfive@57: -- make sure that some initialization is deferred until after PLAYER_ENTERING_WORLD event so that data we need is available; yellowfive@57: -- 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: Amr.Wait(5, function() yellowfive@145: --Amr:InitializeVersions() yellowfive@57: Amr:InitializeGear() yellowfive@57: Amr:InitializeExport() yellowfive@57: Amr:InitializeCombatLog() yellowfive@139: yellowfive@139: upgradeFromOld() yellowfive@57: end) yellowfive@57: end yellowfive@57: yellowfive@133: local function onPlayerEnteringWorld() yellowfive@57: yellowfive@57: _enteredWorld = true yellowfive@57: yellowfive@57: if _pendingInit then yellowfive@57: finishInitialize() yellowfive@57: _pendingInit = false yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: function Amr:OnEnable() yellowfive@57: yellowfive@81: --[[ yellowfive@57: -- listen for changes to the snapshot enable state, and always make sure it is enabled if using the core AskMrRobot addon yellowfive@57: self:RegisterMessage("AMR_SNAPSHOT_STATE_CHANGED", function(eventName, isEnabled) yellowfive@57: if not isEnabled then yellowfive@57: -- immediately re-enable on any attempt to disable yellowfive@57: Amr.Serializer:EnableSnapshots() yellowfive@57: end yellowfive@57: end) yellowfive@57: self.Serializer:EnableSnapshots() yellowfive@81: ]] yellowfive@57: yellowfive@57: -- update based on current configuration whenever enabled yellowfive@57: self:RefreshConfig() yellowfive@57: yellowfive@57: -- if we have fully entered the world, do initialization; otherwise wait for PLAYER_ENTERING_WORLD to continue yellowfive@57: if not _enteredWorld then yellowfive@57: _pendingInit = true yellowfive@57: else yellowfive@57: _pendingInit = false yellowfive@57: finishInitialize() yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: function Amr:OnDisable() yellowfive@57: -- disabling is not supported yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: ---------------------------------------------------------------------------------------- yellowfive@57: -- Slash Commands yellowfive@57: ---------------------------------------------------------------------------------------- yellowfive@57: local _slashMethods = { yellowfive@57: hide = "Hide", yellowfive@57: show = "Show", yellowfive@57: toggle = "Toggle", yellowfive@124: equip = "EquipGearSet", yellowfive@57: version = "PrintVersions", yellowfive@161: junk = "ShowJunkWindow", yellowfive@124: --wipe = "Wipe", yellowfive@124: --undowipe = "UndoWipe", yellowfive@61: reset = "Reset", yellowfive@57: test = "Test" yellowfive@57: } yellowfive@57: yellowfive@57: function Amr:SlashCommand(input) yellowfive@57: input = string.lower(input) yellowfive@57: local parts = {} yellowfive@57: for w in input:gmatch("%S+") do yellowfive@57: table.insert(parts, w) yellowfive@57: end yellowfive@57: yellowfive@57: if #parts == 0 then return end yellowfive@57: yellowfive@57: local func = _slashMethods[parts[1]] yellowfive@57: if not func then return end yellowfive@57: yellowfive@57: local funcArgs = {} yellowfive@57: for i = 2, #parts do yellowfive@57: table.insert(funcArgs, parts[i]) yellowfive@57: end yellowfive@57: yellowfive@57: Amr[func](Amr, unpack(funcArgs)) yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: ---------------------------------------------------------------------------------------- yellowfive@57: -- Configuration yellowfive@57: ---------------------------------------------------------------------------------------- yellowfive@57: yellowfive@57: -- refresh all state based on the current values of configuration options yellowfive@57: function Amr:RefreshConfig() yellowfive@57: yellowfive@57: self:UpdateMinimap() yellowfive@57: self:RefreshOptionsUi() yellowfive@57: self:RefreshLogUi() yellowfive@57: end yellowfive@57: yellowfive@57: function Amr:UpdateMinimap() yellowfive@57: yellowfive@57: if self.db.profile.minimap.hide or not Amr:IsEnabled() then yellowfive@57: _icon:Hide(Amr.ADDON_NAME) yellowfive@57: else yellowfive@57: -- change icon color if logging yellowfive@57: if Amr:IsLogging() then yellowfive@57: _amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon_green' yellowfive@57: else yellowfive@57: _amrLDB.icon = 'Interface\\AddOns\\AskMrRobot\\Media\\icon' yellowfive@57: end yellowfive@57: yellowfive@57: _icon:Show(Amr.ADDON_NAME) yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: ---------------------------------------------------------------------------------------- yellowfive@57: -- Version Checking yellowfive@57: ---------------------------------------------------------------------------------------- yellowfive@57: yellowfive@57: -- version of addon being run by each person in the player's raid or group yellowfive@57: Amr.GroupVersions = {} yellowfive@57: yellowfive@57: local function toGroupVersionKey(realm, name) yellowfive@57: realm = string.gsub(realm, "%s+", "") yellowfive@57: return name .. "-" .. realm yellowfive@57: end yellowfive@57: yellowfive@57: -- prune out version information for players no longer in the current raid group yellowfive@57: local function pruneVersionInfo() yellowfive@57: yellowfive@57: local newVersions = {} yellowfive@57: local units = Amr:GetGroupUnitIdentifiers() yellowfive@57: yellowfive@57: for i, unitId in ipairs(units) do yellowfive@57: local realm, name = Amr:GetRealmAndName(unitId) yellowfive@57: if realm then yellowfive@57: local key = toGroupVersionKey(realm, name) yellowfive@57: newVersions[key] = Amr.GroupVersions[key] yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: Amr.GroupVersions = newVersions yellowfive@57: end yellowfive@57: yellowfive@57: -- send version information to other people in the same raid group yellowfive@57: local function sendVersionInfo() yellowfive@57: yellowfive@57: local realm = GetRealmName() yellowfive@57: local name = UnitName("player") yellowfive@57: local ver = GetAddOnMetadata(Amr.ADDON_NAME, "Version") yellowfive@57: yellowfive@57: local msg = string.format("%s\n%s\n%s\n%s", Amr.MessageTypes.Version, realm, name, ver) yellowfive@57: Amr:SendAmrCommMessage(msg) yellowfive@57: end yellowfive@57: yellowfive@57: local function onVersionInfoReceived(message) yellowfive@57: yellowfive@57: -- message will be of format: realm\nname\nversion yellowfive@57: local parts = {} yellowfive@57: for part in string.gmatch(message, "([^\n]+)") do yellowfive@57: table.insert(parts, part) yellowfive@57: end yellowfive@57: yellowfive@57: local key = toGroupVersionKey(parts[2], parts[3]) yellowfive@57: local ver = parts[4] yellowfive@57: yellowfive@57: Amr.GroupVersions[key] = tonumber(ver) yellowfive@57: yellowfive@57: -- 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: pruneVersionInfo() yellowfive@57: end yellowfive@57: yellowfive@57: -- 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: function Amr:GetAddonVersion(realm, name) yellowfive@57: local ver = Amr.GroupVersions[toGroupVersionKey(realm, name)] yellowfive@57: return ver or 0 yellowfive@57: end yellowfive@57: yellowfive@57: function Amr:PrintVersions() yellowfive@57: yellowfive@57: if not IsInGroup() and not IsInRaid() then yellowfive@57: self:Print(L.VersionChatNotGrouped) yellowfive@57: return yellowfive@57: end yellowfive@57: yellowfive@57: local units = self:GetGroupUnitIdentifiers() yellowfive@57: yellowfive@57: local msg = {} yellowfive@57: table.insert(msg, L.VersionChatTitle) yellowfive@57: yellowfive@57: for i, unitId in ipairs(units) do yellowfive@57: local realm, name = self:GetRealmAndName(unitId) yellowfive@57: if realm then yellowfive@57: local key = toGroupVersionKey(realm, name) yellowfive@57: local ver = Amr.GroupVersions[key] yellowfive@57: if not ver then yellowfive@57: table.insert(msg, key .. " |cFFFF0000" .. L.VersionChatNotInstalled .. "|r") yellowfive@57: else yellowfive@57: table.insert(msg, key .. " v" .. ver) yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: msg = table.concat(msg, "\n") yellowfive@57: print(msg) yellowfive@57: end yellowfive@57: yellowfive@57: function Amr:InitializeVersions() yellowfive@57: Amr:AddEventHandler("GROUP_ROSTER_UPDATE", pruneVersionInfo) yellowfive@57: Amr:AddEventHandler("GROUP_ROSTER_UPDATE", sendVersionInfo) yellowfive@57: yellowfive@57: -- request version information from anyone in my group upon initialization yellowfive@57: if IsInGroup() or IsInRaid() then yellowfive@57: Amr:SendAmrCommMessage(Amr.MessageTypes.VersionRequest) yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: ---------------------------------------------------------------------------------------- yellowfive@57: -- Generic Helpers yellowfive@57: ---------------------------------------------------------------------------------------- yellowfive@57: yellowfive@57: local _waitTable = {} yellowfive@57: local _waitFrame = nil yellowfive@57: yellowfive@57: -- execute the specified function after the specified delay (in seconds) yellowfive@57: function Amr.Wait(delay, func, ...) yellowfive@57: if not _waitFrame then yellowfive@57: _waitFrame = CreateFrame("Frame", "AmrWaitFrame", UIParent) yellowfive@57: _waitFrame:SetScript("OnUpdate", function (self, elapse) yellowfive@57: local count = #_waitTable yellowfive@57: local i = 1 yellowfive@57: while(i <= count) do yellowfive@57: local waitRecord = table.remove(_waitTable, i) yellowfive@57: local d = table.remove(waitRecord, 1) yellowfive@57: local f = table.remove(waitRecord, 1) yellowfive@57: local p = table.remove(waitRecord, 1) yellowfive@57: if d > elapse then yellowfive@57: table.insert(_waitTable, i, { d-elapse, f, p }) yellowfive@57: i = i + 1 yellowfive@57: else yellowfive@57: count = count - 1 yellowfive@57: f(unpack(p)) yellowfive@57: end yellowfive@57: end yellowfive@57: end) yellowfive@57: end yellowfive@57: table.insert(_waitTable, { delay, func, {...} }) yellowfive@57: return true yellowfive@57: end yellowfive@57: yellowfive@57: -- helper to iterate over a table in order by its keys yellowfive@57: function Amr.spairs(t, order) yellowfive@57: -- collect the keys yellowfive@57: local keys = {} yellowfive@57: for k in pairs(t) do keys[#keys+1] = k end yellowfive@57: yellowfive@57: -- if order function given, sort by it by passing the table and keys a, b, yellowfive@57: -- otherwise just sort the keys yellowfive@57: if order then yellowfive@57: table.sort(keys, function(a,b) return order(t, a, b) end) yellowfive@57: else yellowfive@57: table.sort(keys) yellowfive@57: end yellowfive@57: yellowfive@57: -- return the iterator function yellowfive@57: local i = 0 yellowfive@57: return function() yellowfive@57: i = i + 1 yellowfive@57: if keys[i] then yellowfive@57: return keys[i], t[keys[i]] yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: function Amr.StartsWith(str, prefix) yellowfive@57: if string.len(str) < string.len(prefix) then return false end yellowfive@57: return string.sub(str, 1, string.len(prefix)) == prefix yellowfive@57: end yellowfive@57: yellowfive@124: function Amr.IsEmpty(table) yellowfive@124: return next(table) == nil yellowfive@124: end yellowfive@124: yellowfive@124: function Amr.Contains(table, value) yellowfive@124: if not table then return false end yellowfive@124: for k,v in pairs(table) do yellowfive@124: if v == value then yellowfive@124: return true yellowfive@124: end yellowfive@124: end yellowfive@124: return false yellowfive@124: end yellowfive@124: yellowfive@57: -- helper to get the unit identifiers (e.g. to pass to GetUnitName) for all members of the player's current group/raid yellowfive@57: function Amr:GetGroupUnitIdentifiers() yellowfive@57: yellowfive@57: local units = {} yellowfive@57: if IsInRaid() then yellowfive@57: for i = 1,40 do yellowfive@57: table.insert(units, "raid" .. i) yellowfive@57: end yellowfive@57: elseif IsInGroup() then yellowfive@57: table.insert(units, "player") yellowfive@57: for i = 1,4 do yellowfive@57: table.insert(units, "party" .. i) yellowfive@57: end yellowfive@57: else yellowfive@57: table.insert(units, "player") yellowfive@57: end yellowfive@57: yellowfive@57: return units yellowfive@57: end yellowfive@57: yellowfive@57: -- helper to get the realm and name from a unitId (e.g. "player" or "raid1") yellowfive@57: function Amr:GetRealmAndName(unitId) yellowfive@57: yellowfive@57: local name = GetUnitName(unitId, true) yellowfive@57: if not name then return end yellowfive@57: yellowfive@57: local realm = GetRealmName() yellowfive@57: local splitPos = string.find(name, "-") yellowfive@57: if splitPos ~= nil then yellowfive@57: realm = string.sub(name, splitPos + 1) yellowfive@57: name = string.sub(name, 1, splitPos - 1) yellowfive@57: end yellowfive@57: yellowfive@57: return realm, name yellowfive@57: end yellowfive@57: yellowfive@57: -- 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: -- TODO: more robust handling of players with same name but different realms in the same group on non-english clients yellowfive@57: function Amr:GetUnitId(unitRealm, unitName) yellowfive@57: yellowfive@57: local nameMatches = {} yellowfive@57: yellowfive@57: local units = Amr:GetGroupUnitIdentifiers() yellowfive@57: for i, unitId in ipairs(units) do yellowfive@57: local realm, name = Amr:GetRealmAndName(unitId) yellowfive@57: if realm then yellowfive@57: -- remove spaces to ensure proper matches yellowfive@57: realm = string.gsub(realm, "%s+", "") yellowfive@57: unitRealm = string.gsub(unitRealm, "%s+", "") yellowfive@57: yellowfive@57: if unitRealm == realm and unitName == name then return unitId end yellowfive@57: if unitName == name then yellowfive@57: table.insert(nameMatches, unitId) yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: -- only one player with same name, must be the player of interest yellowfive@57: if #nameMatches == 1 then return nameMatches[1] end yellowfive@57: yellowfive@57: -- could not find or ambiguous yellowfive@57: return nil yellowfive@57: end yellowfive@57: yellowfive@133: --[[ yellowfive@57: -- search the tooltip for txt, returns true if it is encountered on any line yellowfive@57: function Amr:IsTextInTooltip(tt, txt) yellowfive@57: local regions = { tt:GetRegions() } yellowfive@57: for i, region in ipairs(regions) do yellowfive@57: if region and region:GetObjectType() == "FontString" then yellowfive@57: if region:GetText() == txt then yellowfive@57: return true yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: return false yellowfive@57: end yellowfive@133: ]] yellowfive@57: yellowfive@124: -- helper to determine if we can equip an item (it is soulbound) yellowfive@59: function Amr:CanEquip(bagId, slotId) yellowfive@124: local item = Item:CreateFromBagAndSlot(bagId, slotId) yellowfive@124: if item then yellowfive@124: local loc = item:GetItemLocation() yellowfive@124: return C_Item.IsBound(loc) yellowfive@124: else yellowfive@124: -- 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: return true yellowfive@124: end yellowfive@124: yellowfive@124: --local tt = Amr.GetItemTooltip(bagId, slotId) yellowfive@124: --if self:IsTextInTooltip(tt, ITEM_SOULBOUND) then return true end yellowfive@124: --if self:IsTextInTooltip(tt, ITEM_BNETACCOUNTBOUND) then return true end yellowfive@124: --if self:IsTextInTooltip(tt, ITEM_ACCOUNTBOUND) then return true end yellowfive@57: end yellowfive@57: yellowfive@57: -- helper to determine if an item has a unique constraint yellowfive@133: --[[ yellowfive@57: function Amr:IsUnique(bagId, slotId) yellowfive@81: local tt = Amr.GetItemTooltip(bagId, slotId) yellowfive@57: if self:IsTextInTooltip(tt, ITEM_UNIQUE_EQUIPPABLE) then return true end yellowfive@57: if self:IsTextInTooltip(tt, ITEM_UNIQUE) then return true end yellowfive@57: return false yellowfive@57: end yellowfive@133: ]] yellowfive@57: yellowfive@57: yellowfive@57: ---------------------------------------------------------------------------------------- yellowfive@57: -- Inter-Addon Communication yellowfive@57: ---------------------------------------------------------------------------------------- yellowfive@57: function Amr:SendAmrCommMessage(message, channel) yellowfive@57: -- prepend version to all messages yellowfive@57: local v = GetAddOnMetadata(Amr.ADDON_NAME, "Version") yellowfive@57: message = v .. "\r" .. message yellowfive@57: yellowfive@79: Amr:SendCommMessage(Amr.ChatPrefix, message, channel or (IsInGroup(LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT" or "RAID")) yellowfive@57: end yellowfive@57: yellowfive@57: function Amr:OnCommReceived(prefix, message, distribution, sender) yellowfive@57: yellowfive@57: local parts = {} yellowfive@57: for part in string.gmatch(message, "([^\r]+)") do yellowfive@57: table.insert(parts, part) yellowfive@57: end yellowfive@57: yellowfive@57: local ver = parts[1] yellowfive@57: if ver then ver = tonumber(ver) end yellowfive@57: if ver then yellowfive@57: -- newest versions of the addon start all messages with a version number yellowfive@57: message = parts[2] yellowfive@57: end yellowfive@57: yellowfive@57: -- we always allow version checks, even from old versions of the addon that aren't otherwise compatible yellowfive@57: if Amr.StartsWith(message, Amr.MessageTypes.Version) then yellowfive@57: -- version checking between group members yellowfive@57: if Amr.StartsWith(message, Amr.MessageTypes.VersionRequest) then yellowfive@57: sendVersionInfo() yellowfive@57: else yellowfive@57: onVersionInfoReceived(message) yellowfive@57: end yellowfive@57: yellowfive@57: return yellowfive@57: end yellowfive@57: yellowfive@57: -- any other kind of message is ignored if the version is too old yellowfive@57: if not ver or ver < Amr.MIN_ADDON_VERSION then return end yellowfive@57: yellowfive@124: --[[ yellowfive@57: if Amr.StartsWith(message, Amr.MessageTypes.Team) then yellowfive@57: -- if fully initialized, process team optimizer messages yellowfive@57: if Amr["ProcessTeamMessage"] then yellowfive@57: Amr:ProcessTeamMessage(message) yellowfive@57: end yellowfive@57: else yellowfive@57: -- if we are fully loaded, process a player snapshot when it is received (combat logging) yellowfive@57: if Amr["ProcessPlayerSnapshot"] then yellowfive@57: self:ProcessPlayerSnapshot(message) yellowfive@57: end yellowfive@57: end yellowfive@124: ]] yellowfive@57: end yellowfive@57: yellowfive@57: yellowfive@57: ---------------------------------------------------------------------------------------- yellowfive@57: -- Events yellowfive@57: ---------------------------------------------------------------------------------------- yellowfive@57: local _eventHandlers = {} yellowfive@57: yellowfive@57: local function handleEvent(eventName, ...) yellowfive@57: local list = _eventHandlers[eventName] yellowfive@57: if list then yellowfive@57: --print(eventName .. " handled") yellowfive@57: for i, handler in ipairs(list) do yellowfive@57: if type(handler) == "function" then yellowfive@57: handler(select(1, ...)) yellowfive@57: else yellowfive@57: Amr[handler](Amr, select(1, ...)) yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: -- 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: -- 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: function Amr:AddEventHandler(eventName, methodOrName) yellowfive@57: local list = _eventHandlers[eventName] yellowfive@57: if not list then yellowfive@57: list = {} yellowfive@57: _eventHandlers[eventName] = list yellowfive@57: Amr:RegisterEvent(eventName, handleEvent) yellowfive@57: end yellowfive@57: table.insert(list, methodOrName) yellowfive@57: end yellowfive@57: yellowfive@57: Amr:AddEventHandler("PLAYER_ENTERING_WORLD", onPlayerEnteringWorld) yellowfive@57: yellowfive@57: yellowfive@57: ---------------------------------------------------------------------------------------- yellowfive@57: -- Debugging yellowfive@57: ---------------------------------------------------------------------------------------- yellowfive@124: function Amr:dump(o) yellowfive@124: if type(o) == 'table' then yellowfive@124: local s = '{ ' yellowfive@124: for k,v in pairs(o) do yellowfive@124: if type(k) ~= 'number' then k = '"'..k..'"' end yellowfive@124: s = s .. '['..k..'] = ' .. Amr:dump(v) .. ',' yellowfive@124: end yellowfive@124: return s .. '} ' yellowfive@124: else yellowfive@124: return tostring(o) yellowfive@124: end yellowfive@124: end yellowfive@124: yellowfive@69: function Amr:Test() yellowfive@124: yellowfive@69: end