WoWInterface SVN Critline

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /tags
    from Rev 9 to Rev 11
    Reverse comparison

Rev 9 → Rev 11

3.1.3/core.lua New file
0,0 → 1,795
local addonName, Critline = ...
_G.Critline = Critline
 
local L = LibStub("AceLocale-3.0"):GetLocale(addonName)
local templates = Critline.templates
local playerClass = select(2, UnitClass("player"))
local debugging
 
-- auto attack spell
local AUTOATK = GetSpellInfo(6603)
 
-- local 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)
}
 
 
local swingDamage = function(amount, _, school, resisted, _, _, critical)
return AUTOATK, amount, resisted, critical, school
end
 
local spellDamage = function(_, spellName, _, amount, _, school, resisted, _, _, critical)
return spellName, amount, resisted, critical, school
end
 
local healing = function(_, spellName, _, amount, _, _, critical)
return 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,
}
 
 
local recordSorters = {
-- alpha: sort by name
alpha = function(a, b)
if a == b then return end
if a.spellName == b.spellName then
-- sort DoT entries after non DoT
return b.isPeriodic
else
return a.spellName < b.spellName
end
end,
-- crit: sort by crit > normal > name
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
-- equal crit amounts, sort by normal amount instead
local normalA, normalB = (a.normal and a.normal.amount or 0), (b.normal and b.normal.amount or 0)
if normalA == normalB then
-- equal normal amounts, sort by name instead
if a.spellName == b.spellName then
return b.isPeriodic
else
return a.spellName < b.spellName
end
else
return normalA > normalB
end
else
return critA > critB
end
end,
-- normal: sort by normal > crit > name
normal = function(a, b)
if a == b then return end
local normalA, normalB = (a.normal and a.normal.amount or 0), (b.normal and b.normal.amount or 0)
if normalA == normalB then
local critA, critB = (a.crit and a.crit.amount or 0), (b.crit and b.crit.amount or 0)
if critA == critB then
if a.spellName == b.spellName then
return b.isPeriodic
else
return a.spellName < b.spellName
end
else
return critA > critB
end
else
return normalA > normalB
end
end,
}
 
 
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 = {}}
 
 
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("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)
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["Chat output"],
tooltipText = L["Prints new record notifications to the chat frame."],
setting = "chatOutput",
},
{
text = L["Play sound"],
tooltipText = L["Plays a sound on a new record."],
setting = "sound",
},
{
text = L["Screenshot"],
tooltipText = L["Saves a screenshot on a new record."],
setting = "screenshot",
newColumn = true,
},
{
text = L["Detailed tooltip"],
tooltipText = L["Use detailed format in the summary tooltip."],
setting = "detailedTooltip",
func = function(self) Critline:UpdateTooltips() end,
},
{
text = L["Ignore vulnerability"],
tooltipText = L["Enable to ignore additional damage due to vulnerability."],
setting = "ignoreVulnerability",
},
}
 
options.checkButtons = checkButtons
 
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
checkButtons[i] = btn
end
 
-- summary sort dropdown
local menu = {
onClick = function(self)
self.owner:SetSelectedValue(self.value)
Critline.db.profile.tooltipSort = self.value
Critline:UpdateTooltips()
end,
{
text = L["Alphabetically"],
value = "alpha",
},
{
text = L["By normal record"],
value = "normal",
},
{
text = L["By crit record"],
value = "crit",
},
}
 
local sorting = templates:CreateDropDownMenu("CritlineTooltipSorting", config, menu)
sorting:SetFrameWidth(120)
sorting:SetPoint("TOPLEFT", checkButtons[#checkButtons], "BOTTOMLEFT", -15, -24)
sorting.label:SetText(L["Tooltip sorting:"])
 
options.tooltipSort = sorting
end
 
 
SlashCmdList.CRITLINE = function(msg)
msg = msg:trim():lower()
if msg == "debug" then
Critline:ToggleDebug()
elseif msg == "reset" then
local display = Critline.display
if display then
display:ClearAllPoints()
display:SetPoint("CENTER")
end
else
Critline:OpenConfig()
end
end
 
SLASH_CRITLINE1 = "/critline"
SLASH_CRITLINE2 = "/cl"
 
 
local defaults = {
profile = {
PvE = true,
PvP = true,
chatOutput = false,
sound = false,
screenshot = false,
detailedTooltip = false,
ignoreVulnerability = true,
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")
 
-- import old records and mob filter
if CritlineSettings then
for i, v in ipairs({"dmg","heal","pet"}) do
percharDB.profile.spells[v] = CritlineDB[v]
CritlineDB[v] = nil
end
 
if self.filters and CritlineMobFilter then
self.filters.db.global.mobs = CritlineMobFilter
end
end
 
self:LoadSettings()
self:LoadPerCharSettings()
 
self.ADDON_LOADED = nil
end
end
 
 
function Critline:COMBAT_LOG_EVENT_UNFILTERED(timestamp, eventType, 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
 
-- check for filtered auras
if self.filters then
if self.filters:IsAuraEvent(eventType, ..., sourceFlags, destFlags, destGUID) then
return
end
end
 
local isPet
 
-- if sourceGUID is not us or our pet, we leave
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(6, 12), 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 isHeal
local isPeriodic
if eventType:find("_HEAL$") then
isHeal = true
if isPet then
return
end
end
if eventType:find("_PERIODIC_") then
isPeriodic = true
end
 
local spellName, amount, resisted, critical, school = combatEvents[eventType](...)
 
-- below are some checks to see if we want to register the hit at all
if amount <= 0 then
self:Debug(format("Amount <= 0. (%s) Return.", self:GetFullSpellName(tree, spellName, isPeriodic)))
return
end
 
local tree = "dmg"
 
if isPet then
tree = "pet"
elseif isHeal then
tree = "heal"
end
 
local passed, isFiltered, targetLevel
if self.filters then
passed, isFiltered, targetLevel = self.filters:SpellPassesFilters(tree, spellName, ..., isPeriodic, destGUID, destName, school)
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
 
-- we only want damage spells from the pet
if isHeal and isPet then
self:Debug("Pet healing. Return.")
return
end
 
-- exit if not recording tree dmg
if not self.percharDB.profile[tree] then
self:Debug(format("Not recording this tree (%s). Return.", tree))
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, spellName, isPeriodic)
 
-- create spell database entries as required
if not data then
self:Debug(format("Creating data for %s (%s)", self:GetFullSpellName(tree, spellName, isPeriodic), tree))
data = {
spellName = spellName,
isPeriodic = isPeriodic,
}
self:AddSpell(tree, data)
self:UpdateSpells(tree)
end
 
if not data[hitType] then
data[hitType] = {amount = 0}
end
 
data = data[hitType]
 
if amount > data.amount then
local oldAmount = data.amount
data.amount = amount
data.target = destName
data.targetLevel = targetLevel
data.isPvPTarget = isPlayer
 
if not isFiltered then
self:NewRecord(self:GetFullSpellName(tree, spellName, isPeriodic), amount, critical, oldAmount)
end
 
self:UpdateRecords(tree, isFiltered)
end
end
 
 
function Critline: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.tooltipSort:SetSelectedValue(self.db.profile.tooltipSort)
end
 
 
function Critline:LoadPerCharSettings()
callbacks:Fire("PerCharSettingsLoaded")
 
self:UpdateTooltips()
 
for _, btn in ipairs(self.options.checkButtons.percharDB) do
btn:LoadSetting()
end
end
 
 
function Critline:NewRecord(spell, amount, crit, oldAmount)
callbacks:Fire("NewRecord", spell, amount, crit, oldAmount)
 
if self.db.profile.chatOutput then
self:Message(format(L["New %s%s record - %d"], crit and L["critical "] or "", spell, amount))
end
 
if self.db.profile.sound then
PlaySound("LEVELUP", 1, 1, 0, 1, 3)
end
 
if self.db.profile.screenshot then
TakeScreenshot()
end
end
 
 
-- return spell table from database, given tree, spell name and isPeriodic value
function Critline:GetSpellInfo(tree, spellName, periodic)
for i, v in ipairs(self.percharDB.profile.spells[tree]) do
if v.spellName == spellName and v.isPeriodic == periodic then
return v, i
end
end
end
 
 
function Critline:GetFullSpellName(tree, spellName, isPeriodic)
local suffix = ""
if isPeriodic then
if tree == "heal" then
suffix = L[" (HoT)"]
else
suffix = L[" (DoT)"]
end
end
return format("%s%s", spellName, suffix)
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:GetHighest(tree)
local normalRecord, critRecord = 0, 0
local normalSpell, critSpell
 
for _, data in ipairs(self.percharDB.profile.spells[tree]) do
if not (self.filters and self.filters:IsFilteredSpell(tree, data.spellName, data.isPeriodic)) then
local normal = data.normal
if normal and normal.amount > normalRecord then
normalRecord = normal.amount
normalSpell = data.spellName
end
local crit = data.crit
if crit and crit.amount > critRecord then
critRecord = crit.amount
critSpell = data.spellName
end
end
end
return normalRecord, critRecord, normalSpell, critSpell
end
 
 
function Critline:AddSpell(tree, spell)
local spells = self.percharDB.profile.spells[tree]
tinsert(spells, spell)
sort(spells, recordSorters.alpha)
end
 
 
function Critline:DeleteSpell(tree, index)
tremove(self.percharDB.profile.spells[tree], index)
end
 
 
-- this "fires" when spells are added to/removed from the database
function Critline:UpdateSpells(tree)
if tree then
self:UpdateTooltip(tree)
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
self:UpdateTooltip(tree)
callbacks:Fire("RecordsChanged", tree, isFiltered)
else
for k in pairs(tooltips) do
self:UpdateRecords(k, isFiltered)
end
end
end
 
 
function Critline:ShowTooltip(tree)
GameTooltip:AddLine("Critline "..treeNames[tree], HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
for i, v in ipairs(tooltips[tree]) do
local left, right = v:match("(.+)\t(.+)")
if left and right then
GameTooltip:AddDoubleLine(left, right)
else
GameTooltip:AddLine(v)
end
end
GameTooltip:Show()
end
 
 
function Critline:UpdateTooltips()
for k in pairs(tooltips) do
self:UpdateTooltip(k)
end
end
 
 
local sortedSpells = {}
 
function Critline:UpdateTooltip(tree)
local line = " |cffc0c0c0%s:|r %s\t%s (%s)"
 
wipe(sortedSpells)
 
local n = 1
 
for i, v in ipairs(self.percharDB.profile.spells[tree]) do
if (v.normal or v.crit) and not (self.filters and self.filters:IsFilteredSpell(tree, v.spellName, v.isPeriodic)) then
sortedSpells[n] = {
spellName = v.spellName,
isPeriodic = v.isPeriodic,
normal = v.normal,
crit = v.crit,
}
n = n + 1
end
end
 
sort(sortedSpells, recordSorters[self.db.profile.tooltipSort])
 
local tooltip = tooltips[tree]
wipe(tooltip)
 
local normalRecord, critRecord = self:GetHighest(tree)
n = 1
 
for _, v in ipairs(sortedSpells) do
-- if this is a DoT/HoT, and a direct entry exists, add the proper suffix
if v.isPeriodic then
for _, v2 in ipairs(sortedSpells) do
if v2.spellName == v.spellName and not v2.isPeriodic then
v.spellName = self:GetFullSpellName(tree, v.spellName, true)
break
end
end
end
 
local normalAmount, critAmount = HIGHLIGHT_FONT_COLOR_CODE..(0)..FONT_COLOR_CODE_CLOSE, HIGHLIGHT_FONT_COLOR_CODE..(0)..FONT_COLOR_CODE_CLOSE
 
-- color the top score amount green
local normal = v.normal
if normal then
normalAmount = (normal.amount == normalRecord and GREEN_FONT_COLOR_CODE or HIGHLIGHT_FONT_COLOR_CODE)..normal.amount..FONT_COLOR_CODE_CLOSE
end
 
local crit = v.crit
if crit then
critAmount = (crit.amount == critRecord and GREEN_FONT_COLOR_CODE or HIGHLIGHT_FONT_COLOR_CODE)..crit.amount..FONT_COLOR_CODE_CLOSE
end
 
if self.db.profile.detailedTooltip then
tooltip[n] = v.spellName
if normal then
n = n + 1
local target = HIGHLIGHT_FONT_COLOR_CODE..self:GetFullTargetName(normal)..FONT_COLOR_CODE_CLOSE
local level = (normal.targetLevel > 0) and normal.targetLevel or "??"
tooltip[n] = format(line, L["Normal"], normalAmount, target, level)
end
if crit then
n = n + 1
local target = HIGHLIGHT_FONT_COLOR_CODE..self:GetFullTargetName(crit)..FONT_COLOR_CODE_CLOSE
local level = (crit.targetLevel > 0) and crit.targetLevel or "??"
tooltip[n] = format(line, L["Crit"], critAmount, target, level)
end
else
tooltip[n] = format("%s\t%s%s", v.spellName, normalAmount, (crit and "/"..critAmount or ""))
end
 
n = n + 1
end
 
if #tooltip == 0 then
tooltip[1] = L["No records"]
end
end
\ No newline at end of file Property changes : Added: svn:eol-style + native
3.1.3/filters.lua New file
0,0 → 1,958
local addonName, addon = ...
 
local L = LibStub("AceLocale-3.0"):GetLocale(addonName)
local templates = addon.templates
 
local CombatLog_Object_IsA = CombatLog_Object_IsA
 
local COMBATLOG_FILTER_ME = COMBATLOG_FILTER_ME
 
-- amount of buttons in the spell, mob and aura filter scroll lists
local NUMSPELLBUTTONS = 8
local SPELLBUTTONHEIGHT = 22
local NUMFILTERBUTTONS = 10
local FILTERBUTTONHEIGHT = 16
 
-- mobs whose received hits won't be tracked due to various vulnerabilities
local specialMobs = {
[12460] = true, -- Death Talon Wyrmguard
[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
[34797] = true, -- Icehowl
[38567] = true, -- Phantom Hallucination
[39698] = true, -- Karsh Steelbender
[40793] = true, -- Ragnaros (Mount Hyjal)
[46273] = true, -- Debilitated Apexar
}
 
-- 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 - 10 man hard mode)
[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 - 25 man hard mode)
[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)
[76133] = true, -- Tidal Surge (Neptulon)
[76159] = true, -- Pyrogenics (Sun-Touched Spriteling)
[76355] = true, -- Blessing of the Sun (Rajh)
[76693] = true, -- Empowering Twilight (Crimsonborne Warlord)
[81096] = true, -- Red Mist (Red Mist)
[86872] = true, -- Frothing Rage (Thundermar Ale)
[90933] = true, -- Ragezone (Defias Blood Wizard)
[93777] = true, -- Invocation of Flame (Skullcrusher the Mountain)
}
 
-- 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 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 = self.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
delete.filters = parent
frame.delete = delete
end
 
return frame
end
 
 
-- tooltip for level scanning
local tooltip = CreateFrame("GameTooltip", "CritlineTooltip", nil, "GameTooltipTemplate")
 
 
local filters = templates:CreateConfigFrame(FILTERS, addonName, true)
addon.filters = filters
 
 
do
local options = {}
filters.options = options
 
local checkButtons = {
{
text = L["Invert spell filter"],
tooltipText = L["Enable to include rather than exclude selected spells in the spell filter."],
setting = "invertFilter",
func = function(self) addon:UpdateRecords() end,
},
{
text = L["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 module = self.module
if self:GetChecked() then
PlaySound("igMainMenuOptionCheckBoxOn")
module:AddSpell(module.spell.tree:GetSelectedValue(), self.spell, self.isPeriodic)
else
PlaySound("igMainMenuOptionCheckBoxOff")
module:DeleteSpell(module.spell.tree:GetSelectedValue(), self.spell, self.isPeriodic)
end
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.module = filters
buttons[i] = btn
end
spellFilter.scrollFrame.buttons = buttons
end
 
-- spell filter scroll frame
local spellScrollFrame = spellFilter.scrollFrame
 
-- spell filter tree dropdown
local menu = {
onClick = function(self)
self.owner:SetSelectedValue(self.value)
FauxScrollFrame_SetOffset(spellScrollFrame, 0)
spellScrollFrame.scrollBar:SetValue(0)
spellScrollFrame:Update()
end,
{text = L["Damage"], value = "dmg"},
{text = L["Healing"], value = "heal"},
{text = L["Pet"], value = "pet"},
}
 
local spellFilterTree = templates:CreateDropDownMenu("CritlineSpellFilterTree", spellFilter, menu)
spellFilterTree:SetFrameWidth(120)
spellFilterTree:SetPoint("BOTTOMRIGHT", spellFilter, "TOPRIGHT", 16, 0)
spellFilterTree:SetSelectedValue("dmg")
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 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 = {
onClick = function(self)
self.owner:SetSelectedValue(self.value)
for k, v in pairs(filterTypes) do
if k == self.value then
v:Show()
else
v:Hide()
end
end
end,
{
text = L["Spell filter"],
value = "spell",
},
{
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")
filters.type = filterType
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.percharDB.profile.spells[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.spell = data.spellName
button.isPeriodic = data.isPeriodic
button:SetText(addon:GetFullSpellName(selectedTree, data.spellName, data.isPeriodic))
button:SetChecked(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 = {
invertFilter = false,
ignoreMobFilter = false,
ignoreAuraFilter = false,
onlyKnown = 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")
 
-- 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:LoadSettings()
self.profile = self.db.profile
 
for _, v in ipairs(self.options.checkButtons) do
v:LoadSetting()
end
 
self.options.slider:SetValue(self.profile.levelFilter)
end
 
 
do
-- local spellButton_OnModifiedClick = SpellButton_OnModifiedClick
 
-- hooksecurefunc("SpellButton_OnModifiedClick", function(self, button, ...)
-- local slot = SpellBook_GetSpellBookSlot(self)
-- if ( slot > MAX_SPELLS ) then
-- return
-- end
-- if IsShiftKeyDown() and filters.spell:IsVisible() and filters:GetParent() then
-- local spellName, subSpellName = GetSpellBookItemName(slot, SpellBookFrame.bookType)
-- filters:AddSpell(filters.spell.tree:GetSelectedValue(), spellName)
-- return
-- end
-- return spellButton_OnModifiedClick(self, button)
-- end)
 
--[[
local function onClick(self, button)
if button == "LeftButton" and IsShiftKeyDown() and filters.auras:IsVisible() and filters:GetParent() then
filters:AddAura(select(11, UnitAura(self.unit, self:GetID(), self.filter)))
end
end
 
-- debuff buttons needs to have an onClick handler, and both buff and debuff buttons needs to monitor left clicks
hooksecurefunc("AuraButton_Update", function(buttonName, index, filter)
local name = UnitAura("player", index, filter)
if name then
local buff = _G[buttonName..index]
if buff and not buff.Critline then
buff:RegisterForClicks("AnyUp")
if not buff:HasScript("OnClick") then
buff:SetScript("OnClick", onClick)
end
buff.Critline = true
end
end
end)
 
hooksecurefunc("BuffButton_OnClick", onClick)]]
end
 
 
function filters:UpdateSpellFilter()
self.spell.scrollFrame:Update()
end
 
 
function filters:UpdateFilter()
self[self.type:GetSelectedValue()].scrollFrame:Update()
end
 
 
function filters:AddSpell(tree, spell, isPeriodic)
local data = addon:GetSpellInfo(tree, spell, isPeriodic)
if not data then
addon:AddSpell(tree, {
spellName = spell,
isPeriodic = isPeriodic,
filtered = true,
})
self:UpdateSpellFilter()
return
end
data.filtered = true
addon:UpdateRecords(tree)
end
 
 
function filters:DeleteSpell(tree, spell, isPeriodic)
local data, index = addon:GetSpellInfo(tree, spell, isPeriodic)
if not (data.normal or data.crit) then
addon:DeleteSpell(tree, index)
self:UpdateSpellFilter()
return
end
data.filtered = nil
addon:UpdateRecords(tree)
end
 
 
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
 
 
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
 
 
function filters:IsAuraEvent(eventType, spellID, sourceFlags, destFlags, destGUID)
if eventType == "SPELL_AURA_APPLIED" or eventType == "SPELL_AURA_REFRESH" then
if CombatLog_Object_IsA(destFlags, COMBATLOG_FILTER_ME) and self:IsFilteredAura(spellID) then
-- if we gain any aura in the filter we can just stop tracking records
if not (self:IsEmpowered() or self.profile.ignoreAuraFilter) then
addon:Debug("Filtered aura gained. Disabling combat log tracking.")
end
activeAuras[spellID] = true
elseif CombatLog_Object_IsA(sourceFlags, COMBATLOG_FILTER_ME) then
corruptSpells[spellID] = corruptSpells[spellID] or {}
corruptSpells[spellID][destGUID] = self:IsEmpowered()
end
return true
elseif (eventType == "SPELL_AURA_REMOVED" or eventType == "SPELL_AURA_BROKEN" or eventType == "SPELL_AURA_BROKEN_SPELL" or eventType == "SPELL_AURA_STOLEN") then
if CombatLog_Object_IsA(destFlags, COMBATLOG_FILTER_ME) and self:IsFilteredAura(spellID) then
-- if we lost a special aura we have to check if any other filtered auras remain
activeAuras[spellID] = nil
if not filters:IsEmpowered() then
addon:Debug("No filtered aura detected. Resuming record tracking.")
end
-- elseif CombatLog_Object_IsA(sourceFlags, COMBATLOG_FILTER_ME) then
-- corruptSpells[spellID] = corruptSpells[spellID] or {}
-- corruptSpells[spellID][destGUID] = nil
end
return true
end
end
 
 
-- check if a spell passes the filter settings
function filters:SpellPassesFilters(tree, spellName, spellID, isPeriodic, destGUID, destName, school)
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
 
local targetLevel = self:GetLevelFromGUID(destGUID)
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
 
if self:IsFilteredMob(destName, destGUID) then
return
end
 
return true, self:IsFilteredSpell(tree, spellName, isPeriodic), targetLevel
end
 
 
-- check if a spell passes the filters depending on inverted setting
function filters:IsFilteredSpell(tree, spellName, periodic)
local spell = addon:GetSpellInfo(tree, spellName, periodic)
return ((spell and spell.filtered) ~= nil) ~= self.db.profile.invertFilter
end
 
 
-- scan for filtered auras from the specialAuras table
function filters:IsEmpowered()
if next(activeAuras) or not self.inControl then
return true
end
end
 
 
function filters:IsFilteredMob(mobName, GUID)
-- GUID is provided if the function was called from the combat event handler
if GUID and not self.profile.ignoreMobFilter then
if specialMobs[tonumber(GUID:sub(7, 10), 16)] then
addon:Debug("Mob ("..mobName..") is in integrated filter.")
return true
end
end
for _, v in ipairs(self.db.global.mobs) do
if v:lower() == mobName:lower() then
addon:Debug("Mob ("..mobName..") is in custom filter.")
return true
end
end
end
 
 
function filters:IsFilteredAura(spellID)
if specialAuras[spellID] then
addon:Debug("Aura ("..GetSpellInfo(spellID)..") is in integrated filter.")
return true
end
for _, v in ipairs(self.db.global.auras) do
if v == spellID then
addon:Debug("Aura ("..GetSpellInfo(spellID)..") is in custom filter.")
return true
end
end
end
 
 
function filters:GetLevelFromGUID(destGUID)
tooltip:SetOwner(UIParent, "ANCHOR_NONE")
tooltip:SetHyperlink("unit:"..destGUID)
 
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
 
 
addon:RegisterEvent("PLAYER_LOGIN")
addon:RegisterEvent("PLAYER_CONTROL_LOST")
addon:RegisterEvent("PLAYER_CONTROL_GAINED")
 
function addon:PLAYER_LOGIN()
for i = 1, 40 do
local buffID = select(11, UnitBuff("player", i))
local debuffID = select(11, UnitDebuff("player", i))
if not (buffID or debuffID) then
break
elseif specialAuras[buffID] then
activeAuras[buffID] = true
elseif specialAuras[debuffID] then
activeAuras[debuffID] = true
else
for _, v in ipairs(filters.db.global.auras) do
if v == buffID then
activeAuras[buffID] = true
break
elseif v == debuffID then
activeAuras[debuffID] = true
break
end
end
end
end
if next(activeAuras) then
addon:Debug("Filtered aura detected. Disabling combat log tracking.")
end
filters.inControl = HasFullControl()
if not filters.inControl then
self:Debug("Lost control. Disabling combat log tracking.")
end
end
 
 
function addon:PLAYER_CONTROL_LOST()
filters.inControl = false
self:Debug("Lost control. Disabling combat log tracking.")
end
 
 
function addon:PLAYER_CONTROL_GAINED()
filters.inControl = true
self:Debug("Regained control. Resuming combat log tracking.")
end
\ No newline at end of file Property changes : Added: svn:eol-style + native
3.1.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
3.1.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
3.1.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
3.1.3/libs/LibDualSpec-1.0.lua New file
0,0 → 1,312
--[[
LibDualSpec-1.0 - Adds dual spec support to individual AceDB-3.0 databases
Copyright (C) 2009 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", 4
assert(LibStub, MAJOR.." requires LibStub")
local lib = LibStub:NewLibrary(MAJOR, MINOR)
if not lib then return end
 
-- ----------------------------------------------------------------------------
-- Library data
-- ----------------------------------------------------------------------------
 
lib.talentGroup = lib.talentGroup or GetActiveTalentGroup()
lib.eventFrame = lib.eventFrame or CreateFrame("Frame")
 
lib.registry = lib.registry or {}
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_ENABLED = 'Activez le second profil'
L_ENABLED_DESC = "Cochez cette case pour échanger automatiquement les profils "..
"lors d'un changement de spécialisation."
L_DUAL_PROFILE = 'Second profil'
L_DUAL_PROFILE_DESC = "Sélectionnez le profil à échanger avec le profil courant "..
"lors du changement de spécialisation."
elseif locale == "deDE" then
L_DUAL_PROFILE = "Duales Profil"
L_DUAL_PROFILE_DESC = "W\195\164hle das Profil, das beim Wechsel der Talente aktiviert wird."
L_DUALSPEC_DESC = "Wenn aktiv, wechselt dieses Feature bei jedem Wechsel "..
"der dualen Talentspezialisierung das Profil. Das duale Profil wird beim "..
"Wechsel automatisch mit dem derzeit aktiven Profil getauscht."
L_ENABLED = "Aktiviere Duale Profile"
L_ENABLED_DESC = "Aktiviere diese Option, um beim Talentwechsel automatisch "..
"zwischen den Profilen zu wechseln."
elseif locale == "zhCN" then
L_DUAL_PROFILE = "双重配置文件"
L_DUAL_PROFILE_DESC = "选择转换天赋时所要使用的配置文件"
L_DUALSPEC_DESC = "启时,你可以为你的双天赋设定另一组配置文件,你的双重配置文件将在你转换天赋时自动与目前使用配置文件交换。"
L_ENABLED = "开启双重配置文件"
L_ENABLED_DESC = "勾选以便转换天赋时自动交换配置文件。"
elseif locale == "zhTW" then
L_DUALSPEC_DESC = "啟用時,你可以為你的雙天賦設定另一組設定檔。你的雙設定檔將在你轉換天賦時自動與目前使用設定檔交換。"
L_ENABLED = "啟用雙設定檔"
L_ENABLED_DESC = "勾選以在轉換天賦時自動交換設定檔"
L_DUAL_PROFILE = "雙設定檔"
L_DUAL_PROFILE_DESC = "選擇轉換天賦後所要使用的設定檔"
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 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
self:SetProfile(newProfile)
db.char.profile = currentProfile
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
 
--- 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
local db = target:GetNamespace(MAJOR, true) or target:RegisterNamespace(MAJOR)
registry[target] = { name = name, db = db }
EmbedMixin(target)
target:CheckDualSpecState()
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()
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
3.1.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
3.1.3/libs/AceLocale-3.0.lua New file
0,0 → 1,136
--- **AceLocale-3.0** manages localization in addons, allowing for multiple locale to be registered with fallback to the base locale for untranslated strings.
-- @class file
-- @name AceLocale-3.0
-- @release $Id: AceLocale-3.0.lua 895 2009-12-06 16:28:55Z nevcairiel $
local MAJOR,MINOR = "AceLocale-3.0", 2
 
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. Can only be set on the default locale.
-- @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)
 
if silent and not isDefault then
error("Usage: NewLocale(application, locale[, isDefault[, silent]]): 'silent' can only be specified for the default locale", 2)
end
 
-- GAME_LOCALE allows translators to test translations of addons without having that wow client installed
-- Ammo: I still think this is a bad idea, for instance an addon that checks for some ingame string will fail, just because some other addon
-- gives the user the illusion that they can run in a different locale? Ditch this whole thing or allow a setting per 'application'. I'm of the
-- opinion to remove this.
local gameLocale = GAME_LOCALE or gameLocale
 
if locale ~= gameLocale and not isDefault then
return -- nop, we don't need these translations
end
 
local app = AceLocale.apps[application]
 
if not app then
app = setmetatable({}, silent and readmetasilent or readmeta)
AceLocale.apps[application] = app
AceLocale.appnames[app] = application
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
3.1.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
3.1.3/minimap.lua New file
0,0 → 1,135
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
if display:IsShown() then
display:Hide()
else
display:Show()
end
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
3.1.3/splash.lua New file
0,0 → 1,347
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["Show old record"],
tooltipText = L["Display previous record along with \"New record\" messages."],
setting = "oldRecord",
},
{
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 function initialize(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
 
local font = templates:CreateDropDownMenu("CritlineSplashFont", config, nil, initialize)
font:SetFrameWidth(120)
font:SetPoint("TOPLEFT", config.title, "BOTTOM", 0, -28)
font.label:SetText(L["Font"])
options.font = font
 
local menu = {
onClick = function(self)
self.owner:SetSelectedValue(self.value)
local font = splash.profile.font
font.flags = self.value
splash:SetFont(LSM:Fetch("font", font.name), font.size, font.flags)
end,
{
text = L["None"],
value = "",
},
{
text = L["Normal"],
value = "OUTLINE",
},
{
text = L["Thick"],
value = "THICKOUTLINE",
},
}
 
local fontFlags = templates:CreateDropDownMenu("CritlineSplashFontFlags", config, menu)
fontFlags:SetFrameWidth(120)
fontFlags:SetPoint("TOP", font, "BOTTOM", 0, -16)
fontFlags.label:SetText(L["Font outline"])
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,
oldRecord = false,
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, spell, amount, crit, oldAmount)
spell = format(L["New %s record!"], spell)
if splash.profile.oldRecord and oldAmount > 0 then
amount = format("%d (%d)", amount, oldAmount)
end
 
local colors = splash.profile.colors
local spellColor = colors.spell
local amountColor = colors.amount
 
if splash.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
3.1.3/Critline.toc New file
0,0 → 1,35
## Interface: 40000
## Title: Critline
## Version: 3.1.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\AceDBOptions-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
3.1.3/templates.lua New file
0,0 → 1,542
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 function setColor(self)
local r, g, b = ColorPickerFrame:GetColorRGB()
self.swatch:SetVertexColor(r, g, b)
local color = self.color
color.r = r
color.g = g
color.b = b
end
 
local function cancel(self, prev)
local r, g, b = prev.r, prev.g, prev.b
self.swatch:SetVertexColor(r, g, b)
local color = self.color
color.r = r
color.g = g
color.b = b
end
 
local function onClick(self)
local info = UIDropDownMenu_CreateInfo()
local color = self.color
info.r, info.g, info.b = color.r, color.g, color.b
info.swatchFunc = function() setColor(self) end
info.cancelFunc = function(c) cancel(self, c) end
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: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 getSelectedValue(self)
return self.selectedValue
end
 
local function setDisabled(self, disable)
if disable then
self:Disable()
else
self:Enable()
end
end
 
local function initialize(self)
local onClick = self.menu.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, initFunc, valueLookup)
local frame = CreateFrame("Frame", name, parent, "UIDropDownMenuTemplate")
 
frame.SetFrameWidth = UIDropDownMenu_SetWidth
frame.SetSelectedValue = setSelectedValue
frame.GetSelectedValue = getSelectedValue
frame.Enable = UIDropDownMenu_EnableDropDown
frame.Disable = UIDropDownMenu_DisableDropDown
frame.SetDisabled = setDisabled
 
if menu then
for _, v in ipairs(menu) do
menu[v.value] = v.text
end
end
frame.menu = menu or valueLookup
 
frame.initialize = initFunc or 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 = 14
local ITEMHEIGHT = 22
 
local spells = {}
 
local function update(self)
local selectedTree = self.tree:GetSelectedValue()
 
wipe(spells)
 
for i, v in ipairs(addon.percharDB.profile.spells[selectedTree]) do
if v.normal or v.crit then
spells[#spells + 1] = {
spellName = v.spellName,
isPeriodic = v.isPeriodic,
normal = v.normal,
crit = v.crit,
pos = i,
}
end
end
 
local size = #spells
 
FauxScrollFrame_Update(self.scrollFrame, size, MAXSPELLBUTTONS, ITEMHEIGHT)
 
local offset = FauxScrollFrame_GetOffset(self.scrollFrame)
 
for line = 1, MAXSPELLBUTTONS do
local button = self.buttons[line]
local lineplusoffset = line + offset
if lineplusoffset <= size then
local data = spells[lineplusoffset]
button.spell = data.spellName
button.isPeriodic = data.isPeriodic
local normal = data.normal and data.normal.amount
local crit = data.crit and data.crit.amount
button:SetText(addon:GetFullSpellName(selectedTree, data.spellName, data.isPeriodic))
button.record:SetFormattedText("%d/%d", (normal or 0), (crit or 0))
button:SetChecked(self.selectedSpells[data.pos])
button:Show()
else
button:Hide()
end
end
end
 
-- this is used for creating the scroll frames for the Reset and Announce frames
function templates:CreateList(name, title)
local frame = templates:CreateConfigFrame(title, addonName)
 
frame.selectedSpells = {}
 
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:SetSize(300, (MAXSPELLBUTTONS * ITEMHEIGHT + 4))
scrollFrame:SetPoint("TOP", 0, -24)
scrollFrame:SetScript("OnVerticalScroll", function(self, offset) FauxScrollFrame_OnVerticalScroll(self, offset, ITEMHEIGHT, update) end)
frame.scrollFrame = scrollFrame
 
-- onClick for check buttons
local function onClick(self)
local _, pos = addon:GetSpellInfo(frame.tree.selectedValue, self.spell, self.isPeriodic)
local selectedSpells = frame.selectedSpells
if self:GetChecked() then
PlaySound("igMainMenuOptionCheckBoxOn")
selectedSpells[pos] = true
else
PlaySound("igMainMenuOptionCheckBoxOff")
selectedSpells[pos] = nil
end
if next(selectedSpells) then
frame.button:Enable()
else
frame.button:Disable()
end
end
 
-- create list of check buttons
local buttons = {}
for i = 1, MAXSPELLBUTTONS do
local btn = CreateFrame("CheckButton", nil, frame, "OptionsBaseCheckButtonTemplate")
if i == 1 then
btn:SetPoint("TOPLEFT", scrollFrame)
else
btn:SetPoint("TOP", buttons[i - 1], "BOTTOM", 0, 4)
end
btn:SetPushedTextOffset(0, 0)
btn:SetScript("OnClick", onClick)
 
-- set default font string for the check button (will contain the spell name)
local text = btn:CreateFontString(nil, nil, "GameFontHighlight")
text:SetPoint("LEFT", btn, "RIGHT", 0, 1)
text:SetJustifyH("LEFT")
btn:SetFontString(text)
 
-- font string for record amounts
local record = btn:CreateFontString(nil, nil, "GameFontHighlight")
record:SetPoint("CENTER", text)
record:SetPoint("RIGHT", scrollFrame)
record:SetJustifyH("RIGHT")
btn.record = record
 
buttons[i] = btn
end
frame.buttons = buttons
 
do
local menu = {
onClick = function(self)
self.owner:SetSelectedValue(self.value)
wipe(frame.selectedSpells)
StaticPopup_Hide("CRITLINE_RESET_ALL")
FauxScrollFrame_SetOffset(scrollFrame, 0)
_G[scrollFrame:GetName().."ScrollBar"]:SetValue(0)
frame:Update()
frame.button:Disable()
end,
{text = L["Damage"], value = "dmg"},
{text = L["Healing"], value = "heal"},
{text = L["Pet"], value = "pet"},
}
 
local tree = templates:CreateDropDownMenu(name.."Tree", frame, menu)
tree:SetPoint("TOPLEFT", scrollFrame, "BOTTOMLEFT", -16, -4)
tree:SetFrameWidth(120)
tree:SetSelectedValue("dmg")
 
frame.tree = tree
end
 
-- reset/announce button
local btn = templates:CreateButton(frame)
btn:SetPoint("TOPRIGHT", scrollFrame, "BOTTOMRIGHT", 0, -7)
btn:SetSize(100, 22)
btn:Disable()
btn:SetText(title)
frame.button = btn
 
return frame
end
end
\ No newline at end of file Property changes : Added: svn:eol-style + native
3.1.3/locales/enUS.lua New file
0,0 → 1,125
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 target"] = true
L["Alphabetically"] = true
L["Amount color"] = true
L["Announce"] = true
L["Are you sure you want to reset all %s records?"] = true
L["Aura filter"] = true
L["Basic options"] = true
L["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["damage"] = true
L["Damage"] = 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["Display previous record along with \"New record\" messages."] = true
L["Don't filter magic"] = true
L[" (DoT)"] = true
L["Duration"] = true
L["Drag to move"] = true
L["Enable to ignore additional damage due to vulnerability."] = true
L["Enable to ignore integrated aura filter."] = true
L["Enable to ignore integrated mob filter."] = true
L["Enable to ignore spells that are not in your (or your pet's) spell book."] = true
L["Enable to include rather than exclude selected spells in the spell filter."] = true
L["Enable to let magical damage ignore the level filter."] = true
L["Enable to show icon indicators instead of text."] = true
L["Enable to use scrolling combat text for \"New record\" messages instead of the default splash frame."] = true
L["Enabled"] = true
L["Enter mob name:"] = true
L["Enter spell ID:"] = true
L["Font"] = true
L["Font outline"] = true
L["Font size"] = true
L["healing"] = true
L["Healing"] = true
L[" (HoT)"] = true
L["If level difference between you and the target is greater than this setting, records will not be registered."] = true
L["Ignore aura filter"] = true
L["Ignore mob filter"] = true
L["Ignore vulnerability"] = true
L["Invalid channel. Please enter a valid channel name or ID."] = true
L["Invalid input. Please enter a spell ID."] = true
L["Invalid mob name."] = true
L["Invalid player name."] = true
L["Invalid spell ID. No such spell exists."] = true
L["Invert spell filter"] = true
L["Left-click to toggle summary frame"] = true
L["Level filter"] = true
L["Lock minimap button."] = true
L["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 - %d"] = 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["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["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 scale of the splash frame."] = true
L["Sets the scale of the summary frame."] = true
L["Sets the time (in seconds) the splash frame is visible before fading out."] = true
L["Show"] = true
L["Show minimap button."] = true
L["Show icons"] = true
L["Show old record"] = 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["Splash frame"] = true
L["%s removed from aura filter."] = true
L["%s removed from mob filter."] = true
L["Summary frame scale"] = true
L["Suppress all records while mind controlled."] = true
L["Suppress mind control"] = true
L["Thick"] = true
L["Tooltip sorting:"] = true
L["Use combat text splash"] = true
L["Use detailed format in the summary tooltip."] = true
\ No newline at end of file Property changes : Added: svn:eol-style + native
3.1.3/locales/zhTW.lua New file
0,0 → 1,111
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["Display previous record along with \"New record\" messages."] = "在\"新的記錄\"訊息一起顯示前一條記錄."
L["Don't filter magic"] = "不過濾魔法"
L[" (DoT)"] = "(DoT)"
-- L["Duration"] = ""
L["Drag to move"] = "拖曳來移動"
L["Edit tooltip format"] = "編輯提示訊息格式"
-- L["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[" (HoT)"] = "(HoT)"
L["If level difference between you and the target is greater than this setting, records will not be registered."] = "如果你和目標的等級差大于這個設定, 記錄將不會被注冊."
L["Ignore aura filter"] = "忽略光環過濾器"
L["Ignore mob filter"] = "忽略怪物過濾器"
-- L["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["Show 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
3.1.3/locales/ruRU.lua New file
0,0 → 1,125
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["Display previous record along with \"New record\" messages."] = "Показывать предыдущие рекорды вместе с сообщениями \"Новый рекорд\""
L["Don't filter magic"] = "Не фильтровать магию"
L[" (DoT)"] = "ДоТ" -- Needs review
L["Drag to move"] = "Зажмите, чтобы двигать"
L["Duration"] = "Длительность"
L["Enabled"] = "Включено."
L["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[" (HoT)"] = "ХоТ" -- Needs review
L["If level difference between you and the target is greater than this setting, records will not be registered."] = "Если разница в уровнях между вами и целью больше чем в настройках, рекорд не будет записан."
L["Ignore aura filter"] = "Игнорировать фильтр аур"
L["Ignore mob filter"] = "Игнорировать фильтр монстров"
L["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 - %d"] = "Новая запись %s%s - %d"
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["Show 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
3.1.3/locales/esES.lua New file
0,0 → 1,93
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["Display previous record along with \"New record\" messages."] = "Mostrar marca anterior junto con la nueva marca."
L["Don't filter magic"] = "No filtrar magia"
-- L[" (DoT)"] = "DP"
L["Drag to move"] = "Arrastrar para mover"
L["Edit tooltip format"] = "Editar el formato del tooltip" -- Needs review
L["Enable to ignore integrated aura filter."] = "Habilita para igonar todas la entradas en el filtro de auras."
L["Enable to 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[" (HoT)"] = "CP"
L["If level difference between you and the target is greater than this setting, records will not be registered."] = "Diferencia máxima de nivel para registra las marcas (entre tu y el objectivo)."
L["Ignore aura filter"] = "Ignorar auras"
L["Ignore mob filter"] = "Ignorar mobs"
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["Show 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
3.1.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["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["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["Display previous record along with \"New record\" messages."] = "Zeige vorhergehenden Rekord zusammen mit Mitteilungen über \"Neuer Rekord\" an."
L["Don't filter magic"] = "Magie nicht filtern"
L[" (DoT)"] = " (DoT)" -- Needs review
L["Drag to move"] = "Ziehen zum bewegen"
L["Duration"] = "Dauer" -- Needs review
L["Enabled"] = "Aktiviert" -- Needs review
L["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[" (HoT)"] = " (HoT)" -- Needs review
L["If level difference between you and the target is greater than this setting, records will not be registered."] = "Falls der Stufenunterschied zwischen dir und dem Ziel größer ist als diese Einstellung, wird der Rekord nicht aufgezeichnet."
L["Ignore aura filter"] = "Aurenfilter ignorieren"
L["Ignore mob filter"] = "Mobfilter ignorieren"
L["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 - %d"] = "Neuer %s%s Rekord - %d" -- 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["Show 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
3.1.3/reset.lua New file
0,0 → 1,84
local addonName, addon = ...
 
local L = LibStub("AceLocale-3.0"):GetLocale(addonName)
local templates = addon.templates
 
local treeNames = addon.treeNames
 
 
local module = templates:CreateList("CritlineReset", L["Reset"])
 
do
local button = module.button
button:SetScript("OnClick", function()
PlaySound("gsTitleOptionOK")
module:ResetRecords()
end)
 
local resetAll = templates:CreateButton(module)
resetAll:SetSize(100, 22)
resetAll:SetPoint("TOP", button, "BOTTOM", 0, -10)
resetAll:SetText(L["Reset all"])
resetAll:SetScript("OnClick", function(self)
PlaySound("gsTitleOptionOK")
StaticPopup_Show("CRITLINE_RESET_ALL", addon.treeNames[module.tree:GetSelectedValue()])
end)
 
-- "edit tooltip format" popup
StaticPopupDialogs["CRITLINE_RESET_ALL"] = {
text = L["Are you sure you want to reset all %s records?"],
button1 = OKAY,
button2 = CANCEL,
OnAccept = function(self)
module:ResetRecords(true)
end,
whileDead = true,
timeout = 0,
}
end
 
 
function module:ResetRecords(resetAll)
local selectedSpells = self.selectedSpells
local tree = self.tree:GetSelectedValue()
local spells = addon.percharDB.profile.spells[tree]
 
if resetAll then
for i = #spells, 1, -1 do
local data = spells[i]
if data.filtered then
data.normal = nil
data.crit = nil
else
tremove(spells, i)
end
end
addon:Message(format(L["Reset all %s records."], treeNames[tree]))
else
-- remove selected spells from database
-- iterate first in ascending order to print chat messages in alphabetical order
for i = 1, table.maxn(selectedSpells) do
if selectedSpells[i] then
local data = spells[i]
if data.filtered then
-- don't remove filtered entries completely; save the 'filtered' flag
data.normal = nil
data.crit = nil
selectedSpells[i] = nil
end
addon:Message(format(L["Reset %s (%s) records."], addon:GetFullSpellName(tree, data.spellName, data.isPeriodic), treeNames[tree]))
end
end
-- then iterate in descending order not to mess up the loop with tremove
for i = table.maxn(selectedSpells), 1, -1 do
if selectedSpells[i] then
tremove(spells, i)
end
end
end
 
wipe(selectedSpells)
self:Update()
self.button:Disable()
addon:UpdateSpells(tree)
end
\ No newline at end of file Property changes : Added: svn:eol-style + native
3.1.3/profiles.lua New file
0,0 → 1,440
local addonName, addon = ...
 
local templates = addon.templates
 
local L = {
profiles = "Profiles",
current = "Current profile:",
default = "Default",
reset = "Reset profile",
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",
choose = "Existing 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_confirm = "Are you sure you want to delete the selected profile?",
 
dualspec_desc = "When enabled, this feature allow you to select a different "..
"profile for each talent spec. The dual profile will be swapped with the "..
"current profile each time you switch from a talent spec to the other.",
enabled = "Enable dual profile",
enabled_desc = "Check this box to automatically swap profiles on talent switch.",
dual_profile = "Dual profile",
}
 
local LOCALE = GetLocale()
if LOCALE == "deDE" then
L["profiles"] = "Profile"
--L["current"] = "Current Profile:"
L["default"] = "Standard"
L["reset"] = "Profil zur\195\188cksetzen"
L["choose"] = "Vorhandene Profile"
L["new"] = "Neu"
L["choose_desc"] = "Du kannst ein neues Profil erstellen, indem du einen neuen Namen in der Eingabebox 'Neu' eingibst, oder w\195\164hle eines der vorhandenen Profile aus."
L["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_confirm"] = "Willst du das ausgew\195\164hlte Profil wirklich l\195\182schen?"
 
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["enabled"] = "Aktiviere Duale Profile"
L["enabled_desc"] = "Aktiviere diese Option, um beim Talentwechsel automatisch zwischen den Profilen zu wechseln."
L["dual_profile"] = "Duales Profil"
elseif LOCALE == "frFR" then
L["profiles"] = "Profils"
--L["current"] = "Current Profile:"
L["default"] = "D\195\169faut"
L["reset"] = "R\195\169initialiser le profil"
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["choose"] = "Profils existants"
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_confirm"] = "Etes-vous s\195\187r de vouloir supprimer le profil s\195\169lectionn\195\169 ?"
 
L["dualspec_desc"] = "Lorsqu'elle est activée, cette fonctionnalité vous permet "..
"de choisir un profil différent pour chaque spécialisation de talents. "..
"Le second profil sera échangé avec le profil courant chaque fois que vous "..
"passerez d'une spécialisation à l'autre."
L["enabled"] = 'Activez le second profil'
L["enabled_desc"] = "Cochez cette case pour échanger automatiquement les profils lors d'un changement de spécialisation."
L["dual_profile"] = 'Second profil'
-- elseif LOCALE == "koKR" then
-- L["profiles"] = "프로필"
-- L["current"] = "Current Profile:"
-- L["default"] = "기본값"
-- L["reset"] = "프로필 초기화"
-- L["choose_desc"] = "새로운 이름을 입력하거나, 이미 있는 프로필중 하나를 선택하여 새로운 프로필을 만들 수 있습니다."
-- L["new"] = "새로운 프로필"
-- L["choose"] = "프로필 선택"
-- L["copy_desc"] = "현재 사용중인 프로필에, 선택한 프로필의 설정을 복사합니다."
-- L["copy"] = "복사"
-- L["delete_desc"] = "데이터베이스에 사용중이거나 저장된 프로파일 삭제로 SavedVariables 파일의 정리와 공간 절약이 됩니다."
-- L["delete"] = "프로필 삭제"
-- L["delete_confirm"] = "정말로 선택한 프로필의 삭제를 원하십니까?"
elseif LOCALE == "esES" or LOCALE == "esMX" then
L["profiles"] = "Perfiles"
--L["current"] = "Current Profile:"
L["default"] = "Por defecto"
L["reset"] = "Reiniciar Perfil"
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["choose"] = "Perfiles existentes"
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_confirm"] = "¿Estas seguro que quieres borrar el perfil seleccionado?"
elseif LOCALE == "zhTW" then
L["profiles"] = "設定檔"
--L["current"] = "Current Profile:"
L["default"] = "預設"
L["reset"] = "重置設定檔"
L["choose_desc"] = "你可以通過在文本框內輸入一個名字創立一個新的設定檔,也可以選擇一個已經存在的設定檔。"
L["new"] = "新建"
L["choose"] = "現有的設定檔"
L["copy_desc"] = "從當前某個已保存的設定檔複製到當前正使用的設定檔。"
L["copy"] = "複製自"
L["delete_desc"] = "從資料庫裏刪除不再使用的設定檔,以節省空間,並且清理SavedVariables檔。"
L["delete"] = "刪除一個設定檔"
L["delete_confirm"] = "你確定要刪除所選擇的設定檔嗎?"
 
L["dualspec_desc"] = "啟用時,你可以為你的雙天賦設定另一組設定檔。你的雙設定檔將在你轉換天賦時自動與目前使用設定檔交換。"
L["enabled"] = "啟用雙設定檔"
L["enabled_desc"] = "勾選以在轉換天賦時自動交換設定檔"
L["dual_profile"] = "雙設定檔"
elseif LOCALE == "zhCN" then
L["profiles"] = "配置文件"
--L["current"] = "Current Profile:"
L["default"] = "默认"
L["reset"] = "重置配置文件"
L["choose_desc"] = "你可以通过在文本框内输入一个名字创立一个新的配置文件,也可以选择一个已经存在的配置文件。"
L["new"] = "新建"
L["choose"] = "现有的配置文件"
L["copy_desc"] = "从当前某个已保存的配置文件复制到当前正使用的配置文件。"
L["copy"] = "复制自"
L["delete_desc"] = "从数据库里删除不再使用的配置文件,以节省空间,并且清理SavedVariables文件。"
L["delete"] = "删除一个配置文件"
L["delete_confirm"] = "你确定要删除所选择的配置文件么?"
 
L["dualspec_desc"] = "启时,你可以为你的双天赋设定另一组配置文件,你的双重配置文件将在你转换天赋时自动与目前使用配置文件交换。"
L["enabled"] = "开启双重配置文件"
L["enabled_desc"] = "勾选以便转换天赋时自动交换配置文件。"
L["dual_profile"] = "双重配置文件"
elseif LOCALE == "ruRU" then
L["profiles"] = "Профили"
--L["current"] = "Current Profile:"
L["default"] = "По умолчанию"
L["reset"] = "Сброс профиля"
L["choose_desc"] = "Вы можете создать новый профиль, введя название в поле ввода, или выбрать один из уже существующих профилей."
L["new"] = "Новый"
L["choose"] = "Существующие профили"
L["copy_desc"] = "Скопировать настройки из выбранного профиля в активный."
L["copy"] = "Скопировать из"
L["delete_desc"] = "Удалить существующий и неиспользуемый профиль из БД для сохранения места, и очистить SavedVariables файл."
L["delete"] = "Удалить профиль"
L["delete_confirm"] = "Вы уверены, что вы хотите удалить выбранный профиль?"
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:SetAutoFocus(false)
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, initializeDropdown, defaultProfiles)
choose:SetFrameWidth(144)
choose:SetPoint("LEFT", newProfile, "RIGHT", 0, -2)
choose.label:SetText(L.choose)
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, initializeDropdown, defaultProfiles)
dualProfile:SetFrameWidth(144)
dualProfile:SetPoint("LEFT", choose)
dualProfile:SetPoint("TOP", enabled)
dualProfile.label:SetText(L.dual_profile)
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, initializeDropdown, defaultProfiles)
copy:SetFrameWidth(144)
copy:SetPoint("TOPLEFT", copyDesc, "BOTTOMLEFT", -16, -8)
copy.label:SetText(L.copy)
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, initializeDropdown, defaultProfiles)
delete:SetFrameWidth(144)
delete:SetPoint("TOPLEFT", deleteDesc, "BOTTOMLEFT", -16, -8)
delete.label:SetText(L.delete)
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
3.1.3/Broker.lua New file
0,0 → 1,49
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"],
}
 
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, normalSpell, critSpell = addon:GetHighest(k)
local normal = format("%s: %s", L["Normal"], (normalSpell and format("%d (%s)", normalRecord, normalSpell) or "n/a"))
local crit = format("%s: %s", L["Crit"], (critSpell and format("%d (%s)", critRecord, critSpell) or "n/a"))
ChatFrame_OpenChat(normal.." - "..crit)
else
addon:OpenConfig()
end
end,
OnTooltipShow = function()
addon:ShowTooltip(k)
end
})
end
 
 
local function updateRecords(event, tree, isFiltered)
if not isFiltered then
if tree then
feeds[tree].text = format("%s/%s", addon:GetHighest(tree))
else
for k in pairs(feeds) do
updateRecords(nil, k)
end
end
end
end
 
addon.RegisterCallback(feeds, "PerCharSettingsLoaded", updateRecords)
addon.RegisterCallback(feeds, "RecordsChanged", updateRecords)
addon.RegisterCallback(feeds, "SpellsChanged", updateRecords)
\ No newline at end of file Property changes : Added: svn:eol-style + native
3.1.3/changeLog.txt New file
0,0 → 1,232
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:keywords + Author Date Id Rev URL Added: svn:eol-style + native
3.1.3/display.lua New file
0,0 → 1,291
local addonName, addon = ...
 
local L = LibStub("AceLocale-3.0"):GetLocale(addonName)
local templates = addon.templates
 
local width, height
 
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 function createDisplay(parent)
local frame = CreateFrame("Frame", nil, parent)
frame:EnableMouse(true)
frame:RegisterForDrag("LeftButton")
frame:SetPoint("LEFT", 4, 0)
frame:SetPoint("RIGHT", -4, 0)
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({
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
edgeSize = 16,
insets = {left = 4, right = 4, top = 4, bottom = 4}
})
display:SetBackdropColor(0, 0, 0, 0.8)
display:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
display.trees = {}
 
 
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 function displayFunc(self)
self.module:UpdateLayout(self.setting, self:GetChecked())
end
 
local options = {
db = {},
{
text = L["Show"],
tooltipText = L["Show summary frame."],
setting = "show",
func = function(self)
if self:GetChecked() then
display:Show()
else
display:Hide()
end
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 slider = templates:CreateSlider(config, {
text = L["Scale"],
tooltipText = L["Sets the scale of the summary frame."],
minValue = 0.5,
maxValue = 1.5,
valueStep = 0.05,
minText = "50%",
maxText = "150%",
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,
})
slider:SetPoint("TOPLEFT", options[#options], "BOTTOMLEFT", 4, -24)
 
 
local defaults = {
profile = {
show = true,
locked = false,
icons = true,
scale = 1,
pos = {
point = "CENTER",
},
}
}
 
function display:AddonLoaded()
self.db = addon.db:RegisterNamespace("display", defaults)
addon.RegisterCallback(self, "SettingsLoaded")
addon.RegisterCallback(self, "PerCharSettingsLoaded", "UpdateRecords")
addon.RegisterCallback(self, "SpellsChanged", "UpdateRecords")
addon.RegisterCallback(self, "RecordsChanged", "UpdateRecords")
end
 
addon.RegisterCallback(display, "AddonLoaded")
 
 
function display:SettingsLoaded()
self.profile = self.db.profile
 
self:UpdateRecords()
 
for _, btn in ipairs(options.db) do
btn:LoadSetting()
end
 
-- 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)
slider:SetValue(scale)
end
 
 
function display:UpdateRecords(event, tree, isFiltered)
if not isFiltered then
if tree then
self.trees[tree].text:SetFormattedText("%6d/%-6d", addon:GetHighest(tree))
else
for k in pairs(self.trees) do
self:UpdateRecords(nil, k)
end
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
 
 
-- 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")
else
heal:SetPoint("TOP", 0, -4)
end
end
if pet:IsShown() then
if heal:IsShown() then
pet:SetPoint("TOP", heal, "BOTTOM")
elseif dmg:IsShown() then
pet:SetPoint("TOP", dmg, "BOTTOM")
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 + 8)
 
-- hide the entire frame if it turns out none of the individual frames are shown
if n == 0 then
self:Hide()
elseif self.profile.show then
self:Show()
end
end
\ No newline at end of file Property changes : Added: svn:eol-style + native
3.1.3/announce.lua New file
0,0 → 1,103
local addonName, addon = ...
 
local L = LibStub("AceLocale-3.0"):GetLocale(addonName)
local templates = addon.templates
 
 
local module = templates:CreateList("CritlineAnnounce", L["Announce"])
 
do
local button = module.button
button:SetScript("OnClick", function()
PlaySound("gsTitleOptionOK")
module:AnnounceRecords()
end)
 
local function onClick(self)
self.owner:SetSelectedValue(self.value)
local target = module.target
if self.value == "WHISPER" or self.value == "CHANNEL" then
target:Show()
target:SetFocus()
else
target:Hide()
end
end
 
local channels = {
"SAY",
"GUILD",
"PARTY",
"RAID",
"WHISPER",
"CHANNEL",
}
 
local function initialize(self)
for i, v in ipairs(channels) do
local info = UIDropDownMenu_CreateInfo()
info.text = _G[v]
info.value = v
info.func = onClick
info.owner = self
UIDropDownMenu_AddButton(info)
end
end
 
local channel = templates:CreateDropDownMenu("CritlineAnnounceChannel", module, nil, initialize, _G)
channel:SetFrameWidth(120)
channel:SetPoint("TOPLEFT", module.tree, "BOTTOMLEFT")
channel:SetSelectedValue("SAY")
module.channel = channel
 
local target = templates:CreateEditBox(module)
target:SetAutoFocus(false)
target:SetWidth(144)
target:SetPoint("LEFT", channel, "RIGHT", 0, 2)
target:SetScript("OnEscapePressed", target.ClearFocus)
target:SetScript("OnEnterPressed", function(self) module:AnnounceRecords() end)
target:Hide()
module.target = target
end
 
 
addon.RegisterCallback(module, "SpellsChanged", "Update")
 
 
function module:AnnounceRecords()
local channel = self.channel:GetSelectedValue()
local tree = self.tree:GetSelectedValue()
local spells = addon.percharDB.profile.spells[tree]
local target = self.target:GetText():trim()
 
if channel == "WHISPER" then
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 sent
 
for i in pairs(self.selectedSpells) do
local data = spells[i]
local normal = data.normal and data.normal.amount
local crit = data.crit and data.crit.amount
local text = format("%s - %s: %s %s: %s", addon:GetFullSpellName(tree, data.spellName, data.isPeriodic), L["Normal"], (normal or "n/a"), L["Crit"], (crit or "n/a"))
SendChatMessage(text, channel, nil, target)
sent = true
end
 
if sent then
wipe(self.selectedSpells)
self:Update()
self.button:Disable()
self.target:SetText("")
end
end
\ No newline at end of file Property changes : Added: svn:eol-style + native
3.1.3 Property changes : Added: wowi:dirname + Critline