Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Core/Defaults.lua
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ addon.defaults = {
sendHistoryToGuildChannel = false,
savePersonalLoot = true,
saveBonusRolls = true,
saveSessionResponses = true,

-- ML - General - Usage
usage = { -- State of enabledness
Expand Down
3 changes: 3 additions & 0 deletions Locale/enUS.lua
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,9 @@ L.chat_command_start_error_onlyUseInRaids = "Cannot start: you're in a party and
L.chat_restrictions_enabled = "Not currently possible due to Addon Restrictions."
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.history_sessionResponses_tip = "Click to show everyone's response to this item."
L.opt_saveSessionResponses_name = "Save Session Responses"
L.opt_saveSessionResponses_desc = "Check to store everyone's response to awarded items in the loot history, viewable through the history's dice button. As Master Looter this also controls whether responses are collected and sent to the group at all. This will increase the storage used by the loot history."
L.response_NOTELIGIBLE = "Not eligible for this item"
L["opt_addButton_desc"] = "Add a new button group for the selected slot."
L.opt_announceAward_WHISPER_WINNER = "/w winner"
Expand Down
118 changes: 118 additions & 0 deletions Modules/History/lootHistory.lua
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ function LootHistory:OnInitialize()
{name = L["Item"], width = 250, comparesort = self.ItemSort, defaultsort = 1, sortnext = 2}, -- Item string
{name = L["Reason"], width = 220, comparesort = self.ResponseSort, defaultsort = 1, sortnext = 2}, -- Response aka the text supplied to lootDB...response
{ name = L["Notes"], width = 40, },
{name = "", width = ROW_HEIGHT}, -- Session responses button
{name = "", width = ROW_HEIGHT}, -- Delete button
}
filterMenu = _G.MSA_DropDownMenu_Create("RCLootCouncil_LootHistory_FilterMenu", UIParent)
Expand Down Expand Up @@ -107,6 +108,7 @@ function LootHistory:Hide()
self.frame:Hide()
self.moreInfo:Hide()
moreInfo = false
if self.sessionResponsesFrame then self.sessionResponsesFrame:Hide() end
end

function LootHistory:SubscribeToPermanentComms ()
Expand All @@ -132,6 +134,9 @@ function LootHistory:OnHistoryReceived (name, history)
if not db.saveBonusRolls and history.responseID == "BONUS_ROLL" then
return addon.Log:D("Not storing bonus rolls", history.lootWon)
end
if not db.saveSessionResponses then -- Both the ML (collection) and each receiver (storage) must opt in
history.sessionResponses = nil
end
-- v3.15.4 check for old date formats
local d, m, y = strsplit("/", history.date, 3)
if #tostring(d) < 4 then
Expand Down Expand Up @@ -226,6 +231,7 @@ function LootHistory:BuildData()
{value = i.lootWon},
{DoCellUpdate = self.SetCellResponse, args = {color = i.color, response = i.response, responseID = i.responseID or 0, isAwardReason = i.isAwardReason}},
{ DoCellUpdate = self.SetCellNote },
{DoCellUpdate = self.SetCellSessionResponses},
{DoCellUpdate = self.SetCellDelete},
}
}
Expand Down Expand Up @@ -486,6 +492,118 @@ function LootHistory.SetCellNote(rowFrame, frame, data, cols, row, realrow, colu
frame.noteBtn = f
end

function LootHistory.SetCellSessionResponses(rowFrame, frame, data, cols, row, realrow, column, fShow, table, ...)
if not data then return end
local row = data[realrow]
local entry = lootDB[row.name] and lootDB[row.name][row.num]
local f = frame.sessionBtn or CreateFrame("Button", nil, frame)
f:SetSize(ROW_HEIGHT, ROW_HEIGHT)
f:SetPoint("CENTER", frame, "CENTER")
if entry and entry.sessionResponses then
f:SetNormalTexture("Interface/Buttons/UI-GroupLoot-Dice-Up")
f:SetHighlightTexture("Interface/Buttons/UI-GroupLoot-Dice-Highlight")
f:SetScript("OnEnter", function() addon:CreateTooltip(L["Responses"], L["history_sessionResponses_tip"]) end)
f:SetScript("OnLeave", function() addon:HideTooltip() end)
f:SetScript("OnClick", function() LootHistory:ShowSessionResponses(row.name, entry) end)
f:Show()
else
f:Hide()
end
frame.sessionBtn = f
end

function LootHistory.SetCellWinnerIndicator(rowFrame, frame, data, cols, row, realrow, column, fShow, table, ...)
if not frame.winnerTex then
frame.winnerTex = frame:CreateTexture(nil, "OVERLAY")
frame.winnerTex:SetPoint("CENTER", frame, "CENTER")
frame.winnerTex:SetSize(ROW_HEIGHT - 6, ROW_HEIGHT - 6)
frame.winnerTex:SetTexture("Interface/RaidFrame/ReadyCheck-Ready")
end
frame.winnerTex:SetShown(data[realrow].cols[column].args.isWinner or false)
end

-- Same as SetCellNote, except the note is delivered in args instead of being fetched from the lootDB.
function LootHistory.SetCellSessionNote(rowFrame, frame, data, cols, row, realrow, column, fShow, table, ...)
local note = data[realrow].cols[column].args.note
local f = frame.noteBtn or CreateFrame("Button", nil, frame)
f:SetSize(ROW_HEIGHT, ROW_HEIGHT)
f:SetPoint("CENTER", frame, "CENTER")
if note then
f:SetNormalTexture("Interface/BUTTONS/UI-GuildButton-PublicNote-Up.png")
f:SetScript("OnEnter", function() addon:CreateTooltip(_G.LABEL_NOTE, note) end)
f:SetScript("OnLeave", function() addon:HideTooltip() end)
else
f:SetScript("OnEnter", nil)
f:SetNormalTexture("Interface/BUTTONS/UI-GuildButton-PublicNote-Disabled.png")
end
frame.noteBtn = f
end

function LootHistory:GetSessionResponsesFrame()
if self.sessionResponsesFrame then return self.sessionResponsesFrame end
local f = addon.UI:NewNamed("RCFrame", UIParent, "RCLootHistorySessionResponsesFrame", L["Responses"], nil, 260)
addon.UI:RegisterForEscapeClose(f, function() f:Hide() end)
local st = LibStub("ScrollingTable"):CreateST({
{name = "", width = ROW_HEIGHT,}, -- Class icon
{name = "", width = ROW_HEIGHT,}, -- Winner indicator
{name = _G.NAME, width = 110, sort = 1,},
{name = L["Reason"], width = 140,},
{name = L["Notes"], width = 40,},
{name = _G.ITEM_LEVEL_ABBR, width = 45,},
{name = _G.ROLL, width = 40,},
{name = L["Votes"], width = 45,},
}, 10, ROW_HEIGHT, nil, f.content)
st.frame:SetPoint("TOPLEFT", f, "TOPLEFT", 10, -30)
f:SetWidth(st.frame:GetWidth() + 20)
f:SetHeight(st.frame:GetHeight() + 75)
local close = addon:CreateButton(_G.CLOSE, f.content)
close:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -10, 10)
close:SetScript("OnClick", function() f:Hide() end)
f.st = st
self.sessionResponsesFrame = f
return f
end

--- Displays everyone's response to a specific history entry, as collected on award.
---@param winner string Name of the history entry's owner.
---@param entry table The history entry containing sessionResponses.
function LootHistory:ShowSessionResponses(winner, entry)
local f = self:GetSessionResponsesFrame()
local rows = {}
for name, v in pairs(entry.sessionResponses) do
local isWinner = addon:UnitIsUnit(name, winner)
local class, response, color, responseID, note, votes
if isWinner then
-- The winner's data lives on the history entry itself
class, response, color, responseID, note, votes =
entry.class, entry.response, entry.color, entry.responseID, entry.note, entry.votes
else
local r = addon:GetResponse(entry.typeCode or "default", v.response)
class, response, color, responseID, note, votes = v.class, r.text, r.color, v.response, v.note, v.votes
end
tinsert(rows, {
cols = {
{DoCellUpdate = addon.SetCellClassIcon, args = {class}, value = class or ""},
{DoCellUpdate = self.SetCellWinnerIndicator, args = {isWinner = isWinner}, value = isWinner and 1 or 0},
{value = addon.Ambiguate(name), color = addon:GetClassColor(class)},
{DoCellUpdate = self.SetCellResponse, args = {color = color, response = response, responseID = responseID or 0, isAwardReason = isWinner and entry.isAwardReason}},
{DoCellUpdate = self.SetCellSessionNote, args = {note = note}, value = note and 1 or 0},
{value = v.ilvl or ""},
{value = v.roll or ""},
{value = votes or 0},
},
})
end
-- Winner on top, then by votes
table.sort(rows, function(a, b)
if a.cols[2].value ~= b.cols[2].value then return a.cols[2].value > b.cols[2].value end
return (a.cols[8].value or 0) > (b.cols[8].value or 0)
end)
f.st:SetData(rows)
f:Show()
f:Raise()
end

function LootHistory.SetCellDelete(rowFrame, frame, data, cols, row, realrow, column, fShow, table, ...)
if not frame.created then
frame:SetNormalTexture("Interface\\Buttons\\UI-GroupLoot-Pass-Up")
Expand Down
6 changes: 6 additions & 0 deletions Modules/options.lua
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,12 @@ function addon:OptionsTable()
desc = L.opt_saveBonusRolls_Desc,
type = "toggle",
},
saveSessionResponses = {
order = 3.4,
name = L.opt_saveSessionResponses_name,
desc = L.opt_saveSessionResponses_desc,
type = "toggle",
},
header = {
order = 4,
type = "header",
Expand Down
33 changes: 33 additions & 0 deletions ml_core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,38 @@ end

local history_table = {}
local historyCounter = 0 -- Used to generate history table entry unique id

--- Collects everyone's response to a session for storage in the loot history.
--- Only candidates with an actual (numeric) response are included - passes and status texts are not.
--- The winner's response, note and votes are already on the history entry, so only their ilvl and roll is stored.
---@param session integer The session to collect responses from.
---@param winner string The winner of the session.
---@return table<string, table>? #Candidate name -> response data, or nil if there's none.
function RCLootCouncilML:GetSessionResponses(session, winner)
if not db.saveSessionResponses then return end -- Opt-in due to the storage increase
if not session then return end
local lootTable = addon:GetActiveModule("votingframe"):GetLootTable()
local sessionData = lootTable and lootTable[session]
if not (sessionData and sessionData.candidates) then return end
local sessionResponses = {}
for name, data in pairs(sessionData.candidates) do
-- On re-awards the winner's response has been replaced with "AWARDED"; their real response is kept seperately.
local response = data.response == "AWARDED" and data.real_response or data.response
if addon:UnitIsUnit(name, winner) then
sessionResponses[name] = { ilvl = data.ilvl, roll = data.roll, }
elseif type(response) == "number" then -- An actual response, i.e. not a pass or status text
sessionResponses[name] = {
response = response,
class = data.class,
ilvl = data.ilvl,
roll = data.roll,
votes = data.votes ~= 0 and data.votes or nil,
note = data.note,
}
end
end
return next(sessionResponses) and sessionResponses or nil
end
-- REVIEW Updated with recent changes in v2.9+.
-- This should be refactored in v3.0 as several of the sources are no longer viable, and were ment to be used with ML.
-- v2.19.0: Boss is included in lootTable, but kept as arg for backwards compatibility.
Expand Down Expand Up @@ -1289,6 +1321,7 @@ function RCLootCouncilML:TrackAndLogLoot(winner, link, responseID, boss, reason,
history_table["id"] = GetServerTime().."-"..historyCounter -- New in v2.7+. A unique id for the history entry.
history_table["owner"] = owner or self.lootTable[session] and self.lootTable[session].owner or winner -- New in v2.9+.
history_table["typeCode"] = self.lootTable[session] and self.lootTable[session].typeCode -- New in v2.15+.
history_table["sessionResponses"] = self:GetSessionResponses(session, winner) -- New in v3.22+.

historyCounter = historyCounter + 1

Expand Down
Loading