local SI, L = unpack(select(2, ...)) local Module = SI:NewModule('BonusRoll', 'AceEvent-3.0') local BonusFrame -- Frame attached to BonusRollFrame local MAX_BONUS_ROLL_RECORD_LIMIT = 25 -- the max cap of bonus roll records local BONUS_ROLL_REQUIRED_CURRENCY = 1580 -- bonus roll currency of current expansion local ignoreItem = { [163827] = true, -- Quartermaster's Coin, obtained when failing a bonus roll in pvp } -- Lua functions local tostring, ipairs, time, pairs, strsplit = tostring, ipairs, time, pairs, strsplit local tonumber, tinsert, sort, select = tonumber, tinsert, sort, select local _G = _G -- WoW API / Variables local CreateFrame = CreateFrame local GetBonusRollEncounterJournalLinkDifficulty = GetBonusRollEncounterJournalLinkDifficulty local GetDifficultyInfo = GetDifficultyInfo local GetInstanceInfo = GetInstanceInfo local GetItemInfoInstant = GetItemInfoInstant local GetRealZoneText = GetRealZoneText local GetSubZoneText = GetSubZoneText local DifficultyUtil_ID_DungeonChallenge = DifficultyUtil.ID.DungeonChallenge local function BonusRollShow() local t = SI.db.Toons[SI.thisToon] local BonusRollFrame = _G.BonusRollFrame if not t or not BonusRollFrame then return end local bonus = SI:BonusRollCount(SI.thisToon, BonusRollFrame.CurrentCountFrame.currencyID) if not bonus or not SI.db.Tooltip.AugmentBonus then if BonusFrame then BonusFrame:Hide() end return end if not BonusFrame then BonusFrame = CreateFrame("Button", "SavedInstancesBonusRollFrame", BonusRollFrame, "SpellBookSkillLineTabTemplate") BonusFrame:SetPoint("LEFT", BonusRollFrame, "RIGHT", 0, 8) BonusFrame.text = BonusFrame:CreateFontString(nil, "OVERLAY","GameFontNormal") BonusFrame.text:SetPoint("CENTER") BonusFrame:SetScript("OnEnter", function() SI.hoverTooltip.ShowBonusTooltip(nil, { SI.thisToon, BonusFrame }) end) BonusFrame:SetScript("OnLeave", function() if SI.indicatortip then SI.indicatortip:Hide() end end) BonusFrame:SetScript("OnClick", nil) SI:SkinFrame(BonusFrame, BonusFrame:GetName()) end BonusFrame.text:SetText((bonus > 0 and "+" or "")..bonus) BonusFrame:Show() end hooksecurefunc("BonusRollFrame_StartBonusRoll", BonusRollShow) function Module:OnEnable() BonusRollShow() -- catch roll-on-load self:RegisterEvent("BONUS_ROLL_RESULT") self:RegisterEvent("CHAT_MSG_MONSTER_YELL") end function Module:CHAT_MSG_MONSTER_YELL(event, msg, bossname) -- cheapest possible outdoor boss detection for players lacking a proper boss mod -- should work for sha and nalak, oon and gal report a related mob local t = SI.db.Toons[SI.thisToon] local now = time() if bossname and t then bossname = tostring(bossname) -- for safety local diff = select(4,GetInstanceInfo()) if diff and #diff > 0 then bossname = bossname .. ": ".. diff end t.lastbossyell = bossname t.lastbossyelltime = now -- SI:Debug("CHAT_MSG_MONSTER_YELL: "..tostring(bossname)); end end function Module:BONUS_ROLL_RESULT(event, rewardType, rewardLink, rewardQuantity, rewardSpecID, _, _, currencyID) local t = SI.db.Toons[SI.thisToon] SI:Debug("BONUS_ROLL_RESULT:%s:%s:%s:%s (boss=%s|%s)", tostring(rewardType), tostring(rewardLink), tostring(rewardQuantity), tostring(rewardSpecID), tostring(t and t.lastboss), tostring(t and t.lastbossyell)) if not t then return end if not rewardType then return end -- sometimes get a bogus message, ignore it t.BonusRoll = t.BonusRoll or {} local now = time() local bossname -- Mythic+ Dungeon Roll if GetBonusRollEncounterJournalLinkDifficulty() == DifficultyUtil_ID_DungeonChallenge then local name, _, difficultyID, difficultyName = GetInstanceInfo() if difficultyID == DifficultyUtil_ID_DungeonChallenge then bossname = name .. ": " .. difficultyName else local tmp = {} for key, value in pairs(SI.db.History) do local _, name, _, diff = strsplit(":", key) if tonumber(diff) == DifficultyUtil_ID_DungeonChallenge then local tbl = { name = name .. ": " .. GetDifficultyInfo(diff), last = value.last, } tinsert(tmp, tbl) end end sort(tmp, function(l, r) return l.last > r.last end) bossname = tmp[1] and tmp[1].name end end if not bossname then bossname = t.lastboss if now > (t.lastbosstime or 0) + 3*60 then -- user rolled before lastboss was updated, ignore the stale one. Roll timeout is 3 min. bossname = nil end if not bossname and t.lastbossyell and now < (t.lastbossyelltime or 0) + 10*60 then bossname = t.lastbossyell -- yell fallback end if not bossname then bossname = GetSubZoneText() or GetRealZoneText() -- zone fallback end end local roll = { name = bossname, time = now, costCurrencyID = _G.BonusRollFrame.CurrentCountFrame.currencyID, } if rewardType == "money" then roll.money = rewardQuantity elseif rewardType == "currency" then roll.currencyID = currencyID roll.money = rewardQuantity elseif rewardType == "item" then roll.item = rewardLink end tinsert(t.BonusRoll, 1, roll) for i = MAX_BONUS_ROLL_RECORD_LIMIT + 1, #t.BonusRoll do t.BonusRoll[i] = nil end end function SI:BonusRollCount(toon, currencyID) local t = SI.db.Toons[toon] if not t or not t.BonusRoll or #t.BonusRoll == 0 then return end currencyID = currencyID or BONUS_ROLL_REQUIRED_CURRENCY local count = 0 for _, tbl in ipairs(t.BonusRoll) do if not tbl.costCurrencyID then break end if tbl.costCurrencyID == currencyID then if not tbl.item then count = count + 1 else local itemID = GetItemInfoInstant(tbl.item) if ignoreItem[itemID] then count = count + 1 else break end end end end return count end function SI:BossRecord(toon, bossname, difficultyID, soft) local t = SI.db.Toons[toon] if not t then return end local now = time() -- boss mods can often detect completion before ENCOUNTER_END -- also some world bosses never send ENCOUNTER_END -- enough timeout to prevent overwriting, but short enough to prevent cross-boss contamination if soft and soft == false and (not bossname or now <= (t.lastbosstime or 0) + 120) then return end bossname = tostring(bossname) -- for safety local difficultyName = GetDifficultyInfo(difficultyID) if difficultyName and #difficultyName > 0 then bossname = bossname .. ": ".. difficultyName end t.lastboss = bossname t.lastbosstime = now end