diff --git a/.scripts/deploy.sh b/.scripts/deploy.sh index aa00f9de..ac897839 100644 --- a/.scripts/deploy.sh +++ b/.scripts/deploy.sh @@ -15,6 +15,7 @@ usage() { echo " -b Pack to _beta_ WoW edition." >&2 echo " -c Pack to _classic_ WoW edition." >&2 echo " -p Pack to _ptr_ WoW edition." >&2 + echo " -x Pack to _xptr_ WoW edition." >&2 } ADDON_LOC="$(pwd)" @@ -22,14 +23,16 @@ ADDON="$(basename $ADDON_LOC)" WOWEDITION="_retail_" # Commandline inputs -while getopts ":bcp" opt; do +while getopts ":bcpx" opt; do case $opt in b) WOWEDITION="_beta_";; c) WOWEDITION="_classic_";; p) - WOWEDITION="_ptr_";; + WOWEDITION="_ptr_";; + x) + WOWEDITION="_xptr_";; /?) usage ;; esac diff --git a/.specs/Integration/MLLogic.spec.lua b/.specs/Integration/MLLogic.spec.lua index fe311bba..01e69d50 100644 --- a/.specs/Integration/MLLogic.spec.lua +++ b/.specs/Integration/MLLogic.spec.lua @@ -76,4 +76,18 @@ describe("#Integration #MLLogic", function () assert.True(addon.handleLoot) assert.spy(addon.OnStartHandleLoot).was.called(1) end) + + it("should send mldb on reconnects", function() + assert.True(addon.isMasterLooter) + addon.isCouncil = false + addon.mldb = {} + -- We won't receive mldb if we're the ML + addon.isMasterLooter = false + addon.Log:D("Sent reconnect") + RCLootCouncilML:OnReconnectReceived(addon.player:GetName()) + _ADVANCE_TIME(1) + addon.isMasterLooter = true + assert.True(addon.isCouncil) + assert.is_table(addon.mldb.buttons) + end) end) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c1ba8e07..aeb81ff9 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -25,6 +25,15 @@ ], "group": "build" }, + { + "label": "Deploy-Local-XPTR", + "type": "shell", + "command": "sh .scripts/deploy.sh", + "args": [ + "-x" + ], + "group": "build" + }, { "label": "Deploy-Local-Beta", "type": "shell", diff --git a/Core/Defaults.lua b/Core/Defaults.lua index 39807774..df671a86 100644 --- a/Core/Defaults.lua +++ b/Core/Defaults.lua @@ -14,7 +14,8 @@ addon.responses = { WAIT = { color = { 1, 1, 0, 1, }, sort = 503, text = L["Candidate is selecting response, please wait"], }, TIMEOUT = { color = { 1, 0, 0, 1, }, sort = 504, text = L["Candidate didn't respond on time"], }, REMOVED = { color = { 0.8, 0.5, 0, 1, }, sort = 505, text = L["Candidate removed"], }, - NOTHING = { color = { 0.5, 0.5, 0.5, 1, }, sort = 505, text = L["Offline or RCLootCouncil not installed"], }, + NOTHING = { color = { 0.5, 0.5, 0.5, 1, }, sort = 506, text = L["Offline or RCLootCouncil not installed"], }, + NOTELIGIBLE = { color = { 0.8, 0.5, 0, 1, }, sort = 507, text = L.response_NOTELIGIBLE, }, BONUSROLL = { color = { 1, 0.8, 0, 1, }, sort = 510, text = _G.BONUS_ROLL_TOOLTIP_TITLE, }, PASS = { color = { 0.7, 0.7, 0.7, 1, }, sort = 800, text = _G.PASS, }, AUTOPASS = { color = { 0.7, 0.7, 0.7, 1, }, sort = 801, text = L["Autopass"], }, diff --git a/Locale/enUS.lua b/Locale/enUS.lua index e5881c1f..471aaa5f 100644 --- a/Locale/enUS.lua +++ b/Locale/enUS.lua @@ -537,6 +537,7 @@ L.chat_command_start_error_usageNever = "Cannot start: your 'usage' option is se L.chat_command_start_error_onlyUseInRaids = "Cannot start: you're in a party and have the 'only use in raids' option enabled." L.history_export_sheets_tip = "Tab delimited export for Google Sheets and English version of Excel that uses ';' as formula delimiter." L.history_export_excel_international_tip = "Tab delimited export for international version of Excel that uses ',' as formula delimiter." +L.response_NOTELIGIBLE = "Not eligible for this item" L["opt_addButton_desc"] = "Add a new button group for the selected slot." L["opt_autoAddBoEs_name"] = "Auto Add BoEs" L["opt_autoAddBoEs_desc"] = "Automatically add all BoE (Bind on Equip) items to a session." diff --git a/Modules/votingFrame.lua b/Modules/votingFrame.lua index 4465bbaf..81b211b3 100644 --- a/Modules/votingFrame.lua +++ b/Modules/votingFrame.lua @@ -350,33 +350,35 @@ function RCVotingFrame:FetchUnawardedSession () return nil end +function RCVotingFrame:SetupCandidate(t, name,response) + local player = Player:Get(name) + name = player and player.name or name + t.candidates[name] = { + class = player.class or "Unknown", + rank = player.rank or "Unknown", + role = player.role or "NONE", + response = response, + ilvl = "", + diff = "", + gear1 = nil, + gear2 = nil, + votes = 0, + note = nil, + roll = nil, + voters = {}, + haveVoted = false, -- Have we voted for this particular candidate in this session? + } +end + function RCVotingFrame:SetupSession(session, t) t.added = true -- This entry has been initiated t.haveVoted = false -- Have we voted for ANY candidate in this session? t.candidates = {} t.hasRolls = false -- Has random rolls been added to this session? for name in addon:GroupIterator() do - local player = Player:Get(name) - -- REVIEW Seems like we occasionally get wrong/invalid names here. - -- but we still need to create the candidate, so use the name provided by - -- GroupIterator, which should be the same as the one from `Player`. - name = player and player.name or name - t.candidates[name] = { - class = player.class or "Unknown", - rank = player.rank or "Unknown", - role = player.role or "NONE", - response = "ANNOUNCED", - ilvl = "", - diff = "", - gear1 = nil, - gear2 = nil, - votes = 0, - note = nil, - roll = nil, - voters = {}, - haveVoted = false, -- Have we voted for this particular candidate in this session? - } + self:SetupCandidate(t, name, "ANNOUNCED") end + -- Init session toggle sessionButtons[session] = self:UpdateSessionButton(session, t.texture, t.link, t.awarded) sessionButtons[session]:Show() @@ -394,7 +396,7 @@ function RCVotingFrame:Setup(table) sessionButtons[i]:Hide() end session = 1 - self:BuildST() + self.frame.st:SetData(self:BuildSTRows()) self:SwitchSession(session) if addon.isMasterLooter and db.autoAddRolls then self:DoAllRandomRolls() @@ -761,9 +763,61 @@ function RCVotingFrame:OnLootTableAdditionsReceived (_, lt) self:DoRandomRolls(i) end end + self:CheckAndHandleCandidateChanges(oldLenght) self:SwitchSession(session) end +--- Ensures all sessions has the exact same candidates. +---@param oldLastSession integer Last session before adding new sessions +function RCVotingFrame:CheckAndHandleCandidateChanges(oldLastSession) + if oldLastSession == #lootTable then return end + -- Build a list of all candidates registered + -- We only need to check the old last session and the first new session + local candidates = TempTable:Acquire() + for i = oldLastSession, oldLastSession + 1 do + for name in pairs(lootTable[i].candidates) do + candidates[name] = true + end + end + -- Find changed candidates + local hasMissing = false + local addedCandidates = TempTable:Acquire() + for name in pairs(candidates) do + for i = oldLastSession, oldLastSession + 1 do + if not lootTable[i].candidates[name] then + hasMissing = true + addedCandidates[name] = true + end + end + end + -- Setup any added candidates + if hasMissing then + local candidatesInData = TempTable:Acquire() + for _, data in ipairs(self.frame.st.data) do + candidatesInData[data.name] = true + end + local cols = self:BuildSTCols() + for name in pairs(addedCandidates) do + for i in ipairs(lootTable) do + if not lootTable[i].candidates[name] then + self:SetupCandidate(lootTable[i], name, "NOTELIGIBLE") + end + end + -- Any new candidates also needs to be added to row data + if not candidatesInData[name] then + tinsert(self.frame.st.data, {name = name, cols = cols}) + end + end + addon.Log:D("Candidates changed:", #addedCandidates) + TempTable:Release(candidatesInData) + self.frame.st:SortData() + else + addon.Log:D("No changes to candidates") + end + TempTable:Release(candidates) + TempTable:Release(addedCandidates) +end + local itemAwardHistoryCache = {} local function cacheItemAwardHistory(item) local itemID = ItemUtils:GetItemIDFromLink(item) @@ -970,23 +1024,26 @@ function RCVotingFrame:SwitchSession(s) addon:SendMessage("RCSessionChangedPost", s) end -function RCVotingFrame:BuildST() +function RCVotingFrame:BuildSTCols() + local data = {} + for num, col in ipairs(self.scrollCols) do + data[num] = { value = "", colName = col.colName, } + end + return data +end + +function RCVotingFrame:BuildSTRows() local rows = {} local i = 1 - -- We need to build the columns from the data in self.scrollCols - -- We only really need the colName and value to get added + local data = self:BuildSTCols() for name in addon:GroupIterator() do - local data = {} - for num, col in ipairs(self.scrollCols) do - data[num] = {value = "", colName = col.colName} - end rows[i] = { name = name, cols = data, } i = i + 1 end - self.frame.st:SetData(rows) + return rows end local invertedEnumMiscellaneousSubclass = tInvert(Enum.ItemMiscellaneousSubclass) @@ -1665,6 +1722,7 @@ end function RCVotingFrame.filterFunc(table, row) db = addon:Getdb() if not db.modules["RCVotingFrame"].filters then return true end -- db hasn't been initialized, so just show it + if not row then return true end local name = row.name if not (lootTable[session] and lootTable[session].candidates[name]) then ErrorHandler:ThrowSilentError(string.format("Couldn't get rank at session %d for candidate %s", session, tostring(name))) @@ -1704,6 +1762,7 @@ end function ResponseSort(table, rowa, rowb, sortbycol) local column = table.cols[sortbycol] local a, b = table:GetRow(rowa), table:GetRow(rowb); + if not (a and b) then return false end if not (lootTable[session].candidates[a.name] and lootTable[session].candidates[a.name].response) or not (lootTable[session].candidates[b.name] and lootTable[session].candidates[b.name].response) then return true end @@ -1735,8 +1794,8 @@ function GuildRankSort(table, rowa, rowb, sortbycol) local column = table.cols[sortbycol] local a, b = table:GetRow(rowa), table:GetRow(rowb); -- Extract the rank index from the name, fallback to 100 if not found - a = guildRanks[lootTable[session].candidates[a.name].rank] or 100 - b = guildRanks[lootTable[session].candidates[b.name].rank] or 100 + a = a and guildRanks[lootTable[session].candidates[a.name].rank] or 100 + b = b and guildRanks[lootTable[session].candidates[b.name].rank] or 100 if a == b then if column.sortnext then local nextcol = table.cols[column.sortnext]; diff --git a/RCLootCouncil.toc b/RCLootCouncil.toc index 78bbb8cf..547427dd 100644 --- a/RCLootCouncil.toc +++ b/RCLootCouncil.toc @@ -1,8 +1,8 @@ ## Author: Potdisc -## Interface: 110002 -## Notes: Interface for running a Loot Council v3.14.1 +## Interface: 110005 +## Notes: Interface for running a Loot Council v3.14.2 ## Title: RCLootCouncil -## Version: 3.14.1 +## Version: 3.14.2 ## SavedVariables: RCLootCouncilDB, RCLootCouncilLootDB ## OptionalDeps: LibStub, CallbackHandler-1.0, Ace3, lib-st, LibWindow-1.1, LibDialog-1.0 ## X-Curse-Project-ID: 39928 diff --git a/Utils/TrinketData.lua b/Utils/TrinketData.lua index 370f76b8..0afe6e45 100644 --- a/Utils/TrinketData.lua +++ b/Utils/TrinketData.lua @@ -1969,4 +1969,15 @@ _G.RCTrinketSpecs = { -- Grim Batol Heroic (id: 71). -- Grim Batol Mythic Keystone (id: 71). -- Grim Batol Mythic (id: 71). + + -- Blackrock Depths (id: 1301). + [231457] = "2082004030010", -- Bottled Magma, Healer + [231424] = "2082004030010", -- Burst of Knowledge, Healer + [231476] = "2082004030010", -- Dope'rel's Calling Rune, Healer + [231414] = "0241000100024", -- Force of Will, Tank + [231471] = "0365002707767", -- Golem Gearbox, Strength/Agility + [231417] = "0365002707767", -- Hand of Justice, Strength/Agility + [231456] = "0000000700067", -- Heart of Roccor, Strength + [231462] = "0241000100024", -- Molten Furnace, Tank + [231420] = "2082004030010", -- Second Wind, Healer } diff --git a/changelog.md b/changelog.md index 5013b0fb..5b084a66 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,15 @@ +# 3.14.2 + +## Changes + +Updated for patch 11.0.5. + +Added trinkets from Blackrock Depths. + +## Bugfixes + +- *Fixed Voting Frame sometimes breaking when extending a session while there's changes in group composition.* + # 3.14.1 ## Changes