diff --git a/Babelfish.lua b/Babelfish.lua
new file mode 100644
index 0000000..98d236b
--- /dev/null
+++ b/Babelfish.lua
@@ -0,0 +1,50 @@
+#!/usr/local/bin/lua
+
+local strings = {}
+
+for i=1,#arg do
+ local file = io.open(arg[i], "r")
+ assert(file, "Could not open " .. arg[i])
+ local text = file:read("*all")
+
+ for match in string.gmatch(text, "L%[\"(.-)\"%]") do
+ strings[match] = true
+ end
+end
+
+local work = {}
+
+for k,v in pairs(strings) do table.insert(work, k) end
+table.sort(work)
+
+local values = {
+ STATUS_ARCANEINT = "Int",
+ STATUS_BLESSINGKINGS = "BoK",
+ STATUS_BLESSINGLIGHT = "BoL",
+ STATUS_BLESSINGMIGHT = "BoM",
+ STATUS_BLESSINGSALVATION = "BoS",
+ STATUS_BLESSINGSANCTUARY = "BoSn",
+ STATUS_BLESSINGWISDOM = "BoW",
+ STATUS_DIVINESPIRIT = "Spi",
+ STATUS_FEARWARD = "FW",
+ STATUS_FORT = "F",
+ STATUS_INNERVATE = "Inn",
+ STATUS_MOTW = "M",
+ STATUS_POWERINFUSION = "PwI",
+ STATUS_PWS = "PwS",
+ STATUS_REGROWTH = "Rg",
+ STATUS_REJUV = "Rj",
+ STATUS_RENEW = "Rn",
+ STATUS_SHADOWPROT = "Sh",
+ STATUS_SOULSTONE = "Ss",
+ STATUS_THORNS = "Th",
+ STATUS_WEAKENEDSOUL = "Ws",
+}
+
+print("--Localization.enUS.lua\n")
+print("PerfectRaidLocals = {")
+for idx,match in ipairs(work) do
+ local val = values[match] or match
+ print(string.format("\t[\"%s\"] = \"%s\",", match, val))
+end
+print("}\n")
diff --git a/Dongle.lua b/Dongle.lua
new file mode 100644
index 0000000..d2cd929
--- /dev/null
+++ b/Dongle.lua
@@ -0,0 +1,776 @@
+--[[-------------------------------------------------------------------------
+ Copyright (c) 2006, Dongle Development Team
+ 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.
+ * Neither the name of the Dongle Development Team 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 = "Dongle"
+local minor = tonumber(string.match("$Revision: 188 $", "(%d+)") or 1)
+
+assert(DongleStub, string.format("%s requires DongleStub.", major))
+if not DongleStub:IsNewerVersion(major, minor) then return end
+
+Dongle = {}
+local methods = {
+ "RegisterEvent", "UnregisterEvent", "UnregisterAllEvents", "TriggerEvent",
+ "EnableDebug", "Print", "PrintF", "Debug", "DebugF",
+ "InitializeDB",
+ "InitializeSlashCommand",
+ "NewModule", "HasModule", "IterateModules",
+}
+
+local registry = {}
+local lookup = {}
+local loadqueue = {}
+local loadorder = {}
+local events = {}
+local databases = {}
+local commands = {}
+
+local function assert(level,condition,message)
+ if not condition then
+ error(message,level)
+ end
+end
+
+local function argcheck(value, num, ...)
+ assert(1, type(num) == "number",
+ "Bad argument #2 to 'argcheck' (number expected, got " .. type(level) .. ")")
+
+ for i=1,select("#", ...) do
+ if type(value) == select(i, ...) then return end
+ end
+
+ local types = strjoin(", ", ...)
+ local name = string.match(debugstack(), "`argcheck'.-[`<](.-)['>]") or "Unknown"
+ error(string.format("bad argument #%d to '%s' (%s expected, got %s)",
+ num, name, types, type(value)), 3)
+end
+
+local function safecall(func,...)
+ local success,err = pcall(func,...)
+ if not success then
+ geterrorhandler()(err)
+ end
+end
+
+function Dongle:New(name, obj)
+ argcheck(name, 2, "string")
+ argcheck(obj, 3, "table", "nil")
+
+ if not obj then
+ obj = {}
+ end
+
+ if registry[name] then
+ error("A Dongle with the name '"..name.."' is already registered.")
+ end
+
+ local reg = {["obj"] = obj, ["name"] = name}
+
+ registry[name] = reg
+ lookup[obj] = reg
+ lookup[name] = reg
+
+ for k,v in pairs(methods) do
+ obj[v] = self[v]
+ end
+
+ -- Add this Dongle to the end of the queue
+ table.insert(loadqueue, obj)
+ return obj,name
+end
+
+function Dongle:NewModule(name, obj)
+ local reg = lookup[self]
+ assert(3, reg, "You must call 'NewModule' from a registered Dongle.")
+ argcheck(name, 2, "string")
+ argcheck(obj, 3, "table", "nil")
+
+ obj,name = Dongle:New(name, obj)
+
+ if not reg.modules then reg.modules = {} end
+ reg.modules[obj] = obj
+ reg.modules[name] = obj
+ table.insert(reg.modules, name)
+ table.sort(reg.modules)
+
+ return obj,name
+end
+
+function Dongle:HasModule(module)
+ local reg = lookup[self]
+ assert(3, reg, "You must call 'HasModule' from a registered Dongle.")
+ argcheck(module, 2, "string", "table")
+
+ return reg.modules[module]
+end
+
+local NIL_FUNC = function() end
+
+function Dongle:IterateModules()
+ local reg = lookup[self]
+ assert(3, reg, "You must call 'IterateModules' from a registered Dongle.")
+
+ if not reg.modules or not next(reg.modules) then return NIL_FUNC end
+
+ local i=1
+ return function()
+ local name = reg.modules[i]
+ if not name then return end
+ i = i + 1
+ return name, reg.modules[name]
+ end
+end
+
+function Dongle:ADDON_LOADED(event, ...)
+ for i=1, #loadqueue do
+ local obj = loadqueue[i]
+ table.insert(loadorder, obj)
+
+ if type(obj.Initialize) == "function" then
+ safecall(obj.Initialize, obj)
+ end
+
+ if self.initialized and type(obj.Enable) == "function" then
+ safecall(obj.Enable, obj)
+ end
+ loadqueue[i] = nil
+ end
+end
+
+function Dongle:PLAYER_LOGIN()
+ self.initialized = true
+ for i,obj in ipairs(loadorder) do
+ if type(obj.Enable) == "function" then
+ safecall(obj.Enable, obj)
+ end
+ end
+end
+
+function Dongle:TriggerEvent(event, ...)
+ argcheck(event, 2, "string")
+ local eventTbl = events[event]
+ if eventTbl then
+ for obj,func in pairs(eventTbl) do
+ if type(func) == "string" then
+ if type(obj[func]) == "function" then
+ safecall(obj[func], obj, event, ...)
+ end
+ else
+ safecall(func,event,...)
+ end
+ end
+ end
+end
+
+function Dongle:OnEvent(frame, event, ...)
+ local eventTbl = events[event]
+ if eventTbl then
+ for obj,func in pairs(eventTbl) do
+ if type(func) == "string" then
+ if type(obj[func]) == "function" then
+ obj[func](obj, event, ...)
+ end
+ else
+ func(event, ...)
+ end
+ end
+ end
+end
+
+function Dongle:RegisterEvent(event, func)
+ local reg = lookup[self]
+ assert(3, reg, "You must call 'RegisterEvent' from a registered Dongle.")
+ argcheck(event, 2, "string")
+ argcheck(func, 3, "string", "function", "nil")
+
+ -- Name the method the same as the event if necessary
+ if not func then func = event end
+
+ if not events[event] then
+ events[event] = {}
+ frame:RegisterEvent(event)
+ end
+ events[event][self] = func
+end
+
+function Dongle:UnregisterEvent(event)
+ local reg = lookup[self]
+ assert(3, reg, "You must call 'UnregisterEvent' from a registered Dongle.")
+ argcheck(event, 2, "string")
+
+ if events[event] then
+ events[event][self] = nil
+ if not next(events[event]) then
+ events[event] = nil
+ frame:UnregisterEvent(event)
+ end
+ end
+end
+
+function Dongle:UnregisterAllEvents()
+ assert(3, lookup[self], "You must call 'UnregisterAllEvents' from a registered Dongle.")
+
+ for event,tbl in pairs(events) do
+ tbl[self] = nil
+ if not next(tbl) then
+ events[event] = nil
+ frame:UnregisterEvent(event)
+ end
+ end
+end
+
+function Dongle:AdminEvents(event)
+ local method
+ if event == "PLAYER_LOGOUT" then
+ Dongle:ClearDBDefaults()
+ method = "Disable"
+ elseif event == "PLAYER_REGEN_DISABLED" then
+ method = "CombatLockdown"
+ elseif event == "PLAYER_REGEN_ENABLED" then
+ method = "CombatUnlock"
+ end
+
+ if method then
+ for k,v in pairs(registry) do
+ local obj = v.obj
+ if obj[method] then obj[method](obj) end
+ end
+ end
+end
+
+function Dongle:EnableDebug(level)
+ local reg = lookup[self]
+ assert(3, reg, "You must call 'EnableDebug' from a registered Dongle.")
+ argcheck(level, 2, "number", "nil")
+
+ reg.debugLevel = level
+end
+
+do
+ local function argsToStrings(a1, ...)
+ if select("#", ...) > 0 then
+ return tostring(a1), argsToStrings(...)
+ else
+ return tostring(a1)
+ end
+ end
+
+ local function printHelp(obj, method, msg, ...)
+ local reg = lookup[obj]
+ assert(4, reg, "You must call '"..method.."' from a registered Dongle.")
+
+ local name = reg.name
+ msg = "|cFF33FF99"..name.."|r: "..tostring(msg)
+ if select("#", ...) > 0 then
+ msg = string.join(", ", msg, argsToStrings(...))
+ end
+
+ ChatFrame1:AddMessage(msg)
+ end
+
+ local function printFHelp(obj, method, msg, ...)
+ local reg = lookup[obj]
+ assert(4, reg, "You must call '"..method.."' from a registered Dongle.")
+
+ local name = reg.name
+ local success,txt = pcall(string.format, "|cFF33FF99%s|r: "..msg, name, ...)
+ if success then
+ ChatFrame1:AddMessage(txt)
+ else
+ error(string.gsub(txt, "'%?'", string.format("'%s'", method)), 3)
+ end
+ end
+
+ function Dongle:Print(...)
+ return printHelp(self, "Print", ...)
+ end
+
+ function Dongle:PrintF(...)
+ return printFHelp(self, "PrintF", ...)
+ end
+
+ function Dongle:Debug(level, ...)
+ local reg = lookup[self]
+ assert(3, reg, "You must call 'Debug' from a registered Dongle.")
+ argcheck(level, 2, "number")
+
+ if reg.debugLevel and level <= reg.debugLevel then
+ printHelp(self, "Debug", ...)
+ end
+ end
+
+ function Dongle:DebugF(level, ...)
+ local reg = lookup[self]
+ assert(3, reg, "You must call 'DebugF' from a registered Dongle.")
+ argcheck(level, 2, "number")
+
+ if reg.debugLevel and level <= reg.debugLevel then
+ printFHelp(self, "DebugF", ...)
+ end
+ end
+end
+
+local dbMethods = {
+ "RegisterDefaults", "SetProfile", "GetProfiles", "DeleteProfile", "CopyProfile",
+ "ResetProfile", "ResetDB",
+}
+
+local function initdb(parent, name, defaults, defaultProfile, olddb)
+ local sv = getglobal(name)
+
+ if not sv then
+ sv = {}
+ setglobal(name, sv)
+
+ -- Lets do the initial setup
+
+ sv.char = {}
+ sv.faction = {}
+ sv.realm = {}
+ sv.class = {}
+ sv.global = {}
+ sv.profiles = {}
+ end
+
+ -- Initialize the specific databases
+ local char = string.format("%s of %s", UnitName("player"), GetRealmName())
+ local realm = string.format("%s", GetRealmName())
+ local class = UnitClass("player")
+ local race = select(2, UnitRace("player"))
+ local faction = UnitFactionGroup("player")
+
+ -- Initialize the containers
+ if not sv.char then sv.char = {} end
+ if not sv.realm then sv.realm = {} end
+ if not sv.class then sv.class = {} end
+ if not sv.faction then sv.faction = {} end
+ if not sv.global then sv.global = {} end
+ if not sv.profiles then sv.profiles = {} end
+ if not sv.profileKeys then sv.profileKeys = {} end
+
+ -- Initialize this characters profiles
+ if not sv.char[char] then sv.char[char] = {} end
+ if not sv.realm[realm] then sv.realm[realm] = {} end
+ if not sv.class[class] then sv.class[class] = {} end
+ if not sv.faction[faction] then sv.faction[faction] = {} end
+
+ -- Try to get the profile selected from the char db
+ local profileKey = sv.profileKeys[char] or defaultProfile or char
+ sv.profileKeys[char] = profileKey
+
+ local profileCreated
+ if not sv.profiles[profileKey] then sv.profiles[profileKey] = {} profileCreated = true end
+
+ if olddb then
+ for k,v in pairs(olddb) do olddb[k] = nil end
+ end
+
+ local db = olddb or {}
+ db.char = sv.char[char]
+ db.realm = sv.realm[realm]
+ db.class = sv.class[class]
+ db.faction = sv.faction[faction]
+ db.profile = sv.profiles[profileKey]
+ db.global = sv.global
+ db.profiles = sv.profiles
+
+ -- Copy methods locally
+ for idx,method in pairs(dbMethods) do
+ db[method] = Dongle[method]
+ end
+
+ -- Set some properties in the object we're returning
+ db.sv = sv
+ db.sv_name = name
+ db.profileKey = profileKey
+ db.parent = parent
+ db.charKey = char
+ db.realmKey = realm
+ db.classKey = class
+ db.factionKey = faction
+
+ databases[db] = true
+
+ if defaults then
+ db:RegisterDefaults(defaults)
+ end
+
+ return db,profileCreated
+end
+
+function Dongle:InitializeDB(name, defaults, defaultProfile)
+ local reg = lookup[self]
+ assert(3, reg, "You must call 'InitializeDB' from a registered Dongle.")
+ argcheck(name, 2, "string")
+ argcheck(defaults, 3, "table", "nil")
+ argcheck(defaultProfile, 4, "string", "nil")
+
+ local db,profileCreated = initdb(self, name, defaults, defaultProfile)
+
+ if profileCreated then
+ Dongle:TriggerEvent("DONGLE_PROFILE_CREATED", db, self, db.sv_name, db.profileKey)
+ end
+ return db
+end
+
+local function copyDefaults(dest, src, force)
+ for k,v in pairs(src) do
+ if type(v) == "table" then
+ if not dest[k] then dest[k] = {} end
+ copyDefaults(dest[k], v, force)
+ else
+ if (dest[k] == nil) or force then
+ dest[k] = v
+ end
+ end
+ end
+end
+
+-- This function operates on a Dongle DB object
+function Dongle.RegisterDefaults(db, defaults)
+ assert(3, databases[db], "You must call 'RegisterDefaults' from a Dongle database object.")
+ argcheck(defaults, 2, "table")
+
+ if defaults.char then copyDefaults(db.char, defaults.char) end
+ if defaults.realm then copyDefaults(db.realm, defaults.realm) end
+ if defaults.class then copyDefaults(db.class, defaults.class) end
+ if defaults.faction then copyDefaults(db.faction, defaults.faction) end
+ if defaults.global then copyDefaults(db.global, defaults.global) end
+ if defaults.profile then copyDefaults(db.profile, defaults.profile) end
+
+ db.defaults = defaults
+end
+
+local function removeDefaults(db, defaults)
+ if not db then return end
+ for k,v in pairs(defaults) do
+ if type(v) == "table" and db[k] then
+ removeDefaults(db[k], v)
+ if not next(db[k]) then
+ db[k] = nil
+ end
+ else
+ if db[k] == defaults[k] then
+ db[k] = nil
+ end
+ end
+ end
+end
+
+function Dongle:ClearDBDefaults()
+ for db in pairs(databases) do
+ local defaults = db.defaults
+ local sv = db.sv
+
+ if db and defaults then
+ if defaults.char then removeDefaults(db.char, defaults.char) end
+ if defaults.realm then removeDefaults(db.realm, defaults.realm) end
+ if defaults.class then removeDefaults(db.class, defaults.class) end
+ if defaults.faction then removeDefaults(db.faction, defaults.faction) end
+ if defaults.global then removeDefaults(db.global, defaults.global) end
+ if defaults.profile then
+ for k,v in pairs(sv.profiles) do
+ removeDefaults(sv.profiles[k], defaults.profile)
+ end
+ end
+
+ -- Remove any blank "profiles"
+ if not next(db.char) then sv.char[db.charKey] = nil end
+ if not next(db.realm) then sv.realm[db.realmKey] = nil end
+ if not next(db.class) then sv.class[db.classKey] = nil end
+ if not next(db.faction) then sv.faction[db.factionKey] = nil end
+ if not next(db.global) then sv.global = nil end
+ end
+ end
+end
+
+function Dongle.SetProfile(db, name)
+ assert(3, databases[db], "You must call 'SetProfile' from a Dongle database object.")
+ argcheck(name, 2, "string")
+
+ local sv = db.sv
+ local old = sv.profiles[db.profileKey]
+ local new = sv.profiles[name]
+ local profileCreated
+
+ if not new then
+ sv.profiles[name] = {}
+ new = sv.profiles[name]
+ profileCreated = true
+ end
+
+ if db.defaults and db.defaults.profile then
+ -- Remove the defaults from the old profile
+ removeDefaults(old, db.defaults.profile)
+
+ -- Inject the defaults into the new profile
+ copyDefaults(new, db.defaults.profile)
+ end
+
+ db.profile = new
+
+ -- Save this new profile name
+ sv.profileKeys[db.charKey] = name
+ db.profileKey = name
+
+ if profileCreated then
+ Dongle:TriggerEvent("DONGLE_PROFILE_CREATED", db, db.parent, db.sv_name, db.profileKey)
+ end
+
+ Dongle:TriggerEvent("DONGLE_PROFILE_CHANGED", db, db.parent, db.sv_name, db.profileKey)
+end
+
+function Dongle.GetProfiles(db, t)
+ assert(3, databases[db], "You must call 'GetProfiles' from a Dongle database object.")
+ argcheck(t, 2, "table", "nil")
+
+ t = t or {}
+ local i = 1
+ for profileKey in pairs(db.profiles) do
+ t[i] = profileKey
+ i = i + 1
+ end
+ return t, i - 1
+end
+
+function Dongle.DeleteProfile(db, name)
+ assert(3, databases[db], "You must call 'DeleteProfile' from a Dongle database object.")
+ argcheck(name, 2, "string")
+
+ if db.profileKey == name then
+ error("You cannot delete your active profile. Change profiles, then attempt to delete.", 2)
+ end
+
+ db.sv.profiles[name] = nil
+ Dongle:TriggerEvent("DONGLE_PROFILE_DELETED", db, db.parent, db.sv_name, name)
+end
+
+function Dongle.CopyProfile(db, name)
+ assert(3, databases[db], "You must call 'CopyProfile' from a Dongle database object.")
+ argcheck(name, 2, "string")
+
+ assert(3, db.profileKey ~= name, "Source/Destination profile cannot be the same profile")
+ assert(3, type(db.sv.profiles[name]) == "table", "Profile \""..name.."\" doesn't exist.")
+
+ local profile = db.profile
+ local source = db.sv.profiles[name]
+
+ copyDefaults(profile, source, true)
+ Dongle:TriggerEvent("DONGLE_PROFILE_COPIED", db, db.parent, db.sv_name, name, db.profileKey)
+end
+
+function Dongle.ResetProfile(db)
+ assert(3, databases[db], "You must call 'ResetProfile' from a Dongle database object.")
+
+ local profile = db.profile
+
+ for k,v in pairs(profile) do
+ profile[k] = nil
+ end
+ if db.defaults and db.defaults.profile then
+ copyDefaults(profile, db.defaults.profile)
+ end
+ Dongle:TriggerEvent("DONGLE_PROFILE_RESET", db, db.parent, db.sv_name, db.profileKey)
+end
+
+
+function Dongle.ResetDB(db, defaultProfile)
+ assert(3, databases[db], "You must call 'ResetDB' from a Dongle database object.")
+ argcheck(defaultProfile, 2, "nil", "string")
+
+ local sv = db.sv
+ for k,v in pairs(sv) do
+ sv[k] = nil
+ end
+
+ local parent = db.parent
+
+ initdb(parent, db.sv_name, db.defaults, defaultProfile, db)
+ Dongle:TriggerEvent("DONGLE_DATABASE_RESET", db, parent, db.sv_name, db.profileKey)
+ Dongle:TriggerEvent("DONGLE_PROFILE_CREATED", db, db.parent, db.sv_name, db.profileKey)
+ Dongle:TriggerEvent("DONGLE_PROFILE_CHANGED", db, db.parent, db.sv_name, db.profileKey)
+ return db
+end
+
+local slashCmdMethods = {
+ "RegisterSlashHandler",
+ "PrintUsage",
+}
+
+local function OnSlashCommand(cmd, cmd_line)
+ if cmd.patterns then
+ for pattern, tbl in pairs(cmd.patterns) do
+ if string.match(cmd_line, pattern) then
+ if type(tbl.handler) == "string" then
+ cmd.parent[tbl.handler](cmd.parent, string.match(cmd_line, pattern))
+ else
+ tbl.handler(cmd.parent, string.match(cmd_line, pattern))
+ end
+ return
+ end
+ end
+ end
+ cmd:PrintUsage()
+end
+
+function Dongle:InitializeSlashCommand(desc, name, ...)
+ local reg = lookup[self]
+ assert(3, reg, "You must call 'InitializeSlashCommand' from a registered Dongle.")
+ argcheck(desc, 2, "string")
+ argcheck(name, 3, "string")
+ argcheck(select(1, ...), 4, "string")
+ for i = 2,select("#", ...) do
+ argcheck(select(i, ...), i+2, "string")
+ end
+
+ local cmd = {}
+ cmd.desc = desc
+ cmd.name = name
+ cmd.parent = self
+ cmd.slashes = { ... }
+ for idx,method in pairs(slashCmdMethods) do
+ cmd[method] = Dongle[method]
+ end
+
+ local genv = getfenv(0)
+
+ for i = 1,select("#", ...) do
+ genv["SLASH_"..name..tostring(i)] = "/"..select(i, ...)
+ end
+
+ genv.SlashCmdList[name] = function(...) OnSlashCommand(cmd, ...) end
+ return cmd
+end
+
+function Dongle.RegisterSlashHandler(cmd, desc, pattern, handler)
+ argcheck(desc, 2, "string")
+ argcheck(pattern, 3, "string")
+ argcheck(handler, 4, "function", "string")
+
+ if not cmd.patterns then
+ cmd.patterns = {}
+ end
+ cmd.patterns[pattern] = {
+ ["desc"] = desc,
+ ["handler"] = handler,
+ }
+end
+
+function Dongle.PrintUsage(cmd)
+ local usage = cmd.desc.."\n".."/"..table.concat(cmd.slashes, ", /")..":\n"
+ if cmd.patterns then
+ local descs = {}
+ for pattern,tbl in pairs(cmd.patterns) do
+ table.insert(descs, tbl.desc)
+ end
+ table.sort(descs)
+ for _,desc in pairs(descs) do
+ usage = usage.." - "..desc.."\n"
+ end
+ end
+ cmd.parent:Print(usage)
+end
+
+--[[-------------------------------------------------------------------------
+ Begin DongleStub required functions and registration
+---------------------------------------------------------------------------]]
+
+function Dongle:GetVersion() return major,minor end
+
+function Dongle:Activate(old)
+ if old then
+ self.registry = old.registry or registry
+ self.lookup = old.lookup or lookup
+ self.loadqueue = old.loadqueue or loadqueue
+ self.loadorder = old.loadorder or loadorder
+ self.events = old.events or events
+ self.databases = old.databases or databases
+ self.commands = old.commands or commands
+
+ registry = self.registry
+ lookup = self.lookup
+ loadqueue = self.loadqueue
+ loadorder = self.loadorder
+ events = self.events
+ databases = self.databases
+ commands = self.commands
+
+ frame = old.frame
+ self.registry[major].obj = self
+ else
+ self.registry = registry
+ self.lookup = lookup
+ self.loadqueue = loadqueue
+ self.loadorder = loadorder
+ self.events = events
+ self.databases = databases
+ self.commands = commands
+
+ local reg = {obj = self, name = "Dongle"}
+ registry[major] = reg
+ lookup[self] = reg
+ lookup[major] = reg
+ end
+
+ if not frame then
+ frame = CreateFrame("Frame")
+ end
+
+ self.frame = frame
+ frame:SetScript("OnEvent", function(...) self:OnEvent(...) end)
+
+ -- Register for events using Dongle itself
+ self:RegisterEvent("ADDON_LOADED")
+ self:RegisterEvent("PLAYER_LOGIN")
+ self:RegisterEvent("PLAYER_LOGOUT", "AdminEvents")
+ self:RegisterEvent("PLAYER_REGEN_ENABLED", "AdminEvents")
+ self:RegisterEvent("PLAYER_REGEN_DISABLED", "AdminEvents")
+
+ -- Convert all the modules handles
+ for name,obj in pairs(registry) do
+ for k,v in ipairs(methods) do
+ obj[k] = self[v]
+ end
+ end
+
+ -- Convert all database methods
+ for db in pairs(databases) do
+ for idx,method in ipairs(dbMethods) do
+ db[method] = self[method]
+ end
+ end
+end
+
+function Dongle:Deactivate(new)
+ lookup[self] = nil
+ self:UnregisterAllEvents()
+end
+
+DongleStub:Register(Dongle)
diff --git a/DongleUtils.lua b/DongleUtils.lua
new file mode 100644
index 0000000..a1c5ae0
--- /dev/null
+++ b/DongleUtils.lua
@@ -0,0 +1,419 @@
+--[[-------------------------------------------------------------------------
+ Copyright (c) 2006, Dongle Development Team
+ 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.
+ * Neither the name of the Dongle Development Team 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 majorUtil, majorGrat, majorMetro = "DongleUtils", "GratuityMini", "MetrognomeNano"
+local minor = tonumber(string.match("$Revision: 178 $", "(%d+)") or 1)
+if not DongleStub:IsNewerVersion(majorUtil, minor) then return end
+
+
+--------------------------------
+-- DongleUtils --
+-- Misc handy utils --
+--------------------------------
+
+local DongleUtils = {}
+
+
+function DongleUtils:GetVersion()
+ return majorUtil, minor
+end
+
+
+function DongleUtils:Activate(old)
+
+end
+
+
+function DongleUtils:Deactivate(new)
+ DongleUtils = nil
+end
+
+
+---------------------------
+-- Common locale strings --
+---------------------------
+
+local locale = GetLocale()
+-- Localized class names. Index == enUS, value == localized
+DongleUtils.classnames = locale == "deDE" and {
+ ["Warlock"] = "Hexenmeister",
+ ["Warrior"] = "Krieger",
+ ["Hunter"] = "Jger",
+ ["Mage"] = "Magier",
+ ["Priest"] = "Priester",
+ ["Druid"] = "Druide",
+ ["Paladin"] = "Paladin",
+ ["Shaman"] = "Schamane",
+ ["Rogue"] = "Schurke",
+} or locale == "frFR" and {
+ ["Warlock"] = "D\195\169moniste",
+ ["Warrior"] = "Guerrier",
+ ["Hunter"] = "Chasseur",
+ ["Mage"] = "Mage",
+ ["Priest"] = "Pr\195\170tre",
+ ["Druid"] = "Druide",
+ ["Paladin"] = "Paladin",
+ ["Shaman"] = "Chaman",
+ ["Rogue"] = "Voleur",
+} or {
+ ["Warlock"] = "Warlock",
+ ["Warrior"] = "Warrior",
+ ["Hunter"] = "Hunter",
+ ["Mage"] = "Mage",
+ ["Priest"] = "Priest",
+ ["Druid"] = "Druid",
+ ["Paladin"] = "Paladin",
+ ["Shaman"] = "Shaman",
+ ["Rogue"] = "Rogue",
+}
+
+-- Reversed version of .classnames, for locale -> enUS translation
+DongleUtils.classnamesreverse = {}
+for i,v in pairs(DongleUtils.classnames) do DongleUtils.classnamesreverse[v] = i end
+
+
+-- Handy method to attach to secure frames, to aid in setting attributes quickly
+-- Example: someframe:SetManyAttributes("type1", "spell", "spell", "Innervate", "unit1", "player")
+function DongleUtils.SetManyAttributes(self, ...)
+ for i=1,select("#", ...),2 do
+ local att,val = select(i, ...)
+ if not att then return end
+ self:SetAttribute(att,val)
+ end
+end
+
+
+function DongleUtils.RGBToHex(r, g, b)
+ return string.format("%02x%02x%02x", r, g, b)
+end
+
+
+function DongleUtils.RGBPercToHex(r, g, b)
+ return string.format("%02x%02x%02x", r*255, g*255, b*255)
+end
+
+
+function DongleUtils.HexToRGB(hex)
+ local rhex, ghex, bhex = string.sub(hex, 1, 2), string.sub(hex, 3, 4), string.sub(hex, 5, 6)
+ return tonumber(rhex, 16), tonumber(ghex, 16), tonumber(bhex, 16)
+end
+
+
+function DongleUtils.HexToRGBPerc(hex)
+ local rhex, ghex, bhex = string.sub(hex, 1, 2), string.sub(hex, 3, 4), string.sub(hex, 5, 6)
+ return tonumber(rhex, 16)/255, tonumber(ghex, 16)/255, tonumber(bhex, 16)/255
+end
+
+
+function DongleUtils.ColorGradient(perc, ...)
+ local num = select("#", ...)
+ local hexes = type(select(1, ...)) == "string"
+
+ if perc == 1 then
+ if hexes then return select(num, ...)
+ else return select(num-2, ...), select(num-1, ...), select(num, ...) end
+ end
+
+ if not hexes then num = num / 3 end
+
+ local segment, relperc = math.modf(perc*(num-1))
+ local r1, g1, b1, r2, g2, b2
+ if hexes then
+ r1, g1, b1 = DongleUtils.HexToRGBPerc(select(segment+1, ...))
+ r2, g2, b2 = DongleUtils.HexToRGBPerc(select(segment+2, ...))
+ else
+ r1, g1, b1 = select((segment*3)+1, ...), select((segment*3)+2, ...), select((segment*3)+3, ...)
+ r2, g2, b2 = select((segment*3)+4, ...), select((segment*3)+5, ...), select((segment*3)+6, ...)
+ end
+
+ if hexes then
+ return DongleUtils.RGBToHex(r1 + (r2-r1)*relperc,
+ g1 + (g2-g1)*relperc,
+ b1 + (b2-b1)*relperc)
+ else
+ return r1 + (r2-r1)*relperc,
+ g1 + (g2-g1)*relperc,
+ b1 + (b2-b1)*relperc
+ end
+end
+
+
+function DongleUtils.GetHPSeverity(perc, class)
+ if not class then return DongleUtils.ColorGradient(perc, 1,0,0, 1,1,0, 0,1,0)
+ else
+ local c = RAID_CLASS_COLORS[class]
+ return DongleUtils.ColorGradient(perc, 1,0,0, 1,1,0, c.r,c.g,c.b)
+ end
+end
+
+
+DongleStub:Register(DongleUtils)
+
+
+---------------------------------------
+-- GratuityMini --
+-- Tooltip parsing library --
+---------------------------------------
+
+local GratuityMini = {}
+local CreateTooltip
+
+function GratuityMini:GetVersion()
+ return majorGrat, minor
+end
+
+
+function GratuityMini:Activate(old)
+ self.tooltip = old and old.tooltip or CreateTooltip()
+ CreateTooltip = nil
+end
+
+
+function GratuityMini:Deactivate(new)
+ GratuityMini = nil
+end
+
+
+function GratuityMini:GetTooltip()
+ return self.tooltip
+end
+
+
+CreateTooltip = function()
+ local tip = CreateFrame("GameTooltip")
+ tip:SetOwner(WorldFrame, "ANCHOR_NONE")
+ tip.Llines, tip.Rlines = {}, {}
+ for i=1,30 do
+ tip.Llines[i], tip.Rlines[i] = tip:CreateFontString(), tip:CreateFontString()
+ tip.Llines[i]:SetFontObject(GameFontNormal); tip.Rlines[i]:SetFontObject(GameFontNormal)
+ tip:AddFontStrings(tip.Llines[i], tip.Rlines[i])
+ end
+
+ tip.Erase = function(self)
+ self:ClearLines() -- Ensures tooltip's NumLines is reset
+ for i=1,30 do
+ self.Rlines[i]:SetText() -- Clear text from right side (ClearLines only hides them)
+ self.L[i], self.R[i] = nil, nil -- Flush the metatable cache
+ end
+ if not self:IsOwned(WorldFrame) then self:SetOwner(WorldFrame, "ANCHOR_NONE") end
+ end
+
+ local methods = {"SetMerchantCostItem", "SetBagItem", "SetAction", "SetAuctionItem", "SetAuctionSellItem", "SetBuybackItem",
+ "SetCraftItem", "SetCraftSpell", "SetHyperlink", "SetInboxItem", "SetInventoryItem", "SetLootItem", "SetLootRollItem",
+ "SetMerchantItem", "SetPetAction", "SetPlayerBuff", "SetQuestItem", "SetQuestLogItem", "SetQuestRewardSpell",
+ "SetSendMailItem", "SetShapeshift", "SetSpell", "SetTalent", "SetTrackingSpell", "SetTradePlayerItem", "SetTradeSkillItem",
+ "SetTradeTargetItem", "SetTrainerService", "SetUnit", "SetUnitBuff", "SetUnitDebuff"}
+ for _,m in pairs(methods) do
+ local orig = tip[m]
+ tip[m] = function(self, ...)
+ self:Erase()
+ return orig(self, ...)
+ end
+ end
+
+ tip.L, tip.R = {}, {}
+ setmetatable(tip.L, {
+ __index = function(t, key)
+ if tip:NumLines() >= key and tip.Llines[key] then
+ local v = tip.Llines[key]:GetText()
+ t[key] = v
+ return v
+ end
+ return nil
+ end,
+ })
+ setmetatable(tip.R, {
+ __index = function(t, key)
+ if tip:NumLines() >= key and tip.Rlines[key] then
+ local v = tip.Rlines[key]:GetText()
+ t[key] = v
+ return v
+ end
+ return nil
+ end,
+ })
+
+ return tip
+end
+
+
+DongleStub:Register(GratuityMini)
+
+
+--------------------------------------------------
+-- MetrognomeNano --
+-- OnUpdate and delayed event manager --
+--------------------------------------------------
+
+local Metrognome = {}
+local frame, handlers, eventargs
+
+
+function Metrognome:GetVersion()
+ return majorMetro, minor
+end
+
+
+function Metrognome:Activate(old)
+ self.eventargs = old and old.eventargs or {}
+ self.handlers = old and old.handlers or {}
+ self.frame = old and old.frame or CreateFrame("Frame")
+ handlers, frame, eventargs = self.handlers, self.frame, self.eventargs
+ if not old then frame:Hide() end
+ frame.name = "MetrognomeNano Frame"
+ frame:SetScript("OnUpdate", self.OnUpdate)
+
+ if old then
+ self.olds = old.olds or {}
+ if not getmetatable(self.olds) then setmetatable(self.olds, {__mode = "k"}) end
+ self.olds[old] = true
+
+ for o in pairs(self.olds) do
+ for i,v in pairs(self) do
+ if i ~= "GetVersion" and i ~= "Activate" and i ~= "Deactivate" then o[i] = v end
+ end
+ end
+ end
+ self.Activate = nil
+end
+
+
+function Metrognome:Deactivate(new)
+ Metrognome, frame, handlers, eventargs = nil, nil, nil, nil
+ self.Deactivate = nil
+end
+
+
+function Metrognome:FireDelayedEvent(event, delay, ...)
+ local id = event..GetTime()
+
+ self:Register(self, id, "DelayedEventHandler", rate, id, event, ...)
+ self:Start(id, 1)
+
+ return id
+end
+
+
+function Metrognome:DelayedEventHandler(id, event, ...)
+ self:Unregister(id)
+
+ if Dongle then Dongle:FireEvent(event, ...) end
+end
+
+
+function Metrognome:Register(addon, name, func, rate, ...)
+--~ self:argCheck(name, 2, "string")
+--~ self:argCheck(func, 3, "function")
+--~ self:assert(not handlers[name], "A timer with the name "..name.." is already registered")
+
+ handlers[name] = {
+ handler = type(func) == "string" and addon,
+ name = name,
+ func = func,
+ rate = rate or 0,
+ ...
+ }
+
+ return true
+end
+
+
+function Metrognome:Unregister(name)
+--~ self:argCheck(name, 2, "string")
+
+ if not handlers[name] then return end
+ handlers[name] = nil
+ if not next(handlers) then frame:Hide() end
+ return true
+end
+
+
+function Metrognome:Start(name, numexec)
+--~ self:argCheck(name, 2, "string")
+
+ if not handlers[name] then return end
+ handlers[name].limit = numexec
+ handlers[name].elapsed = 0
+ handlers[name].running = true
+ frame:Show()
+ return true
+end
+
+
+function Metrognome:Stop(name)
+--~ self:argCheck(name, 2, "string")
+
+ if not handlers[name] then return end
+ handlers[name].running = nil
+ handlers[name].limit = nil
+ if not next(handlers) then frame:Hide() end
+ return true
+end
+
+
+function Metrognome:ChangeRate(name, newrate)
+--~ self:argCheck(name, 2, "string")
+
+ if not handlers[name] then return end
+
+ local t = handlers[name]
+ t.elapsed = 0
+ t.rate = newrate or 0
+ return true
+end
+
+
+function Metrognome:GetHandlerTable(name)
+--~ self:argCheck(name, 2, "string")
+
+ return handlers[name]
+end
+
+
+function Metrognome.OnUpdate(frame, elapsed)
+ for i,v in pairs(handlers) do
+ if v.running then
+ v.elapsed = v.elapsed + elapsed
+ if v.elapsed >= v.rate then
+ if v.handler then v.handler[v.func](v.handler, v.elapsed, unpack(v))
+ else v.func(v.elapsed, unpack(v)) end
+ v.elapsed = 0
+ if v.limit then
+ v.limit = v.limit - 1
+ if v.limit <= 0 then Metrognome:Stop(i) end
+ end
+ end
+ end
+ end
+end
+
+
+DongleStub:Register(Metrognome)
diff --git a/Localization.enUS.lua b/Localization.enUS.lua
index e0dfb82..c5d75ee 100644
--- a/Localization.enUS.lua
+++ b/Localization.enUS.lua
@@ -1,6 +1,65 @@
+--Localization.enUS.lua
+
PerfectRaidLocals = {
+ ["Adding defaults to new profile \"%s\""] = "Adding defaults to new profile \"%s\"",
+ ["Aggro"] = "Aggro",
+ ["Arcane Brilliance"] = "Arcane Brilliance",
+ ["Arcane Intellect"] = "Arcane Intellect",
+ ["Blessing of Kings"] = "Blessing of Kings",
+ ["Blessing of Light"] = "Blessing of Light",
+ ["Blessing of Might"] = "Blessing of Might",
+ ["Blessing of Salvation"] = "Blessing of Salvation",
+ ["Blessing of Sanctuary"] = "Blessing of Sanctuary",
+ ["Blessing of Wisdom"] = "Blessing of Wisdom",
["Dead"] = "Dead",
+ ["Divine Spirit"] = "Divine Spirit",
+ ["Fear Ward"] = "Fear Ward",
["Ghost"] = "Ghost",
+ ["Gift of the Wild"] = "Gift of the Wild",
+ ["Greater Blessing of Kings"] = "Greater Blessing of Kings",
+ ["Greater Blessing of Light"] = "Greater Blessing of Light",
+ ["Greater Blessing of Might"] = "Greater Blessing of Might",
+ ["Greater Blessing of Salvation"] = "Greater Blessing of Salvation",
+ ["Greater Blessing of Sanctuary"] = "Greater Blessing of Sanctuary",
+ ["Greater Blessing of Wisdom"] = "Greater Blessing of Wisdom",
+ ["Innervate"] = "Innervate",
+ ["Mark of the Wild"] = "Mark of the Wild",
+ ["Mortal Strike"] = "Mortal Strike",
["Offline"] = "Offline",
- ["Aggro"] = "|cFFFF1111Aggro|r",
+ ["Power Infusion"] = "Power Infusion",
+ ["Power Word: Fortitude"] = "Power Word: Fortitude",
+ ["Power Word: Shield"] = "Power Word: Shield",
+ ["Prayer Spirit"] = "Prayer Spirit",
+ ["Prayer of Fortitude"] = "Prayer of Fortitude",
+ ["Prayer of Shadow Protection"] = "Prayer of Shadow Protection",
+ ["Regrowth"] = "Regrowth",
+ ["Rejuvenation"] = "Rejuvenation",
+ ["Renew"] = "Renew",
+ ["STATUS_ARCANEINT"] = "Int",
+ ["STATUS_BLESSINGKINGS"] = "BoK",
+ ["STATUS_BLESSINGLIGHT"] = "BoL",
+ ["STATUS_BLESSINGMIGHT"] = "BoM",
+ ["STATUS_BLESSINGSALVATION"] = "BoS",
+ ["STATUS_BLESSINGSANCTUARY"] = "BoSn",
+ ["STATUS_BLESSINGWISDOM"] = "BoW",
+ ["STATUS_DIVINESPIRIT"] = "Spi",
+ ["STATUS_FEARWARD"] = "FW",
+ ["STATUS_FORT"] = "F",
+ ["STATUS_INNERVATE"] = "Inn",
+ ["STATUS_MORTALSTRIKE"] = "Ms",
+ ["STATUS_MOTW"] = "M",
+ ["STATUS_POWERINFUSION"] = "PwI",
+ ["STATUS_PWS"] = "PwS",
+ ["STATUS_REGROWTH"] = "Rg",
+ ["STATUS_REJUV"] = "Rj",
+ ["STATUS_RENEW"] = "Rn",
+ ["STATUS_SHADOWPROT"] = "Sh",
+ ["STATUS_SOULSTONE"] = "Ss",
+ ["STATUS_THORNS"] = "Th",
+ ["STATUS_WEAKENEDSOUL"] = "Ws",
+ ["Shadow Protection"] = "Shadow Protection",
+ ["Soulstone"] = "Soulstone",
+ ["Thorns"] = "Thorns",
+ ["Weakened Soul"] = "Weakened Soul",
}
+
diff --git a/PerfectRaidOptions.lua b/PerfectRaidOptions.lua
deleted file mode 100644
index d9db593..0000000
--- a/PerfectRaidOptions.lua
+++ /dev/null
@@ -1,66 +0,0 @@
---[[-------------------------------------------------------------------------
- Copyright (c) 2006, Jim Whitehead II <cladhaire@gmail.com>
- 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.
- * Neither the name of PerfectRaid 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 Options = PerfectRaid:NewModule("PerfectRaid-Options")
-
-function Options:Initialize()
-end
-
-function Options:Enable()
-end
-
-function Options:CreateOptions()
-foo = CreateFrame("Frame", "PRTestOptions", UIParent, "PROptionsTemplate")
-foo:SetHeight(300) foo:SetWidth(450)
-foo:SetPoint("CENTER", 0, 0)
-foo:Show()
-
-local tab1 = CreateFrame("Button", "PRTab1", foo, "PRTabTemplate")
-tab1:SetPoint("TOPLEFT", foo, "BOTTOMLEFT", 10, 12)
-tab1:Show()
-
-local tab2 = CreateFrame("Button", "PRTab2", foo, "PRTabTemplate")
-tab2:SetPoint("TOPLEFT", tab1, "TOPRIGHT", 3, 0)
-tab2:Show()
-
-local tab3 = CreateFrame("Button", "PRTab3", foo, "PRTabTemplate")
-tab3:SetPoint("TOPLEFT", tab2, "TOPRIGHT", 3, 0)
-tab3:SetWidth(150)
-tab3:Show()
-
-local check1 = CreateFrame("CheckButton", "PRCheck1", foo, "PRCheckTemplate")
-check1:SetHeight(16) check1:SetWidth(16)
-check1:SetPoint("TOPLEFT", 20, -30)
-check1:Show()
-
-local button1 = CreateFrame("Button", "PRButton1", foo, "PRButtonTemplate")
-button1:SetPoint("BOTTOMRIGHT", foo, -15, 17)
-button1:Show()
diff --git a/PerfectRaid_Aggro.lua b/PerfectRaid_Aggro.lua
index c6aa998..8b285ae 100644
--- a/PerfectRaid_Aggro.lua
+++ b/PerfectRaid_Aggro.lua
@@ -35,13 +35,15 @@ local frames,aggro
local victims = {}
local targets = {}
local marked = {}
+local rate
function Aggro:Initialize()
PerfectRaid.defaults.profile.AggroCheck = true
- PerfectRaid.defaults.profile.AggroRate = 0.2
+ PerfectRaid.defaults.profile.AggroRate = 1.0
frames = PerfectRaid.frames
aggro = PerfectRaid.aggro
+ self:RegisterEvent("DONGLE_PROFILE_CHANGED")
end
function Aggro:Enable()
@@ -50,6 +52,7 @@ function Aggro:Enable()
self.frame = CreateFrame("Frame")
end
self.frame:SetScript("OnUpdate", self.OnUpdate)
+ rate = PerfectRaid.db.profile.AggroRate
end
function Aggro:Disable()
@@ -59,11 +62,22 @@ function Aggro:Disable()
for k,v in pairs(aggro) do aggro[k] = 0 end
end
+function Aggro:DONGLE_PROFILE_CHANGED(event, addon, svname, name)
+ if svname == "PerfectRaidDB" then
+ rate = PerfectRaid.db.profile.AggroRate
+ if PerfectRaid.db.profile.AggroCheck then
+ self:Enable()
+ else
+ self:Disable()
+ end
+ end
+end
+
local elapsed = 0
function Aggro.OnUpdate()
local self = Aggro
elapsed = elapsed + arg1
- if elapsed >= 0.2 then
+ if elapsed >= rate then
for unit,tbl in pairs(frames) do
-- Aggro check
for unit,tbl in pairs(frames) do
diff --git a/PerfectRaid_Options.lua b/PerfectRaid_Options.lua
new file mode 100644
index 0000000..68402e2
--- /dev/null
+++ b/PerfectRaid_Options.lua
@@ -0,0 +1,103 @@
+--[[-------------------------------------------------------------------------
+ Copyright (c) 2006, Jim Whitehead II <cladhaire@gmail.com>
+ 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.
+ * Neither the name of PerfectRaid 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 Options = PerfectRaid:NewModule("PerfectRaid-Options")
+
+function Options:Initialize()
+end
+
+function Options:Enable()
+ self.cmd = self:InitializeSlashCommand("PerfectRaid Options", "PERFECTRAID", "praid", "perfectraid")
+ self.cmd:RegisterSlashHandler("Show options GUI", ".*", "ShowOptions")
+end
+
+function Options:ShowOptions()
+ if not PROptions then
+ self:CreateOptions()
+ PROptions:Show()
+ elseif PROptions:IsVisible() then
+ PROptions:Hide()
+ else
+ PROptions:Show()
+ end
+end
+
+function Options:CreateOptions()
+ local frame = CreateFrame("Frame", "PROptions", UIParent, "PROptionsTemplate")
+ frame:SetHeight(300)
+ frame:SetWidth(450)
+ frame:SetPoint("CENTER", 0, 0)
+
+ for name,module in PerfectRaid:IterateModules() do
+ if module ~= self and type(module.CreateOptions) == "function" then
+ module:CreateOptions(self)
+ end
+ end
+end
+
+local tabs = {}
+function Options:AddOptionsTab(title, frame)
+ local num = #tabs + 1
+ local name = "PROptionsTab"..num
+ local tab = CreateFrame("Button", name, PROptions, "PRTabTemplate")
+
+ if num == 1 then
+ tab:SetPoint("TOPLEFT", PROptions, "BOTTOMLEFT", 10, 12)
+ else
+ local prev = getglobal("PROptionsTab"..num-1)
+ tab:SetPoint("TOPLEFT", prev, "TOPRIGHT", 3, 0)
+ end
+
+ tab.Name:SetText(title)
+ tab.frame = frame
+ local width = tab.Name:GetWidth()
+ tab:SetWidth(width + 30)
+ tab:Show()
+
+ table.insert(tabs, tab)
+end
+
+local tabselected
+function Options:TabOnClick(tab)
+ if tabselected then
+ tabselected.Left:SetTexture("Interface\\AddOns\\PerfectRaid\\images\\OrangeTheme\\TabLeftInactive")
+ tabselected.Middle:SetTexture("Interface\\AddOns\\PerfectRaid\\images\\OrangeTheme\\TabMiddleInactive")
+ tabselected.Right:SetTexture("Interface\\AddOns\\Perfectraid\\images\\OrangeTheme\\TabRightInactive")
+ tabselected.frame:Hide()
+ end
+ tab.Left:SetTexture("Interface\\AddOns\\PerfectRaid\\images\\OrangeTheme\\TabLeft")
+ tab.Middle:SetTexture("Interface\\AddOns\\PerfectRaid\\images\\OrangeTheme\\TabMiddle")
+ tab.Right:SetTexture("Interface\\AddOns\\Perfectraid\\images\\OrangeTheme\\TabRight")
+ tab.frame:SetPoint("TOPLEFT", 15, -25)
+ tab.frame:SetPoint("BOTTOMRIGHT", -15, 15)
+ tab.frame:Show()
+ tabselected = tab
+end
diff --git a/PerfectRaid_Range.lua b/PerfectRaid_Range.lua
index bd92537..20a48b2 100644
--- a/PerfectRaid_Range.lua
+++ b/PerfectRaid_Range.lua
@@ -30,7 +30,7 @@
---------------------------------------------------------------------------]]
local Range = PerfectRaid:NewModule("PerfectRaid-Range")
-local frames, rangespell
+local frames, rangespell, rate, alpha
function Range:Initialize()
PerfectRaid.defaults.profile.RangeCheck = true
@@ -56,6 +56,9 @@ function Range:Enable()
self.frame = CreateFrame("Frame")
end
self.frame:SetScript("OnUpdate", self.OnUpdate)
+ rate = PerfectRaid.db.profile.RangeRate
+ alpha = PerfectRaid.db.profile.RangeAlpha
+ self:RegisterEvent("DONGLE_PROFILE_CHANGED")
end
function Range:Disable()
@@ -69,13 +72,25 @@ function Range:Disable()
end
end
+function Range:DONGLE_PROFILE_CHANGED(event, addon, svname, name)
+ if svname == "PerfectRaidDB" then
+ rate = PerfectRaid.db.profile.RangeRate
+ alpha = PerfectRaid.db.profile.RangeAlphaa
+ if PerfectRaid.db.profile.RangeCheck then
+ self:Enable()
+ else
+ self:Disable()
+ end
+ end
+end
+
local elapsed = 0
function Range.OnUpdate()
elapsed = elapsed + arg1
- if elapsed >= 0.2 then
+ if elapsed >= rate then
for unit,tbl in pairs(frames) do
local range = IsSpellInRange(rangespell, unit) == 1
- local alpha = range and 1.0 or 0.3
+ local alpha = range and 1.0 or alpha
for frame in pairs(tbl) do
frame:SetAlpha(alpha)
end