Add files via upload

This commit is contained in:
acolis
2023-09-27 20:37:32 +03:00
committed by GitHub
parent 68ddfeee7b
commit be1d3f6efe
53 changed files with 15281 additions and 0 deletions
+1249
View File
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
## Interface: 20003
## Author: Thrae
## Title: VitalWatch |cff7fff7f -Ace2-|r
## Notes: A powerfully customizable health, mana, and aggro alert system for solo, group, and raid.
## SavedVariables: VitalWatchDB
## SavedVariablesPerCharacter: VitalWatchCharDB
libs\AceLibrary\AceLibrary.lua
libs\AceOO-2.0\AceOO-2.0.lua
libs\AceEvent-2.0\AceEvent-2.0.lua
libs\AceDB-2.0\AceDB-2.0.lua
libs\AceAddon-2.0\AceAddon-2.0.lua
libs\Dewdrop-2.0\Dewdrop-2.0.lua
libs\Tablet-2.0\Tablet-2.0.lua
libs\FuBarPlugin-2.0\FuBarPlugin-2.0.lua
libs\Roster-2.1\Roster-2.1.lua
libs\Banzai-1.1\Banzai-1.1.lua
VitalWatchLocale_enEN.lua
VitalWatch.lua
+41
View File
@@ -0,0 +1,41 @@
--[[ VitalWatch by Thrae
--
--
-- English Localization (Default)
--
-- VitalWatchLocale should be defined in your FIRST localization
-- code.
--
--]]
VitalWatchLocale = {}
VitalWatchLocale.locale = getglobal("GetLocale")()
if VitalWatchLocale.locale then
VitalWatchLocale.LogTitle = "VitalWatch Log"
VitalWatchLocale.Floating_Message_Self_LowHealth = "Your health is low!"
VitalWatchLocale.Floating_Message_Self_CritHealth = "Your health is CRITICAL!"
VitalWatchLocale.Floating_Message_Self_LowMana = "Your mana is low!"
VitalWatchLocale.Floating_Message_Self_CritMana = "Your mana is CRITICAL!"
VitalWatchLocale.Floating_Message_LowHealth = "'s health is low!"
VitalWatchLocale.Floating_Message_CritHealth = "'s health is CRITICAL!"
VitalWatchLocale.Floating_Message_LowMana = "'s mana is low!"
VitalWatchLocale.Floating_Message_CritMana = "'s mana is CRITICAL!"
VitalWatchLocale.Floating_Message_Aggro = "AGGRO: "
VitalWatchLocale.Floating_Message_AggroLost = "Lost aggro."
VitalWatchLocale.MyPetTag = " (my pet)"
VitalWatchLocale.PetTag = " (pet)"
VitalWatchLocale.DEFAULT_MsgCritHealth = "My health is CRITICAL!"
-- Below are voice emotes for your locale. On an English client, you
-- would type in /oom to announce to everyone you're out of mana. To
-- properly translate, you must find out the command for your locale.
VitalWatchLocale.DEFAULT_EmoteCritMana = "/oom"
VitalWatchLocale.locale = nil -- we no longer need this
end
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+702
View File
@@ -0,0 +1,702 @@
--[[
Name: AceLibrary
Revision: $Rev: 23383 $
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
Inspired By: Iriel (iriel@vigilance-committee.org)
Tekkub (tekkub@gmail.com)
Revision: $Rev: 23383 $
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/index.php/AceLibrary
SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary
Description: Versioning library to handle other library instances, upgrading,
and proper access.
It also provides a base for libraries to work off of, providing
proper error tools. It is handy because all the errors occur in the
file that called it, not in the library file itself.
Dependencies: None
License: LGPL v2.1
]]
local ACELIBRARY_MAJOR = "AceLibrary"
local ACELIBRARY_MINOR = "$Revision: 23383 $"
local _G = getfenv(0)
local previous = _G[ACELIBRARY_MAJOR]
if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end
local function safecall(func,...)
local success, err = pcall(func,...)
if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("\n(.-: )in.-\n") or "") .. err) end
end
-- @table AceLibrary
-- @brief System to handle all versioning of libraries.
local AceLibrary = {}
local AceLibrary_mt = {}
setmetatable(AceLibrary, AceLibrary_mt)
local function error(self, message, ...)
if type(self) ~= "table" then
return _G.error(("Bad argument #1 to `error' (table expected, got %s)"):format(type(self)), 2)
end
local stack = debugstack()
if not message then
local _,_,second = stack:find("\n(.-)\n")
message = "error raised! " .. second
else
local arg = { ... } -- not worried about table creation, as errors don't happen often
for i = 1, #arg do
arg[i] = tostring(arg[i])
end
for i = 1, 10 do
table.insert(arg, "nil")
end
message = message:format(unpack(arg))
end
if getmetatable(self) and getmetatable(self).__tostring then
message = ("%s: %s"):format(tostring(self), message)
elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then
message = ("%s: %s"):format(self:GetLibraryVersion(), message)
elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then
message = ("%s: %s"):format(self.class:GetLibraryVersion(), message)
end
local first = stack:gsub("\n.*", "")
local file = first:gsub(".*\\(.*).lua:%d+: .*", "%1")
file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
local i = 0
for s in stack:gmatch("\n([^\n]*)") do
i = i + 1
if not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then
file = s:gsub("^.*\\(.*).lua:%d+: .*", "%1")
file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
break
end
end
local j = 0
for s in stack:gmatch("\n([^\n]*)") do
j = j + 1
if j > i and not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then
return _G.error(message, j+1)
end
end
return _G.error(message, 2)
end
local function assert(self, condition, message, ...)
if not condition then
if not message then
local stack = debugstack()
local _,_,second = stack:find("\n(.-)\n")
message = "assertion failed! " .. second
end
return error(self, message, ...)
end
return condition
end
local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5)
if type(num) ~= "number" then
return error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num))
elseif type(kind) ~= "string" then
return error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind))
end
local errored = false
arg = type(arg)
if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then
local stack = debugstack()
local _,_,func = stack:find("`argCheck'.-([`<].-['>])")
if not func then
_,_,func = stack:find("([`<].-['>])")
end
if kind5 then
return error(self, "Bad argument #%s to %s (%s, %s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, kind5, arg)
elseif kind4 then
return error(self, "Bad argument #%s to %s (%s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, arg)
elseif kind3 then
return error(self, "Bad argument #%s to %s (%s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, arg)
elseif kind2 then
return error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg)
else
return error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg)
end
end
end
local pcall
do
local function check(self, ret, ...)
if not ret then
local s = ...
return error(self, (s:gsub(".-%.lua:%d-: ", "")))
else
return ...
end
end
function pcall(self, func, ...)
return check(self, _G.pcall(func, ...))
end
end
local recurse = {}
local function addToPositions(t, major)
if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then
rawset(t, recurse, true)
AceLibrary.positions[t] = major
for k,v in pairs(t) do
if type(v) == "table" and not rawget(v, recurse) then
addToPositions(v, major)
end
if type(k) == "table" and not rawget(k, recurse) then
addToPositions(k, major)
end
end
local mt = getmetatable(t)
if mt and not rawget(mt, recurse) then
addToPositions(mt, major)
end
rawset(t, recurse, nil)
end
end
local function svnRevisionToNumber(text)
if type(text) == "string" then
if text:find("^%$Revision: (%d+) %$$") then
return tonumber((text:gsub("^%$Revision: (%d+) %$$", "%1")))
elseif text:find("^%$Rev: (%d+) %$$") then
return tonumber((text:gsub("^%$Rev: (%d+) %$$", "%1")))
elseif text:find("^%$LastChangedRevision: (%d+) %$$") then
return tonumber((text:gsub("^%$LastChangedRevision: (%d+) %$$", "%1")))
end
elseif type(text) == "number" then
return text
end
return nil
end
local crawlReplace
do
local recurse = {}
local function func(t, to, from)
if recurse[t] then
return
end
recurse[t] = true
local mt = getmetatable(t)
setmetatable(t, nil)
rawset(t, to, rawget(t, from))
rawset(t, from, nil)
for k,v in pairs(t) do
if v == from then
t[k] = to
elseif type(v) == "table" then
if not recurse[v] then
func(v, to, from)
end
end
if type(k) == "table" then
if not recurse[k] then
func(k, to, from)
end
end
end
setmetatable(t, mt)
if mt then
if mt == from then
setmetatable(t, to)
elseif not recurse[mt] then
func(mt, to, from)
end
end
end
function crawlReplace(t, to, from)
func(t, to, from)
for k in pairs(recurse) do
recurse[k] = nil
end
end
end
-- @function destroyTable
-- @brief remove all the contents of a table
-- @param t table to destroy
local function destroyTable(t)
setmetatable(t, nil)
for k,v in pairs(t) do
t[k] = nil
end
end
local function isFrame(frame)
return type(frame) == "table" and type(rawget(frame, 0)) == "userdata" and type(rawget(frame, 'IsFrameType')) == "function" and getmetatable(frame) and type(rawget(getmetatable(frame), '__index')) == "function"
end
-- @function copyTable
-- @brief Create a shallow copy of a table and return it.
-- @param from The table to copy from
-- @return A shallow copy of the table
local function copyTable(from)
local to = {}
for k,v in pairs(from) do
to[k] = v
end
setmetatable(to, getmetatable(from))
return to
end
-- @function deepTransfer
-- @brief Fully transfer all data, keeping proper previous table
-- backreferences stable.
-- @param to The table with which data is to be injected into
-- @param from The table whose data will be injected into the first
-- @param saveFields If available, a shallow copy of the basic data is saved
-- in here.
-- @param list The account of table references
-- @param list2 The current status on which tables have been traversed.
local deepTransfer
do
-- @function examine
-- @brief Take account of all the table references to be shared
-- between the to and from tables.
-- @param to The table with which data is to be injected into
-- @param from The table whose data will be injected into the first
-- @param list An account of the table references
local function examine(to, from, list, major)
list[from] = to
for k,v in pairs(from) do
if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then
if from[k] == to[k] then
list[from[k]] = to[k]
elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then
list[from[k]] = from[k]
elseif not list[from[k]] then
examine(to[k], from[k], list, major)
end
end
end
return list
end
function deepTransfer(to, from, saveFields, major, list, list2)
setmetatable(to, nil)
if not list then
list = {}
list2 = {}
examine(to, from, list, major)
end
list2[to] = to
for k,v in pairs(to) do
if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then
if saveFields then
saveFields[k] = v
end
to[k] = nil
elseif v ~= _G then
if saveFields then
saveFields[k] = copyTable(v)
end
end
end
for k in pairs(from) do
if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then
if not list2[to[k]] then
deepTransfer(to[k], from[k], nil, major, list, list2)
end
to[k] = list[to[k]] or list2[to[k]]
else
rawset(to, k, from[k])
end
end
setmetatable(to, getmetatable(from))
local mt = getmetatable(to)
if mt then
if list[mt] then
setmetatable(to, list[mt])
elseif mt.__index and list[mt.__index] then
mt.__index = list[mt.__index]
end
end
destroyTable(from)
end
end
-- @method TryToLoadStandalone
-- @brief Attempt to find and load a standalone version of the requested library
-- @param major A string representing the major version
-- @return If library is found, return values from the call to LoadAddOn are returned
-- If the library has been requested previously, nil is returned.
local function TryToLoadStandalone(major)
if not AceLibrary.scannedlibs then AceLibrary.scannedlibs = {} end
if AceLibrary.scannedlibs[major] then return end
AceLibrary.scannedlibs[major] = true
local name, _, _, enabled, loadable = GetAddOnInfo(major)
if loadable then
return LoadAddOn(name)
end
for i=1,GetNumAddOns() do
if GetAddOnMetadata(i, "X-AceLibrary-"..major) then
local name, _, _, enabled, loadable = GetAddOnInfo(i)
if loadable then
return LoadAddOn(name)
end
end
end
end
-- @method IsNewVersion
-- @brief Obtain whether the supplied version would be an upgrade to the
-- current version. This allows for bypass code in library
-- declaration.
-- @param major A string representing the major version
-- @param minor An integer or an svn revision string representing the minor version
-- @return whether the supplied version would be newer than what is
-- currently available.
function AceLibrary:IsNewVersion(major, minor)
argCheck(self, major, 2, "string")
TryToLoadStandalone(major)
if type(minor) == "string" then
local m = svnRevisionToNumber(minor)
if m then
minor = m
else
_G.error(("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
end
end
argCheck(self, minor, 3, "number")
local data = self.libs[major]
if not data then
return true
end
return data.minor < minor
end
-- @method HasInstance
-- @brief Returns whether an instance exists. This allows for optional support of a library.
-- @param major A string representing the major version.
-- @param minor (optional) An integer or an svn revision string representing the minor version.
-- @return Whether an instance exists.
function AceLibrary:HasInstance(major, minor)
argCheck(self, major, 2, "string")
TryToLoadStandalone(major)
if minor then
if type(minor) == "string" then
local m = svnRevisionToNumber(minor)
if m then
minor = m
else
_G.error(("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
end
end
argCheck(self, minor, 3, "number")
if not self.libs[major] then
return
end
return self.libs[major].minor == minor
end
return self.libs[major] and true
end
-- @method GetInstance
-- @brief Returns the library with the given major/minor version.
-- @param major A string representing the major version.
-- @param minor (optional) An integer or an svn revision string representing the minor version.
-- @return The library with the given major/minor version.
function AceLibrary:GetInstance(major, minor)
argCheck(self, major, 2, "string")
TryToLoadStandalone(major)
local data = self.libs[major]
if not data then
_G.error(("Cannot find a library instance of %s."):format(major), 2)
return
end
if minor then
if type(minor) == "string" then
local m = svnRevisionToNumber(minor)
if m then
minor = m
else
_G.error(("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
end
end
argCheck(self, minor, 2, "number")
if data.minor ~= minor then
_G.error(("Cannot find a library instance of %s, minor version %d."):format(major, minor), 2)
end
end
return data.instance
end
-- Syntax sugar. AceLibrary("FooBar-1.0")
AceLibrary_mt.__call = AceLibrary.GetInstance
local donothing = function() end
local AceEvent
-- @method Register
-- @brief Registers a new version of a given library.
-- @param newInstance the library to register
-- @param major the major version of the library
-- @param minor the minor version of the library
-- @param activateFunc (optional) A function to be called when the library is
-- fully activated. Takes the arguments
-- (newInstance [, oldInstance, oldDeactivateFunc]). If
-- oldInstance is given, you should probably call
-- oldDeactivateFunc(oldInstance).
-- @param deactivateFunc (optional) A function to be called by a newer library's
-- activateFunc.
-- @param externalFunc (optional) A function to be called whenever a new
-- library is registered.
function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc)
argCheck(self, newInstance, 2, "table")
argCheck(self, major, 3, "string")
if major ~= ACELIBRARY_MAJOR then
for k,v in pairs(_G) do
if v == newInstance then
geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k))
end
end
end
if major ~= ACELIBRARY_MAJOR and not major:find("^[%a%-][%a%d%-]*%-%d+%.%d+$") then
_G.error(string.format("Bad argument #3 to `Register'. Must be in the form of \"Name-1.0\". %q is not appropriate", major), 2)
end
if type(minor) == "string" then
local m = svnRevisionToNumber(minor)
if m then
minor = m
else
_G.error(("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
end
end
argCheck(self, minor, 4, "number")
if math.floor(minor) ~= minor or minor < 0 then
error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor)
end
argCheck(self, activateFunc, 5, "function", "nil")
argCheck(self, deactivateFunc, 6, "function", "nil")
argCheck(self, externalFunc, 7, "function", "nil")
if not deactivateFunc then
deactivateFunc = donothing
end
local data = self.libs[major]
if not data then
-- This is new
local instance = copyTable(newInstance)
crawlReplace(instance, instance, newInstance)
destroyTable(newInstance)
if AceLibrary == newInstance then
self = instance
AceLibrary = instance
end
self.libs[major] = {
instance = instance,
minor = minor,
deactivateFunc = deactivateFunc,
externalFunc = externalFunc,
}
rawset(instance, 'GetLibraryVersion', function(self)
return major, minor
end)
if not rawget(instance, 'error') then
rawset(instance, 'error', error)
end
if not rawget(instance, 'assert') then
rawset(instance, 'assert', assert)
end
if not rawget(instance, 'argCheck') then
rawset(instance, 'argCheck', argCheck)
end
if not rawget(instance, 'pcall') then
rawset(instance, 'pcall', pcall)
end
addToPositions(instance, major)
if activateFunc then
safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil
--[[ if major ~= ACELIBRARY_MAJOR then
for k,v in pairs(_G) do
if v == instance then
geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k))
end
end
end]]
end
if externalFunc then
for k,data in pairs(self.libs) do
if k ~= major then
safecall(externalFunc, instance, k, data.instance)
end
end
end
for k,data in pairs(self.libs) do
if k ~= major and data.externalFunc then
safecall(data.externalFunc, data.instance, major, instance)
end
end
if major == "AceEvent-2.0" then
AceEvent = instance
end
if AceEvent then
AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance)
end
return instance
end
local instance = data.instance
if minor <= data.minor then
-- This one is already obsolete, raise an error.
_G.error(("Obsolete library registered. %s is already registered at version %d. You are trying to register version %d. Hint: if not AceLibrary:IsNewVersion(%q, %d) then return end"):format(major, data.minor, minor, major, minor), 2)
return
end
-- This is an update
local oldInstance = {}
addToPositions(newInstance, major)
local isAceLibrary = (AceLibrary == newInstance)
local old_error, old_assert, old_argCheck, old_pcall
if isAceLibrary then
self = instance
AceLibrary = instance
old_error = instance.error
old_assert = instance.assert
old_argCheck = instance.argCheck
old_pcall = instance.pcall
self.error = error
self.assert = assert
self.argCheck = argCheck
self.pcall = pcall
end
deepTransfer(instance, newInstance, oldInstance, major)
crawlReplace(instance, instance, newInstance)
local oldDeactivateFunc = data.deactivateFunc
data.minor = minor
data.deactivateFunc = deactivateFunc
data.externalFunc = externalFunc
rawset(instance, 'GetLibraryVersion', function(self)
return major, minor
end)
if not rawget(instance, 'error') then
rawset(instance, 'error', error)
end
if not rawget(instance, 'assert') then
rawset(instance, 'assert', assert)
end
if not rawget(instance, 'argCheck') then
rawset(instance, 'argCheck', argCheck)
end
if not rawget(instance, 'pcall') then
rawset(instance, 'pcall', pcall)
end
if isAceLibrary then
for _,v in pairs(self.libs) do
local i = type(v) == "table" and v.instance
if type(i) == "table" then
if not rawget(i, 'error') or i.error == old_error then
rawset(i, 'error', error)
end
if not rawget(i, 'assert') or i.assert == old_assert then
rawset(i, 'assert', assert)
end
if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then
rawset(i, 'argCheck', argCheck)
end
if not rawget(i, 'pcall') or i.pcall == old_pcall then
rawset(i, 'pcall', pcall)
end
end
end
end
if activateFunc then
safecall(activateFunc, instance, oldInstance, oldDeactivateFunc)
--[[ if major ~= ACELIBRARY_MAJOR then
for k,v in pairs(_G) do
if v == instance then
geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k))
end
end
end]]
else
safecall(oldDeactivateFunc, oldInstance)
end
oldInstance = nil
if externalFunc then
for k,data in pairs(self.libs) do
if k ~= major then
safecall(externalFunc, instance, k, data.instance)
end
end
end
return instance
end
local iter
function AceLibrary:IterateLibraries()
if not iter then
local function iter(t, k)
k = next(t, k)
if not k then
return nil
else
return k, t[k].instance
end
end
end
return iter, self.libs, nil
end
-- @function Activate
-- @brief The activateFunc for AceLibrary itself. Called when
-- AceLibrary properly registers.
-- @param self Reference to AceLibrary
-- @param oldLib (optional) Reference to an old version of AceLibrary
-- @param oldDeactivate (optional) Function to deactivate the old lib
local function activate(self, oldLib, oldDeactivate)
AceLibrary = self
if not self.libs then
self.libs = oldLib and oldLib.libs or {}
self.scannedlibs = oldLib and oldLib.scannedlibs or {}
end
if not self.positions then
self.positions = oldLib and oldLib.positions or setmetatable({}, { __mode = "k" })
end
-- Expose the library in the global environment
_G[ACELIBRARY_MAJOR] = self
if oldDeactivate then
oldDeactivate(oldLib)
end
end
if not previous then
previous = AceLibrary
end
if not previous.libs then
previous.libs = {}
end
AceLibrary.libs = previous.libs
if not previous.positions then
previous.positions = setmetatable({}, { __mode = "k" })
end
AceLibrary.positions = previous.positions
AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate)
+986
View File
@@ -0,0 +1,986 @@
--[[
Name: AceOO-2.0
Revision: $Rev: 23383 $
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
Inspired By: Ace 1.x by Turan (turan@gryphon.com)
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/index.php/AceOO-2.0
SVN: http://svn.wowace.com/root/trunk/Ace2/AceOO-2.0
Description: Library to provide an object-orientation framework.
Dependencies: AceLibrary
License: MIT
]]
local MAJOR_VERSION = "AceOO-2.0"
local MINOR_VERSION = "$Revision: 23383 $"
-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
local AceOO = {
error = AceLibrary.error,
argCheck = AceLibrary.argCheck
}
-- @function getuid
-- @brief Obtain a unique string identifier for the object in question.
-- @param t The object to obtain the uid for.
-- @return The uid string.
local function pad(cap)
return ("0"):rep(8 - cap:len()) .. cap
end
local function getuid(t)
local mt = getmetatable(t)
setmetatable(t, nil)
local str = tostring(t)
setmetatable(t, mt)
local cap = str:match("[^:]*: 0x(.*)$")
if not cap then
cap = str:match("[^:]*: (.*)$")
end
if cap then
return pad(cap)
end
end
local function getlibrary(o)
if type(o) == "table" then
return o
elseif type(o) == "string" then
if not AceLibrary:HasInstance(o) then
AceOO:error("Library %q does not exist.", o)
end
return AceLibrary(o)
end
end
local function deeprawget(self, k)
while true do
local v = rawget(self, k)
if v ~= nil then
return v
end
local mt = getmetatable(self)
if not mt or type(mt.__index) ~= "table" then
return nil
end
self = mt.__index
end
end
-- @function Factory
-- @brief Construct a factory for the creation of objects.
-- @param obj The object whose init method will be called on the new factory
-- object.
-- @param newobj The object whose init method will be called on the new
-- objects that the Factory creates, to initialize them.
-- @param (...) Arguments which will be passed to obj.init() in addition
-- to the Factory object.
-- @return The new factory which creates a newobj when its new method is called,
-- or when it is called directly (__call metamethod).
local Factory
do
local function getlibraries(...)
if select('#', ...) == 0 then
return
end
return getlibrary((select(1, ...))), getlibraries(select(2, ...))
end
local arg = {}
local function new(obj, ...)
local t = {}
local uid = getuid(t)
obj:init(t, getlibraries(...))
t.uid = uid
return t
end
local function createnew(self, ...)
local o = self.prototype
local x = new(o, getlibraries(...))
return x
end
function Factory(obj, newobj, ...)
local t = new(obj, ...)
t.prototype = newobj
t.new = createnew
getmetatable(t).__call = t.new
return t
end
end
local function objtostring(self)
if self.ToString then
return self:ToString()
elseif self.GetLibraryVersion then
return (self:GetLibraryVersion())
elseif self.super then
local s = "Sub-" .. tostring(self.super)
local first = true
if self.interfaces then
for interface in pairs(self.interfaces) do
if first then
s = s .. "(" .. tostring(interface)
first = false
else
s = s .. ", " .. tostring(interface)
end
end
end
if self.mixins then
for mixin in pairs(self.mixins) do
if first then
s = s .. tostring(mixin)
first = false
else
s = s .. ", " .. tostring(mixin)
end
end
end
if first then
if self.uid then
return s .. ":" .. self.uid
else
return s
end
else
return s .. ")"
end
else
return self.uid and 'Subclass:' .. self.uid or 'Subclass'
end
end
-- @table Object
-- @brief Base of all objects, including Class.
--
-- @method init
-- @brief Initialize a new object.
-- @param newobject The object to initialize
-- @param class The class to make newobject inherit from
local Object
do
Object = {}
function Object:init(newobject, class)
local parent = class or self
if not rawget(newobject, 'uid') then
newobject.uid = getuid(newobject)
end
local mt = {
__index = parent,
__tostring = objtostring,
}
setmetatable(newobject, mt)
end
Object.uid = getuid(Object)
setmetatable(Object, { __tostring = function() return 'Object' end })
end
local Interface
local function validateInterface(object, interface)
if not object.class and object.prototype then
object = object.prototype
end
for k,v in pairs(interface.interface) do
if tostring(type(object[k])) ~= v then
return false
end
end
if interface.superinterfaces then
for superinterface in pairs(interface.superinterfaces) do
if not validateInterface(object, superinterface) then
return false
end
end
end
if type(object.class) == "table" and rawequal(object.class.prototype, object) then
if not object.class.interfaces then
rawset(object.class, 'interfaces', {})
end
object.class.interfaces[interface] = true
elseif type(object.class) == "table" and type(object.class.prototype) == "table" then
validateInterface(object.class.prototype, interface)
-- check if class is proper, thus preventing future checks.
end
return true
end
-- @function inherits
-- @brief Return whether an Object or Class inherits from a given
-- parent.
-- @param object Object or Class to check
-- @param parent Parent to test inheritance from
-- @return whether an Object or Class inherits from a given
-- parent.
local function inherits(object, parent)
object = getlibrary(object)
if type(parent) == "string" then
if not AceLibrary:HasInstance(parent) then
return false
else
parent = AceLibrary(parent)
end
end
AceOO:argCheck(parent, 2, "table")
if type(object) ~= "table" then
return false
end
local current
local class = deeprawget(object, 'class')
if class then
current = class
else
current = object
end
if type(current) ~= "table" then
return false
end
if rawequal(current, parent) then
return true
end
if parent.class then
while true do
if rawequal(current, Object) then
break
end
if current.mixins then
for mixin in pairs(current.mixins) do
if rawequal(mixin, parent) then
return true
end
end
end
if current.interfaces then
for interface in pairs(current.interfaces) do
if rawequal(interface, parent) then
return true
end
end
end
current = deeprawget(current, 'super')
if type(current) ~= "table" then
break
end
end
local isInterface = false
local curr = parent.class
while true do
if rawequal(curr, Object) then
break
elseif rawequal(curr, Interface) then
isInterface = true
break
end
curr = deeprawget(curr, 'super')
if type(curr) ~= "table" then
break
end
end
return isInterface and validateInterface(object, parent)
else
while true do
if rawequal(current, parent) then
return true
elseif rawequal(current, Object) then
return false
end
current = deeprawget(current, 'super')
if type(current) ~= "table" then
return false
end
end
end
end
-- @table Class
-- @brief An object factory which sets up inheritence and supports
-- 'mixins'.
--
-- @metamethod Class call
-- @brief Call ClassFactory:new() to create a new class.
--
-- @method Class new
-- @brief Construct a new object.
-- @param (...) Arguments to pass to the object init function.
-- @return The new object.
--
-- @method Class init
-- @brief Initialize a new class.
-- @param parent Superclass.
-- @param (...) Mixins.
--
-- @method Class ToString
-- @return A string representing the object, in this case 'Class'.
local initStatus
local Class
local Mixin
local autoEmbed = false
local function traverseInterfaces(bit, total)
if bit.superinterfaces then
for interface in pairs(bit.superinterfaces) do
if not total[interface] then
total[interface] = true
traverseInterfaces(interface, total)
end
end
end
end
local class_new
do
Class = Factory(Object, setmetatable({}, {__index = Object}), Object)
Class.super = Object
local function protostring(t)
return '<' .. tostring(t.class) .. ' prototype>'
end
local function classobjectstring(t)
if t.ToString then
return t:ToString()
elseif t.GetLibraryVersion then
return (t:GetLibraryVersion())
else
return '<' .. tostring(t.class) .. ' instance>'
end
end
local function classobjectequal(self, other)
if type(self) == "table" and self.Equals then
return self:Equals(other)
elseif type(other) == "table" and other.Equals then
return other:Equals(self)
elseif type(self) == "table" and self.CompareTo then
return self:CompareTo(other) == 0
elseif type(other) == "table" and other.CompareTo then
return other:CompareTo(self) == 0
else
return rawequal(self, other)
end
end
local function classobjectlessthan(self, other)
if type(self) == "table" and self.IsLessThan then
return self:IsLessThan(other)
elseif type(other) == "table" and other.IsLessThanOrEqualTo then
return not other:IsLessThanOrEqualTo(self)
elseif type(self) == "table" and self.CompareTo then
return self:CompareTo(other) < 0
elseif type(other) == "table" and other.CompareTo then
return other:CompareTo(self) > 0
elseif type(other) == "table" and other.IsLessThan and other.Equals then
return other:Equals(self) or other:IsLessThan(self)
else
AceOO:error("cannot compare two objects")
end
end
local function classobjectlessthanequal(self, other)
if type(self) == "table" and self.IsLessThanOrEqualTo then
return self:IsLessThanOrEqualTo(other)
elseif type(other) == "table" and other.IsLessThan then
return not other:IsLessThan(self)
elseif type(self) == "table" and self.CompareTo then
return self:CompareTo(other) <= 0
elseif type(other) == "table" and other.CompareTo then
return other:CompareTo(self) >= 0
elseif type(self) == "table" and self.IsLessThan and self.Equals then
return self:Equals(other) or self:IsLessThan(other)
else
AceOO:error("cannot compare two incompatible objects")
end
end
local function classobjectadd(self, other)
if type(self) == "table" and self.Add then
return self:Add(other)
else
AceOO:error("cannot add two incompatible objects")
end
end
local function classobjectsub(self, other)
if type(self) == "table" and self.Subtract then
return self:Subtract(other)
else
AceOO:error("cannot subtract two incompatible objects")
end
end
local function classobjectunm(self, other)
if type(self) == "table" and self.UnaryNegation then
return self:UnaryNegation(other)
else
AceOO:error("attempt to negate an incompatible object")
end
end
local function classobjectmul(self, other)
if type(self) == "table" and self.Multiply then
return self:Multiply(other)
else
AceOO:error("cannot multiply two incompatible objects")
end
end
local function classobjectdiv(self, other)
if type(self) == "table" and self.Divide then
return self:Divide(other)
else
AceOO:error("cannot divide two incompatible objects")
end
end
local function classobjectpow(self, other)
if type(self) == "table" and self.Exponent then
return self:Exponent(other)
else
AceOO:error("cannot exponentiate two incompatible objects")
end
end
local function classobjectconcat(self, other)
if type(self) == "table" and self.Concatenate then
return self:Concatenate(other)
else
AceOO:error("cannot concatenate two incompatible objects")
end
end
function class_new(self, ...)
if self.virtual then
AceOO:error("Cannot instantiate a virtual class.")
end
local o = self.prototype
local newobj = {}
if o.class and o.class.instancemeta then
setmetatable(newobj, o.class.instancemeta)
else
Object:init(newobj, o)
end
if self.interfaces and not self.interfacesVerified then
-- Verify the interfaces
for interface in pairs(self.interfaces) do
for field,kind in pairs(interface.interface) do
if tostring(type(newobj[field])) ~= kind then
AceOO:error("Class did not satisfy all interfaces. %q is required to be a %s. It is a %s", field, kind, tostring(type(newobj[field])))
end
end
end
self.interfacesVerified = true
end
local tmp = initStatus
initStatus = newobj
newobj:init(...)
if initStatus then
initStatus = tmp
AceOO:error("Initialization not completed, be sure to call the superclass's init method.")
return
end
initStatus = tmp
return newobj
end
local classmeta = {
__tostring = objtostring,
__call = function(self, ...)
return self:new(...)
end,
}
function Class:init(newclass, parent, ...)
parent = parent or self
local total
if parent.class then
total = { parent, ... }
parent = self
else
total = { ... }
end
if not inherits(parent, Class) then
AceOO:error("Classes must inherit from a proper class")
end
if parent.sealed then
AceOO:error("Cannot inherit from a sealed class")
end
for i,v in ipairs(total) do
if inherits(v, Mixin) and v.class then
if v.__deprecated then
AceOO:error(v.__deprecated)
end
if not newclass.mixins then
newclass.mixins = {}
end
if newclass.mixins[v] then
AceOO:error("Cannot explicitly inherit from the same mixin twice")
end
newclass.mixins[v] = true
elseif inherits(v, Interface) and v.class then
if not newclass.interfaces then
newclass.interfaces = {}
end
if newclass.interfaces[v] then
AceOO:error("Cannot explicitly inherit from the same interface twice")
end
newclass.interfaces[v] = true
else
AceOO:error("Classes can only inherit from one or zero classes and any number of mixins or interfaces")
end
end
if parent.interfaces then
if not newclass.interfaces then
newclass.interfaces = {}
end
for interface in pairs(parent.interfaces) do
newclass.interfaces[interface] = true
end
end
for k in pairs(total) do
total[k] = nil
end
newclass.super = parent
newclass.prototype = setmetatable(total, {
__index = parent.prototype,
__tostring = protostring,
})
total = nil
newclass.instancemeta = {
__index = newclass.prototype,
__tostring = classobjectstring,
__eq = classobjectequal,
__lt = classobjectlessthan,
__le = classobjectlessthanequal,
__add = classobjectadd,
__sub = classobjectsub,
__unm = classobjectunm,
__mul = classobjectmul,
__div = classobjectdiv,
__pow = classobjectpow,
__concat = classobjectconcat,
}
setmetatable(newclass, classmeta)
newclass.new = class_new
if newclass.mixins then
-- Fold in the mixins
local err, msg
for mixin in pairs(newclass.mixins) do
local ret
autoEmbed = true
ret, msg = pcall(mixin.embed, mixin, newclass.prototype)
autoEmbed = false
if not ret then
err = true
break
end
end
if err then
local pt = newclass.prototype
for k,v in pairs(pt) do
pt[k] = nil
end
-- method conflict
AceOO:error(msg)
end
end
newclass.prototype.class = newclass
if newclass.interfaces then
for interface in pairs(newclass.interfaces) do
traverseInterfaces(interface, newclass.interfaces)
end
end
if newclass.mixins then
for mixin in pairs(newclass.mixins) do
if mixin.interfaces then
if not newclass.interfaces then
newclass.interfaces = {}
end
for interface in pairs(mixin.interfaces) do
newclass.interfaces[interface] = true
end
end
end
end
end
function Class:ToString()
if type(self.GetLibraryVersion) == "function" then
return (self:GetLibraryVersion())
else
return "Class"
end
end
local tmp
function Class.prototype:init()
if rawequal(self, initStatus) then
initStatus = nil
else
AceOO:error("Improper self passed to init. You must do MyClass.super.prototype.init(self, ...)", 2)
end
self.uid = getuid(self)
local current = self.class
while true do
if current == Class then
break
end
if current.mixins then
for mixin in pairs(current.mixins) do
if type(mixin.OnInstanceInit) == "function" then
mixin:OnInstanceInit(self)
end
end
end
current = current.super
end
end
end
-- @object ClassFactory
-- @brief A factory for creating classes. Rarely used directly.
local ClassFactory = Factory(Object, Class, Object)
function Class:new(...)
local x = ClassFactory:new(...)
if AceOO.classes then
AceOO.classes[x] = true
end
return x
end
getmetatable(Class).__call = Class.new
-- @class Mixin
-- @brief A class to create mixin objects, which contain methods that get
-- "mixed in" to class prototypes.
--
-- @object Mixin prototype
-- @brief The prototype that mixin objects inherit their methods from.
--
-- @method Mixin prototype embed
-- @brief Mix in the methods of our object which are listed in our interface
-- to the supplied target table.
--
-- @method Mixin prototype init
-- @brief Initialize the mixin object.
-- @param newobj The new object we're initializing.
-- @param interface The interface we implement (the list of methods our
-- prototype provides which should be mixed into the target
-- table by embed).
do
Mixin = Class()
function Mixin:ToString()
if self.GetLibraryVersion then
return (self:GetLibraryVersion())
else
return 'Mixin'
end
end
local function _Embed(state, field, target)
field = next(state.export, field)
if field == nil then
return
end
if rawget(target, field) or (target[field] and target[field] ~= state[field]) then
AceOO:error("Method conflict in attempt to mixin. Field %q", field)
end
target[field] = state[field]
local ret,msg = pcall(_Embed, state, field, target)
if not ret then
-- Mix in the next method according to the defined interface. If that
-- fails due to a conflict, re-raise to back out the previous mixed
-- methods.
target[field] = nil
AceOO:error(msg)
end
end
function Mixin.prototype:embed(target)
if self.__deprecated then
AceOO:error(self.__deprecated)
end
local mt = getmetatable(target)
setmetatable(target, nil)
local err, msg = pcall(_Embed, self, nil, target)
if not err then
setmetatable(target, mt)
AceOO:error(msg)
return
end
if type(self.embedList) == "table" then
self.embedList[target] = true
end
if type(target.class) ~= "table" then
target[self] = true
end
if not autoEmbed and type(self.OnManualEmbed) == "function" then
self:OnManualEmbed(target)
end
setmetatable(target, mt)
end
function Mixin.prototype:activate(oldLib, oldDeactivate)
if oldLib and oldLib.embedList then
for target in pairs(oldLib.embedList) do
local mt = getmetatable(target)
setmetatable(target, nil)
for field in pairs(oldLib.export) do
target[field] = nil
end
setmetatable(target, mt)
end
self.embedList = oldLib.embedList
for target in pairs(self.embedList) do
self:embed(target)
end
else
self.embedList = setmetatable({}, {__mode="k"})
end
end
function Mixin.prototype:init(export, ...)
AceOO:argCheck(export, 2, "table")
for k,v in pairs(export) do
if type(k) ~= "number" then
AceOO:error("All keys to argument #2 must be numbers.")
elseif type(v) ~= "string" then
AceOO:error("All values to argument #2 must be strings.")
end
end
local num = #export
for i = 1, num do
local v = export[i]
export[i] = nil
export[v] = true
end
local interfaces
if select('#', ...) >= 1 then
interfaces = { ... }
for i,v in ipairs(interfaces) do
v = getlibrary(v)
interfaces[i] = v
if not v.class or not inherits(v, Interface) then
AceOO:error("Mixins can inherit only from interfaces")
end
end
local num = #interfaces
for i = 1, num do
local v = interfaces[i]
interfaces[i] = nil
interfaces[v] = true
end
for interface in pairs(interfaces) do
traverseInterfaces(interface, interfaces)
end
for interface in pairs(interfaces) do
for field,kind in pairs(interface.interface) do
if kind ~= "nil" then
local good = false
for bit in pairs(export) do
if bit == field then
good = true
break
end
end
if not good then
AceOO:error("Mixin does not fully accommodate field %q", field)
end
end
end
end
end
self.super = Mixin.prototype
Mixin.super.prototype.init(self)
self.export = export
self.interfaces = interfaces
end
end
-- @class Interface
-- @brief A class to create interfaces, which contain contracts that classes
-- which inherit from this must comply with.
--
-- @object Interface prototype
-- @brief The prototype that interface objects must adhere to.
--
-- @method Interface prototype init
-- @brief Initialize the mixin object.
-- @param interface The interface we contract (the hash of fields forced).
-- @param (...) Superinterfaces
do
Interface = Class()
function Interface:ToString()
if self.GetLibraryVersion then
return (self:GetLibraryVersion())
else
return 'Instance'
end
end
function Interface.prototype:init(interface, ...)
Interface.super.prototype.init(self)
AceOO:argCheck(interface, 2, "table")
for k,v in pairs(interface) do
if type(k) ~= "string" then
AceOO:error("All keys to argument #2 must be numbers.")
elseif type(v) ~= "string" then
AceOO:error("All values to argument #2 must be strings.")
elseif v ~= "nil" and v ~= "string" and v ~= "number" and v ~= "table" and v ~= "function" then
AceOO:error('All values to argument #2 must either be "nil", "string", "number", "table", or "function".')
end
end
if select('#', ...) >= 1 then
self.superinterfaces = { ... }
for i,v in ipairs(self.superinterfaces) do
v = getlibrary(v)
self.superinterfaces[i] = v
if not inherits(v, Interface) or not v.class then
AceOO:error('Cannot provide a non-Interface to inherit from')
end
end
local num = #self.superinterfaces
for i = 1, num do
local v = self.superinterfaces[i]
self.superinterfaces[i] = nil
self.superinterfaces[v] = true
end
end
self.interface = interface
end
end
-- @function Classpool
-- @brief Obtain a read only class from our pool of classes, indexed by the
-- superclass and mixins.
-- @param sc The superclass of the class we want.
-- @param (m1..m20) Mixins of the class we want's objects.
-- @return A read only class from the class pool.
local Classpool
do
local pool = setmetatable({}, {__mode = 'v'})
local function newindex(k, v)
AceOO:error('Attempt to modify a read-only class.')
end
local function protonewindex(k, v)
AceOO:error('Attempt to modify a read-only class prototype.')
end
local function ts(bit)
if type(bit) ~= "table" then
return tostring(bit)
elseif getmetatable(bit) and bit.__tostring then
return tostring(bit)
elseif type(bit.GetLibraryVersion) == "function" then
return bit:GetLibraryVersion()
else
return tostring(bit)
end
end
local t = {}
local function getcomplexuid(sc, ...)
if sc then
if sc.uid then
table.insert(t, sc.uid)
else
AceOO:error("%s is not an appropriate class/mixin", ts(sc))
end
end
for i = 1, select('#', ...) do
local m = select(i, ...)
if m.uid then
table.insert(t, m.uid)
else
AceOO:error("%s is not an appropriate mixin", ts(m))
end
end
table.sort(t)
local uid = table.concat(t, '')
local num = #t
for i = 1, num do
t[i] = nil
end
return uid
end
local classmeta
local arg = {}
function Classpool(superclass, ...)
local l = getlibrary
superclass = getlibrary(superclass)
arg = { ... }
for i, v in ipairs(arg) do
arg[i] = getlibrary(v)
end
if superclass then
if superclass.class then -- mixin
table.insert(arg, 1, superclass)
superclass = Class
end
else
superclass = Class
end
local key = getcomplexuid(superclass, unpack(arg))
if not pool[key] then
local class = Class(superclass, unpack(arg))
if not classmeta then
classmeta = {}
local mt = getmetatable(class)
for k,v in pairs(mt) do
classmeta[k] = v
end
classmeta.__newindex = newindex
end
-- Prevent the user from adding methods to this class.
-- NOTE: I'm not preventing modifications of existing class members,
-- but it's likely that only a truly malicious user will be doing so.
class.sealed = true
setmetatable(class, classmeta)
getmetatable(class.prototype).__newindex = protonewindex
pool[key] = class
end
return pool[key]
end
end
AceOO.Factory = Factory
AceOO.Object = Object
AceOO.Class = Class
AceOO.Mixin = Mixin
AceOO.Interface = Interface
AceOO.Classpool = Classpool
AceOO.inherits = inherits
-- Library handling bits
local function activate(self, oldLib, oldDeactivate)
AceOO = self
Factory = self.Factory
Object = self.Object
Class = self.Class
ClassFactory.prototype = Class
Mixin = self.Mixin
Interface = self.Interface
Classpool = self.Classpool
if oldLib then
self.classes = oldLib.classes
end
if not self.classes then
self.classes = setmetatable({}, {__mode="k"})
else
for class in pairs(self.classes) do
class.new = class_new
end
end
if oldDeactivate then
oldDeactivate(oldLib)
end
end
AceLibrary:Register(AceOO, MAJOR_VERSION, MINOR_VERSION, activate)
AceOO = AceLibrary(MAJOR_VERSION)
+217
View File
@@ -0,0 +1,217 @@
--[[
Name: Banzai-1.1
Revision: $Rev: 21576 $
Author(s): Rabbit (rabbit.magtheridon@gmail.com), maia
Documentation: http://www.wowace.com/index.php/Banzai-1.1_API_Documentation
SVN: http://svn.wowace.com/wowace/trunk/BanzaiLib/Banzai-1.1
Description: Aggro notification library.
Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0, Roster-2.1
]]
--[[
BanzaiLib is copyrighted 2006 by Rabbit.
This addon is distributed under the terms of the Creative Commons
Attribution-NonCommercial-ShareAlike 2.0 license.
http://creativecommons.org/licenses/by-nc-sa/2.5/
You may distribute and use these libraries without making your mod adhere to the
same license, as long as you preserve the license text embedded in the
libraries.
Any and all questions regarding our stance and licensing should be
directed to the #wowace IRC channel on irc.freenode.net.
]]
-------------------------------------------------------------------------------
-- Locals
-------------------------------------------------------------------------------
local MAJOR_VERSION = "Banzai-1.1"
local MINOR_VERSION = "$Revision: 21576 $"
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
if AceLibrary:HasInstance("Banzai-1.0") then error(MAJOR_VERSION .. " can't run alongside Banzai-1.0.") end
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end
if not AceLibrary:HasInstance("AceEvent-2.0") then error(MAJOR_VERSION .. " requires AceEvent-2.0.") end
if not AceLibrary:HasInstance("Roster-2.1") then error(MAJOR_VERSION .. " requires Roster-2.1.") end
local lib = {}
AceLibrary("AceEvent-2.0"):embed(lib)
local RL = nil
local roster = nil
local playerName = nil
-------------------------------------------------------------------------------
-- Local heap
-------------------------------------------------------------------------------
local new, del
do
local cache = setmetatable({},{__mode='k'})
function new()
local t = next(cache)
if t then
cache[t] = nil
return t
else
return {}
end
end
function del(t)
for k in pairs(t) do
t[k] = nil
end
cache[t] = true
return nil
end
end
-------------------------------------------------------------------------------
-- Initialization
-------------------------------------------------------------------------------
-- Activate a new instance of this library
function activate(self, oldLib, oldDeactivate)
if oldLib then
self.vars = oldLib.vars
if oldLib:IsEventScheduled("UpdateAggroList") then
oldLib:CancelScheduledEvent("UpdateAggroList")
self:StartOrStop()
end
else
self.vars = {}
end
RL = AceLibrary("Roster-2.1")
roster = RL.roster
playerName = UnitName("player")
if not self.vars then self.vars = {} end
self:RegisterEvent("AceEvent_EventRegistered", "StartOrStop")
self:RegisterEvent("AceEvent_EventUnregistered", "StartOrStop")
if oldDeactivate then oldDeactivate(oldLib) end
end
-------------------------------------------------------------------------------
-- Events
-------------------------------------------------------------------------------
function lib:StartOrStop()
local aceEvent = AceLibrary("AceEvent-2.0")
if aceEvent:IsEventRegistered("Banzai_UnitGainedAggro") or
aceEvent:IsEventRegistered("Banzai_PlayerGainedAggro") or
aceEvent:IsEventRegistered("Banzai_UnitLostAggro") or
aceEvent:IsEventRegistered("Banzai_PlayerLostAggro") or
aceEvent:IsEventRegistered("Banzai_Run") then
if not self:IsEventScheduled("UpdateAggroList") then
self:ScheduleRepeatingEvent("UpdateAggroList", self.UpdateAggroList, 0.2, self)
self.vars.running = true
self:TriggerEvent("Banzai_Enabled")
end
elseif self:IsEventRegistered("UpdateAggroList") then
self:CancelScheduledEvent("UpdateAggroList")
for i, unit in pairs(roster) do
unit.banzai = nil
unit.banzaiModifier = nil
end
self.vars.running = nil
self:TriggerEvent("Banzai_Disabled")
end
end
-------------------------------------------------------------------------------
-- Library
-------------------------------------------------------------------------------
function lib:UpdateAggroList()
local oldBanzai = nil
for name, unit in pairs(roster) do
if not oldBanzai then oldBanzai = new() end
oldBanzai[name] = unit.banzai
-- deduct aggro for all, increase it later for everyone with aggro
if not unit.banzaiModifier then unit.banzaiModifier = 0 end
unit.banzaiModifier = math.max(0, unit.banzaiModifier - 5)
-- check for aggro
local targetId = unit.unitid .. "target"
if UnitExists(targetId) and UnitExists(targetId .. "target") then
local targetName = UnitName(targetId .. "target")
if roster[targetName] and UnitCanAttack("player", targetId) and UnitCanAttack(targetId, "player") then
if not roster[targetName].banzaiModifier then roster[targetName].banzaiModifier = 0 end
roster[targetName].banzaiModifier = roster[targetName].banzaiModifier + 10
if not roster[targetName].banzaiTarget then roster[targetName].banzaiTarget = new() end
table.insert(roster[targetName].banzaiTarget, targetId)
end
end
-- cleanup
unit.banzaiModifier = math.max(0, unit.banzaiModifier)
unit.banzaiModifier = math.min(25, unit.banzaiModifier)
-- set aggro
unit.banzai = (unit.banzaiModifier > 15)
end
for name, unit in pairs(roster) do
if oldBanzai[name] ~= nil and oldBanzai[name] ~= unit.banzai then
-- Aggro status has changed.
if unit.banzai == true and unit.banzaiTarget then
-- Unit has aggro
self:TriggerEvent("Banzai_UnitGainedAggro", unit.unitid, unit.banzaiTarget)
if name == playerName then
self:TriggerEvent("Banzai_PlayerGainedAggro", unit.banzaiTarget)
end
elseif unit.banzai == false then
-- Unit lost aggro
self:TriggerEvent("Banzai_UnitLostAggro", unit.unitid)
if name == playerName then
self:TriggerEvent("Banzai_PlayerLostAggro", unit.unitid)
end
end
end
if unit.banzaiTarget then
unit.banzaiTarget = del(unit.banzaiTarget)
end
end
if oldBanzai then
oldBanzai = del(oldBanzai)
end
end
-------------------------------------------------------------------------------
-- API
-------------------------------------------------------------------------------
function lib:GetUnitAggroByUnitId( unitId )
if not self.vars.running then error(MAJOR_VERSION.." is not running. You must register for one of the events.") end
local rosterUnit = RL:GetUnitObjectFromUnit(unitId)
if not rosterUnit then return nil end
return rosterUnit.banzai
end
function lib:GetUnitAggroByUnitName( unitName )
if not self.vars.running then error(MAJOR_VERSION.." is not running. You must register for one of the events.") end
local rosterUnit = RL:GetUnitObjectFromName(unitName)
if not rosterUnit then return nil end
return rosterUnit.banzai
end
function lib:IsRunning()
return self:IsEventScheduled("UpdateAggroList")
end
-------------------------------------------------------------------------------
-- Register
-------------------------------------------------------------------------------
AceLibrary:Register(lib, MAJOR_VERSION, MINOR_VERSION, activate)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+425
View File
@@ -0,0 +1,425 @@
--[[
Name: Roster-2.1
Revision: $Revision: 21071 $
X-ReleaseDate: $Date: 2006-12-17 13:56:50 -0500 (Sun, 17 Dec 2006) $
Author: Maia (maia.proudmoore@gmail.com)
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/wiki/Roster-2.1
SVN: http://svn.wowace.com/wowace/trunk/RosterLib/
Description: Party/raid roster management
Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0
]]
local MAJOR_VERSION = "Roster-2.1"
local MINOR_VERSION = "$Revision: 21071 $"
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
if not AceLibrary:HasInstance("AceEvent-2.0") then error(MAJOR_VERSION .. " requires AceEvent-2.0") end
local unknownUnits = {}
local Lib = {}
local roster
local new, del
do
local cache = setmetatable({}, {__mode='k'})
function new()
local t = next(cache)
if t then
cache[t] = nil
return t
else
return {}
end
end
function del(t)
for k in pairs(t) do
t[k] = nil
end
cache[t] = true
return nil
end
end
------------------------------------------------
-- activate, enable, disable
------------------------------------------------
local function activate(self, oldLib, oldDeactivate)
if oldLib then
if oldLib.roster then self.roster = oldLib.roster end
oldLib:UnregisterAllEvents()
oldLib:CancelAllScheduledEvents()
end
if not self.roster then self.roster = {} end
if oldDeactivate then oldDeactivate(oldLib) end
roster = self.roster
end
local function external(self, major, instance)
if major == "AceEvent-2.0" then
instance:embed(self)
if instance:IsFullyInitialized() then
self:AceEvent_FullyInitialized()
else
self:RegisterEvent("AceEvent_FullyInitialized")
end
end
end
------------------------------------------------
-- Internal functions
------------------------------------------------
function Lib:AceEvent_FullyInitialized()
self:TriggerEvent("RosterLib_Enabled")
self:RegisterBucketEvent({"RAID_ROSTER_UPDATE", "PARTY_MEMBERS_CHANGED"}, 0.2, "ScanFullRoster")
self:RegisterBucketEvent("UNIT_PET", 0.2, "ScanPet")
self:ScanFullRoster()
end
------------------------------------------------
-- Unit iterator
------------------------------------------------
local UnitIterator
do
local rmem, pmem, step, count
local function SelfIterator()
while step do
local unit
if step == 1 then
-- STEP 1: player
unit = "player"
step = 2
elseif step == 2 then
-- STEP 2: pet
unit = "pet"
step = nil
end
if unit and UnitExists(unit) then return unit end
end
end
local function SelfAndPartyIterator()
while step do
local unit
if step <= 2 then
unit = SelfIterator()
if not step then step = 3 end
elseif step == 3 then
-- STEP 3: party units
unit = string.format("party%d", count)
step = 4
elseif step == 4 then
-- STEP 4: party pets
unit = string.format("partypet%d", count)
count = count + 1
step = count <= pmem and 3 or nil
end
if unit and UnitExists(unit) then return unit end
end
end
local function RaidIterator()
while step do
local unit
if step == 1 then
-- STEP 1: raid units
unit = string.format("raid%d", count)
step = 2
elseif step == 2 then
-- STEP 2: raid pets
unit = string.format("raidpet%d", count)
count = count + 1
step = count <= rmem and 1 or nil
end
if unit and UnitExists(unit) then return unit end
end
end
function UnitIterator()
rmem = GetNumRaidMembers()
step = 1
if rmem == 0 then
pmem = GetNumPartyMembers()
if pmem == 0 then
return SelfIterator, false
else
count = 1
return SelfAndPartyIterator, false
end
else
count = 1
return RaidIterator, true
end
end
end
------------------------------------------------
-- Roster code
------------------------------------------------
local rosterScanCache = {}
function Lib:ScanFullRoster()
local changed
local it, isInRaid = UnitIterator()
-- save all units we currently have, this way we can check who to remove from roster later.
for name in pairs(roster) do
rosterScanCache[name] = true
end
-- update data
for unitid in it do
local name = self:CreateOrUpdateUnit(unitid, isInRaid)
-- we successfully added a unit, so we don't need to remove it next step
if name then
rosterScanCache[name] = nil
changed = true
end
end
-- clear units we had in roster that either left the raid or are unknown for some reason.
for name in pairs(rosterScanCache) do
self:RemoveUnit(name)
rosterScanCache[name] = nil
changed = true
end
self:ProcessRoster()
if changed then
self:TriggerEvent("RosterLib_RosterUpdated")
end
end
function Lib:ScanPet(owner_list)
local changed
for owner in pairs(owner_list) do
local unitid = self:GetPetFromOwner(owner)
if not unitid then return end
if not UnitExists(unitid) then
unknownUnits[unitid] = nil
-- find the pet in the roster we need to delete
for _,u in pairs(roster) do
if u.unitid == unitid then
self:RemoveUnit(u.name)
changed = true
end
end
else
_, changed = self:CreateOrUpdateUnit(unitid)
end
self:ProcessRoster()
end
if changed then
self:TriggerEvent("RosterLib_RosterUpdated")
end
end
function Lib:GetPetFromOwner(id)
-- convert party3 crap to raid IDs when in raid.
local owner = self:GetUnitIDFromUnit(id)
if not owner then return end
-- get ID
if owner:find("raid") then
return owner:gsub("raid", "raidpet")
elseif owner:find("party") then
return owner:gsub("party", "partypet")
elseif owner == "player" then
return "pet"
else
return nil
end
end
function Lib:ScanUnknownUnits()
local changed
for unitid in pairs(unknownUnits) do
local name, c
if UnitExists(unitid) then
name, c = self:CreateOrUpdateUnit(unitid)
else
unknownUnits[unitid] = nil
end
-- some pets never have a name. too bad for them, farewell!
if not name and unitid:find("pet") then
unknownUnits[unitid] = nil
else
changed = changed or c
end
end
self:ProcessRoster()
if changed then
self:TriggerEvent("RosterLib_RosterUpdated")
end
end
function Lib:ProcessRoster()
if next(unknownUnits, nil) then
self:CancelScheduledEvent("ScanUnknownUnits")
self:ScheduleEvent("ScanUnknownUnits", self.ScanUnknownUnits, 1, self)
end
end
function Lib:CreateOrUpdateUnit(unitid, isInRaid)
-- check for name
local name = UnitName(unitid)
if name and name ~= UNKNOWNOBJECT and name ~= UKNOWNBEING and not UnitIsCharmed(unitid) then
local unit = roster[name]
local isPet = unitid:find("pet")
-- clear stuff
unknownUnits[unitid] = nil
-- return if a pet attempts to replace a player name
-- this doesnt fix the problem with 2 pets overwriting each other FIXME
if isPet and unit and unit.class ~= "PET" then
return name
end
-- save old data if existing
local old_name, old_unitid, old_class, old_rank, old_subgroup, old_online
if unit then
old_name = unit.name
old_unitid = unit.unitid
old_class = unit.class
old_rank = unit.rank
old_subgroup = unit.subgroup
old_online = unit.online
else
-- object
unit = new()
roster[name] = unit
end
local new_name, new_unitid, new_class, new_rank, new_subgroup, new_online
-- name
new_name = name
-- unitid
new_unitid = unitid
-- class
if isPet then
new_class = "PET"
else
_, new_class = UnitClass(unitid)
end
if isInRaid == nil and GetNumRaidMembers() > 0 then
isInRaid = true
end
-- subgroup and rank
new_subgroup = 1
new_rank = 0 -- XXX Support party leader?
if isInRaid then
local _,_,num = unitid:find("(%d+)")
if num then
_, new_rank, new_subgroup = GetRaidRosterInfo(num)
end
end
-- online/offline status
new_online = UnitIsConnected(unitid)
-- compare data
if not old_name
or new_name ~= old_name
or new_unitid ~= old_unitid
or new_class ~= old_class
or new_subgroup ~= old_subgroup
or new_rank ~= old_rank
or new_online ~= old_online
then
unit.name = new_name
unit.unitid = new_unitid
unit.class = new_class
unit.subgroup = new_subgroup
unit.rank = new_rank
unit.online = new_online
self:TriggerEvent("RosterLib_UnitChanged",
new_unitid, new_name, new_class, new_subgroup, new_rank,
old_name, old_unitid, old_class, old_subgroup, old_rank)
return name, true
end
return name
else
unknownUnits[unitid] = true
return false
end
end
function Lib:RemoveUnit(name)
local r = roster[name]
roster[name] = nil
self:TriggerEvent("RosterLib_UnitChanged",
nil, nil, nil, nil, nil,
r.name, r.unitid, r.class, r.subgroup, r.rank)
r = del(r)
end
------------------------------------------------
-- API
------------------------------------------------
function Lib:GetUnitIDFromName(name)
if roster[name] then
return roster[name].unitid
else
return nil
end
end
function Lib:GetUnitIDFromUnit(unit)
local name = UnitName(unit)
if name and roster[name] then
return roster[name].unitid
else
return nil
end
end
function Lib:GetUnitObjectFromName(name)
return roster[name]
end
function Lib:GetUnitObjectFromUnit(unit)
local name = UnitName(unit)
return roster[name]
end
local function iter(t)
local key = t.key
local pets = t.pets
repeat
key = next(roster, key)
if not key then
t = del(t)
return nil
end
until (pets or roster[key].class ~= "PET")
t.key = key
return roster[key]
end
function Lib:IterateRoster(pets)
local t = new()
t.pets = pets
return iter, t
end
AceLibrary:Register(Lib, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.