WoWInterface SVN Critline

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /
    from Rev 23 to Rev 24
    Reverse comparison

Rev 23 → Rev 24

tags/4.0.3/Critline.toc New file
0,0 → 1,34
## Interface: 40100
## Title: Critline
## Version: 4.0.3
## Notes: Saves your normal and critical records and flashes a message if you break the record.
## Author: L'ombra
## SavedVariables: CritlineDB
## SavedVariablesPerCharacter: CritlinePerCharDB
## OptionalDeps: Ace3, LibSharedMedia-3.0
 
libs\LibStub.lua
libs\CallbackHandler-1.0.lua
libs\AceDB-3.0.lua
libs\AceLocale-3.0.lua
libs\LibDataBroker-1.1.lua
libs\LibDualSpec-1.0.lua
libs\LibSharedMedia-3.0.lua
 
locales\enUS.lua
locales\deDE.lua
locales\esES.lua
locales\ruRU.lua
locales\zhTW.lua
 
templates.lua
core.lua
 
filters.lua
splash.lua
display.lua
minimap.lua
announce.lua
reset.lua
Broker.lua
profiles.lua
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3/templates.lua New file
0,0 → 1,599
local addonName, addon = ...
 
local L = LibStub("AceLocale-3.0"):GetLocale(addonName)
 
local templates = {}
addon.templates = templates
 
 
do -- config frame
local function createTitle(frame)
local title = frame:CreateFontString(nil, nil, "GameFontNormalLarge")
title:SetPoint("TOPLEFT", 16, -16)
title:SetPoint("RIGHT", -16, 0)
title:SetJustifyH("LEFT")
title:SetJustifyV("TOP")
title:SetText(frame.name)
frame.title = title
end
 
local function createDesc(frame)
local desc = frame:CreateFontString(nil, nil, "GameFontHighlightSmall")
desc:SetHeight(32)
desc:SetPoint("TOPLEFT", frame.title, "BOTTOMLEFT", 0, -8)
desc:SetPoint("RIGHT", -32, 0)
desc:SetJustifyH("LEFT")
desc:SetJustifyV("TOP")
desc:SetNonSpaceWrap(true)
frame.desc = desc
end
 
function templates:CreateConfigFrame(name, parent, addTitle, addDesc)
local frame = CreateFrame("Frame")
frame.name = name
frame.parent = parent
if addTitle then
createTitle(frame)
if addDesc then
createDesc(frame)
end
end
InterfaceOptions_AddCategory(frame)
return frame
end
end
 
 
do -- click button
local textures = {"l", "r", "m"}
 
local function setTexture(self, texture)
for _, v in ipairs(textures) do
self[v]:SetTexture(texture)
end
end
 
local function onMouseDown(self)
if self:IsEnabled() == 1 then
setTexture(self, "Interface\\Buttons\\UI-Panel-Button-Down")
end
end
 
local function onMouseUp(self)
if self:IsEnabled() == 1 then
setTexture(self, "Interface\\Buttons\\UI-Panel-Button-Up")
end
end
 
local function onDisable(self)
setTexture(self, "Interface\\Buttons\\UI-Panel-Button-Disabled")
end
 
local function onEnable(self)
setTexture(self, "Interface\\Buttons\\UI-Panel-Button-Up")
end
 
function templates:CreateButton(parent)
local btn = CreateFrame("Button", nil, parent)
btn:SetNormalFontObject("GameFontNormal")
btn:SetHighlightFontObject("GameFontHighlight")
btn:SetDisabledFontObject("GameFontDisable")
btn:SetScript("OnMouseDown", onMouseDown)
btn:SetScript("OnMouseUp", onMouseUp)
btn:SetScript("OnDisable", onDisable)
btn:SetScript("OnEnable", onEnable)
 
local highlight = btn:CreateTexture(nil, nil, "UIPanelButtonHighlightTexture")
btn:SetHighlightTexture(highlight)
 
local l = btn:CreateTexture(nil, "BACKGROUND")
l:SetTexture("Interface\\Buttons\\UI-Panel-Button-Up")
l:SetTexCoord(0, 0.09375, 0, 0.6875)
l:SetWidth(12)
l:SetPoint("TOPLEFT")
l:SetPoint("BOTTOMLEFT")
btn.l = l
 
local r = btn:CreateTexture(nil, "BACKGROUND")
r:SetTexture("Interface\\Buttons\\UI-Panel-Button-Up")
r:SetTexCoord(0.53125, 0.625, 0, 0.6875)
r:SetWidth(12)
r:SetPoint("TOPRIGHT")
r:SetPoint("BOTTOMRIGHT")
btn.r = r
 
local m = btn:CreateTexture(nil, "BACKGROUND")
m:SetTexture("Interface\\Buttons\\UI-Panel-Button-Up")
m:SetTexCoord(0.09375, 0.53125, 0, 0.6875)
m:SetPoint("TOPLEFT", l, "TOPRIGHT")
m:SetPoint("BOTTOMRIGHT", r, "BOTTOMLEFT")
btn.m = m
 
return btn
end
end
 
 
do -- check button
local function onClick(self)
local checked = self:GetChecked()
 
if checked then
PlaySound("igMainMenuOptionCheckBoxOn")
else
PlaySound("igMainMenuOptionCheckBoxOff")
end
 
self.module[self.db].profile[self.setting] = checked and true or false
 
if self.func then
self:func()
end
 
addon:Debug(self.setting..(checked and " on" or " off"))
end
 
local function loadSetting(self)
self:SetChecked(self.module[self.db].profile[self.setting])
if self.func then
self:func()
end
end
 
function templates:CreateCheckButton(parent, data)
local btn = CreateFrame("CheckButton", nil, parent, "OptionsBaseCheckButtonTemplate")
btn:SetPushedTextOffset(0, 0)
btn:SetScript("OnClick", onClick)
 
btn.LoadSetting = loadSetting
 
local text = btn:CreateFontString(nil, nil, "GameFontHighlight")
text:SetPoint("LEFT", btn, "RIGHT", 0, 1)
btn:SetFontString(text)
 
if data then
btn:SetText(data.text)
data.text = nil
data.db = data.perchar and "percharDB" or "db"
data.perchar = nil
for k, v in pairs(data) do
btn[k] = v
end
end
 
return btn
end
end
 
 
do -- slider template
local backdrop = {
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}
}
 
local function onEnter(self)
if self:IsEnabled() then
if self.tooltipText then
GameTooltip:SetOwner(self, self.tooltipOwnerPoint or "ANCHOR_RIGHT")
GameTooltip:SetText(self.tooltipText, nil, nil, nil, nil, true)
end
end
end
 
function templates:CreateSlider(parent, data)
local slider = CreateFrame("Slider", nil, parent)
slider:EnableMouse(true)
slider:SetSize(144, 17)
slider:SetOrientation("HORIZONTAL")
slider:SetHitRectInsets(0, 0, -10, -10)
slider:SetBackdrop(backdrop)
slider:SetScript("OnEnter", onEnter)
slider:SetScript("OnLeave", GameTooltip_Hide)
 
local text = slider:CreateFontString(nil, nil, "GameFontNormal")
text:SetPoint("BOTTOM", slider, "TOP")
slider.text = text
 
local min = slider:CreateFontString(nil, nil, "GameFontHighlightSmall")
min:SetPoint("TOPLEFT", slider, "BOTTOMLEFT", -4, 3)
slider.min = min
 
local max = slider:CreateFontString(nil, nil, "GameFontHighlightSmall")
max:SetPoint("TOPRIGHT", slider, "BOTTOMRIGHT", 4, 3)
slider.max = max
 
if data then
slider:SetMinMaxValues(data.minValue, data.maxValue)
slider:SetValueStep(data.valueStep)
slider:SetScript("OnValueChanged", data.func)
text:SetText(data.text)
min:SetText(data.minText or data.minValue)
max:SetText(data.maxText or data.maxValue)
slider.tooltipText = data.tooltipText
end
 
-- font string for current value
local value = slider:CreateFontString(nil, "BACKGROUND", "GameFontHighlightSmall")
value:SetPoint("CENTER", 0, -15)
slider.value = value
 
local thumb = slider:CreateTexture()
thumb:SetTexture("Interface\\Buttons\\UI-SliderBar-Button-Horizontal")
thumb:SetSize(32, 32)
slider:SetThumbTexture(thumb)
 
return slider
end
end
 
 
do -- swatch button template
local ColorPickerFrame = ColorPickerFrame
 
local function swatchFunc()
local button = ColorPickerFrame.extraInfo
local r, g, b = ColorPickerFrame:GetColorRGB()
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 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 = swatchFunc
-- info.hasOpacity = self.hasOpacity
-- info.opacityFunc = opacityFunc
-- info.opacity = color.a
info.cancelFunc = cancelFunc
info.extraInfo = self
OpenColorPicker(info)
end
 
local function onEnter(self)
self.bg:SetVertexColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b)
if self.tooltipText then
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
GameTooltip:SetText(self.tooltipText, nil, nil, nil, nil, true)
end
end
 
local function onLeave(self)
self.bg:SetVertexColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
GameTooltip:Hide()
end
 
function templates:CreateColorButton(parent)
local btn = CreateFrame("Button", nil, parent)
btn:SetSize(16, 16)
btn:SetPushedTextOffset(0, 0)
 
btn:SetNormalTexture("Interface\\ChatFrame\\ChatFrameColorSwatch")
btn.swatch = btn:GetNormalTexture()
 
local bg = btn:CreateTexture(nil, "BACKGROUND")
bg:SetTexture(1.0, 1.0, 1.0)
bg:SetSize(14, 14)
bg:SetPoint("CENTER")
btn.bg = bg
 
local text = btn:CreateFontString(nil, nil, "GameFontHighlight")
text:SetPoint("LEFT", btn, "RIGHT", 5, 1)
text:SetJustifyH("LEFT")
btn:SetFontString(text)
 
btn:SetScript("OnClick", onClick)
btn:SetScript("OnEnter", onEnter)
btn:SetScript("OnLeave", onLeave)
 
return btn
end
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)
 
local left = editbox:CreateTexture("BACKGROUND")
left:SetTexture("Interface\\Common\\Common-Input-Border")
left:SetTexCoord(0, 0.0625, 0, 0.625)
left:SetWidth(8)
left:SetPoint("TOPLEFT")
left:SetPoint("BOTTOMLEFT")
 
local right = editbox:CreateTexture("BACKGROUND")
right:SetTexture("Interface\\Common\\Common-Input-Border")
right:SetTexCoord(0.9375, 1, 0, 0.625)
right:SetWidth(8)
right:SetPoint("TOPRIGHT")
right:SetPoint("BOTTOMRIGHT")
 
local mid = editbox:CreateTexture("BACKGROUND")
mid:SetTexture("Interface\\Common\\Common-Input-Border")
mid:SetTexCoord(0.0625, 0.9375, 0, 0.625)
mid:SetPoint("TOPLEFT", left, "TOPRIGHT")
mid:SetPoint("BOTTOMRIGHT", right, "BOTTOMLEFT")
 
return editbox
end
end
 
 
do -- dropdown menu frame
local function setSelectedValue(self, value)
UIDropDownMenu_SetSelectedValue(self, value)
UIDropDownMenu_SetText(self, self.menu and self.menu[value] or value)
end
 
local function setDisabled(self, disable)
if disable then
self:Disable()
else
self:Enable()
end
end
 
local function initialize(self)
local onClick = self.onClick
for _, v in ipairs(self.menu) do
local info = UIDropDownMenu_CreateInfo()
info.text = v.text
info.value = v.value
info.func = onClick or v.func
info.owner = self
info.fontObject = v.fontObject
UIDropDownMenu_AddButton(info)
end
end
 
function templates:CreateDropDownMenu(name, parent, menu, valueLookup)
local frame = CreateFrame("Frame", name, parent, "UIDropDownMenuTemplate")
 
frame.SetFrameWidth = UIDropDownMenu_SetWidth
frame.SetSelectedValue = setSelectedValue
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
menu[v.value] = v.text
end
end
frame.menu = menu or valueLookup
 
frame.initialize = initialize
 
local label = frame:CreateFontString(name.."Label", "BACKGROUND", "GameFontNormalSmall")
label:SetPoint("BOTTOMLEFT", frame, "TOPLEFT", 16, 3)
frame.label = label
 
return frame
end
end
 
 
do -- used in Reset and Announce
local MAXSPELLBUTTONS = 8
local ITEMHEIGHT = 36
 
local function update(self)
local selectedTree = self.tree:GetSelectedValue()
 
local spells = addon:GetSpellArray(selectedTree)
 
local size = #spells
 
FauxScrollFrame_Update(self.scrollFrame, size, MAXSPELLBUTTONS, ITEMHEIGHT)
 
local offset = FauxScrollFrame_GetOffset(self.scrollFrame)
 
for line = 1, MAXSPELLBUTTONS do
local item = self.buttons[line]
local lineplusoffset = line + offset
if lineplusoffset <= size then
local data = spells[lineplusoffset]
item.data = data
item.button.data = data
local normal = data.normal and data.normal.amount
local crit = data.crit and data.crit.amount
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
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, action)
local frame = templates:CreateConfigFrame(title, addonName)
 
frame.Update = update
local function update()
frame:Update()
end
 
addon.RegisterCallback(frame, "PerCharSettingsLoaded", "Update")
addon.RegisterCallback(frame, "RecordsChanged", "Update")
 
local scrollFrame = CreateFrame("ScrollFrame", name.."ScrollFrame", frame, "FauxScrollFrameTemplate")
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
 
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
GameTooltip:Show()
end
 
-- create list of check buttons
local buttons = {}
for i = 1, MAXSPELLBUTTONS do
local item = CreateFrame("Frame", nil, frame)
item:SetHeight(ITEMHEIGHT)
if i == 1 then
item:SetPoint("TOPLEFT", scrollFrame)
else
item:SetPoint("TOP", buttons[i - 1], "BOTTOM")
end
item:SetPoint("RIGHT", scrollFrame)
item:SetScript("OnEnter", onEnter)
item:SetScript("OnLeave", GameTooltip_Hide)
 
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 = item:CreateFontString(nil, nil, "GameFontHighlightSmall")
record:SetPoint("TOPRIGHT", -36, 0)
record:SetPoint("BOTTOMRIGHT", -36, 0)
record:SetJustifyH("RIGHT")
record:SetSpacing(2)
item.record = record
 
buttons[i] = item
end
frame.buttons = buttons
 
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
 
return frame
end
end
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3/locales/esES.lua New file
0,0 → 1,91
local L = LibStub("AceLocale-3.0"):NewLocale("Critline", "esES")
if not L then return end
 
-- Spanish translation by Vladixlaus
L["Add by name"] = "Agregar por nombre"
L["Add target"] = "Agregar objectivo"
L["Advanced options"] = "Opciones avanzadas"
L["Alphabetically"] = "Alfabeticamente"
L["Amount color"] = "Color del daño"
L["Basic options"] = "Opciones básicas"
L["By crit record"] = "por marca"
L["By normal record"] = "normal"
L["Cannot add players to mob filter."] = "No puedes agregar jugadores."
L["Chat output"] = "Salida en el Chat"
L["Check to enable damage events to be recorded."] = "Grabar el daño"
L["Check to enable healing events to be recorded."] = "Grabar la curaciones."
L["Check to enable pet damage events to be recorded."] = "Grabar daño de la mascota."
L["Crit"] = "Crítico"
L["Critical!"] = "Crítico!"
L["Critline splash frame unlocked"] = "Desbloquear el marco de mensaje"
L["Delete mob"] = "Borrar mob"
L["Detailed summary"] = "Sumario detallado"
L["Disable to ignore records where the target is an NPC."] = "Inhabilitar: Ignorar las marcas cuando el objectivo no es un jugador."
L["Disable to ignore records where the target is a player."] = "Inhabilitar: Ignorar las marcas cuando el objectivo es un jugador."
L["Includes previous record along with \"New record\" messages."] = "Mostrar marca anterior junto con la nueva marca."
L["Don't filter magic"] = "No filtrar magia"
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 ignore integrated mob filter."] = "Habilita para ingnorar todas las entradas en el filtro de mobs."
-- L["Enable to include rather than exclude selected spells in the spell filter."] = ""
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["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"
L["Invalid mob name."] = "Nombre invalido."
L["Invalid player name."] = "Nombre inválido."
L["Invert spell filter"] = "Invertir filtro de habilidades"
L["Left-click to open options\nRight-click to hide button"] = "Click-Izq para opciones\nClick-Dcho para ocultar botón"
L["Left-click to toggle summary frame\nRight-click to open options\nDrag to move"] = "Click-Izq para ver sumario\nClick-Dcho para opciones\nArrastrar para mover"
L["Level filter"] = "Ajuste de nivel"
L["Minimap button"] = "Botón del minimapa"
L["Mob filter"] = "Filtro de mob"
L["Move splash screen"] = "Mover cuadro de mensaje"
L["New %s record!"] = "Nueva marca de: %s!"
L["No records"] = "No hay marcas aún"
L["Normal"] = "Normal"
L["No target selected."] = "Seleccione un objectivo primero."
L["Plays a sound on a new record."] = "Tocar sonido cuando halla una nueva marca."
L["Play sound"] = "Tocar sonido"
L["Prints new record notifications to the chat frame."] = "Mostrar notificcaciones en el chat."
L["Record damage"] = "Grabar daño"
L["Record healing"] = "Grabar curaciones"
L["Record pet damage"] = "Grabar daño de mascota"
L["Record PvE"] = "Grabar PvE" -- Needs review
L["Record PvP"] = "Grabar JcJ"
L["Reset all records for this tree"] = "Borrar los records de este tipo"
L["Right-click to lock"] = "Click-Dcho para bloquear"
L["%s added to mob filter."] = "%s agregado al filtro de mobs."
L["Saves a screenshot on a new record."] = "Guardar una foto cuando halla una nueva marca."
L["Saves your high normal and critical damage records and flashes a message if you break the record."] = "Guardar tus marcas normales y críticas; y mostar un mesaje cuando repes tus marcas."
L["Screenshot"] = "Foto"
L["Sets the number of seconds you wish to display the splash frame."] = "Timpo del mensaje en la pantalla."
L["Sets the scale of the splash frame."] = "Escala del marco de mensaje."
L["Sets the scale of the summary frame."] = "Escala del sumario."
L["Set the color for the amount text in the splash frame."] = "Color del daño."
L["Set the color for the spell text in the splash frame."] = "Color de la habulidad."
L["Show button on minimap."] = "Mostar boton en el minimapa."
L["Show damage"] = "Mostrar daño"
L["Show damage in summary frame."] = "Mostrar daño en el sumario."
L["Show healing"] = "Mostar curaciones"
L["Show healing in summary frame."] = "Mostrar curaciones en el sumario."
L["Include old record"] = "Mostra marcas antigüas"
L["Show pet damage"] = "Mostrar daño de mascota"
L["Show pet damage in summary frame."] = "Mostrar daño de mascota en el sumario."
L["Shows the new record on the middle of the screen."] = "Mostar la nueva marca en el medio de la pantalla."
L["%s is already in mob filter."] = "%s ya esta en el filtro de mobs."
L["Sort summary spells:"] = "Organizar sumario por:"
L["Spell color"] = "Color de la habilidad"
L["Spell filter"] = "Filtro de habilidad"
L["Splash frame"] = "Marco de mensaje"
L["Splash frame scale"] = "Escala del marco de mensajes"
L["Splash frame timer"] = "Tiempo del mensaje"
L["%s removed from mob filter."] = "Quitar %s del filtro de mobs."
L["Summary frame scale"] = "Escala del sumario"
L["Suppress all records while mind controlled."] = "Ignorar las marcas mientra se usa Control mental."
L["Suppress mind control"] = "Ignorar Control mental"
L["Use combat text splash"] = "Usar el marco de texto de combate"
L["Use detailed format in the Critline summary tooltip."] = "Mostrar formato detallado en el tooltip del sumario."
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3/locales/deDE.lua New file
0,0 → 1,125
local L = LibStub("AceLocale-3.0"):NewLocale("Critline", "deDE")
if not L then return end
 
-- German translation by Destoxillo
L["Add by name"] = "Nach Namen hinzufügen"
L["Add by spell ID"] = "Nach Zauber-ID hinzufügen" -- Needs review
L["Add target"] = "Ziel hinzufügen"
L["Alphabetically"] = "Alphabetisch"
L["Amount color"] = "Farbe des Betrages"
L["Announce"] = "Ansagen" -- Needs review
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["Chat output"] = "Ausgabe im Chat"
L["Check to enable damage events to be recorded."] = "Haken setzen um Schaden aufzuzeichnen."
L["Check to enable healing events to be recorded."] = "Haken setzen um Heilung aufzuzeichnen."
L["Check to enable pet damage events to be recorded."] = "Haken setzen um den Begleiterschaden aufzuzeichnen."
L["Crit"] = "Crit" -- Needs review
L["critical "] = "kritisch " -- Needs review
L["Critical!"] = "Kritisch!"
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 an NPC."] = "Deaktivieren um Rekorde zu ignorieren bei denen das Ziel ein NPC ist."
L["Disable to ignore records where the target is a player."] = "Deaktivieren um Rekorde zu ignorieren bei denen das Ziel ein Spieler ist."
L["Includes 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["Drag to move"] = "Ziehen zum bewegen"
L["Duration"] = "Dauer" -- Needs review
L["Enabled"] = "Aktiviert" -- Needs review
L["Enable to ignore additional damage due to vulnerability."] = "Aktivieren um zusätzlichen Schaden durch Verwundbarkeit zu ignorieren." -- Needs review
L["Enable to ignore integrated aura filter."] = "Aktivieren um integrierten Aurenfilter zu ignorieren."
L["Enable to ignore integrated mob filter."] = "Aktivieren um integrierten Mobfilter zu ignorieren."
L["Enable to ignore spells that are not in your (or your pet's) spell book."] = "Aktivieren um Zauber zu ignorieren, die nicht in deinem (oder deinem Pet seinem) Zauberbuch sind." -- Needs review
L["Enable to include rather than exclude selected spells in the spell filter."] = "Aktivieren um ausgewählte Zauber in den Zauberfilter einzufügen anstatt sie auszuschließen."
L["Enable to let magical damage ignore the level filter."] = "Aktivieren um magischem Schaden erlauben, den Levelfilter zu ignorieren." -- Needs review
L["Enable to show icon indicators instead of text."] = "Aktivieren um Icons als Indikatoren anzuzeigen statt Text." -- Needs review
L["Enable to use scrolling combat text for \"New record\" messages instead of the default splash frame."] = "Aktivieren um scrollenden Kampftext für die \"Neuer Rekord\" Nachricht zu verwenden anstatt der standartmäßigen Splash Anzeige."
L["Enter mob name:"] = "Mobname eingeben:"
L["Enter spell ID:"] = "Zauber-ID eingeben:" -- Needs review
L["Font"] = "Schrift" -- Needs review
L["Font outline"] = "Schriftumrandung" -- Needs review
L["Font size"] = "Schriftgrösse" -- Needs review
L["healing"] = "Heilung" -- Needs review
L["Healing"] = "Heilung" -- 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["Ignore vulnerability"] = "Verwundbarkeit ignorieren" -- Needs review
L["Invalid channel. Please enter a valid channel name or ID."] = "Ungültiger Channel. Bitte gib einen gültigen Channel Name oder ID ein." -- Needs review
L["Invalid input. Please enter a spell ID."] = "Ungültige Eingabe. Bitte gib eine Zauber-ID ein." -- Needs review
L["Invalid mob name."] = "Ungültiger Mobname."
L["Invalid player name."] = "Ungültiger Spielername."
L["Invalid spell ID. No such spell exists."] = "Ungültige Zauber-ID. Kein solcher Zauber existiert." -- Needs review
L["Invert spell filter"] = "Zauberfilter umkehren"
L["Left-click to toggle summary frame"] = "Linksklicken um das Zusammenfassungs-Fenster umzuschalten" -- Needs review
L["Level filter"] = "Levelfilter" -- Needs review
L["Locked"] = "Gesperrt" -- Needs review
L["Lock minimap button."] = "Minimap Button sperren" -- Needs review
L["Lock summary frame."] = "Zusammenfassungs-Fenster sperren." -- Needs review
L["Minimap button"] = "Minimap Knopf"
L["Mob filter"] = "Mobfilter"
L["Move splash screen"] = "'Splash Anzeige' bewegen"
L["New %s record!"] = "Neuer %s Rekord!"
L["New %s%s record - %s"] = "Neuer %s%s Rekord - %s" -- Needs review
L["None"] = "Kein" -- Needs review
L["No records"] = "Keine Rekorde"
L["Normal"] = "Normal"
L["No target selected."] = "Kein Ziel ausgewählt."
L["Only known spells"] = "Nur bekannte Zauber" -- Needs review
L["pet"] = "Tier" -- Needs review
L["Pet"] = "Tier" -- Needs review
L["Plays a sound on a new record."] = "Spielt einen Sound bei einem neuen Rekord ab."
L["Play sound"] = "Sound abspielen"
L["Prints new record notifications to the chat frame."] = "Gibt neue Rekord-Mitteilungen im Chat-Fenster aus."
L["Record damage"] = "Schaden aufzeichnen"
L["Record healing"] = "Heilung aufzeichnen"
L["Record pet damage"] = "Begleiterschaden aufzeichnen"
L["Record PvE"] = "PvE aufzeichnen"
L["Record PvP"] = "PvP aufzeichnen"
L["Reset"] = "Zurücksetzen" -- Needs review
L["Reset all"] = "Alles zurücksetzen" -- Needs review
L["Reset all %s records."] = "Alle %s Rekorde zurücksetzen." -- Needs review
L["Reset %s (%s) records."] = "Rekord %s (%s) zurücksetzen." -- Needs review
L["Right-click to lock"] = "Rechtsklick zum fixieren"
L["Right-click to open options"] = "Rechtsklicken um die Optionen zu öffnen" -- Needs review
L["%s added to aura filter."] = "%s zum Aurafilter hinzugefügt." -- Needs review
L["%s added to mob filter."] = "%s zum Mobfilter hinzugefügt."
L["Saves a screenshot on a new record."] = "Speichert einen Screenshot bei einem neuen Rekord."
L["Scale"] = "Skalierung" -- Needs review
L["Screenshot"] = "Screenshot"
L["Sets the color for the amount text in the splash frame."] = "Setzt die Farbe für die Anzeige des Wertes im Splash-Fenster." -- Needs review
L["Sets the color for the spell text in the splash frame."] = "Setzt die Farbe für den Zaubertext im Splash-Fenster." -- Needs review
L["Sets the font size of the splash frame."] = "Setzt die Schriftgrösse im Splash-Fenster." -- Needs review
L["Sets the scale of the splash frame."] = "Legt die Skalierung der 'Splash Anzeige' fest."
L["Sets the scale of the summary frame."] = "Legt die Skalierung der Zusammenfassung fest."
L["Sets the time (in seconds) the splash frame is visible before fading out."] = "Setzt die Zeit (in Sekunden) bis das Splash-Fenster ausgeblendet wird." -- Needs review
L["Show"] = "Zeige" -- Needs review
L["Show icons"] = "Zeige Icons" -- Needs review
L["Show minimap button."] = "Zeige Minimap Button." -- Needs review
L["Include old record"] = "Alten Rekord anzeigen"
L["Shows the new record on the middle of the screen."] = "Zeigt den neuen Rekord in der Mitte des Bildschirms."
L["Show summary frame."] = "Zeige Zusammenfassungs-Fenster." -- Needs review
L["%s is already in aura filter."] = "%s ist schon im Aurafilter." -- Needs review
L["%s is already in mob filter."] = "%s ist schon im Mobfilter."
L["Spell color"] = "Zauberfarbe"
L["Spell filter"] = "Zauberfilter"
L["Splash frame"] = "'Splash Anzeige'"
L["%s removed from aura filter."] = "%s vom Aurafilter entfernt." -- Needs review
L["%s removed from mob filter."] = "%s vom Mobfilter entfernt."
L["Summary frame scale"] = "Zusammenfassung Skalierung"
L["Suppress all records while mind controlled."] = "Unterdrücke alle Rekorde unter Gedankenkontrolle."
L["Suppress mind control"] = "Unterdrücke Gedankenkontrolle"
L["Thick"] = "Stark" -- Needs review
L["Tooltip sorting:"] = "Tooltip Sortierung:" -- Needs review
L["Use combat text splash"] = "Benutze 'Kampftext Splash'"
L["Use detailed format in the summary tooltip."] = "Benutze detailiertes Format im Zusammenfassungs-Tooltip." -- Needs review
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3/locales/enUS.lua New file
0,0 → 1,147
local L = LibStub("AceLocale-3.0"):NewLocale("Critline", "enUS", true)
if not L then return end
 
 
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["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["Chat output"] = true
L["Check to enable damage events to be recorded."] = true
L["Check to enable healing events to be recorded."] = true
L["Check to enable pet damage events to be recorded."] = true
L["Crit"] = 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 an NPC."] = true
L["Disable to ignore records where the target is a player."] = true
L["Don't filter magic"] = 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 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["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 old record"] = true
L["Include (unfiltered) records in spell tooltips."] = true
L["Includes previous record along with \"New record\" messages."] = 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["Left-click to toggle summary frame"] = true
L["Level filter"] = true
L["Lock minimap button."] = true
L["Lock summary frame."] = true
L["Locked"] = true
L["Minimap button"] = true
L["Mob filter"] = true
L["Move splash screen"] = true
L["New %s record!"] = true
L["New %s%s record - %s"] = true
L["n/a"] = true
L["None"] = true
L["No records"] = true
L["Normal"] = true
L["No target selected."] = true
L["Only known spells"] = true
L["pet"] = 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["Reset %s (%s) records."] = true
L["Right-click to lock"] = true
L["Right-click to open options"] = true
L["%s added to aura filter."] = true
L["%s added to mob filter."] = true
L["Saves a screenshot on a new record."] = true
L["Scale"] = true
L["Screenshot"] = 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 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 summary frame."] = true
L["Shows the new record on the middle of the screen."] = true
L["%s is already in aura filter."] = 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["Use shorter format for records numbers."] = true
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3/locales/zhTW.lua New file
0,0 → 1,109
local L = LibStub("AceLocale-3.0"):NewLocale("Critline", "zhTW")
if not L then return end
 
-- Traditional Chinese localisation by wowuicn
L["Add by name"] = "按名字"
-- L["Add by spell ID"] = ""
L["Add target"] = "新增目標"
L["Advanced options"] = "一般設定"
L["Alphabetically"] = "按字母"
L["Amount color"] = "數額顏色"
-- L["Aura filter"] = ""
L["Basic options"] = "基礎選項"
L["By crit record"] = "按暴擊記錄"
L["By normal record"] = "按普通記錄"
L["Cannot add players to mob filter."] = "不能添加玩家到怪物過濾器"
L["Chat output"] = "聊天框輸出"
L["Check to enable damage events to be recorded."] = "選中來開啟要記錄的傷害事件."
L["Check to enable healing events to be recorded."] = "選中來開啟要記錄的治療事件."
L["Check to enable pet damage events to be recorded."] = "選中來開啟要記錄的寵物傷害事件."
L["Crit"] = "暴擊"
L["Critical!"] = "暴擊!"
L["Critline splash frame unlocked"] = "Critline 噴濺效果框體已解鎖"
-- L["Delete aura"] = ""
L["Delete mob"] = "刪除怪物"
L["Detailed summary"] = "詳細的訊息"
L["Disable to ignore records where the target is an NPC."] = "禁用來忽略目標是一個NPC的記錄."
L["Disable to ignore records where the target is a player."] = "禁用來忽略目標是一個玩家的記錄."
L["Includes previous record along with \"New record\" messages."] = "在\"新的記錄\"訊息一起顯示前一條記錄."
L["Don't filter magic"] = "不過濾魔法"
-- L["Duration"] = ""
L["Drag to move"] = "拖曳來移動"
L["Edit tooltip format"] = "編輯提示訊息格式"
-- L["Enable to ignore additional/loss of damage or healing due to vulnerability, resistance, absorption or blocking."] = ""
L["Enable to ignore integrated aura filter."] = "開啟來忽略完整的光環過濾器."
L["Enable to ignore integrated mob filter."] = "開啟來忽略完整的怪物過濾器"
L["Enable to include rather than exclude selected spells in the spell filter."] = "開啟來包括而不是排除在法術過濾器中的已選中法術."
L["Enable to let magical damage ignore the level adjustment."] = "開啟來讓魔法傷害忽略等級調整."
L["Enable to use scrolling combat text for \"New record\" messages instead of the default splash frame."] = "開啟來為\"新的記錄\"使用滾動戰斗文字訊息來代替預設的噴濺框體."
-- L["Enabled"] = ""
L["Enter mob name:"] = "輸入怪物名字"
-- L["Enter spell ID:"] = ""
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["Ignore modifiers"] = "
-- L["Invalid channel. Please enter a valid channel name or ID."] = "
-- L["Invalid input. Please enter a spell ID."] = "
L["Invalid mob name."] = "無效的怪物名字."
L["Invalid player name."] = "無效的玩家名字."
-- L["Invalid spell ID. No such spell exists."] = ""
L["Invert spell filter"] = "反轉法術過濾器"
L["Left-click to toggle summary frame"] = "左鍵點擊 開啟/關閉概要框體"
-- L["Level filter"] = ""
-- L["Lock minimap button."] = ""
-- L["Lock summary frame."] = ""
-- L["Locked"] = ""
-- L["Minimap button"] = "小地圖按鈕"
L["Mob filter"] = "怪物過濾器"
L["Move splash screen"] = "移動噴濺效果"
L["New %s record!"] = "新的 %s 記錄!"
L["No records"] = "沒有記錄"
L["Normal"] = "普通"
L["No target selected."] = "沒有選擇目標."
L["Plays a sound on a new record."] = "當有一個新的記錄時播放音效."
L["Play sound"] = "播放音效"
L["Prints new record notifications to the chat frame."] = "打印新的記錄提醒到聊天框."
L["Record damage"] = "記錄傷害"
L["Record healing"] = "記錄治療"
L["Record pet damage"] = "記錄寵物傷害"
L["Record PvE"] = "記錄 PvE"
L["Record PvP"] = "記錄 PvP"
L["Reset all records for this tree"] = "重置所有記錄"
L["Right-click to lock"] = "右鍵點擊 鎖定"
L["Right-click to open options"] = "右鍵點擊 打開選項"
-- L["%s added to aura filter."] = ""
L["%s added to mob filter."] = "%s 已添加到怪物過濾器."
L["Saves a screenshot on a new record."] = "當有一個新的記錄時保存截屏."
L["Saves your high normal and critical damage records and flashes a message if you break the record."] = "保存你的普通或暴擊傷害的最高記錄并當你打破這個記錄時閃動訊息."
-- L["Scale"] = ""
L["Screenshot"] = "截屏"
L["Sets the number of seconds you wish to display the splash frame."] = "設定你想要在噴濺框體中顯示的記錄數量."
L["Sets the scale of the splash frame."] = "設定噴濺框體的縮放值."
L["Sets the scale of the summary frame."] = "設定概要框體的縮放值."
L["Set the color for the amount text in the splash frame."] = "設定在噴濺框體中數字文字的顏色."
L["Set the color for the spell text in the splash frame."] = "設定在噴濺框體中法術文字的顏色."
-- L["Show"] = ""
L["Show minimap button."] = "在小地圖上顯示按鈕."
L["Show damage"] = "顯示傷害"
L["Show damage in summary frame."] = "在概要框體中顯示傷害"
L["Show healing"] = "顯示治療"
L["Show healing in summary frame."] = "在概要框體中顯示治療"
L["Include old record"] = "顯示舊的記錄"
L["Show pet damage"] = "顯示寵物傷害"
L["Show pet damage in summary frame."] = "在概要框體中顯示寵物傷害"
-- L["Show summary frame."] = ""
L["Shows the new record on the middle of the screen."] = "在屏幕中間顯示新的記錄."
-- L["%s is already in aura filter."] = ""
L["%s is already in mob filter."] = "%s 已在怪物過濾器."
L["Sort summary spells:"] = "分類概要法術:"
L["Spell color"] = "法術顏色"
L["Spell filter"] = "法術過濾器"
L["Splash frame"] = "噴濺框體"
-- L["%s removed from aura filter."] = ""
L["%s removed from mob filter."] = "%s 已從怪物過濾器中移除."
L["Summary frame scale"] = "概要框體縮放"
L["Suppress all records while mind controlled."] = "當被精神控制時取締所有記錄."
L["Suppress mind control"] = "取締精神控制"
L["Use combat text splash"] = "使用戰斗文字噴濺"
L["Use detailed format in the Critline summary tooltip."] = "在 Critline 概要提示訊息中使用詳細格式."
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3/locales/ruRU.lua New file
0,0 → 1,123
local L = LibStub("AceLocale-3.0"):NewLocale("Critline", "ruRU")
if not L then return end
 
-- Russian localisation by unw1s3, juline and getaddoncom
L["Add by name"] = "Добавить по имени"
L["Add by spell ID"] = "Добавлен по ИД заклинания"
L["Add target"] = "Добавить цель"
L["Alphabetically"] = "В алфавитном порядке"
L["Amount color"] = "Цвет урона"
L["Announce"] = "Сообщение"
L["Are you sure you want to reset all %s records?"] = "Вы уверены что хотите сбросить все % записи?"
L["Aura filter"] = "Фильтр Аур"
L["Basic options"] = "Базовые опции"
L["By crit record"] = "По записям критов"
L["By normal record"] = "По записям хитов"
L["Cannot add players to mob filter."] = "Невозможно добавить игрока в фильтр монстров"
L["Chat output"] = "Вывод в чат"
L["Check to enable damage events to be recorded."] = "Включите, чтобы записывать урон."
L["Check to enable healing events to be recorded."] = "Включите, чтобы записывать лечение."
L["Check to enable pet damage events to be recorded."] = "Включите, чтобы записывать урон питомца"
L["Crit"] = "Крит" -- Needs review
L["critical "] = "кртитический(ое)"
L["Critical!"] = "Критический!"
L["Critline splash frame unlocked"] = "Всплывающее окно Critline разблокировано"
L["damage"] = "урон"
L["Damage"] = "Урон"
L["Delete aura"] = "Удалить Ауру"
L["Delete mob"] = "Удалить монстра"
L["Detailed tooltip"] = "Подсказка с деталями"
L["Disable to ignore records where the target is an NPC."] = "Отключите, чтобы игнорировать записи, когда цель - НПЦ"
L["Disable to ignore records where the target is a player."] = "Отключите, чтобы игнорировать записи, когда цель - игрок"
L["Includes previous record along with \"New record\" messages."] = "Показывать предыдущие рекорды вместе с сообщениями \"Новый рекорд\""
L["Don't filter magic"] = "Не фильтровать магию"
L["Drag to move"] = "Зажмите, чтобы двигать"
L["Duration"] = "Длительность"
L["Enabled"] = "Включено."
L["Enable to ignore additional damage due to vulnerability."] = "Включить что бы игнорировать дополнительный урон из-за уязвимости."
L["Enable to ignore integrated aura filter."] = "Включить, чтобы игнорировать встроенный фильтр аур"
L["Enable to ignore integrated mob filter."] = "Включить, чтобы игнорировать встроенный фильтр монстров"
L["Enable to ignore spells that are not in your (or your pet's) spell book."] = "Включить что бы игнорировать заклинания которых нет в вашей книге заклинания(а также питомца)."
L["Enable to include rather than exclude selected spells in the spell filter."] = "Включить, чтобы учитывать выбранные заклинания в фильтре заклинаний, а не исключать их"
L["Enable to let magical damage ignore the level filter."] = "Включите что бы магический урон игнорировал фильтр уровня."
L["Enable to show icon indicators instead of text."] = "Включить что бы отображать иконку индикаторов вместо текста."
L["Enable to use scrolling combat text for \"New record\" messages instead of the default splash frame."] = "Включить, чтобы использовать прокручивающийся комбат-текст для сообщений \"Новый рекорд\" взамен стандартному всплывающему окну"
L["Enter mob name:"] = "Введите имя монстра"
L["Enter spell ID:"] = "Введите ИД заклинания:"
L["Font"] = "Шрифт"
L["Font outline"] = "Выходящий шрифт"
L["Font size"] = "Размер шрифта"
L["healing"] = "лечение"
L["Healing"] = "Лечение"
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["Ignore vulnerability"] = "Игнорировать уязвимость"
L["Invalid channel. Please enter a valid channel name or ID."] = "Неверный канал. Пожалуйста введите верное имя канала или его ИД."
L["Invalid input. Please enter a spell ID."] = "Неверный ввод. Пожалуйста введите ИД заклинания."
L["Invalid mob name."] = "Некорректное имя монстра"
L["Invalid player name."] = "Некорректное имя игрока"
L["Invalid spell ID. No such spell exists."] = "Неверное ИД заклинания. Такого заклинания не существует."
L["Invert spell filter"] = "Инвертировать фильтр заклинаний"
L["Left-click to toggle summary frame"] = "Левый клик для вкл/выкл окна статистики"
L["Level filter"] = "Фильтр уровня"
L["Locked"] = "Заблокировано"
L["Lock minimap button."] = "Заблокировать кнопку миникарты."
L["Lock summary frame."] = "Заблокировать окно статистики."
L["Minimap button"] = "Кнопка мини-карты"
L["Mob filter"] = "Фильтр монстра"
L["Move splash screen"] = "Передвинуть всплывающее окно"
L["New %s record!"] = "Новый %s рекорд!"
L["New %s%s record - %s"] = "Новая запись %s%s - %s"
L["None"] = "Нет"
L["No records"] = "Нет записей."
L["Normal"] = "Нормальный"
L["No target selected."] = "Нет цели"
L["Only known spells"] = "Только известные заклинания"
L["pet"] = "питомец"
L["Pet"] = "Питомец"
L["Plays a sound on a new record."] = "Воспроизвести мелодию при новом рекорде."
L["Play sound"] = "Воспроизвести мелодию"
L["Prints new record notifications to the chat frame."] = "Показывать уведомление о новом рекорде в окно чата"
L["Record damage"] = "Запись урона"
L["Record healing"] = "Запись лечения"
L["Record pet damage"] = "Запись урона питомца"
L["Record PvE"] = "Запись PVE"
L["Record PvP"] = "Запись PVP"
L["Reset"] = "Сбросить"
L["Reset all"] = "Сбросить все"
L["Reset all %s records."] = "Сбросить все %s записи."
L["Reset %s (%s) records."] = "Сброшено %s (%s) записей."
L["Right-click to lock"] = "Правый клик для разблокировки"
L["Right-click to open options"] = "Правый клик что бы открыть настройки"
L["%s added to aura filter."] = "% добавлен в фильтр аур"
L["%s added to mob filter."] = "%s добавлено в фильтр монстров"
L["Saves a screenshot on a new record."] = "Делать скриншот при каждом новом рекорде"
L["Scale"] = "Размер"
L["Screenshot"] = "Скриншот"
L["Sets the color for the amount text in the splash frame."] = "Устанавливает цвет для текста в окне вспышек."
L["Sets the color for the spell text in the splash frame."] = "Устанавливает цвет для текста заклинаний в окне вспышек."
L["Sets the font size of the splash frame."] = "Устанавливает размер текста для окна вспышек."
L["Sets the scale of the splash frame."] = "Установить масштаб всплывающего окна"
L["Sets the scale of the summary frame."] = "Установить масштаб окна сводки"
L["Sets the time (in seconds) the splash frame is visible before fading out."] = "Устанавливает время вспышек (в секундах) пока окно видно до исчезновения."
L["Show"] = "Показать"
L["Show icons"] = "Показать иконки"
L["Show minimap button."] = "Показать кнопку миникарты"
L["Include old record"] = "Показывать старые рекорды"
L["Shows the new record on the middle of the screen."] = "Показывать новые рекорды в центре экрана"
L["Show summary frame."] = "Показать окно статистики"
L["%s is already in aura filter."] = "% уже в фильтре аур"
L["%s is already in mob filter."] = "%s уже в фильтре монстров"
L["Spell color"] = "Цвет заклинания"
L["Spell filter"] = "Фильтр заклинаний"
L["Splash frame"] = "Всплывающее окно"
L["%s removed from aura filter."] = "% удален из фильтра аур"
L["%s removed from mob filter."] = "%s удалено из фильтра монстров"
L["Summary frame scale"] = "Масштаб окна статистики"
L["Suppress all records while mind controlled."] = "Не записывать, когда вы под действием \"Контроль над разумом\""
L["Suppress mind control"] = "Скрывать МК"
L["Thick"] = "Тонкий"
L["Tooltip sorting:"] = "Сортировка подсказок:"
L["Use combat text splash"] = "Использовать эффект текста боя"
L["Use detailed format in the summary tooltip."] = "Использовать детализированый формат статистики подсказки"
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3/reset.lua New file
0,0 → 1,115
local addonName, addon = ...
 
local L = LibStub("AceLocale-3.0"):GetLocale(addonName)
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)
 
module.history = history
 
-- 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: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]
 
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
spell[periodic][hitType] = {}
end
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()
end
 
addon.RegisterCallback(module, "PerCharSettingsLoaded", "ClearHistory")
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3/profiles.lua New file
0,0 → 1,496
local addonName, addon = ...
 
local templates = addon.templates
 
local L = {
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",
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?",
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.",
}
 
local LOCALE = GetLocale()
if LOCALE == "deDE" then
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["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["dual_profile"] = "Duales Profil"
L["enabled"] = "Aktiviere Duale Profile"
L["enabled_desc"] = "Aktiviere diese Option, um beim Talentwechsel automatisch zwischen den Profilen zu wechseln."
elseif LOCALE == "frFR" then
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["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."
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["dualspec_desc"] = "가능하면 사용합니다. 이중 특성에 의하여 다른 프로필을 선택할 수 있게 하니다. 이중 프로필은 현재 프로필과 번갈아서 특성이 변경될 때 같이 적용됩니다."
L["dual_profile"] = "이중 프로필"
L["enabled"] = "이중 프로필 사용"
L["enabled_desc"] = "특성이 변경 될때 자동으로 프로필을 변경하도록 선택합니다."
elseif LOCALE == "esES" or LOCALE == "esMX" then
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["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:"
elseif LOCALE == "zhCN" 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["dualspec_desc"] = "启时,你可以为你的双天赋设定另一组配置文件,你的双重配置文件将在你转换天赋时自动与目前使用配置文件交换。"
L["dual_profile"] = "双重配置文件"
L["enabled"] = "开启双重配置文件"
L["enabled_desc"] = "勾选以便转换天赋时自动交换配置文件。"
elseif LOCALE == "ruRU" 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["dualspec_desc"] = "Двойной профиль позволяет вам выбрать различные профили для каждой раскладки талантов. Профили будут переключаться каждый раз, когда вы переключаете раскладку талантов."
L["dual_profile"] = "Второй профиль"
L["enabled"] = "Включить двойной профиль"
L["enabled_desc"] = "Включите эту опцию для автоматического переключения между профилями при переключении раскладки талантов."
end
 
 
local defaultProfiles = {}
 
 
local function profileSort(a, b)
return a.value < b.value
end
 
local tempProfiles = {}
 
local function getProfiles(db, common, nocurrent)
local profiles = {}
 
-- copy existing profiles into the table
local currentProfile = db:GetCurrentProfile()
for _, v in ipairs(db:GetProfiles(tempProfiles)) do
if not (nocurrent and v == currentProfile) then
profiles[v] = v
end
end
 
-- add our default profiles to choose from ( or rename existing profiles)
for k, v in pairs(defaultProfiles) do
if (common or profiles[k]) and not (nocurrent and k == currentProfile) then
profiles[k] = v
end
end
 
local sortProfiles = {}
local n = 1
 
for k, v in pairs(profiles) do
sortProfiles[n] = {text = v, value = k}
n = n + 1
end
 
sort(sortProfiles, profileSort)
 
return sortProfiles
end
 
 
local function createFontString(parent)
local text = parent:CreateFontString(nil, "BACKGROUND", "GameFontHighlightSmall")
-- text:SetHeight(32)
text:SetPoint("LEFT", parent.title)
text:SetPoint("RIGHT", -32, 0)
text:SetJustifyH("LEFT")
text:SetJustifyV("TOP")
return text
end
 
 
local function profilesLoaded(self)
local db = addon[self.db]
self.db = db
 
for k, object in pairs(self.objects) do
object.db = db
self[k] = object
end
 
db.RegisterCallback(self, "OnProfileChanged")
db.RegisterCallback(self, "OnNewProfile")
db.RegisterCallback(self, "OnProfileDeleted")
 
local keys = db.keys
defaultProfiles["Default"] = L.default
defaultProfiles[keys.char] = keys.char
defaultProfiles[keys.realm] = keys.realm
defaultProfiles[keys.class] = UnitClass("player")
 
self.currProfile:SetFormattedText("Current profile: %s%s%s", NORMAL_FONT_COLOR_CODE, db:GetCurrentProfile(), FONT_COLOR_CODE_CLOSE)
 
self.choose:SetSelectedValue(db:GetCurrentProfile())
 
self.dualProfile:SetSelectedValue(db:GetDualSpecProfile())
 
local isDualSpecEnabled = db:IsDualSpecEnabled()
self.dualEnabled:SetChecked(isDualSpecEnabled)
self.dualProfile:SetDisabled(not isDualSpecEnabled)
 
self:CheckProfiles()
end
 
local function onProfileChanged(self, event, db, profile)
self.currProfile:SetFormattedText("Current profile: %s%s%s", NORMAL_FONT_COLOR_CODE, profile, FONT_COLOR_CODE_CLOSE)
self.choose:SetSelectedValue(profile)
self.dualProfile:SetSelectedValue(db:GetDualSpecProfile())
self:CheckProfiles()
end
 
local function onNewProfile(self, event, db, profile)
self:CheckProfiles()
end
 
local function onProfileDeleted(self, event, db, profile)
self:CheckProfiles()
end
 
local function checkProfiles(self)
local hasNoProfiles = self:HasNoProfiles()
self.copy:SetDisabled(hasNoProfiles)
self.delete:SetDisabled(hasNoProfiles)
end
 
local function hasNoProfiles(self)
return next(getProfiles(self.db, nil, true)) == nil
end
 
 
local function initializeDropdown(self)
for _, v in ipairs(getProfiles(self.db, self.common, self.nocurrent)) do
local info = UIDropDownMenu_CreateInfo()
info.text = v.text
info.value = v.value
info.func = self.func
info.owner = self
UIDropDownMenu_AddButton(info)
end
end
 
local function newProfileOnEnterPressed(self)
self.db:SetProfile(self:GetText())
self:SetText("")
self:ClearFocus()
end
 
local function chooseProfileOnClick(self)
self.owner.db:SetProfile(self.value)
end
 
local function enableDualProfileOnClick(self)
local checked = self:GetChecked()
self.db:SetDualSpecEnabled(checked)
self.dualProfile:SetDisabled(not checked)
end
 
local function dualProfileOnClick(self)
self.owner.db:SetDualSpecProfile(self.value)
UIDropDownMenu_SetSelectedValue(self.owner, self.value)
end
 
local function copyProfileOnClick(self)
self.owner.db:CopyProfile(self.value)
end
 
local function deleteProfileOnClick(self)
UIDropDownMenu_SetSelectedValue(self.owner, self.value)
StaticPopup_Show("CRITLINE_DELETE_PROFILE", nil, nil, {db = self.owner.db, obj = self.owner})
end
 
 
local function createProfileUI(name, db)
local frame = templates:CreateConfigFrame(name, addonName, true, true)
frame.db = db
 
frame.ProfilesLoaded = profilesLoaded
frame.OnProfileChanged = onProfileChanged
frame.OnNewProfile = onNewProfile
frame.OnProfileDeleted = onProfileDeleted
 
addon.RegisterCallback(frame, "AddonLoaded", "ProfilesLoaded")
 
frame.CheckProfiles = checkProfiles
frame.HasNoProfiles = hasNoProfiles
 
local objects = {}
frame.objects = objects
 
local reset = addon.templates:CreateButton(frame)
reset:SetSize(160, 22)
reset:SetPoint("TOPLEFT", frame.desc, "BOTTOMLEFT")
reset:SetScript("OnClick", function(self) self.db:ResetProfile() end)
reset:SetText(L.reset)
objects.reset = reset
 
local currProfile = frame:CreateFontString(nil, "BACKGROUND", "GameFontHighlightSmall")
currProfile:SetPoint("LEFT", reset, "RIGHT")
currProfile:SetJustifyH("LEFT")
currProfile:SetJustifyV("CENTER")
objects.currProfile = currProfile
 
local chooseDesc = createFontString(frame)
chooseDesc:SetHeight(32)
chooseDesc:SetPoint("TOP", reset, "BOTTOM", 0, -8)
-- chooseDesc:SetWordWrap(true)
chooseDesc:SetText(L.choose_desc)
 
local newProfile = templates:CreateEditBox(frame)
newProfile:SetWidth(160)
newProfile:SetPoint("TOPLEFT", chooseDesc, "BOTTOMLEFT", 0, -16)
newProfile:SetScript("OnEscapePressed", newProfile.ClearFocus)
newProfile:SetScript("OnEnterPressed", newProfileOnEnterPressed)
objects.newProfile = newProfile
 
local label = newProfile:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
label:SetPoint("BOTTOMLEFT", newProfile, "TOPLEFT", 0, -2)
label:SetPoint("BOTTOMRIGHT", newProfile, "TOPRIGHT", 0, -2)
label:SetJustifyH("LEFT")
label:SetHeight(18)
label:SetText(L.new)
 
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
 
do
local dualDesc = createFontString(frame)
dualDesc:SetHeight(32)
dualDesc:SetPoint("TOP", newProfile, "BOTTOM", 0, -8)
-- dualDesc:SetWordWrap(true)
dualDesc:SetText(L.dualspec_desc)
 
local enabled = CreateFrame("CheckButton", nil, frame, "OptionsBaseCheckButtonTemplate")
enabled:SetPoint("TOPLEFT", dualDesc, "BOTTOMLEFT", 0, -16)
enabled:SetPushedTextOffset(0, 0)
enabled:SetScript("OnClick", enableDualProfileOnClick)
enabled.tooltipText = L.enable_desc
local text = enabled:CreateFontString(nil, nil, "GameFontHighlight")
text:SetPoint("LEFT", enabled, "RIGHT", 0, 1)
text:SetText(L.enabled)
objects.dualEnabled = enabled
 
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
 
enabled.dualProfile = dualProfile
end
 
local copyDesc = createFontString(frame)
copyDesc:SetHeight(32)
copyDesc:SetPoint("TOP", objects.dualEnabled, "BOTTOM", 0, -8)
copyDesc:SetWordWrap(true)
copyDesc:SetText(L.copy_desc)
 
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
 
local deleteDesc = createFontString(frame)
deleteDesc:SetHeight(32)
deleteDesc:SetPoint("TOP", copy, "BOTTOM", 0, -8)
deleteDesc:SetWordWrap(true)
deleteDesc:SetText(L.delete_desc)
 
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
 
return frame
end
 
 
StaticPopupDialogs["CRITLINE_DELETE_PROFILE"] = {
text = L.delete_confirm,
button1 = ACCEPT,
button2 = CANCEL,
OnAccept = function(self, data)
local delete = data.obj
self.data.db:DeleteProfile(delete:GetSelectedValue())
delete:SetSelectedValue(nil)
end,
OnCancel = function(self, data)
data.obj:SetSelectedValue(nil)
end,
whileDead = true,
timeout = 0,
}
 
 
local profiles = createProfileUI("Profiles", "db")
profiles.desc:SetText("This profile controls all settings that are not related to individual trees or their records.")
 
local spellProfiles = createProfileUI("Spell profiles", "percharDB")
spellProfiles.desc:SetText("This profile stores individual tree settings, including which trees will be registered, and spell records.")
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3/Broker.lua New file
0,0 → 1,79
local addonName, addon = ...
 
local LDB = LibStub("LibDataBroker-1.1")
local L = LibStub("AceLocale-3.0"):GetLocale(addonName)
 
 
local feeds = {
dmg = L["Damage"],
heal = L["Healing"],
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",
label = v,
icon = addon.icons[k],
OnClick = function()
if IsShiftKeyDown() then
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,
OnTooltipShow = function()
addon:ShowTooltip(k)
end
})
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
 
 
local function onTreeStateChanged(event, tree, enabled)
if enabled then
updateRecords(event, tree)
else
feeds[tree].text = L["n/a"]
end
end
 
 
local function addonLoaded()
addon.RegisterCallback(feeds, "OnNewTopRecord", updateRecords)
addon.RegisterCallback(feeds, "OnTreeStateChanged", onTreeStateChanged)
end
 
addon.RegisterCallback(feeds, "AddonLoaded", addonLoaded)
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3/changeLog.txt New file
0,0 → 1,286
4.0.3
- Updated for WoW 4.1.
- Added Sweeping Strikes and Aimed Shot! exceptions.
- Removed pre-4.1 compability code.
 
4.0.2
- Hunter traps will now properly display tooltip records.
- "Include old record" moved from splash module to core. Applies to both chat frame and splash messages.
- Second attempt at including old record in chat frame output.
- Made critical record message in the chat frame slightly more distinguishable from a non crit.
- Do not show pet auto attack records in the regular auto attack tooltip.
 
4.0.1
- Records printed to the chat frame can now be shortened, and can once again include the previous record.
- Added Aerial Command Unit back to mob filter. Not eligible for aura tracking.
- Splash frame records can now be shortened when no previous record is included in the message.
- Do not try to load AceDBOptions since we're not using it.
- You can now specify the sound effect played on new records. (LibSharedMedia)
- Pet auto attack records are now displayed on the pet attack action tooltip.
- Added Hellscream's Warsong, Strength of Wrynn, Singed and Chemical Cloud to aura filters.
 
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,
Invocation of Flame, Red Mist, Ragezone and Frothing Rage to aura filter.
 
3.1.2
- Clicking the Announce button rather than pressing enter should now properly announce.
- Added support for LibSharedMedia for selecting splash font.
- Disabled adding spells to filter from the spell book to avoid tainting.
- Removed leftover debugging for when moving the display frame.
 
3.1.1
- Fixed adding spells to filter from the spell book.
- Fixed display moving on login when using a scale other than 100%.
 
3.1.0
- Updated for 4.0.
- Disabled adding auras to the aura filter by clicking for now.
 
3.0.2
- Updated German localisation.
- Added Russian localisation.
- The announce spell list will now properly be refreshed after resetting records.
- Updated Ace3 libraries. (fixes occasional error upon deleting profiles if you didn't already have updated libraries)
- Fixed tooltip sorting option.
 
3.0.1
- DoT/HoT suffix is now properly hidden in the tooltip when no direct entry of the same spell exists.
- Fixed some errors related to dual spec.
- Fixed adding mobs to filter.
- Added hack for direct spells being treated as periodic, and thus getting filtered incorrectly. (so far only the Divine Storm heal)
- Fixed error that could, under certain circumstances, occur when you gained or lost a (de)buff.
 
3.0.0
* Rewrote a lot of code. Modules are now actually independent of each other, and can be removed as desired.
* Options are now split up more, in their respective module's category.
* Now using AceDB-3.0 for saved variables management. See addon description for more info.
* Also implemented LibDualSpec. You can have profiles automatically change upon respeccing.
* Unfortunately, the first time you login on a character that has data from a previous version, all general settings will be reset.
* It is recommended that you delete your old Critline folder, as the folder structure has changed significantly.
- Added aura filter UI. You can now add custom auras by providing spell ID or shift-clicking a buff or debuff with the UI open.
- Improved aura filter. Will now try to filter spells that were cast with a special aura, but ticks after you lost it.
- Removed the ability to specify custom tooltip formats. Was just a lot of trouble for little to no gain.
- The trees in the standalone frame can no longer be explicitly set, and are instead tied to whether they are being registered.
- Added an option to to show a text label instead of an icon in the display frame.
- Added option to only register spells from the spell book. (eg no items, procs, vehicles etc)
- The font style of the splash frame can now be changed.
- Fixed rare bug where spells would not be properly alphabetically sorted.
- Added option to disregard of additional damage due to vulnerability.
- Added support for Ebon Gargoyle attacks.
- Water Elemental attacks will now be registered when using the Glyph of Eternal Water.
- Added Essence of the Blood Queen (Blood Queen Lana'thel) to aura filter.
- Added Storm Power from 25 man Hodir to aura filter.
 
2.3.1
- Added Phantom Hallucination to mob filter.
- Added Gastric Bloat (Festergut) to aura filter.
- Scaling the summary frame or the splash frame will no longer relocate them.
- Filtered auras are now tracked more accurately. (spell ID is available where it previously was not)
- You can now reset the standalone display's position with '/cl reset'.
 
2.3.0
- Added Spanish localisation by Vladixlaus.
- Added Aerial Command Unit, Fjola Lightbane, Eydis Darkbane and Icehowl to mob filter.
- Reset scroll frame should no longer misbehave after deleting filtered records.
- Modified some text colors in the detailed summary view to make it more readable.
- Removed some legacy code. Data from versions <2.1.0 will no longer be converted.
- Shift clicking the Broker plugin will now announce the highest record in that tree.
- ToC bump for 3.3.
 
2.2.2
- Added Parrot SCT support.
- You can now choose to print record notifications to the chat frame.
 
2.2.1
- 'Show old record' setting will now stick between sessions.
 
2.2.0
- Updated for patch 3.2.
- Added German localisation by Destoxillo.
- Changed spell ID for Burning Adrenaline, again. (should've worked the first time...)
- Added option to display previous record with "New record" messages.
 
2.1.2
- Should no longer register unwanted quest pet records when you have your regular pet summoned.
- Added Overwhelming Power (hard mode Assembly of Iron, normal and heroic) to aura filter.
 
2.1.1
- Fixed scaling settings not being remembered between sessions.
 
2.1.0
- Spells with a direct and a periodic effect will now be stored separately.
- As a result of the above, database structure has changed slightly.
- Spells that has no crit record will now only display its normal record in the summary.
- Periodic spells will have DoT/HoT appended to its name in the summary tooltip only if the non periodic effect is visible, as well.
- Now using combat log unit flags for new possibilities!
- Record PvP no longer needs to be enabled to track healing done to players.
- Removed "Ignore self damage" option. Damage done to any friendly unit is no longer tracked.
- Healing done to hostile units is no longer tracked.
- Healing now ignores the level filter.
- DAMAGE_SHIELD type effects (Thorns, Retribution Aura etc) are no longer tracked.
- Hopefully fixed custom tooltip formatting.
- Added Fury of the Storm, Speed of Invention, Fortitude of Frost and Resilience of Nature from the Yogg-Saron encounter to aura filter.
- Added Shadow Crash (General Vezax) to aura filter. (untested)
- Added options to ignore integrated mob and aura filters respectively. (off by default)
- Record sorting dropdown menu should no longer be empty on login.
- Added Metanoia (Valkyrion Aspirant) to aura filter.
- Improved pet tracking. Should now only register your class pets.
- Merged the invert options for each tree into one.
- Lots of small performance and memory improvements.
 
2.0.2
- Added Potent Pheromones (Freya) to aura filter.
- Added Unstable Sun Beam (Elder Brightleaf) to aura filter.
- Added Death Knight Understudy to mob filter.
- Added Storm Power (Hodir) to aura filter.
 
2.0.1
- Fixed combat log event handler. Records should now be recorded again.
- Fixed slash command.
 
2.0.0
* Renamed simply Critline with new version numbering. (hopefully for the last time!)
- Mobs can now be added by name, in addition to by target. (case insensitive)
- Added Heart of the Deconstructor to mob filter.
- Added Rune of Power (Assembly of Iron) to aura filter.
- Actually register UNIT_ENTERED/EXITED_VEHICLE events...
 
r16
- All XML code rewritten in Lua. End user shouldn't notice any significant difference.
- Changed database formats for better readability. Records and filters are kept, but other settings are reset.
- Tooltip format for detailed summary can now be customised to your liking. See advanced options for details.
- Main GUI is now draggable by right mouse button at the text area. Left clicking the icon will open options, right click hides the button.
- Added sorting option for summary tooltip. (alphabetically/crit/normal)
- Changed default detailed tooltip format.
- Added Might of Mograine (death knight story line) to aura filter.
- Various cosmetic changes.
 
r15
- Fixed an error caused by an unintentionally added database entry. Broken databases will be repaired.
- Removed some deprecated database entries that will never be used. (date and count)
 
r14
- Records in the tooltip is now sorted by crit amount > normal amount > spell name.
- Added an option to use scrolling combat text instead of the default splash frame. Currently supports Mik SBT and SCT in addition to Blizzard's.
- 'Move splash frame' is now a regular button rather than a check button.
- Now using another (hopefully correct) spell ID for Burning Adrenaline.
 
r13
- Toggling standalone display via the minimap button is now permanent.
- Dragging the minimap button should now function properly.
- Added a single letter indicator for the Broker feeds.
 
r12
- Added option to ignore self inflicted damage. (off by default)
- Fixed critical strike text error that occured on certain locales.
- Added Blessing of the Crusade (Icecrown quest) to aura filter.
 
r11
- Fixed 'Move splash frame' option.
- Added Iron's Bane (Storm Peaks quest) to aura filter.
 
r10
- Implemented LibDataBroker feed, which replaces Titan and FuBar plugins. FuBar users need install Broker2FuBar.
- Fixed standalone display scaling not being saved between sessions.
- Fixed the "Play sound" option.
- Attacks when in a vehicle should no longer be recorded in the pet section.
 
r9
- FuBar plugins should now properly use the new icons.
- An attempt at using mob IDs instead of names for the default mob filter. No need for localisations anymore.
- Announce and reset check buttons will now reset when leaving the respective view.
- Added Aura of Anger and Aura of Desire (Reliquary of Souls) to aura filter.
- Added Shade of Akama to mob filter.
- Added Energy Infusion and Energy Feedback (Vexallus) to aura filter.
- Mob filter list should now work properly when scrollable.
- Code cleanup.
 
r8
- Fixed FuBar error.
- Fixed minimap icon.
- Removed more legacy code.
 
r7
- Moved the options to Blizzard's interface panel and redesigned it slightly.
- Non existant entries now won't be added to the tooltip when using detailed view.
- Added Malygos' Power Spark and Potent Fungus (Amanitar) to aura filters.
- Removed some legacy code.
- Added options to record PvE/PvP and removed the old "PvP only" option.
- Added option to let magical damage ignore level adjustment.
- New (non custom made) icons.
 
r6
- Implemented (so far very simple) special (de)buff filtering.
- Magical damage will yet again take level adjustment into consideration.
 
r5
- Fixed occasional error upon zoning.
 
r4
- Fixed level adjustment filter issues.
- You can now set level adjustment to 0.
- Default filtering is now in place for mobs that mostly receives extra damage.
- Addition and removal of filtered mobs is now notified in the chat frame.
 
r3
- Splash frame will now be cleared before every record splash.
- Mob filter list should no longer error when scrolled.
- Polished some code.
 
r2
- Hopefully fixed FuBar plugins.
- Splash screen spell text colour is now correctly yellow by default.
- Fixed splash screen text colour picker.
 
r1
- Updated for 3.0.
- Level adjustment should now work properly.
- Level adjustment will now only apply to physical damage.
- When displaying all three types, the display frames should now not appear on top of each other on login.
- Splash screen should now behave correctly when using inverted filters.
- Buttons in the Reset records scroll pane now should not become unchecked when you scroll it.
- Self healing spells on your pet will no longer be recorded. (Prayer of Mending, Lifebloom etc)
- Fixed the Titan Panel plugins' right click menu.
- Cleaned up some code.
 
v5.0
- Renamed "Titan [Critline]" to "Critline"
- Redesigned from the ground up to be a stand-alone mod, that supports Titan Panel.
- Split out the Summary to show Damage, Healing and Pet on separate summary screens.
- Added the option for Detailed or Simple summary display.
- Code is now broken down into function sections. makes maintenance and feature development much easier.
\ No newline at end of file Property changes : Added: svn:eol-style + native Added: svn:keywords + Author Date Id Rev URL
tags/4.0.3/display.lua New file
0,0 → 1,352
local addonName, addon = ...
 
local L = LibStub("AceLocale-3.0"):GetLocale(addonName)
local templates = addon.templates
 
local width, height = 128, 22
 
local trees = {
dmg = L["Damage"],
heal = L["Healing"],
pet = L["Pet"],
}
 
 
local function onDragStart(self)
self.owner:StartMoving()
end
 
local function onDragStop(self)
local owner = self.owner
owner:StopMovingOrSizing()
local pos = owner.profile.pos
pos.point, pos.x, pos.y = select(3, owner:GetPoint())
end
 
local function onEnter(self)
GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT")
addon:ShowTooltip(self.tree)
if not self.owner.profile.locked then
GameTooltip:AddLine(" ")
GameTooltip:AddLine(L["Drag to move"], GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b)
end
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)
frame:SetScript("OnLeave", GameTooltip_Hide)
 
frame.owner = parent
 
local text = frame:CreateFontString(nil, nil, "GameFontHighlightSmall")
text:SetPoint("CENTER", frame, "RIGHT", -50, 0)
frame.text = text
 
local icon = frame:CreateTexture(nil, "OVERLAY")
icon:SetSize(20, 20)
icon:SetPoint("LEFT", 2, 0)
frame.icon = icon
 
local label = frame:CreateFontString(nil, nil, "GameFontHighlightSmall")
label:SetPoint("LEFT", 4, 0)
frame.label = label
 
return frame
end
 
 
local display = CreateFrame("Frame", nil, UIParent)
addon.display = display
display:SetMovable(true)
display:SetBackdrop({
edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]],
edgeSize = 12,
})
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])
frame.label:SetText(treeName..":")
frame.tree = k
display.trees[k] = frame
end
 
display.trees.dmg:SetPoint("TOP", 0, -4)
 
 
local config = templates:CreateConfigFrame("Display", addonName, true)
 
local options = {
db = {},
{
text = L["Show"],
tooltipText = L["Show summary frame."],
setting = "show",
func = function(self)
display:UpdateLayout()
end,
},
{
text = L["Locked"],
tooltipText = L["Lock summary frame."],
setting = "locked",
func = function(self)
local btn = not self:GetChecked() and "LeftButton"
for _, tree in pairs(display.trees) do
tree:RegisterForDrag(btn)
end
end,
},
{
text = L["Show icons"],
tooltipText = L["Enable to show icon indicators instead of text."],
setting = "icons",
func = function(self)
local checked = self:GetChecked()
width = checked and 128 or 152
height = checked and 22 or 16
for _, tree in pairs(display.trees) do
if checked then
tree.icon:Show()
tree.label:Hide()
else
tree.icon:Hide()
tree.label:Show()
end
tree:SetHeight(height)
end
display:UpdateLayout()
end,
},
}
 
for i, v in ipairs(options) do
local btn = templates:CreateCheckButton(config, v)
if i == 1 then
btn:SetPoint("TOPLEFT", config.title, "BOTTOMLEFT", -2, -16)
else
btn:SetPoint("TOP", options[i - 1], "BOTTOM", 0, -8)
end
btn.module = display
local btns = options[btn.db]
btns[#btns + 1] = btn
options[i] = btn
end
 
 
local sliders = {}
 
sliders[1] = templates:CreateSlider(config, {
text = L["Scale"],
tooltipText = L["Sets the scale of the display."],
minValue = 0.5,
maxValue = 2,
valueStep = 0.05,
minText = "50%",
maxText = "200%",
func = function(self)
local value = self:GetValue()
self.value:SetFormattedText("%.0f%%", value * 100)
local os = display:GetScale()
display:SetScale(value)
local point, relativeTo, relativePoint, xOffset, yOffset = display:GetPoint()
display:SetPoint(point, relativeTo, relativePoint, (xOffset * os / value), (yOffset * os / value))
display.profile.scale = value
end,
})
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, "OnNewTopRecord", "UpdateRecords")
end
 
addon.RegisterCallback(display, "AddonLoaded")
 
 
function display:SettingsLoaded()
self.profile = self.db.profile
 
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()
self:SetPoint(pos.point, pos.x, pos.y)
 
local scale = self.profile.scale
-- need to set scale separately first to ensure proper behaviour in scale-friendly repositioning
self:SetScale(scale)
sliders[1]:SetValue(scale)
 
sliders[2]:SetValue(self.profile.alpha)
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
 
 
function display:UpdateTree(tree)
if addon.percharDB.profile[tree] then
self.trees[tree]:Show()
else
self.trees[tree]:Hide()
end
self:UpdateLayout()
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
local dmg = trees.dmg
local heal = trees.heal
local pet = trees.pet
 
if heal:IsShown() then
if dmg:IsShown() then
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", 0, -2)
elseif dmg:IsShown() then
pet:SetPoint("TOP", dmg, "BOTTOM", 0, -2)
else
pet:SetPoint("TOP", 0, -4)
end
end
 
local n = 0
 
if dmg:IsShown() then
n = n + 1
end
if heal:IsShown() then
n = n + 1
end
if pet:IsShown() then
n = n + 1
end
 
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 or not self.profile.show then
self:Hide()
else
self:Show()
end
end
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3/announce.lua New file
0,0 → 1,81
local addonName, addon = ...
 
local L = LibStub("AceLocale-3.0"):GetLocale(addonName)
local templates = addon.templates
 
 
local module = templates:CreateList("CritlineAnnounce", L["Announce"], "Announce")
 
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
end
 
local channels = {
"SAY",
"GUILD",
"PARTY",
"RAID",
"WHISPER",
"CHANNEL",
}
 
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
 
 
local announceFormat = format("%%s - %s: %%s %s: %%s", L["Normal"], L["Crit"])
 
function module:Announce(data)
local channel = self.channel:GetSelectedValue()
local target = self.target:GetText():trim()
 
if channel == "WHISPER" then
if target == "" then
addon:Message(L["Invalid player name."])
return
end
elseif channel == "CHANNEL" then
target = GetChannelName(target)
if target == 0 then
addon:Message(L["Invalid channel. Please enter a valid channel name or ID."])
return
end
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
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3/core.lua New file
0,0 → 1,1290
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 LSM = LibStub("LibSharedMedia-3.0")
local templates = Critline.templates
local playerClass = select(2, UnitClass("player"))
local debugging
 
-- auto attack spell
local AUTOATK_ID = 6603
local AUTOATK = GetSpellInfo(AUTOATK_ID)
 
-- local references to commonly used functions and variables for faster access
local HasPetUI = HasPetUI
local tonumber = tonumber
local CombatLog_Object_IsA = CombatLog_Object_IsA
local bor = bit.bor
local band = bit.band
 
local COMBATLOG_FILTER_MINE = COMBATLOG_FILTER_MINE
local COMBATLOG_FILTER_MY_PET = COMBATLOG_FILTER_MY_PET
local COMBATLOG_OBJECT_REACTION_FRIENDLY = COMBATLOG_OBJECT_REACTION_FRIENDLY
local COMBATLOG_OBJECT_REACTION_HOSTILE = COMBATLOG_OBJECT_REACTION_HOSTILE
local COMBATLOG_OBJECT_CONTROL_PLAYER = COMBATLOG_OBJECT_CONTROL_PLAYER
local COMBATLOG_OBJECT_TYPE_GUARDIAN = COMBATLOG_OBJECT_TYPE_GUARDIAN
 
 
local treeNames = {
dmg = L["damage"],
heal = L["healing"],
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]],
}
 
 
-- non hunter pets whose damage we may want to register
local classPets = {
[510] = true, -- Water Elemental
[11859] = true, -- Doomguard
[15438] = true, -- Greater Fire Elemental
[27829] = true, -- Ebon Gargoyle
[29264] = true, -- Spirit Wolf
[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 = {
[26654] = 12723, -- Sweeping Strikes (Whirlwind)
[27285] = 27243, -- Seed of Corruption (direct)
[33778] = 33763, -- Lifebloom (direct)
[44461] = 44457, -- Living Bomb (direct)
[47960] = 47897, -- Shadowflame (tick)
[81170] = 6785, -- Ravage (Stampede)
[82928] = 19434, -- Aimed Shot (Master Marksman)
[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
[12328] = 12723, -- Sweeping Strikes
[13795] = 13797, -- Immolation Trap
[13813] = 13812, -- Explosive Trap
[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
[82939] = 13812, -- Explosive Trap (trap launcher)
[82945] = 13797, -- Immolation Trap (trap launcher)
[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_ID, AUTOATK, amount, resisted, critical, school
end
 
local spellDamage = function(spellID, spellName, _, amount, _, school, resisted, _, _, critical)
return spellID, spellName, amount, resisted, critical, school
end
 
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_DAMAGE = spellDamage,
SPELL_PERIODIC_DAMAGE = spellDamage,
SPELL_HEAL = healing,
SPELL_PERIODIC_HEAL = healing,
SPELL_AURA_APPLIED = absorb,
SPELL_AURA_REFRESH = absorb,
}
 
 
-- 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 a.periodic < b.periodic
else
return a.spellID < b.spellID
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 too, sort by name instead
return alpha(a, b)
else
return critA > critB
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,
}
 
 
local callbacks = LibStub("CallbackHandler-1.0"):New(Critline)
Critline.callbacks = callbacks
 
 
-- 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 = {}}
 
 
LSM:Register("sound", "Level up", [[Sound\Interface\LevelUp.ogg]])
 
 
-- tooltip for level scanning
local tooltip = CreateFrame("GameTooltip", "CritlineTooltip", nil, "GameTooltipTemplate")
 
 
Critline.eventFrame = CreateFrame("Frame")
function Critline:RegisterEvent(event)
self.eventFrame:RegisterEvent(event)
end
function Critline:UnregisterEvent(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, ...)
end)
 
 
local config = templates:CreateConfigFrame(addonName, nil, true)
 
 
do
local options = {}
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)
end
end
 
local checkButtons = {
db = {},
percharDB = {},
{
text = L["Record damage"],
tooltipText = L["Check to enable damage events to be recorded."],
setting = "dmg",
perchar = true,
func = toggleTree,
},
{
text = L["Record healing"],
tooltipText = L["Check to enable healing events to be recorded."],
setting = "heal",
perchar = true,
func = toggleTree,
},
{
text = L["Record pet damage"],
tooltipText = L["Check to enable pet damage events to be recorded."],
setting = "pet",
perchar = true,
func = toggleTree,
},
{
text = L["Record PvE"],
tooltipText = L["Disable to ignore records where the target is an NPC."],
setting = "PvE",
gap = 16,
},
{
text = L["Record PvP"],
tooltipText = L["Disable to ignore records where the target is a player."],
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"],
tooltipText = L["Plays a sound on a new record."],
setting = "playSound",
func = function(self) options.sound:SetDisabled(not self:GetChecked()) end,
},
{
text = L["Screenshot"],
tooltipText = L["Saves a screenshot on a new record."],
setting = "screenshot",
gap = 48,
},
{
text = L["Include old record"],
tooltipText = L["Includes previous record along with \"New record\" messages."],
setting = "oldRecord",
},
{
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,
},
}
 
options.checkButtons = checkButtons
 
for i, v in ipairs(checkButtons) do
local btn = templates:CreateCheckButton(config, v)
if i == 1 then
btn:SetPoint("TOPLEFT", config.title, "BOTTOMLEFT", -2, -16)
elseif v.newColumn then
btn:SetPoint("TOPLEFT", config.title, "BOTTOM", 0, -16)
else
btn:SetPoint("TOP", checkButtons[i - 1], "BOTTOM", 0, -(v.gap or 8))
end
btn.module = Critline
local btns = checkButtons[btn.db]
btns[#btns + 1] = btn
options[v.setting] = btn
checkButtons[i] = btn
end
 
local function onClick(self)
self.owner:SetSelectedValue(self.value)
Critline.db.profile.sound = self.value
PlaySoundFile(LSM:Fetch("sound", self.value))
end
 
local sound = templates:CreateDropDownMenu("CritlineSoundEffect", config)
sound:SetFrameWidth(160)
sound:SetPoint("TOPLEFT", options.playSound, "BOTTOMLEFT", -15, -8)
sound.initialize = function(self)
for _, v in ipairs(LSM:List("sound")) do
local info = UIDropDownMenu_CreateInfo()
info.text = v
info.func = onClick
info.owner = self
UIDropDownMenu_AddButton(info)
end
end
options.sound = sound
 
-- summary sort dropdown
local menu = {
{
text = L["Alphabetically"],
value = "alpha",
},
{
text = L["By normal record"],
value = "normal",
},
{
text = L["By crit record"],
value = "crit",
},
}
 
local sorting = templates:CreateDropDownMenu("CritlineTooltipSorting", config, menu)
sorting:SetFrameWidth(160)
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()
local slashCmdHandler = Critline.SlashCmdHandlers[msg]
if slashCmdHandler then
slashCmdHandler()
else
Critline:OpenConfig()
end
end
 
SLASH_CRITLINE1 = "/critline"
SLASH_CRITLINE2 = "/cl"
 
 
local defaults = {
profile = {
PvE = true,
PvP = true,
ignoreVulnerability = true,
chatOutput = false,
playSound = true,
sound = "Level up",
screenshot = false,
oldRecord = false,
shortFormat = false,
spellTooltips = false,
detailedTooltip = false,
tooltipSort = "normal",
},
}
 
 
-- which trees are enabled by default for a given class
local treeDefaults = {
DEATHKNIGHT = {dmg = true, heal = false, pet = false},
DRUID = {dmg = true, heal = true, pet = false},
HUNTER = {dmg = true, heal = false, pet = true},
MAGE = {dmg = true, heal = false, pet = false},
PALADIN = {dmg = true, heal = true, pet = false},
PRIEST = {dmg = true, heal = true, pet = false},
ROGUE = {dmg = true, heal = false, pet = false},
SHAMAN = {dmg = true, heal = true, pet = false},
WARLOCK = {dmg = true, heal = false, pet = true},
WARRIOR = {dmg = true, heal = false, pet = false},
}
 
function Critline:ADDON_LOADED(addon)
if addon == addonName then
local AceDB = LibStub("AceDB-3.0")
local db = AceDB:New("CritlineDB", defaults, nil)
self.db = db
 
local percharDefaults = {
profile = treeDefaults[playerClass],
}
 
percharDefaults.profile.spells = {
dmg = {},
heal = {},
pet = {},
}
 
local percharDB = AceDB:New("CritlinePerCharDB", percharDefaults)
self.percharDB = percharDB
 
-- dual spec support
local LibDualSpec = LibStub("LibDualSpec-1.0")
LibDualSpec:EnhanceDatabase(self.db, addonName)
LibDualSpec:EnhanceDatabase(self.percharDB, addonName)
 
db.RegisterCallback(self, "OnProfileChanged", "LoadSettings")
db.RegisterCallback(self, "OnProfileCopied", "LoadSettings")
db.RegisterCallback(self, "OnProfileReset", "LoadSettings")
 
percharDB.RegisterCallback(self, "OnProfileChanged", "LoadPerCharSettings")
percharDB.RegisterCallback(self, "OnProfileCopied", "LoadPerCharSettings")
percharDB.RegisterCallback(self, "OnProfileReset", "LoadPerCharSettings")
 
self:UnregisterEvent("ADDON_LOADED")
callbacks:Fire("AddonLoaded")
 
self:LoadSettings()
self:LoadPerCharSettings()
 
self.ADDON_LOADED = nil
end
end
 
 
-- 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
 
 
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 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
self:Debug("nil source/dest")
return
end
 
local isPet
 
-- if sourceGUID is not us or our pet, we leave
if not CombatLog_Object_IsA(sourceFlags, COMBATLOG_FILTER_MINE) then
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(7, 10), 16)]) then
isPet = true
-- self:Debug(format("This is my pet (%s)", sourceName))
else
-- self:Debug("This is not me, my trap or my pet; return.")
return
end
else
-- self:Debug(format("This is me or my trap (%s)", sourceName))
end
 
if not combatEvents[eventType] then
return
end
 
local isPeriodic
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 == "SPELL_PERIODIC_DAMAGE" or eventType == "SPELL_PERIODIC_HEAL" then
isPeriodic = true
periodic = 2
end
 
-- get the relevants arguments
local spellID, spellName, amount, resisted, critical, school = combatEvents[eventType](...)
 
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(spellID, periodic)))
return
end
 
local tree = "dmg"
 
if isPet then
tree = "pet"
elseif isHeal then
tree = "heal"
end
 
local targetLevel = self:GetLevelFromGUID(destGUID)
local passed, isFiltered
if self.filters then
passed, isFiltered = self.filters:SpellPassesFilters(tree, spellName, spellID, isPeriodic, destGUID, destName, school, targetLevel)
if not passed then
return
end
end
 
local isPlayer = band(destFlags, COMBATLOG_OBJECT_CONTROL_PLAYER) ~= 0
local friendlyFire = band(destFlags, COMBATLOG_OBJECT_REACTION_FRIENDLY) ~= 0
local hostileTarget = band(destFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) ~= 0
 
if not (isPlayer or self.db.profile.PvE or isHeal) then
self:Debug(format("Target (%s) is an NPC and PvE damage is not registered.", destName))
return
end
 
if isPlayer and not (self.db.profile.PvP or isHeal or friendlyFire) then
self:Debug(format("Target (%s) is a player and PvP damage is not registered.", destName))
return
end
 
-- ignore damage done to friendly targets
if friendlyFire and not isHeal then
self:Debug(format("Friendly fire (%s, %s).", spellName, destName))
return
end
 
-- ignore healing done to hostile targets
if hostileTarget and isHeal then
self:Debug(format("Healing hostile target (%s, %s).", spellName, destName))
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))
return
end
 
-- ignore vulnerability damage if necessary
if self.db.profile.ignoreVulnerability and resisted and resisted < 0 then
amount = amount + resisted
self:Debug(format("%d vulnerability damage ignored for a real value of %d.", abs(resisted), amount))
end
 
local hitType = critical and "crit" or "normal"
local data = self:GetSpellInfo(tree, spellID, periodic)
local arrayData
 
-- create spell database entries as required
if not data then
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
self:NewRecord(tree, spellID, periodic, amount, critical, data, isFiltered)
 
if not isFiltered then
-- update the highest record if needed
local topRecords = topRecords[tree]
if amount > topRecords[hitType] then
topRecords[hitType] = amount
callbacks:Fire("OnNewTopRecord", tree)
end
end
 
data.amount = amount
data.target = destName
data.targetLevel = targetLevel
data.isPvPTarget = isPlayer
 
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)
end
end
 
 
function Critline:Debug(msg)
if debugging then
DEFAULT_CHAT_FRAME:AddMessage("|cff56a3ffCritlineDebug:|r "..msg)
end
end
 
 
function Critline:ToggleDebug()
debugging = not debugging
self:Message("Debugging "..(debugging and "enabled" or "disabled"))
end
 
 
function Critline:OpenConfig()
InterfaceOptionsFrame_OpenToCategory(config)
end
 
 
function Critline:LoadSettings()
callbacks:Fire("SettingsLoaded")
 
local options = self.options
 
for _, btn in ipairs(options.checkButtons.db) do
btn:LoadSetting()
end
 
options.sound:SetSelectedValue(self.db.profile.sound)
options.tooltipSort:SetSelectedValue(self.db.profile.tooltipSort)
end
 
 
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
btn:LoadSetting()
end
end
 
 
function Critline:NewRecord(tree, spellID, periodic, amount, critical, prevRecord, isFiltered)
callbacks:Fire("NewRecord", tree, spellID, periodic, amount, critical, prevRecord, isFiltered)
 
if isFiltered then
return
end
 
amount = self:ShortenNumber(amount)
 
if self.db.profile.oldRecord and prevRecord.amount > 0 then
amount = format("%s (%s)", amount, self:ShortenNumber(prevRecord.amount))
end
 
if self.db.profile.chatOutput then
self:Message(format(L["New %s%s record - %s"], critical and "|cffff0000"..L["critical "].."|r" or "", self:GetFullSpellName(spellID, periodic, true), amount))
end
 
if self.db.profile.playSound then
PlaySoundFile(LSM:Fetch("sound", self.db.profile.sound))
end
 
if self.db.profile.screenshot then
TakeScreenshot()
end
end
 
 
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: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
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
suffix = format(" (%s)", PVP)
end
return format("%s%s", spell.target, suffix)
end
 
 
-- retrieves the top, non filtered record amounts and spell names for a given 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
 
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
end
end
local topRecords = topRecords[tree]
topRecords.normal = normalRecord
topRecords.crit = critRecord
 
callbacks:Fire("OnNewTopRecord", tree)
end
 
 
-- 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]
 
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, 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
doTooltipUpdate[tree] = true
callbacks:Fire("SpellsChanged", tree)
else
for k in pairs(tooltips) do
self:UpdateSpells(k)
end
end
end
 
 
-- this "fires" when a new record has been registered
function Critline:UpdateRecords(tree, isFiltered)
if tree then
doTooltipUpdate[tree] = true
callbacks:Fire("RecordsChanged", tree, isFiltered)
else
for k in pairs(tooltips) do
self:UpdateRecords(k, isFiltered)
end
end
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)
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
GameTooltip:Show()
end
 
 
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 hitTypes = {
normal = L["Normal"],
crit = L["Crit"],
}
 
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)
local spell = Critline.percharDB.profile.spells[k][spellID]
if not spell then
return
end
local direct = spell[1]
local tick = spell[2]
if Critline.filters then
direct = direct and not direct.filtered and direct
tick = tick and not tick.filtered and tick
end
return direct, tick
end
end
 
local function addLine(header, nonTick, tick)
if header then
GameTooltip:AddLine(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
 
local spellName, rank, spellID = self:GetSpell()
 
local indirectSpell = indirectSpells[spellID]
local indirectHeal = indirectHeals[spellID]
 
local dmg1, dmg2 = funcset.dmg(indirectSpell or spellID)
local dmg = dmg1 or dmg2
 
local heal1, heal2 = funcset.heal(indirectHeal or indirectSpell or spellID)
local heal = heal1 or heal2
 
-- ignore pet auto attack records here, since that's handled by another function
local pet1, pet2 = spellID ~= AUTOATK_ID and funcset.pet(indirectSpell or spellID)
local pet = pet1 or pet2
 
if dmg or heal or pet then
self:AddLine(" ")
end
 
if dmg then
addLine((heal or pet) and L["Damage"], dmg1, dmg2)
end
 
if heal then
if dmg then
GameTooltip:AddLine(" ")
end
addLine((dmg or pet) and L["Healing"], heal1, heal2)
end
 
if pet then
if dmg or heal then
GameTooltip:AddLine(" ")
end
addLine((dmg or heal) and L["Pet"], pet1, pet2)
end
end)
 
 
GameTooltip:HookScript("OnTooltipCleared", function(self)
self.Critline = nil
end)
 
hooksecurefunc(GameTooltip, "SetPetAction", function(self, action)
if not Critline.db.profile.spellTooltips then
return
end
 
if GetPetActionInfo(action) == "PET_ACTION_ATTACK" then
addLine(" ", (funcset.pet(AUTOATK_ID)))
self:Show()
end
end)
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3/filters.lua New file
0,0 → 1,1380
local addonName, addon = ...
 
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
 
-- mobs whose received hits won't be tracked due to various vulnerabilities
local specialMobs = {
[12460] = true, -- Death Talon Wyrmguard
[12461] = true, -- Death Talon Overseer
[14020] = true, -- Chromaggus
[15339] = true, -- Ossirian the Unscarred
[15928] = true, -- Thaddius
[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
[38567] = true, -- Phantom Hallucination
[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
}
 
-- auras that when gained will suppress record tracking
local specialAuras = {
[18173] = true, -- Burning Adrenaline (Vaelastrasz the Corrupt)
[41337] = true, -- Aura of Anger (Reliquary of Souls)
[41350] = true, -- Aura of Desire (Reliquary of Souls)
[44335] = true, -- Energy Feedback (Vexallus)
[44406] = true, -- Energy Infusion (Vexallus)
[53642] = true, -- Might of Mograine (Light's Hope Chapel)
[55849] = true, -- Power Spark (Malygos)
[56330] = true, -- Iron's Bane (Storm Peaks quest)
[56648] = true, -- Potent Fungus (Amanitar)
[57524] = true, -- Metanoia (Valkyrion Aspirant)
[58026] = true, -- Blessing of the Crusade (Icecrown quest)
[58361] = true, -- Might of Mograine (Patchwerk)
[58549] = true, -- Tenacity (Lake Wintergrasp)
[59641] = true, -- Warchief's Blessing (The Battle For The Undercity)
[60964] = true, -- Strength of Wrynn (The Battle For The Undercity)
[61888] = true, -- Overwhelming Power (Assembly of Iron - 25 man)
[62243] = true, -- Unstable Sun Beam (Elder Brightleaf)
[62650] = true, -- Fortitude of Frost (Yogg-Saron)
[62670] = true, -- Resilience of Nature (Yogg-Saron)
[62671] = true, -- Speed of Invention (Yogg-Saron)
[62702] = true, -- Fury of the Storm (Yogg-Saron)
[63277] = true, -- Shadow Crash (General Vezax)
[63711] = true, -- Storm Power (Hodir - 10 man)
[64320] = true, -- Rune of Power (Assembly of Iron)
[64321] = true, -- Potent Pheromones (Freya)
[64637] = true, -- Overwhelming Power (Assembly of Iron - 10 man)
[65134] = true, -- Storm Power (Hodir - 25 man)
[70867] = true, -- Essence of the Blood Queen (Blood Queen Lana'thel)
[70879] = true, -- Essence of the Blood Queen (Blood Queen Lana'thel, bitten by a player)
[72219] = true, -- Gastric Bloat (Festergut)
[73822] = true, -- Hellscream's Warsong (Icecrown Citadel)
[73828] = true, -- Strength of Wrynn (Icecrown Citadel)
[76133] = true, -- Tidal Surge (Neptulon)
[76155] = true, -- Tidal Surge (Neptulon)
[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) ?
[65280] = true, -- Singed (Hodir)
[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)
[80164] = true, -- Chemical Cloud (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
-- [63106] = "Corruption", -- Siphon Life
}
 
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 offset = FauxScrollFrame_GetOffset(scrollFrame)
local id = self:GetID()
 
local selection = scrollFrame.selected
if selection then
if selection - offset == id then
-- clicking the selected button, clear selection
self:UnlockHighlight()
selection = nil
else
-- clear selection if visible, and set new selection
local prevHilite = scrollFrame.buttons[selection - offset]
if prevHilite then
prevHilite:UnlockHighlight()
end
self:LockHighlight()
selection = id + offset
end
else
-- no previous selection, just set new and lock highlight
self:LockHighlight()
selection = id + offset
end
 
-- enable/disable "Delete" button depending on if selection exists
if selection then
module.delete:Enable()
else
module.delete:Disable()
end
scrollFrame.selected = selection
end
 
-- template function for mob filter buttons
local function createFilterButton(parent)
local btn = CreateFrame("Button", nil, parent)
btn:SetHeight(FILTERBUTTONHEIGHT)
btn:SetPoint("LEFT")
btn:SetPoint("RIGHT")
btn:SetNormalFontObject("GameFontNormal")
btn:SetHighlightFontObject("GameFontHighlight")
btn:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
btn:SetPushedTextOffset(0, 0)
btn:SetScript("OnClick", filterButtonOnClick)
return btn
end
 
local function createFilterButtons(parent, onEnter)
local buttons = {}
for i = 1, NUMFILTERBUTTONS do
local btn = createFilterButton(parent)
if i == 1 then
btn:SetPoint("TOP")
else
btn:SetPoint("TOP", buttons[i - 1], "BOTTOM")
end
btn:SetID(i)
if onEnter then
btn:SetScript("OnEnter", onEnter)
btn:SetScript("OnLeave", GameTooltip_Hide)
end
btn.module = parent
buttons[i] = btn
end
parent.scrollFrame.buttons = buttons
end
 
local function resetScroll(self)
FauxScrollFrame_SetOffset(self, 0)
self.scrollBar:SetValue(0)
self:Update()
end
 
local function onVerticalScroll(self, offset)
FauxScrollFrame_OnVerticalScroll(self, offset, self.buttonHeight, self.Update)
end
 
local function filterFrameOnShow(self)
local scrollFrame = self.scrollFrame
if scrollFrame.selected then
local prevHilite = scrollFrame.buttons[scrollFrame.selected - FauxScrollFrame_GetOffset(scrollFrame)]
if prevHilite then
prevHilite:UnlockHighlight()
end
scrollFrame.selected = nil
self.delete:Disable()
end
end
 
local function addButtonOnClick(self)
StaticPopup_Show(self.popup)
end
 
local function deleteButtonOnClick(self)
local scrollFrame = self.scrollFrame
local filterName = scrollFrame.filter
local selection = scrollFrame.selected
if selection then
local filter = filters.db.global[filterName]
local selectedEntry = filter[selection]
tremove(filter, selection)
local prevHighlight = scrollFrame.buttons[selection - FauxScrollFrame_GetOffset(scrollFrame)]
if prevHighlight then
prevHighlight:UnlockHighlight()
end
scrollFrame.selected = nil
scrollFrame:Update()
self:Disable()
addon:Message(self.msg:format(GetSpellInfo(selectedEntry) or selectedEntry))
if self.func then
self.func(selectedEntry)
end
end
end
 
local function createFilterFrame(name, parent, numButtons, buttonHeight)
local frame = CreateFrame("Frame", nil, parent)
frame:SetHeight(numButtons * buttonHeight)
parent[name] = frame
 
local scrollName = "CritlineFilters"..name.."ScrollFrame"
local scrollFrame = CreateFrame("ScrollFrame", scrollName, frame, "FauxScrollFrameTemplate")
scrollFrame:SetAllPoints()
scrollFrame:SetScript("OnShow", resetScroll)
scrollFrame:SetScript("OnVerticalScroll", onVerticalScroll)
scrollFrame.scrollBar = _G[scrollName.."ScrollBar"]
scrollFrame.buttons = frame.buttons
scrollFrame.numButtons = numButtons
scrollFrame.buttonHeight = buttonHeight
scrollFrame.filter = name
frame.scrollFrame = scrollFrame
 
if name ~= "spell" then
frame:SetScript("OnShow", filterFrameOnShow)
 
local add = templates:CreateButton(frame)
add:SetScript("OnClick", addButtonOnClick)
frame.add = add
 
local delete = templates:CreateButton(frame)
delete:Disable()
delete:SetScript("OnClick", deleteButtonOnClick)
delete.scrollFrame = scrollFrame
frame.delete = delete
end
 
return frame
end
 
 
do
local options = {}
filters.options = options
 
local checkButtons = {
{
text = L["Filter new spells"],
tooltipText = L["Enable to filter out new spell entries by default."],
setting = "filterNew",
},
{
text = L["Ignore mob filter"],
tooltipText = L["Enable to ignore integrated mob filter."],
setting = "ignoreMobFilter",
},
{
text = L["Ignore aura filter"],
tooltipText = L["Enable to ignore integrated aura filter."],
setting = "ignoreAuraFilter",
},
{
text = L["Only known spells"],
tooltipText = L["Enable to ignore spells that are not in your (or your pet's) spell book."],
setting = "onlyKnown",
},
{
text = L["Suppress mind control"],
tooltipText = L["Suppress all records while mind controlled."],
setting = "suppressMC",
newColumn = true,
},
{
text = L["Don't filter magic"],
tooltipText = L["Enable to let magical damage ignore the level filter."],
setting = "dontFilterMagic",
},
}
 
options.checkButtons = checkButtons
 
local columnEnd = #checkButtons
 
for i, v in ipairs(checkButtons) do
local btn = templates:CreateCheckButton(filters, v)
if i == 1 then
btn:SetPoint("TOPLEFT", filters.title, "BOTTOMLEFT", -2, -16)
elseif btn.newColumn then
btn:SetPoint("TOPLEFT", filters.title, "BOTTOM", 0, -16)
columnEnd = i - 1
else
btn:SetPoint("TOP", checkButtons[i - 1], "BOTTOM", 0, -8)
end
btn.module = filters
checkButtons[i] = btn
end
 
local slider = templates:CreateSlider(filters, {
text = L["Level filter"],
tooltipText = L["If level difference between you and the target is greater than this setting, records will not be registered."],
minValue = -1,
maxValue = 10,
valueStep = 1,
minText = OFF,
maxText = 10,
func = function(self)
local value = self:GetValue()
self.value:SetText(value == -1 and OFF or value)
filters.profile.levelFilter = value
end,
})
slider:SetPoint("TOPLEFT", checkButtons[#checkButtons], "BOTTOMLEFT", 4, -24)
options.slider = slider
 
local filterTypes = {}
 
-- spell filter frame
local spellFilter = createFilterFrame("spell", filters, NUMSPELLBUTTONS, SPELLBUTTONHEIGHT)
spellFilter:SetPoint("TOP", checkButtons[columnEnd], "BOTTOM", 0, -48)
spellFilter:SetPoint("LEFT", 48, 0)
spellFilter:SetPoint("RIGHT", -48, 0)
filterTypes.spell = spellFilter
 
do -- spell filter buttons
local function spellButtonOnClick(self)
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)
if i == 1 then
btn:SetPoint("TOPLEFT")
else
btn:SetPoint("TOP", buttons[i - 1], "BOTTOM", 0, 4)
end
btn:SetScript("OnClick", spellButtonOnClick)
btn:SetScript("OnEnter", spellButtonOnEnter)
buttons[i] = btn
end
spellFilter.scrollFrame.buttons = buttons
end
 
-- spell filter scroll frame
local spellScrollFrame = spellFilter.scrollFrame
 
-- spell filter tree dropdown
local menu = {
{text = L["Damage"], value = "dmg"},
{text = L["Healing"], value = "heal"},
{text = L["Pet"], value = "pet"},
}
 
local spellFilterTree = templates:CreateDropDownMenu("CritlineSpellFilterTree", spellFilter, menu)
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
 
do -- mob filter frame
local mobFilter = createFilterFrame("mobs", filters, NUMFILTERBUTTONS, FILTERBUTTONHEIGHT)
mobFilter:SetPoint("TOP", spellFilter)
mobFilter:SetPoint("LEFT", spellFilter)
mobFilter:SetPoint("RIGHT", spellFilter)
mobFilter:Hide()
filterTypes.mobs = mobFilter
 
createFilterButtons(mobFilter)
 
local addTarget = templates:CreateButton(mobFilter)
addTarget:SetSize(96, 22)
addTarget:SetPoint("TOPLEFT", mobFilter, "BOTTOMLEFT", 0, -8)
addTarget:SetText(L["Add target"])
addTarget:SetScript("OnClick", function()
local targetName = UnitName("target")
if targetName then
-- we don't want to add PCs to the filter
if UnitIsPlayer("target") then
addon:Message(L["Cannot add players to mob filter."])
else
filters:AddMob(targetName)
end
else
addon:Message(L["No target selected."])
end
end)
 
local add = mobFilter.add
add:SetSize(96, 22)
add:SetPoint("TOP", mobFilter, "BOTTOM", 0, -8)
add:SetText(L["Add by name"])
add.popup = "CRITLINE_ADD_MOB_BY_NAME"
 
local delete = mobFilter.delete
delete:SetSize(96, 22)
delete:SetPoint("TOPRIGHT", mobFilter, "BOTTOMRIGHT", 0, -8)
delete:SetText(L["Delete mob"])
delete.msg = L["%s removed from mob filter."]
end
 
do -- aura filter frame
local auraFilter = createFilterFrame("auras", filters, NUMFILTERBUTTONS, FILTERBUTTONHEIGHT)
auraFilter:SetPoint("TOP", spellFilter)
auraFilter:SetPoint("LEFT", spellFilter)
auraFilter:SetPoint("RIGHT", spellFilter)
auraFilter:Hide()
filterTypes.auras = auraFilter
 
createFilterButtons(auraFilter, function(self)
GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT")
GameTooltip:SetHyperlink("spell:"..self.spellID)
end)
 
local add = auraFilter.add
add:SetSize(128, 22)
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)
delete:SetPoint("TOPRIGHT", auraFilter, "BOTTOMRIGHT", 0, -8)
delete:SetText(L["Delete aura"])
delete.msg = L["%s removed from aura filter."]
delete.func = function(spellID)
activeAuras[spellID] = nil
if not filters:IsEmpowered() then
addon:Debug("No filtered aura detected. Resuming record tracking.")
end
end
end
 
do -- filter tree dropdown
local menu = {
{
text = L["Spell filter"],
value = "spell",
},
{
text = L["Mob filter"],
value = "mobs",
},
{
text = L["Aura filter"],
value = "auras",
},
}
 
local filterType = templates:CreateDropDownMenu("CritlineFilterType", filters, menu)
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,
button2 = CANCEL,
hasEditBox = true,
OnAccept = function(self)
local name = self.editBox:GetText():trim()
if not name:match("%S+") then
addon:Message(L["Invalid mob name."])
return
end
filters:AddMob(name)
end,
EditBoxOnEnterPressed = function(self)
local name = self:GetText():trim()
if not name:match("%S+") then
addon:Message(L["Invalid mob name."])
return
end
filters:AddMob(name)
self:GetParent():Hide()
end,
EditBoxOnEscapePressed = function(self)
self:GetParent():Hide()
end,
OnShow = function(self)
self.editBox:SetFocus()
end,
whileDead = true,
timeout = 0,
}
 
StaticPopupDialogs["CRITLINE_ADD_AURA_BY_ID"] = {
text = L["Enter spell ID:"],
button1 = OKAY,
button2 = CANCEL,
hasEditBox = true,
OnAccept = function(self)
local id = tonumber(self.editBox:GetText())
if not id then
addon:Message(L["Invalid input. Please enter a spell ID."])
return
elseif not GetSpellInfo(id) then
addon:Message(L["Invalid spell ID. No such spell."])
return
end
filters:AddAura(id)
end,
EditBoxOnEnterPressed = function(self)
local id = tonumber(self:GetText())
if not id then
addon:Message(L["Invalid input. Please enter a spell ID."])
return
elseif not GetSpellInfo(id) then
addon:Message(L["Invalid spell ID. No such spell exists."])
return
end
filters:AddAura(id)
self:GetParent():Hide()
end,
EditBoxOnEscapePressed = function(self)
self:GetParent():Hide()
end,
OnShow = function(self)
self.editBox:SetFocus()
end,
whileDead = true,
timeout = 0,
}
 
 
local function updateSpellFilter(self)
local selectedTree = self.tree:GetSelectedValue()
local spells = addon:GetSpellArray(selectedTree)
local size = #spells
 
FauxScrollFrame_Update(self, size, self.numButtons, self.buttonHeight)
 
local offset = FauxScrollFrame_GetOffset(self)
local buttons = self.buttons
for line = 1, NUMSPELLBUTTONS do
local button = buttons[line]
local lineplusoffset = line + offset
if lineplusoffset <= size then
local data = spells[lineplusoffset]
button.data = data
button:SetText(addon:GetFullSpellName(data.spellID, data.periodic))
button:SetChecked(not data.filtered)
button:Show()
else
button:Hide()
end
end
end
 
local function updateFilter(self)
local filter = filters.db.global[self.filter]
local size = #filter
 
FauxScrollFrame_Update(self, size, self.numButtons, self.buttonHeight)
 
local offset = FauxScrollFrame_GetOffset(self)
local buttons = self.buttons
for line = 1, self.numButtons do
local button = buttons[line]
local lineplusoffset = line + offset
if lineplusoffset <= size then
if self.selected then
if self.selected - offset == line then
button:LockHighlight()
else
button:UnlockHighlight()
end
end
local entry = filter[lineplusoffset]
button.spellID = entry
button:SetText(type(entry) == "number" and GetSpellInfo(entry) or entry)
button:Show()
else
button:Hide()
end
end
end
 
 
local defaults = {
profile = {
filterNew = false,
onlyKnown = false,
ignoreMobFilter = false,
ignoreAuraFilter = false,
suppressMC = true,
dontFilterMagic = false,
levelFilter = -1,
},
global = {
mobs = {},
auras = {},
},
}
 
function filters:AddonLoaded()
self.db = addon.db:RegisterNamespace("filters", defaults)
addon.RegisterCallback(self, "SettingsLoaded", "LoadSettings")
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
self.auras.scrollFrame.Update = updateFilter
end
 
addon.RegisterCallback(filters, "AddonLoaded")
 
 
function filters:COMBAT_LOG_EVENT_UNFILTERED(timestamp, eventType, hideCaster, 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(format("Filtered aura gained. (%s) Disabling combat log tracking.", spellName))
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 i, v in ipairs(self.options.checkButtons) do
v:LoadSetting()
end
 
self.options.slider:SetValue(self.profile.levelFilter)
end
 
 
local auraTypes = {
BUFF = "HELPFUL",
DEBUFF = "HARMFUL",
}
 
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
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
 
 
function filters:UpdateSpellFilter()
self.spell.scrollFrame:Update()
end
 
 
function filters:UpdateFilter()
self[self.type:GetSelectedValue()].scrollFrame:Update()
end
 
 
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
 
 
-- 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))
else
tinsert(self.db.global.mobs, name)
self:UpdateFilter()
addon:Message(L["%s added to mob filter."]:format(name))
end
end
 
 
-- adds an aura to the aura filter
function filters:AddAura(spellID)
local spellName = GetSpellInfo(spellID)
if self:IsFilteredAura(spellID) then
addon:Message(L["%s is already in aura filter."]:format(spellName))
else
tinsert(self.db.global.auras, spellID)
-- after we add an aura to the filter; check if we have it
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
else
for _, v in ipairs(self.db.global.auras) do
if v == buffID then
activeAuras[buffID] = true
break
elseif v == debuffID then
activeAuras[debuffID] = true
break
end
end
end
end
self:UpdateFilter()
addon:Message(L["%s added to aura filter."]:format(spellName))
end
end
 
 
-- check if a spell passes the filter settings
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
end
 
if ((corruptSpells[spellID] and corruptSpells[spellID][destGUID]) or (self:IsEmpowered() and (not isPeriodic or directHoTs[spellID]))) and not self.profile.ignoreAuraFilter then
addon:Debug(format("Spell (%s) was cast under the influence of a filtered aura. Return.", spellName))
return
end
 
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)
end
 
-- ignore level adjustment if magic damage and the setting is enabled
if not isHeal and (self.profile.levelFilter >= 0) and (self.profile.levelFilter < levelDiff) and (school == 1 or not self.profile.dontFilterMagic) then
-- target level is too low to pass level filter
addon:Debug(format("Target (%s) level too low (%d) and damage school is filtered. Return.", destName, targetLevel))
return
end
 
local filteredMob = self:IsFilteredMob(destName, destGUID)
if filteredMob then
addon:Debug(format("Target (%s) is in %s target filter.", mobName, filteredMob))
return
end
 
return true, self:IsFilteredSpell(tree, spellID, isPeriodic and 2 or 1), targetLevel
end
 
 
-- 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
 
 
-- scan for filtered auras from the specialAuras table
function filters:IsEmpowered()
if next(activeAuras) or not self.inControl then
return true
end
end
 
 
-- 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 and specialMobs[tonumber(guid:sub(7, 10), 16)] then
return "default"
end
for _, v in ipairs(self.db.global.mobs) do
if v:lower() == mobName:lower() then
return "custom"
end
end
end
 
 
function filters:IsFilteredAura(spellID)
if specialAuras[spellID] then
return true
end
for _, v in ipairs(self.db.global.auras) do
if v == spellID then
return true
end
end
end
 
 
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 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(7, 10), 16)
sourceType = "npc"
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
session[spellID] = aura
CritlineAuraListScrollFrame:Update()
end
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3/libs/LibDataBroker-1.1.lua New file
0,0 → 1,90
 
assert(LibStub, "LibDataBroker-1.1 requires LibStub")
assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0")
 
local lib, oldminor = LibStub:NewLibrary("LibDataBroker-1.1", 4)
if not lib then return end
oldminor = oldminor or 0
 
 
lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib)
lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {}
local attributestorage, namestorage, callbacks = lib.attributestorage, lib.namestorage, lib.callbacks
 
if oldminor < 2 then
lib.domt = {
__metatable = "access denied",
__index = function(self, key) return attributestorage[self] and attributestorage[self][key] end,
}
end
 
if oldminor < 3 then
lib.domt.__newindex = function(self, key, value)
if not attributestorage[self] then attributestorage[self] = {} end
if attributestorage[self][key] == value then return end
attributestorage[self][key] = value
local name = namestorage[self]
if not name then return end
callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value, self)
callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value, self)
callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value, self)
callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value, self)
end
end
 
if oldminor < 2 then
function lib:NewDataObject(name, dataobj)
if self.proxystorage[name] then return end
 
if dataobj then
assert(type(dataobj) == "table", "Invalid dataobj, must be nil or a table")
self.attributestorage[dataobj] = {}
for i,v in pairs(dataobj) do
self.attributestorage[dataobj][i] = v
dataobj[i] = nil
end
end
dataobj = setmetatable(dataobj or {}, self.domt)
self.proxystorage[name], self.namestorage[dataobj] = dataobj, name
self.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj)
return dataobj
end
end
 
if oldminor < 1 then
function lib:DataObjectIterator()
return pairs(self.proxystorage)
end
 
function lib:GetDataObjectByName(dataobjectname)
return self.proxystorage[dataobjectname]
end
 
function lib:GetNameByDataObject(dataobject)
return self.namestorage[dataobject]
end
end
 
if oldminor < 4 then
local next = pairs(attributestorage)
function lib:pairs(dataobject_or_name)
local t = type(dataobject_or_name)
assert(t == "string" or t == "table", "Usage: ldb:pairs('dataobjectname') or ldb:pairs(dataobject)")
 
local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
assert(attributestorage[dataobj], "Data object not found")
 
return next, attributestorage[dataobj], nil
end
 
local ipairs_iter = ipairs(attributestorage)
function lib:ipairs(dataobject_or_name)
local t = type(dataobject_or_name)
assert(t == "string" or t == "table", "Usage: ldb:ipairs('dataobjectname') or ldb:ipairs(dataobject)")
 
local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
assert(attributestorage[dataobj], "Data object not found")
 
return ipairs_iter, attributestorage[dataobj], 0
end
end
Property changes : Added: svn:eol-style + native
tags/4.0.3/libs/LibDualSpec-1.0.lua New file
0,0 → 1,330
--[[
LibDualSpec-1.0 - Adds dual spec support to individual AceDB-3.0 databases
Copyright (C) 2009-2011 Adirelle
 
All rights reserved.
 
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
 
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Redistribution of a stand alone version is strictly prohibited without
prior written authorization from the LibDualSpec project manager.
* Neither the name of the LibDualSpec authors nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--]]
 
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.eventFrame = lib.eventFrame or CreateFrame("Frame")
 
lib.registry = lib.registry or {}
lib.options = lib.options or {}
lib.mixin = lib.mixin or {}
 
-- ----------------------------------------------------------------------------
-- Locals
-- ----------------------------------------------------------------------------
 
local registry = lib.registry
local options = lib.options
local mixin = lib.mixin
 
-- "Externals"
local AceDB3 = LibStub('AceDB-3.0', true)
local AceDBOptions3 = LibStub('AceDBOptions-3.0', true)
 
-- ----------------------------------------------------------------------------
-- Localization
-- ----------------------------------------------------------------------------
 
local L_DUALSPEC_DESC, L_ENABLED, L_ENABLED_DESC, L_DUAL_PROFILE, L_DUAL_PROFILE_DESC
 
do
L_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."
L_ENABLED = 'Enable dual profile'
L_ENABLED_DESC = 'Check this box to automatically swap profiles on talent switch.'
L_DUAL_PROFILE = 'Dual profile'
L_DUAL_PROFILE_DESC = 'Select the profile to swap with on talent switch.'
 
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_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ä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."
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_ENABLED = "开启双重配置文件"
L_ENABLED_DESC = "勾选以便转换天赋时自动交换配置文件。"
elseif locale == "zhTW" then
L_DUALSPEC_DESC = "啟用時,你可以為你的雙天賦設定另一組設定檔。你的雙設定檔將在你轉換天賦時自動與目前使用設定檔交換。"
L_DUAL_PROFILE = "雙設定檔"
L_DUAL_PROFILE_DESC = "選擇轉換天賦後所要使用的設定檔"
L_ENABLED = "啟用雙設定檔"
L_ENABLED_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.
-- @return (boolean) true is dual spec feature enabled.
-- @name enhancedDB:IsDualSpecEnabled
function mixin:IsDualSpecEnabled()
return registry[self].db.char.enabled
end
 
--- Enable/disabled dual spec feature.
-- @param enabled (boolean) true to enable dual spec feature, false to disable it.
-- @name enhancedDB:SetDualSpecEnabled
function mixin:SetDualSpecEnabled(enabled)
local db = registry[self].db
if enabled and not db.char.talentGroup then
db.char.talentGroup = lib.talentGroup
db.char.profile = self:GetCurrentProfile()
db.char.enabled = true
else
db.char.enabled = enabled
self:CheckDualSpecState()
end
end
 
--- Get the alternate profile name.
-- Defaults to the current profile.
-- @return (string) Alternate profile name.
-- @name enhancedDB:GetDualSpecProfile
function mixin:GetDualSpecProfile()
return registry[self].db.char.profile or self:GetCurrentProfile()
end
 
--- Set the alternate profile name.
-- No validation are done to ensure the profile is valid.
-- @param profileName (string) the profile name to use.
-- @name enhancedDB:SetDualSpecProfile
function mixin:SetDualSpecProfile(profileName)
registry[self].db.char.profile = profileName
end
 
--- Check if a profile swap should occur.
-- Do nothing if the dual spec feature is disabled. In the other
-- case, if the internally stored talent spec is different from the
-- actual active talent spec, the database swaps to the alternate profile.
-- There is normally no reason to call this method directly as LibDualSpec
-- takes care of calling it at appropriate times.
-- @name enhancedDB:CheckDualSpecState
function mixin:CheckDualSpecState()
local db = registry[self].db
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)
end
end
end
 
-- ----------------------------------------------------------------------------
-- AceDB-3.0 support
-- ----------------------------------------------------------------------------
 
local function EmbedMixin(target)
for k,v in pairs(mixin) do
rawset(target, k, v)
end
end
 
-- Upgrade existing mixins
for target in pairs(registry) do
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
-- @param target (table) the AceDB-3.0 instance.
-- @param name (string) a user-friendly name of the database (best bet is the addon name).
function lib:EnhanceDatabase(target, name)
AceDB3 = AceDB3 or LibStub('AceDB-3.0', true)
if type(target) ~= "table" then
error("Usage: LibDualSpec:EnhanceDatabase(target, name): target should be a table.", 2)
elseif type(name) ~= "string" then
error("Usage: LibDualSpec:EnhanceDatabase(target, name): name should be a string.", 2)
elseif not AceDB3 or not AceDB3.db_registry[target] then
error("Usage: LibDualSpec:EnhanceDatabase(target, name): target should be an AceDB-3.0 database.", 2)
elseif target.parent then
error("Usage: LibDualSpec:EnhanceDatabase(target, name): cannot enhance a namespace.", 2)
elseif registry[target] then
return
end
registry[target] = { name = name }
lib:_EnhanceDatabase("EnhanceDatabase", target)
target.RegisterCallback(lib, "OnDatabaseReset", "_EnhanceDatabase")
end
 
-- ----------------------------------------------------------------------------
-- AceDBOptions-3.0 support
-- ----------------------------------------------------------------------------
 
local function NoDualSpec()
return GetNumTalentGroups() == 1
end
 
options.dualSpecDesc = {
name = L_DUALSPEC_DESC,
type = 'description',
order = 40.1,
hidden = NoDualSpec,
}
 
options.enabled = {
name = L_ENABLED,
desc = L_ENABLED_DESC,
type = 'toggle',
order = 40.2,
get = function(info) return info.handler.db:IsDualSpecEnabled() end,
set = function(info, value) info.handler.db:SetDualSpecEnabled(value) end,
hidden = NoDualSpec,
}
 
options.dualProfile = {
name = L_DUAL_PROFILE,
desc = L_DUAL_PROFILE_DESC,
type = 'select',
order = 40.3,
get = function(info) return info.handler.db:GetDualSpecProfile() end,
set = function(info, value) info.handler.db:SetDualSpecProfile(value) end,
values = "ListProfiles",
arg = "common",
hidden = NoDualSpec,
disabled = function(info) return not info.handler.db:IsDualSpecEnabled() end,
}
 
--- Embed dual spec options into an existing AceDBOptions-3.0 option table.
-- @name LibDualSpec:EnhanceOptions
-- @param optionTable (table) The option table returned by AceDBOptions-3.0.
-- @param target (table) The AceDB-3.0 the options operate on.
function lib:EnhanceOptions(optionTable, target)
AceDBOptions3 = AceDBOptions3 or LibStub('AceDBOptions-3.0', true)
if type(optionTable) ~= "table" then
error("Usage: LibDualSpec:EnhanceOptions(optionTable, target): optionTable should be a table.", 2)
elseif type(target) ~= "table" then
error("Usage: LibDualSpec:EnhanceOptions(optionTable, target): target should be a table.", 2)
elseif not (AceDBOptions3 and AceDBOptions3.optionTables[target]) then
error("Usage: LibDualSpec:EnhanceOptions(optionTable, target): optionTable is not an AceDBOptions-3.0 table.", 2)
elseif optionTable.handler.db ~= target then
error("Usage: LibDualSpec:EnhanceOptions(optionTable, target): optionTable must be the option table of target.", 2)
elseif not registry[target] then
error("Usage: LibDualSpec:EnhanceOptions(optionTable, target): EnhanceDatabase should be called before EnhanceOptions(optionTable, target).", 2)
elseif optionTable.plugins and optionTable.plugins[MAJOR] then
return
end
if not optionTable.plugins then
optionTable.plugins = {}
end
optionTable.plugins[MAJOR] = options
end
 
-- ----------------------------------------------------------------------------
-- Inspection
-- ----------------------------------------------------------------------------
 
local function iterator(registry, key)
local data
key, data = next(registry, key)
if key then
return key, data.name
end
end
 
--- Iterate through enhanced AceDB3.0 instances.
-- The iterator returns (instance, name) pairs where instance and name are the
-- arguments that were provided to lib:EnhanceDatabase.
-- @name LibDualSpec:IterateDatabases
-- @return Values to be used in a for .. in .. do statement.
function lib:IterateDatabases()
return iterator, lib.registry
end
 
-- ----------------------------------------------------------------------------
-- Switching logic
-- ----------------------------------------------------------------------------
 
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
for target in pairs(registry) do
target:CheckDualSpecState()
end
end
end)
 
 
Property changes : Added: svn:eol-style + native
tags/4.0.3/libs/LibStub.lua New file
0,0 → 1,30
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
local LibStub = _G[LIBSTUB_MAJOR]
 
if not LibStub or LibStub.minor < LIBSTUB_MINOR then
LibStub = LibStub or {libs = {}, minors = {} }
_G[LIBSTUB_MAJOR] = LibStub
LibStub.minor = LIBSTUB_MINOR
 
function LibStub:NewLibrary(major, minor)
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
 
local oldminor = self.minors[major]
if oldminor and oldminor >= minor then return nil end
self.minors[major], self.libs[major] = minor, self.libs[major] or {}
return self.libs[major], oldminor
end
 
function LibStub:GetLibrary(major, silent)
if not self.libs[major] and not silent then
error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
end
return self.libs[major], self.minors[major]
end
 
function LibStub:IterateLibraries() return pairs(self.libs) end
setmetatable(LibStub, { __call = LibStub.GetLibrary })
end
Property changes : Added: svn:eol-style + native
tags/4.0.3/libs/AceLocale-3.0.lua New file
0,0 → 1,137
--- **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 1005 2011-01-29 14:19:43Z mikk $
local MAJOR,MINOR = "AceLocale-3.0", 5
 
local AceLocale, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
 
if not AceLocale then return end -- no upgrade needed
 
-- Lua APIs
local assert, tostring, error = assert, tostring, error
local setmetatable, rawset, rawget = setmetatable, rawset, rawget
 
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: GAME_LOCALE, geterrorhandler
 
local gameLocale = GetLocale()
if gameLocale == "enGB" then
gameLocale = "enUS"
end
 
AceLocale.apps = AceLocale.apps or {} -- array of ["AppName"]=localetableref
AceLocale.appnames = AceLocale.appnames or {} -- array of [localetableref]="AppName"
 
-- This metatable is used on all tables returned from GetLocale
local readmeta = {
__index = function(self, key) -- requesting totally unknown entries: fire off a nonbreaking error and return key
rawset(self, key, key) -- only need to see the warning once, really
geterrorhandler()(MAJOR..": "..tostring(AceLocale.appnames[self])..": Missing entry for '"..tostring(key).."'")
return key
end
}
 
-- This metatable is used on all tables returned from GetLocale if the silent flag is true, it does not issue a warning on unknown keys
local readmetasilent = {
__index = function(self, key) -- requesting totally unknown entries: return key
rawset(self, key, key) -- only need to invoke this function once
return key
end
}
 
-- Remember the locale table being registered right now (it gets set by :NewLocale())
-- NOTE: Do never try to register 2 locale tables at once and mix their definition.
local registering
 
-- local assert false function
local assertfalse = function() assert(false) end
 
-- This metatable proxy is used when registering nondefault locales
local writeproxy = setmetatable({}, {
__newindex = function(self, key, value)
rawset(registering, key, value == true and key or value) -- assigning values: replace 'true' with key string
end,
__index = assertfalse
})
 
-- This metatable proxy is used when registering the default locale.
-- It refuses to overwrite existing values
-- Reason 1: Allows loading locales in any order
-- Reason 2: If 2 modules have the same string, but only the first one to be
-- loaded has a translation for the current locale, the translation
-- doesn't get overwritten.
--
local writedefaultproxy = setmetatable({}, {
__newindex = function(self, key, value)
if not rawget(registering, key) then
rawset(registering, key, value == true and key or value)
end
end,
__index = assertfalse
})
 
--- Register a new locale (or extend an existing one) for the specified application.
-- :NewLocale will return a table you can fill your locale into, or nil if the locale isn't needed for the players
-- game locale.
-- @paramsig application, locale[, isDefault[, silent]]
-- @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. 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)
-- L["string1"] = true
--
-- -- deDE.lua
-- local L = LibStub("AceLocale-3.0"):NewLocale("TestLocale", "deDE")
-- if not L then return end
-- L["string1"] = "Zeichenkette1"
-- @return Locale Table to add localizations to, or nil if the current locale is not required.
function AceLocale:NewLocale(application, locale, isDefault, silent)
 
-- GAME_LOCALE allows translators to test translations of addons without having that wow client installed
local gameLocale = GAME_LOCALE or gameLocale
 
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
 
if not app then
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
return writedefaultproxy
end
 
return writeproxy
end
 
--- Returns localizations for the current locale (or default locale if translations are missing).
-- Errors if nothing is registered (spank developer, not just a missing translation)
-- @param application Unique name of addon / module
-- @param silent If true, the locale is optional, silently return nil if it's not found (defaults to false, optional)
-- @return The locale table for the current language.
function AceLocale:GetLocale(application, silent)
if not silent and not AceLocale.apps[application] then
error("Usage: GetLocale(application[, silent]): 'application' - No locales registered for '"..tostring(application).."'", 2)
end
return AceLocale.apps[application]
end
Property changes : Added: svn:eol-style + native
tags/4.0.3/libs/CallbackHandler-1.0.lua New file
0,0 → 1,240
--[[ $Id: CallbackHandler-1.0.lua 965 2010-08-09 00:47:52Z mikk $ ]]
local MAJOR, MINOR = "CallbackHandler-1.0", 6
local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR)
 
if not CallbackHandler then return end -- No upgrade needed
 
local meta = {__index = function(tbl, key) tbl[key] = {} return tbl[key] end}
 
-- Lua APIs
local tconcat = table.concat
local assert, error, loadstring = assert, error, loadstring
local setmetatable, rawset, rawget = setmetatable, rawset, rawget
local next, select, pairs, type, tostring = next, select, pairs, type, tostring
 
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: geterrorhandler
 
local xpcall = xpcall
 
local function errorhandler(err)
return geterrorhandler()(err)
end
 
local function CreateDispatcher(argCount)
local code = [[
local next, xpcall, eh = ...
 
local method, ARGS
local function call() method(ARGS) end
 
local function dispatch(handlers, ...)
local index
index, method = next(handlers)
if not method then return end
local OLD_ARGS = ARGS
ARGS = ...
repeat
xpcall(call, eh)
index, method = next(handlers, index)
until not method
ARGS = OLD_ARGS
end
 
return dispatch
]]
 
local ARGS, OLD_ARGS = {}, {}
for i = 1, argCount do ARGS[i], OLD_ARGS[i] = "arg"..i, "old_arg"..i end
code = code:gsub("OLD_ARGS", tconcat(OLD_ARGS, ", ")):gsub("ARGS", tconcat(ARGS, ", "))
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(next, xpcall, errorhandler)
end
 
local Dispatchers = setmetatable({}, {__index=function(self, argCount)
local dispatcher = CreateDispatcher(argCount)
rawset(self, argCount, dispatcher)
return dispatcher
end})
 
--------------------------------------------------------------------------
-- CallbackHandler:New
--
-- target - target object to embed public APIs in
-- RegisterName - name of the callback registration API, default "RegisterCallback"
-- UnregisterName - name of the callback unregistration API, default "UnregisterCallback"
-- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API.
 
function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName, OnUsed, OnUnused)
-- TODO: Remove this after beta has gone out
assert(not OnUsed and not OnUnused, "ACE-80: OnUsed/OnUnused are deprecated. Callbacks are now done to registry.OnUsed and registry.OnUnused")
 
RegisterName = RegisterName or "RegisterCallback"
UnregisterName = UnregisterName or "UnregisterCallback"
if UnregisterAllName==nil then -- false is used to indicate "don't want this method"
UnregisterAllName = "UnregisterAllCallbacks"
end
 
-- we declare all objects and exported APIs inside this closure to quickly gain access
-- to e.g. function names, the "target" parameter, etc
 
 
-- Create the registry object
local events = setmetatable({}, meta)
local registry = { recurse=0, events=events }
 
-- registry:Fire() - fires the given event/message into the registry
function registry:Fire(eventname, ...)
if not rawget(events, eventname) or not next(events[eventname]) then return end
local oldrecurse = registry.recurse
registry.recurse = oldrecurse + 1
 
Dispatchers[select('#', ...) + 1](events[eventname], eventname, ...)
 
registry.recurse = oldrecurse
 
if registry.insertQueue and oldrecurse==0 then
-- Something in one of our callbacks wanted to register more callbacks; they got queued
for eventname,callbacks in pairs(registry.insertQueue) do
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
for self,func in pairs(callbacks) do
events[eventname][self] = func
-- fire OnUsed callback?
if first and registry.OnUsed then
registry.OnUsed(registry, target, eventname)
first = nil
end
end
end
registry.insertQueue = nil
end
end
 
-- Registration of a callback, handles:
-- self["method"], leads to self["method"](self, ...)
-- self with function ref, leads to functionref(...)
-- "addonId" (instead of self) with function ref, leads to functionref(...)
-- all with an optional arg, which, if present, gets passed as first argument (after self if present)
target[RegisterName] = function(self, eventname, method, ... --[[actually just a single arg]])
if type(eventname) ~= "string" then
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2)
end
 
method = method or eventname
 
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
 
if type(method) ~= "string" and type(method) ~= "function" then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - string or function expected.", 2)
end
 
local regfunc
 
if type(method) == "string" then
-- self["method"] calling style
if type(self) ~= "table" then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): self was not a table?", 2)
elseif self==target then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): do not use Library:"..RegisterName.."(), use your own 'self'", 2)
elseif type(self[method]) ~= "function" then
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - method '"..tostring(method).."' not found on self.", 2)
end
 
if select("#",...)>=1 then -- this is not the same as testing for arg==nil!
local arg=select(1,...)
regfunc = function(...) self[method](self,arg,...) end
else
regfunc = function(...) self[method](self,...) end
end
else
-- function ref with self=object or self="addonId" or self=thread
if type(self)~="table" and type(self)~="string" and type(self)~="thread" then
error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string or thread expected.", 2)
end
 
if select("#",...)>=1 then -- this is not the same as testing for arg==nil!
local arg=select(1,...)
regfunc = function(...) method(arg,...) end
else
regfunc = method
end
end
 
 
if events[eventname][self] or registry.recurse<1 then
-- if registry.recurse<1 then
-- we're overwriting an existing entry, or not currently recursing. just set it.
events[eventname][self] = regfunc
-- fire OnUsed callback?
if registry.OnUsed and first then
registry.OnUsed(registry, target, eventname)
end
else
-- we're currently processing a callback in this registry, so delay the registration of this new entry!
-- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency
registry.insertQueue = registry.insertQueue or setmetatable({},meta)
registry.insertQueue[eventname][self] = regfunc
end
end
 
-- Unregister a callback
target[UnregisterName] = function(self, eventname)
if not self or self==target then
error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2)
end
if type(eventname) ~= "string" then
error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2)
end
if rawget(events, eventname) and events[eventname][self] then
events[eventname][self] = nil
-- Fire OnUnused callback?
if registry.OnUnused and not next(events[eventname]) then
registry.OnUnused(registry, target, eventname)
end
end
if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then
registry.insertQueue[eventname][self] = nil
end
end
 
-- OPTIONAL: Unregister all callbacks for given selfs/addonIds
if UnregisterAllName then
target[UnregisterAllName] = function(...)
if select("#",...)<1 then
error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or \"addonId\" to unregister events for.", 2)
end
if select("#",...)==1 and ...==target then
error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or \"addonId\"", 2)
end
 
 
for i=1,select("#",...) do
local self = select(i,...)
if registry.insertQueue then
for eventname, callbacks in pairs(registry.insertQueue) do
if callbacks[self] then
callbacks[self] = nil
end
end
end
for eventname, callbacks in pairs(events) do
if callbacks[self] then
callbacks[self] = nil
-- Fire OnUnused callback?
if registry.OnUnused and not next(callbacks) then
registry.OnUnused(registry, target, eventname)
end
end
end
end
end
end
 
return registry
end
 
 
-- CallbackHandler purposefully does NOT do explicit embedding. Nor does it
-- try to upgrade old implicit embeds since the system is selfcontained and
-- relies on closures to work.
 
Property changes : Added: svn:eol-style + native
tags/4.0.3/libs/LibSharedMedia-3.0.lua New file
0,0 → 1,235
--[[
Name: LibSharedMedia-3.0
Revision: $Revision: 62 $
Author: Elkano (elkano@gmx.de)
Inspired By: SurfaceLib by Haste/Otravi (troeks@gmail.com)
Website: http://www.wowace.com/projects/libsharedmedia-3-0/
Description: Shared handling of media data (fonts, sounds, textures, ...) between addons.
Dependencies: LibStub, CallbackHandler-1.0
License: LGPL v2.1
]]
 
local MAJOR, MINOR = "LibSharedMedia-3.0", 100001 -- increase manualy on changes
local lib = LibStub:NewLibrary(MAJOR, MINOR)
 
if not lib then return end
 
local _G = getfenv(0)
 
local pairs = _G.pairs
local type = _G.type
 
local band = _G.bit.band
 
local table_insert = _G.table.insert
local table_sort = _G.table.sort
 
local locale = GetLocale()
local locale_is_western
local LOCALE_MASK = 0
lib.LOCALE_BIT_koKR = 1
lib.LOCALE_BIT_ruRU = 2
lib.LOCALE_BIT_zhCN = 4
lib.LOCALE_BIT_zhTW = 8
lib.LOCALE_BIT_western = 128
 
local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0")
 
lib.callbacks = lib.callbacks or CallbackHandler:New(lib)
 
lib.DefaultMedia = lib.DefaultMedia or {}
lib.MediaList = lib.MediaList or {}
lib.MediaTable = lib.MediaTable or {}
lib.MediaType = lib.MediaType or {}
lib.OverrideMedia = lib.OverrideMedia or {}
 
local defaultMedia = lib.DefaultMedia
local mediaList = lib.MediaList
local mediaTable = lib.MediaTable
local overrideMedia = lib.OverrideMedia
 
 
-- create mediatype constants
lib.MediaType.BACKGROUND = "background" -- background textures
lib.MediaType.BORDER = "border" -- border textures
lib.MediaType.FONT = "font" -- fonts
lib.MediaType.STATUSBAR = "statusbar" -- statusbar textures
lib.MediaType.SOUND = "sound" -- sound files
 
-- populate lib with default Blizzard data
-- BACKGROUND
if not lib.MediaTable.background then lib.MediaTable.background = {} end
lib.MediaTable.background["Blizzard Dialog Background"] = [[Interface\DialogFrame\UI-DialogBox-Background]]
lib.MediaTable.background["Blizzard Dialog Background Dark"] = [[Interface\DialogFrame\UI-DialogBox-Background-Dark]]
lib.MediaTable.background["Blizzard Dialog Background Gold"] = [[Interface\DialogFrame\UI-DialogBox-Gold-Background]]
lib.MediaTable.background["Blizzard Low Health"] = [[Interface\FullScreenTextures\LowHealth]]
lib.MediaTable.background["Blizzard Marble"] = [[Interface\FrameGeneral\UI-Background-Marble]]
lib.MediaTable.background["Blizzard Out of Control"] = [[Interface\FullScreenTextures\OutOfControl]]
lib.MediaTable.background["Blizzard Parchment"] = [[Interface\AchievementFrame\UI-Achievement-Parchment-Horizontal]]
lib.MediaTable.background["Blizzard Parchment 2"] = [[Interface\AchievementFrame\UI-GuildAchievement-Parchment-Horizontal]]
lib.MediaTable.background["Blizzard Rock"] = [[Interface\FrameGeneral\UI-Background-Rock]]
lib.MediaTable.background["Blizzard Tabard Background"] = [[Interface\TabardFrame\TabardFrameBackground]]
lib.MediaTable.background["Blizzard Tooltip"] = [[Interface\Tooltips\UI-Tooltip-Background]]
lib.MediaTable.background["Solid"] = [[Interface\Buttons\WHITE8X8]]
 
-- BORDER
if not lib.MediaTable.border then lib.MediaTable.border = {} end
lib.MediaTable.border["None"] = [[Interface\None]]
lib.MediaTable.border["Blizzard Achievement Wood"] = [[Interface\AchievementFrame\UI-Achievement-WoodBorder]]
lib.MediaTable.border["Blizzard Chat Bubble"] = [[Interface\Tooltips\ChatBubble-Backdrop]]
lib.MediaTable.border["Blizzard Dialog"] = [[Interface\DialogFrame\UI-DialogBox-Border]]
lib.MediaTable.border["Blizzard Dialog Gold"] = [[Interface\DialogFrame\UI-DialogBox-Gold-Border]]
lib.MediaTable.border["Blizzard Party"] = [[Interface\CHARACTERFRAME\UI-Party-Border]]
lib.MediaTable.border["Blizzard Tooltip"] = [[Interface\Tooltips\UI-Tooltip-Border]]
 
-- FONT
if not lib.MediaTable.font then lib.MediaTable.font = {} end
local SML_MT_font = lib.MediaTable.font
if locale == "koKR" then
LOCALE_MASK = lib.LOCALE_BIT_koKR
--
SML_MT_font["굵은 글꼴"] = [[Fonts\2002B.TTF]]
SML_MT_font["기본 글꼴"] = [[Fonts\2002.TTF]]
SML_MT_font["데미지 글꼴"] = [[Fonts\K_Damage.TTF]]
SML_MT_font["퀘스트 글꼴"] = [[Fonts\K_Pagetext.TTF]]
--
lib.DefaultMedia["font"] = "기본 글꼴" -- someone from koKR please adjust if needed
--
elseif locale == "zhCN" then
LOCALE_MASK = lib.LOCALE_BIT_zhCN
--
SML_MT_font["伤害数字"] = [[Fonts\ZYKai_C.ttf]]
SML_MT_font["默认"] = [[Fonts\ZYKai_T.ttf]]
SML_MT_font["聊天"] = [[Fonts\ZYHei.ttf]]
--
lib.DefaultMedia["font"] = "默认" -- someone from zhCN please adjust if needed
--
elseif locale == "zhTW" then
LOCALE_MASK = lib.LOCALE_BIT_zhTW
--
SML_MT_font["提示訊息"] = [[Fonts\bHEI00M.ttf]]
SML_MT_font["聊天"] = [[Fonts\bHEI01B.ttf]]
SML_MT_font["傷害數字"] = [[Fonts\bKAI00M.ttf]]
SML_MT_font["預設"] = [[Fonts\bLEI00D.ttf]]
--
lib.DefaultMedia["font"] = "預設" -- someone from zhTW please adjust if needed
 
elseif locale == "ruRU" then
LOCALE_MASK = lib.LOCALE_BIT_ruRU
--
SML_MT_font["Arial Narrow"] = [[Fonts\ARIALN.TTF]]
SML_MT_font["Friz Quadrata TT"] = [[Fonts\FRIZQT__.TTF]]
SML_MT_font["Morpheus"] = [[Fonts\MORPHEUS.TTF]]
SML_MT_font["Nimrod MT"] = [[Fonts\NIM_____.ttf]]
SML_MT_font["Skurri"] = [[Fonts\SKURRI.TTF]]
--
lib.DefaultMedia.font = "Friz Quadrata TT"
--
else
LOCALE_MASK = lib.LOCALE_BIT_western
locale_is_western = true
--
SML_MT_font["Arial Narrow"] = [[Fonts\ARIALN.TTF]]
SML_MT_font["Friz Quadrata TT"] = [[Fonts\FRIZQT__.TTF]]
SML_MT_font["Morpheus"] = [[Fonts\MORPHEUS.TTF]]
SML_MT_font["Skurri"] = [[Fonts\SKURRI.TTF]]
--
lib.DefaultMedia.font = "Friz Quadrata TT"
--
end
 
-- STATUSBAR
if not lib.MediaTable.statusbar then lib.MediaTable.statusbar = {} end
lib.MediaTable.statusbar["Blizzard"] = [[Interface\TargetingFrame\UI-StatusBar]]
lib.MediaTable.statusbar["Blizzard Character Skills Bar"] = [[Interface\PaperDollInfoFrame\UI-Character-Skills-Bar]]
lib.DefaultMedia.statusbar = "Blizzard"
 
-- SOUND
if not lib.MediaTable.sound then lib.MediaTable.sound = {} end
lib.MediaTable.sound["None"] = [[Interface\Quiet.ogg]] -- Relies on the fact that PlaySound[File] doesn't error on non-existing input.
lib.DefaultMedia.sound = "None"
 
local function rebuildMediaList(mediatype)
local mtable = mediaTable[mediatype]
if not mtable then return end
if not mediaList[mediatype] then mediaList[mediatype] = {} end
local mlist = mediaList[mediatype]
-- list can only get larger, so simply overwrite it
local i = 0
for k in pairs(mtable) do
i = i + 1
mlist[i] = k
end
table_sort(mlist)
end
 
function lib:Register(mediatype, key, data, langmask)
if type(mediatype) ~= "string" then
error(MAJOR..":Register(mediatype, key, data, langmask) - mediatype must be string, got "..type(mediatype))
end
if type(key) ~= "string" then
error(MAJOR..":Register(mediatype, key, data, langmask) - key must be string, got "..type(key))
end
mediatype = mediatype:lower()
if mediatype == lib.MediaType.FONT and ((langmask and band(langmask, LOCALE_MASK) == 0) or not (langmask or locale_is_western)) then return false end
if not mediaTable[mediatype] then mediaTable[mediatype] = {} end
local mtable = mediaTable[mediatype]
if mtable[key] then return false end
 
mtable[key] = data
rebuildMediaList(mediatype)
self.callbacks:Fire("LibSharedMedia_Registered", mediatype, key)
return true
end
 
function lib:Fetch(mediatype, key, noDefault)
local mtt = mediaTable[mediatype]
local overridekey = overrideMedia[mediatype]
local result = mtt and ((overridekey and mtt[overridekey] or mtt[key]) or (not noDefault and defaultMedia[mediatype] and mtt[defaultMedia[mediatype]])) or nil
 
return result
end
 
function lib:IsValid(mediatype, key)
return mediaTable[mediatype] and (not key or mediaTable[mediatype][key]) and true or false
end
 
function lib:HashTable(mediatype)
return mediaTable[mediatype]
end
 
function lib:List(mediatype)
if not mediaTable[mediatype] then
return nil
end
if not mediaList[mediatype] then
rebuildMediaList(mediatype)
end
return mediaList[mediatype]
end
 
function lib:GetGlobal(mediatype)
return overrideMedia[mediatype]
end
 
function lib:SetGlobal(mediatype, key)
if not mediaTable[mediatype] then
return false
end
overrideMedia[mediatype] = (key and mediaTable[mediatype][key]) and key or nil
self.callbacks:Fire("LibSharedMedia_SetGlobal", mediatype, overrideMedia[mediatype])
return true
end
 
function lib:GetDefault(mediatype)
return defaultMedia[mediatype]
end
 
function lib:SetDefault(mediatype, key)
if mediaTable[mediatype] and mediaTable[mediatype][key] and not defaultMedia[mediatype] then
defaultMedia[mediatype] = key
return true
else
return false
end
end
Property changes : Added: svn:eol-style + native
tags/4.0.3/libs/AceDB-3.0.lua New file
0,0 → 1,728
--- **AceDB-3.0** manages the SavedVariables of your addon.
-- It offers profile management, smart defaults and namespaces for modules.\\
-- Data can be saved in different data-types, depending on its intended usage.
-- The most common data-type is the `profile` type, which allows the user to choose
-- the active profile, and manage the profiles of all of his characters.\\
-- The following data types are available:
-- * **char** Character-specific data. Every character has its own database.
-- * **realm** Realm-specific data. All of the players characters on the same realm share this database.
-- * **class** Class-specific data. All of the players characters of the same class share this database.
-- * **race** Race-specific data. All of the players characters of the same race share this database.
-- * **faction** Faction-specific data. All of the players characters of the same faction share this database.
-- * **factionrealm** Faction and realm specific data. All of the players characters on the same realm and of the same faction share this database.
-- * **global** Global Data. All characters on the same account share this database.
-- * **profile** Profile-specific data. All characters using the same profile share this database. The user can control which profile should be used.
--
-- Creating a new Database using the `:New` function will return a new DBObject. A database will inherit all functions
-- of the DBObjectLib listed here. \\
-- If you create a new namespaced child-database (`:RegisterNamespace`), you'll get a DBObject as well, but note
-- that the child-databases cannot individually change their profile, and are linked to their parents profile - and because of that,
-- the profile related APIs are not available. Only `:RegisterDefaults` and `:ResetProfile` are available on child-databases.
--
-- For more details on how to use AceDB-3.0, see the [[AceDB-3.0 Tutorial]].
--
-- You may also be interested in [[libdualspec-1-0|LibDualSpec-1.0]] to do profile switching automatically when switching specs.
--
-- @usage
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("DBExample")
--
-- -- declare defaults to be used in the DB
-- local defaults = {
-- profile = {
-- setting = true,
-- }
-- }
--
-- function MyAddon:OnInitialize()
-- -- Assuming the .toc says ## SavedVariables: MyAddonDB
-- self.db = LibStub("AceDB-3.0"):New("MyAddonDB", defaults, true)
-- end
-- @class file
-- @name AceDB-3.0.lua
-- @release $Id: AceDB-3.0.lua 940 2010-06-19 08:01:47Z nevcairiel $
local ACEDB_MAJOR, ACEDB_MINOR = "AceDB-3.0", 21
local AceDB, oldminor = LibStub:NewLibrary(ACEDB_MAJOR, ACEDB_MINOR)
 
if not AceDB then return end -- No upgrade needed
 
-- Lua APIs
local type, pairs, next, error = type, pairs, next, error
local setmetatable, getmetatable, rawset, rawget = setmetatable, getmetatable, rawset, rawget
 
-- WoW APIs
local _G = _G
 
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
-- List them here for Mikk's FindGlobals script
-- GLOBALS: LibStub
 
AceDB.db_registry = AceDB.db_registry or {}
AceDB.frame = AceDB.frame or CreateFrame("Frame")
 
local CallbackHandler
local CallbackDummy = { Fire = function() end }
 
local DBObjectLib = {}
 
--[[-------------------------------------------------------------------------
AceDB Utility Functions
---------------------------------------------------------------------------]]
 
-- Simple shallow copy for copying defaults
local function copyTable(src, dest)
if type(dest) ~= "table" then dest = {} end
if type(src) == "table" then
for k,v in pairs(src) do
if type(v) == "table" then
-- try to index the key first so that the metatable creates the defaults, if set, and use that table
v = copyTable(v, dest[k])
end
dest[k] = v
end
end
return dest
end
 
-- Called to add defaults to a section of the database
--
-- When a ["*"] default section is indexed with a new key, a table is returned
-- and set in the host table. These tables must be cleaned up by removeDefaults
-- in order to ensure we don't write empty default tables.
local function copyDefaults(dest, src)
-- this happens if some value in the SV overwrites our default value with a non-table
--if type(dest) ~= "table" then return end
for k, v in pairs(src) do
if k == "*" or k == "**" then
if type(v) == "table" then
-- This is a metatable used for table defaults
local mt = {
-- This handles the lookup and creation of new subtables
__index = function(t,k)
if k == nil then return nil end
local tbl = {}
copyDefaults(tbl, v)
rawset(t, k, tbl)
return tbl
end,
}
setmetatable(dest, mt)
-- handle already existing tables in the SV
for dk, dv in pairs(dest) do
if not rawget(src, dk) and type(dv) == "table" then
copyDefaults(dv, v)
end
end
else
-- Values are not tables, so this is just a simple return
local mt = {__index = function(t,k) return k~=nil and v or nil end}
setmetatable(dest, mt)
end
elseif type(v) == "table" then
if not rawget(dest, k) then rawset(dest, k, {}) end
if type(dest[k]) == "table" then
copyDefaults(dest[k], v)
if src['**'] then
copyDefaults(dest[k], src['**'])
end
end
else
if rawget(dest, k) == nil then
rawset(dest, k, v)
end
end
end
end
 
-- Called to remove all defaults in the default table from the database
local function removeDefaults(db, defaults, blocker)
-- remove all metatables from the db, so we don't accidentally create new sub-tables through them
setmetatable(db, nil)
-- loop through the defaults and remove their content
for k,v in pairs(defaults) do
if k == "*" or k == "**" then
if type(v) == "table" then
-- Loop through all the actual k,v pairs and remove
for key, value in pairs(db) do
if type(value) == "table" then
-- if the key was not explicitly specified in the defaults table, just strip everything from * and ** tables
if defaults[key] == nil and (not blocker or blocker[key] == nil) then
removeDefaults(value, v)
-- if the table is empty afterwards, remove it
if next(value) == nil then
db[key] = nil
end
-- if it was specified, only strip ** content, but block values which were set in the key table
elseif k == "**" then
removeDefaults(value, v, defaults[key])
end
end
end
elseif k == "*" then
-- check for non-table default
for key, value in pairs(db) do
if defaults[key] == nil and v == value then
db[key] = nil
end
end
end
elseif type(v) == "table" and type(db[k]) == "table" then
-- if a blocker was set, dive into it, to allow multi-level defaults
removeDefaults(db[k], v, blocker and blocker[k])
if next(db[k]) == nil then
db[k] = nil
end
else
-- check if the current value matches the default, and that its not blocked by another defaults table
if db[k] == defaults[k] and (not blocker or blocker[k] == nil) then
db[k] = nil
end
end
end
end
 
-- This is called when a table section is first accessed, to set up the defaults
local function initSection(db, section, svstore, key, defaults)
local sv = rawget(db, "sv")
 
local tableCreated
if not sv[svstore] then sv[svstore] = {} end
if not sv[svstore][key] then
sv[svstore][key] = {}
tableCreated = true
end
 
local tbl = sv[svstore][key]
 
if defaults then
copyDefaults(tbl, defaults)
end
rawset(db, section, tbl)
 
return tableCreated, tbl
end
 
-- Metatable to handle the dynamic creation of sections and copying of sections.
local dbmt = {
__index = function(t, section)
local keys = rawget(t, "keys")
local key = keys[section]
if key then
local defaultTbl = rawget(t, "defaults")
local defaults = defaultTbl and defaultTbl[section]
 
if section == "profile" then
local new = initSection(t, section, "profiles", key, defaults)
if new then
-- Callback: OnNewProfile, database, newProfileKey
t.callbacks:Fire("OnNewProfile", t, key)
end
elseif section == "profiles" then
local sv = rawget(t, "sv")
if not sv.profiles then sv.profiles = {} end
rawset(t, "profiles", sv.profiles)
elseif section == "global" then
local sv = rawget(t, "sv")
if not sv.global then sv.global = {} end
if defaults then
copyDefaults(sv.global, defaults)
end
rawset(t, section, sv.global)
else
initSection(t, section, section, key, defaults)
end
end
 
return rawget(t, section)
end
}
 
local function validateDefaults(defaults, keyTbl, offset)
if not defaults then return end
offset = offset or 0
for k in pairs(defaults) do
if not keyTbl[k] or k == "profiles" then
error(("Usage: AceDBObject:RegisterDefaults(defaults): '%s' is not a valid datatype."):format(k), 3 + offset)
end
end
end
 
local preserve_keys = {
["callbacks"] = true,
["RegisterCallback"] = true,
["UnregisterCallback"] = true,
["UnregisterAllCallbacks"] = true,
["children"] = true,
}
 
local realmKey = GetRealmName()
local charKey = UnitName("player") .. " - " .. realmKey
local _, classKey = UnitClass("player")
local _, raceKey = UnitRace("player")
local factionKey = UnitFactionGroup("player")
local factionrealmKey = factionKey .. " - " .. realmKey
-- Actual database initialization function
local function initdb(sv, defaults, defaultProfile, olddb, parent)
-- Generate the database keys for each section
 
-- map "true" to our "Default" profile
if defaultProfile == true then defaultProfile = "Default" end
 
local profileKey
if not parent then
-- Make a container for profile keys
if not sv.profileKeys then sv.profileKeys = {} end
 
-- Try to get the profile selected from the char db
profileKey = sv.profileKeys[charKey] or defaultProfile or charKey
 
-- save the selected profile for later
sv.profileKeys[charKey] = profileKey
else
-- Use the profile of the parents DB
profileKey = parent.keys.profile or defaultProfile or charKey
 
-- clear the profileKeys in the DB, namespaces don't need to store them
sv.profileKeys = nil
end
 
-- This table contains keys that enable the dynamic creation
-- of each section of the table. The 'global' and 'profiles'
-- have a key of true, since they are handled in a special case
local keyTbl= {
["char"] = charKey,
["realm"] = realmKey,
["class"] = classKey,
["race"] = raceKey,
["faction"] = factionKey,
["factionrealm"] = factionrealmKey,
["profile"] = profileKey,
["global"] = true,
["profiles"] = true,
}
 
validateDefaults(defaults, keyTbl, 1)
 
-- This allows us to use this function to reset an entire database
-- Clear out the old database
if olddb then
for k,v in pairs(olddb) do if not preserve_keys[k] then olddb[k] = nil end end
end
 
-- Give this database the metatable so it initializes dynamically
local db = setmetatable(olddb or {}, dbmt)
 
if not rawget(db, "callbacks") then
-- try to load CallbackHandler-1.0 if it loaded after our library
if not CallbackHandler then CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0", true) end
db.callbacks = CallbackHandler and CallbackHandler:New(db) or CallbackDummy
end
 
-- Copy methods locally into the database object, to avoid hitting
-- the metatable when calling methods
 
if not parent then
for name, func in pairs(DBObjectLib) do
db[name] = func
end
else
-- hack this one in
db.RegisterDefaults = DBObjectLib.RegisterDefaults
db.ResetProfile = DBObjectLib.ResetProfile
end
 
-- Set some properties in the database object
db.profiles = sv.profiles
db.keys = keyTbl
db.sv = sv
--db.sv_name = name
db.defaults = defaults
db.parent = parent
 
-- store the DB in the registry
AceDB.db_registry[db] = true
 
return db
end
 
-- handle PLAYER_LOGOUT
-- strip all defaults from all databases
-- and cleans up empty sections
local function logoutHandler(frame, event)
if event == "PLAYER_LOGOUT" then
for db in pairs(AceDB.db_registry) do
db.callbacks:Fire("OnDatabaseShutdown", db)
db:RegisterDefaults(nil)
 
-- cleanup sections that are empty without defaults
local sv = rawget(db, "sv")
for section in pairs(db.keys) do
if rawget(sv, section) then
-- global is special, all other sections have sub-entrys
-- also don't delete empty profiles on main dbs, only on namespaces
if section ~= "global" and (section ~= "profiles" or rawget(db, "parent")) then
for key in pairs(sv[section]) do
if not next(sv[section][key]) then
sv[section][key] = nil
end
end
end
if not next(sv[section]) then
sv[section] = nil
end
end
end
end
end
end
 
AceDB.frame:RegisterEvent("PLAYER_LOGOUT")
AceDB.frame:SetScript("OnEvent", logoutHandler)
 
 
--[[-------------------------------------------------------------------------
AceDB Object Method Definitions
---------------------------------------------------------------------------]]
 
--- Sets the defaults table for the given database object by clearing any
-- that are currently set, and then setting the new defaults.
-- @param defaults A table of defaults for this database
function DBObjectLib:RegisterDefaults(defaults)
if defaults and type(defaults) ~= "table" then
error("Usage: AceDBObject:RegisterDefaults(defaults): 'defaults' - table or nil expected.", 2)
end
 
validateDefaults(defaults, self.keys)
 
-- Remove any currently set defaults
if self.defaults then
for section,key in pairs(self.keys) do
if self.defaults[section] and rawget(self, section) then
removeDefaults(self[section], self.defaults[section])
end
end
end
 
-- Set the DBObject.defaults table
self.defaults = defaults
 
-- Copy in any defaults, only touching those sections already created
if defaults then
for section,key in pairs(self.keys) do
if defaults[section] and rawget(self, section) then
copyDefaults(self[section], defaults[section])
end
end
end
end
 
--- Changes the profile of the database and all of it's namespaces to the
-- supplied named profile
-- @param name The name of the profile to set as the current profile
function DBObjectLib:SetProfile(name)
if type(name) ~= "string" then
error("Usage: AceDBObject:SetProfile(name): 'name' - string expected.", 2)
end
 
-- changing to the same profile, dont do anything
if name == self.keys.profile then return end
 
local oldProfile = self.profile
local defaults = self.defaults and self.defaults.profile
 
-- Callback: OnProfileShutdown, database
self.callbacks:Fire("OnProfileShutdown", self)
 
if oldProfile and defaults then
-- Remove the defaults from the old profile
removeDefaults(oldProfile, defaults)
end
 
self.profile = nil
self.keys["profile"] = name
 
-- if the storage exists, save the new profile
-- this won't exist on namespaces.
if self.sv.profileKeys then
self.sv.profileKeys[charKey] = name
end
 
-- populate to child namespaces
if self.children then
for _, db in pairs(self.children) do
DBObjectLib.SetProfile(db, name)
end
end
 
-- Callback: OnProfileChanged, database, newProfileKey
self.callbacks:Fire("OnProfileChanged", self, name)
end
 
--- Returns a table with the names of the existing profiles in the database.
-- You can optionally supply a table to re-use for this purpose.
-- @param tbl A table to store the profile names in (optional)
function DBObjectLib:GetProfiles(tbl)
if tbl and type(tbl) ~= "table" then
error("Usage: AceDBObject:GetProfiles(tbl): 'tbl' - table or nil expected.", 2)
end
 
-- Clear the container table
if tbl then
for k,v in pairs(tbl) do tbl[k] = nil end
else
tbl = {}
end
 
local curProfile = self.keys.profile
 
local i = 0
for profileKey in pairs(self.profiles) do
i = i + 1
tbl[i] = profileKey
if curProfile and profileKey == curProfile then curProfile = nil end
end
 
-- Add the current profile, if it hasn't been created yet
if curProfile then
i = i + 1
tbl[i] = curProfile
end
 
return tbl, i
end
 
--- Returns the current profile name used by the database
function DBObjectLib:GetCurrentProfile()
return self.keys.profile
end
 
--- Deletes a named profile. This profile must not be the active profile.
-- @param name The name of the profile to be deleted
-- @param silent If true, do not raise an error when the profile does not exist
function DBObjectLib:DeleteProfile(name, silent)
if type(name) ~= "string" then
error("Usage: AceDBObject:DeleteProfile(name): 'name' - string expected.", 2)
end
 
if self.keys.profile == name then
error("Cannot delete the active profile in an AceDBObject.", 2)
end
 
if not rawget(self.profiles, name) and not silent then
error("Cannot delete profile '" .. name .. "'. It does not exist.", 2)
end
 
self.profiles[name] = nil
 
-- populate to child namespaces
if self.children then
for _, db in pairs(self.children) do
DBObjectLib.DeleteProfile(db, name, true)
end
end
 
-- Callback: OnProfileDeleted, database, profileKey
self.callbacks:Fire("OnProfileDeleted", self, name)
end
 
--- Copies a named profile into the current profile, overwriting any conflicting
-- settings.
-- @param name The name of the profile to be copied into the current profile
-- @param silent If true, do not raise an error when the profile does not exist
function DBObjectLib:CopyProfile(name, silent)
if type(name) ~= "string" then
error("Usage: AceDBObject:CopyProfile(name): 'name' - string expected.", 2)
end
 
if name == self.keys.profile then
error("Cannot have the same source and destination profiles.", 2)
end
 
if not rawget(self.profiles, name) and not silent then
error("Cannot copy profile '" .. name .. "'. It does not exist.", 2)
end
 
-- Reset the profile before copying
DBObjectLib.ResetProfile(self, nil, true)
 
local profile = self.profile
local source = self.profiles[name]
 
copyTable(source, profile)
 
-- populate to child namespaces
if self.children then
for _, db in pairs(self.children) do
DBObjectLib.CopyProfile(db, name, true)
end
end
 
-- Callback: OnProfileCopied, database, sourceProfileKey
self.callbacks:Fire("OnProfileCopied", self, name)
end
 
--- Resets the current profile to the default values (if specified).
-- @param noChildren if set to true, the reset will not be populated to the child namespaces of this DB object
-- @param noCallbacks if set to true, won't fire the OnProfileReset callback
function DBObjectLib:ResetProfile(noChildren, noCallbacks)
local profile = self.profile
 
for k,v in pairs(profile) do
profile[k] = nil
end
 
local defaults = self.defaults and self.defaults.profile
if defaults then
copyDefaults(profile, defaults)
end
 
-- populate to child namespaces
if self.children and not noChildren then
for _, db in pairs(self.children) do
DBObjectLib.ResetProfile(db, nil, noCallbacks)
end
end
 
-- Callback: OnProfileReset, database
if not noCallbacks then
self.callbacks:Fire("OnProfileReset", self)
end
end
 
--- Resets the entire database, using the string defaultProfile as the new default
-- profile.
-- @param defaultProfile The profile name to use as the default
function DBObjectLib:ResetDB(defaultProfile)
if defaultProfile and type(defaultProfile) ~= "string" then
error("Usage: AceDBObject:ResetDB(defaultProfile): 'defaultProfile' - string or nil expected.", 2)
end
 
local sv = self.sv
for k,v in pairs(sv) do
sv[k] = nil
end
 
local parent = self.parent
 
initdb(sv, self.defaults, defaultProfile, self)
 
-- fix the child namespaces
if self.children then
if not sv.namespaces then sv.namespaces = {} end
for name, db in pairs(self.children) do
if not sv.namespaces[name] then sv.namespaces[name] = {} end
initdb(sv.namespaces[name], db.defaults, self.keys.profile, db, self)
end
end
 
-- Callback: OnDatabaseReset, database
self.callbacks:Fire("OnDatabaseReset", self)
-- Callback: OnProfileChanged, database, profileKey
self.callbacks:Fire("OnProfileChanged", self, self.keys["profile"])
 
return self
end
 
--- Creates a new database namespace, directly tied to the database. This
-- is a full scale database in it's own rights other than the fact that
-- it cannot control its profile individually
-- @param name The name of the new namespace
-- @param defaults A table of values to use as defaults
function DBObjectLib:RegisterNamespace(name, defaults)
if type(name) ~= "string" then
error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - string expected.", 2)
end
if defaults and type(defaults) ~= "table" then
error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'defaults' - table or nil expected.", 2)
end
if self.children and self.children[name] then
error ("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - a namespace with that name already exists.", 2)
end
 
local sv = self.sv
if not sv.namespaces then sv.namespaces = {} end
if not sv.namespaces[name] then
sv.namespaces[name] = {}
end
 
local newDB = initdb(sv.namespaces[name], defaults, self.keys.profile, nil, self)
 
if not self.children then self.children = {} end
self.children[name] = newDB
return newDB
end
 
--- Returns an already existing namespace from the database object.
-- @param name The name of the new namespace
-- @param silent if true, the addon is optional, silently return nil if its not found
-- @usage
-- local namespace = self.db:GetNamespace('namespace')
-- @return the namespace object if found
function DBObjectLib:GetNamespace(name, silent)
if type(name) ~= "string" then
error("Usage: AceDBObject:GetNamespace(name): 'name' - string expected.", 2)
end
if not silent and not (self.children and self.children[name]) then
error ("Usage: AceDBObject:GetNamespace(name): 'name' - namespace does not exist.", 2)
end
if not self.children then self.children = {} end
return self.children[name]
end
 
--[[-------------------------------------------------------------------------
AceDB Exposed Methods
---------------------------------------------------------------------------]]
 
--- Creates a new database object that can be used to handle database settings and profiles.
-- By default, an empty DB is created, using a character specific profile.
--
-- You can override the default profile used by passing any profile name as the third argument,
-- or by passing //true// as the third argument to use a globally shared profile called "Default".
--
-- Note that there is no token replacement in the default profile name, passing a defaultProfile as "char"
-- will use a profile named "char", and not a character-specific profile.
-- @param tbl The name of variable, or table to use for the database
-- @param defaults A table of database defaults
-- @param defaultProfile The name of the default profile. If not set, a character specific profile will be used as the default.
-- You can also pass //true// to use a shared global profile called "Default".
-- @usage
-- -- Create an empty DB using a character-specific default profile.
-- self.db = LibStub("AceDB-3.0"):New("MyAddonDB")
-- @usage
-- -- Create a DB using defaults and using a shared default profile
-- self.db = LibStub("AceDB-3.0"):New("MyAddonDB", defaults, true)
function AceDB:New(tbl, defaults, defaultProfile)
if type(tbl) == "string" then
local name = tbl
tbl = _G[name]
if not tbl then
tbl = {}
_G[name] = tbl
end
end
 
if type(tbl) ~= "table" then
error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'tbl' - table expected.", 2)
end
 
if defaults and type(defaults) ~= "table" then
error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaults' - table expected.", 2)
end
 
if defaultProfile and type(defaultProfile) ~= "string" and defaultProfile ~= true then
error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaultProfile' - string or true expected.", 2)
end
 
return initdb(tbl, defaults, defaultProfile)
end
 
-- upgrade existing databases
for db in pairs(AceDB.db_registry) do
if not db.parent then
for name,func in pairs(DBObjectLib) do
db[name] = func
end
else
db.RegisterDefaults = DBObjectLib.RegisterDefaults
db.ResetProfile = DBObjectLib.ResetProfile
end
end
Property changes : Added: svn:eol-style + native
tags/4.0.3/minimap.lua New file
0,0 → 1,131
local addonName, addon = ...
 
local L = LibStub("AceLocale-3.0"):GetLocale(addonName)
local templates = addon.templates
 
 
local function onUpdate(self)
local xpos, ypos = GetCursorPosition()
local xmin, ymin = Minimap:GetLeft(), Minimap:GetBottom()
 
xpos = xmin - xpos / Minimap:GetEffectiveScale() + 70
ypos = ypos / Minimap:GetEffectiveScale() - ymin - 70
 
self.profile.pos = atan2(ypos, xpos)
self:Move()
end
 
 
local minimap = CreateFrame("Button", nil, Minimap)
minimap:SetToplevel(true)
minimap:SetMovable(true)
minimap:RegisterForClicks("LeftButtonUp", "RightButtonUp")
minimap:RegisterForDrag("LeftButton")
minimap:SetPoint("TOPLEFT", -15, 0)
minimap:SetSize(32, 32)
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
display:Toggle()
elseif button == "RightButton" then
addon:OpenConfig()
end
end)
minimap:SetScript("OnEnter", function(self)
GameTooltip:SetOwner(self, "ANCHOR_LEFT")
GameTooltip:AddLine("Critline")
if addon.display then
GameTooltip:AddLine(L["Left-click to toggle summary frame"], HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
end
GameTooltip:AddLine(L["Right-click to open options"], HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
if not self.profile.locked then
GameTooltip:AddLine(L["Drag to move"], HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
end
GameTooltip:Show()
end)
minimap:SetScript("OnLeave", GameTooltip_Hide)
minimap:SetScript("OnDragStart", function(self) self:SetScript("OnUpdate", onUpdate) end)
minimap:SetScript("OnDragStop", function(self) self:SetScript("OnUpdate", nil) end)
minimap:SetScript("OnHide", function(self) self:SetScript("OnUpdate", nil) end)
 
local icon = minimap:CreateTexture(nil, "BORDER")
icon:SetTexture(addon.icons.dmg)
icon:SetSize(20, 20)
icon:SetPoint("TOPLEFT", 6, -6)
 
local border = minimap:CreateTexture(nil, "OVERLAY")
border:SetTexture([[Interface\Minimap\MiniMap-TrackingBorder]])
border:SetSize(54, 54)
border:SetPoint("TOPLEFT")
 
 
local config = templates:CreateConfigFrame(L["Minimap button"], addonName, true)
 
local options = {
{
text = L["Show"],
tooltipText = L["Show minimap button."],
setting = "show",
func = function(self)
if self:GetChecked() then
minimap:Show()
else
minimap:Hide()
end
end,
},
{
text = L["Locked"],
tooltipText = L["Lock minimap button."],
setting = "locked",
func = function(self)
minimap:RegisterForDrag(not self:GetChecked() and "LeftButton")
end,
},
}
 
for i, v in ipairs(options) do
local btn = templates:CreateCheckButton(config, v)
if i == 1 then
btn:SetPoint("TOPLEFT", config.title, "BOTTOMLEFT", -2, -16)
else
btn:SetPoint("TOP", options[i - 1], "BOTTOM", 0, -8)
end
btn.module = minimap
options[i] = btn
end
 
 
local defaults = {
profile = {
show = true,
locked = false,
pos = 0,
}
}
 
function minimap:AddonLoaded()
self.db = addon.db:RegisterNamespace("minimap", defaults)
addon.RegisterCallback(self, "SettingsLoaded", "LoadSettings")
end
 
addon.RegisterCallback(minimap, "AddonLoaded")
 
 
function minimap:LoadSettings()
self.profile = self.db.profile
 
for _, btn in ipairs(options) do
btn:LoadSetting()
end
 
self:Move()
end
 
 
function minimap:Move()
local angle = self.profile.pos
self:SetPoint("TOPLEFT", (52 - 80 * cos(angle)), (80 * sin(angle) - 52))
end
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3/splash.lua New file
0,0 → 1,336
local addonName, addon = ...
 
local L = LibStub("AceLocale-3.0"):GetLocale(addonName)
local LSM = LibStub("LibSharedMedia-3.0")
local templates = addon.templates
 
 
local splash = CreateFrame("MessageFrame", nil, UIParent)
splash:SetMovable(true)
splash:RegisterForDrag("LeftButton")
splash:SetSize(512, 96)
splash:SetScript("OnMouseUp", function(self, button)
if button == "RightButton" then
if self.profile.enabled then
addon.RegisterCallback(splash, "NewRecord")
end
self:SetFrameStrata("MEDIUM")
self:EnableMouse(false)
self:SetFading(true)
self:Clear()
end
end)
splash:EnableMouse(false)
splash:SetScript("OnDragStart", splash.StartMoving)
splash:SetScript("OnDragStop", function(self)
self:StopMovingOrSizing()
local pos = self.profile.pos
pos.point, pos.x, pos.y = select(3, self:GetPoint())
end)
 
 
do -- create the options frame
local config = templates:CreateConfigFrame(L["Splash frame"], addonName, true)
 
local options = {}
splash.options = options
 
local checkButtons = {
{
text = L["Enabled"],
tooltipText = L["Shows the new record on the middle of the screen."],
setting = "enabled",
func = function(self)
if self:GetChecked() then
if not splash:IsMouseEnabled() then
addon.RegisterCallback(splash, "NewRecord")
end
else
addon.UnregisterCallback(splash, "NewRecord")
end
end,
},
{
text = L["Use combat text splash"],
tooltipText = L["Enable to use scrolling combat text for \"New record\" messages instead of the default splash frame."],
setting = "sct",
},
}
 
options.checkButtons = checkButtons
 
for i, v in ipairs(checkButtons) do
local btn = templates:CreateCheckButton(config, v)
if i == 1 then
btn:SetPoint("TOPLEFT", config.title, "BOTTOMLEFT", -2, -16)
else
btn:SetPoint("TOP", checkButtons[i - 1], "BOTTOM", 0, -8)
end
btn.module = splash
checkButtons[i] = btn
end
 
options.colorButtons = {}
 
-- splash frame spell name color
local spellColor = templates:CreateColorButton(config)
spellColor:SetPoint("TOP", checkButtons[#checkButtons], "BOTTOM", 0, -13)
spellColor:SetText(L["Spell color"])
spellColor.tooltipText = L["Sets the color for the spell text in the splash frame."]
spellColor.setting = "spell"
options.colorButtons[1] = spellColor
 
-- splash frame amount color
local amountColor = templates:CreateColorButton(config)
amountColor:SetPoint("TOP", spellColor, "BOTTOM", 0, -18)
amountColor:SetText(L["Amount color"])
amountColor.tooltipText = L["Sets the color for the amount text in the splash frame."]
amountColor.setting = "amount"
options.colorButtons[2] = amountColor
 
local sliders = {
{
text = L["Scale"],
tooltipText = L["Sets the scale of the splash frame."],
minValue = 0.5,
maxValue = 1,
valueStep = 0.05,
minText = "50%",
maxText = "100%",
func = function(self)
local value = self:GetValue()
self.value:SetFormattedText("%.0f%%", value * 100)
local os = splash:GetScale()
splash:SetScale(value)
local point, relativeTo, relativePoint, xOff, yOff = splash:GetPoint()
splash:SetPoint(point, relativeTo, relativePoint, (xOff*os/value), (yOff*os/value))
splash.profile.scale = value
end,
},
{
text = L["Duration"],
tooltipText = L["Sets the time (in seconds) the splash frame is visible before fading out."],
minValue = 0,
maxValue = 5,
valueStep = 0.5,
func = function(self)
local value = self:GetValue()
self.value:SetText(value)
splash:SetTimeVisible(value)
splash.profile.duration = value
end,
},
}
 
options.sliders = sliders
 
for i, v in ipairs(sliders) do
local slider = templates:CreateSlider(config, v)
if i == 1 then
slider:SetPoint("TOPLEFT", amountColor, "BOTTOMLEFT", 4, -24)
else
slider:SetPoint("TOP", sliders[i - 1], "BOTTOM", 0, -32)
end
sliders[i] = slider
end
 
local moveSplash = templates:CreateButton(config)
moveSplash:SetPoint("TOP", sliders[2], "BOTTOM", 0, -24)
moveSplash:SetSize(148, 22)
moveSplash:SetText(L["Move splash screen"])
moveSplash:SetScript("OnClick", function()
-- don't want to be interrupted by new records
addon.UnregisterCallback(splash, "NewRecord")
splash:SetFrameStrata("FULLSCREEN")
splash:EnableMouse(true)
splash:SetFading(false)
splash:Clear()
local colors = splash.profile.colors
splash:AddMessage(L["Critline splash frame unlocked"], colors.spell)
splash:AddMessage(L["Drag to move"], colors.amount)
splash:AddMessage(L["Right-click to lock"], colors.amount)
end)
options.button = moveSplash
 
local function onClick(self)
self.owner:SetSelectedValue(self.value)
local font = splash.profile.font
font.name = self.value
splash:SetFont(LSM:Fetch("font", font.name), font.size, font.flags)
end
 
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
info.func = onClick
info.owner = self
UIDropDownMenu_AddButton(info)
end
end
options.font = font
 
local menu = {
{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, {
text = L["Font size"],
tooltipText = L["Sets the font size of the splash frame."],
minValue = 8,
maxValue = 30,
valueStep = 1,
func = function(self)
local value = self:GetValue()
self.value:SetText(value)
local font = splash.profile.font
font.size = value
splash:SetFont(LSM:Fetch("font", font.name), font.size, font.flags)
end,
})
fontSize:SetPoint("TOP", fontFlags, "BOTTOM", 0, -24)
options.fontSize = fontSize
end
 
 
local defaults = {
profile = {
enabled = true,
sct = false,
scale = 1,
duration = 2,
font = {
name = "Skurri",
size = 30,
flags = "OUTLINE",
},
colors = {
spell = {r = 1, g = 1, b = 0},
amount = {r = 1, g = 1, b = 1},
},
pos = {
point = "CENTER"
},
}
}
 
function splash:AddonLoaded()
self.db = addon.db:RegisterNamespace("splash", defaults)
addon.RegisterCallback(self, "SettingsLoaded", "LoadSettings")
end
 
addon.RegisterCallback(splash, "AddonLoaded")
 
 
function splash:LoadSettings()
self.profile = self.db.profile
local options = self.options
 
for _, btn in ipairs(options.checkButtons) do
btn:LoadSetting()
end
 
local colors = self.profile.colors
for _, btn in ipairs(options.colorButtons) do
local color = colors[btn.setting]
btn.swatch:SetVertexColor(color.r, color.g, color.b)
btn.color = color
end
 
local font = self.profile.font
self:SetFont(LSM:Fetch("font", font.name), font.size, font.flags)
 
options.font:SetSelectedValue(font.name)
options.fontFlags:SetSelectedValue(font.flags)
options.fontSize:SetValue(font.size)
 
local pos = self.profile.pos
self:ClearAllPoints()
self:SetPoint(pos.point, pos.x, pos.y)
 
local sliders = options.sliders
sliders[1]:SetValue(self.profile.scale)
sliders[2]:SetValue(self.profile.duration)
end
 
 
local addMessage = splash.AddMessage
 
function splash:AddMessage(msg, color, ...)
addMessage(self, msg, color.r, color.g, color.b, ...)
end
 
 
local red1 = {r = 1, g = 0, b = 0}
local red255 = {r = 255, g = 0, b = 0}
 
function splash:NewRecord(event, tree, spellID, periodic, amount, crit, prevRecord, isFiltered)
if isFiltered then
return
end
 
spell = format(L["New %s record!"], addon:GetFullSpellName(spellID, periodic, true))
amount = addon:ShortenNumber(amount)
if addon.db.profile.oldRecord and prevRecord.amount > 0 then
amount = format("%s (%s)", amount, addon:ShortenNumber(prevRecord.amount))
end
 
local colors = self.profile.colors
local spellColor = colors.spell
local amountColor = colors.amount
 
if self.profile.sct then
-- check if any custom SCT addon is loaded and use it accordingly
if MikSBT then
if crit then
MikSBT.DisplayMessage(L["Critical!"], nil, true, 255, 0, 0)
end
MikSBT.DisplayMessage(spell, nil, true, spellColor.r * 255, spellColor.g * 255, spellColor.b * 255)
MikSBT.DisplayMessage(amount, nil, true, amountColor.r * 255, amountColor.g * 255, amountColor.b * 255)
elseif SCT then
if crit then
SCT:DisplayMessage(L["Critical!"], red255)
end
SCT:DisplayMessage(spell, spellColor)
SCT:DisplayMessage(amount, amountColor)
elseif Parrot then
local Parrot = Parrot:GetModule("Display")
Parrot:ShowMessage(amount, nil, true, amountColor.r, amountColor.g, amountColor.b)
Parrot:ShowMessage(spell, nil, true, spellColor.r, spellColor.g, spellColor.b)
if crit then
Parrot:ShowMessage(L["Critical!"], nil, true, 1, 0, 0)
end
elseif SHOW_COMBAT_TEXT == "1" then
CombatText_AddMessage(amount, CombatText_StandardScroll, amountColor.r, amountColor.g, amountColor.b)
CombatText_AddMessage(spell, CombatText_StandardScroll, spellColor.r, spellColor.g, spellColor.b)
if crit then
CombatText_AddMessage(L["Critical!"], CombatText_StandardScroll, 1, 0, 0)
end
end
else
self:Clear()
if crit then
self:AddMessage(L["Critical!"], red1)
end
self:AddMessage(spell, spellColor)
self:AddMessage(amount, amountColor)
end
end
\ No newline at end of file Property changes : Added: svn:eol-style + native
tags/4.0.3 Property changes : Added: wowi:dirname + Critline