/trunk
## Interface: 40000 |
## Title: Critline |
## Version: 3.1.3 |
## Version: 4.0.0 |
## Notes: Saves your normal and critical records and flashes a message if you break the record. |
## Author: L'ombra |
## SavedVariables: CritlineDB |
do -- slider template |
local backdrop = { |
bgFile = "Interface\\Buttons\\UI-SliderBar-Background", |
edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", |
bgFile = [[Interface\Buttons\UI-SliderBar-Background]], |
edgeFile = [[Interface\Buttons\UI-SliderBar-Border]], |
tile = true, tileSize = 8, edgeSize = 8, |
insets = {left = 3, right = 3, top = 6, bottom = 6} |
} |
do -- swatch button template |
local function setColor(self) |
local ColorPickerFrame = ColorPickerFrame |
local function swatchFunc() |
local button = ColorPickerFrame.extraInfo |
local r, g, b = ColorPickerFrame:GetColorRGB() |
self.swatch:SetVertexColor(r, g, b) |
local color = self.color |
button.swatch:SetVertexColor(r, g, b) |
if button.func then button:func(r, g, b) end |
local color = button.color |
color.r = r |
color.g = g |
color.b = b |
end |
local function cancel(self, prev) |
local r, g, b = prev.r, prev.g, prev.b |
self.swatch:SetVertexColor(r, g, b) |
local color = self.color |
local function cancelFunc(prev) |
local button = ColorPickerFrame.extraInfo |
local r, g, b, a = prev.r, prev.g, prev.b, prev.opacity |
button.swatch:SetVertexColor(r, g, b) |
if button.func then button:func(r, g, b) end |
local color = button.color |
color.r = r |
color.g = g |
color.b = b |
end |
-- local function opacityFunc() |
-- local button = ColorPickerFrame.extraInfo |
-- local alpha = 1.0 - OpacitySliderFrame:GetValue() |
-- if button.opacityFunc then button:opacityFunc(alpha) end |
-- end |
local function onClick(self) |
local info = UIDropDownMenu_CreateInfo() |
local color = self.color |
info.r, info.g, info.b = color.r, color.g, color.b |
info.swatchFunc = function() setColor(self) end |
info.cancelFunc = function(c) cancel(self, c) end |
info.swatchFunc = swatchFunc |
-- info.hasOpacity = self.hasOpacity |
-- info.opacityFunc = opacityFunc |
-- info.opacity = color.a |
info.cancelFunc = cancelFunc |
info.extraInfo = self |
OpenColorPicker(info) |
end |
do -- editbox |
function templates:CreateEditBox(parent) |
local editbox = CreateFrame("EditBox", nil, parent) |
editbox:SetAutoFocus(false) |
editbox:SetHeight(20) |
editbox:SetFontObject("ChatFontNormal") |
editbox:SetTextInsets(5, 0, 0, 0) |
UIDropDownMenu_SetText(self, self.menu and self.menu[value] or value) |
end |
local function getSelectedValue(self) |
return self.selectedValue |
end |
local function setDisabled(self, disable) |
if disable then |
self:Disable() |
end |
local function initialize(self) |
local onClick = self.menu.onClick |
local onClick = self.onClick |
for _, v in ipairs(self.menu) do |
local info = UIDropDownMenu_CreateInfo() |
info.text = v.text |
end |
end |
function templates:CreateDropDownMenu(name, parent, menu, initFunc, valueLookup) |
function templates:CreateDropDownMenu(name, parent, menu, valueLookup) |
local frame = CreateFrame("Frame", name, parent, "UIDropDownMenuTemplate") |
frame.SetFrameWidth = UIDropDownMenu_SetWidth |
frame.SetSelectedValue = setSelectedValue |
frame.GetSelectedValue = getSelectedValue |
frame.GetSelectedValue = UIDropDownMenu_GetSelectedValue |
frame.Refresh = UIDropDownMenu_Refresh |
frame.SetText = UIDropDownMenu_SetText |
frame.Enable = UIDropDownMenu_EnableDropDown |
frame.Disable = UIDropDownMenu_DisableDropDown |
frame.SetDisabled = setDisabled |
frame.JustifyText = UIDropDownMenu_JustifyText |
if menu then |
for _, v in ipairs(menu) do |
end |
frame.menu = menu or valueLookup |
frame.initialize = initFunc or initialize |
frame.initialize = initialize |
local label = frame:CreateFontString(name.."Label", "BACKGROUND", "GameFontNormalSmall") |
label:SetPoint("BOTTOMLEFT", frame, "TOPLEFT", 16, 3) |
do -- used in Reset and Announce |
local MAXSPELLBUTTONS = 14 |
local ITEMHEIGHT = 22 |
local MAXSPELLBUTTONS = 8 |
local ITEMHEIGHT = 36 |
local spells = {} |
local function update(self) |
local selectedTree = self.tree:GetSelectedValue() |
wipe(spells) |
local spells = addon:GetSpellArray(selectedTree) |
for i, v in ipairs(addon.percharDB.profile.spells[selectedTree]) do |
if v.normal or v.crit then |
spells[#spells + 1] = { |
spellName = v.spellName, |
isPeriodic = v.isPeriodic, |
normal = v.normal, |
crit = v.crit, |
pos = i, |
} |
end |
end |
local size = #spells |
FauxScrollFrame_Update(self.scrollFrame, size, MAXSPELLBUTTONS, ITEMHEIGHT) |
local offset = FauxScrollFrame_GetOffset(self.scrollFrame) |
for line = 1, MAXSPELLBUTTONS do |
local button = self.buttons[line] |
local item = self.buttons[line] |
local lineplusoffset = line + offset |
if lineplusoffset <= size then |
local data = spells[lineplusoffset] |
button.spell = data.spellName |
button.isPeriodic = data.isPeriodic |
item.data = data |
item.button.data = data |
local normal = data.normal and data.normal.amount |
local crit = data.crit and data.crit.amount |
button:SetText(addon:GetFullSpellName(selectedTree, data.spellName, data.isPeriodic)) |
button.record:SetFormattedText("%d/%d", (normal or 0), (crit or 0)) |
button:SetChecked(self.selectedSpells[data.pos]) |
button:Show() |
item.icon:SetTexture(addon:GetSpellTexture(data.spellID)) |
item.spell:SetText(addon:GetFullSpellName(data.spellID, data.periodic)) |
item.target:SetFormattedText("%s / %s", data.normal and data.normal.target or "n/a", data.crit and data.crit.target or "n/a") |
item.record:SetFormattedText("%s\n%s", addon:ShortenNumber(normal or 0), addon:ShortenNumber(crit or 0)) |
if self.history then |
local prevRecord = self.history[selectedTree][data.spellID] |
if prevRecord and prevRecord[data.periodic] then |
item.button.bg:Hide() |
item.button.texture:Hide() |
item.button.action = "Undo" |
else |
item.button.bg:Show() |
item.button.texture:Show() |
item.button.action = "Reset" |
end |
end |
item:Show() |
else |
button:Hide() |
item:Hide() |
end |
end |
end |
local function onMouseDown(self) |
self:SetPoint("RIGHT", -7, -1) |
end |
local function onMouseUp(self) |
self:SetPoint("RIGHT", -8, 0) |
end |
-- this is used for creating the scroll frames for the Reset and Announce frames |
function templates:CreateList(name, title) |
function templates:CreateList(name, title, action) |
local frame = templates:CreateConfigFrame(title, addonName) |
frame.selectedSpells = {} |
frame.Update = update |
local function update() |
frame:Update() |
addon.RegisterCallback(frame, "RecordsChanged", "Update") |
local scrollFrame = CreateFrame("ScrollFrame", name.."ScrollFrame", frame, "FauxScrollFrameTemplate") |
scrollFrame:SetSize(300, (MAXSPELLBUTTONS * ITEMHEIGHT + 4)) |
scrollFrame:SetPoint("TOP", 0, -24) |
scrollFrame:SetHeight(MAXSPELLBUTTONS * ITEMHEIGHT + 4) |
scrollFrame:SetPoint("TOPLEFT", 32, -24) |
scrollFrame:SetPoint("TOPRIGHT", -32, -24) |
scrollFrame:SetScript("OnVerticalScroll", function(self, offset) FauxScrollFrame_OnVerticalScroll(self, offset, ITEMHEIGHT, update) end) |
frame.scrollFrame = scrollFrame |
local function onClick(self) |
frame[self.action](frame, self.data) |
end |
-- onClick for check buttons |
local function onClick(self) |
local _, pos = addon:GetSpellInfo(frame.tree.selectedValue, self.spell, self.isPeriodic) |
local selectedSpells = frame.selectedSpells |
if self:GetChecked() then |
PlaySound("igMainMenuOptionCheckBoxOn") |
selectedSpells[pos] = true |
else |
PlaySound("igMainMenuOptionCheckBoxOff") |
selectedSpells[pos] = nil |
local function onEnter(self) |
GameTooltip.Critline = true |
GameTooltip:SetOwner(self, "ANCHOR_LEFT") |
GameTooltip:SetSpellByID(self.data.spellID) |
GameTooltip:AddLine(" ") |
addon:AddTooltipLine(self.data) |
if frame.history then |
local prevRecord = frame.history[frame.tree.selectedValue][self.data.spellID] |
prevRecord = prevRecord and prevRecord[self.data.periodic] |
if prevRecord then |
GameTooltip:AddLine(" ") |
GameTooltip:AddLine(L["Previous record:"]) |
addon:AddTooltipLine(prevRecord) |
end |
end |
if next(selectedSpells) then |
frame.button:Enable() |
else |
frame.button:Disable() |
end |
GameTooltip:Show() |
end |
-- create list of check buttons |
local buttons = {} |
for i = 1, MAXSPELLBUTTONS do |
local btn = CreateFrame("CheckButton", nil, frame, "OptionsBaseCheckButtonTemplate") |
local item = CreateFrame("Frame", nil, frame) |
item:SetHeight(ITEMHEIGHT) |
if i == 1 then |
btn:SetPoint("TOPLEFT", scrollFrame) |
item:SetPoint("TOPLEFT", scrollFrame) |
else |
btn:SetPoint("TOP", buttons[i - 1], "BOTTOM", 0, 4) |
item:SetPoint("TOP", buttons[i - 1], "BOTTOM") |
end |
btn:SetPushedTextOffset(0, 0) |
btn:SetScript("OnClick", onClick) |
item:SetPoint("RIGHT", scrollFrame) |
item:SetScript("OnEnter", onEnter) |
item:SetScript("OnLeave", GameTooltip_Hide) |
-- set default font string for the check button (will contain the spell name) |
local text = btn:CreateFontString(nil, nil, "GameFontHighlight") |
text:SetPoint("LEFT", btn, "RIGHT", 0, 1) |
text:SetJustifyH("LEFT") |
btn:SetFontString(text) |
local icon = item:CreateTexture() |
icon:SetSize(32, 32) |
icon:SetPoint("LEFT") |
item.icon = icon |
local spell = item:CreateFontString(nil, nil, "GameFontNormal") |
spell:SetPoint("TOPLEFT", icon, "TOPRIGHT", 4, -4) |
spell:SetJustifyH("LEFT") |
item.spell = spell |
local target = item:CreateFontString(nil, nil, "GameFontHighlightSmall") |
target:SetPoint("BOTTOMLEFT", icon, "BOTTOMRIGHT", 4, 4) |
target:SetPoint("RIGHT", -64, 0) |
target:SetJustifyH("LEFT") |
item.target = target |
local button = CreateFrame("Button", nil, item) |
button:SetSize(24, 24) |
button:SetPoint("RIGHT", -8, 0) |
button:SetScript("OnClick", onClick) |
button:SetScript("OnMouseDown", onMouseDown) |
button:SetScript("OnMouseUp", onMouseUp) |
button.action = action |
item.button = button |
local border = button:CreateTexture(nil, "BORDER") |
border:SetAllPoints() |
border:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Undo]]) |
local bg = button:CreateTexture() |
bg:SetSize(20, 20) |
bg:SetPoint("CENTER") |
bg:SetTexture(0, 0, 0) |
button.bg = bg |
local texture = button:CreateTexture() |
texture:SetPoint("CENTER") |
button.texture = texture |
if action == "Announce" then |
texture:SetSize(16, 16) |
texture:SetTexture([[Interface\Buttons\UI-GuildButton-MOTD-Up]]) |
else |
texture:SetSize(22, 22) |
texture:SetTexture([[Interface\RaidFrame\ReadyCheck-NotReady]]) |
end |
-- font string for record amounts |
local record = btn:CreateFontString(nil, nil, "GameFontHighlight") |
record:SetPoint("CENTER", text) |
record:SetPoint("RIGHT", scrollFrame) |
local record = item:CreateFontString(nil, nil, "GameFontHighlightSmall") |
record:SetPoint("TOPRIGHT", -36, 0) |
record:SetPoint("BOTTOMRIGHT", -36, 0) |
record:SetJustifyH("RIGHT") |
btn.record = record |
record:SetSpacing(2) |
item.record = record |
buttons[i] = btn |
buttons[i] = item |
end |
frame.buttons = buttons |
do |
local menu = { |
onClick = function(self) |
self.owner:SetSelectedValue(self.value) |
wipe(frame.selectedSpells) |
StaticPopup_Hide("CRITLINE_RESET_ALL") |
FauxScrollFrame_SetOffset(scrollFrame, 0) |
_G[scrollFrame:GetName().."ScrollBar"]:SetValue(0) |
frame:Update() |
frame.button:Disable() |
end, |
{text = L["Damage"], value = "dmg"}, |
{text = L["Healing"], value = "heal"}, |
{text = L["Pet"], value = "pet"}, |
} |
local tree = templates:CreateDropDownMenu(name.."Tree", frame, menu) |
tree:SetPoint("TOPLEFT", scrollFrame, "BOTTOMLEFT", -16, -4) |
tree:SetFrameWidth(120) |
tree:SetSelectedValue("dmg") |
frame.tree = tree |
local menu = { |
{text = L["Damage"], value = "dmg"}, |
{text = L["Healing"], value = "heal"}, |
{text = L["Pet"], value = "pet"}, |
} |
local tree = templates:CreateDropDownMenu(name.."Tree", frame, menu) |
tree:SetPoint("TOPLEFT", scrollFrame, "BOTTOMLEFT", -16, -4) |
tree:SetFrameWidth(120) |
tree:SetSelectedValue("dmg") |
tree.onClick = function(self) |
self.owner:SetSelectedValue(self.value) |
StaticPopup_Hide("CRITLINE_RESET_ALL") |
FauxScrollFrame_SetOffset(scrollFrame, 0) |
_G[scrollFrame:GetName().."ScrollBar"]:SetValue(0) |
frame:Update() |
end |
frame.tree = tree |
-- reset/announce button |
local btn = templates:CreateButton(frame) |
btn:SetPoint("TOPRIGHT", scrollFrame, "BOTTOMRIGHT", 0, -7) |
btn:SetSize(100, 22) |
btn:Disable() |
btn:SetText(title) |
frame.button = btn |
return frame |
end |
end |
L["Are you sure you want to reset all %s records?"] = "Bist du sicher, dass du alle %s Rekorde zurücksetzen willst?" -- Needs review |
L["Aura filter"] = "Aurafilter" -- Needs review |
L["Basic options"] = "Einfache Optionen" |
L["Buffs"] = "Stärkungszauber" |
L["By crit record"] = "Nach kritischen Rekorden" |
L["By normal record"] = "Nach normalen Rekorden" |
L["Cannot add players to mob filter."] = "Es können keine Spieler zum Mob Filter hinzugefügt werden." |
L["Critline splash frame unlocked"] = "Critline 'Splash Anzeige' freigestellt" |
L["damage"] = "Schaden" -- Needs review |
L["Damage"] = "Schaden" |
L["Debuffs"] = "Schwächungszauber" |
L["Delete aura"] = "Aura löschen" -- Needs review |
L["Delete mob"] = "Mob löschen" |
L["Detailed tooltip"] = "Detailierter Tooltip" -- Needs review |
L["Disable to ignore records where the target is a player."] = "Deaktivieren um Rekorde zu ignorieren bei denen das Ziel ein Spieler ist." |
L["Display previous record along with \"New record\" messages."] = "Zeige vorhergehenden Rekord zusammen mit Mitteilungen über \"Neuer Rekord\" an." |
L["Don't filter magic"] = "Magie nicht filtern" |
L[" (DoT)"] = " (DoT)" -- Needs review |
L["Drag to move"] = "Ziehen zum bewegen" |
L["Duration"] = "Dauer" -- Needs review |
L["Enabled"] = "Aktiviert" -- Needs review |
L["Font size"] = "Schriftgrösse" -- Needs review |
L["healing"] = "Heilung" -- Needs review |
L["Healing"] = "Heilung" -- Needs review |
L[" (HoT)"] = " (HoT)" -- Needs review |
L["If level difference between you and the target is greater than this setting, records will not be registered."] = "Falls der Stufenunterschied zwischen dir und dem Ziel größer ist als diese Einstellung, wird der Rekord nicht aufgezeichnet." |
L["Ignore aura filter"] = "Aurenfilter ignorieren" |
L["Ignore mob filter"] = "Mobfilter ignorieren" |
L["Add by name"] = true |
L["Add by spell ID"] = true |
L["Add from spell book"] = true |
L["Add target"] = true |
L["Alpha"] = true |
L["Alphabetically"] = true |
L["Amount color"] = true |
L["Announce"] = true |
L["Are you sure you want to reset all %s records?"] = true |
L["Aura filter"] = true |
L["Basic options"] = true |
L["Aura type"] = true |
L["Buffs"] = true |
L["By crit record"] = true |
L["By normal record"] = true |
L["Cannot add players to mob filter."] = true |
L["critical "] = true |
L["Critical!"] = true |
L["Critline splash frame unlocked"] = true |
L["Current fight"] = true |
L["Current instance (%s)"] = true |
L["Current session"] = true |
L["damage"] = true |
L["Damage"] = true |
L["Debuffs"] = true |
L["Delete aura"] = true |
L["Delete mob"] = true |
L["Detailed tooltip"] = true |
L["Disable to ignore records where the target is a player."] = true |
L["Display previous record along with \"New record\" messages."] = true |
L["Don't filter magic"] = true |
L[" (DoT)"] = true |
L["Duration"] = true |
L["Drag to move"] = true |
L["Enable to filter out new spell entries by default."] = true |
L["Enable to ignore additional damage due to vulnerability."] = true |
L["Enable to ignore integrated aura filter."] = true |
L["Enable to ignore integrated mob filter."] = true |
L["Enable to ignore spells that are not in your (or your pet's) spell book."] = true |
L["Enable to include rather than exclude selected spells in the spell filter."] = true |
L["Enable to let magical damage ignore the level filter."] = true |
L["Enable to show icon indicators instead of text."] = true |
L["Enable to use scrolling combat text for \"New record\" messages instead of the default splash frame."] = true |
L["Enabled"] = true |
L["Enter mob name:"] = true |
L["Enter spell ID:"] = true |
L["Filter new spells"] = true |
L["Font"] = true |
L["Font outline"] = true |
L["Font size"] = true |
L["healing"] = true |
L["Healing"] = true |
L[" (HoT)"] = true |
L["If level difference between you and the target is greater than this setting, records will not be registered."] = true |
L["Ignore aura filter"] = true |
L["Ignore mob filter"] = true |
L["Ignore vulnerability"] = true |
L["Include (unfiltered) records in spell tooltips."] = true |
L["Invalid channel. Please enter a valid channel name or ID."] = true |
L["Invalid input. Please enter a spell ID."] = true |
L["Invalid mob name."] = true |
L["Invalid player name."] = true |
L["Invalid spell ID. No such spell exists."] = true |
L["Invert spell filter"] = true |
L["Left-click to toggle summary frame"] = true |
L["Level filter"] = true |
L["Lock minimap button."] = true |
L["Move splash screen"] = true |
L["New %s record!"] = true |
L["New %s%s record - %d"] = true |
L["n/a"] = true |
L["None"] = true |
L["No records"] = true |
L["Normal"] = true |
L["Pet"] = true |
L["Plays a sound on a new record."] = true |
L["Play sound"] = true |
L["Previous record:"] = true |
L["Prints new record notifications to the chat frame."] = true |
L["Record damage"] = true |
L["Record healing"] = true |
L["Record pet damage"] = true |
L["Record PvE"] = true |
L["Record PvP"] = true |
L["Records in spell tooltips"] = true |
L["Reset"] = true |
L["Reset all"] = true |
L["Reset all %s records."] = true |
L["Sets the color for the amount text in the splash frame."] = true |
L["Sets the color for the spell text in the splash frame."] = true |
L["Sets the font size of the splash frame."] = true |
L["Sets the opacity of the display."] = true |
L["Sets the scale of the splash frame."] = true |
L["Sets the scale of the summary frame."] = true |
L["Sets the scale of the display."] = true |
L["Sets the time (in seconds) the splash frame is visible before fading out."] = true |
L["Shorten records"] = true |
L["Show"] = true |
L["Show auras cast by NPCs"] = true |
L["Show auras cast by players"] = true |
L["Show auras cast on hostile NPCs"] = true |
L["Show auras cast on me"] = true |
L["Show minimap button."] = true |
L["Show icons"] = true |
L["Show old record"] = true |
L["%s is already in mob filter."] = true |
L["Spell color"] = true |
L["Spell filter"] = true |
L["Spell ID: |cffffffff%d|r"] = true |
L["Splash frame"] = true |
L["%s removed from aura filter."] = true |
L["%s removed from mob filter."] = true |
L["Sort by aura name"] = true |
L["Sort by source name"] = true |
L["Summary frame scale"] = true |
L["Suppress all records while mind controlled."] = true |
L["Suppress mind control"] = true |
L["Text filter"] = true |
L["Thick"] = true |
L["tick"] = true |
L["Tick"] = true |
L["Tooltip sorting:"] = true |
L["Use combat text splash"] = true |
L["Use detailed format in the summary tooltip."] = true |
L["Disable to ignore records where the target is a player."] = "ç¦ç¨ä¾å¿½ç¥ç®æ¨æ¯ä¸åç©å®¶çè¨é." |
L["Display previous record along with \"New record\" messages."] = "å¨\"æ°çè¨é\"è¨æ¯ä¸èµ·é¡¯ç¤ºåä¸æ¢è¨é." |
L["Don't filter magic"] = "ä¸é濾éæ³" |
L[" (DoT)"] = "(DoT)" |
-- L["Duration"] = "" |
L["Drag to move"] = "ææ³ä¾ç§»å" |
L["Edit tooltip format"] = "編輯æ示è¨æ¯æ ¼å¼" |
-- L["Enabled"] = "" |
L["Enter mob name:"] = "è¼¸å ¥æªç©åå" |
-- L["Enter spell ID:"] = "" |
L[" (HoT)"] = "(HoT)" |
L["If level difference between you and the target is greater than this setting, records will not be registered."] = "å¦æä½ åç®æ¨ççç´å·®å¤§äºéåè¨å®, è¨éå°ä¸æ被注å." |
L["Ignore aura filter"] = "忽ç¥å ç°é濾å¨" |
L["Ignore mob filter"] = "忽ç¥æªç©é濾å¨" |
L["Disable to ignore records where the target is a player."] = "ÐÑклÑÑиÑе, ÑÑÐ¾Ð±Ñ Ð¸Ð³Ð½Ð¾ÑиÑоваÑÑ Ð·Ð°Ð¿Ð¸Ñи, когда ÑÐµÐ»Ñ - игÑок" |
L["Display previous record along with \"New record\" messages."] = "ÐоказÑваÑÑ Ð¿ÑедÑдÑÑие ÑекоÑÐ´Ñ Ð²Ð¼ÐµÑÑе Ñ ÑообÑениÑми \"ÐовÑй ÑекоÑд\"" |
L["Don't filter magic"] = "Ðе ÑилÑÑÑоваÑÑ Ð¼Ð°Ð³Ð¸Ñ" |
L[" (DoT)"] = "ÐоТ" -- Needs review |
L["Drag to move"] = "ÐажмиÑе, ÑÑÐ¾Ð±Ñ Ð´Ð²Ð¸Ð³Ð°ÑÑ" |
L["Duration"] = "ÐлиÑелÑноÑÑÑ" |
L["Enabled"] = "ÐклÑÑено." |
L["Font size"] = "Ð Ð°Ð·Ð¼ÐµÑ ÑÑиÑÑа" |
L["healing"] = "леÑение" |
L["Healing"] = "ÐеÑение" |
L[" (HoT)"] = "ХоТ" -- Needs review |
L["If level difference between you and the target is greater than this setting, records will not be registered."] = "ÐÑли ÑазниÑа в ÑÑовнÑÑ Ð¼ÐµÐ¶Ð´Ñ Ð²Ð°Ð¼Ð¸ и ÑелÑÑ Ð±Ð¾Ð»ÑÑе Ñем в наÑÑÑÐ¾Ð¹ÐºÐ°Ñ , ÑекоÑд не бÑÐ´ÐµÑ Ð·Ð°Ð¿Ð¸Ñан." |
L["Ignore aura filter"] = "ÐгноÑиÑоваÑÑ ÑилÑÑÑ Ð°ÑÑ" |
L["Ignore mob filter"] = "ÐгноÑиÑоваÑÑ ÑилÑÑÑ Ð¼Ð¾Ð½ÑÑÑов" |
L["Disable to ignore records where the target is a player."] = "Inhabilitar: Ignorar las marcas cuando el objectivo es un jugador." |
L["Display previous record along with \"New record\" messages."] = "Mostrar marca anterior junto con la nueva marca." |
L["Don't filter magic"] = "No filtrar magia" |
-- L[" (DoT)"] = "DP" |
L["Drag to move"] = "Arrastrar para mover" |
L["Edit tooltip format"] = "Editar el formato del tooltip" -- Needs review |
L["Enable to ignore integrated aura filter."] = "Habilita para igonar todas la entradas en el filtro de auras." |
L["Enable to let magical damage ignore the level filter."] = "Habilitar: el daño mágico igonara el ajuste de nivel." |
L["Enable to use scrolling combat text for \"New record\" messages instead of the default splash frame."] = "Mostrar los records en el marco de texto de combate de Blizzard." |
L["Enter mob name:"] = "Entrar nombre de mob:" |
-- L[" (HoT)"] = "CP" |
L["If level difference between you and the target is greater than this setting, records will not be registered."] = "Diferencia máxima de nivel para registra las marcas (entre tu y el objectivo)." |
L["Ignore aura filter"] = "Ignorar auras" |
L["Ignore mob filter"] = "Ignorar mobs" |
local templates = addon.templates |
local treeNames = addon.treeNames |
-- history for undoing recent (last fight) records |
local history = {dmg = {}, heal = {}, pet = {}} |
local module = templates:CreateList("CritlineReset", L["Reset"]) |
module:RegisterEvent("PLAYER_REGEN_DISABLED") |
module:SetScript("OnEvent", function(self) |
-- previous records are wiped upon entering combat |
self:ClearHistory() |
end) |
do |
local button = module.button |
button:SetScript("OnClick", function() |
PlaySound("gsTitleOptionOK") |
module:ResetRecords() |
end) |
local resetAll = templates:CreateButton(module) |
resetAll:SetSize(100, 22) |
resetAll:SetPoint("TOP", button, "BOTTOM", 0, -10) |
resetAll:SetText(L["Reset all"]) |
resetAll:SetScript("OnClick", function(self) |
PlaySound("gsTitleOptionOK") |
StaticPopup_Show("CRITLINE_RESET_ALL", addon.treeNames[module.tree:GetSelectedValue()]) |
end) |
module.history = history |
-- "edit tooltip format" popup |
StaticPopupDialogs["CRITLINE_RESET_ALL"] = { |
text = L["Are you sure you want to reset all %s records?"], |
button1 = OKAY, |
button2 = CANCEL, |
OnAccept = function(self) |
module:ResetRecords(true) |
end, |
whileDead = true, |
timeout = 0, |
} |
-- reset/announce button |
local button = templates:CreateButton(module) |
button:SetPoint("TOPRIGHT", module.scrollFrame, "BOTTOMRIGHT", 0, -7) |
button:SetSize(100, 22) |
button:SetText(L["Reset all"]) |
button:SetScript("OnClick", function(self) |
PlaySound("gsTitleOptionOK") |
StaticPopup_Show("CRITLINE_RESET_ALL", addon.treeNames[module.tree:GetSelectedValue()]) |
end) |
-- "edit tooltip format" popup |
StaticPopupDialogs["CRITLINE_RESET_ALL"] = { |
text = L["Are you sure you want to reset all %s records?"], |
button1 = OKAY, |
button2 = CANCEL, |
OnAccept = function(self) |
module:ResetAll() |
end, |
whileDead = true, |
timeout = 0, |
} |
function module:Reset(data) |
local tree = self.tree.selectedValue |
addon:DeleteSpell(tree, data.spellID, data.periodic) |
addon:UpdateRecords(tree) |
end |
function module:ResetRecords(resetAll) |
local selectedSpells = self.selectedSpells |
function module:Undo(data) |
local tree = self.tree.selectedValue |
local history = history[tree][data.spellID] |
local spell = addon:GetSpellInfo(tree, data.spellID, data.periodic) |
for k, v in pairs(history[data.periodic]) do |
local hitType = spell[k] |
local amount, target = hitType.amount, hitType.target |
for k, v in pairs(v) do |
hitType[k] = v |
end |
addon:Message(format("Reverted %s (%d, %s) record on %s.", data.spellName, amount, tree, target)) |
end |
history[data.periodic] = nil |
addon:UpdateTopRecords(tree) |
addon:UpdateRecords(tree) |
end |
function module:ResetAll() |
local tree = self.tree:GetSelectedValue() |
local spells = addon.percharDB.profile.spells[tree] |
if resetAll then |
for i = #spells, 1, -1 do |
local data = spells[i] |
if data.filtered then |
data.normal = nil |
data.crit = nil |
else |
tremove(spells, i) |
end |
end |
addon:Message(format(L["Reset all %s records."], treeNames[tree])) |
wipe(spells) |
wipe(addon:GetSpellArray(tree)) |
addon:Message(format(L["Reset all %s records."], treeNames[tree])) |
self:Update() |
addon:UpdateTopRecords(tree) |
addon:UpdateSpells(tree) |
end |
-- stores previous record for the undo feature |
function module:NewRecord(event, tree, spellID, periodic, amount, crit, prevRecord) |
-- do not store previous record if it was 0 |
if prevRecord.amount == 0 then |
return |
end |
history[tree][spellID] = history[tree][spellID] or {} |
local hitType = crit and "crit" or "normal" |
local spell = history[tree][spellID] |
spell[periodic] = spell[periodic] or {} |
-- do not store previous records gained in current fight |
if spell[periodic][hitType] then |
return |
else |
-- remove selected spells from database |
-- iterate first in ascending order to print chat messages in alphabetical order |
for i = 1, table.maxn(selectedSpells) do |
if selectedSpells[i] then |
local data = spells[i] |
if data.filtered then |
-- don't remove filtered entries completely; save the 'filtered' flag |
data.normal = nil |
data.crit = nil |
selectedSpells[i] = nil |
end |
addon:Message(format(L["Reset %s (%s) records."], addon:GetFullSpellName(tree, data.spellName, data.isPeriodic), treeNames[tree])) |
end |
end |
-- then iterate in descending order not to mess up the loop with tremove |
for i = table.maxn(selectedSpells), 1, -1 do |
if selectedSpells[i] then |
tremove(spells, i) |
end |
end |
spell[periodic][hitType] = {} |
end |
wipe(selectedSpells) |
for k, v in pairs(prevRecord) do |
spell[periodic][hitType][k] = v |
end |
addon:Debug(format("Storing previous record for %s = %d (%s, %s, %s)", addon:GetSpellName(spellID), prevRecord.amount, tree, periodic == 2 and "periodic" or "direct", hitType)) |
end |
addon.RegisterCallback(module, "NewRecord") |
function module:ClearHistory() |
for k, tree in pairs(history) do |
wipe(tree) |
end |
self:Update() |
self.button:Disable() |
addon:UpdateSpells(tree) |
end |
local templates = addon.templates |
local L = { |
profiles = "Profiles", |
current = "Current profile:", |
default = "Default", |
-- intro = "You can change the active database profile, so you can have different settings for every character.", |
-- reset_desc = "Reset the current profile back to its default values, in case your configuration is broken, or you simply want to start over.", |
reset = "Reset profile", |
-- reset_sub = "Reset the current profile to the default", |
choose_desc = "You can either create a new profile by entering a name in the editbox, or choose one of the already existing profiles.", |
new = "New", |
-- new_sub = "Create a new empty profile.", |
choose = "Existing profiles", |
-- choose_sub = "Select one of your currently available profiles.", |
copy_desc = "Copy the settings from one existing profile into the currently active profile.", |
copy = "Copy from", |
copy = "Copy From", |
delete_desc = "Delete existing and unused profiles from the database to save space, and cleanup the SavedVariables file.", |
delete = "Delete a profile", |
-- delete_sub = "Deletes a profile from the database.", |
delete_confirm = "Are you sure you want to delete the selected profile?", |
dualspec_desc = "When enabled, this feature allow you to select a different ".. |
"profile for each talent spec. The dual profile will be swapped with the ".. |
"current profile each time you switch from a talent spec to the other.", |
profiles = "Profiles", |
-- profiles_sub = "Manage Profiles", |
current = "Current profile:", |
dualspec_desc = "When enabled, this feature allow you to select a different profile for each talent spec. The dual profile will be swapped with the current profile each time you switch from a talent spec to the other.", |
dual_profile = "Dual profile", |
enabled = "Enable dual profile", |
enabled_desc = "Check this box to automatically swap profiles on talent switch.", |
dual_profile = "Dual profile", |
} |
local LOCALE = GetLocale() |
if LOCALE == "deDE" then |
L["profiles"] = "Profile" |
--L["current"] = "Current Profile:" |
L["default"] = "Standard" |
-- L["intro"] = "Hier kannst du das aktive Datenbankprofile \195\164ndern, damit du verschiedene Einstellungen f\195\188r jeden Charakter erstellen kannst, wodurch eine sehr flexible Konfiguration m\195\182glich wird." |
-- L["reset_desc"] = "Setzt das momentane Profil auf Standardwerte zur\195\188ck, f\195\188r den Fall das mit der Konfiguration etwas schief lief oder weil du einfach neu starten willst." |
L["reset"] = "Profil zur\195\188cksetzen" |
-- L["reset_sub"] = "Das aktuelle Profil auf Standard zur\195\188cksetzen." |
L["choose_desc"] = "Du kannst ein neues Profil erstellen, indem du einen neuen Namen in der Eingabebox 'Neu' eingibst, oder w\195\164hle eines der vorhandenen Profile aus." |
L["new"] = "Neu" |
-- L["new_sub"] = "Ein neues Profil erstellen." |
L["choose"] = "Vorhandene Profile" |
L["new"] = "Neu" |
L["choose_desc"] = "Du kannst ein neues Profil erstellen, indem du einen neuen Namen in der Eingabebox 'Neu' eingibst, oder w\195\164hle eines der vorhandenen Profile aus." |
-- L["choose_sub"] = "W\195\164hlt ein bereits vorhandenes Profil aus." |
L["copy_desc"] = "Kopiere die Einstellungen von einem vorhandenen Profil in das aktive Profil." |
L["copy"] = "Kopieren von..." |
L["delete_desc"] = "L\195\182sche vorhandene oder unbenutzte Profile aus der Datenbank um Platz zu sparen und um die SavedVariables Datei 'sauber' zu halten." |
L["delete"] = "Profil l\195\182schen" |
-- L["delete_sub"] = "L\195\182scht ein Profil aus der Datenbank." |
L["delete_confirm"] = "Willst du das ausgew\195\164hlte Profil wirklich l\195\182schen?" |
L["profiles"] = "Profile" |
-- L["profiles_sub"] = "Profile verwalten" |
--L["current"] = "Current Profile:" |
L["dualspec_desc"] = "Wenn aktiv, wechselt dieses Feature bei jedem Wechsel ".. |
"der dualen Talentspezialisierung das Profil. Das duale Profil wird beim ".. |
"Wechsel automatisch mit dem derzeit aktiven Profil getauscht." |
L["dualspec_desc"] = "Wenn aktiv, wechselt dieses Feature bei jedem Wechsel der dualen Talentspezialisierung das Profil. Das duale Profil wird beim Wechsel automatisch mit dem derzeit aktiven Profil getauscht." |
L["dual_profile"] = "Duales Profil" |
L["enabled"] = "Aktiviere Duale Profile" |
L["enabled_desc"] = "Aktiviere diese Option, um beim Talentwechsel automatisch zwischen den Profilen zu wechseln." |
L["dual_profile"] = "Duales Profil" |
elseif LOCALE == "frFR" then |
L["profiles"] = "Profils" |
--L["current"] = "Current Profile:" |
L["default"] = "D\195\169faut" |
-- L["intro"] = "Vous pouvez changer le profil actuel afin d'avoir des param\195\168tres diff\195\169rents pour chaque personnage, permettant ainsi d'avoir une configuration tr\195\168s flexible." |
-- L["reset_desc"] = "R\195\169initialise le profil actuel au cas o\195\185 votre configuration est corrompue ou si vous voulez tout simplement faire table rase." |
L["reset"] = "R\195\169initialiser le profil" |
-- L["reset_sub"] = "R\195\169initialise le profil actuel avec les param\195\168tres par d\195\169faut." |
L["choose_desc"] = "Vous pouvez cr\195\169er un nouveau profil en entrant un nouveau nom dans la bo\195\174te de saisie, ou en choississant un des profils d\195\169j\195\160 existants." |
L["new"] = "Nouveau" |
-- L["new_sub"] = "Cr\195\169\195\169e un nouveau profil vierge." |
L["choose"] = "Profils existants" |
-- L["choose_sub"] = "Permet de choisir un des profils d\195\169j\195\160 disponibles." |
L["copy_desc"] = "Copie les param\195\168tres d'un profil d\195\169j\195\160 existant dans le profil actuellement actif." |
L["copy"] = "Copier \195\160 partir de" |
L["delete_desc"] = "Supprime les profils existants inutilis\195\169s de la base de donn\195\169es afin de gagner de la place et de nettoyer le fichier SavedVariables." |
L["delete"] = "Supprimer un profil" |
-- L["delete_sub"] = "Supprime un profil de la base de donn\195\169es." |
L["delete_confirm"] = "Etes-vous s\195\187r de vouloir supprimer le profil s\195\169lectionn\195\169 ?" |
L["dualspec_desc"] = "Lorsqu'elle est activée, cette fonctionnalité vous permet ".. |
"de choisir un profil différent pour chaque spécialisation de talents. ".. |
"Le second profil sera échangé avec le profil courant chaque fois que vous ".. |
"passerez d'une spécialisation à l'autre." |
L["profiles"] = "Profils" |
-- L["profiles_sub"] = "Gestion des profils" |
L["dualspec_desc"] = "Lorsqu'elle est activée, cette fonctionnalité vous permet de choisir un profil différent pour chaque spécialisation de talents. Le second profil sera échangé avec le profil courant chaque fois que vous passerez d'une spécialisation à l'autre." |
L["dual_profile"] = 'Second profil' |
L["enabled"] = 'Activez le second profil' |
L["enabled_desc"] = "Cochez cette case pour échanger automatiquement les profils lors d'un changement de spécialisation." |
L["dual_profile"] = 'Second profil' |
-- elseif LOCALE == "koKR" then |
-- L["profiles"] = "íë¡í" |
elseif LOCALE == "koKR" then |
L["default"] = "기본ê°" |
-- L["intro"] = "모ë ìºë¦í°ì ë¤ìí ì¤ì ê³¼ ì¬ì©ì¤ì¸ ë°ì´í°ë² ì´ì¤ íë¡í, ì´ëê²ì´ëì§ ë§¤ì° ë¤ë£¨ê¸° ì½ê² ë°ê¿ì ììµëë¤." |
-- L["reset_desc"] = "ë¨ìí ë¤ì ìë¡ê² 구ì±ì ìíë ê²½ì°, íì¬ íë¡íì 기본ê°ì¼ë¡ ì´ê¸°í í©ëë¤." |
L["reset"] = "íë¡í ì´ê¸°í" |
-- L["reset_sub"] = "íì¬ì íë¡íì 기본ê°ì¼ë¡ ì´ê¸°í í©ëë¤" |
L["choose_desc"] = "ìë¡ì´ ì´ë¦ì ì ë ¥íê±°ë, ì´ë¯¸ ìë íë¡íì¤ íë를 ì ííì¬ ìë¡ì´ íë¡íì ë§ë¤ ì ììµëë¤." |
L["new"] = "ìë¡ì´ íë¡í" |
-- L["new_sub"] = "ìë¡ì´ íë¡íì ë§ëëë¤." |
L["choose"] = "íë¡í ì í" |
-- L["choose_sub"] = "ë¹ì ì´ íì¬ ì´ì©í ì ìë íë¡íì ì íí©ëë¤." |
L["copy_desc"] = "íì¬ ì¬ì©ì¤ì¸ íë¡íì, ì íí íë¡íì ì¤ì ì ë³µì¬í©ëë¤." |
L["copy"] = "ë³µì¬" |
L["delete_desc"] = "ë°ì´í°ë² ì´ì¤ì ì¬ì©ì¤ì´ê±°ë ì ì¥ë íë¡íì¼ ìì ë¡ SavedVariables íì¼ì ì 리ì ê³µê° ì ì½ì´ ë©ëë¤." |
L["delete"] = "íë¡í ìì " |
-- L["delete_sub"] = "ë°ì´í°ë² ì´ì¤ì íë¡íì ìì í©ëë¤." |
L["delete_confirm"] = "ì ë§ë¡ ì íí íë¡íì ìì 를 ìíìëê¹?" |
L["profiles"] = "íë¡í" |
L["profiles_sub"] = "íë¡í ì¤ì " |
-- L["current"] = "Current Profile:" |
-- L["default"] = "기본ê°" |
-- L["reset"] = "íë¡í ì´ê¸°í" |
-- L["choose_desc"] = "ìë¡ì´ ì´ë¦ì ì ë ¥íê±°ë, ì´ë¯¸ ìë íë¡íì¤ íë를 ì ííì¬ ìë¡ì´ íë¡íì ë§ë¤ ì ììµëë¤." |
-- L["new"] = "ìë¡ì´ íë¡í" |
-- L["choose"] = "íë¡í ì í" |
-- L["copy_desc"] = "íì¬ ì¬ì©ì¤ì¸ íë¡íì, ì íí íë¡íì ì¤ì ì ë³µì¬í©ëë¤." |
-- L["copy"] = "ë³µì¬" |
-- L["delete_desc"] = "ë°ì´í°ë² ì´ì¤ì ì¬ì©ì¤ì´ê±°ë ì ì¥ë íë¡íì¼ ìì ë¡ SavedVariables íì¼ì ì 리ì ê³µê° ì ì½ì´ ë©ëë¤." |
-- L["delete"] = "íë¡í ìì " |
-- L["delete_confirm"] = "ì ë§ë¡ ì íí íë¡íì ìì 를 ìíìëê¹?" |
L["dualspec_desc"] = "ê°ë¥íë©´ ì¬ì©í©ëë¤. ì´ì¤ í¹ì±ì ìíì¬ ë¤ë¥¸ íë¡íì ì íí ì ìê² íëë¤. ì´ì¤ íë¡íì íì¬ íë¡íê³¼ ë²ê°ìì í¹ì±ì´ ë³ê²½ë ë ê°ì´ ì ì©ë©ëë¤." |
L["dual_profile"] = "ì´ì¤ íë¡í" |
L["enabled"] = "ì´ì¤ íë¡í ì¬ì©" |
L["enabled_desc"] = "í¹ì±ì´ ë³ê²½ ë ë ìëì¼ë¡ íë¡íì ë³ê²½íëë¡ ì íí©ëë¤." |
elseif LOCALE == "esES" or LOCALE == "esMX" then |
L["profiles"] = "Perfiles" |
--L["current"] = "Current Profile:" |
L["default"] = "Por defecto" |
-- L["intro"] = "Puedes cambiar el perfil activo de tal manera que cada personaje tenga diferentes configuraciones." |
-- L["reset_desc"] = "Reinicia el perfil actual a los valores por defectos, en caso de que se haya estropeado la configuración o quieras volver a empezar de nuevo." |
L["reset"] = "Reiniciar Perfil" |
-- L["reset_sub"] = "Reinicar el perfil actual al de por defecto" |
L["choose_desc"] = "Puedes crear un nuevo perfil introduciendo un nombre en el recuadro o puedes seleccionar un perfil de los ya existentes." |
L["new"] = "Nuevo" |
-- L["new_sub"] = "Crear un nuevo perfil vacio." |
L["choose"] = "Perfiles existentes" |
-- L["choose_sub"] = "Selecciona uno de los perfiles disponibles." |
L["copy_desc"] = "Copia los ajustes de un perfil existente al perfil actual." |
L["copy"] = "Copiar de" |
L["delete_desc"] = "Borra los perfiles existentes y sin uso de la base de datos para ganar espacio y limpiar el archivo SavedVariables." |
L["delete"] = "Borrar un Perfil" |
-- L["delete_sub"] = "Borra un perfil de la base de datos." |
L["delete_confirm"] = "¿Estas seguro que quieres borrar el perfil seleccionado?" |
L["profiles"] = "Perfiles" |
-- L["profiles_sub"] = "Manejar Perfiles" |
--L["current"] = "Current Profile:" |
elseif LOCALE == "zhTW" then |
L["profiles"] = "è¨å®æª" |
--L["current"] = "Current Profile:" |
L["default"] = "é è¨" |
-- L["intro"] = "ä½ å¯ä»¥é¸æä¸åæ´»åçè³æè¨å®æªï¼éæ¨£ä½ çæ¯åè§è²å°±å¯ä»¥ææä¸åçè¨å®å¼ï¼å¯ä»¥çµ¦ä½ çæ件è¨å®å¸¶ä¾æ¥µå¤§çéæ´»æ§ã" |
-- L["reset_desc"] = "å°ç¶åçè¨å®æªæ¢å¾©å°å®çé è¨å¼ï¼ç¨æ¼ä½ çè¨å®æªæå£ï¼æè ä½ åªæ¯æ³éä¾çæ æ³ã" |
L["reset"] = "éç½®è¨å®æª" |
-- L["reset_sub"] = "å°ç¶åçè¨å®æªæ¢å¾©çºé è¨å¼" |
L["choose_desc"] = "ä½ å¯ä»¥ééå¨ææ¬æ¡å §è¼¸å ¥ä¸ååååµç«ä¸åæ°çè¨å®æªï¼ä¹å¯ä»¥é¸æä¸åå·²ç¶åå¨çè¨å®æªã" |
L["new"] = "æ°å»º" |
-- L["new_sub"] = "æ°å»ºä¸å空çè¨å®æªã" |
L["choose"] = "ç¾æçè¨å®æª" |
-- L["choose_sub"] = "å¾ç¶åå¯ç¨çè¨å®æªè£é¢é¸æä¸åã" |
L["copy_desc"] = "å¾ç¶åæåå·²ä¿åçè¨å®æªè¤è£½å°ç¶åæ£ä½¿ç¨çè¨å®æªã" |
L["copy"] = "è¤è£½èª" |
L["delete_desc"] = "å¾è³æ庫è£åªé¤ä¸å使ç¨çè¨å®æªï¼ä»¥ç¯ç空éï¼ä¸¦ä¸æ¸ çSavedVariablesæªã" |
L["delete"] = "åªé¤ä¸åè¨å®æª" |
-- L["delete_sub"] = "å¾è³æ庫è£åªé¤ä¸åè¨å®æªã" |
L["delete_confirm"] = "ä½ ç¢ºå®è¦åªé¤æé¸æçè¨å®æªåï¼" |
L["dualspec_desc"] = "åç¨æï¼ä½ å¯ä»¥çºä½ çé天賦è¨å®å¦ä¸çµè¨å®æªãä½ çéè¨å®æªå°å¨ä½ è½æ天賦æèªåèç®å使ç¨è¨å®æªäº¤æã" |
L["enabled"] = "åç¨éè¨å®æª" |
L["enabled_desc"] = "å¾é¸ä»¥å¨è½æ天賦æèªå交æè¨å®æª" |
L["dual_profile"] = "éè¨å®æª" |
L["profiles"] = "è¨å®æª" |
-- L["profiles_sub"] = "管çè¨å®æª" |
--L["current"] = "Current Profile:" |
elseif LOCALE == "zhCN" then |
L["profiles"] = "é ç½®æ件" |
--L["current"] = "Current Profile:" |
L["default"] = "é»è®¤" |
-- L["intro"] = "ä½ å¯ä»¥éæ©ä¸ä¸ªæ´»å¨çæ°æ®é ç½®æ件ï¼è¿æ ·ä½ çæ¯ä¸ªè§è²å°±å¯ä»¥æ¥æä¸åç设置å¼ï¼å¯ä»¥ç»ä½ çæ件é 置带æ¥æ大ççµæ´»æ§ã" |
-- L["reset_desc"] = "å°å½åçé ç½®æ件æ¢å¤å°å®çé»è®¤å¼ï¼ç¨äºä½ çé ç½®æ件æåï¼æè ä½ åªæ¯æ³éæ¥çæ åµã" |
L["reset"] = "éç½®é ç½®æ件" |
-- L["reset_sub"] = "å°å½åçé ç½®æ件æ¢å¤ä¸ºé»è®¤å¼" |
L["choose_desc"] = "ä½ å¯ä»¥éè¿å¨ææ¬æ¡å è¾å ¥ä¸ä¸ªåååç«ä¸ä¸ªæ°çé ç½®æ件ï¼ä¹å¯ä»¥éæ©ä¸ä¸ªå·²ç»åå¨çé ç½®æ件ã" |
L["new"] = "æ°å»º" |
-- L["new_sub"] = "æ°å»ºä¸ä¸ªç©ºçé ç½®æ件ã" |
L["choose"] = "ç°æçé ç½®æ件" |
-- L["choose_sub"] = "ä»å½åå¯ç¨çé ç½®æ件éé¢éæ©ä¸ä¸ªã" |
L["copy_desc"] = "ä»å½åæ个已ä¿åçé ç½®æ件å¤å¶å°å½åæ£ä½¿ç¨çé ç½®æ件ã" |
L["copy"] = "å¤å¶èª" |
L["delete_desc"] = "ä»æ°æ®åºéå é¤ä¸å使ç¨çé ç½®æ件ï¼ä»¥èç空é´ï¼å¹¶ä¸æ¸ çSavedVariablesæ件ã" |
L["delete"] = "å é¤ä¸ä¸ªé ç½®æ件" |
-- L["delete_sub"] = "ä»æ°æ®åºéå é¤ä¸ä¸ªé ç½®æ件ã" |
L["delete_confirm"] = "ä½ ç¡®å®è¦å é¤æéæ©çé ç½®æ件ä¹ï¼" |
L["profiles"] = "é ç½®æ件" |
-- L["profiles_sub"] = "管çé ç½®æ件" |
--L["current"] = "Current Profile:" |
L["dualspec_desc"] = "å¯æ¶ï¼ä½ å¯ä»¥ä¸ºä½ çå天èµè®¾å®å¦ä¸ç»é ç½®æ件ï¼ä½ çåéé ç½®æ件å°å¨ä½ 转æ¢å¤©èµæ¶èªå¨ä¸ç®å使ç¨é ç½®æ件交æ¢ã" |
L["dual_profile"] = "åéé ç½®æ件" |
L["enabled"] = "å¼å¯åéé ç½®æ件" |
L["enabled_desc"] = "å¾é以便转æ¢å¤©èµæ¶èªå¨äº¤æ¢é ç½®æ件ã" |
L["dual_profile"] = "åéé ç½®æ件" |
elseif LOCALE == "ruRU" then |
L["profiles"] = "ÐÑоÑили" |
--L["current"] = "Current Profile:" |
L["default"] = "Ðо ÑмолÑаниÑ" |
-- L["intro"] = "ÐзменÑÑ Ð°ÐºÑивнÑй пÑоÑилÑ, Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе задаÑÑ ÑазлиÑнÑе наÑÑÑойки модиÑикаÑий Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ пеÑÑонажа." |
-- L["reset_desc"] = "ÐÑли ваÑа конÑигÑÑаÑии иÑпоÑÑена или еÑли Ð²Ñ Ñ Ð¾ÑиÑе наÑÑÑоиÑÑ Ð²ÑÑ Ð·Ð°Ð½Ð¾Ð²Ð¾ - ÑбÑоÑÑÑе ÑекÑÑий пÑоÑÐ¸Ð»Ñ Ð½Ð° ÑÑандаÑÑнÑе знаÑениÑ." |
L["reset"] = "СбÑÐ¾Ñ Ð¿ÑоÑилÑ" |
-- L["reset_sub"] = "СбÑÐ¾Ñ ÑекÑÑего пÑоÑÐ¸Ð»Ñ Ð½Ð° ÑÑандаÑÑнÑй" |
L["choose_desc"] = "ÐÑ Ð¼Ð¾Ð¶ÐµÑе ÑоздаÑÑ Ð½Ð¾Ð²Ñй пÑоÑилÑ, Ð²Ð²ÐµÐ´Ñ Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ в поле ввода, или вÑбÑаÑÑ Ð¾Ð´Ð¸Ð½ из Ñже ÑÑÑеÑÑвÑÑÑÐ¸Ñ Ð¿ÑоÑилей." |
L["new"] = "ÐовÑй" |
-- L["new_sub"] = "СоздаÑÑ Ð½Ð¾Ð²Ñй ÑиÑÑÑй пÑоÑилÑ" |
L["choose"] = "СÑÑеÑÑвÑÑÑие пÑоÑили" |
-- L["choose_sub"] = "ÐÑÐ±Ð¾Ñ Ð¾Ð´Ð¸Ð½Ð¾Ð³Ð¾ из Ñже доÑÑÑпнÑÑ Ð¿ÑоÑилей" |
L["copy_desc"] = "СкопиÑоваÑÑ Ð½Ð°ÑÑÑойки из вÑбÑанного пÑоÑÐ¸Ð»Ñ Ð² акÑивнÑй." |
L["copy"] = "СкопиÑоваÑÑ Ð¸Ð·" |
L["delete_desc"] = "УдалиÑÑ ÑÑÑеÑÑвÑÑÑий и неиÑполÑзÑемÑй пÑоÑÐ¸Ð»Ñ Ð¸Ð· ÐÐ Ð´Ð»Ñ ÑÐ¾Ñ ÑÐ°Ð½ÐµÐ½Ð¸Ñ Ð¼ÐµÑÑа, и оÑиÑÑиÑÑ SavedVariables Ñайл." |
L["delete"] = "УдалиÑÑ Ð¿ÑоÑилÑ" |
-- L["delete_sub"] = "Удаление пÑоÑÐ¸Ð»Ñ Ð¸Ð· ÐÐ" |
L["delete_confirm"] = "ÐÑ ÑвеÑенÑ, ÑÑо Ð²Ñ Ñ Ð¾ÑиÑе ÑдалиÑÑ Ð²ÑбÑаннÑй пÑоÑилÑ?" |
L["profiles"] = "ÐÑоÑили" |
-- L["profiles_sub"] = "УпÑавление пÑоÑилÑми" |
--L["current"] = "Current Profile:" |
L["dualspec_desc"] = "Ðвойной пÑоÑÐ¸Ð»Ñ Ð¿Ð¾Ð·Ð²Ð¾Ð»ÑÐµÑ Ð²Ð°Ð¼ вÑбÑаÑÑ ÑазлиÑнÑе пÑоÑили Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð¹ ÑаÑкладки ÑаланÑов. ÐÑоÑили бÑдÑÑ Ð¿ÐµÑеклÑÑаÑÑÑÑ ÐºÐ°Ð¶Ð´Ñй Ñаз, когда Ð²Ñ Ð¿ÐµÑеклÑÑаеÑе ÑаÑÐºÐ»Ð°Ð´ÐºÑ ÑаланÑов." |
L["dual_profile"] = "ÐÑоÑой пÑоÑилÑ" |
L["enabled"] = "ÐклÑÑиÑÑ Ð´Ð²Ð¾Ð¹Ð½Ð¾Ð¹ пÑоÑилÑ" |
L["enabled_desc"] = "ÐклÑÑиÑе ÑÑÑ Ð¾Ð¿ÑÐ¸Ñ Ð´Ð»Ñ Ð°Ð²ÑомаÑиÑеÑкого пеÑеклÑÑÐµÐ½Ð¸Ñ Ð¼ÐµÐ¶Ð´Ñ Ð¿ÑоÑилÑми пÑи пеÑеклÑÑении ÑаÑкладки ÑаланÑов." |
end |
chooseDesc:SetText(L.choose_desc) |
local newProfile = templates:CreateEditBox(frame) |
newProfile:SetAutoFocus(false) |
newProfile:SetWidth(160) |
newProfile:SetPoint("TOPLEFT", chooseDesc, "BOTTOMLEFT", 0, -16) |
newProfile:SetScript("OnEscapePressed", newProfile.ClearFocus) |
label:SetHeight(18) |
label:SetText(L.new) |
local choose = templates:CreateDropDownMenu("CritlineDBChooseProfile"..name, frame, nil, initializeDropdown, defaultProfiles) |
local choose = templates:CreateDropDownMenu("CritlineDBChooseProfile"..name, frame, nil, defaultProfiles) |
choose:SetFrameWidth(144) |
choose:SetPoint("LEFT", newProfile, "RIGHT", 0, -2) |
choose.label:SetText(L.choose) |
choose.initialize = initializeDropdown |
choose.func = chooseProfileOnClick |
choose.common = true |
objects.choose = choose |
text:SetText(L.enabled) |
objects.dualEnabled = enabled |
local dualProfile = templates:CreateDropDownMenu("CritlineDBDualProfile"..name, frame, nil, initializeDropdown, defaultProfiles) |
local dualProfile = templates:CreateDropDownMenu("CritlineDBDualProfile"..name, frame, nil, defaultProfiles) |
dualProfile:SetFrameWidth(144) |
dualProfile:SetPoint("LEFT", choose) |
dualProfile:SetPoint("TOP", enabled) |
dualProfile.label:SetText(L.dual_profile) |
dualProfile.initialize = initializeDropdown |
dualProfile.func = dualProfileOnClick |
dualProfile.common = true |
objects.dualProfile = dualProfile |
copyDesc:SetWordWrap(true) |
copyDesc:SetText(L.copy_desc) |
local copy = templates:CreateDropDownMenu("CritlineDBCopyProfile"..name, frame, nil, initializeDropdown, defaultProfiles) |
local copy = templates:CreateDropDownMenu("CritlineDBCopyProfile"..name, frame, nil, defaultProfiles) |
copy:SetFrameWidth(144) |
copy:SetPoint("TOPLEFT", copyDesc, "BOTTOMLEFT", -16, -8) |
copy.label:SetText(L.copy) |
copy.initialize = initializeDropdown |
copy.func = copyProfileOnClick |
copy.nocurrent = true |
objects.copy = copy |
deleteDesc:SetWordWrap(true) |
deleteDesc:SetText(L.delete_desc) |
local delete = templates:CreateDropDownMenu("CritlineDBDeleteProfile"..name, frame, nil, initializeDropdown, defaultProfiles) |
local delete = templates:CreateDropDownMenu("CritlineDBDeleteProfile"..name, frame, nil, defaultProfiles) |
delete:SetFrameWidth(144) |
delete:SetPoint("TOPLEFT", deleteDesc, "BOTTOMLEFT", -16, -8) |
delete.label:SetText(L.delete) |
delete.initialize = initializeDropdown |
delete.func = deleteProfileOnClick |
delete.nocurrent = true |
objects.delete = delete |
local feeds = { |
dmg = L["Damage"], |
dmg = L["Damage"], |
heal = L["Healing"], |
pet = L["Pet"], |
pet = L["Pet"], |
} |
local msgFormat = format("%s: %%s - %s: %%s", L["Normal"], L["Crit"]) |
for k, v in pairs(feeds) do |
feeds[k] = LDB:NewDataObject("Critline "..addon.treeNames[k], { |
type = "data source", |
icon = addon.icons[k], |
OnClick = function() |
if IsShiftKeyDown() then |
local normalRecord, critRecord, normalSpell, critSpell = addon:GetHighest(k) |
local normal = format("%s: %s", L["Normal"], (normalSpell and format("%d (%s)", normalRecord, normalSpell) or "n/a")) |
local crit = format("%s: %s", L["Crit"], (critSpell and format("%d (%s)", critRecord, critSpell) or "n/a")) |
ChatFrame_OpenChat(normal.." - "..crit) |
local normalRecord, critRecord = addon:GetHighest(k) |
local normalSpell, critSpell |
local spells = addon:GetSpellArray(k) |
for i = 1, #spells do |
local v = spells[i] |
local normal = v.normal |
if normal and normal.amount == normalRecord then |
normalSpell = v.spellName |
end |
local crit = v.crit |
if crit and crit.amount == critRecord then |
critSpell = v.spellName |
end |
if (normalSpell or not normalRecord) and (critSpell or not normalRecord) then |
break |
end |
end |
local normal = normalSpell and format("%s (%s)", addon:ShortenNumber(normalRecord), normalSpell) or L["n/a"] |
local crit = critSpell and format("%s (%s)", addon:ShortenNumber(critRecord), critSpell) or L["n/a"] |
ChatFrame_OpenChat(format(msgFormat, normal, crit)) |
else |
addon:OpenConfig() |
end |
end |
local function updateRecords(event, tree, isFiltered) |
if not isFiltered then |
if tree then |
feeds[tree].text = format("%s/%s", addon:GetHighest(tree)) |
else |
for k in pairs(feeds) do |
updateRecords(nil, k) |
end |
local function updateRecords(event, tree) |
if tree then |
local normal, crit = addon:GetHighest(tree) |
feeds[tree].text = format("%s/%s", addon:ShortenNumber(normal), addon:ShortenNumber(crit)) |
else |
for k in pairs(feeds) do |
updateRecords(event, k) |
end |
end |
end |
addon.RegisterCallback(feeds, "PerCharSettingsLoaded", updateRecords) |
addon.RegisterCallback(feeds, "RecordsChanged", updateRecords) |
addon.RegisterCallback(feeds, "SpellsChanged", updateRecords) |
4.0.0 |
Core: |
* Rewrote database to store spells by ID rather than name. This is more flexible and opens up a few possibilities. |
* Most spells will be carried over to the new database, but others (mostly procs and item effects) will be lost. |
- Absorb effects are now stored as healing records. Uses the remaining absorb amount when you apply shields. |
- Added option to use shorter number format for records. (eg 13 k instead of 13000) |
- Added option to display records in its spell's tooltip. |
- Spells that have different versions, but identical effects, (such as "Pyroblast!" and "Ravage!") are now treated as one and the same. |
- Added compability for patch 4.1. |
- Certain guardian type pets should once again have their records properly registered. |
- Periodic damage is now annotated by an asterisk (*) or the word "tick" rather than DoT/HoT. |
- Removed some legacy code. |
- Some code and performance optimisation. |
Filter: |
- Implemented basic filtering based on target auras. For now you have to be in combat log range when an aura is applied or removed for the addon to register it. See filters.lua for details. |
- Implemented a basic aura tracker (mostly for debugging purposes). Type '/critline aura' to show or hide it. |
- Removed option to invert spell filter. From now on unchecked spells are filtered, and new spells are not filtered by default. |
- Added option to automatically filter newly registered spells. (disabled by default) |
- Spell filter now shows spell tooltip and associated records on mouseover. |
- Spells with no records are no longer kept in the database for the purpose of saving its filter flag. |
- Added Drakeadon Mongrel and Exposed Head of Magmaw to mob filter. |
- Added Power Generator, Engulfing Magic, Lightning Charge and Blessing of the Sun (heroic) to aura filter. |
Display: |
- The different panels' background color can now be changed as desired. |
- The frame can now be scaled up to 200%. (from 150%) |
- The frame's alpha can now be changed. |
- It should no longer be possible to force show the frame when no trees are enabled, resulting in a weird appearance. |
- Reset and announce spell lists had their appearance redesigned. Click to directly to your thing, instead of checking and then clicking. |
- New records achieved in the last fight can now be undone in the reset module. Eligible spells will have a different button. |
- Spell profiles should now display the correct used profiles at all times. |
- Updated libraries. |
3.1.3 |
- Added Karsh Steelbender, Ragnaros (Mount Hyjal) and Debilitated Apexar to mob filter. |
- Added Tidal Surge, Pyrogenics, Blessing of the Sun, Empowering Twilight, |
local L = LibStub("AceLocale-3.0"):GetLocale(addonName) |
local templates = addon.templates |
local width, height |
local width, height = 128, 22 |
local trees = { |
dmg = L["Damage"], |
dmg = L["Damage"], |
heal = L["Healing"], |
pet = L["Pet"], |
pet = L["Pet"], |
} |
GameTooltip:Show() |
end |
local backdrop = { |
bgFile = [[Interface\ChatFrame\ChatFrameBackground]], |
insets = {left = -1, right = -1, top = -1, bottom = -1}, |
} |
local function createDisplay(parent) |
local frame = CreateFrame("Frame", nil, parent) |
frame:SetFrameStrata("LOW") |
frame:EnableMouse(true) |
frame:RegisterForDrag("LeftButton") |
frame:SetPoint("LEFT", 4, 0) |
frame:SetPoint("RIGHT", -4, 0) |
frame:SetBackdrop(backdrop) |
frame:SetScript("OnDragStart", onDragStart) |
frame:SetScript("OnDragStop", onDragStop) |
frame:SetScript("OnEnter", onEnter) |
addon.display = display |
display:SetMovable(true) |
display:SetBackdrop({ |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
edgeSize = 16, |
insets = {left = 4, right = 4, top = 4, bottom = 4} |
edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]], |
edgeSize = 12, |
}) |
display:SetBackdropColor(0, 0, 0, 0.8) |
display:SetBackdropBorderColor(0.5, 0.5, 0.5, 1) |
display.trees = {} |
Critline.SlashCmdHandlers["reset"] = function() |
display:ClearAllPoints() |
display:SetPoint("CENTER") |
end |
for k, treeName in pairs(trees) do |
local frame = createDisplay(display) |
frame.icon:SetTexture(addon.icons[k]) |
local config = templates:CreateConfigFrame("Display", addonName, true) |
local function displayFunc(self) |
self.module:UpdateLayout(self.setting, self:GetChecked()) |
end |
local options = { |
db = {}, |
{ |
tooltipText = L["Show summary frame."], |
setting = "show", |
func = function(self) |
if self:GetChecked() then |
display:Show() |
else |
display:Hide() |
end |
display:UpdateLayout() |
end, |
}, |
{ |
end |
local slider = templates:CreateSlider(config, { |
local sliders = {} |
sliders[1] = templates:CreateSlider(config, { |
text = L["Scale"], |
tooltipText = L["Sets the scale of the summary frame."], |
tooltipText = L["Sets the scale of the display."], |
minValue = 0.5, |
maxValue = 1.5, |
maxValue = 2, |
valueStep = 0.05, |
minText = "50%", |
maxText = "150%", |
maxText = "200%", |
func = function(self) |
local value = self:GetValue() |
self.value:SetFormattedText("%.0f%%", value * 100) |
display.profile.scale = value |
end, |
}) |
slider:SetPoint("TOPLEFT", options[#options], "BOTTOMLEFT", 4, -24) |
sliders[1]:SetPoint("TOPLEFT", options[#options], "BOTTOMLEFT", 4, -24) |
sliders[2] = templates:CreateSlider(config, { |
text = L["Alpha"], |
tooltipText = L["Sets the opacity of the display."], |
minValue = 0, |
maxValue = 1, |
valueStep = 0.05, |
minText = "0%", |
maxText = "100%", |
func = function(self) |
local value = self:GetValue() |
self.value:SetFormattedText("%.0f%%", value * 100) |
display:SetAlpha(value) |
display.profile.alpha = value |
end, |
}) |
sliders[2]:SetPoint("TOP", sliders[1], "BOTTOM", 0, -32) |
local function swatchFunc(self, r, g, b) |
display.trees[self.setting]:SetBackdropColor(r, g, b) |
end |
local colorButtons = {} |
for i, v in ipairs({"dmg", "heal", "pet"}) do |
local btn = templates:CreateColorButton(config) |
if i == 1 then |
btn:SetPoint("TOPLEFT", config.title, "BOTTOM", 0, -21) |
else |
btn:SetPoint("TOP", colorButtons[i - 1], "BOTTOM", 0, -18) |
end |
btn:SetText(trees[v]) |
btn.setting = v |
btn.func = swatchFunc |
btn.opacityFunc = opacityFunc |
colorButtons[i] = btn |
end |
local defaults = { |
profile = { |
show = true, |
locked = false, |
icons = true, |
scale = 1, |
alpha = 1, |
colors = { |
dmg = {r = 0, g = 0, b = 0}, |
heal = {r = 0, g = 0, b = 0}, |
pet = {r = 0, g = 0, b = 0}, |
}, |
pos = { |
point = "CENTER", |
}, |
function display:AddonLoaded() |
self.db = addon.db:RegisterNamespace("display", defaults) |
addon.RegisterCallback(self, "SettingsLoaded") |
addon.RegisterCallback(self, "PerCharSettingsLoaded", "UpdateRecords") |
addon.RegisterCallback(self, "SpellsChanged", "UpdateRecords") |
addon.RegisterCallback(self, "RecordsChanged", "UpdateRecords") |
addon.RegisterCallback(self, "OnNewTopRecord", "UpdateRecords") |
end |
addon.RegisterCallback(display, "AddonLoaded") |
function display:SettingsLoaded() |
self.profile = self.db.profile |
self:UpdateRecords() |
for _, btn in ipairs(options.db) do |
btn:LoadSetting() |
end |
local colors = self.profile.colors |
for _, btn in ipairs(colorButtons) do |
local color = colors[btn.setting] |
local r, g, b = color.r, color.g, color.b |
btn:func(r, g, b) |
btn.swatch:SetVertexColor(r, g, b) |
btn.color = color |
end |
-- restore stored position |
local pos = self.profile.pos |
self:ClearAllPoints() |
local scale = self.profile.scale |
-- need to set scale separately first to ensure proper behaviour in scale-friendly repositioning |
self:SetScale(scale) |
slider:SetValue(scale) |
sliders[1]:SetValue(scale) |
sliders[2]:SetValue(self.profile.alpha) |
end |
function display:UpdateRecords(event, tree, isFiltered) |
if not isFiltered then |
if tree then |
self.trees[tree].text:SetFormattedText("%6d/%-6d", addon:GetHighest(tree)) |
else |
for k in pairs(self.trees) do |
self:UpdateRecords(nil, k) |
end |
function display:UpdateRecords(event, tree) |
if tree then |
local normal, crit = addon:GetHighest(tree) |
self.trees[tree].text:SetFormattedText("%8s / %-8s", addon:ShortenNumber(normal), addon:ShortenNumber(crit)) |
else |
for k in pairs(trees) do |
self:UpdateRecords(nil, k) |
end |
end |
end |
end |
function display:Toggle() |
local show = not self.profile.show |
self.profile.show = show |
options[1]:SetChecked(show) |
self:UpdateLayout() |
end |
-- rearrange display buttons when any of them is shown or hidden |
function display:UpdateLayout() |
local trees = self.trees |
if heal:IsShown() then |
if dmg:IsShown() then |
heal:SetPoint("TOP", dmg, "BOTTOM") |
heal:SetPoint("TOP", dmg, "BOTTOM", 0, -2) |
else |
heal:SetPoint("TOP", 0, -4) |
end |
end |
if pet:IsShown() then |
if heal:IsShown() then |
pet:SetPoint("TOP", heal, "BOTTOM") |
pet:SetPoint("TOP", heal, "BOTTOM", 0, -2) |
elseif dmg:IsShown() then |
pet:SetPoint("TOP", dmg, "BOTTOM") |
pet:SetPoint("TOP", dmg, "BOTTOM", 0, -2) |
else |
pet:SetPoint("TOP", 0, -4) |
end |
n = n + 1 |
end |
self:SetSize(width, n * height + 8) |
self:SetSize(width, n * (height + 2) + 6) |
-- hide the entire frame if it turns out none of the individual frames are shown |
if n == 0 then |
if n == 0 or not self.profile.show then |
self:Hide() |
elseif self.profile.show then |
else |
self:Show() |
end |
end |
local templates = addon.templates |
local module = templates:CreateList("CritlineAnnounce", L["Announce"]) |
local module = templates:CreateList("CritlineAnnounce", L["Announce"], "Announce") |
do |
local button = module.button |
button:SetScript("OnClick", function() |
PlaySound("gsTitleOptionOK") |
module:AnnounceRecords() |
end) |
addon.RegisterCallback(module, "SpellsChanged", "Update") |
local function onClick(self) |
self.owner:SetSelectedValue(self.value) |
local target = module.target |
if self.value == "WHISPER" or self.value == "CHANNEL" then |
target:Show() |
target:SetFocus() |
else |
target:Hide() |
end |
local function onClick(self) |
self.owner:SetSelectedValue(self.value) |
local target = module.target |
if self.value == "WHISPER" or self.value == "CHANNEL" then |
target:Show() |
target:SetFocus() |
else |
target:Hide() |
end |
end |
local channels = { |
"SAY", |
"GUILD", |
"PARTY", |
"RAID", |
"WHISPER", |
"CHANNEL", |
} |
local function initialize(self) |
for i, v in ipairs(channels) do |
local info = UIDropDownMenu_CreateInfo() |
info.text = _G[v] |
info.value = v |
info.func = onClick |
info.owner = self |
UIDropDownMenu_AddButton(info) |
end |
end |
local channels = { |
"SAY", |
"GUILD", |
"PARTY", |
"RAID", |
"WHISPER", |
"CHANNEL", |
} |
local channel = templates:CreateDropDownMenu("CritlineAnnounceChannel", module, nil, initialize, _G) |
channel:SetFrameWidth(120) |
channel:SetPoint("TOPLEFT", module.tree, "BOTTOMLEFT") |
channel:SetSelectedValue("SAY") |
module.channel = channel |
local target = templates:CreateEditBox(module) |
target:SetAutoFocus(false) |
target:SetWidth(144) |
target:SetPoint("LEFT", channel, "RIGHT", 0, 2) |
target:SetScript("OnEscapePressed", target.ClearFocus) |
target:SetScript("OnEnterPressed", function(self) module:AnnounceRecords() end) |
target:Hide() |
module.target = target |
local channel = templates:CreateDropDownMenu("CritlineAnnounceChannel", module, nil, _G) |
channel:SetFrameWidth(120) |
channel:SetPoint("TOPLEFT", module.tree, "BOTTOMLEFT") |
channel:SetSelectedValue("SAY") |
channel.initialize = function(self) |
for i, v in ipairs(channels) do |
local info = UIDropDownMenu_CreateInfo() |
info.text = _G[v] |
info.value = v |
info.func = onClick |
info.owner = self |
UIDropDownMenu_AddButton(info) |
end |
end |
module.channel = channel |
local target = templates:CreateEditBox(module) |
target:SetWidth(144) |
target:SetPoint("LEFT", channel, "RIGHT", 0, 2) |
target:SetScript("OnEnterPressed", target.ClearFocus) |
target:SetScript("OnEscapePressed", target.ClearFocus) |
target:Hide() |
module.target = target |
addon.RegisterCallback(module, "SpellsChanged", "Update") |
local announceFormat = format("%%s - %s: %%s %s: %%s", L["Normal"], L["Crit"]) |
function module:AnnounceRecords() |
function module:Announce(data) |
local channel = self.channel:GetSelectedValue() |
local tree = self.tree:GetSelectedValue() |
local spells = addon.percharDB.profile.spells[tree] |
local target = self.target:GetText():trim() |
if channel == "WHISPER" then |
end |
end |
local sent |
for i in pairs(self.selectedSpells) do |
local data = spells[i] |
local normal = data.normal and data.normal.amount |
local crit = data.crit and data.crit.amount |
local text = format("%s - %s: %s %s: %s", addon:GetFullSpellName(tree, data.spellName, data.isPeriodic), L["Normal"], (normal or "n/a"), L["Crit"], (crit or "n/a")) |
SendChatMessage(text, channel, nil, target) |
sent = true |
end |
if sent then |
wipe(self.selectedSpells) |
self:Update() |
self.button:Disable() |
self.target:SetText("") |
end |
local normal = data.normal and addon:ShortenNumber(data.normal.amount) |
local crit = data.crit and addon:ShortenNumber(data.crit.amount) |
local text = format(announceFormat, addon:GetFullSpellName(data.spellID, data.periodic, true), (normal or L["n/a"]), (crit or L["n/a"])) |
SendChatMessage(text, channel, nil, target) |
self.target:SetText("") |
end |
local addonName, Critline = ... |
_G.Critline = Critline |
-- local addon = { } |
-- local mt_func = { __index = function() return function() end end } |
-- local empty_tbl = { } |
-- local mt = { __index = function() return setmetatable(empty_tbl, mt_func) end } |
-- setmetatable(addon, mt) |
-- print(addon.module.method()) |
local L = LibStub("AceLocale-3.0"):GetLocale(addonName) |
local templates = Critline.templates |
local playerClass = select(2, UnitClass("player")) |
local debugging |
-- auto attack spell |
local AUTOATK = GetSpellInfo(6603) |
local AUTOATK_ID = 6603 |
local AUTOATK = GetSpellInfo(AUTOATK_ID) |
-- local references to commonly used functions and variables for faster access |
local HasPetUI = HasPetUI |
local treeNames = { |
dmg = L["damage"], |
dmg = L["damage"], |
heal = L["healing"], |
pet = L["pet"], |
pet = L["pet"], |
} |
Critline.treeNames = treeNames |
Critline.icons = { |
dmg = "Interface\\Icons\\Ability_SteelMelee", |
heal = "Interface\\Icons\\Spell_Holy_FlashHeal", |
pet = "Interface\\Icons\\Ability_Hunter_Pet_Bear", |
dmg = [[Interface\Icons\Ability_SteelMelee]], |
heal = [[Interface\Icons\Spell_Holy_FlashHeal]], |
pet = [[Interface\Icons\Ability_Hunter_Pet_Bear]], |
} |
[37994] = true, -- Water Elemental (glyphed) |
} |
-- spells that are essentially the same, but has different IDs, we'll register them under the same ID |
local similarSpells = { |
[27285] = 27243, -- Seed of Corruption (direct) |
[33778] = 33763, -- Lifebloom (direct) |
[44461] = 44457, -- Living Bomb (direct) |
[47960] = 47897, -- Shadowflame (tick) |
[81170] = 6785, -- Ravage (Stampede) |
[83853] = 11129, -- Combustion (tick) |
[88148] = 2120, -- Flamestrike (Improved Flamestrike) |
[92315] = 11366, -- Pyroblast (Hot Streak) |
} |
-- spells whose actual effect is the result of a different spell, eg seals, damage shields, used for displaying relevant records in spell tooltips |
local indirectSpells = { |
[324] = 26364, -- Lightning Shield |
[724] = 7001, -- Lightwell |
[740] = 44203, -- Tranquility |
[772] = 94009, -- Rend |
[974] = 379, -- Earth Shield |
[1329] = 5374, -- Mutilate |
[1535] = 8349, -- Fire Nova |
[1949] = 5857, -- Hellfire |
[5938] = 5940, -- Shiv |
[8024] = 10444, -- Flametongue Weapon |
[8033] = 8034, -- Frostbrand Weapon |
[8232] = 25504, -- Windfury Weapon |
[16857] = 60089, -- Faerie Fire (Feral) |
[16914] = 42231, -- Hurricane |
[17364] = 32175, -- Stormstrike |
[20154] = 25742, -- Seal of Righteousness |
[20164] = 20170, -- Seal of Justice |
[20165] = 20167, -- Seal of Insight |
[20473] = 25912, -- Holy Shock |
[22842] = 22845, -- Frenzied Regeneration |
[26573] = 81297, -- Consecration |
[31801] = 42463, -- Seal of Truth |
[31850] = 66235, -- Ardent Defender |
[33076] = 33110, -- Prayer of Mending |
[43265] = 52212, -- Death and Decay |
[47540] = 47666, -- Penance |
[47541] = 47632, -- Death Coil (death knight) |
[47788] = 48153, -- Guardian Spirit |
[48045] = 49821, -- Mind Sear |
[51730] = 51945, -- Earthliving |
[61882] = 77478, -- Earthquake |
[64843] = 64844, -- Divine Hymn |
[73920] = 73921, -- Healing Rain |
[82327] = 86452, -- Holy Radiance |
[88685] = 88686, -- Holy Word: Sanctuary |
} |
-- those that has both a damage and a healing component has their healing spell listed here |
local indirectHeals = { |
[15237] = 23455, -- Holy Nova |
[20473] = 25914, -- Holy Shock |
[47540] = 47750, -- Penance |
[47541] = 47633, -- Death Coil (death knight) |
[49998] = 45470, -- Death Strike |
[53385] = 54172, -- Divine Storm |
} |
-- cache of spell ID -> spell name |
local spellNameCache = { |
-- add form name to hybrid druid abilities, so the user can tell which is cat and which is bear |
[33878] = format("%s (%s)", GetSpellInfo(33878)), -- Mangle (Bear Form) |
[33876] = format("%s (%s)", GetSpellInfo(33876)), -- Mangle (Cat Form) |
[779] = format("%s (%s)", GetSpellInfo(779)), -- Swipe (Bear Form) |
[62078] = format("%s (%s)", GetSpellInfo(62078)), -- Swipe (Cat Form) |
} |
-- cache of spell textures |
local spellTextureCache = { |
-- use a static icon for auto attack (otherwise uses your weapon's icon) |
[AUTOATK_ID] = [[Interface\Icons\INV_Sword_04]], |
-- "fix" some other misleading icons |
[20253] = GetSpellTexture(20252), -- Intercept |
[26364] = GetSpellTexture(324), -- use Lightning Shield icon for Lightning Shield damage |
[66235] = GetSpellTexture(31850), -- Ardent Defender icon for Ardent Defender heal |
} |
local swingDamage = function(amount, _, school, resisted, _, _, critical) |
return AUTOATK, amount, resisted, critical, school |
return AUTOATK_ID, AUTOATK, amount, resisted, critical, school |
end |
local spellDamage = function(_, spellName, _, amount, _, school, resisted, _, _, critical) |
return spellName, amount, resisted, critical, school |
local spellDamage = function(spellID, spellName, _, amount, _, school, resisted, _, _, critical) |
return spellID, spellName, amount, resisted, critical, school |
end |
local healing = function(_, spellName, _, amount, _, _, critical) |
return spellName, amount, 0, critical, 0 |
local healing = function(spellID, spellName, _, amount, _, _, critical) |
return spellID, spellName, amount, 0, critical, 0 |
end |
local absorb = function(spellID, spellName, _, _, amount) |
return spellID, spellName, amount, 0, critical, 0 |
end |
local combatEvents = { |
SWING_DAMAGE = swingDamage, |
RANGE_DAMAGE = spellDamage, |
SPELL_PERIODIC_DAMAGE = spellDamage, |
SPELL_HEAL = healing, |
SPELL_PERIODIC_HEAL = healing, |
SPELL_AURA_APPLIED = absorb, |
SPELL_AURA_REFRESH = absorb, |
} |
local recordSorters = { |
-- alpha: sort by name |
alpha = function(a, b) |
if a == b then return end |
if a.spellName == b.spellName then |
-- alpha: sort by name |
local alpha = function(a, b) |
if a == b then return end |
if a.spellName == b.spellName then |
if a.spellID == b.spellID then |
-- sort DoT entries after non DoT |
return b.isPeriodic |
return a.periodic < b.periodic |
else |
return a.spellName < b.spellName |
return a.spellID < b.spellID |
end |
end, |
-- crit: sort by crit > normal > name |
crit = function(a, b) |
if a == b then return end |
else |
return a.spellName < b.spellName |
end |
end |
-- normal: sort by normal > crit > name |
local normal = function(a, b) |
if a == b then return end |
local normalA, normalB = (a.normal and a.normal.amount or 0), (b.normal and b.normal.amount or 0) |
if normalA == normalB then |
-- equal normal amounts, sort by crit amount instead |
local critA, critB = (a.crit and a.crit.amount or 0), (b.crit and b.crit.amount or 0) |
if critA == critB then |
-- equal crit amounts, sort by normal amount instead |
local normalA, normalB = (a.normal and a.normal.amount or 0), (b.normal and b.normal.amount or 0) |
if normalA == normalB then |
-- equal normal amounts, sort by name instead |
if a.spellName == b.spellName then |
return b.isPeriodic |
else |
return a.spellName < b.spellName |
end |
else |
return normalA > normalB |
end |
-- equal crit amounts too, sort by name instead |
return alpha(a, b) |
else |
return critA > critB |
end |
end, |
-- normal: sort by normal > crit > name |
normal = function(a, b) |
if a == b then return end |
local normalA, normalB = (a.normal and a.normal.amount or 0), (b.normal and b.normal.amount or 0) |
if normalA == normalB then |
local critA, critB = (a.crit and a.crit.amount or 0), (b.crit and b.crit.amount or 0) |
if critA == critB then |
if a.spellName == b.spellName then |
return b.isPeriodic |
else |
return a.spellName < b.spellName |
end |
else |
return critA > critB |
end |
else |
return normalA > normalB |
end |
end, |
else |
return normalA > normalB |
end |
end |
-- crit: sort by crit > normal > name |
local crit = function(a, b) |
if a == b then return end |
local critA, critB = (a.crit and a.crit.amount or 0), (b.crit and b.crit.amount or 0) |
if critA == critB then |
return normal(a, b) |
else |
return critA > critB |
end |
end |
local recordSorters = { |
alpha = alpha, |
normal = normal, |
crit = crit, |
} |
-- this will hold the text for the summary tooltip |
local tooltips = {dmg = {}, heal = {}, pet = {}} |
-- indicates whether a given tree will need to have its tooltip updated before next use |
local doTooltipUpdate = {} |
-- overall record for each tree |
local topRecords = { |
dmg = {normal = 0, crit = 0}, |
heal = {normal = 0, crit = 0}, |
pet = {normal = 0, crit = 0}, |
} |
-- sortable spell tables |
local spellArrays = {dmg = {}, heal = {}, pet = {}} |
-- tooltip for level scanning |
local tooltip = CreateFrame("GameTooltip", "CritlineTooltip", nil, "GameTooltipTemplate") |
Critline.eventFrame = CreateFrame("Frame") |
function Critline:RegisterEvent(event) |
self.eventFrame:RegisterEvent(event) |
self.eventFrame:UnregisterEvent(event) |
end |
Critline:RegisterEvent("ADDON_LOADED") |
Critline:RegisterEvent("PLAYER_TALENT_UPDATE") |
Critline:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") |
Critline.eventFrame:SetScript("OnEvent", function(self, event, ...) |
return Critline[event] and Critline[event](Critline, ...) |
Critline.options = options |
local function toggleTree(self) |
callbacks:Fire("OnTreeStateChanged", self.setting, self:GetChecked()) |
local display = Critline.display |
if display then |
display:UpdateTree(self.setting) |
setting = "PvP", |
}, |
{ |
text = L["Ignore vulnerability"], |
tooltipText = L["Enable to ignore additional damage due to vulnerability."], |
setting = "ignoreVulnerability", |
}, |
{ |
text = L["Chat output"], |
tooltipText = L["Prints new record notifications to the chat frame."], |
setting = "chatOutput", |
newColumn = true, |
}, |
{ |
text = L["Play sound"], |
text = L["Screenshot"], |
tooltipText = L["Saves a screenshot on a new record."], |
setting = "screenshot", |
newColumn = true, |
}, |
{ |
text = L["Shorten records"], |
tooltipText = L["Use shorter format for records numbers."], |
setting = "shortFormat", |
func = function(self) callbacks:Fire("OnNewTopRecord") Critline:UpdateTooltips() end, |
gap = 16, |
}, |
{ |
text = L["Records in spell tooltips"], |
tooltipText = L["Include (unfiltered) records in spell tooltips."], |
setting = "spellTooltips", |
}, |
{ |
text = L["Detailed tooltip"], |
tooltipText = L["Use detailed format in the summary tooltip."], |
setting = "detailedTooltip", |
func = function(self) Critline:UpdateTooltips() end, |
}, |
{ |
text = L["Ignore vulnerability"], |
tooltipText = L["Enable to ignore additional damage due to vulnerability."], |
setting = "ignoreVulnerability", |
}, |
} |
options.checkButtons = checkButtons |
-- summary sort dropdown |
local menu = { |
onClick = function(self) |
self.owner:SetSelectedValue(self.value) |
Critline.db.profile.tooltipSort = self.value |
Critline:UpdateTooltips() |
end, |
{ |
text = L["Alphabetically"], |
value = "alpha", |
sorting:SetFrameWidth(120) |
sorting:SetPoint("TOPLEFT", checkButtons[#checkButtons], "BOTTOMLEFT", -15, -24) |
sorting.label:SetText(L["Tooltip sorting:"]) |
sorting.onClick = function(self) |
self.owner:SetSelectedValue(self.value) |
Critline.db.profile.tooltipSort = self.value |
Critline:UpdateTooltips() |
end |
options.tooltipSort = sorting |
end |
Critline.SlashCmdHandlers = { |
debug = function() Critline:ToggleDebug() end, |
} |
SlashCmdList.CRITLINE = function(msg) |
msg = msg:trim():lower() |
if msg == "debug" then |
Critline:ToggleDebug() |
elseif msg == "reset" then |
local display = Critline.display |
if display then |
display:ClearAllPoints() |
display:SetPoint("CENTER") |
end |
local slashCmdHandler = Critline.SlashCmdHandlers[msg] |
if slashCmdHandler then |
slashCmdHandler() |
else |
Critline:OpenConfig() |
end |
profile = { |
PvE = true, |
PvP = true, |
ignoreVulnerability = true, |
chatOutput = false, |
sound = false, |
screenshot = false, |
shortFormat = false, |
spellTooltips = false, |
detailedTooltip = false, |
ignoreVulnerability = true, |
tooltipSort = "normal", |
}, |
} |
self:UnregisterEvent("ADDON_LOADED") |
callbacks:Fire("AddonLoaded") |
-- import old records and mob filter |
if CritlineSettings then |
for i, v in ipairs({"dmg","heal","pet"}) do |
percharDB.profile.spells[v] = CritlineDB[v] |
CritlineDB[v] = nil |
end |
if self.filters and CritlineMobFilter then |
self.filters.db.global.mobs = CritlineMobFilter |
end |
end |
self:LoadSettings() |
self:LoadPerCharSettings() |
end |
function Critline:COMBAT_LOG_EVENT_UNFILTERED(timestamp, eventType, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, ...) |
-- import native spells to new database format (4.0) |
function Critline:PLAYER_TALENT_UPDATE() |
if GetMajorTalentTreeBonuses(1) then |
self:UnregisterEvent("PLAYER_TALENT_UPDATE") |
self.PLAYER_TALENT_UPDATE = nil |
else |
return |
end |
local tooltip = CreateFrame("GameTooltip", "CritlineImportScanTooltip", nil, "GameTooltipTemplate") |
local function getID(query) |
local link = GetSpellLink(query) |
if link then |
return tonumber(link:match("spell:(%d+)")) |
end |
for tab = 1, 3 do |
local id = GetMajorTalentTreeBonuses(tab) |
if GetSpellInfo(id) == query then |
return id |
end |
for i = 1, GetNumTalents(tab) do |
local name, _, _, _, _, _, _, _, _, isExceptional = GetTalentInfo(tab, i) |
if name == query and isExceptional then |
tooltip:SetOwner(UIParent) |
tooltip:SetTalent(tab, i) |
return select(3, tooltip:GetSpell()) |
end |
end |
end |
end |
for k, profile in pairs(self.percharDB.profiles) do |
if profile.spells then |
for k, tree in pairs(profile.spells) do |
local spells = {} |
for i, spell in pairs(tree) do |
if not spell.spellName then |
return |
end |
local id = getID(spell.spellName) |
id = (tree == heal and indirectHeals[id]) or indirectSpells[id] or id |
if id and (spell.normal or spell.crit) then |
spells[id] = spells[id] or {} |
spells[id][spell.isPeriodic and 2 or 1] = spell |
spell.spellName = nil |
spell.isPeriodic = nil |
end |
end |
profile.spells[k] = spells |
end |
end |
end |
tooltip:Hide() |
-- invert filter flag on all spells if inverted filter was enabled |
if self.filters then |
if self.filters.db.profile.invertFilter then |
for k, profile in pairs(self.percharDB.profiles) do |
if profile.spells then |
for k, tree in pairs(profile.spells) do |
for i, spell in pairs(tree) do |
for i, spell in pairs(spell) do |
spell.filtered = not spell.filtered |
end |
end |
end |
end |
end |
end |
for k, profile in pairs(self.filters.db.profiles) do |
profile.invertFilter = nil |
end |
end |
self:LoadPerCharSettings() |
end |
local TOC |
local dummyTable = {} |
do |
-- Because GetBuildInfo() still returns 40000 on the PTR |
local major, minor, rev = strsplit(".", (GetBuildInfo())) |
TOC = major * 10000 + minor * 100 |
end |
function Critline:COMBAT_LOG_EVENT_UNFILTERED(timestamp, eventType, hideCaster, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, ...) |
-- we seem to get events with standard arguments equal to nil, so they need to be ignored |
if not (timestamp and eventType) then |
self:Debug("nil errors on start") |
return |
end |
if TOC < 40100 and hideCaster ~= dummyTable then |
self:COMBAT_LOG_EVENT_UNFILTERED(timestamp, eventType, dummyTable, hideCaster, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, ...) |
return |
end |
-- if we don't have a destName (who we hit or healed) and we don't have a sourceName (us or our pets) then we leave |
if not (destName or sourceName) then |
return |
end |
-- check for filtered auras |
if self.filters then |
if self.filters:IsAuraEvent(eventType, ..., sourceFlags, destFlags, destGUID) then |
return |
end |
end |
local isPet |
-- if sourceGUID is not us or our pet, we leave |
local isMyPet = CombatLog_Object_IsA(sourceFlags, COMBATLOG_FILTER_MY_PET) |
local isGuardian = band(sourceFlags, COMBATLOG_OBJECT_TYPE_GUARDIAN) ~= 0 |
-- only register if it's a real pet, or a guardian tree pet that's included in the filter |
if isMyPet and ((not isGuardian and HasPetUI()) or classPets[tonumber(sourceGUID:sub(6, 12), 16)]) then |
if isMyPet and ((not isGuardian and HasPetUI()) or classPets[tonumber(sourceGUID:sub(7, 10), 16)]) then |
isPet = true |
-- self:Debug(format("This is my pet (%s)", sourceName)) |
else |
return |
end |
local isHeal |
local isPeriodic |
if eventType:find("_HEAL$") then |
isHeal = true |
if isPet then |
return |
end |
local periodic = 1 |
local isHeal = eventType == "SPELL_HEAL" or eventType == "SPELL_PERIODIC_HEAL" or eventType == "SPELL_AURA_APPLIED" or eventType == "SPELL_AURA_REFRESH" |
-- we don't care about healing done by the pet |
if isHeal and isPet then |
self:Debug("Pet healing. Return.") |
return |
end |
if eventType:find("_PERIODIC_") then |
if eventType == "SPELL_PERIODIC_DAMAGE" or eventType == "SPELL_PERIODIC_HEAL" then |
isPeriodic = true |
periodic = 2 |
end |
local spellName, amount, resisted, critical, school = combatEvents[eventType](...) |
-- get the relevants arguments |
local spellID, spellName, amount, resisted, critical, school = combatEvents[eventType](...) |
-- below are some checks to see if we want to register the hit at all |
local similarSpell = similarSpells[spellID] |
if similarSpell then |
spellID = similarSpell |
spellName = self:GetSpellName(similarSpell) |
end |
-- return if the event has no amount (non-absorbing aura applied) |
if not amount then |
return |
end |
if amount <= 0 then |
self:Debug(format("Amount <= 0. (%s) Return.", self:GetFullSpellName(tree, spellName, isPeriodic))) |
self:Debug(format("Amount <= 0. (%s) Return.", self:GetFullSpellName(spellID, periodic))) |
return |
end |
tree = "heal" |
end |
local passed, isFiltered, targetLevel |
local targetLevel = self:GetLevelFromGUID(destGUID) |
local passed, isFiltered |
if self.filters then |
passed, isFiltered, targetLevel = self.filters:SpellPassesFilters(tree, spellName, ..., isPeriodic, destGUID, destName, school) |
passed, isFiltered = self.filters:SpellPassesFilters(tree, spellName, spellID, isPeriodic, destGUID, destName, school, targetLevel) |
if not passed then |
return |
end |
return |
end |
-- we only want damage spells from the pet |
if isHeal and isPet then |
self:Debug("Pet healing. Return.") |
return |
end |
-- exit if not recording tree dmg |
if not self.percharDB.profile[tree] then |
self:Debug(format("Not recording this tree (%s). Return.", tree)) |
end |
local hitType = critical and "crit" or "normal" |
local data = self:GetSpellInfo(tree, spellID, periodic) |
local arrayData |
local data = self:GetSpellInfo(tree, spellName, isPeriodic) |
-- create spell database entries as required |
if not data then |
self:Debug(format("Creating data for %s (%s)", self:GetFullSpellName(tree, spellName, isPeriodic), tree)) |
data = { |
spellName = spellName, |
isPeriodic = isPeriodic, |
} |
self:AddSpell(tree, data) |
self:Debug(format("Creating data for %s (%s)", self:GetFullSpellName(spellID, periodic), tree)) |
data, arrayData = self:AddSpell(tree, spellID, periodic, spellName, isFiltered) |
self:UpdateSpells(tree) |
end |
if not data[hitType] then |
data[hitType] = {amount = 0} |
(arrayData or self:GetSpellArrayEntry(tree, spellID, periodic))[hitType] = data[hitType] |
end |
data = data[hitType] |
-- if new amount is larger than the stored amount we'll want to store it |
if amount > data.amount then |
local oldAmount = data.amount |
if not isFiltered then |
self:NewRecord(self:GetFullSpellName(spellID, periodic, true), amount, critical) |
-- update the highest record if needed |
local topRecords = topRecords[tree] |
if amount > topRecords[hitType] then |
topRecords[hitType] = amount |
callbacks:Fire("OnNewTopRecord", tree) |
end |
end |
callbacks:Fire("NewRecord", tree, spellID, periodic, amount, critical, data, isFiltered) |
data.amount = amount |
data.target = destName |
data.targetLevel = targetLevel |
data.isPvPTarget = isPlayer |
if not isFiltered then |
self:NewRecord(self:GetFullSpellName(tree, spellName, isPeriodic), amount, critical, oldAmount) |
end |
self:UpdateRecords(tree, isFiltered) |
end |
end |
function Critline:GetLevelFromGUID(destGUID) |
tooltip:SetOwner(UIParent, "ANCHOR_NONE") |
tooltip:SetHyperlink("unit:"..destGUID) |
local level = -1 |
for i = 1, tooltip:NumLines() do |
local text = _G["CritlineTooltipTextLeft"..i]:GetText() |
if text then |
if text:match(LEVEL) then -- our destGUID has the word Level in it. |
level = text:match("(%d+)") -- find the level |
if level then -- if we found the level, break from the for loop |
level = tonumber(level) |
else |
-- well, the word Level is in this tooltip, but we could not find the level |
-- either the destGUID is at least 10 levels higher than us, or we just couldn't find it. |
level = -1 |
end |
end |
end |
end |
return level |
end |
function Critline:Message(msg) |
if msg then |
DEFAULT_CHAT_FRAME:AddMessage("|cffffff00Critline:|r "..msg) |
function Critline:LoadPerCharSettings() |
for tree in pairs(treeNames) do |
wipe(spellArrays[tree]) |
for spellID, spell in pairs(self.percharDB.profile.spells[tree]) do |
for i, v in pairs(spell) do |
if type(v) ~= "table" or v.spellName then return end -- avoid error in pre 4.0 DB |
spellArrays[tree][#spellArrays[tree] + 1] = { |
spellID = spellID, |
spellName = self:GetSpellName(spellID), |
filtered = v.filtered, |
periodic = i, |
normal = v.normal, |
crit = v.crit, |
} |
end |
end |
end |
callbacks:Fire("PerCharSettingsLoaded") |
self:UpdateTopRecords() |
self:UpdateTooltips() |
for _, btn in ipairs(self.options.checkButtons.percharDB) do |
end |
function Critline:NewRecord(spell, amount, crit, oldAmount) |
callbacks:Fire("NewRecord", spell, amount, crit, oldAmount) |
function Critline:NewRecord(spell, amount, crit) |
if self.db.profile.chatOutput then |
self:Message(format(L["New %s%s record - %d"], crit and L["critical "] or "", spell, amount)) |
end |
end |
-- return spell table from database, given tree, spell name and isPeriodic value |
function Critline:GetSpellInfo(tree, spellName, periodic) |
for i, v in ipairs(self.percharDB.profile.spells[tree]) do |
if v.spellName == spellName and v.isPeriodic == periodic then |
return v, i |
local FIRST_NUMBER_CAP = FIRST_NUMBER_CAP:lower() |
function Critline:ShortenNumber(amount) |
if tonumber(amount) and self.db.profile.shortFormat then |
if amount >= 1e7 then |
amount = (floor(amount / 1e5) / 10)..SECOND_NUMBER_CAP |
elseif amount >= 1e6 then |
amount = (floor(amount / 1e4) / 100)..SECOND_NUMBER_CAP |
elseif amount >= 1e4 then |
amount = (floor(amount / 100) / 10)..FIRST_NUMBER_CAP |
end |
end |
return amount |
end |
function Critline:GetFullSpellName(tree, spellName, isPeriodic) |
local suffix = "" |
if isPeriodic then |
if tree == "heal" then |
suffix = L[" (HoT)"] |
else |
suffix = L[" (DoT)"] |
function Critline:GetSpellArrayEntry(tree, spellID, periodic) |
for i, v in ipairs(spellArrays[tree]) do |
if v.spellID == spellID and v.periodic == periodic then |
return v |
end |
end |
return format("%s%s", spellName, suffix) |
end |
-- local previousTree |
-- local previousSort |
function Critline:GetSpellArray(tree, useProfileSort) |
local array = spellArrays[tree] |
local sortMethod = useProfileSort and self.db.profile.tooltipSort or "alpha" |
-- no need to sort if it's already sorted the way we want it |
-- if sortMethod ~= previousSort or tree ~= previousTree then |
sort(array, recordSorters[sortMethod]) |
-- previousTree = tree |
-- previousSort = sortMethod |
-- end |
return array |
end |
-- return spell table from database, given tree, spell name and isPeriodic value |
function Critline:GetSpellInfo(tree, spellID, periodic) |
local spell = self.percharDB.profile.spells[tree][spellID] |
return spell and spell[periodic] |
end |
function Critline:GetSpellName(spellID) |
local spellName = spellNameCache[spellID] or GetSpellInfo(spellID) |
spellNameCache[spellID] = spellName |
return spellName |
end |
function Critline:GetSpellTexture(spellID) |
local spellTexture = spellTextureCache[spellID] or GetSpellTexture(spellID) |
spellTextureCache[spellID] = spellTexture |
return spellTexture |
end |
function Critline:GetFullSpellName(spellID, periodic, verbose) |
local spellName = self:GetSpellName(spellID) |
if periodic == 2 then |
spellName = format("%s (%s)", spellName, verbose and L["tick"] or "*") |
end |
return spellName |
end |
function Critline:GetFullTargetName(spell) |
local suffix = "" |
if spell.isPvPTarget then |
-- retrieves the top, non filtered record amounts and spell names for a given tree |
function Critline:GetHighest(tree) |
function Critline:UpdateTopRecords(tree) |
if not tree then |
for tree in pairs(topRecords) do |
self:UpdateTopRecords(tree) |
end |
return |
end |
local normalRecord, critRecord = 0, 0 |
local normalSpell, critSpell |
for _, data in ipairs(self.percharDB.profile.spells[tree]) do |
if not (self.filters and self.filters:IsFilteredSpell(tree, data.spellName, data.isPeriodic)) then |
local normal = data.normal |
if normal and normal.amount > normalRecord then |
normalRecord = normal.amount |
normalSpell = data.spellName |
for spellID, spell in pairs(self.percharDB.profile.spells[tree]) do |
for i, v in pairs(spell) do |
if type(v) ~= "table" then return end -- avoid error in pre 4.0 DB |
if not (self.filters and v.filtered) then |
local normal = v.normal |
if normal then |
normalRecord = max(normal.amount, normalRecord) |
end |
local crit = v.crit |
if crit then |
critRecord = max(crit.amount, critRecord) |
end |
end |
local crit = data.crit |
if crit and crit.amount > critRecord then |
critRecord = crit.amount |
critSpell = data.spellName |
end |
end |
end |
return normalRecord, critRecord, normalSpell, critSpell |
local topRecords = topRecords[tree] |
topRecords.normal = normalRecord |
topRecords.crit = critRecord |
callbacks:Fire("OnNewTopRecord", tree) |
end |
function Critline:AddSpell(tree, spell) |
-- retrieves the top, non filtered record amounts and spell names for a given tree |
function Critline:GetHighest(tree) |
local topRecords = topRecords[tree] |
return topRecords.normal, topRecords.crit |
end |
function Critline:AddSpell(tree, spellID, periodic, spellName, filtered) |
local spells = self.percharDB.profile.spells[tree] |
tinsert(spells, spell) |
sort(spells, recordSorters.alpha) |
local spell = spells[spellID] or {} |
spells[spellID] = spell |
spell[periodic] = {filtered = filtered} |
local spellArray = spellArrays[tree] |
local arrayData = { |
spellID = spellID, |
spellName = spellName, |
filtered = filtered, |
periodic = periodic, |
} |
spellArray[#spellArray + 1] = arrayData |
return spell[periodic], arrayData |
end |
function Critline:DeleteSpell(tree, index) |
tremove(self.percharDB.profile.spells[tree], index) |
function Critline:DeleteSpell(tree, spellID, periodic) |
do |
local tree = self.percharDB.profile.spells[tree] |
local spell = tree[spellID] |
spell[periodic] = nil |
-- remove this entire spell entry if neither direct nor tick entries remain |
if not spell[3 - periodic] then |
tree[spellID] = nil |
end |
end |
for i, v in ipairs(spellArrays[tree]) do |
if v.spellID == spellID and v.periodic == periodic then |
tremove(spellArrays[tree], i) |
self:Message(format(L["Reset %s (%s) records."], self:GetFullSpellName(v.spellID, v.periodic), treeNames[tree])) |
break |
end |
end |
self:UpdateTopRecords(tree) |
end |
-- this "fires" when spells are added to/removed from the database |
function Critline:UpdateSpells(tree) |
if tree then |
self:UpdateTooltip(tree) |
doTooltipUpdate[tree] = true |
callbacks:Fire("SpellsChanged", tree) |
else |
for k in pairs(tooltips) do |
-- this "fires" when a new record has been registered |
function Critline:UpdateRecords(tree, isFiltered) |
if tree then |
self:UpdateTooltip(tree) |
doTooltipUpdate[tree] = true |
callbacks:Fire("RecordsChanged", tree, isFiltered) |
else |
for k in pairs(tooltips) do |
end |
function Critline:UpdateTooltips() |
for k in pairs(tooltips) do |
doTooltipUpdate[k] = true |
end |
end |
local LETHAL_LEVEL = "??" |
local leftFormat = "|cffc0c0c0%s:|r %s" |
local leftFormatIndent = leftFormat |
local rightFormat = format("%s%%s|r (%%s)", HIGHLIGHT_FONT_COLOR_CODE) |
local recordFormat = format("%s%%s|r", GREEN_FONT_COLOR_CODE) |
local r, g, b = HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b |
function Critline:ShowTooltip(tree) |
GameTooltip:AddLine("Critline "..treeNames[tree], HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b) |
for i, v in ipairs(tooltips[tree]) do |
local left, right = v:match("(.+)\t(.+)") |
if left and right then |
GameTooltip:AddDoubleLine(left, right) |
if doTooltipUpdate[tree] then |
self:UpdateTooltip(tree) |
end |
local r, g, b = r, g, b |
local rR, gR, bR |
GameTooltip:AddLine("Critline "..treeNames[tree], r, g, b) |
if not self.db.profile.detailedTooltip then |
-- advanced tooltip uses different text color |
rR, gR, bR = r, g, b |
r, g, b = nil |
end |
local tooltip = tooltips[tree] |
for i = 1, #tooltips[tree] do |
local v = tooltip[i] |
-- v is either an array containing the left and right tooltip strings, or a single string |
if type(v) == "table" then |
local left, right = unpack(v) |
GameTooltip:AddDoubleLine(left, right, r, g, b, rR, gR, bR) |
else |
GameTooltip:AddLine(v) |
end |
end |
function Critline:UpdateTooltips() |
for k in pairs(tooltips) do |
self:UpdateTooltip(k) |
function Critline:UpdateTooltip(tree) |
local tooltip = tooltips[tree] |
wipe(tooltip) |
local normalRecord, critRecord = self:GetHighest(tree) |
local n = 1 |
for _, v in ipairs(self:GetSpellArray(tree, true)) do |
if not (self.filters and self:GetSpellInfo(tree, v.spellID, v.periodic).filtered) then |
local spellName = self:GetFullSpellName(v.spellID, v.periodic) |
-- if this is a DoT/HoT, and a direct entry exists, add the proper suffix |
-- if v.periodic == 2 and not (self.filters and self.filters:IsFilteredSpell(tree, v.spellID, 1)) then |
-- spellName = self:GetFullSpellName(v.spellID, 2) |
-- end |
if self.db.profile.detailedTooltip then |
tooltip[n] = spellName |
n = n + 1 |
tooltip[n] = {self:GetTooltipLine(v, "normal", tree)} |
n = n + 1 |
tooltip[n] = {self:GetTooltipLine(v, "crit", tree)} |
else |
local normalAmount, critAmount = 0, 0 |
-- color the top score amount green |
local normal = v.normal |
if normal then |
normalAmount = self:ShortenNumber(normal.amount) |
normalAmount = normal.amount == normalRecord and GREEN_FONT_COLOR_CODE..normalAmount..FONT_COLOR_CODE_CLOSE or normalAmount |
end |
local crit = v.crit |
if crit then |
critAmount = self:ShortenNumber(crit.amount) |
critAmount = crit.amount == critRecord and GREEN_FONT_COLOR_CODE..critAmount..FONT_COLOR_CODE_CLOSE or critAmount |
end |
tooltip[n] = {spellName, crit and format("%s / %s", normalAmount, critAmount) or normalAmount} |
end |
n = n + 1 |
end |
end |
if #tooltip == 0 then |
tooltip[1] = L["No records"] |
end |
doTooltipUpdate[tree] = nil |
end |
local sortedSpells = {} |
local hitTypes = { |
normal = L["Normal"], |
crit = L["Crit"], |
} |
function Critline:UpdateTooltip(tree) |
local line = " |cffc0c0c0%s:|r %s\t%s (%s)" |
function Critline:GetTooltipLine(data, hitType, tree) |
local leftFormat = tree and " "..leftFormat or leftFormat |
data = data and data[hitType] |
if data then |
local amount = self:ShortenNumber(data.amount) |
if tree and data.amount == topRecords[tree][hitType] then |
amount = format(recordFormat, amount) |
end |
local level = data.targetLevel |
level = level > 0 and level or LETHAL_LEVEL |
return format(leftFormat, hitTypes[hitType], amount), format(rightFormat, self:GetFullTargetName(data), level), r, g, b |
end |
end |
function Critline:AddTooltipLine(data, tree) |
GameTooltip:AddDoubleLine(self:GetTooltipLine(data, "normal", tree)) |
GameTooltip:AddDoubleLine(self:GetTooltipLine(data, "crit", tree)) |
end |
local funcset = {} |
for k in pairs(treeNames)do |
funcset[k] = function(spellID, num) |
local data = Critline:GetSpellInfo(k, spellID, num) |
return not (Critline.filters and data and data.filtered) and data |
end |
end |
local function addLine(header, nonTick, tick) |
if header then |
GameTooltip:AddLine(L[header]) |
end |
Critline:AddTooltipLine(nonTick) |
if tick and nonTick then |
GameTooltip:AddLine(" ") |
GameTooltip:AddLine(L["Tick"]) |
end |
Critline:AddTooltipLine(tick) |
end |
GameTooltip:HookScript("OnTooltipSetSpell", function(self) |
if self.Critline or not Critline.db.profile.spellTooltips then |
return |
end |
wipe(sortedSpells) |
local spellName, rank, spellID = self:GetSpell() |
local n = 1 |
local indirectSpell = indirectSpells[spellID] |
local indirectHeal = indirectHeals[spellID] |
for i, v in ipairs(self.percharDB.profile.spells[tree]) do |
if (v.normal or v.crit) and not (self.filters and self.filters:IsFilteredSpell(tree, v.spellName, v.isPeriodic)) then |
sortedSpells[n] = { |
spellName = v.spellName, |
isPeriodic = v.isPeriodic, |
normal = v.normal, |
crit = v.crit, |
} |
n = n + 1 |
end |
end |
local dmg1 = funcset.dmg(indirectSpell or spellID, 1) |
local dmg2 = funcset.dmg(indirectSpell or spellID, 2) |
local dmg = dmg1 or dmg2 |
sort(sortedSpells, recordSorters[self.db.profile.tooltipSort]) |
local heal1 = funcset.heal(indirectHeal or indirectSpell or spellID, 1) |
local heal2 = funcset.heal(indirectHeal or indirectSpell or spellID, 2) |
local heal = heal1 or heal2 |
local tooltip = tooltips[tree] |
wipe(tooltip) |
local pet1 = funcset.pet(indirectSpell or spellID, 1) |
local pet2 = funcset.pet(indirectSpell or spellID, 2) |
local pet = pet1 or pet2 |
local normalRecord, critRecord = self:GetHighest(tree) |
n = 1 |
if dmg or heal or pet then |
self:AddLine(" ") |
end |
for _, v in ipairs(sortedSpells) do |
-- if this is a DoT/HoT, and a direct entry exists, add the proper suffix |
if v.isPeriodic then |
for _, v2 in ipairs(sortedSpells) do |
if v2.spellName == v.spellName and not v2.isPeriodic then |
v.spellName = self:GetFullSpellName(tree, v.spellName, true) |
break |
end |
end |
if dmg then |
addLine((heal or pet) and "Damage", dmg1, dmg2) |
end |
if heal then |
if dmg then |
GameTooltip:AddLine(" ") |
end |
local normalAmount, critAmount = HIGHLIGHT_FONT_COLOR_CODE..(0)..FONT_COLOR_CODE_CLOSE, HIGHLIGHT_FONT_COLOR_CODE..(0)..FONT_COLOR_CODE_CLOSE |
-- color the top score amount green |
local normal = v.normal |
if normal then |
normalAmount = (normal.amount == normalRecord and GREEN_FONT_COLOR_CODE or HIGHLIGHT_FONT_COLOR_CODE)..normal.amount..FONT_COLOR_CODE_CLOSE |
end |
local crit = v.crit |
if crit then |
critAmount = (crit.amount == critRecord and GREEN_FONT_COLOR_CODE or HIGHLIGHT_FONT_COLOR_CODE)..crit.amount..FONT_COLOR_CODE_CLOSE |
end |
if self.db.profile.detailedTooltip then |
tooltip[n] = v.spellName |
if normal then |
n = n + 1 |
local target = HIGHLIGHT_FONT_COLOR_CODE..self:GetFullTargetName(normal)..FONT_COLOR_CODE_CLOSE |
local level = (normal.targetLevel > 0) and normal.targetLevel or "??" |
tooltip[n] = format(line, L["Normal"], normalAmount, target, level) |
end |
if crit then |
n = n + 1 |
local target = HIGHLIGHT_FONT_COLOR_CODE..self:GetFullTargetName(crit)..FONT_COLOR_CODE_CLOSE |
local level = (crit.targetLevel > 0) and crit.targetLevel or "??" |
tooltip[n] = format(line, L["Crit"], critAmount, target, level) |
end |
else |
tooltip[n] = format("%s\t%s%s", v.spellName, normalAmount, (crit and "/"..critAmount or "")) |
end |
n = n + 1 |
addLine((dmg or pet) and "Healing", heal1, heal2) |
end |
if #tooltip == 0 then |
tooltip[1] = L["No records"] |
if pet then |
if dmg or heal then |
GameTooltip:AddLine(" ") |
end |
addLine((dmg or heal) and "Pet", pet1, pet2) |
end |
end |
local L = LibStub("AceLocale-3.0"):GetLocale(addonName) |
local templates = addon.templates |
local IsSpellKnown = IsSpellKnown |
local UnitAura = UnitAura |
local UnitName = UnitName |
local UnitGUID = UnitGUID |
local CombatLog_Object_IsA = CombatLog_Object_IsA |
local band = bit.band |
local COMBATLOG_FILTER_ME = COMBATLOG_FILTER_ME |
local COMBATLOG_OBJECT_REACTION_FRIENDLY = COMBATLOG_OBJECT_REACTION_FRIENDLY |
-- amount of buttons in the spell, mob and aura filter scroll lists |
local NUMSPELLBUTTONS = 8 |
local SPELLBUTTONHEIGHT = 22 |
local NUMFILTERBUTTONS = 10 |
local FILTERBUTTONHEIGHT = 16 |
-- mobs whose received hits won't be tracked due to various vulnerabilities |
local specialMobs = { |
[12460] = true, -- Death Talon Wyrmguard |
[16803] = true, -- Death Knight Understudy |
[22841] = true, -- Shade of Akama |
[33329] = true, -- Heart of the Deconstructor |
[33670] = true, -- Aerial Command Unit |
[34496] = true, -- Eydis Darkbane |
[34497] = true, -- Fjola Lightbane |
[34797] = true, -- Icehowl |
[38567] = true, -- Phantom Hallucination |
[39698] = true, -- Karsh Steelbender |
[40484] = true, -- Erudax |
[40793] = true, -- Ragnaros (Mount Hyjal) |
[42347] = true, -- Exposed Head of Magmaw (Point of Vulnerability [79011]) * |
[42803] = true, -- Drakeadon Mongrel (Brood Power: Red/Green/Black/Blue/Bronze [80368+80369+80370+80371+80372]) |
[46083] = true, -- Drakeadon Mongrel (Brood Power: Red/Green/Black/Blue/Bronze [80368+80369+80370+80371+80372]) |
[46273] = true, -- Debilitated Apexar |
[48270] = true, -- Exposed Head of Magmaw (Point of Vulnerability [79011]) |
} |
-- auras that when gained will suppress record tracking |
[76159] = true, -- Pyrogenics (Sun-Touched Spriteling) |
[76355] = true, -- Blessing of the Sun (Rajh) |
[76693] = true, -- Empowering Twilight (Crimsonborne Warlord) |
[79624] = true, -- Power Generator (Arcanotron) ? |
[81096] = true, -- Red Mist (Red Mist) |
[86622] = true, -- Engulfing Magic (Theralion) ? |
[86872] = true, -- Frothing Rage (Thundermar Ale) |
[89879] = true, -- Blessing of the Sun (Rajh - heroic) |
[90933] = true, -- Ragezone (Defias Blood Wizard) |
[91871] = true, -- Lightning Charge (Siamat) |
[93777] = true, -- Invocation of Flame (Skullcrusher the Mountain) |
[95639] = true, -- Engulfing Magic (Theralion) ? |
[95640] = true, -- Engulfing Magic (Theralion) ? |
[95641] = true, -- Engulfing Magic (Theralion) ? |
} |
-- these are auras that increases the target's damage or healing received |
local targetAuras = { |
[64436] = true, -- Magnetic Core (Aerial Command Unit) ? |
[66758] = true, -- Staggered Daze (Icehowl) ? |
[75664] = true, -- Shadow Gale (Erudax) ? |
[75846] = true, -- Superheated Quicksilver Armor (Karsh Steelbender) ? |
[76015] = true, -- Superheated Quicksilver Armor (Karsh Steelbender) ? |
[76232] = true, -- Storm's Fury (Ragnaros - Mount Hyjal) ? |
[77717] = true, -- Vertigo (Atramedes) |
[80157] = true, -- Chemical Bomb (Toxitron) ? |
[87683] = true, -- Dragon's Vengeance (Halfus Wyrmbreaker) |
[87904] = true, -- Feedback (Al'Akir) |
[90933] = true, -- Ragezone (Defias Blood Wizard) ? |
[91086] = true, -- Shadow Gale (Erudax - heroic) |
[92390] = true, -- Vertigo (Atramedes) ? |
[92910] = true, -- Debilitating Slime (Maloriak) ? |
[93567] = true, -- Superheated Quicksilver Armor (Karsh Steelbender) ? |
[95723] = true, -- Storm's Fury (Ragnaros - Mount Hyjal) ? |
} |
-- these heals are treated as periodic, but has no aura associated with them, or is associated to an aura with a different name, need to add exceptions for them to filter properly |
local directHoTs = { |
[54172] = true, -- Divine Storm |
local activeAuras = {} |
local corruptSpells = {} |
local corruptTargets = {} |
local playerAuras = { |
session = {}, |
instance = {}, |
lastFight = {}, |
} |
local enemyAuras = { |
session = {}, |
instance = {}, |
lastFight = {}, |
} |
-- name of current instance |
local currentInstance = L["n/a"] |
-- amount of buttons in the spell, mob and aura filter scroll lists |
local NUMSPELLBUTTONS = 8 |
local SPELLBUTTONHEIGHT = 22 |
local NUMFILTERBUTTONS = 10 |
local FILTERBUTTONHEIGHT = 16 |
local filters = templates:CreateConfigFrame(FILTERS, addonName, true) |
filters:SetScript("OnEvent", function(self, event, ...) |
return self[event] and self[event](self, ...) |
end) |
addon.filters = filters |
local function filterButtonOnClick(self) |
local module = self.module |
local scrollFrame = module.scrollFrame |
local filterName = scrollFrame.filter |
local selection = scrollFrame.selected |
if selection then |
local filter = self.filters.db.global[filterName] |
local filter = filters.db.global[filterName] |
local selectedEntry = filter[selection] |
tremove(filter, selection) |
local prevHighlight = scrollFrame.buttons[selection - FauxScrollFrame_GetOffset(scrollFrame)] |
delete:Disable() |
delete:SetScript("OnClick", deleteButtonOnClick) |
delete.scrollFrame = scrollFrame |
delete.filters = parent |
frame.delete = delete |
end |
end |
-- tooltip for level scanning |
local tooltip = CreateFrame("GameTooltip", "CritlineTooltip", nil, "GameTooltipTemplate") |
local filters = templates:CreateConfigFrame(FILTERS, addonName, true) |
addon.filters = filters |
do |
local options = {} |
filters.options = options |
local checkButtons = { |
{ |
text = L["Invert spell filter"], |
tooltipText = L["Enable to include rather than exclude selected spells in the spell filter."], |
setting = "invertFilter", |
func = function(self) addon:UpdateRecords() end, |
text = L["Filter new spells"], |
tooltipText = L["Enable to filter out new spell entries by default."], |
setting = "filterNew", |
}, |
{ |
text = L["Ignore mob filter"], |
spellFilter:SetPoint("LEFT", 48, 0) |
spellFilter:SetPoint("RIGHT", -48, 0) |
filterTypes.spell = spellFilter |
do -- spell filter buttons |
local function spellButtonOnClick(self) |
local module = self.module |
if self:GetChecked() then |
PlaySound("igMainMenuOptionCheckBoxOn") |
module:AddSpell(module.spell.tree:GetSelectedValue(), self.spell, self.isPeriodic) |
else |
PlaySound("igMainMenuOptionCheckBoxOff") |
module:DeleteSpell(module.spell.tree:GetSelectedValue(), self.spell, self.isPeriodic) |
end |
local checked = self:GetChecked() == 1 |
PlaySound(checked and "igMainMenuOptionCheckBoxOn" or "igMainMenuOptionCheckBoxOff") |
filters:FilterSpell(not checked, filters.spell.tree:GetSelectedValue(), self.data) |
end |
local function spellButtonOnEnter(self) |
-- prevent records being added twice |
GameTooltip.Critline = true |
GameTooltip:SetOwner(self, "ANCHOR_LEFT") |
GameTooltip:SetSpellByID(self.data.spellID) |
GameTooltip:AddLine(" ") |
addon:AddTooltipLine(self.data) |
GameTooltip:Show() |
end |
local buttons = {} |
for i = 1, NUMSPELLBUTTONS do |
local btn = templates:CreateCheckButton(spellFilter) |
btn:SetPoint("TOP", buttons[i - 1], "BOTTOM", 0, 4) |
end |
btn:SetScript("OnClick", spellButtonOnClick) |
btn.module = filters |
btn:SetScript("OnEnter", spellButtonOnEnter) |
buttons[i] = btn |
end |
spellFilter.scrollFrame.buttons = buttons |
-- spell filter tree dropdown |
local menu = { |
onClick = function(self) |
self.owner:SetSelectedValue(self.value) |
FauxScrollFrame_SetOffset(spellScrollFrame, 0) |
spellScrollFrame.scrollBar:SetValue(0) |
spellScrollFrame:Update() |
end, |
{text = L["Damage"], value = "dmg"}, |
{text = L["Healing"], value = "heal"}, |
{text = L["Pet"], value = "pet"}, |
spellFilterTree:SetFrameWidth(120) |
spellFilterTree:SetPoint("BOTTOMRIGHT", spellFilter, "TOPRIGHT", 16, 0) |
spellFilterTree:SetSelectedValue("dmg") |
spellFilterTree.onClick = function(self) |
self.owner:SetSelectedValue(self.value) |
FauxScrollFrame_SetOffset(spellScrollFrame, 0) |
spellScrollFrame.scrollBar:SetValue(0) |
spellScrollFrame:Update() |
end |
spellFilter.tree = spellFilterTree |
spellScrollFrame.tree = spellFilter.tree |
add:SetPoint("TOPLEFT", auraFilter, "BOTTOMLEFT", 0, -8) |
add:SetText(L["Add by spell ID"]) |
add.popup = "CRITLINE_ADD_AURA_BY_ID" |
-- local addAura = templates:CreateButton(auraFilter) |
-- addAura:SetSize(48, 22) |
-- addAura:SetPoint("TOP", auraFilter, "BOTTOM") |
-- addAura:SetText("Add") |
-- addAura:SetScript("OnClick", function() if auraList:IsShown() then auraList:Hide() else auraList:Show() end end) |
local delete = auraFilter.delete |
delete:SetSize(128, 22) |
do -- filter tree dropdown |
local menu = { |
onClick = function(self) |
self.owner:SetSelectedValue(self.value) |
for k, v in pairs(filterTypes) do |
if k == self.value then |
v:Show() |
else |
v:Hide() |
end |
end |
end, |
{ |
text = L["Spell filter"], |
value = "spell", |
filterType:SetPoint("BOTTOMLEFT", spellFilter, "TOPLEFT", -16, 0) |
filterType:SetFrameWidth(120) |
filterType:SetSelectedValue("spell") |
filterType.onClick = function(self) |
self.owner:SetSelectedValue(self.value) |
for k, v in pairs(filterTypes) do |
if k == self.value then |
v:Show() |
else |
v:Hide() |
end |
end |
end |
filters.type = filterType |
end |
end |
do |
local auraList = CreateFrame("Frame", nil, UIParent) |
auraList:SetFrameStrata("DIALOG") |
auraList:EnableMouse(true) |
auraList:SetSize(320, 360) |
auraList:SetPoint("CENTER") |
auraList:SetBackdrop({ |
bgFile = [[Interface\ChatFrame\ChatFrameBackground]], |
edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]], |
edgeSize = 16, |
insets = {left = 4, right = 4, top = 4, bottom = 4}, |
}) |
auraList:SetBackdropColor(0, 0, 0) |
auraList:SetBackdropBorderColor(0.5, 0.5, 0.5) |
auraList:Hide() |
local closeButton = CreateFrame("Button", nil, auraList, "UIPanelCloseButton") |
closeButton:SetPoint("TOPRIGHT") |
Critline.SlashCmdHandlers["aura"] = function() auraList:Show() end |
local currentFilter = playerAuras.session |
local function auraSort(a, b) |
return currentFilter[a].spellName < currentFilter[b].spellName |
end |
local function sourceSort(a, b) |
a, b = currentFilter[a], currentFilter[b] |
if a.source == b.source then |
return a.spellName < b.spellName |
else |
return a.source < b.source |
end |
end |
local auraFilters = { |
BUFF = true, |
DEBUFF = true, |
targetAffiliation = playerAuras, |
sourceType = "npc", |
sort = auraSort, |
} |
local function onClick(self, text) |
self.owner:SetSelectedValue(self.value) |
self.owner:SetText(text) |
currentFilter = auraFilters.targetAffiliation[self.value] |
CritlineAuraListScrollFrame:Update() |
end |
local menuList = { |
{ |
text = L["Current fight"], |
value = "lastFight", |
}, |
{ |
text = L["Current instance (%s)"], |
value = "instance", |
}, |
{ |
text = L["Current session"], |
value = "session", |
}, |
} |
local auraListFilter = templates:CreateDropDownMenu("CritlineAuraListFilter", auraList) |
auraListFilter:SetPoint("TOP", 0, -16) |
auraListFilter:SetFrameWidth(220) |
auraListFilter:JustifyText("LEFT") |
auraListFilter:SetSelectedValue("session") |
auraListFilter:SetText(L["Current session"]) |
auraListFilter.initialize = function(self) |
for i, v in ipairs(menuList) do |
local info = UIDropDownMenu_CreateInfo() |
info.text = format(v.text, currentInstance) |
info.value = v.value |
info.func = onClick |
info.owner = self |
info.arg1 = info.text |
UIDropDownMenu_AddButton(info) |
end |
end |
local auraListAuraType = templates:CreateDropDownMenu("CritlineAuraListAuraType", auraList) |
auraListAuraType:SetPoint("TOPLEFT", auraListFilter, "BOTTOMLEFT") |
auraListAuraType:SetFrameWidth(96) |
auraListAuraType:JustifyText("LEFT") |
auraListAuraType:SetText(L["Aura type"]) |
do |
local function onClick(self) |
auraFilters[self.value] = self.checked |
CritlineAuraListScrollFrame:Update() |
end |
local menuList = { |
{ |
text = L["Buffs"], |
value = "BUFF", |
}, |
{ |
text = L["Debuffs"], |
value = "DEBUFF", |
}, |
} |
auraListAuraType.initialize = function(self) |
for i, v in ipairs(menuList) do |
local info = UIDropDownMenu_CreateInfo() |
info.text = v.text |
info.value = v.value |
info.func = onClick |
info.checked = auraFilters[v.value] |
info.isNotRadio = true |
info.keepShownOnClick = true |
UIDropDownMenu_AddButton(info) |
end |
end |
end |
local auraListFilters = templates:CreateDropDownMenu("CritlineAuraListFilters", auraList) |
auraListFilters:SetPoint("TOPRIGHT", auraListFilter, "BOTTOMRIGHT") |
auraListFilters:SetFrameWidth(96) |
auraListFilters:JustifyText("LEFT") |
auraListFilters:SetText(FILTERS) |
do |
local function onClick(self, key) |
auraFilters[key] = self.value |
self.owner:Refresh() |
self.owner:SetText(FILTERS) |
currentFilter = auraFilters.targetAffiliation[auraListFilter:GetSelectedValue()] |
CritlineAuraListScrollFrame:Update() |
end |
local function checked(self) |
return auraFilters[self.arg1] == self.value |
end |
local menuList = { |
{ |
text = L["Show auras cast on me"], |
value = playerAuras, |
arg1 = "targetAffiliation", |
}, |
{ |
text = L["Show auras cast on hostile NPCs"], |
value = enemyAuras, |
arg1 = "targetAffiliation", |
}, |
{ |
text = L["Show auras cast by NPCs"], |
value = "npc", |
arg1 = "sourceType", |
}, |
{ |
text = L["Show auras cast by players"], |
value = "pvp", |
arg1 = "sourceType", |
}, |
{ |
text = L["Sort by aura name"], |
value = auraSort, |
arg1 = "sort", |
}, |
{ |
text = L["Sort by source name"], |
value = sourceSort, |
arg1 = "sort", |
}, |
} |
auraListFilters.initialize = function(self) |
for i, v in ipairs(menuList) do |
local info = UIDropDownMenu_CreateInfo() |
info.text = v.text |
info.value = v.value |
info.func = onClick |
info.checked = checked |
info.owner = self |
info.keepShownOnClick = true |
info.arg1 = v.arg1 |
UIDropDownMenu_AddButton(info) |
end |
end |
end |
local search = templates:CreateEditBox(auraList) |
search:SetPoint("TOPLEFT", auraListAuraType, "BOTTOMLEFT", 18, -8) |
search:SetPoint("TOPRIGHT", auraListFilters, "BOTTOMRIGHT", -18, -8) |
search:SetWidth(192) |
search:SetScript("OnTextChanged", function() CritlineAuraListScrollFrame:Update() end) |
search:SetScript("OnEscapePressed", search.ClearFocus) |
local label = search:CreateFontString(nil, nil, "GameFontNormalSmall") |
label:SetPoint("BOTTOMLEFT", search, "TOPLEFT") |
label:SetText(L["Text filter"]) |
local NUM_BUTTONS = 6 |
local BUTTON_HEIGHT = 36 |
local auraListScrollFrame = CreateFrame("ScrollFrame", "CritlineAuraListScrollFrame", auraList, "FauxScrollFrameTemplate") |
auraListScrollFrame:SetHeight(NUM_BUTTONS * BUTTON_HEIGHT) |
auraListScrollFrame:SetPoint("TOP", search, "BOTTOM", 0, -8) |
auraListScrollFrame:SetPoint("LEFT", 32, 0) |
auraListScrollFrame:SetPoint("RIGHT", -32, 0) |
auraListScrollFrame:SetScript("OnVerticalScroll", function(self, offset) FauxScrollFrame_OnVerticalScroll(self, offset, BUTTON_HEIGHT, self.Update) end) |
local sortedAuras = {} |
function auraListScrollFrame:Update() |
if not auraList:IsShown() then |
self.doUpdate = true |
return |
end |
self.doUpdate = nil |
wipe(sortedAuras) |
local n = 0 |
local search = search:GetText():lower() |
for spellID, v in pairs(currentFilter) do |
if auraFilters[v.type] and v.sourceType == auraFilters.sourceType and (v.spellName:lower():find(search, nil, true) or v.sourceName:lower():find(search, nil, true)) then |
n = n + 1 |
sortedAuras[n] = spellID |
end |
end |
sort(sortedAuras, auraFilters.sort) |
FauxScrollFrame_Update(self, n, NUM_BUTTONS, BUTTON_HEIGHT) |
local offset = FauxScrollFrame_GetOffset(self) |
local buttons = self.buttons |
for line = 1, NUM_BUTTONS do |
local button = buttons[line] |
local lineplusoffset = line + offset |
if lineplusoffset <= n then |
local spellID = sortedAuras[lineplusoffset] |
button:SetFormattedText("%s (%d)", currentFilter[spellID].spellName, spellID) |
button.source:SetText(currentFilter[spellID].source) |
button.icon:SetTexture(addon:GetSpellTexture(spellID)) |
button.spellID = spellID |
-- local disabled = filters:IsFilteredAura(spellID) |
-- button.icon:SetDesaturated(disabled) |
-- button.text:SetFontObject(disabled and "GameFontDisable" or "GameFontNormal") |
button:Show() |
else |
button:Hide() |
end |
end |
end |
auraList:SetScript("OnShow", function(self) |
if auraListScrollFrame.doUpdate then |
auraListScrollFrame:Update() |
end |
end) |
local auraListButtons = {} |
auraListScrollFrame.buttons = auraListButtons |
-- local function onClick(self) |
-- local disabled = filters:IsFilteredAura(self.spellID) |
-- if disabled then |
-- if specialAuras[self.spellID] then |
-- addon:Message("Cannot delete integrated auras.") |
-- return |
-- else |
-- local t = filters.db.global.auras |
-- for i = 1, #t do |
-- if t[i] == self.spellID then |
-- tremove(t, i) |
-- addon:Message(format("Removed aura (%s) from filter.", GetSpellInfo(self.spellID))) |
-- break |
-- end |
-- end |
-- end |
-- else |
-- filters:AddAura(self.spellID) |
-- end |
-- disabled = not disabled |
-- self.icon:SetDesaturated(disabled) |
-- self.text:SetFontObject(disabled and "GameFontDisable" or "GameFontNormal") |
-- end |
local function onEnter(self) |
GameTooltip:SetOwner(self, "ANCHOR_LEFT") |
GameTooltip:SetSpellByID(self.spellID) |
GameTooltip:AddLine(" ") |
GameTooltip:AddLine(format(L["Spell ID: |cffffffff%d|r"], self.spellID)) |
GameTooltip:Show() |
end |
for i = 1, NUM_BUTTONS do |
local btn = CreateFrame("Button", nil, auraList) |
btn:SetHeight(BUTTON_HEIGHT) |
if i == 1 then |
btn:SetPoint("TOP", auraListScrollFrame) |
else |
btn:SetPoint("TOP", auraListButtons[i - 1], "BOTTOM") |
end |
btn:SetPoint("LEFT", auraListScrollFrame) |
btn:SetPoint("RIGHT", auraListScrollFrame) |
btn:SetPushedTextOffset(0, 0) |
-- btn:SetScript("OnClick", onClick) |
btn:SetScript("OnEnter", onEnter) |
btn:SetScript("OnLeave", GameTooltip_Hide) |
if i % 2 == 0 then |
local bg = btn:CreateTexture(nil, "BACKGROUND") |
bg:SetAllPoints() |
bg:SetTexture(1, 1, 1, 0.1) |
end |
local icon = btn:CreateTexture() |
icon:SetSize(32, 32) |
icon:SetPoint("LEFT") |
btn.icon = icon |
local text = btn:CreateFontString(nil, nil, "GameFontNormal") |
text:SetPoint("TOPLEFT", icon, "TOPRIGHT", 4, -4) |
text:SetPoint("RIGHT") |
text:SetJustifyH("LEFT") |
btn:SetFontString(text) |
btn.text = text |
local source = btn:CreateFontString(nil, nil, "GameFontHighlightSmall") |
source:SetPoint("BOTTOMLEFT", icon, "BOTTOMRIGHT", 4, 4) |
source:SetPoint("RIGHT") |
source:SetJustifyH("LEFT") |
btn.source = source |
auraListButtons[i] = btn |
end |
end |
StaticPopupDialogs["CRITLINE_ADD_MOB_BY_NAME"] = { |
text = L["Enter mob name:"], |
button1 = OKAY, |
local function updateSpellFilter(self) |
local selectedTree = self.tree:GetSelectedValue() |
local spells = addon.percharDB.profile.spells[selectedTree] |
local spells = addon:GetSpellArray(selectedTree) |
local size = #spells |
FauxScrollFrame_Update(self, size, self.numButtons, self.buttonHeight) |
local lineplusoffset = line + offset |
if lineplusoffset <= size then |
local data = spells[lineplusoffset] |
button.spell = data.spellName |
button.isPeriodic = data.isPeriodic |
button:SetText(addon:GetFullSpellName(selectedTree, data.spellName, data.isPeriodic)) |
button:SetChecked(data.filtered) |
button.data = data |
button:SetText(addon:GetFullSpellName(data.spellID, data.periodic)) |
button:SetChecked(not data.filtered) |
button:Show() |
else |
button:Hide() |
local defaults = { |
profile = { |
invertFilter = false, |
filterNew = false, |
onlyKnown = false, |
ignoreMobFilter = false, |
ignoreAuraFilter = false, |
onlyKnown = false, |
suppressMC = true, |
dontFilterMagic = false, |
levelFilter = -1, |
addon.RegisterCallback(self, "PerCharSettingsLoaded", "UpdateSpellFilter") |
addon.RegisterCallback(self, "SpellsChanged", "UpdateSpellFilter") |
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") |
self:RegisterEvent("PLAYER_REGEN_DISABLED") |
self:RegisterEvent("PLAYER_ENTERING_WORLD") |
self:RegisterEvent("PLAYER_LOGIN") |
self:RegisterEvent("UNIT_NAME_UPDATE") |
self:RegisterEvent("PLAYER_CONTROL_LOST") |
self:RegisterEvent("PLAYER_CONTROL_GAINED") |
-- mix in scroll frame update functions |
self.spell.scrollFrame.Update = updateSpellFilter |
self.mobs.scrollFrame.Update = updateFilter |
addon.RegisterCallback(filters, "AddonLoaded") |
function filters:COMBAT_LOG_EVENT_UNFILTERED(timestamp, eventType, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, spellID, spellName, spellSchool, auraType) |
if eventType == "SPELL_AURA_APPLIED" or eventType == "SPELL_AURA_REFRESH" then |
if targetAuras[spellID] then |
corruptTargets[destGUID] = corruptTargets[destGUID] or {} |
corruptTargets[destGUID][spellID] = true |
addon:Debug(format("Target (%s) gained filtered aura (%s). Ignore received damage.", destName, spellID)) |
end |
if CombatLog_Object_IsA(destFlags, COMBATLOG_FILTER_ME) then |
self:RegisterAura(playerAuras, sourceName, sourceGUID, spellID, spellName, auraType) |
if self:IsFilteredAura(spellID) then |
-- if we gain any aura in the filter we can just stop tracking records |
if not (self:IsEmpowered() or self.profile.ignoreAuraFilter) then |
addon:Debug("Filtered aura gained. Disabling combat log tracking.") |
end |
activeAuras[spellID] = true |
end |
else |
if CombatLog_Object_IsA(sourceFlags, COMBATLOG_FILTER_ME) then |
corruptSpells[spellID] = corruptSpells[spellID] or {} |
corruptSpells[spellID][destGUID] = self:IsEmpowered() or self:IsVulnerableTarget(destGUID) |
end |
-- only non friendly NPC units |
local unitType = band(destGUID:sub(1, 5), 0x007) |
if (unitType ~= 0 and unitType ~= 4) and (band(destFlags, COMBATLOG_OBJECT_REACTION_FRIENDLY) == 0) then |
self:RegisterAura(enemyAuras, sourceName, sourceGUID, spellID, spellName, auraType) |
end |
end |
elseif (eventType == "SPELL_AURA_REMOVED" or eventType == "SPELL_AURA_BROKEN" or eventType == "SPELL_AURA_BROKEN_SPELL" or eventType == "SPELL_AURA_STOLEN") then |
if targetAuras[spellID] then |
corruptTargets[destGUID] = corruptTargets[destGUID] or {} |
corruptTargets[destGUID][spellID] = nil |
addon:Debug(format("Filtered aura (%s) faded from %s.", spellName, destName)) |
end |
if CombatLog_Object_IsA(destFlags, COMBATLOG_FILTER_ME) then |
if self:IsFilteredAura(spellID) then |
addon:Debug(format("Filtered aura (%s) faded from player.", spellName)) |
-- if we lost a special aura we have to check if any other filtered auras remain |
activeAuras[spellID] = nil |
if not filters:IsEmpowered() then |
addon:Debug("No filtered aura detected. Resuming record tracking.") |
end |
-- elseif CombatLog_Object_IsA(sourceFlags, COMBATLOG_FILTER_ME) then |
-- corruptSpells[spellID] = corruptSpells[spellID] or {} |
-- corruptSpells[spellID][destGUID] = nil |
end |
-- else |
end |
end |
end |
-- reset current fight auras upon entering combat |
function filters:PLAYER_REGEN_DISABLED() |
wipe(playerAuras.lastFight) |
wipe(enemyAuras.lastFight) |
CritlineAuraListScrollFrame:Update() |
end |
function filters:PLAYER_ENTERING_WORLD() |
-- wipe instance buff data when entering a new instance |
local instanceName = GetInstanceInfo() |
if IsInInstance() and instanceName ~= currentInstance then |
wipe(playerAuras.instance) |
wipe(enemyAuras.instance) |
currentInstance = instanceName |
if CritlineAuraListFilter:GetSelectedValue() == "instance" then |
CritlineAuraListFilter:SetText(format(L["Current instance (%s)"], currentInstance)) |
end |
CritlineAuraListScrollFrame:Update() |
end |
end |
function filters:PLAYER_LOGIN() |
self:ScanAuras() |
end |
function filters:UNIT_NAME_UPDATE() |
self:ScanAuras() |
self:UnregisterEvent("UNIT_NAME_UPDATE") |
end |
function filters:PLAYER_CONTROL_LOST() |
self.inControl = false |
addon:Debug("Lost control. Disabling combat log tracking.") |
end |
function filters:PLAYER_CONTROL_GAINED() |
self.inControl = true |
addon:Debug("Regained control. Resuming combat log tracking.") |
end |
function filters:LoadSettings() |
self.profile = self.db.profile |
for _, v in ipairs(self.options.checkButtons) do |
for i, v in ipairs(self.options.checkButtons) do |
v:LoadSetting() |
end |
end |
do |
-- local spellButton_OnModifiedClick = SpellButton_OnModifiedClick |
local auraTypes = { |
BUFF = "HELPFUL", |
DEBUFF = "HARMFUL", |
} |
-- hooksecurefunc("SpellButton_OnModifiedClick", function(self, button, ...) |
-- local slot = SpellBook_GetSpellBookSlot(self) |
-- if ( slot > MAX_SPELLS ) then |
-- return |
-- end |
-- if IsShiftKeyDown() and filters.spell:IsVisible() and filters:GetParent() then |
-- local spellName, subSpellName = GetSpellBookItemName(slot, SpellBookFrame.bookType) |
-- filters:AddSpell(filters.spell.tree:GetSelectedValue(), spellName) |
-- return |
-- end |
-- return spellButton_OnModifiedClick(self, button) |
-- end) |
--[[ |
local function onClick(self, button) |
if button == "LeftButton" and IsShiftKeyDown() and filters.auras:IsVisible() and filters:GetParent() then |
filters:AddAura(select(11, UnitAura(self.unit, self:GetID(), self.filter))) |
function filters:ScanAuras() |
local auras = {} |
for auraType, filter in pairs(auraTypes) do |
for i = 1, 40 do |
local spellName, _, _, _, _, _, _, source, _, _, spellID = UnitAura("player", i, filter) |
if not spellID then break end |
auras[spellID] = true |
if specialAuras[spellID] then |
activeAuras[spellID] = true |
end |
self:RegisterAura(playerAuras, source and UnitName(source), source and UnitGUID(source), spellID, spellName, auraType) |
end |
end |
-- debuff buttons needs to have an onClick handler, and both buff and debuff buttons needs to monitor left clicks |
hooksecurefunc("AuraButton_Update", function(buttonName, index, filter) |
local name = UnitAura("player", index, filter) |
if name then |
local buff = _G[buttonName..index] |
if buff and not buff.Critline then |
buff:RegisterForClicks("AnyUp") |
if not buff:HasScript("OnClick") then |
buff:SetScript("OnClick", onClick) |
end |
buff.Critline = true |
end |
end |
end) |
hooksecurefunc("BuffButton_OnClick", onClick)]] |
CritlineAuraListScrollFrame:Update() |
if next(auras) then |
self:UnregisterEvent("UNIT_NAME_UPDATE") |
end |
for i, v in ipairs(self.db.global.auras) do |
activeAuras[v] = auras[v] |
end |
if next(activeAuras) then |
addon:Debug("Filtered aura detected. Disabling combat log tracking.") |
end |
self.inControl = HasFullControl() |
if not self.inControl then |
addon:Debug("Lost control. Disabling combat log tracking.") |
end |
end |
end |
function filters:AddSpell(tree, spell, isPeriodic) |
local data = addon:GetSpellInfo(tree, spell, isPeriodic) |
if not data then |
addon:AddSpell(tree, { |
spellName = spell, |
isPeriodic = isPeriodic, |
filtered = true, |
}) |
self:UpdateSpellFilter() |
return |
end |
data.filtered = true |
function filters:FilterSpell(filter, tree, data) |
data.filtered = filter |
addon:GetSpellInfo(tree, data.spellID, data.periodic).filtered = filter |
addon:UpdateTopRecords(tree) |
addon:UpdateRecords(tree) |
end |
function filters:DeleteSpell(tree, spell, isPeriodic) |
local data, index = addon:GetSpellInfo(tree, spell, isPeriodic) |
if not (data.normal or data.crit) then |
addon:DeleteSpell(tree, index) |
self:UpdateSpellFilter() |
return |
end |
data.filtered = nil |
addon:UpdateRecords(tree) |
end |
-- adds a mob to the mob filter |
function filters:AddMob(name) |
if self:IsFilteredMob(name) then |
addon:Message(L["%s is already in mob filter."]:format(name)) |
end |
-- adds an aura to the aura filter |
function filters:AddAura(spellID) |
local spellName = GetSpellInfo(spellID) |
if self:IsFilteredAura(spellID) then |
end |
function filters:IsAuraEvent(eventType, spellID, sourceFlags, destFlags, destGUID) |
if eventType == "SPELL_AURA_APPLIED" or eventType == "SPELL_AURA_REFRESH" then |
if CombatLog_Object_IsA(destFlags, COMBATLOG_FILTER_ME) and self:IsFilteredAura(spellID) then |
-- if we gain any aura in the filter we can just stop tracking records |
if not (self:IsEmpowered() or self.profile.ignoreAuraFilter) then |
addon:Debug("Filtered aura gained. Disabling combat log tracking.") |
end |
activeAuras[spellID] = true |
elseif CombatLog_Object_IsA(sourceFlags, COMBATLOG_FILTER_ME) then |
corruptSpells[spellID] = corruptSpells[spellID] or {} |
corruptSpells[spellID][destGUID] = self:IsEmpowered() |
end |
return true |
elseif (eventType == "SPELL_AURA_REMOVED" or eventType == "SPELL_AURA_BROKEN" or eventType == "SPELL_AURA_BROKEN_SPELL" or eventType == "SPELL_AURA_STOLEN") then |
if CombatLog_Object_IsA(destFlags, COMBATLOG_FILTER_ME) and self:IsFilteredAura(spellID) then |
-- if we lost a special aura we have to check if any other filtered auras remain |
activeAuras[spellID] = nil |
if not filters:IsEmpowered() then |
addon:Debug("No filtered aura detected. Resuming record tracking.") |
end |
-- elseif CombatLog_Object_IsA(sourceFlags, COMBATLOG_FILTER_ME) then |
-- corruptSpells[spellID] = corruptSpells[spellID] or {} |
-- corruptSpells[spellID][destGUID] = nil |
end |
return true |
end |
end |
-- check if a spell passes the filter settings |
function filters:SpellPassesFilters(tree, spellName, spellID, isPeriodic, destGUID, destName, school) |
function filters:SpellPassesFilters(tree, spellName, spellID, isPeriodic, destGUID, destName, school, targetLevel) |
if spellID and not IsSpellKnown(spellID, tree == "pet") and self.profile.onlyKnown then |
addon:Debug(format("%s is not in your%s spell book. Return.", spellName, tree == "pet" and " pet's" or "")) |
return |
return |
end |
local targetLevel = self:GetLevelFromGUID(destGUID) |
if self:IsVulnerableTarget(destGUID) and not self.profile.ignoreAuraFilter then |
addon:Debug("Target is vulnerable. Return.") |
return |
end |
local levelDiff = 0 |
if (targetLevel > 0) and (targetLevel < UnitLevel("player")) then |
levelDiff = (UnitLevel("player") - targetLevel) |
return |
end |
return true, self:IsFilteredSpell(tree, spellName, isPeriodic), targetLevel |
return true, self:IsFilteredSpell(tree, spellID, isPeriodic and 2 or 1), targetLevel |
end |
-- check if a spell passes the filters depending on inverted setting |
function filters:IsFilteredSpell(tree, spellName, periodic) |
local spell = addon:GetSpellInfo(tree, spellName, periodic) |
return ((spell and spell.filtered) ~= nil) ~= self.db.profile.invertFilter |
-- check if a spell will be filtered out |
function filters:IsFilteredSpell(tree, spellID, periodic) |
local spell = addon:GetSpellInfo(tree, spellID, periodic) |
return (not spell and self.db.profile.filterNew) or (spell and spell.filtered) |
end |
end |
function filters:IsFilteredMob(mobName, GUID) |
-- checks if a target is affected by any vulnerability auras |
function filters:IsVulnerableTarget(guid) |
local corruptTarget = corruptTargets[guid] |
if corruptTarget and next(corruptTarget) then |
return true |
end |
end |
function filters:IsFilteredMob(mobName, guid) |
-- GUID is provided if the function was called from the combat event handler |
if GUID and not self.profile.ignoreMobFilter then |
if specialMobs[tonumber(GUID:sub(7, 10), 16)] then |
if guid and not self.profile.ignoreMobFilter then |
if specialMobs[tonumber(guid:sub(7, 10), 16)] then |
addon:Debug("Mob ("..mobName..") is in integrated filter.") |
return true |
end |
function filters:IsFilteredAura(spellID) |
if specialAuras[spellID] then |
addon:Debug("Aura ("..GetSpellInfo(spellID)..") is in integrated filter.") |
return true |
end |
for _, v in ipairs(self.db.global.auras) do |
if v == spellID then |
addon:Debug("Aura ("..GetSpellInfo(spellID)..") is in custom filter.") |
return true |
end |
end |
end |
function filters:GetLevelFromGUID(destGUID) |
tooltip:SetOwner(UIParent, "ANCHOR_NONE") |
tooltip:SetHyperlink("unit:"..destGUID) |
function filters:RegisterAura(auraTable, sourceName, sourceGUID, spellID, spellName, auraType) |
local session = auraTable.session |
if session[spellID] or IsSpellKnown(spellID) or not sourceName then |
return |
end |
local source = L["n/a"] |
local sourceType |
local level = -1 |
local unitType = bit.band(sourceGUID:sub(1, 5), 0x007) |
if unitType == 0 or unitType == 4 then |
-- this is a player or a player's permanent pet |
source = PVP |
sourceType = "pvp" |
else |
source = tonumber(sourceGUID:sub(6, 10), 16) |
sourceType = "npc" |
end |
for i = 1, tooltip:NumLines() do |
local text = _G["CritlineTooltipTextLeft"..i]:GetText() |
if text then |
if text:match(LEVEL) then -- our destGUID has the word Level in it. |
level = text:match("(%d+)") -- find the level |
if level then -- if we found the level, break from the for loop |
level = tonumber(level) |
else |
-- well, the word Level is in this tooltip, but we could not find the level |
-- either the destGUID is at least 10 levels higher than us, or we just couldn't find it. |
level = -1 |
end |
end |
end |
end |
return level |
end |
addon:RegisterEvent("PLAYER_LOGIN") |
addon:RegisterEvent("PLAYER_CONTROL_LOST") |
addon:RegisterEvent("PLAYER_CONTROL_GAINED") |
function addon:PLAYER_LOGIN() |
for i = 1, 40 do |
local buffID = select(11, UnitBuff("player", i)) |
local debuffID = select(11, UnitDebuff("player", i)) |
if not (buffID or debuffID) then |
break |
elseif specialAuras[buffID] then |
activeAuras[buffID] = true |
elseif specialAuras[debuffID] then |
activeAuras[debuffID] = true |
else |
for _, v in ipairs(filters.db.global.auras) do |
if v == buffID then |
activeAuras[buffID] = true |
break |
elseif v == debuffID then |
activeAuras[debuffID] = true |
break |
end |
end |
end |
local aura = { |
source = format("%s (%s)", sourceName, source), |
sourceName = sourceName, |
spellName = spellName, |
sourceType = sourceType, |
type = auraType, |
} |
auraTable.lastFight[spellID] = aura |
if IsInInstance() then |
auraTable.instance[spellID] = aura |
end |
if next(activeAuras) then |
addon:Debug("Filtered aura detected. Disabling combat log tracking.") |
end |
filters.inControl = HasFullControl() |
if not filters.inControl then |
self:Debug("Lost control. Disabling combat log tracking.") |
end |
end |
function addon:PLAYER_CONTROL_LOST() |
filters.inControl = false |
self:Debug("Lost control. Disabling combat log tracking.") |
end |
function addon:PLAYER_CONTROL_GAINED() |
filters.inControl = true |
self:Debug("Regained control. Resuming combat log tracking.") |
session[spellID] = aura |
CritlineAuraListScrollFrame:Update() |
end |
--[[ |
LibDualSpec-1.0 - Adds dual spec support to individual AceDB-3.0 databases |
Copyright (C) 2009 Adirelle |
Copyright (C) 2009-2011 Adirelle |
All rights reserved. |
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
--]] |
local MAJOR, MINOR = "LibDualSpec-1.0", 4 |
local MAJOR, MINOR = "LibDualSpec-1.0", 9 |
assert(LibStub, MAJOR.." requires LibStub") |
local lib = LibStub:NewLibrary(MAJOR, MINOR) |
if not lib then return end |
-- Library data |
-- ---------------------------------------------------------------------------- |
lib.talentGroup = lib.talentGroup or GetActiveTalentGroup() |
lib.eventFrame = lib.eventFrame or CreateFrame("Frame") |
lib.registry = lib.registry or {} |
local locale = GetLocale() |
if locale == "frFR" then |
L_DUALSPEC_DESC = "Lorsqu'elle est activée, cette fonctionnalité vous permet ".. |
"de choisir un profil différent pour chaque spécialisation de talents. ".. |
"Le second profil sera échangé avec le profil courant chaque fois que vous ".. |
"passerez d'une spécialisation à l'autre." |
L_ENABLED = 'Activez le second profil' |
L_ENABLED_DESC = "Cochez cette case pour échanger automatiquement les profils ".. |
"lors d'un changement de spécialisation." |
L_DUAL_PROFILE = 'Second profil' |
L_DUAL_PROFILE_DESC = "Sélectionnez le profil à échanger avec le profil courant ".. |
"lors du changement de spécialisation." |
L_DUALSPEC_DESC = "Lorsqu'elle est activée, cette fonctionnalité vous permet de choisir un profil différent pour chaque spécialisation de talents. Le second profil sera échangé avec le profil courant chaque fois que vous passerez d'une spécialisation à l'autre." |
L_DUAL_PROFILE = "Second profil" |
L_DUAL_PROFILE_DESC = "Sélectionnez le profil à échanger avec le profil courant lors du changement de spécialisation." |
L_ENABLED = "Activez le second profil" |
L_ENABLED_DESC = "Cochez cette case pour échanger automatiquement les profils lors d'un changement de spécialisation." |
elseif locale == "deDE" then |
L_DUALSPEC_DESC = "Wenn aktiv, wechselt dieses Feature bei jedem Wechsel der dualen Talentspezialisierung das Profil. Das duale Profil wird beim Wechsel automatisch mit dem derzeit aktiven Profil getauscht." |
L_DUAL_PROFILE = "Duales Profil" |
L_DUAL_PROFILE_DESC = "W\195\164hle das Profil, das beim Wechsel der Talente aktiviert wird." |
L_DUALSPEC_DESC = "Wenn aktiv, wechselt dieses Feature bei jedem Wechsel ".. |
"der dualen Talentspezialisierung das Profil. Das duale Profil wird beim ".. |
"Wechsel automatisch mit dem derzeit aktiven Profil getauscht." |
L_DUAL_PROFILE_DESC = "Wähle das Profil, das beim Wechsel der Talente aktiviert wird." |
L_ENABLED = "Aktiviere Duale Profile" |
L_ENABLED_DESC = "Aktiviere diese Option, um beim Talentwechsel automatisch ".. |
"zwischen den Profilen zu wechseln." |
L_ENABLED_DESC = "Aktiviere diese Option, um beim Talentwechsel automatisch zwischen den Profilen zu wechseln." |
elseif locale == "koKR" then |
L_DUALSPEC_DESC = "ê°ë¥íë©´ ì¬ì©í©ëë¤. ì´ì¤ í¹ì±ì ìíì¬ ë¤ë¥¸ íë¡íì ì íí ì ìê² íëë¤. ì´ì¤ íë¡íì íì¬ íë¡íê³¼ ë²ê°ìì í¹ì±ì´ ë³ê²½ë ë ê°ì´ ì ì©ë©ëë¤." |
L_DUAL_PROFILE = "ì´ì¤ íë¡í" |
L_DUAL_PROFILE_DESC = "í¹ì±ì´ ë°ë ë íë¡íì ì íí©ëë¤." |
L_ENABLED = "ì´ì¤ íë¡í ì¬ì©" |
L_ENABLED_DESC = "í¹ì±ì´ ë³ê²½ ë ë ìëì¼ë¡ íë¡íì ë³ê²½íëë¡ ì íí©ëë¤." |
elseif locale == "ruRU" then |
L_DUALSPEC_DESC = "Ðвойной пÑоÑÐ¸Ð»Ñ Ð¿Ð¾Ð·Ð²Ð¾Ð»ÑÐµÑ Ð²Ð°Ð¼ вÑбÑаÑÑ ÑазлиÑнÑе пÑоÑили Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð¹ ÑаÑкладки ÑаланÑов. ÐÑоÑили бÑдÑÑ Ð¿ÐµÑеклÑÑаÑÑÑÑ ÐºÐ°Ð¶Ð´Ñй Ñаз, когда Ð²Ñ Ð¿ÐµÑеклÑÑаеÑе ÑаÑÐºÐ»Ð°Ð´ÐºÑ ÑаланÑов." |
L_DUAL_PROFILE = "ÐÑоÑой пÑоÑилÑ" |
L_DUAL_PROFILE_DESC = "ÐÑбеÑиÑе пÑоÑилÑ, коÑоÑÑй Ð½ÐµÐ¾Ð±Ñ Ð¾Ð´Ð¸Ð¼Ð¾ акÑивиÑоваÑÑ Ð¿Ñи пеÑеклÑÑениии ÑаланÑов." |
L_ENABLED = "ÐклÑÑиÑÑ Ð´Ð²Ð¾Ð¹Ð½Ð¾Ð¹ пÑоÑилÑ" |
L_ENABLED_DESC = "ÐклÑÑиÑе ÑÑÑ Ð¾Ð¿ÑÐ¸Ñ Ð´Ð»Ñ Ð°Ð²ÑомаÑиÑеÑкого пеÑеклÑÑÐµÐ½Ð¸Ñ Ð¼ÐµÐ¶Ð´Ñ Ð¿ÑоÑилÑми пÑи пеÑеклÑÑении ÑаÑкладки ÑаланÑов." |
elseif locale == "zhCN" then |
L_DUALSPEC_DESC = "å¯æ¶ï¼ä½ å¯ä»¥ä¸ºä½ çå天èµè®¾å®å¦ä¸ç»é ç½®æ件ï¼ä½ çåéé ç½®æ件å°å¨ä½ 转æ¢å¤©èµæ¶èªå¨ä¸ç®å使ç¨é ç½®æ件交æ¢ã" |
L_DUAL_PROFILE = "åéé ç½®æ件" |
L_DUAL_PROFILE_DESC = "éæ©è½¬æ¢å¤©èµæ¶æè¦ä½¿ç¨çé ç½®æ件" |
L_DUALSPEC_DESC = "å¯æ¶ï¼ä½ å¯ä»¥ä¸ºä½ çå天èµè®¾å®å¦ä¸ç»é ç½®æ件ï¼ä½ çåéé ç½®æ件å°å¨ä½ 转æ¢å¤©èµæ¶èªå¨ä¸ç®å使ç¨é ç½®æ件交æ¢ã" |
L_ENABLED = "å¼å¯åéé ç½®æ件" |
L_ENABLED_DESC = "å¾é以便转æ¢å¤©èµæ¶èªå¨äº¤æ¢é ç½®æ件ã" |
elseif locale == "zhTW" then |
L_DUALSPEC_DESC = "åç¨æï¼ä½ å¯ä»¥çºä½ çé天賦è¨å®å¦ä¸çµè¨å®æªãä½ çéè¨å®æªå°å¨ä½ è½æ天賦æèªåèç®å使ç¨è¨å®æªäº¤æã" |
L_DUAL_PROFILE = "éè¨å®æª" |
L_DUAL_PROFILE_DESC = "é¸æè½æ天賦å¾æè¦ä½¿ç¨çè¨å®æª" |
L_ENABLED = "åç¨éè¨å®æª" |
L_ENABLED_DESC = "å¾é¸ä»¥å¨è½æ天賦æèªå交æè¨å®æª" |
L_DUAL_PROFILE = "éè¨å®æª" |
L_DUAL_PROFILE_DESC = "é¸æè½æ天賦å¾æè¦ä½¿ç¨çè¨å®æª" |
elseif locale == "esES" then |
L_DUALSPEC_DESC = "Si está activa, esta caracterÃstica te permite seleccionar un perfil distinto para cada configuración de talentos. El perfil secundario será intercambiado por el activo cada vez que cambies de una configuración de talentos a otra." |
L_DUAL_PROFILE = "Perfil secundario" |
L_DUAL_PROFILE_DESC = "Elige el perfil secundario que se usará cuando cambies de talentos." |
L_ENABLED = "Activar perfil secundario" |
L_ENABLED_DESC = "Activa esta casilla para alternar automáticamente entre prefiles cuando cambies de talentos." |
end |
end |
-- Mixin |
-- ---------------------------------------------------------------------------- |
--- Get dual spec feature status. |
--- Get dual spec feature status. |
-- @return (boolean) true is dual spec feature enabled. |
-- @name enhancedDB:IsDualSpecEnabled |
function mixin:IsDualSpecEnabled() |
if enabled and not db.char.talentGroup then |
db.char.talentGroup = lib.talentGroup |
db.char.profile = self:GetCurrentProfile() |
db.char.enabled = true |
db.char.enabled = true |
else |
db.char.enabled = enabled |
self:CheckDualSpecState() |
--- Set the alternate profile name. |
-- No validation are done to ensure the profile is valid. |
-- @param profileName (string) the profile name to use. |
-- @param profileName (string) the profile name to use. |
-- @name enhancedDB:SetDualSpecProfile |
function mixin:SetDualSpecProfile(profileName) |
registry[self].db.char.profile = profileName |
-- @name enhancedDB:CheckDualSpecState |
function mixin:CheckDualSpecState() |
local db = registry[self].db |
if db.char.enabled and db.char.talentGroup ~= lib.talentGroup then |
if lib.talentsLoaded and db.char.enabled and db.char.talentGroup ~= lib.talentGroup then |
local currentProfile = self:GetCurrentProfile() |
local newProfile = db.char.profile |
db.char.talentGroup = lib.talentGroup |
if newProfile ~= currentProfile then |
db.char.profile = currentProfile |
self:SetProfile(newProfile) |
db.char.profile = currentProfile |
end |
end |
end |
EmbedMixin(target) |
end |
-- Actually enhance the database |
-- This is used on first initialization and everytime the database is reset using :ResetDB |
function lib:_EnhanceDatabase(event, target) |
registry[target].db = target:GetNamespace(MAJOR, true) or target:RegisterNamespace(MAJOR) |
EmbedMixin(target) |
target:CheckDualSpecState() |
end |
--- Embed dual spec feature into an existing AceDB-3.0 database. |
-- LibDualSpec specific methods are added to the instance. |
-- @name LibDualSpec:EnhanceDatabase |
elseif registry[target] then |
return |
end |
local db = target:GetNamespace(MAJOR, true) or target:RegisterNamespace(MAJOR) |
registry[target] = { name = name, db = db } |
EmbedMixin(target) |
target:CheckDualSpecState() |
registry[target] = { name = name } |
lib:_EnhanceDatabase("EnhanceDatabase", target) |
target.RegisterCallback(lib, "OnDatabaseReset", "_EnhanceDatabase") |
end |
-- ---------------------------------------------------------------------------- |
lib.eventFrame:RegisterEvent('PLAYER_TALENT_UPDATE') |
lib.eventFrame:SetScript('OnEvent', function() |
lib.talentsLoaded = true |
local newTalentGroup = GetActiveTalentGroup() |
if lib.talentGroup ~= newTalentGroup then |
lib.talentGroup = newTalentGroup |
end |
end) |
--- **AceLocale-3.0** manages localization in addons, allowing for multiple locale to be registered with fallback to the base locale for untranslated strings. |
-- @class file |
-- @name AceLocale-3.0 |
-- @release $Id: AceLocale-3.0.lua 895 2009-12-06 16:28:55Z nevcairiel $ |
local MAJOR,MINOR = "AceLocale-3.0", 2 |
-- @release $Id: AceLocale-3.0.lua 1005 2011-01-29 14:19:43Z mikk $ |
local MAJOR,MINOR = "AceLocale-3.0", 5 |
local AceLocale, oldminor = LibStub:NewLibrary(MAJOR, MINOR) |
-- @param application Unique name of addon / module |
-- @param locale Name of the locale to register, e.g. "enUS", "deDE", etc. |
-- @param isDefault If this is the default locale being registered (your addon is written in this language, generally enUS) |
-- @param silent If true, the locale will not issue warnings for missing keys. Can only be set on the default locale. |
-- @param silent If true, the locale will not issue warnings for missing keys. Must be set on the first locale registered. If set to "raw", nils will be returned for unknown keys (no metatable used). |
-- @usage |
-- -- enUS.lua |
-- local L = LibStub("AceLocale-3.0"):NewLocale("TestLocale", "enUS", true) |
-- @return Locale Table to add localizations to, or nil if the current locale is not required. |
function AceLocale:NewLocale(application, locale, isDefault, silent) |
if silent and not isDefault then |
error("Usage: NewLocale(application, locale[, isDefault[, silent]]): 'silent' can only be specified for the default locale", 2) |
end |
-- GAME_LOCALE allows translators to test translations of addons without having that wow client installed |
-- Ammo: I still think this is a bad idea, for instance an addon that checks for some ingame string will fail, just because some other addon |
-- gives the user the illusion that they can run in a different locale? Ditch this whole thing or allow a setting per 'application'. I'm of the |
-- opinion to remove this. |
local gameLocale = GAME_LOCALE or gameLocale |
if locale ~= gameLocale and not isDefault then |
return -- nop, we don't need these translations |
local app = AceLocale.apps[application] |
if silent and app then |
geterrorhandler()("Usage: NewLocale(application, locale[, isDefault[, silent]]): 'silent' must be specified for the first locale registered") |
end |
local app = AceLocale.apps[application] |
if not app then |
app = setmetatable({}, silent and readmetasilent or readmeta) |
if silent=="raw" then |
app = {} |
else |
app = setmetatable({}, silent and readmetasilent or readmeta) |
end |
AceLocale.apps[application] = app |
AceLocale.appnames[app] = application |
end |
if locale ~= gameLocale and not isDefault then |
return -- nop, we don't need these translations |
end |
registering = app -- remember globally for writeproxy and writedefaultproxy |
if isDefault then |
minimap:RegisterForDrag("LeftButton") |
minimap:SetPoint("TOPLEFT", -15, 0) |
minimap:SetSize(32, 32) |
minimap:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight") |
minimap:SetHighlightTexture([[Interface\Minimap\UI-Minimap-ZoomButton-Highlight]]) |
minimap:Hide() |
minimap:SetScript("OnClick", function(self, button) |
local display = addon.display |
if button == "LeftButton" and display then |
if display:IsShown() then |
display:Hide() |
else |
display:Show() |
end |
display:Toggle() |
elseif button == "RightButton" then |
addon:OpenConfig() |
end |
icon:SetPoint("TOPLEFT", 6, -6) |
local border = minimap:CreateTexture(nil, "OVERLAY") |
border:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder") |
border:SetTexture([[Interface\Minimap\MiniMap-TrackingBorder]]) |
border:SetSize(54, 54) |
border:SetPoint("TOPLEFT") |
splash:SetFont(LSM:Fetch("font", font.name), font.size, font.flags) |
end |
local function initialize(self) |
local font = templates:CreateDropDownMenu("CritlineSplashFont", config) |
font:SetFrameWidth(120) |
font:SetPoint("TOPLEFT", config.title, "BOTTOM", 0, -28) |
font.label:SetText(L["Font"]) |
font.initialize = function(self) |
for _, v in ipairs(LSM:List("font")) do |
local info = UIDropDownMenu_CreateInfo() |
info.text = v |
UIDropDownMenu_AddButton(info) |
end |
end |
local font = templates:CreateDropDownMenu("CritlineSplashFont", config, nil, initialize) |
font:SetFrameWidth(120) |
font:SetPoint("TOPLEFT", config.title, "BOTTOM", 0, -28) |
font.label:SetText(L["Font"]) |
options.font = font |
local menu = { |
onClick = function(self) |
self.owner:SetSelectedValue(self.value) |
local font = splash.profile.font |
font.flags = self.value |
splash:SetFont(LSM:Fetch("font", font.name), font.size, font.flags) |
end, |
{ |
text = L["None"], |
value = "", |
}, |
{ |
text = L["Normal"], |
value = "OUTLINE", |
}, |
{ |
text = L["Thick"], |
value = "THICKOUTLINE", |
}, |
{text = L["None"], value = ""}, |
{text = L["Normal"], value = "OUTLINE"}, |
{text = L["Thick"], value = "THICKOUTLINE"}, |
} |
local fontFlags = templates:CreateDropDownMenu("CritlineSplashFontFlags", config, menu) |
fontFlags:SetFrameWidth(120) |
fontFlags:SetPoint("TOP", font, "BOTTOM", 0, -16) |
fontFlags.label:SetText(L["Font outline"]) |
fontFlags.onClick = function(self) |
self.owner:SetSelectedValue(self.value) |
local font = splash.profile.font |
font.flags = self.value |
splash:SetFont(LSM:Fetch("font", font.name), font.size, font.flags) |
end |
options.fontFlags = fontFlags |
local fontSize = templates:CreateSlider(config, { |
flags = "OUTLINE", |
}, |
colors = { |
spell = {r = 1, g = 1, b = 0}, |
spell = {r = 1, g = 1, b = 0}, |
amount = {r = 1, g = 1, b = 1}, |
}, |
pos = { |
local red1 = {r = 1, g = 0, b = 0} |
local red255 = {r = 255, g = 0, b = 0} |
function splash:NewRecord(event, spell, amount, crit, oldAmount) |
spell = format(L["New %s record!"], spell) |
if splash.profile.oldRecord and oldAmount > 0 then |
amount = format("%d (%d)", amount, oldAmount) |
function splash:NewRecord(event, tree, spellID, periodic, amount, crit, prevRecord, isFiltered) |
if isFiltered then |
return |
end |
local colors = splash.profile.colors |
spell = format(L["New %s record!"], addon:GetFullSpellName(spellID, periodic, true)) |
if self.profile.oldRecord and prevRecord.amount > 0 then |
amount = format("%s (%s)", addon:ShortenNumber(amount), addon:ShortenNumber(prevRecord.amount)) |
end |
local colors = self.profile.colors |
local spellColor = colors.spell |
local amountColor = colors.amount |
if splash.profile.sct then |
if self.profile.sct then |
-- check if any custom SCT addon is loaded and use it accordingly |
if MikSBT then |
if crit then |