Mercurial > wow > askmrrobot
changeset 125:4d175fb190f1 v58
Updated for 8.0 pre-patch and BfA.
author | yellowfive |
---|---|
date | Tue, 17 Jul 2018 09:58:51 -0700 |
parents | e31b02b24488 |
children | c005f9a393c1 |
files | Loot.lua TeamOptimizer.lua |
diffstat | 2 files changed, 0 insertions(+), 2435 deletions(-) [+] |
line wrap: on
line diff
--- a/Loot.lua Tue Jul 17 09:57:39 2018 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1042 +0,0 @@ -local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot") -local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true) -local AceGUI = LibStub("AceGUI-3.0") - -local _frameLoot -local _panelLoot -local _selectedIndex -local _disenchant = {} -local _rankPanels -local _lootButtons - -local _widthItemList = 220 -local _widthRankList = 640 -local _widthSpacing = 10 -local _widthRank = 48 -local _widthRankBar = 195 -local _widthRollType = 45 -local _widthColPadding = 8 -local _widthRollExtraSpacing = 4 - -local _rollTypePos = { - Need = 1, - Off = 2, - Greed = 3, - Pass = 4 -} - --- get the index of the loot item that matches up to the specified item -local function getLootIndex(itemIndex) - - local ranking = Amr.db.global.TeamOpt.Rankings[itemIndex] - if not ranking then return nil end - - local itemUniqueId = Amr.GetItemUniqueId(ranking.item) - for i = 1, GetNumLootItems() do - --local texture, item, quantity, quality, locked = GetLootSlotInfo(i) - local lootType = GetLootSlotType(i) - if lootType == 1 then - local link = GetLootSlotLink(i) - local lootItem = Amr.ParseItemLink(link) - if Amr.GetItemUniqueId(lootItem) == itemUniqueId then - return i, link - end - end - end -end - -local function getLootCandidateIndex(lootIndex, realm, name) - -- remove spaces to ensure proper matches, and make all lower - realm = string.gsub(realm, "%s+", "") - realm = string.lower(realm) - name = string.lower(name) - - local nameMatches = {} - for i = 1, 40 do - local candidate = GetMasterLootCandidate(lootIndex, i) - if candidate then - local candidateName = candidate - local candidateRealm = GetRealmName() - - -- see if realm is in the name - local splitPos = string.find(candidateName, "-") - if splitPos ~= nil then - candidateRealm = string.sub(candidateName, splitPos + 1) - candidateName = string.sub(candidateName, 1, splitPos - 1) - end - - -- remove spaces to ensure proper matches, and make all lower - candidateRealm = string.gsub(candidateRealm, "%s+", "") - candidateRealm = string.lower(candidateRealm) - candidateName = string.lower(candidateName) - - -- if perfect match then we are done - if candidateRealm == realm and candidateName == name then - return i - end - - if candidateName == name then - table.insert(nameMatches, i) - end - end - end - - -- only one player with same name, must be the player of interest - if #nameMatches == 1 then - return nameMatches[1] - end - - -- could not be found or ambiguous - return nil -end - --- helper to send a message telling everyone that an item was just given out -local function sendGiveLootMessage(itemLink, unitId, isDisenchant) - -- some display info - local cls, clsEn = UnitClass(unitId) - local name = UnitName(unitId) - - local result = isDisenchant and "Disenchant" or (roll and roll.rollType or nil) - if result == nil then result = "??" end - - local msg = string.format("%s\n%d\n%s\n%s\n%s\n%s", Amr.LootMessagePrefixes.Give, _selectedIndex, itemLink, result, clsEn, name) - Amr:SendAmrCommMessage(msg) -end - -local function onGiveLootClick(widget) - local rollIndex = widget:GetUserData("index") - - local rankings = Amr.db.global.TeamOpt.Rankings - local ranking = rankings[_selectedIndex] - local rank = ranking and ranking.ranks[rollIndex] or nil - if rank then - local roll = Amr.db.char.TeamOpt.Rolls[_selectedIndex][rollIndex] - local isDisenchant = not not _disenchant[_selectedIndex] - - local unitId = Amr:GetUnitId(rank.realm, rank.name) - if unitId then - -- find the item and loot candidate index - local itemIndex, itemLink = getLootIndex(_selectedIndex) - - -- for debugging when don't actually have any loot - --itemLink = Amr.CreateItemLink(ranking.item) - - local playerIndex = itemIndex and getLootCandidateIndex(itemIndex, rank.realm, rank.name) or nil - if itemIndex and playerIndex then - GiveMasterLoot(itemIndex, playerIndex) - sendGiveLootMessage(itemLink, unitId, isDisenchant) - return - end - - end - end - - -- if we make it here, we could not give out the item for some reason - Amr:Print(L.LootMasterGiveFail) -end - -function Amr:OnLootGiveReceived(parts) - if not parts or #parts < 6 then return end - - local rankings = Amr.db.global.TeamOpt.Rankings - - -- index of the item that was given, flag it to hide it from the ui now - local index = tonumber(parts[2]) - local ranking = rankings[index] - if ranking then - ranking.given = true - end - - -- change the selected item index to the next ungiven item - for i, obj in ipairs(rankings) do - if not obj.given then - _selectedIndex = i - break - end - end - - -- add a loot history entry - local entry = { - link = parts[3], - result = parts[4], - class = parts[5], - name = parts[6] - } - table.insert(Amr.db.char.TeamOpt.History, entry) - - -- redraw any open windows - Amr:RefreshTeamUi() - Amr:RefreshLootWindow() - Amr:RefreshLootRolls() - - -- if this is the master looter, check if all items have been given out - if IsMasterLooter() then - local allDone = true - for i, ranking in ipairs(rankings) do - if not ranking.given then - allDone = false - break - end - end - - if allDone then - -- send a message indicating that looting is done - Amr:SendAmrCommMessage(Amr.LootMessagePrefixes.Finish) - end - end - -end - -local function onDisenchantClick() - local val = not _disenchant[_selectedIndex] - _disenchant[_selectedIndex] = val - - Amr:RefreshLootWindow() - Amr:RefreshLootRolls() -end - -local function onRollClick() - -- generate a roll for everyone on the current item - local rands = {} - - local ranking = Amr.db.global.TeamOpt.Rankings[_selectedIndex] - for i, rank in ipairs(ranking.ranks) do - local r = math.random(100) - rands[i] = r - end - - -- transmit the roll data to all group members - local msg = string.format("%s\n%d\n%s", Amr.LootMessagePrefixes.Rand, _selectedIndex, Amr:Serialize(rands)) - Amr:SendAmrCommMessage(msg) -end - -function Amr:OnLootRandReceived(parts) - if not parts or #parts < 3 then return end - - local index = tonumber(parts[2]) - local success, rands = Amr:Deserialize(parts[3]) - if not index or not success then return end - - local rolls = Amr.db.char.TeamOpt.Rolls[index] - for i, r in pairs(rands) do - local roll = rolls[i] - if not roll then - roll = {} - rolls[i] = roll - end - - roll.rand = r - end - - Amr:RefreshLootRolls() -end - -local function onVetoClick(widget) - local rollIndex = widget:GetUserData("rollIndex") - local rollType = widget:GetUserData("rollType") - - -- acts like a toggle - local roll = Amr.db.char.TeamOpt.Rolls[_selectedIndex][rollIndex] - local veto = not roll or not roll.vetoes or not roll.vetoes[rollType] - - -- send a message that a veto has been changed - local msg = string.format("%s\n%d\n%d\n%s\n%s", Amr.LootMessagePrefixes.Veto, _selectedIndex, rollIndex, rollType, veto and "t" or "f") - Amr:SendAmrCommMessage(msg) -end - -function Amr:OnLootVetoReceived(parts) - if not parts or #parts < 5 then return end - - local itemIndex = tonumber(parts[2]) - local rollIndex = tonumber(parts[3]) - local rollType = parts[4] - local veto = parts[5] == "t" - - if itemIndex and rollIndex then - local roll = Amr.db.char.TeamOpt.Rolls[itemIndex][rollIndex] - if not roll then - roll = {} - Amr.db.char.TeamOpt.Rolls[itemIndex][rollIndex] = roll - end - - if not roll.vetoes then - roll.vetoes = {} - end - - roll.vetoes[rollType] = veto - - -- if the player chose this option, have to remove it because it has been vetoed - if veto and roll.rollType == rollType then - roll.rollType = nil - end - - Amr:RefreshLootRolls() - end -end - --- a user choice for what they want to do on an item -local function doRoll(rollType) - - local msg = string.format("%s\n%d\n%s\n%s\n%s", Amr.LootMessagePrefixes.Roll, _selectedIndex, rollType, GetRealmName(), UnitName("player")) - Amr:SendAmrCommMessage(msg) -end - -function Amr:OnLootRollReceived(parts) - local index = tonumber(parts[2]) - local rollType = parts[3] - local realm = parts[4] - local name = parts[5] - - -- for now, this code matches up name/realm to one in the rankings - -- TODO: more robust handling of players with same name but different realms in the same group on non-english clients - local nameMatches = {} - local ranking = Amr.db.global.TeamOpt.Rankings[index] - for i, rank in ipairs(ranking.ranks) do - if name == rank.name and realm == rank.realm then - nameMatches = {} - break - end - - if name == rank.name then - table.insert(nameMatches, rank) - end - end - if #nameMatches == 1 then - realm = nameMatches[1].realm - name = nameMatches[1].name - end - - -- find index of the ranking - local rankIndex = nil - for i, rank in ipairs(ranking.ranks) do - if name == rank.name and realm == rank.realm then - rankIndex = i - break - end - end - - if rankIndex then - local obj = Amr.db.char.TeamOpt.Rolls[index][rankIndex] - if not obj then - obj = {} - Amr.db.char.TeamOpt.Rolls[index][rankIndex] = obj - end - obj.rollType = rollType - end - - Amr:RefreshLootRolls() -end - -local function renderRollType(rp, rollType, roll, index) - - local icon = rp:GetUserData(rollType) - if not icon and roll then - -- create icon if we need one - icon = AceGUI:Create("AmrUiTextButton") - icon:SetWidth(16) - icon:SetHeight(16) - local pos = _rollTypePos[rollType] - local left = _widthRank + _widthColPadding + _widthRankBar + (pos * _widthColPadding) + ((pos - 1) * _widthRollType) + ((_widthRollType - 16) / 2) - icon:SetPoint("LEFT", rp.content, "LEFT", left, 0) - rp:AddChild(icon) - rp:SetUserData(rollType, icon) - - icon:SetUserData("rollType", rollType) - icon:SetUserData("rollIndex", index) - icon:SetVisible(false) - - icon:SetCallback("OnClick", onVetoClick) - end - - if icon then - if roll and roll.rollType == rollType then - icon:SetVisible(true) - icon:SetBackgroundImage("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\IconCheck") - icon:SetHoverBackgroundImage("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\IconCheck") - elseif roll and roll.vetoes and roll.vetoes[rollType] then - icon:SetVisible(true) - icon:SetBackgroundImage("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\IconX") - icon:SetHoverBackgroundImage("Interface\\AddOns\\" .. Amr.ADDON_NAME .. "\\Media\\IconX") - else - icon:SetVisible(false) - end - - icon:SetDisabled(not IsMasterLooter()) - end - - -- update button state for this roll type - if _lootButtons and _lootButtons[rollType] then - _lootButtons[rollType]:SetDisabled(roll and roll.vetoes and roll.vetoes[rollType]) - end -end - --- gets the current winner based on rolls and currently selected roll type for each user (returns index into rankings for item, or -1 if no winner yet) -local function getWinner(itemIndex) - - local rolls = Amr.db.char.TeamOpt.Rolls[itemIndex] - if not rolls then return -1 end - - -- go through and find the highest priority roll type - local bestRollType - local bestTypePos = 100 - for i, roll in pairs(rolls) do - if roll.rollType then - local rollPos = _rollTypePos[roll.rollType] - if rollPos < bestTypePos and rollPos < _rollTypePos["Pass"] then - bestRollType = roll.rollType - bestTypePos = rollPos - end - end - end - - -- nobody has chosen anything yet - if not bestRollType then return -1 end - - -- find highest roll in the highest priority roll type - local maxRoll = -1 - local bestRoll = -1 - for i, roll in pairs(rolls) do - if roll.rollType == bestRollType and roll.rand and roll.rand > maxRoll then - bestRoll = i - maxRoll = roll.rand - end - end - - return bestRoll -end - -function Amr:RefreshLootRolls() - if not _rankPanels then return end - - local ranking = Amr.db.global.TeamOpt.Rankings[_selectedIndex] - local rolls = Amr.db.char.TeamOpt.Rolls[_selectedIndex] - local isDisenchant = _disenchant[_selectedIndex] - - local winnerIndex = getWinner(_selectedIndex) - - for i, rp in pairs(_rankPanels) do - local rank = ranking.ranks[i] - local roll = rolls[i] - if isDisenchant then roll = nil end - - -- clear or set the value of each roll column - renderRollType(rp, "Need", roll, i) - renderRollType(rp, "Off", roll, i) - renderRollType(rp, "Greed", roll, i) - renderRollType(rp, "Pass", roll, i) - - -- render the random roll - local lbl = rp:GetUserData("randLabel") - if roll and roll.rand then - if not lbl then - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetJustifyH("RIGHT") - - local left = _widthRank + _widthColPadding + _widthRankBar + (5 * _widthColPadding) + (5 * _widthRollType) - lbl:SetPoint("RIGHT", rp.content, "LEFT", left, 0) - rp:AddChild(lbl) - rp:SetUserData("randLabel", lbl) - end - - -- highlight this roll if winner, otherwise unhighlight - if i == winnerIndex then - lbl:SetFont(Amr.CreateFont("Bold", 18, Amr.Colors.BrightGreen)) - else - lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White)) - end - - lbl:SetText(roll.rand) - else - if lbl then - lbl:SetVisible(false) - end - end - - -- if this person does not have the addon, show a message (except in DE mode) - local hasAddon = true - local unitId = Amr:GetUnitId(rank.realm, rank.name) - if unitId then - local realm, name = Amr:GetRealmAndName(unitId) - if realm then - local ver = Amr:GetAddonVersion(realm, name) - hasAddon = ver >= Amr.MIN_ADDON_VERSION - end - end - - lbl = rp:GetUserData("noaddonLabel") - if not hasAddon and not isDisenchant then - if not lbl then - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.Red)) - lbl:SetText(L.LootRankLabelNoAddon) - lbl:SetPoint("LEFT", rp.content, "LEFT", _widthRank + _widthColPadding + _widthRankBar + _widthColPadding + 5, 0) - rp:AddChild(lbl) - rp:SetUserData("noaddonLabel", lbl) - end - else - if lbl then - lbl:SetVisible(false) - end - end - - end -end - --- helper to create the column bg and header for rank list -local function createLootRankColumn(container, prevColumn, width, txt, txtAlign, extraPadding) - extraPadding = extraPadding and extraPadding or 0 - - local panel = AceGUI:Create("AmrUiPanel") - panel:SetBackgroundColor(Amr.Colors.Black, 0.3) - container:AddChild(panel) - - if prevColumn then - -- pad a bit to right of previous column - panel:SetPoint("TOPLEFT", prevColumn.content, "TOPRIGHT", _widthColPadding + extraPadding, 0) - panel:SetPoint("BOTTOMRIGHT", prevColumn.content, "BOTTOMRIGHT", _widthColPadding + extraPadding + width, 0) - else - -- first column abs position in the main ranking panel - panel:SetPoint("TOPLEFT", container.content, "TOPLEFT", _widthItemList + _widthSpacing, -115) - panel:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMLEFT", _widthItemList + _widthSpacing + width, 0) - end - - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWordWrap(false) - lbl:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.TextHeaderDisabled)) - lbl:SetText(txt) - lbl:SetJustifyH(txtAlign) - lbl:SetWidth(width) - lbl:SetPoint("BOTTOMLEFT", panel.content, "TOPLEFT", 0, 5) - container:AddChild(lbl) - - return panel, lbl -end - -function Amr:RefreshLootWindow() - if not _panelLoot then return end - - -- clear out any children of the main loot frame and re-render - _panelLoot:ReleaseChildren() - _rankPanels = {} - _lootButtons = {} - - local ml = IsMasterLooter() - local myUnitId = Amr:GetUnitId(GetRealmName(), UnitName("player")) - - local rankings = Amr.db.global.TeamOpt.Rankings - if rankings and #rankings > 0 then - - -- make sure that an item is selected - if not _selectedIndex then - for i, ranking in ipairs(rankings) do - if not ranking.given then - _selectedIndex = i - break - end - end - end - - -- render list of items - local panelItems = AceGUI:Create("AmrUiPanel") - panelItems:SetLayout("Fill") - panelItems:SetWidth(_widthItemList) - panelItems:SetBackgroundColor(Amr.Colors.Black, 0) - panelItems:SetPoint("TOPLEFT", _panelLoot.content, "TOPLEFT", 0, 0) - panelItems:SetPoint("BOTTOMLEFT", _panelLoot.content, "BOTTOMLEFT") - _panelLoot:AddChild(panelItems) - - local scrollItems = AceGUI:Create("AmrUiScrollFrame") - scrollItems:SetLayout("List") - panelItems:AddChild(scrollItems) - - -- render the divider between items and ranks - local divider = AceGUI:Create("AmrUiPanel") - divider:SetBackgroundColor(Amr.Colors.Black, 0.5) - divider:SetPoint("TOPLEFT", _panelLoot.content, "TOPLEFT", _widthItemList, 0) - divider:SetPoint("BOTTOMRIGHT", _panelLoot.content, "BOTTOMLEFT", _widthItemList + 5, 0) - _panelLoot:AddChild(divider) - - local btn, btn2, lbl, lbl2, panel, panel2, chk - - local remainingItems = {} - for i, ranking in ipairs(rankings) do - if not ranking.given then - remainingItems[i] = ranking - end - end - - for i, ranking in pairs(remainingItems) do - btn = AceGUI:Create("AmrUiTextButton") - btn:SetWidth(_widthItemList) - btn:SetHeight(50) - btn:SetJustifyH("LEFT") - btn:SetJustifyV("TOP") - btn:SetTextPadding(9, nil, nil, 2) - btn:SetWordWrap(false) - btn:SetUserData("index", i) - - local f - if _selectedIndex == i then - f = Amr.CreateFont("Bold", 16, Amr.Colors.Text) - btn:SetBackgroundColor(Amr.Colors.Black, 0.5) - btn:SetHoverBackgroundColor(Amr.Colors.Black, 0.5) - else - f = Amr.CreateFont("Regular", 14, Amr.Colors.Text) - btn:SetHoverBackgroundColor(Amr.Colors.Black, 0.2) - end - - btn:SetFont(f) - btn:SetHoverFont(f) - - scrollItems:AddChild(btn) - - btn:SetCallback("OnClick", function(widget) - Amr:SelectLootItem(widget:GetUserData("index")) - end) - - local rankLink = Amr.CreateItemLink(ranking.item) - Amr.GetItemInfo(rankLink, function(obj, name, link) - -- set item name, tooltip - obj:SetText(" " .. link:gsub("%[", ""):gsub("%]", "")) - Amr:SetItemTooltip(obj, link, "ANCHOR_BOTTOMLEFT", 0, obj.frame:GetHeight()) - end, btn) - - -- add a label for slot, armor type - local slotText = Amr.SlotEnumDisplayText[ranking.itemInfo.slot] - if ranking.itemInfo.slot == 'MainHand' then - slotText = ranking.itemInfo.subclass == 'TwoHand' and L.TwoHand or L.OneHand - elseif ranking.itemInfo.slot == 'OffHand' then - slotText = L.OffHand - end - - if ranking.itemInfo.armorType == 'None' and ranking.itemInfo.weaponType ~= 'None' then - if ranking.itemInfo.weaponType ~= 'OffHand' then - slotText = slotText .. ", " .. L.WeaponTypes[ranking.itemInfo.weaponType] - end - elseif ranking.itemInfo.armorType ~= 'None' then - slotText = slotText .. ", " .. L.ArmorTypes[ranking.itemInfo.armorType] - end - - btn:SetSubtextFont(Amr.CreateFont("Regular", 13, Amr.Colors.TextGray)) - btn:SetSubtextJustifyH("LEFT") - btn:SetSubtextJustifyV("BOTTOM") - btn:SetSubtextPadding(nil, nil, 9, 7) - btn:SetSubtextWordWrap(false) - btn:SetSubtext(slotText) - - - local isDisenchant = not not _disenchant[i] - - if _selectedIndex == i then - - -- see if I am in the list - local canLoot = false - for j, rank in ipairs(ranking.ranks) do - local unitId = Amr:GetUnitId(rank.realm, rank.name) - if unitId == myUnitId then - canLoot = not rank.notRanked or rank.offspec - break - end - end - - -- render loot options - if canLoot then - btn = AceGUI:Create("AmrUiButton") - btn:SetWidth(120) - btn:SetHeight(26) - btn:SetFont(Amr.CreateFont("Bold", 15, Amr.Colors.White)) - btn:SetBackgroundColor(Amr.Colors.Green) - btn:SetText(L.TeamLootOptionNeed) - btn:SetPoint("TOPLEFT", _panelLoot.content, "TOPLEFT", _widthItemList + _widthSpacing, -7) - btn:SetCallback("OnClick", function(widget) doRoll("Need") end) - _panelLoot:AddChild(btn) - _lootButtons["Need"] = btn - - btn2 = AceGUI:Create("AmrUiButton") - btn2:SetWidth(120) - btn2:SetHeight(26) - btn2:SetFont(Amr.CreateFont("Bold", 15, Amr.Colors.White)) - btn2:SetBackgroundColor(Amr.Colors.Orange) - btn2:SetText(L.TeamLootOptionPass) - btn2:SetPoint("TOPLEFT", btn.frame, "BOTTOMLEFT", 0, -15) - btn2:SetCallback("OnClick", function(widget) doRoll("Pass") end) - _panelLoot:AddChild(btn2) - _lootButtons["Pass"] = btn2 - - btn = AceGUI:Create("AmrUiButton") - btn:SetWidth(120) - btn:SetHeight(26) - btn:SetFont(Amr.CreateFont("Bold", 15, Amr.Colors.White)) - btn:SetBackgroundColor(Amr.Colors.Blue) - btn:SetText(L.TeamLootOptionOff) - btn:SetPoint("BOTTOMLEFT", btn2.frame, "TOPRIGHT", 15, 15) - btn:SetCallback("OnClick", function(widget) doRoll("Off") end) - _panelLoot:AddChild(btn) - _lootButtons["Off"] = btn - - btn2 = AceGUI:Create("AmrUiButton") - btn2:SetWidth(120) - btn2:SetHeight(26) - btn2:SetFont(Amr.CreateFont("Bold", 15, Amr.Colors.White)) - btn2:SetBackgroundColor(Amr.Colors.Blue) - btn2:SetText(L.TeamLootOptionGreed) - btn2:SetPoint("TOPLEFT", btn.frame, "BOTTOMLEFT", 0, -15) - btn2:SetCallback("OnClick", function(widget) doRoll("Greed") end) - _panelLoot:AddChild(btn2) - _lootButtons["Greed"] = btn2 - else - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan)) - lbl:SetText(L.LootIneligible) - lbl:SetWidth(255) - lbl:SetPoint("TOPLEFT", _panelLoot.content, "TOPLEFT", _widthItemList + _widthSpacing, -7) - _panelLoot:AddChild(lbl) - end - - -- master loot options - if ml then - chk = AceGUI:Create("AmrUiCheckBox") - chk:SetText(L.LootMasterDisenchantText) - chk:SetPoint("TOPRIGHT", _panelLoot.content, "TOPRIGHT", -18, -12) - chk:SetCallback("OnClick", onDisenchantClick) - chk:SetChecked(_disenchant[i]) - _panelLoot:AddChild(chk) - - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(120) - lbl:SetJustifyH("CENTER") - lbl:SetText(L.LootMasterDisenchantLabel) - lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan)) - lbl:SetPoint("TOP", chk.frame, "BOTTOM", 0, -5) - _panelLoot:AddChild(lbl) - - btn2 = AceGUI:Create("AmrUiButton") - btn2:SetWidth(120) - btn2:SetHeight(26) - btn2:SetFont(Amr.CreateFont("Bold", 15, Amr.Colors.White)) - btn2:SetBackgroundColor(Amr.Colors.Green) - btn2:SetText(L.LootMasterRollText) - btn2:SetPoint("RIGHT", chk.frame, "LEFT", -50, 0) - btn2:SetCallback("OnClick", onRollClick) - _panelLoot:AddChild(btn2) - - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(120) - lbl:SetJustifyH("CENTER") - lbl:SetText(L.LootMasterRollLabel) - lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextTan)) - lbl:SetPoint("TOP", btn2.frame, "BOTTOM", 0, -5) - _panelLoot:AddChild(lbl) - - end - - -- backgrounds for the rank list and headers - panel = createLootRankColumn(_panelLoot, nil, _widthRank, isDisenchant and "" or L.LootRankHeaderRank, "RIGHT") - panel = createLootRankColumn(_panelLoot, panel, _widthRankBar, isDisenchant and L.LootRankHeaderScoreDisenchant or L.LootRankHeaderScore, "LEFT") - - if not isDisenchant then - panel = createLootRankColumn(_panelLoot, panel, _widthRollType, L.LootRankHeaderNeed, "CENTER") - panel = createLootRankColumn(_panelLoot, panel, _widthRollType, L.LootRankHeaderOff, "CENTER") - panel = createLootRankColumn(_panelLoot, panel, _widthRollType, L.LootRankHeaderGreed, "CENTER") - panel = createLootRankColumn(_panelLoot, panel, _widthRollType, L.LootRankHeaderPass, "CENTER") - panel = createLootRankColumn(_panelLoot, panel, _widthRollType, L.LootRankHeaderRoll, "RIGHT", _widthRollExtraSpacing) - end - - -- rank list for selected item - panel = AceGUI:Create("AmrUiPanel") - panel:SetLayout("Fill") - panel:SetBackgroundColor(Amr.Colors.Black, 0) - panel:SetPoint("TOPLEFT", _panelLoot.content, "TOPLEFT", _widthItemList + _widthSpacing, -115) - panel:SetPoint("BOTTOMRIGHT", _panelLoot.content, "BOTTOMRIGHT") - _panelLoot:AddChild(panel) - - local scrollRanks = AceGUI:Create("AmrUiScrollFrame") - scrollRanks:SetLayout("List") - panel:AddChild(scrollRanks) - - -- find min and max value, used for sizing the bars - local rankMin = -0.02 - local rankMax = 0.02 - for j, rank in ipairs(ranking.ranks) do - if rank.score < rankMin then - rankMin = rank.score - end - if rank.score > rankMax then - rankMax = rank.score - end - end - - -- just make min less than max if they are the same, doesn't really matter what it is, would be a wacky case - if rankMin == rankMax then - rankMin = rankMax - 1 - end - - local minWidth = 10 - local maxWidth = _widthRankBar - 36 - 65 - 2 -- reserve 36 for icon, 65 for bar label, and 2 for a border around the bar - local rankCount = 0 - - for j, rank in ipairs(ranking.ranks) do - local unitId = Amr:GetUnitId(rank.realm, rank.name) - if unitId then - local skip = false - if isDisenchant then - skip = true - if rank.isMasterLooter or (rank.enchantingSkill and rank.enchantingSkill > 0) then - skip = false - end - end - - if not skip then - rankCount = rankCount + 1 - - local rp = AceGUI:Create("AmrUiPanel") - rp:SetLayout("None") - rp:SetBackgroundColor(Amr.Colors.Black, 0) - rp:SetWidth(_widthRankList) - rp:SetHeight(45) - scrollRanks:AddChild(rp) - _rankPanels[j] = rp - - if not isDisenchant then - panel = AceGUI:Create("AmrUiPanel") - panel:SetBackgroundColor(Amr.Colors.Black, 1) - panel:SetPoint("TOPLEFT", rp.content, "BOTTOMLEFT", 0, 0) - panel:SetPoint("BOTTOMRIGHT", rp.content, "BOTTOMRIGHT", -120, -1) - rp:AddChild(panel) - end - - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetFont(Amr.CreateFont("Bold", 32, Amr.Colors.White)) - lbl:SetText(rankCount) - lbl:SetWidth(_widthRank - 6) - lbl:SetJustifyH("RIGHT") - lbl:SetPoint("BOTTOMLEFT", rp.content, "BOTTOMLEFT", 0, 3) - rp:AddChild(lbl) - - local cls, clsEn = UnitClass(unitId) - local color = clsEn and Amr.Colors.Classes[clsEn] or Amr.Colors.TextHeaderDisabled - - local icon = AceGUI:Create("AmrUiIcon") - icon:SetIconBorderColor(color) - icon:SetWidth(36) - icon:SetHeight(36) - icon:SetIcon("Interface\\Icons\\" .. Amr.SpecIcons[rank.specId]) - icon:SetPoint("BOTTOMLEFT", rp.content, "BOTTOMLEFT", 48 + 8, 0) - rp:AddChild(icon) - - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetFont(Amr.CreateFont("Bold", 16, color)) - lbl:SetText(UnitName(unitId)) - lbl:SetWidth(_widthRankBar - 36 - 8) -- 4px on left and right side - lbl:SetPoint("TOPLEFT", icon.frame, "TOPRIGHT", 4, -2) - rp:AddChild(lbl) - - if isDisenchant or rank.notRanked then - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetFont(Amr.CreateFont("Italic", 13, Amr.Colors.TextHeaderDisabled)) - lbl:SetWidth(_widthRankBar - 36 - 4) -- 4px on left side - lbl:SetWordWrap(false) - lbl:SetPoint("BOTTOMLEFT", icon.frame, "BOTTOMRIGHT", 4, 2) - rp:AddChild(lbl) - - if isDisenchant then - -- will be disenchanter or ML if we are DEing the item - lbl:SetText((rank.enchantingSkill and rank.enchantingSkill > 0) and string.format(L.LootRankLabelDisenchant .. " (%d)", rank.enchantingSkill) or L.LootRankLabelMasterLooter) - else - -- if this is off spec or just a disenchanter, no score bar just description text - lbl:SetText(rank.offspec and L.LootRankLabelOff or ((rank.enchantingSkill and rank.enchantingSkill > 0) and string.format(L.LootRankLabelDisenchant .. " (%d)", rank.enchantingSkill) or L.LootRankLabelMasterLooter)) - end - else - local scoreText = rank.score .. "%" - local val = rank.score; - if rank.isEquipped then - scoreText = "E" - val = 0 - elseif val >= 0 then - scoreText = "+" .. scoreText - end - - local per = (val - rankMin) / (rankMax - rankMin); - local w = minWidth + (per * (maxWidth - minWidth)); - color = val > 0 and Amr.Colors.BarHigh or (val == 0 and Amr.Colors.BarMed or Amr.Colors.BarLow) - - panel = AceGUI:Create("AmrUiPanel") - panel:SetLayout("None") - panel:SetWidth(w + 2) - panel:SetHeight(16) - panel:SetBackgroundColor(Amr.Colors.Black, 1) - panel:SetPoint("BOTTOMLEFT", icon.frame, "BOTTOMRIGHT", 0, -1) - rp:AddChild(panel) - - panel2 = AceGUI:Create("AmrUiPanel") - panel2:SetLayout("None") - panel2:SetWidth(w) - panel2:SetHeight(14) - panel2:SetBackgroundColor(color, 1) - panel2:SetPoint("TOPLEFT", panel.content, "TOPLEFT", 1, -1) - panel:AddChild(panel2) - - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetFont(Amr.CreateFont("Bold", 13, color)) - lbl:SetText(scoreText) - lbl:SetWidth(63) - lbl:SetWordWrap(false) - lbl:SetPoint("LEFT", panel.content, "RIGHT", 2, 0) - rp:AddChild(lbl) - end - - if ml then - btn2 = AceGUI:Create("AmrUiButton") - btn2:SetHeight(24) - btn2:SetFont(Amr.CreateFont("Regular", 13, Amr.Colors.White)) - btn2:SetBackgroundColor(Amr.Colors.Green) - - if isDisenchant then - btn2:SetWidth(200) - btn2:SetText(L.LootMasterGiveDisenchant) - btn2:SetPoint("LEFT", rp.content, "LEFT", _widthRank + _widthRankBar + (3 * _widthColPadding), 0) - else - btn2:SetWidth(85) - btn2:SetText(L.LootMasterGiveLoot) - btn2:SetPoint("RIGHT", rp.content, "RIGHT", -30, 0) - end - - btn2:SetUserData("index", j) - btn2:SetCallback("OnClick", onGiveLootClick) - rp:AddChild(btn2) - end - end - - end - end - - end - - end - - else - local lbl = AceGUI:Create("AmrUiLabel") - lbl:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan)) - lbl:SetWidth(800) - lbl:SetText(L.LootEmpty) - lbl:SetPoint("CENTER", _panelLoot.content, "CENTER") - _panelLoot:AddChild(lbl) - end - -end - --- select a particular loot item to display -function Amr:SelectLootItem(index) - _selectedIndex = index - self:RefreshLootWindow() - self:RefreshLootRolls() -end - -local function onLootFrameClose(widget) - AceGUI:Release(widget) - _frameLoot = nil - _panelLoot = nil - _rankPanels = nil - _lootButtons = nil -end - -function Amr:HideLootWindow() - if not _frameLoot then return end - _frameLoot:Hide() -end - -function Amr:ShowLootWindow() - if not _frameLoot then - _frameLoot = AceGUI:Create("AmrUiFrame") - _frameLoot:SetStatusTable(Amr.db.profile.lootWindow) -- window position is remembered in db - _frameLoot:SetCallback("OnClose", onLootFrameClose) - _frameLoot:SetLayout("None") - _frameLoot:SetWidth(900) - _frameLoot:SetHeight(600) - _frameLoot:SetBorderColor(Amr.Colors.BorderBlue) - _frameLoot:SetBackgroundColor(Amr.Colors.Bg) - - if Amr.db.profile.options.uiScale ~= 1 then - local scale = tonumber(Amr.db.profile.options.uiScale) - _frameLoot:SetScale(scale) - end - - local lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(600) - lbl:SetFont(Amr.CreateFont("Bold", 28, Amr.Colors.White)) - lbl:SetText(L.LootTitle) - lbl:SetWordWrap(false) - lbl:SetJustifyH("CENTER") - lbl:SetPoint("TOP", _frameLoot.content, "TOP", 0, 30) - _frameLoot:AddChild(lbl) - - lbl:SetCallback("OnMouseDown", function(widget) _frameLoot:StartMove() end) - lbl:SetCallback("OnMouseUp", function(widget) _frameLoot:EndMove() end) - - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(_widthItemList) - lbl:SetFont(Amr.CreateFont("Regular", 18, Amr.Colors.TextHeaderActive)) - lbl:SetText(L.LootHelpItems) - lbl:SetPoint("TOPLEFT", _frameLoot.content, "TOPLEFT", 0, -10) - _frameLoot:AddChild(lbl) - - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(_widthItemList) - lbl:SetFont(Amr.CreateFont("Regular", 18, Amr.Colors.TextHeaderActive)) - lbl:SetText(L.LootHelpRanks) - lbl:SetPoint("TOPLEFT", _frameLoot.content, "TOPLEFT", _widthItemList + _widthSpacing, -10) - _frameLoot:AddChild(lbl) - - if IsMasterLooter() then - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(_widthItemList) - lbl:SetFont(Amr.CreateFont("Regular", 18, Amr.Colors.TextHeaderActive)) - lbl:SetText(L.LootHelpMaster) - lbl:SetPoint("TOPLEFT", _frameLoot.content, "TOPLEFT", _widthItemList + _widthSpacing + _widthRank + _widthRankBar + _widthRollType + (_widthColPadding * 3), -10) - _frameLoot:AddChild(lbl) - end - - _panelLoot = AceGUI:Create("AmrUiPanel") - _panelLoot:SetLayout("None") - _panelLoot:SetBackgroundColor(Amr.Colors.Black, 0) - _panelLoot:SetPoint("TOPLEFT", _frameLoot.content, "TOPLEFT", 0, -40) - _panelLoot:SetPoint("BOTTOMRIGHT", _frameLoot.content, "BOTTOMRIGHT", 0, 0) - _frameLoot:AddChild(_panelLoot) - else - _frameLoot:Show() - end - - _frameLoot:Raise() -end - -function Amr:OnStartLootReceived(parts) - local data = {} - for i = 2, #parts do - table.insert(data, parts[i]) - end - data = table.concat(data, "\n") - - -- reset rankings to the new data sent out by person in control - local rankings = Amr:ParseRankingString(data) - Amr.db.global.TeamOpt.Rankings = rankings - - -- reset disenchant state - _disenchant = {} - - -- reset roll information when loot is started - local rolls = {} - for i = 1, #rankings do - table.insert(rolls, {}) - end - Amr.db.char.TeamOpt.Rolls = rolls - - -- select first item by default - _selectedIndex = #rankings > 0 and 1 or nil - - -- begin looting - Amr.db.char.TeamOpt.LootInProgress = true - - Amr:RefreshTeamUi() - Amr:ShowLootWindow() - Amr:RefreshLootWindow() -end
--- a/TeamOptimizer.lua Tue Jul 17 09:57:39 2018 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1417 +0,0 @@ -local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot") -local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true) -local AceGUI = LibStub("AceGUI-3.0") - -local _panelSplash -local _panelStartLoot -local _lblStartLoot -local _btnStartLoot -local _scrollHistory -local _tabs - -local _messagePrefixes = { - RosterRequestGear = "_TRR", - RosterGear = "_TRG", - ItemExportRequestGear = "_TLR", - ItemExportGear = "_TLG", - ItemExportLoot = "_TLL", - SyncRequest = "_TSR", - Sync = "_TSS" -} - -Amr.LootMessagePrefixes = { - Start = "_TCS", - Roll = "_TCR", - Veto = "_TCV", - Rand = "_TCD", - Give = "_TCG", - Finish = "_TCF" -} - -local function renderExportWindow(container, instructions, text) - - local bg = Amr:RenderCoverChrome(container, 800, 450) - - local lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(750) - lbl:SetText(L.TeamExportHelp) - lbl:SetPoint("TOP", bg.content, "TOP", 0, -10) - bg:AddChild(lbl) - - local lbl2 = AceGUI:Create("AmrUiLabel") - lbl2:SetWidth(750) - lbl2:SetText(instructions) - lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -10) - bg:AddChild(lbl2) - - local txt = AceGUI:Create("AmrUiTextarea") - txt:SetWidth(750) - txt:SetHeight(300) - txt:SetPoint("TOP", lbl2.frame, "BOTTOM", 0, -10) - txt:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.Text)) - txt:SetText(text) - bg:AddChild(txt) - - local btn = AceGUI:Create("AmrUiButton") - btn:SetText(L.TeamButtonExportClose) - btn:SetBackgroundColor(Amr.Colors.Green) - btn:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) - btn:SetWidth(120) - btn:SetHeight(28) - btn:SetPoint("TOPLEFT", txt.frame, "BOTTOMLEFT", 0, -10) - btn:SetCallback("OnClick", function(widget) Amr:HideCover() end) - bg:AddChild(btn) - - return txt -end - -local function renderImportWindow(container) - - local bg = Amr:RenderCoverChrome(container, 700, 450) - - local lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(600) - lbl:SetText(L.TeamImportRankingsHeader) - lbl:SetPoint("TOP", bg.content, "TOP", 0, -10) - bg:AddChild(lbl) - - local txt = AceGUI:Create("AmrUiTextarea") - txt:SetWidth(600) - txt:SetHeight(300) - txt:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -10) - txt:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.Text)) - bg:AddChild(txt) - - local btnImportOk = AceGUI:Create("AmrUiButton") - btnImportOk:SetText(L.ImportButtonOk) - btnImportOk:SetBackgroundColor(Amr.Colors.Green) - btnImportOk:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) - btnImportOk:SetWidth(120) - btnImportOk:SetHeight(28) - btnImportOk:SetPoint("TOPLEFT", txt.frame, "BOTTOMLEFT", 0, -10) - bg:AddChild(btnImportOk) - - local btnImportCancel = AceGUI:Create("AmrUiButton") - btnImportCancel:SetText(L.ImportButtonCancel) - btnImportCancel:SetBackgroundColor(Amr.Colors.Green) - btnImportCancel:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) - btnImportCancel:SetWidth(120) - btnImportCancel:SetHeight(28) - btnImportCancel:SetPoint("LEFT", btnImportOk.frame, "RIGHT", 20, 0) - btnImportCancel:SetCallback("OnClick", function(widget) Amr:HideCover() end) - bg:AddChild(btnImportCancel) - - local lblErr = AceGUI:Create("AmrUiLabel") - lblErr:SetWidth(600) - lblErr:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.Red)) - lblErr:SetText("") - lblErr:SetPoint("TOPLEFT", btnImportOk.frame, "BOTTOMLEFT", 0, -20) - bg:AddChild(lblErr) - - btnImportOk:SetCallback("OnClick", function(widget) - local msg = txt:GetText() - local err = Amr:ImportRankings(msg) - if err then - lblErr:SetText(err) - txt:SetFocus(true) - else - Amr:HideCover() - Amr:RefreshTeamUi() - end - end) - - return txt -end - -local function renderVersionWindow(container) - - local windowWidth = 500 - local lbl, lbl2 - local bg, border = Amr:RenderCoverChrome(container, windowWidth, 600) - - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(windowWidth - 60) - lbl:SetJustifyH("CENTER") - lbl:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.TextHeaderActive)) - lbl:SetText(L.TeamVersionTitle) - lbl:SetPoint("TOP", bg.content, "TOP", 0, -10) - bg:AddChild(lbl) - - if not IsInGroup() and not IsInRaid() then - lbl2 = AceGUI:Create("AmrUiLabel") - lbl2:SetWidth(windowWidth - 20) - lbl2:SetJustifyH("CENTER") - lbl2:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan)) - lbl2:SetText(L.TeamVersionNoGroup) - lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -25) - bg:AddChild(lbl2) - border:SetHeight(150) - else - local units = Amr:GetGroupUnitIdentifiers() - - local missing = {} - local tooLow = {} - - for i, unitId in ipairs(units) do - local realm, name = Amr:GetRealmAndName(unitId) - if realm then - local ver = Amr:GetAddonVersion(realm, name) - if ver == 0 then - table.insert(missing, { unitId, realm, name }) - elseif ver < Amr.MIN_ADDON_VERSION then - table.insert(tooLow, { unitId, realm, name, ver }) - end - end - end - - if #missing == 0 and #tooLow == 0 then - lbl2 = AceGUI:Create("AmrUiLabel") - lbl2:SetWidth(windowWidth - 20) - lbl2:SetJustifyH("CENTER") - lbl2:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan)) - lbl2:SetText(L.TeamVersionGood) - lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -25) - bg:AddChild(lbl2) - border:SetHeight(150) - else - local prev = lbl - local h = 0 - - -- helper to render a player name - local function renderItem(obj, showVer) - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(120) - - local cls, clsEn = UnitClass(obj[1]) - local color = clsEn and Amr.Colors.Classes[clsEn] or Amr.Colors.TextHeaderDisabled - lbl:SetFont(Amr.CreateFont("Regular", 14, color)) - - lbl:SetText(obj[3]) - lbl:SetPoint("TOPLEFT", prev.frame, "BOTTOMLEFT", 0, -5) - bg:AddChild(lbl) - prev = lbl - h = h + lbl:GetHeight() + 5 - - if showVer then - lbl2 = AceGUI:Create("AmrUiLabel") - lbl2:SetWidth(60) - lbl2:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White)) - lbl2:SetText("v" .. obj[4]) - lbl2:SetPoint("LEFT", lbl.frame, "RIGHT", 5, 0) - bg:AddChild(lbl2) - end - end - - if #missing > 0 then - lbl2 = AceGUI:Create("AmrUiLabel") - lbl2:SetWidth(180) - lbl2:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.Red)) - lbl2:SetText(L.TeamVersionMissing) - lbl2:SetJustifyH("CENTER") - lbl2:SetPoint("TOP", prev.frame, "BOTTOM", 0, -20) - bg:AddChild(lbl2) - h = h + lbl2:GetHeight() + 20 - - prev = lbl2 - for i, obj in ipairs(missing) do - renderItem(obj) - end - end - - if #tooLow > 0 then - lbl2 = AceGUI:Create("AmrUiLabel") - lbl2:SetWidth(180) - lbl2:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.Gold)) - lbl2:SetText(L.TeamVersionOld) - lbl2:SetJustifyH("CENTER") - lbl2:SetPoint("TOP", prev.frame, "BOTTOM", 0, -20) - bg:AddChild(lbl2) - h = h + lbl2:GetHeight() + 20 - - prev = lbl2 - for i, obj in ipairs(tooLow) do - renderItem(obj, true) - end - end - - border:SetHeight(h + 100) - end - end - - local btn = AceGUI:Create("AmrUiButton") - btn:SetText(L.TeamButtonExportClose) - btn:SetBackgroundColor(Amr.Colors.Green) - btn:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) - btn:SetWidth(120) - btn:SetHeight(28) - btn:SetPoint("BOTTOM", bg.content, "BOTTOM", 0, 10) - btn:SetCallback("OnClick", function(widget) Amr:HideCover() end) - bg:AddChild(btn) -end - -local function onVersionClick() - -- show a window with players who do not have the addon or too low a version - Amr:ShowCover(renderVersionWindow) -end - -local function onExportRosterClick() - - Amr:ShowCover(L.TeamExportRosterLoading) - - Amr:ExportRosterAsync(function(txt) - Amr:HideCover() - - if not txt then - Amr:ShowAlert(L.TeamAlertNoGroup, L.AlertOk) - return - end - - Amr:ShowCover(function(container) - local textbox = renderExportWindow(container, L.TeamExportRosterText, txt) - textbox:SetFocus(true) - end) - end) - -end - -local function onExportLootClick() - - Amr:ShowCover(L.TeamExportRosterLoading) - - Amr:ExportLootAsync(function(txt) - Amr:HideCover() - - if txt == "NOGROUP" then - Amr:ShowAlert(L.TeamAlertNoGroup, L.AlertOk) - return - elseif txt == "NOLOOT" then - Amr:ShowAlert(L.TeamAlertNoLoot, L.AlertOk) - return - else - Amr:ShowCover(function(container) - local textbox = renderExportWindow(container, L.TeamExportLootText, txt) - textbox:SetFocus(true) - end) - end - end) -end - -local function onImportRankingsClick() - Amr:ShowCover(function(container) - local textbox = renderImportWindow(container) - textbox:SetFocus(true) - end) -end - -local function renderTab(tab, container) - - local lbl, lbl2 - - if tab == "Member" then - local lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(500) - lbl:SetFont(Amr.CreateFont("Regular", 24, Amr.Colors.TextTan)) - lbl:SetText(L.TeamMemberText) - lbl:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -40) - container:AddChild(lbl) - - -- if loot is still going on, show a button to re-show the loot window - if Amr.db.char.TeamOpt.LootInProgress then - lbl2 = AceGUI:Create("AmrUiLabel") - lbl2:SetWidth(500) - lbl2:SetFont(Amr.CreateFont("Italic", 18, Amr.Colors.TextTan)) - lbl2:SetText(L.TeamMemberShowLootLabel) - lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -60) - container:AddChild(lbl2) - - local btn = AceGUI:Create("AmrUiButton") - btn:SetWidth(180) - btn:SetHeight(26) - btn:SetBackgroundColor(Amr.Colors.Blue) - btn:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) - btn:SetText(L.TeamMemberShowLoot) - btn:SetPoint("TOPLEFT", lbl2.frame, "BOTTOMLEFT", 0, -10) - btn:SetCallback("OnClick", function(widget) - Amr:ShowLootWindow() - Amr:RefreshLootWindow() - Amr:RefreshLootRolls() - end) - container:AddChild(btn) - end - - elseif tab == "Leader" then - - local lblNum = AceGUI:Create("AmrUiLabel") - lblNum:SetFont(Amr.CreateFont("Bold", 26, Amr.Colors.White)) - lblNum:SetText("0.") - lblNum:SetWidth(40) - lblNum:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, -40) - container:AddChild(lblNum) - - local btnVersion = AceGUI:Create("AmrUiButton") - btnVersion:SetText(L.TeamButtonVersionText) - btnVersion:SetBackgroundColor(Amr.Colors.Orange) - btnVersion:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) - btnVersion:SetWidth(180) - btnVersion:SetHeight(26) - btnVersion:SetPoint("LEFT", lblNum.frame, "RIGHT", 0, -1) - btnVersion:SetCallback("OnClick", onVersionClick) - container:AddChild(btnVersion) - - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan)) - lbl:SetText(L.TeamExportVersionLabel) - lbl:SetWidth(400) - lbl:SetPoint("TOPLEFT", btnVersion.frame, "TOPRIGHT", 20, 0) - container:AddChild(lbl) - - lblNum = AceGUI:Create("AmrUiLabel") - lblNum:SetFont(Amr.CreateFont("Bold", 26, Amr.Colors.White)) - lblNum:SetText("1.") - lblNum:SetWidth(40) - lblNum:SetPoint("TOPRIGHT", btnVersion.frame, "BOTTOMLEFT", 0, -39) - container:AddChild(lblNum) - - local btnRoster = AceGUI:Create("AmrUiButton") - btnRoster:SetText(L.TeamButtonExportRosterText) - btnRoster:SetBackgroundColor(Amr.Colors.Orange) - btnRoster:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) - btnRoster:SetWidth(180) - btnRoster:SetHeight(26) - btnRoster:SetPoint("LEFT", lblNum.frame, "RIGHT", 0, -1) - btnRoster:SetCallback("OnClick", onExportRosterClick) - container:AddChild(btnRoster) - - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan)) - lbl:SetText(L.TeamExportRosterLabel) - lbl:SetWidth(400) - lbl:SetPoint("TOPLEFT", btnRoster.frame, "TOPRIGHT", 20, 0) - container:AddChild(lbl) - - lblNum = AceGUI:Create("AmrUiLabel") - lblNum:SetFont(Amr.CreateFont("Bold", 26, Amr.Colors.White)) - lblNum:SetText("2.") - lblNum:SetWidth(40) - lblNum:SetPoint("TOPRIGHT", btnRoster.frame, "BOTTOMLEFT", 0, -89) - container:AddChild(lblNum) - - local btnLoot = AceGUI:Create("AmrUiButton") - btnLoot:SetText(L.TeamButtonExportLootText) - btnLoot:SetBackgroundColor(Amr.Colors.Orange) - btnLoot:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) - btnLoot:SetWidth(180) - btnLoot:SetHeight(26) - btnLoot:SetPoint("LEFT", lblNum.frame, "RIGHT", 0, -1) - btnLoot:SetCallback("OnClick", onExportLootClick) - container:AddChild(btnLoot) - - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan)) - lbl:SetText(L.TeamExportLootLabel) - lbl:SetWidth(400) - lbl:SetPoint("TOPLEFT", btnLoot.frame, "TOPRIGHT", 20, 0) - container:AddChild(lbl) - - lbl2 = AceGUI:Create("AmrUiLabel") - lbl2:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.Blue)) - lbl2:SetText(L.TeamExportLootLabel2) - lbl2:SetWidth(400) - lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -5) - container:AddChild(lbl2) - - lblNum = AceGUI:Create("AmrUiLabel") - lblNum:SetFont(Amr.CreateFont("Bold", 26, Amr.Colors.White)) - lblNum:SetText("3.") - lblNum:SetWidth(40) - lblNum:SetPoint("TOPRIGHT", btnLoot.frame, "BOTTOMLEFT", 0, -89) - container:AddChild(lblNum) - - local btnRank = AceGUI:Create("AmrUiButton") - btnRank:SetText(L.TeamButtonImportRankingsText) - btnRank:SetBackgroundColor(Amr.Colors.Green) - btnRank:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) - btnRank:SetWidth(180) - btnRank:SetHeight(26) - btnRank:SetPoint("LEFT", lblNum.frame, "RIGHT", 0, -1) - btnRank:SetCallback("OnClick", onImportRankingsClick) - container:AddChild(btnRank) - - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan)) - lbl:SetText(L.TeamImportRankingsLabel) - lbl:SetWidth(400) - lbl:SetPoint("TOPLEFT", btnRank.frame, "TOPRIGHT", 20, 0) - container:AddChild(lbl) - - _panelStartLoot = AceGUI:Create("AmrUiPanel") - _panelStartLoot:SetLayout("None") - _panelStartLoot:SetBackgroundColor(Amr.Colors.Black, 0) - _panelStartLoot:SetPoint("TOPLEFT", lblNum.frame, "BOTTOMLEFT", 0, -90) - container:AddChild(_panelStartLoot) - _panelStartLoot:SetVisible(false) - - lblNum = AceGUI:Create("AmrUiLabel") - lblNum:SetFont(Amr.CreateFont("Bold", 26, Amr.Colors.White)) - lblNum:SetText("4.") - lblNum:SetWidth(40) - lblNum:SetPoint("TOPLEFT", _panelStartLoot.content, "TOPLEFT") - _panelStartLoot:AddChild(lblNum) - - _btnStartLoot = AceGUI:Create("AmrUiButton") - _btnStartLoot:SetText(L.TeamButtonStartLootText) - _btnStartLoot:SetBackgroundColor(Amr.Colors.Blue) - _btnStartLoot:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White)) - _btnStartLoot:SetWidth(180) - _btnStartLoot:SetHeight(26) - _btnStartLoot:SetPoint("LEFT", lblNum.frame, "RIGHT", 0, -1) - _btnStartLoot:SetCallback("OnClick", function(widget) - if Amr.db.char.TeamOpt.LootInProgress then - Amr:ShowLootWindow() - Amr:RefreshLootWindow() - Amr:RefreshLootRolls() - else - Amr:StartLoot() - end - end) - _panelStartLoot:AddChild(_btnStartLoot) - - _lblStartLoot = AceGUI:Create("AmrUiLabel") - _lblStartLoot:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.Text)) - _lblStartLoot:SetWidth(400) - _lblStartLoot:SetPoint("LEFT", _btnStartLoot.frame, "RIGHT", 20, 0) - _panelStartLoot:AddChild(_lblStartLoot) - end - - -- loot history shows on either tab - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.TextTan)) - lbl:SetText(L.TeamHistoryTitle) - lbl:SetWidth(280) - lbl:SetPoint("TOPRIGHT", container.content, "TOPRIGHT", 0, -12) - container:AddChild(lbl) - - local panelHistory = AceGUI:Create("AmrUiPanel") - panelHistory:SetLayout("Fill") - panelHistory:SetBackgroundColor(Amr.Colors.Black, 0.3) - panelHistory:SetPoint("TOPRIGHT", lbl.frame, "BOTTOMRIGHT", 0, -5) - panelHistory:SetPoint("BOTTOMLEFT", container.content, "BOTTOMRIGHT", -280, 0) - container:AddChild(panelHistory) - - _scrollHistory = AceGUI:Create("AmrUiScrollFrame") - _scrollHistory:SetLayout("List") - panelHistory:AddChild(_scrollHistory) -end - -local function renderHistory() - if not _scrollHistory then return end - _scrollHistory:ReleaseChildren() - - -- history is list of objects with: - -- link, result, class, name - - local history = Amr.db.char.TeamOpt.History - local historyWidth = 260 - - local emptyMsg = nil - if not IsInGroup() and not IsInRaid() then - emptyMsg = L.TeamHistoryNoGroup - elseif not history or #history == 0 then - emptyMsg = L.TeamHistoryEmpty - end - - if emptyMsg then - local panel = AceGUI:Create("AmrUiPanel") - panel:SetLayout("None") - panel:SetBackgroundColor(Amr.Colors.Black, 0) - panel:SetWidth(historyWidth) - panel:SetHeight(30) - _scrollHistory:AddChild(panel) - - local lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(historyWidth) - lbl:SetJustifyH("CENTER") - lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan)) - lbl:SetText(emptyMsg) - lbl:SetPoint("LEFT", panel.content, "LEFT", 8, 0) - panel:AddChild(lbl) - else - for i = #history, 1, -1 do - local obj = history[i] - local itemLink = obj.link - - local panel = AceGUI:Create("AmrUiPanel") - panel:SetLayout("None") - panel:SetBackgroundColor(Amr.Colors.Black, 0) - panel:SetWidth(historyWidth) - panel:SetHeight(45) - _scrollHistory:AddChild(panel) - - local lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(historyWidth - 5) - lbl:SetWordWrap(false) - lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text)) - lbl:SetPoint("TOPLEFT", panel.content, "TOPLEFT", 5, -5) - panel:AddChild(lbl) - - Amr.GetItemInfo(itemLink, function(obj, name, link) - -- set item name, tooltip - obj:SetText(link:gsub("%[", ""):gsub("%]", "")) - Amr:SetItemTooltip(obj, link, "ANCHOR_BOTTOMRIGHT", 0, obj.frame:GetHeight()) - end, lbl) - - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(historyWidth - 5) - lbl:SetWordWrap(false) - lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.White)) - - if obj.result == "Disenchant" then - lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextHeaderDisabled)) - lbl:SetText(L.TeamLootOptionDisenchant) - else - local color = obj.class and Amr.Colors.Classes[obj.class] or Amr.Colors.TextHeaderDisabled - lbl:SetText((obj.result == "??" and "" or L["TeamLootOption" .. obj.result] .. ": ") .."|c" .. Amr.ColorToHex(color, 1) .. obj.name .. "|r") - end - - lbl:SetPoint("BOTTOMLEFT", panel.content, "BOTTOMLEFT", 5, 8) - panel:AddChild(lbl) - - local line = AceGUI:Create("AmrUiPanel") - line:SetBackgroundColor(Amr.Colors.Black, 1) - line:SetWidth(historyWidth) - line:SetHeight(1) - line:SetPoint("BOTTOM", panel.content, "BOTTOM") - panel:AddChild(line) - end - end -end - -local function onTabSelected(container, event, group) - container:ReleaseChildren() - - -- clear references to tab elements - _panelStartLoot = nil - _lblStartLoot = nil - _btnStartLoot = nil - _scrollHistory = nil - - Amr.db.char.TeamOpt.Role = group - renderTab(group, container) - Amr:RefreshTeamUi() -end - --- renders the main UI for the Team Optimizer tab -function Amr:RenderTabTeam(container) - - -- splash screen to customize team optimizer ui for the user - if not Amr.db.char.TeamOpt.Role then - _panelSplash = AceGUI:Create("AmrUiPanel") - _panelSplash:SetLayout("None") - _panelSplash:SetBackgroundColor(Amr.Colors.Black, 0) - _panelSplash:SetPoint("TOPLEFT", container.content, "TOPLEFT") - _panelSplash:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT") - container:AddChild(_panelSplash) - - local lblSplash = AceGUI:Create("AmrUiLabel") - lblSplash:SetWidth(800) - lblSplash:SetJustifyH("CENTER") - lblSplash:SetFont(Amr.CreateFont("Regular", 24, Amr.Colors.Text)) - lblSplash:SetText(L.TeamSplashHeader) - lblSplash:SetPoint("TOP", _panelSplash.content, "TOP", 0, -40) - _panelSplash:AddChild(lblSplash) - - local btn = AceGUI:Create("AmrUiButton") - btn:SetText(L.TeamTabLeaderText) - btn:SetBackgroundColor(Amr.Colors.Orange) - btn:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.White)) - btn:SetWidth(280) - btn:SetHeight(60) - btn:SetPoint("TOPRIGHT", lblSplash.frame, "BOTTOM", -50, -50) - btn:SetCallback("OnClick", function(widget) - Amr.db.char.TeamOpt.Role = "Leader" - _panelSplash:SetVisible(false) - _tabs:SetVisible(true) - _tabs:SelectTab("Leader") - end) - _panelSplash:AddChild(btn) - - local lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(280) - lbl:SetJustifyH("CENTER") - lbl:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan)) - lbl:SetText(L.TeamSplashLeaderLabel) - lbl:SetPoint("TOP", btn.frame, "BOTTOM", 0, -20) - _panelSplash:AddChild(lbl) - - btn = AceGUI:Create("AmrUiButton") - btn:SetText(L.TeamTabMemberText) - btn:SetBackgroundColor(Amr.Colors.Orange) - btn:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.White)) - btn:SetWidth(280) - btn:SetHeight(60) - btn:SetPoint("TOPLEFT", lblSplash.frame, "BOTTOM", 50, -50) - btn:SetCallback("OnClick", function(widget) - Amr.db.char.TeamOpt.Role = "Member" - _panelSplash:SetVisible(false) - _tabs:SetVisible(true) - _tabs:SelectTab("Member") - end) - _panelSplash:AddChild(btn) - - lbl = AceGUI:Create("AmrUiLabel") - lbl:SetWidth(280) - lbl:SetJustifyH("CENTER") - lbl:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan)) - lbl:SetText(L.TeamSplashMemberLabel) - lbl:SetPoint("TOP", btn.frame, "BOTTOM", 0, -20) - _panelSplash:AddChild(lbl) - end - - -- tabstrip - _tabs = AceGUI:Create("AmrUiTabGroup") - _tabs:SetLayout("None") - _tabs:SetTabs({ - {text=L.TeamTabLeaderText, value="Leader", style="bold"}, - {text=L.TeamTabMemberText, value="Member", style="bold"} - }) - _tabs:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, -30) - _tabs:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT") - _tabs:SetCallback("OnGroupSelected", onTabSelected) - container:AddChild(_tabs) - - local role = Amr.db.char.TeamOpt.Role - - _tabs:SetVisible(not not role) - if role then - -- if a role has been chosen, select the proper tab (which will also refresh the UI) - _tabs:SelectTab(role) - else - -- no role, refresh the UI manually - self:RefreshTeamUi() - end - -end - -function Amr:ReleaseTabTeam() - _panelSplash = nil - _panelStartLoot = nil - _lblStartLoot = nil - _btnStartLoot = nil - _scrollHistory = nil - _tabs = nil -end - -function Amr:RefreshTeamUi() - - -- if rankings have been loaded, render the 'start loot' panel - if _panelStartLoot then - local rankString = Amr.db.global.TeamOpt.RankingString - if rankString then - _panelStartLoot:SetVisible(true) - _lblStartLoot:SetText(L.TeamStartLootLabel(#Amr.db.global.TeamOpt.Rankings)) - _btnStartLoot:SetText(Amr.db.char.TeamOpt.LootInProgress and L.TeamButtonResumeLootText or L.TeamButtonStartLootText) - else - _panelStartLoot:SetVisible(false) - end - end - - -- render loot history - renderHistory() -end - -local function getItemIdsFromLinks(all, list) - for i, v in ipairs(list) do - local obj = Amr.ParseItemLink(v) - local id = Amr.GetItemUniqueId(obj) - if id then - table.insert(all, id) - end - end -end - --- update AllItems, used to determine when a new item is actually a new equippable item -local function snapshotAllItems(data) - - local all = {} - for k, v in pairs(data.Equipped[data.ActiveSpec]) do - local obj = Amr.ParseItemLink(v) - local id = Amr.GetItemUniqueId(obj) - if id then - table.insert(all, id) - end - end - getItemIdsFromLinks(all, data.BagItems) - getItemIdsFromLinks(all, data.BankItems) - getItemIdsFromLinks(all, data.VoidItems) - - table.sort(all) - return all -end - -local function sendGear(prefix, empty) - - local region = Amr.RegionNames[GetCurrentRegion()] - local realm = GetRealmName() - local name = UnitName("player") - - -- get all data, including inventory - local txt = "_" - if not empty then - local data = Amr:ExportCharacter() - txt = Amr.Serializer:SerializePlayerData(data, true) - - -- snapshot items when gear is sent - Amr.db.char.TeamOpt.AllItems = snapshotAllItems(data) - end - - local msg = string.format("%s\n%s\n%s\n%s\n%s", prefix, region, realm, name, txt) - Amr:SendAmrCommMessage(msg) -end - -local function toPlayerKey(realm, name) - return name .. "-" .. realm -end - - ------------------------------------------------------------------------------------------------- --- Loot Export ------------------------------------------------------------------------------------------------- - --- prune out any characters no longer in the player's group -local function pruneGearForItemExport() - - local newInfo = {} - local units = Amr:GetGroupUnitIdentifiers() - - for i, unitId in ipairs(units) do - local realm, name = Amr:GetRealmAndName(unitId) - if realm then - local key = toPlayerKey(realm, name) - newInfo[key] = Amr.db.global.TeamOpt.LootGear[key] - end - end - - Amr.db.global.TeamOpt.LootGear = newInfo -end - -local _ignoreLoot = { - [139562] = true, - [136903] = true -} - -local function scanMasterLoot() - -- only care if we are in a raid or group - if not IsInGroup() and not IsInRaid() then return end - - -- we only care about the master looter - if not IsMasterLooter() then return end - - -- guid of the unit being looted - local npcGuid = UnitGUID("target") - if not npcGuid then - -- this could wack shit out... but no raid bosses drop loot from containers right now, so should be fine - npcGuid = "container" .. time() - end - - -- if we already have loot data for this unit, then we can ignore - if Amr.db.char.TeamOpt.LootGuid == npcGuid then return end - - local loot = {} - for i = 1, GetNumLootItems() do - --local texture, item, quantity, quality, locked = GetLootSlotInfo(i) - local lootType = GetLootSlotType(i) - if lootType == 1 then - local link = GetLootSlotLink(i) - local success, itemObj = pcall(Amr.ParseItemLink, link) - - if not success or not itemObj or _ignoreLoot[itemObj.id] then - -- ignore items that can't be read or that we explicitly don't care about - else - table.insert(loot, link) - end - end - end - - Amr.db.char.TeamOpt.LootGuid = npcGuid - Amr.db.char.TeamOpt.Loot = loot - - -- publish loot information to everyone else running the addon in case team optimizer user is not the master looter - local msg = _messagePrefixes.ItemExportLoot .. "\n" .. npcGuid .. "\n" .. table.concat(loot, "\n") - Amr:SendAmrCommMessage(msg) -end - -local function onLootReceived(parts) - - Amr.db.char.TeamOpt.LootGuid = parts[2] - - local loot = {} - for i = 3, #parts do - table.insert(loot, parts[i]) - end - Amr.db.char.TeamOpt.Loot = loot -end - -local function onLeaveGroup() - -- if the current player is no longer in a group or raid, finish any looting in progress - Amr:FinishLoot(true) - - -- clear loot when leave a group - Amr.db.char.TeamOpt.Loot = {} - Amr.db.char.TeamOpt.LootGuid = nil - Amr.db.global.TeamOpt.LootGear = {} -end - -local function onGroupChanged() - - if not IsInGroup() and not IsInRaid() then - onLeaveGroup() - end -end - - -local _lootExPlayersRemaining = 0 -local _lootExRoster = nil -local _lootExCallback = nil - -local function serializeLootExport() - if not IsInGroup() and not IsInRaid() then return "NOGROUP" end - - local loot = Amr.db.char.TeamOpt.Loot - if not loot or #loot == 0 then return "NOLOOT" end - - local itemObjects = {} - for i, link in ipairs(loot) do - local obj = Amr.ParseItemLink(link) - if obj then - table.insert(itemObjects, obj) - end - end - - -- DEBUG: just grab all currently equipped items - --[[ - itemObjects = {} - local blah = Amr:ExportCharacter() - for k, v in pairs(blah.Equipped[blah.ActiveSpec]) do - local obj = Amr.ParseItemLink(v) - if obj then - table.insert(itemObjects, obj) - end - end - ]] - - local parts = {} - - -- unique ids of items - local lootPart = {} - for i, obj in ipairs(itemObjects) do - table.insert(lootPart, Amr.GetItemUniqueId(obj)) - end - table.insert(parts, table.concat(lootPart, ";")) - - -- gear for players who have gained loot since the last item import or roster export - pruneGearForItemExport() - local lootGear = Amr.db.global.TeamOpt.LootGear - for k, v in pairs(lootGear) do - table.insert(parts, v) - end - - return table.concat(parts, "\n") -end - -local function onLootExportCompleted() - - -- fill in LootGear with just those players who have changed - Amr.db.global.TeamOpt.LootGear = _lootExRoster - - if _lootExCallback then - local txt = serializeLootExport() - _lootExCallback(txt) - end - - -- reset state - _lootExPlayersRemaining = 0 - _lootExRoster = nil - _lootExCallback = nil -end - --- called when this player's gear info has been requested by someone exporting loot -local function onGearForLootExportRequested() - - local hasNewItem = false - local oldItems = Amr.db.char.TeamOpt.AllItems - - if oldItems and #oldItems > 0 then - -- see if any new equippable items have been gained by comparing to the last snapshot - local data = Amr:ExportCharacter() - local allItems = snapshotAllItems(data) - - if #oldItems ~= #allItems then - hasNewItem = true - else - -- go through items from front to back, if there are any that don't match then something has changed - for i = 1, #allItems do - local oldItem = oldItems[i] - local newItem = allItems[i] - if oldItem ~= newItem then - hasNewItem = true - break - end - end - end - end - - -- whenever a new item is received, send out updated gear information that should be added to the next item export - sendGear(_messagePrefixes.ItemExportGear, not hasNewItem) -end - -local function onGearForLootExportReceived(region, realm, name, data) - -- if I am not listening for incoming gear data for an item export, then ignore this message - if _lootExPlayersRemaining == 0 then return end - - local key = toPlayerKey(realm, name) - if not data or data == "_" then - _lootExRoster[key] = nil - else - _lootExRoster[key] = data - end - - _lootExPlayersRemaining = _lootExPlayersRemaining - 1 - if _lootExPlayersRemaining <= 0 then - onLootExportCompleted() - end -end - --- Export the current loot, including any known gear data for players with the in-game addon, but only if it has changed since the last snapshot. --- This is asynchronous because it needs to wait for gear data to arrive from each player. -function Amr:ExportLootAsync(callback) - - if not IsInGroup() and not IsInRaid() then - callback("NOGROUP") - end - - local loot = Amr.db.char.TeamOpt.Loot - if not loot or #loot == 0 then - callback("NOLOOT") - end - - local playersNoGear = {} - _lootExPlayersRemaining = 0 - - local units = self:GetGroupUnitIdentifiers() - for i, unitId in ipairs(units) do - local realm, name = self:GetRealmAndName(unitId) - if realm then - local ver = self:GetAddonVersion(realm, name) - local key = toPlayerKey(realm, name) - - if ver >= Amr.MIN_ADDON_VERSION then - _lootExPlayersRemaining = _lootExPlayersRemaining + 1 - else - table.insert(playersNoGear, unitId) - end - end - end - - _lootExRoster = {} - _lootExCallback = callback - - if _lootExPlayersRemaining > 0 then - -- send a message to receive player data, when the last player is received onLootExportCompleted will be called - Amr:SendAmrCommMessage(_messagePrefixes.ItemExportRequestGear) - else - -- don't need to wait for anybody, just call immediately - onLootExportCompleted() - end -end - - ------------------------------------------------------------------------------------------------- --- Roster Export ------------------------------------------------------------------------------------------------- - -local _rosterPlayersRemaining = 0 -local _roster = nil -local _rosterCallback = nil - -local function onRosterCompleted() - - if _rosterCallback then - -- serialize the roster - local parts = {} - for key, data in pairs(_roster) do - table.insert(parts, data) - end - local msg = table.concat(parts, "\n") - - -- send to callback - _rosterCallback(msg) - end - - -- reset state - _rosterPlayersRemaining = 0 - _roster = nil - _rosterCallback = nil - - -- clear out loot gear needed, an export will refresh everyone at the time of export - Amr.db.global.TeamOpt.LootGear = {} -end - --- called when this player's gear info has been requested by someone exporting the raid roster -local function onGearForRosterRequested() - - sendGear(_messagePrefixes.RosterGear) -end - -local function onGearForRosterReceived(region, realm, name, data) - -- if I am not listening for incoming gear data for the roster, then ignore this message - if _rosterPlayersRemaining == 0 then return end - - local key = toPlayerKey(realm, name) - _roster[key] = data - - _rosterPlayersRemaining = _rosterPlayersRemaining - 1 - if _rosterPlayersRemaining <= 0 then - onRosterCompleted() - end -end - --- Export the current roster, including any known gear data for players with the in-game addon. --- This is asynchronous because it needs to wait for gear data to arrive from each player. -function Amr:ExportRosterAsync(callback) - if not IsInGroup() and not IsInRaid() then - callback() - return - end - - local playersNoGear = {} - _rosterPlayersRemaining = 0 - - local units = self:GetGroupUnitIdentifiers() - for i, unitId in ipairs(units) do - local realm, name = self:GetRealmAndName(unitId) - if realm then - local ver = self:GetAddonVersion(realm, name) - local key = toPlayerKey(realm, name) - - if ver >= Amr.MIN_ADDON_VERSION then - _rosterPlayersRemaining = _rosterPlayersRemaining + 1 - else - table.insert(playersNoGear, unitId) - end - end - end - - -- fill the roster with any players who can't send us data - _roster = {} - for i, unitId in ipairs(playersNoGear) do - local realm, name = self:GetRealmAndName(unitId) - if realm then - local key = toPlayerKey(realm, name) - local obj = { - Region = Amr.RegionNames[GetCurrentRegion()], - Realm = realm, - Name = name - } - _roster[key] = Amr.Serializer:SerializePlayerIdentity(obj) - end - end - - _rosterCallback = callback - - if _rosterPlayersRemaining > 0 then - -- send a message to receive player data, when the last player is received onRosterCompleted will be called - Amr:SendAmrCommMessage(_messagePrefixes.RosterRequestGear) - else - -- don't need to wait for anybody, just call immediately - onRosterCompleted() - end -end - - ------------------------------------------------------------------------------------------------- --- Ranking Import ------------------------------------------------------------------------------------------------- - --- helper to parse import item identifier format into an item object -local function parseItemIdentifier(ident) - - local parts = { strsplit(":", ident) } - local item = {} - item.id = tonumber(parts[1]) - item.enchantId = 0 - item.gemIds = { 0, 0, 0, 0 } - item.suffixId = math.abs(tonumber(parts[2])) - item.upgradeId = tonumber(parts[3]) - item.level = tonumber(parts[4]) - - if #parts > 4 then - item.bonusIds = {} - for b = 5, #parts do - table.insert(item.bonusIds, tonumber(parts[b])) - end - table.sort(item.bonusIds) - end - - return item -end - -function Amr:ParseRankingString(data) - local rankings = {} - - local player = Amr:ExportCharacter() - local myUnitId = Amr:GetUnitId(player.Realm, player.Name) - local ml = IsMasterLooter() - - local itemList = { strsplit("\n", data) } - for i = 1, #itemList do - local ranking = {} - - local itemParts = { strsplit("_", itemList[i]) } - - -- first part has the item identifier - ranking.item = parseItemIdentifier(itemParts[1]) - - -- second part has item info - local infoParts = { strsplit(";", itemParts[2]) } - ranking.itemInfo = { - slot = infoParts[1], - subclass = infoParts[2], - weaponType = infoParts[3], - armorType = infoParts[4] - } - - local meInList = false - - -- parse each ranking - ranking.ranks = {} - for j = 3, #itemParts do - local rankParts = { strsplit(";", itemParts[j]) } - - local rank = {} - rank.realm = rankParts[1] - rank.name = rankParts[2] - rank.specId = tonumber(rankParts[3]) - if rankParts[4] ~= "--" then - rank.equipped = parseItemIdentifier(rankParts[4]) - end - rank.score = tonumber(rankParts[5]) - rank.isEquipped = rankParts[6] == "t" - rank.notRanked = rankParts[7] == "t" - rank.offspec = rankParts[8] == "t" - rank.enchantingSkill = tonumber(rankParts[9]) - - table.insert(ranking.ranks, rank) - - if myUnitId == Amr:GetUnitId(rank.realm, rank.name) then - meInList = true - rank.isMasterLooter = ml - end - end - - -- if the current player is the master looter and he is not in the list, then add him at the end - if ml and not meInList then - local rank = { - realm = player.Realm, - name = player.Name, - specId = player.Specs[player.ActiveSpec], - equipped = "--", - score = 0, - isEquipped = false, - notRanked = true, - offspec = false, - enchantingSkill = 0, - isMasterLooter = true - } - table.insert(ranking.ranks, rank) - end - - table.insert(rankings, ranking) - end - - return rankings -end - --- import rankings from the website, save into the database, returns a string error if can't import for some reason -function Amr:ImportRankings(data) - - if not data or string.len(data) == 0 then - return L.ImportErrorEmpty - end - - local success, rankings = pcall(Amr.ParseRankingString, self, data) - - if not success then - return L.ImportErrorFormat - end - - -- finish any looting in progress, effectively canceling it, user will have to press Start Loot again - Amr:FinishLoot() - - -- save the rankings - Amr.db.global.TeamOpt.Rankings = rankings - Amr.db.global.TeamOpt.RankingString = data - - -- clear loot gear needed on successful ranking import - Amr.db.global.TeamOpt.LootGear = {} -end - - ------------------------------------------------------------------------------------------------- --- Loot Distribution ------------------------------------------------------------------------------------------------- - -function Amr:StartLoot() - - if not IsInGroup() and not IsInRaid() then - Amr:ShowAlert(L.TeamAlertNoGroup, L.AlertOk) - return - end - - -- broadcast the loot data to everyone, this triggers the loot window to show - local msg = string.format("%s\n%s", Amr.LootMessagePrefixes.Start, Amr.db.global.TeamOpt.RankingString) - Amr:SendAmrCommMessage(msg) -end - -function Amr:FinishLoot(clearHistory) - - -- reset all state - Amr.db.char.TeamOpt.LootInProgress = false - - -- don't reset these for now... only reset these if someone leaves a group - --Amr.db.char.TeamOpt.Loot = {} - --Amr.db.char.TeamOpt.LootGuid = nil - --Amr.db.global.TeamOpt.LootGear = {} - - Amr.db.global.TeamOpt.Rankings = {} - Amr.db.global.TeamOpt.RankingString = nil - - Amr.db.char.TeamOpt.Rolls = {} - - if clearHistory then - Amr.db.char.TeamOpt.History = {} - end - - -- close the loot window - Amr:HideLootWindow() - - -- re-render the team optimizer UI - Amr:RefreshTeamUi() -end - - ------------------------------------------------------------------------------------------------- --- Synchronization ------------------------------------------------------------------------------------------------- -local _waitingForSync = false - --- check if we need to synchronize -local function checkSync() - -- if loot is in progress and this person is not the ML, send a request to synchronize on startup, this player may have missed some data - if not IsMasterLooter() and Amr.db.char.TeamOpt.LootInProgress then - _waitingForSync = true - Amr:SendAmrCommMessage(_messagePrefixes.SyncRequest) - end -end - --- send data to anyone who needs to synchronize their loot data: history, rolls, rankings -local function sendSyncData() - -- only the master looter sends sync data to ensure that everyone gets the same stuff and we don't spam - if not IsMasterLooter() then return end - - local msgParts = {} - table.insert(msgParts, _messagePrefixes.Sync) - table.insert(msgParts, Amr:Serialize(Amr.db.char.TeamOpt.History)) - table.insert(msgParts, Amr:Serialize(Amr.db.char.TeamOpt.Rolls)) - table.insert(msgParts, Amr:Serialize(Amr.db.global.TeamOpt.Rankings)) - - Amr:SendAmrCommMessage(table.concat(msgParts, "\n")) -end - -local function receiveSyncData(parts) - if not _waitingForSync then return end - _waitingForSync = false - - local success, obj = Amr:Deserialize(parts[2]) - if success then - Amr.db.char.TeamOpt.History = obj - end - - success, obj = Amr:Deserialize(parts[3]) - if success then - Amr.db.char.TeamOpt.Rolls = obj - end - - success, obj = Amr:Deserialize(parts[4]) - if success then - Amr.db.global.TeamOpt.Rankings = obj - end - - -- refresh any windows that may be visible - Amr:RefreshTeamUi() - Amr:RefreshLootWindow() - Amr:RefreshLootRolls() -end - - -function Amr:ProcessTeamMessage(message) - - local parts = {} - for part in string.gmatch(message, "([^\n]+)") do - table.insert(parts, part) - end - - local prefix = parts[1] - - if prefix == _messagePrefixes.RosterRequestGear then - -- request for me to send my gear data - onGearForRosterRequested() - elseif prefix == _messagePrefixes.ItemExportRequestGear then - -- request for me to send my gear data - onGearForLootExportRequested() - elseif prefix == _messagePrefixes.ItemExportLoot then - -- the last loot that dropped - onLootReceived(parts) - elseif prefix == _messagePrefixes.SyncRequest then - sendSyncData() - elseif prefix == _messagePrefixes.Sync then - receiveSyncData(parts) - elseif prefix == Amr.LootMessagePrefixes.Start then - Amr:OnStartLootReceived(parts) - elseif prefix == Amr.LootMessagePrefixes.Roll then - Amr:OnLootRollReceived(parts) - elseif prefix == Amr.LootMessagePrefixes.Veto then - Amr:OnLootVetoReceived(parts) - elseif prefix == Amr.LootMessagePrefixes.Rand then - Amr:OnLootRandReceived(parts) - elseif prefix == Amr.LootMessagePrefixes.Give then - Amr:OnLootGiveReceived(parts) - elseif prefix == Amr.LootMessagePrefixes.Finish then - Amr:FinishLoot() - else - -- message will be of format: prefix\nregion\nrealm\nname\n[stuff] - local region = parts[2] - local realm = parts[3] - local name = parts[4] - local data = parts[5] - - if prefix == _messagePrefixes.RosterGear then - -- receive gear data from someone - onGearForRosterReceived(region, realm, name, data) - elseif prefix == _messagePrefixes.ItemExportGear then - -- receive gear data for item export - onGearForLootExportReceived(region, realm, name, data) - end - end -end - -function Amr:InitializeTeamOpt() - - if not IsInGroup() and not IsInRaid() then - onLeaveGroup() - end - - Amr:AddEventHandler("LOOT_OPENED", scanMasterLoot) - Amr:AddEventHandler("GROUP_ROSTER_UPDATE", onGroupChanged) - - checkSync() -end