Add files via upload
This commit is contained in:
@@ -0,0 +1 @@
|
||||
pyinstaller --noconsole --onefile --add-data "Payload;Payload" --add-data "vanilla-tweaks.exe;." setup_tool.py
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,249 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Script file="AutoLogin.lua"/>
|
||||
<Button name="AutologinAccountButtonTemplate" virtual="true" registerForClicks="LeftButton,RightButton">
|
||||
<Size>
|
||||
<AbsDimension x="256" y="60"/>
|
||||
</Size>
|
||||
<HitRectInsets>
|
||||
<AbsInset left="0" right="0" top="0" bottom="15"/>
|
||||
</HitRectInsets>
|
||||
<Frames>
|
||||
<Frame name="$parentButtonText" setAllPoints="true">
|
||||
<Layers>
|
||||
<Layer level="BORDER">
|
||||
<FontString name="$parentName" inherits="GlueFontNormal" justifyH="LEFT">
|
||||
<Size>
|
||||
<AbsDimension x="217" y="13"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="0" y="-5"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString name="$parentZone" inherits="GlueFontDisableSmall" justifyH="RIGHT">
|
||||
<Size>
|
||||
<AbsDimension x="217" y="13"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMRIGHT">
|
||||
<Offset>
|
||||
<AbsDimension x="-40" y="25"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
</Frame>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnMouseUp>
|
||||
AutologinAccountButton_OnClick(arg1);
|
||||
</OnMouseUp>
|
||||
<OnDoubleClick>
|
||||
AutologinAccountButton_OnDoubleClick();
|
||||
</OnDoubleClick>
|
||||
</Scripts>
|
||||
<HighlightTexture file="Interface\Glues\CharacterSelect\Glue-CharacterSelect-Highlight" alphaMode="ADD">
|
||||
<Size>
|
||||
<AbsDimension x="256" y="60"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="-20" y="8"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</HighlightTexture>
|
||||
<NormalFont inherits="GlueFontNormal"/>
|
||||
<HighlightFont inherits="GlueFontHighlight"/>
|
||||
</Button>
|
||||
<Frame name="AutologinAccountsFrame" parent="AccountLoginUI">
|
||||
<Size>
|
||||
<AbsDimension x="260" y="490"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPRIGHT">
|
||||
<Offset>
|
||||
<AbsDimension x="-5" y="-15"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<FontString text="Select account" inherits="GlueFontDisableLarge" name="AutologinSelectAccountText">
|
||||
<Size>
|
||||
<AbsDimension x="256" y="13"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOP">
|
||||
<Offset>
|
||||
<AbsDimension x="0" y="-10"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<Button name="AutologinAccountButton1" inherits="AutologinAccountButtonTemplate" id="1">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="AutologinAccountsFrame" relativePoint="TOP">
|
||||
<Offset>
|
||||
<AbsDimension x="22" y="-30"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="AutologinAccountButton2" inherits="AutologinAccountButtonTemplate" id="2">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="AutologinAccountButton1" relativePoint="BOTTOM">
|
||||
<Offset>
|
||||
<AbsDimension y="13"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="AutologinAccountButton3" inherits="AutologinAccountButtonTemplate" id="3">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="AutologinAccountButton2" relativePoint="BOTTOM">
|
||||
<Offset>
|
||||
<AbsDimension y="13"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="AutologinAccountButton4" inherits="AutologinAccountButtonTemplate" id="4">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="AutologinAccountButton3" relativePoint="BOTTOM">
|
||||
<Offset>
|
||||
<AbsDimension y="13"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="AutologinAccountButton5" inherits="AutologinAccountButtonTemplate" id="5">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="AutologinAccountButton4" relativePoint="BOTTOM">
|
||||
<Offset>
|
||||
<AbsDimension y="13"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="AutologinAccountButton6" inherits="AutologinAccountButtonTemplate" id="6">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="AutologinAccountButton5" relativePoint="BOTTOM">
|
||||
<Offset>
|
||||
<AbsDimension y="13"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="AutologinAccountButton7" inherits="AutologinAccountButtonTemplate" id="7">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="AutologinAccountButton6" relativePoint="BOTTOM">
|
||||
<Offset>
|
||||
<AbsDimension y="13"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="AutologinAccountButton8" inherits="AutologinAccountButtonTemplate" id="8">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="AutologinAccountButton7" relativePoint="BOTTOM">
|
||||
<Offset>
|
||||
<AbsDimension y="13"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="AutologinAccountButton9" inherits="AutologinAccountButtonTemplate" id="9">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="AutologinAccountButton8" relativePoint="BOTTOM">
|
||||
<Offset>
|
||||
<AbsDimension y="13"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="AutologinRemoveAccountButton" inherits="GlueButtonSmallTemplate" text="Remove account">
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOM" relativeTo="AutologinAccountsFrame" relativePoint="BOTTOM">
|
||||
<Offset>
|
||||
<AbsDimension x="0" y="5"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
AutologinRemoveAccountButton_OnClick()
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="AutologinNextPage">
|
||||
<Size>
|
||||
<AbsDimension x="32" y="32"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMRIGHT" relativeTo="AutologinAccountsFrame" relativePoint="BOTTOMRIGHT">
|
||||
<Offset>
|
||||
<AbsDimension x="-5" y="10"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
LoginManager:NextPage()
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
<NormalTexture file="Interface\Glues\Common\Glue-RightArrow-Button-Up"/>
|
||||
<PushedTexture file="Interface\Glues\Common\Glue-RightArrow-Button-Down"/>
|
||||
<HighlightTexture file="Interface\Glues\Common\Glue-RightArrow-Button-Highlight" alphaMode="ADD"/>
|
||||
</Button>
|
||||
<Button name="AutologinPrevPage">
|
||||
<Size>
|
||||
<AbsDimension x="32" y="32"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMLEFT" relativeTo="AutologinAccountsFrame" relativePoint="BOTTOMLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="10" y="10"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
LoginManager:PrevPage()
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
<NormalTexture file="Interface\Glues\Common\Glue-LeftArrow-Button-Up"/>
|
||||
<PushedTexture file="Interface\Glues\Common\Glue-LeftArrow-Button-Down"/>
|
||||
<HighlightTexture file="Interface\Glues\Common\Glue-LeftArrow-Button-Highlight" alphaMode="ADD"/>
|
||||
</Button>
|
||||
</Frames>
|
||||
<Backdrop bgFile="Interface\Glues\Common\Glue-Tooltip-Background" edgeFile="Interface\Glues\Common\Glue-Tooltip-Border" tile="true">
|
||||
<BackgroundInsets>
|
||||
<AbsInset left="10" right="5" top="4" bottom="9"/>
|
||||
</BackgroundInsets>
|
||||
<TileSize>
|
||||
<AbsValue val="16"/>
|
||||
</TileSize>
|
||||
<EdgeSize>
|
||||
<AbsValue val="16"/>
|
||||
</EdgeSize>
|
||||
</Backdrop>
|
||||
<Scripts>
|
||||
<OnShow>
|
||||
if not (LoginManager and (LoginManager.has_nampower or LoginManager.has_superwow)) then
|
||||
this:Hide()
|
||||
end
|
||||
</OnShow>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
</Ui>
|
||||
@@ -0,0 +1,25 @@
|
||||
GlueConstants.lua
|
||||
GlueStrings.lua
|
||||
GlueFonts.xml
|
||||
GlueLocalization.xml
|
||||
GlueBasicControls.xml
|
||||
GlueParent.xml
|
||||
GlueButtons.xml
|
||||
GlueTemplates.xml
|
||||
GlueDialog.xml
|
||||
GlueDropDownMenu.xml
|
||||
GlueSplash.xml
|
||||
RealmList.xml
|
||||
AddonList.xml
|
||||
AccountLogin.xml
|
||||
CharacterSelect.xml
|
||||
CharacterCreate.xml
|
||||
PatchDownload.xml
|
||||
MovieFrame.xml
|
||||
GlueTooltip.xml
|
||||
RealmWizard.xml
|
||||
CreditsFrame.xml
|
||||
ChallengeList.xml
|
||||
|
||||
# Autologin 2.3.1
|
||||
AutoLogin.xml
|
||||
@@ -0,0 +1,22 @@
|
||||
### Addon to allow compability between the mod and vanilla default interface. Also adds a minimap icon to change some mod settings. It is recommended for addon makers to read this to get a gist of some of the mod's features
|
||||
|
||||
### Settings
|
||||
Right click the minimap icon to choose persistent options for:
|
||||
- Autoloot
|
||||
- Clickthrough
|
||||
- GUID in combat log/events
|
||||
- FoV
|
||||
- Background Sound
|
||||
- Uncapped Sounds
|
||||
- Targetting circle style
|
||||
|
||||
**NOTE: If using Vanilla Tweaks quickloot all of the Autoloot options will be reversed (always on will actually be always off, Shift to toggle on will be Shift to toggle off etc).**
|
||||
|
||||
### Other features
|
||||
- Adds "mouseover" support to all default blizzard unitframes
|
||||
- Shows item charges in bag
|
||||
- Allows Shift clicking a spell in your spellbook to link it to chat
|
||||
- Adds chat command to toggle showing unit names or GUIDs in combat log
|
||||
|
||||
## Modules:
|
||||
https://github.com/balakethelock/SuperAPI_Castlib provides improved castbars, alternatively the latest version of pfui/shaguplates now do the same
|
||||
@@ -0,0 +1,151 @@
|
||||
-- No superwow, no superapi
|
||||
if not SUPERWOW_VERSION then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("No SuperWoW detected");
|
||||
-- this version of SuperAPI is made for SuperWoW 1.2
|
||||
-- can somebody make this warning better?
|
||||
return
|
||||
end
|
||||
|
||||
SUPERAPI_ContainerItemsTable = {}
|
||||
|
||||
SuperAPI = AceLibrary("AceAddon-2.0"):new("AceEvent-2.0", "AceDebug-2.0", "AceModuleCore-2.0", "AceConsole-2.0", "AceDB-2.0", "AceHook-2.1")
|
||||
SuperAPI:RegisterDB("SuperAPIDB")
|
||||
SuperAPI.frame = CreateFrame("Frame", "SuperAPI", UIParent)
|
||||
|
||||
function SuperAPI:OnEnable()
|
||||
-- Let macro frame allow 511 characters
|
||||
MacroFrame_LoadUI();
|
||||
MacroFrameText:SetMaxLetters(511);
|
||||
MACROFRAME_CHAR_LIMIT = "%d/511 Characters Used";
|
||||
|
||||
-- Change chat bubbles options name
|
||||
OPTION_TOOLTIP_PARTY_CHAT_BUBBLES = "Shows whisper, party, raid, and battleground chat text in speech bubbles above characters' heads.";
|
||||
PARTY_CHAT_BUBBLES_TEXT = "Show Whisper and Group Chat Bubbles";
|
||||
|
||||
SuperAPI.SetItemRefOriginal = SetItemRef
|
||||
SuperAPI.SpellButton_OnClickOriginal = SpellButton_OnClick
|
||||
SuperAPI.SetItemButtonCountOriginal = SetItemButtonCount
|
||||
SuperAPI.SetActionOriginal = GameTooltip.SetAction
|
||||
SuperAPI.UnitFrame_OnEnterOriginal = UnitFrame_OnEnter
|
||||
SuperAPI.UnitFrame_OnLeaveOriginal = UnitFrame_OnLeave
|
||||
SuperAPI.CombatText_AddMessageOriginal = CombatText_AddMessage
|
||||
|
||||
-- activate hooks
|
||||
SetItemRef = SuperAPI.SetItemRef
|
||||
SpellButton_OnClick = SuperAPI.SpellButton_OnClick
|
||||
SetItemButtonCount = SuperAPI.SetItemButtonCount
|
||||
GameTooltip.SetAction = SuperAPI.SetAction
|
||||
UnitFrame_OnEnter = SuperAPI.UnitFrame_OnEnter
|
||||
UnitFrame_OnLeave = SuperAPI.UnitFrame_OnLeave
|
||||
CombatText_AddMessage = SuperAPI.CombatText_AddMessage
|
||||
|
||||
|
||||
-- SuperAPI.frame:RegisterEvent("BAG_UPDATE")
|
||||
-- SuperAPI.frame:RegisterEvent("BAG_UPDATE_COOLDOWN")
|
||||
SuperAPI.frame:SetScript("OnEvent", SuperAPI.OnEvent)
|
||||
|
||||
-- this chatcommand is empty. It is essential for showing tooltips of macros
|
||||
-- the format for showing a tooltip on a macro is EXACTLY this: /tooltip spell:spellid and then skip line
|
||||
SLASH_MACROTOOLTIP1 = "/tooltip"
|
||||
SlashCmdList["MACROTOOLTIP"] = function(cmd)
|
||||
end
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00SuperAPI|cffffaaaa Loaded. Check the minimap icon for options.")
|
||||
end
|
||||
|
||||
function SuperAPI:OnEvent()
|
||||
if (event == "BAG_UPDATE_COOLDOWN" or event == "BAG_UPDATE") then
|
||||
SUPERAPI_ContainerItemsTable = {}
|
||||
for ibag = 0, 4 do
|
||||
for islot = 1, GetContainerNumSlots(ibag) do
|
||||
local bagitemlink = GetContainerItemLink(ibag, islot)
|
||||
if bagitemlink then
|
||||
local _, _, bagitemID = strfind(bagitemlink, "item:(%d+)")
|
||||
bagitemID = tonumber(bagitemID)
|
||||
SUPERAPI_ContainerItemsTable[bagitemID] = { bag = ibag; slot = islot }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- HOOKS --
|
||||
-- Global function to get a spell link from its exact id
|
||||
SuperAPI.GetSpellLink = function(id)
|
||||
local spellname = SpellInfo(id)
|
||||
local link = "\124cffffffff\124Henchant:" .. id .. "\124h[" .. spellname .. "]\124h\124r"
|
||||
return link
|
||||
end
|
||||
|
||||
-- reformat "Enchant" itemlinks to better supported "Spell" itemlinks
|
||||
SuperAPI.SetItemRef = function(link, text, button)
|
||||
link = gsub(link, "spell:", "enchant:")
|
||||
SuperAPI.SetItemRefOriginal(link, text, button)
|
||||
end
|
||||
|
||||
-- hooking spellbook frame to get a spell link on shift clicking a spell's button with chatframe open
|
||||
SuperAPI.SpellButton_OnClick = function(drag)
|
||||
if ((not drag) and IsShiftKeyDown() and ChatFrameEditBox:IsVisible() and (not MacroFrame or not MacroFrame:IsVisible())) then
|
||||
local bookId = SpellBook_GetSpellID(this:GetID());
|
||||
local _, _, spellID = GetSpellName(bookId, SpellBookFrame.bookType)
|
||||
local link = SuperAPI.GetSpellLink(spellID)
|
||||
ChatFrameEditBox:Insert(link)
|
||||
else
|
||||
SuperAPI.SpellButton_OnClickOriginal(drag)
|
||||
end
|
||||
end
|
||||
|
||||
-- hooking bags item button frames to show uses count
|
||||
SuperAPI.SetItemButtonCount = function(button, count)
|
||||
if not button or not count then
|
||||
return SuperAPI.SetItemButtonCountOriginal(button, count)
|
||||
end
|
||||
if (count < 0) then
|
||||
if (count < -999) then
|
||||
count = "*";
|
||||
end
|
||||
getglobal(button:GetName() .. "Count"):SetText(-count);
|
||||
getglobal(button:GetName() .. "Count"):Show();
|
||||
getglobal(button:GetName() .. "Count"):SetFontObject(NumberFontNormalYellow);
|
||||
else
|
||||
getglobal(button:GetName() .. "Count"):SetFontObject(NumberFontNormal);
|
||||
SuperAPI.SetItemButtonCountOriginal(button, count)
|
||||
end
|
||||
end
|
||||
|
||||
-- hooking actionbutton tooltip to show item tooltip on macros
|
||||
SuperAPI.SetAction = function(this, buttonID)
|
||||
--local name, actiontype, macroID = GetActionText(buttonID)
|
||||
--if actiontype == "MACRO" then
|
||||
-- local _,_, body = GetMacroInfo(macroID)
|
||||
-- local _,_, itemID = strfind(body, "^/tooltip item:(%d+)")
|
||||
-- if itemID then
|
||||
-- itemID = tonumber(itemID)
|
||||
-- iteminfo = SUPERAPI_ContainerItemsTable[itemID]
|
||||
-- if iteminfo then
|
||||
-- return this:SetBagItem(iteminfo.bag, iteminfo.slot)
|
||||
-- end
|
||||
-- end
|
||||
--end
|
||||
--
|
||||
return SuperAPI.SetActionOriginal(this, buttonID)
|
||||
end
|
||||
|
||||
-- Add Mouseover casting to default blizzard unitframes and all unitframe addons that use the same function
|
||||
SuperAPI.UnitFrame_OnEnter = function()
|
||||
SuperAPI.UnitFrame_OnEnterOriginal()
|
||||
SetMouseoverUnit(this.unit)
|
||||
end
|
||||
|
||||
SuperAPI.UnitFrame_OnLeave = function()
|
||||
SuperAPI.UnitFrame_OnLeaveOriginal()
|
||||
SetMouseoverUnit()
|
||||
end
|
||||
|
||||
-- Fix scrolling combat text healer name
|
||||
SuperAPI.CombatText_AddMessage = function(message, scrollFunction, r, g, b, displayType, isStaggered)
|
||||
local newMessage = gsub(message, "(%s%[)(0x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x)(%])", function(bracket1, hex, bracket2)
|
||||
if UnitIsUnit(hex, "player") then return nil
|
||||
else return " ["..UnitName(hex).."]" end
|
||||
end)
|
||||
return SuperAPI.CombatText_AddMessageOriginal(newMessage, scrollFunction, r, g, b, displayType, isStaggered)
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
## Interface: 11200
|
||||
## Title: SuperAPI |cFFFF8080-balake-|r
|
||||
## Notes: the companion compatibility addon to the mod of the same name.
|
||||
## Author: balake
|
||||
## SavedVariablesPerCharacter: SuperAPIDB
|
||||
## X-Embeds: Ace2, FuBarPlugin-2.0, CompostLib, DewDropLib, TabletLib
|
||||
|
||||
libs\AceLibrary\AceLibrary.lua
|
||||
|
||||
libs\AceOO-2.0\AceOO-2.0.lua
|
||||
libs\AceAddon-2.0\AceAddon-2.0.lua
|
||||
libs\AceConsole-2.0\AceConsole-2.0.lua
|
||||
libs\AceHook-2.1\AceHook-2.1.lua
|
||||
libs\AceDB-2.0\AceDB-2.0.lua
|
||||
libs\AceDebug-2.0\AceDebug-2.0.lua
|
||||
libs\AceEvent-2.0\AceEvent-2.0.lua
|
||||
libs\AceLocale-2.2\AceLocale-2.2.lua
|
||||
libs\AceModuleCore-2.0\AceModuleCore-2.0.lua
|
||||
|
||||
libs\Dewdrop\Dewdrop-2.0.lua
|
||||
libs\Tablet\Tablet-2.0.lua
|
||||
libs\FuBarPlugin\FuBarPlugin-2.0.lua
|
||||
|
||||
SuperAPI.lua
|
||||
SuperAPIOptions.lua
|
||||
@@ -0,0 +1,210 @@
|
||||
-- No superwow, no superapi
|
||||
if not SetAutoloot then
|
||||
return
|
||||
end
|
||||
|
||||
SuperAPI.AUTOLOOT_OPTIONS = {
|
||||
"Always on",
|
||||
"Always off",
|
||||
"Shift to toggle on",
|
||||
"Shift to toggle off",
|
||||
}
|
||||
|
||||
SuperAPI.SELECTION_CIRCLE_STYLE = {
|
||||
"Default - incomplete circle",
|
||||
"Full circle (must download texture)",
|
||||
"Full circle with arrow for facing direction (must download texture)",
|
||||
"Classic incomplete circle oriented in facing direction",
|
||||
}
|
||||
|
||||
SuperAPI:RegisterDefaults("profile", {
|
||||
autoloot = SuperAPI.AUTOLOOT_OPTIONS[3],
|
||||
clickthrough = false,
|
||||
})
|
||||
|
||||
SuperAPI.IfShiftAutoloot = function()
|
||||
if IsShiftKeyDown() then
|
||||
SetAutoloot(1)
|
||||
else
|
||||
SetAutoloot(0)
|
||||
end
|
||||
end
|
||||
|
||||
SuperAPI.IfShiftNoAutoloot = function()
|
||||
if IsShiftKeyDown() then
|
||||
SetAutoloot(0)
|
||||
else
|
||||
SetAutoloot(1)
|
||||
end
|
||||
end
|
||||
|
||||
SuperAPI.cmdtable = {
|
||||
type = "group",
|
||||
handler = SuperAPI,
|
||||
args = {
|
||||
autoloot = {
|
||||
type = "text",
|
||||
name = "Autoloot (Read tooltip)",
|
||||
desc = "Specifies autoloot behavior. If using Vanilla Tweaks quickloot all of these will be reversed (always on will actually be always off, Shift to toggle on will be Shift to toggle off etc).",
|
||||
order = 10,
|
||||
validate = SuperAPI.AUTOLOOT_OPTIONS,
|
||||
get = function()
|
||||
return SuperAPI.db.profile.autoloot
|
||||
end,
|
||||
set = function(v)
|
||||
SuperAPI.db.profile.autoloot = v
|
||||
if v == SuperAPI.AUTOLOOT_OPTIONS[1] then
|
||||
-- "Always on"
|
||||
SetAutoloot(1)
|
||||
SuperAPI.frame:SetScript("OnUpdate", nil)
|
||||
elseif v == SuperAPI.AUTOLOOT_OPTIONS[2] then
|
||||
-- "Always off"
|
||||
SetAutoloot(0)
|
||||
SuperAPI.frame:SetScript("OnUpdate", nil)
|
||||
elseif v == SuperAPI.AUTOLOOT_OPTIONS[3] then
|
||||
-- "Shift to toggle on"
|
||||
SetAutoloot(0)
|
||||
SuperAPI.frame:SetScript("OnUpdate", SuperAPI.IfShiftAutoloot)
|
||||
elseif v == SuperAPI.AUTOLOOT_OPTIONS[4] then
|
||||
-- "Shift to toggle off"
|
||||
SetAutoloot(1)
|
||||
SuperAPI.frame:SetScript("OnUpdate", SuperAPI.IfShiftNoAutoloot)
|
||||
end
|
||||
end,
|
||||
},
|
||||
clickthrough = {
|
||||
type = "toggle",
|
||||
name = "Clickthrough corpses",
|
||||
desc = "Allows you to click through corpses to loot corpses underneath them.",
|
||||
order = 20,
|
||||
get = function()
|
||||
return Clickthrough() == 1
|
||||
end,
|
||||
set = function(v)
|
||||
if v == true then
|
||||
Clickthrough(1)
|
||||
else
|
||||
Clickthrough(0)
|
||||
end
|
||||
SuperAPI.db.profile.clickthrough = v
|
||||
end,
|
||||
},
|
||||
fov = {
|
||||
type = "range",
|
||||
name = "Field of view (Requires reload)",
|
||||
desc = "Changes the field of view of the game. Requires reload to take effect.",
|
||||
order = 30,
|
||||
min = 0.1,
|
||||
max = 3.14,
|
||||
step = 0.05,
|
||||
get = function()
|
||||
return GetCVar("FoV")
|
||||
end,
|
||||
set = function(v)
|
||||
SetCVar("FoV", v)
|
||||
end,
|
||||
},
|
||||
selectioncircle = {
|
||||
type = "text",
|
||||
name = "Selection circle style",
|
||||
desc = "Changes the style of the selection circle.",
|
||||
order = 40,
|
||||
validate = SuperAPI.SELECTION_CIRCLE_STYLE,
|
||||
get = function()
|
||||
local selectioncircle = GetCVar("SelectionCircleStyle")
|
||||
if selectioncircle then
|
||||
return SuperAPI.SELECTION_CIRCLE_STYLE[tonumber(selectioncircle)]
|
||||
end
|
||||
end,
|
||||
set = function(v)
|
||||
if v == SuperAPI.SELECTION_CIRCLE_STYLE[1] then
|
||||
SetCVar("SelectionCircleStyle", "1")
|
||||
elseif v == SuperAPI.SELECTION_CIRCLE_STYLE[2] then
|
||||
SetCVar("SelectionCircleStyle", "2")
|
||||
elseif v == SuperAPI.SELECTION_CIRCLE_STYLE[3] then
|
||||
SetCVar("SelectionCircleStyle", "3")
|
||||
elseif v == SuperAPI.SELECTION_CIRCLE_STYLE[4] then
|
||||
SetCVar("SelectionCircleStyle", "4")
|
||||
end
|
||||
end,
|
||||
},
|
||||
backgroundsound = {
|
||||
type = "toggle",
|
||||
name = "Background sound",
|
||||
desc = "Allows game sound to play even when the window is in the background.",
|
||||
order = 60,
|
||||
get = function()
|
||||
return GetCVar("BackgroundSound") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
if v == true then
|
||||
SetCVar("BackgroundSound", "1")
|
||||
else
|
||||
SetCVar("BackgroundSound", "0")
|
||||
end
|
||||
end,
|
||||
},
|
||||
uncappedsounds = {
|
||||
type = "toggle",
|
||||
name = "Uncapped sounds",
|
||||
desc = "Allows more game sounds to play at the same time by removing hardcoded limit. This will also set SoundSoftwareChannels and SoundMaxHardwareChannels to 64. If you experience any weird crashes you may want to turn this off.",
|
||||
order = 70,
|
||||
get = function()
|
||||
return GetCVar("UncapSounds") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
if v == true then
|
||||
SetCVar("UncapSounds", "1")
|
||||
SetCVar("SoundSoftwareChannels", "64")
|
||||
SetCVar("SoundMaxHardwareChannels", "64")
|
||||
else
|
||||
SetCVar("UncapSounds", "0")
|
||||
SetCVar("SoundSoftwareChannels", "12")
|
||||
SetCVar("SoundMaxHardwareChannels", "12")
|
||||
end
|
||||
end,
|
||||
},
|
||||
lootsparkle = {
|
||||
type = "toggle",
|
||||
name = "Loot Sparkle",
|
||||
desc = "Toggle loot sparkle effect on lootable treasure.",
|
||||
order = 80,
|
||||
get = function()
|
||||
return GetCVar("LootSparkle") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
if v == true then
|
||||
SetCVar("LootSparkle", "1")
|
||||
else
|
||||
SetCVar("LootSparkle", "0")
|
||||
end
|
||||
end,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
local deuce = SuperAPI:NewModule("SuperAPI Options Menu")
|
||||
deuce.hasFuBar = IsAddOnLoaded("FuBar") and FuBar
|
||||
deuce.consoleCmd = not deuce.hasFuBar
|
||||
|
||||
SuperAPIOptions = AceLibrary("AceAddon-2.0"):new("AceDB-2.0", "FuBarPlugin-2.0")
|
||||
SuperAPIOptions.name = "FuBar - SuperAPI"
|
||||
SuperAPIOptions:RegisterDB("SuperAPIDB")
|
||||
SuperAPIOptions.hasIcon = "Interface\\Icons\\inv_misc_book_06"
|
||||
SuperAPIOptions.defaultMinimapPosition = 180
|
||||
SuperAPIOptions.independentProfile = true
|
||||
SuperAPIOptions.hideWithoutStandby = false
|
||||
|
||||
SuperAPIOptions.OnMenuRequest = SuperAPI.cmdtable
|
||||
local args = AceLibrary("FuBarPlugin-2.0"):GetAceOptionsDataTable(SuperAPIOptions)
|
||||
for k, v in pairs(args) do
|
||||
if SuperAPIOptions.OnMenuRequest.args[k] == nil then
|
||||
SuperAPIOptions.OnMenuRequest.args[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
function SuperAPIOptions:OnEnable()
|
||||
-- activate saved settings
|
||||
SuperAPI.cmdtable.args.autoloot.set(SuperAPI.db.profile.autoloot)
|
||||
SuperAPI.cmdtable.args.clickthrough.set(SuperAPI.db.profile.clickthrough)
|
||||
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
@@ -0,0 +1,245 @@
|
||||
--[[
|
||||
Name: AceDebug-2.0
|
||||
Revision: $Rev: 17638 $
|
||||
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/AceDebug-2.0
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceDebug-2.0
|
||||
Description: Mixin to allow for simple debugging capabilities.
|
||||
Dependencies: AceLibrary, AceOO-2.0
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceDebug-2.0"
|
||||
local MINOR_VERSION = "$Revision: 17639 $"
|
||||
|
||||
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
|
||||
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
|
||||
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
|
||||
|
||||
local DEBUGGING, TOGGLE_DEBUGGING
|
||||
|
||||
if GetLocale() == "frFR" then
|
||||
DEBUGGING = "D\195\169boguage"
|
||||
TOGGLE_DEBUGGING = "Activer/d\195\169sactiver le d\195\169boguage"
|
||||
elseif GetLocale() == "esES" then
|
||||
DEBUGGING = "Depurar"
|
||||
TOGGLE_DEBUGGING = "Activar/desactivar depurar"
|
||||
elseif GetLocale() == "deDE" then
|
||||
DEBUGGING = "Debuggen"
|
||||
TOGGLE_DEBUGGING = "Aktiviert/Deaktiviert Debugging"
|
||||
elseif GetLocale() == "koKR" then
|
||||
DEBUGGING = "디버깅"
|
||||
TOGGLE_DEBUGGING = "디버깅 기능 사용함/사용안함"
|
||||
elseif GetLocale() == "zhTW" then
|
||||
DEBUGGING = "除錯"
|
||||
TOGGLE_DEBUGGING = "啟用/停用除錯功能"
|
||||
elseif GetLocale() == "zhCN" then
|
||||
DEBUGGING = "\232\176\131\232\175\149"
|
||||
TOGGLE_DEBUGGING = "\229\144\175\231\148\168/\231\166\129\231\148\168 \232\176\131\232\175\149"
|
||||
elseif GetLocale() == "ruRU" then
|
||||
DEBUGGING = "Отладка"
|
||||
TOGGLE_DEBUGGING = "Вкл/Выкл отладку для этого аддона."
|
||||
else -- enUS
|
||||
DEBUGGING = "Debugging"
|
||||
TOGGLE_DEBUGGING = "Enable/disable debugging"
|
||||
end
|
||||
|
||||
local table_setn
|
||||
do
|
||||
local version = GetBuildInfo()
|
||||
if string.find(version, "^2%.") then
|
||||
-- 2.0.0
|
||||
table_setn = function() end
|
||||
else
|
||||
table_setn = table.setn
|
||||
end
|
||||
end
|
||||
|
||||
local math_mod = math.mod or math.fmod
|
||||
|
||||
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
|
||||
local AceDebug = AceOO.Mixin {"Debug", "CustomDebug", "IsDebugging", "SetDebugging", "SetDebugLevel", "LevelDebug", "CustomLevelDebug", "GetDebugLevel"}
|
||||
|
||||
local function print(text, r, g, b, frame, delay)
|
||||
(frame or DEFAULT_CHAT_FRAME):AddMessage(text, r, g, b, 1, delay or 5)
|
||||
end
|
||||
|
||||
local tmp
|
||||
|
||||
function AceDebug:CustomDebug(r, g, b, frame, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not self.debugging then return end
|
||||
|
||||
local output = string.format("|cff7fff7f(DEBUG) %s:[%s%3d]|r", tostring(self), date("%H:%M:%S"), math_mod(GetTime(), 1) * 1000)
|
||||
|
||||
if string.find(tostring(a1), "%%") then
|
||||
output = output .. " " .. string.format(tostring(a1), tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20))
|
||||
else
|
||||
if not tmp then
|
||||
tmp = {}
|
||||
end
|
||||
|
||||
-- This block dynamically rebuilds the tmp array stopping on the first nil.
|
||||
table.insert(tmp, output)
|
||||
|
||||
table.insert(tmp, tostring(a1))
|
||||
table.insert(tmp, a2)
|
||||
table.insert(tmp, a3)
|
||||
table.insert(tmp, a4)
|
||||
table.insert(tmp, a5)
|
||||
table.insert(tmp, a6)
|
||||
table.insert(tmp, a7)
|
||||
table.insert(tmp, a8)
|
||||
table.insert(tmp, a9)
|
||||
table.insert(tmp, a10)
|
||||
table.insert(tmp, a11)
|
||||
table.insert(tmp, a12)
|
||||
table.insert(tmp, a13)
|
||||
table.insert(tmp, a14)
|
||||
table.insert(tmp, a15)
|
||||
table.insert(tmp, a16)
|
||||
table.insert(tmp, a17)
|
||||
table.insert(tmp, a18)
|
||||
table.insert(tmp, a19)
|
||||
table.insert(tmp, a20)
|
||||
while tmp[table.getn(tmp)] == nil do
|
||||
table.remove(tmp)
|
||||
end
|
||||
for k = 1, table.getn(tmp) do
|
||||
tmp[k] = tostring(tmp[k])
|
||||
end
|
||||
|
||||
output = table.concat(tmp, " ")
|
||||
|
||||
for k,v in pairs(tmp) do
|
||||
tmp[k] = nil
|
||||
end
|
||||
table_setn(tmp, 0)
|
||||
end
|
||||
|
||||
print(output, r, g, b, frame or self.debugFrame, delay)
|
||||
end
|
||||
|
||||
function AceDebug:Debug(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
AceDebug.CustomDebug(self, nil, nil, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
|
||||
function AceDebug:IsDebugging()
|
||||
return self.debugging
|
||||
end
|
||||
|
||||
function AceDebug:SetDebugging(debugging)
|
||||
self.debugging = debugging
|
||||
end
|
||||
|
||||
-- Takes a number 1-3
|
||||
-- Level 1: Critical messages that every user should receive
|
||||
-- Level 2: Should be used for local debugging (function calls, etc)
|
||||
-- Level 3: Very verbose debugging, will dump everything and anything
|
||||
-- If set to nil, you will receive no debug information
|
||||
function AceDebug:SetDebugLevel(level)
|
||||
AceDebug:argCheck(level, 1, "number", "nil")
|
||||
if not level then
|
||||
self.debuglevel = nil
|
||||
return
|
||||
end
|
||||
if level < 1 or level > 3 then
|
||||
AceDebug:error("Bad argument #1 to `SetDebugLevel`, must be a number 1-3")
|
||||
end
|
||||
self.debuglevel = level
|
||||
end
|
||||
|
||||
function AceDebug:GetDebugLevel()
|
||||
return self.debuglevel
|
||||
end
|
||||
|
||||
function AceDebug:CustomLevelDebug(level, r, g, b, frame, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not self.debugging or not self.debuglevel then return end
|
||||
AceDebug:argCheck(level, 1, "number")
|
||||
if level < 1 or level > 3 then
|
||||
AceDebug:error("Bad argument #1 to `LevelDebug`, must be a number 1-3")
|
||||
end
|
||||
if level > self.debuglevel then return end
|
||||
|
||||
local output = string.format("|cff7fff7f(DEBUG) %s:[%s.%3d]|r", tostring(self), date("%H:%M:%S"), math_mod(GetTime(), 1) * 1000)
|
||||
|
||||
if string.find(tostring(a1), "%%") then
|
||||
output = output .. " " .. string.format(tostring(a1), tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20))
|
||||
else
|
||||
if not tmp then
|
||||
tmp = {}
|
||||
end
|
||||
|
||||
-- This block dynamically rebuilds the tmp array stopping on the first nil.
|
||||
table.insert(tmp, output)
|
||||
|
||||
table.insert(tmp, tostring(a1))
|
||||
table.insert(tmp, a2)
|
||||
table.insert(tmp, a3)
|
||||
table.insert(tmp, a4)
|
||||
table.insert(tmp, a5)
|
||||
table.insert(tmp, a6)
|
||||
table.insert(tmp, a7)
|
||||
table.insert(tmp, a8)
|
||||
table.insert(tmp, a9)
|
||||
table.insert(tmp, a10)
|
||||
table.insert(tmp, a11)
|
||||
table.insert(tmp, a12)
|
||||
table.insert(tmp, a13)
|
||||
table.insert(tmp, a14)
|
||||
table.insert(tmp, a15)
|
||||
table.insert(tmp, a16)
|
||||
table.insert(tmp, a17)
|
||||
table.insert(tmp, a18)
|
||||
table.insert(tmp, a19)
|
||||
table.insert(tmp, a20)
|
||||
while tmp[table.getn(tmp)] == nil do
|
||||
table.remove(tmp)
|
||||
end
|
||||
for k = 1, table.getn(tmp) do
|
||||
tmp[k] = tostring(tmp[k])
|
||||
end
|
||||
|
||||
output = table.concat(tmp, " ")
|
||||
|
||||
for k,v in pairs(tmp) do
|
||||
tmp[k] = nil
|
||||
end
|
||||
table_setn(tmp, 0)
|
||||
end
|
||||
|
||||
print(output, r, g, b, frame or self.debugFrame, delay)
|
||||
end
|
||||
|
||||
function AceDebug:LevelDebug(level, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not self.debugging or not self.debuglevel then return end
|
||||
AceDebug:argCheck(level, 1, "number")
|
||||
if level < 1 or level > 3 then
|
||||
AceDebug:error("Bad argument #1 to `LevelDebug`, must be a number 1-3")
|
||||
end
|
||||
if level > self.debuglevel then return end
|
||||
|
||||
AceDebug.CustomLevelDebug(self, level, nil, nil, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
|
||||
|
||||
local options
|
||||
function AceDebug:GetAceOptionsDataTable(target)
|
||||
if not options then
|
||||
options = {
|
||||
debug = {
|
||||
name = DEBUGGING,
|
||||
desc = TOGGLE_DEBUGGING,
|
||||
type = "toggle",
|
||||
get = "IsDebugging",
|
||||
set = "SetDebugging",
|
||||
order = -2,
|
||||
}
|
||||
}
|
||||
end
|
||||
return options
|
||||
end
|
||||
AceLibrary:Register(AceDebug, MAJOR_VERSION, MINOR_VERSION, AceDebug.activate)
|
||||
AceDebug = AceLibrary(MAJOR_VERSION)
|
||||
@@ -0,0 +1,973 @@
|
||||
--[[
|
||||
Name: AceEvent-2.0
|
||||
Revision: $Rev: 17803 $
|
||||
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/AceEvent-2.0
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceEvent-2.0
|
||||
Description: Mixin to allow for event handling, scheduling, and inter-addon
|
||||
communication.
|
||||
Dependencies: AceLibrary, AceOO-2.0
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceEvent-2.0"
|
||||
local MINOR_VERSION = "$Revision: 17803 $"
|
||||
|
||||
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
|
||||
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
|
||||
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
|
||||
|
||||
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
|
||||
local Mixin = AceOO.Mixin
|
||||
local AceEvent = Mixin {
|
||||
"RegisterEvent",
|
||||
"RegisterAllEvents",
|
||||
"UnregisterEvent",
|
||||
"UnregisterAllEvents",
|
||||
"TriggerEvent",
|
||||
"ScheduleEvent",
|
||||
"ScheduleRepeatingEvent",
|
||||
"CancelScheduledEvent",
|
||||
"CancelAllScheduledEvents",
|
||||
"IsEventRegistered",
|
||||
"IsEventScheduled",
|
||||
"RegisterBucketEvent",
|
||||
"UnregisterBucketEvent",
|
||||
"UnregisterAllBucketEvents",
|
||||
"IsBucketEventRegistered",
|
||||
}
|
||||
|
||||
local table_setn
|
||||
do
|
||||
local version = GetBuildInfo()
|
||||
if string.find(version, "^2%.") then
|
||||
-- 2.0.0
|
||||
table_setn = function() end
|
||||
else
|
||||
table_setn = table.setn
|
||||
end
|
||||
end
|
||||
|
||||
local weakKey = {__mode="k"}
|
||||
local new, del
|
||||
do
|
||||
local list = setmetatable({}, weakKey)
|
||||
function new()
|
||||
local t = next(list)
|
||||
if t then
|
||||
list[t] = nil
|
||||
return t
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
function del(t)
|
||||
setmetatable(t, nil)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
list[t] = true
|
||||
end
|
||||
end
|
||||
|
||||
local FAKE_NIL
|
||||
local RATE
|
||||
|
||||
local eventsWhichHappenOnce = {
|
||||
PLAYER_LOGIN = true,
|
||||
AceEvent_FullyInitialized = true,
|
||||
VARIABLES_LOADED = true,
|
||||
PLAYER_LOGOUT = true,
|
||||
}
|
||||
|
||||
local registeringFromAceEvent
|
||||
function AceEvent:RegisterEvent(event, method, once)
|
||||
AceEvent:argCheck(event, 2, "string")
|
||||
if self == AceEvent and not registeringFromAceEvent then
|
||||
AceEvent:argCheck(method, 3, "function")
|
||||
self = method
|
||||
else
|
||||
AceEvent:argCheck(method, 3, "string", "function", "nil", "boolean", "number")
|
||||
if type(method) == "boolean" or type(method) == "number" then
|
||||
AceEvent:argCheck(once, 4, "nil")
|
||||
once, method = method, event
|
||||
end
|
||||
end
|
||||
AceEvent:argCheck(once, 4, "number", "boolean", "nil")
|
||||
if eventsWhichHappenOnce[event] then
|
||||
once = true
|
||||
end
|
||||
local throttleRate
|
||||
if type(once) == "number" then
|
||||
throttleRate, once = once
|
||||
end
|
||||
if not method then
|
||||
method = event
|
||||
end
|
||||
if type(method) == "string" and type(self[method]) ~= "function" then
|
||||
AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
|
||||
else
|
||||
assert(type(method) == "function" or type(method) == "string")
|
||||
end
|
||||
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if not AceEvent_registry[event] then
|
||||
AceEvent_registry[event] = new()
|
||||
AceEvent.frame:RegisterEvent(event)
|
||||
end
|
||||
|
||||
local remember = true
|
||||
if AceEvent_registry[event][self] then
|
||||
remember = false
|
||||
end
|
||||
AceEvent_registry[event][self] = method
|
||||
|
||||
local AceEvent_onceRegistry = AceEvent.onceRegistry
|
||||
if once then
|
||||
if not AceEvent_onceRegistry then
|
||||
AceEvent.onceRegistry = new()
|
||||
AceEvent_onceRegistry = AceEvent.onceRegistry
|
||||
end
|
||||
if not AceEvent_onceRegistry[event] then
|
||||
AceEvent_onceRegistry[event] = new()
|
||||
end
|
||||
AceEvent_onceRegistry[event][self] = true
|
||||
else
|
||||
if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
|
||||
AceEvent_onceRegistry[event][self] = nil
|
||||
if not next(AceEvent_onceRegistry[event]) then
|
||||
AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local AceEvent_throttleRegistry = AceEvent.throttleRegistry
|
||||
if throttleRate then
|
||||
if not AceEvent_throttleRegistry then
|
||||
AceEvent.throttleRegistry = new()
|
||||
AceEvent_throttleRegistry = AceEvent.throttleRegistry
|
||||
end
|
||||
if not AceEvent_throttleRegistry[event] then
|
||||
AceEvent_throttleRegistry[event] = new()
|
||||
end
|
||||
if AceEvent_throttleRegistry[event][self] then
|
||||
AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
|
||||
end
|
||||
AceEvent_throttleRegistry[event][self] = setmetatable(new(), weakKey)
|
||||
local t = AceEvent_throttleRegistry[event][self]
|
||||
t[RATE] = throttleRate
|
||||
else
|
||||
if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] then
|
||||
if AceEvent_throttleRegistry[event][self] then
|
||||
AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
|
||||
end
|
||||
if not next(AceEvent_throttleRegistry[event]) then
|
||||
AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if remember then
|
||||
AceEvent:TriggerEvent("AceEvent_EventRegistered", self, event)
|
||||
end
|
||||
end
|
||||
|
||||
local ALL_EVENTS
|
||||
|
||||
function AceEvent:RegisterAllEvents(method)
|
||||
if self == AceEvent then
|
||||
AceEvent:argCheck(method, 1, "function")
|
||||
self = method
|
||||
else
|
||||
AceEvent:argCheck(method, 1, "string", "function")
|
||||
if type(method) == "string" and type(self[method]) ~= "function" then
|
||||
AceEvent:error("Cannot register all events to method %q, it does not exist", method)
|
||||
end
|
||||
end
|
||||
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if not AceEvent_registry[ALL_EVENTS] then
|
||||
AceEvent_registry[ALL_EVENTS] = new()
|
||||
AceEvent.frame:RegisterAllEvents()
|
||||
end
|
||||
|
||||
AceEvent_registry[ALL_EVENTS][self] = method
|
||||
end
|
||||
|
||||
local _G = getfenv(0)
|
||||
local memstack, timestack = {}, {}
|
||||
local memdiff, timediff
|
||||
function AceEvent:TriggerEvent(event, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
AceEvent:argCheck(event, 2, "string")
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if (not AceEvent_registry[event] or not next(AceEvent_registry[event])) and (not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS])) then
|
||||
return
|
||||
end
|
||||
local _G_event = _G.event
|
||||
_G.event = event
|
||||
local lastEvent = AceEvent.currentEvent
|
||||
AceEvent.currentEvent = event
|
||||
|
||||
local AceEvent_onceRegistry = AceEvent.onceRegistry
|
||||
local AceEvent_debugTable = AceEvent.debugTable
|
||||
if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
|
||||
local tmp = new()
|
||||
for obj, method in pairs(AceEvent_onceRegistry[event]) do
|
||||
tmp[obj] = AceEvent_registry[event] and AceEvent_registry[event][obj] or nil
|
||||
end
|
||||
local obj = next(tmp)
|
||||
while obj do
|
||||
local mem, time
|
||||
if AceEvent_debugTable then
|
||||
if not AceEvent_debugTable[event] then
|
||||
AceEvent_debugTable[event] = new()
|
||||
end
|
||||
if not AceEvent_debugTable[event][obj] then
|
||||
AceEvent_debugTable[event][obj] = new()
|
||||
AceEvent_debugTable[event][obj].mem = 0
|
||||
AceEvent_debugTable[event][obj].time = 0
|
||||
AceEvent_debugTable[event][obj].count = 0
|
||||
end
|
||||
if memdiff then
|
||||
table.insert(memstack, memdiff)
|
||||
table.insert(timestack, timediff)
|
||||
end
|
||||
memdiff, timediff = 0, 0
|
||||
mem, time = gcinfo(), GetTime()
|
||||
end
|
||||
local method = tmp[obj]
|
||||
AceEvent.UnregisterEvent(obj, event)
|
||||
if type(method) == "string" then
|
||||
local obj_method = obj[method]
|
||||
if obj_method then
|
||||
obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
elseif method then -- function
|
||||
method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
if AceEvent_debugTable then
|
||||
local dmem, dtime = memdiff, timediff
|
||||
mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
|
||||
AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
|
||||
AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
|
||||
AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1
|
||||
|
||||
memdiff, timediff = table.remove(memstack), table.remove(timestack)
|
||||
if memdiff then
|
||||
memdiff = memdiff + mem + dmem
|
||||
timediff = timediff + time + dtime
|
||||
end
|
||||
end
|
||||
tmp[obj] = nil
|
||||
obj = next(tmp)
|
||||
end
|
||||
del(tmp)
|
||||
end
|
||||
|
||||
local AceEvent_throttleRegistry = AceEvent.throttleRegistry
|
||||
local throttleTable = AceEvent_throttleRegistry and AceEvent_throttleRegistry[event]
|
||||
if AceEvent_registry[event] then
|
||||
local tmp = new()
|
||||
for obj, method in pairs(AceEvent_registry[event]) do
|
||||
tmp[obj] = method
|
||||
end
|
||||
local obj = next(tmp)
|
||||
while obj do
|
||||
local method = tmp[obj]
|
||||
local continue = false
|
||||
if throttleTable and throttleTable[obj] then
|
||||
local a1 = a1
|
||||
if a1 == nil then
|
||||
a1 = FAKE_NIL
|
||||
end
|
||||
if not throttleTable[obj][a1] or GetTime() - throttleTable[obj][a1] >= throttleTable[obj][RATE] then
|
||||
throttleTable[obj][a1] = GetTime()
|
||||
else
|
||||
continue = true
|
||||
end
|
||||
end
|
||||
if not continue then
|
||||
local mem, time
|
||||
if AceEvent_debugTable then
|
||||
if not AceEvent_debugTable[event] then
|
||||
AceEvent_debugTable[event] = new()
|
||||
end
|
||||
if not AceEvent_debugTable[event][obj] then
|
||||
AceEvent_debugTable[event][obj] = new()
|
||||
AceEvent_debugTable[event][obj].mem = 0
|
||||
AceEvent_debugTable[event][obj].time = 0
|
||||
AceEvent_debugTable[event][obj].count = 0
|
||||
end
|
||||
if memdiff then
|
||||
table.insert(memstack, memdiff)
|
||||
table.insert(timestack, timediff)
|
||||
end
|
||||
memdiff, timediff = 0, 0
|
||||
mem, time = gcinfo(), GetTime()
|
||||
end
|
||||
if type(method) == "string" then
|
||||
local obj_method = obj[method]
|
||||
if obj_method then
|
||||
obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
elseif method then -- function
|
||||
method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
if AceEvent_debugTable then
|
||||
local dmem, dtime = memdiff, timediff
|
||||
mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
|
||||
AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
|
||||
AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
|
||||
AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1
|
||||
|
||||
memdiff, timediff = table.remove(memstack), table.remove(timestack)
|
||||
if memdiff then
|
||||
memdiff = memdiff + mem + dmem
|
||||
timediff = timediff + time + dtime
|
||||
end
|
||||
end
|
||||
end
|
||||
tmp[obj] = nil
|
||||
obj = next(tmp)
|
||||
end
|
||||
del(tmp)
|
||||
end
|
||||
if AceEvent_registry[ALL_EVENTS] then
|
||||
local tmp = new()
|
||||
for obj, method in pairs(AceEvent_registry[ALL_EVENTS]) do
|
||||
tmp[obj] = method
|
||||
end
|
||||
local obj = next(tmp)
|
||||
while obj do
|
||||
local method = tmp[obj]
|
||||
local mem, time
|
||||
if AceEvent_debugTable then
|
||||
if not AceEvent_debugTable[event] then
|
||||
AceEvent_debugTable[event] = new()
|
||||
end
|
||||
if not AceEvent_debugTable[event][obj] then
|
||||
AceEvent_debugTable[event][obj] = new()
|
||||
AceEvent_debugTable[event][obj].mem = 0
|
||||
AceEvent_debugTable[event][obj].time = 0
|
||||
AceEvent_debugTable[event][obj].count = 0
|
||||
end
|
||||
if memdiff then
|
||||
table.insert(memstack, memdiff)
|
||||
table.insert(timestack, timediff)
|
||||
end
|
||||
memdiff, timediff = 0, 0
|
||||
mem, time = gcinfo(), GetTime()
|
||||
end
|
||||
if type(method) == "string" then
|
||||
local obj_method = obj[method]
|
||||
if obj_method then
|
||||
obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
elseif method then -- function
|
||||
method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
if AceEvent_debugTable then
|
||||
local dmem, dtime = memdiff, timediff
|
||||
mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
|
||||
AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
|
||||
AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
|
||||
AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1
|
||||
|
||||
memdiff, timediff = table.remove(memstack), table.remove(timestack)
|
||||
if memdiff then
|
||||
memdiff = memdiff + mem + dmem
|
||||
timediff = timediff + time + dtime
|
||||
end
|
||||
end
|
||||
tmp[obj] = nil
|
||||
obj = next(tmp)
|
||||
end
|
||||
del(tmp)
|
||||
end
|
||||
_G.event = _G_event
|
||||
AceEvent.currentEvent = lastEvent
|
||||
end
|
||||
|
||||
-- local accessors
|
||||
local getn = table.getn
|
||||
local tinsert = table.insert
|
||||
local tremove = table.remove
|
||||
local floor = math.floor
|
||||
local GetTime = GetTime
|
||||
local next = next
|
||||
local pairs = pairs
|
||||
local unpack = unpack
|
||||
|
||||
local delayRegistry
|
||||
local tmp = {}
|
||||
local function OnUpdate()
|
||||
local t = GetTime()
|
||||
for k,v in pairs(delayRegistry) do
|
||||
tmp[k] = true
|
||||
end
|
||||
for k in pairs(tmp) do
|
||||
local v = delayRegistry[k]
|
||||
if v then
|
||||
local v_time = v.time
|
||||
if not v_time then
|
||||
delayRegistry[k] = del(v)
|
||||
elseif v_time <= t then
|
||||
local v_repeatDelay = v.repeatDelay
|
||||
if v_repeatDelay then
|
||||
-- use the event time, not the current time, else timing inaccuracies add up over time
|
||||
v.time = v_time + v_repeatDelay
|
||||
end
|
||||
local event = v.event
|
||||
local mem, time
|
||||
if AceEvent_debugTable then
|
||||
mem, time = gcinfo(), GetTime()
|
||||
end
|
||||
if type(event) == "function" then
|
||||
event(unpack(v))
|
||||
else
|
||||
AceEvent:TriggerEvent(event, unpack(v))
|
||||
end
|
||||
if AceEvent_debugTable then
|
||||
mem, time = gcinfo() - mem, GetTime() - time
|
||||
v.mem = v.mem + mem
|
||||
v.timeSpent = v.timeSpent + time
|
||||
v.count = v.count + 1
|
||||
end
|
||||
if not v_repeatDelay then
|
||||
local x = delayRegistry[k]
|
||||
if x and x.time == v_time then -- check if it was manually reset
|
||||
delayRegistry[k] = del(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for k in pairs(tmp) do
|
||||
tmp[k] = nil
|
||||
end
|
||||
if not next(delayRegistry) then
|
||||
AceEvent.frame:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local function ScheduleEvent(self, repeating, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
local id
|
||||
if type(event) == "string" or type(event) == "table" then
|
||||
if type(event) == "table" then
|
||||
if not delayRegistry or not delayRegistry[event] then
|
||||
AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
|
||||
end
|
||||
end
|
||||
if type(delay) ~= "number" then
|
||||
id, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20
|
||||
AceEvent:argCheck(event, 3, "string", "function", --[[ so message is right ]] "number")
|
||||
AceEvent:argCheck(delay, 4, "number")
|
||||
self:CancelScheduledEvent(id)
|
||||
end
|
||||
else
|
||||
AceEvent:argCheck(event, 2, "string", "function")
|
||||
AceEvent:argCheck(delay, 3, "number")
|
||||
end
|
||||
|
||||
if not delayRegistry then
|
||||
AceEvent.delayRegistry = new()
|
||||
delayRegistry = AceEvent.delayRegistry
|
||||
AceEvent.frame:SetScript("OnUpdate", OnUpdate)
|
||||
end
|
||||
local t
|
||||
if type(id) == "table" then
|
||||
for k in pairs(id) do
|
||||
id[k] = nil
|
||||
end
|
||||
t = id
|
||||
else
|
||||
t = new()
|
||||
end
|
||||
t[1] = a1
|
||||
t[2] = a2
|
||||
t[3] = a3
|
||||
t[4] = a4
|
||||
t[5] = a5
|
||||
t[6] = a6
|
||||
t[7] = a7
|
||||
t[8] = a8
|
||||
t[9] = a9
|
||||
t[10] = a10
|
||||
t[11] = a11
|
||||
t[12] = a12
|
||||
t[13] = a13
|
||||
t[14] = a14
|
||||
t[15] = a15
|
||||
t[16] = a16
|
||||
t[17] = a17
|
||||
t[18] = a18
|
||||
t[19] = a19
|
||||
t[20] = a20
|
||||
table_setn(t, 20)
|
||||
t.event = event
|
||||
t.time = GetTime() + delay
|
||||
t.self = self
|
||||
t.id = id or t
|
||||
t.repeatDelay = repeating and delay
|
||||
if AceEvent_debugTable then
|
||||
t.mem = 0
|
||||
t.count = 0
|
||||
t.timeSpent = 0
|
||||
end
|
||||
delayRegistry[t.id] = t
|
||||
AceEvent.frame:Show()
|
||||
return t.id
|
||||
end
|
||||
|
||||
function AceEvent:ScheduleEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if type(event) == "string" or type(event) == "table" then
|
||||
if type(event) == "table" then
|
||||
if not delayRegistry or not delayRegistry[event] then
|
||||
AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
|
||||
end
|
||||
end
|
||||
if type(delay) ~= "number" then
|
||||
AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
|
||||
AceEvent:argCheck(a1, 4, "number")
|
||||
end
|
||||
else
|
||||
AceEvent:argCheck(event, 2, "string", "function")
|
||||
AceEvent:argCheck(delay, 3, "number")
|
||||
end
|
||||
|
||||
return ScheduleEvent(self, false, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
|
||||
function AceEvent:ScheduleRepeatingEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if type(event) == "string" or type(event) == "table" then
|
||||
if type(event) == "table" then
|
||||
if not delayRegistry or not delayRegistry[event] then
|
||||
AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
|
||||
end
|
||||
end
|
||||
if type(delay) ~= "number" then
|
||||
AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
|
||||
AceEvent:argCheck(a1, 4, "number")
|
||||
end
|
||||
else
|
||||
AceEvent:argCheck(event, 2, "string", "function")
|
||||
AceEvent:argCheck(delay, 3, "number")
|
||||
end
|
||||
|
||||
return ScheduleEvent(self, true, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
|
||||
function AceEvent:CancelScheduledEvent(t)
|
||||
AceEvent:argCheck(t, 2, "string", "table")
|
||||
if delayRegistry then
|
||||
local v = delayRegistry[t]
|
||||
if v then
|
||||
delayRegistry[t] = del(v)
|
||||
if not next(delayRegistry) then
|
||||
AceEvent.frame:Hide()
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function AceEvent:IsEventScheduled(t)
|
||||
AceEvent:argCheck(t, 2, "string", "table")
|
||||
if delayRegistry then
|
||||
local v = delayRegistry[t]
|
||||
if v then
|
||||
return true, v.time - GetTime()
|
||||
end
|
||||
end
|
||||
return false, nil
|
||||
end
|
||||
|
||||
function AceEvent:UnregisterEvent(event)
|
||||
AceEvent:argCheck(event, 2, "string")
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if AceEvent_registry[event] and AceEvent_registry[event][self] then
|
||||
AceEvent_registry[event][self] = nil
|
||||
local AceEvent_onceRegistry = AceEvent.onceRegistry
|
||||
if AceEvent_onceRegistry and AceEvent_onceRegistry[event] and AceEvent_onceRegistry[event][self] then
|
||||
AceEvent_onceRegistry[event][self] = nil
|
||||
if not next(AceEvent_onceRegistry[event]) then
|
||||
AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
|
||||
end
|
||||
end
|
||||
local AceEvent_throttleRegistry = AceEvent.throttleRegistry
|
||||
if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] and AceEvent_throttleRegistry[event][self] then
|
||||
AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
|
||||
if not next(AceEvent_throttleRegistry[event]) then
|
||||
AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
|
||||
end
|
||||
end
|
||||
if not next(AceEvent_registry[event]) then
|
||||
AceEvent_registry[event] = del(AceEvent_registry[event])
|
||||
if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then
|
||||
AceEvent.frame:UnregisterEvent(event)
|
||||
end
|
||||
end
|
||||
else
|
||||
if self == AceEvent then
|
||||
error(string.format("Cannot unregister event %q. Improperly unregistering from AceEvent-2.0.", event), 2)
|
||||
else
|
||||
AceEvent:error("Cannot unregister event %q. %q is not registered with it.", event, self)
|
||||
end
|
||||
end
|
||||
AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
|
||||
end
|
||||
|
||||
function AceEvent:UnregisterAllEvents()
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then
|
||||
AceEvent_registry[ALL_EVENTS][self] = nil
|
||||
if not next(AceEvent_registry[ALL_EVENTS]) then
|
||||
del(AceEvent_registry[ALL_EVENTS])
|
||||
AceEvent.frame:UnregisterAllEvents()
|
||||
for k,v in pairs(AceEvent_registry) do
|
||||
if k ~= ALL_EVENTS then
|
||||
AceEvent.frame:RegisterEvent(k)
|
||||
end
|
||||
end
|
||||
AceEvent_registry[event] = nil
|
||||
end
|
||||
end
|
||||
local first = true
|
||||
for event, data in pairs(AceEvent_registry) do
|
||||
if first then
|
||||
if AceEvent_registry.AceEvent_EventUnregistered then
|
||||
event = "AceEvent_EventUnregistered"
|
||||
else
|
||||
first = false
|
||||
end
|
||||
end
|
||||
local x = data[self]
|
||||
data[self] = nil
|
||||
if x and event ~= ALL_EVENTS then
|
||||
if not next(data) then
|
||||
del(data)
|
||||
if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then
|
||||
AceEvent.frame:UnregisterEvent(event)
|
||||
end
|
||||
AceEvent_registry[event] = nil
|
||||
end
|
||||
AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
|
||||
end
|
||||
if first then
|
||||
event = nil
|
||||
end
|
||||
end
|
||||
if AceEvent.onceRegistry then
|
||||
for event, data in pairs(AceEvent.onceRegistry) do
|
||||
data[self] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceEvent:CancelAllScheduledEvents()
|
||||
if delayRegistry then
|
||||
for k,v in pairs(delayRegistry) do
|
||||
if v.self == self then
|
||||
delayRegistry[k] = del(v)
|
||||
end
|
||||
end
|
||||
if not next(delayRegistry) then
|
||||
AceEvent.frame:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceEvent:IsEventRegistered(event)
|
||||
AceEvent:argCheck(event, 2, "string")
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if self == AceEvent then
|
||||
return AceEvent_registry[event] and next(AceEvent_registry[event]) and true or false
|
||||
end
|
||||
if AceEvent_registry[event] and AceEvent_registry[event][self] then
|
||||
return true, AceEvent_registry[event][self]
|
||||
end
|
||||
return false, nil
|
||||
end
|
||||
|
||||
local bucketfunc
|
||||
function AceEvent:RegisterBucketEvent(event, delay, method)
|
||||
AceEvent:argCheck(event, 2, "string", "table")
|
||||
if type(event) == "table" then
|
||||
for k,v in pairs(event) do
|
||||
if type(k) ~= "number" then
|
||||
AceEvent:error("All keys to argument #2 to `RegisterBucketEvent' must be numbers.")
|
||||
elseif type(v) ~= "string" then
|
||||
AceEvent:error("All values to argument #2 to `RegisterBucketEvent' must be strings.")
|
||||
end
|
||||
end
|
||||
end
|
||||
AceEvent:argCheck(delay, 3, "number")
|
||||
if AceEvent == self then
|
||||
AceEvent:argCheck(method, 4, "function")
|
||||
self = method
|
||||
else
|
||||
if type(event) == "string" then
|
||||
AceEvent:argCheck(method, 4, "string", "function", "nil")
|
||||
if not method then
|
||||
method = event
|
||||
end
|
||||
else
|
||||
AceEvent:argCheck(method, 4, "string", "function")
|
||||
end
|
||||
|
||||
if type(method) == "string" and type(self[method]) ~= "function" then
|
||||
AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
|
||||
end
|
||||
end
|
||||
if not AceEvent.buckets then
|
||||
AceEvent.buckets = new()
|
||||
end
|
||||
if not AceEvent.buckets[event] then
|
||||
AceEvent.buckets[event] = new()
|
||||
end
|
||||
if not AceEvent.buckets[event][self] then
|
||||
AceEvent.buckets[event][self] = new()
|
||||
AceEvent.buckets[event][self].current = new()
|
||||
AceEvent.buckets[event][self].self = self
|
||||
else
|
||||
AceEvent.CancelScheduledEvent(self, AceEvent.buckets[event][self].id)
|
||||
end
|
||||
local bucket = AceEvent.buckets[event][self]
|
||||
bucket.method = method
|
||||
|
||||
local func = function(arg1)
|
||||
bucket.run = true
|
||||
if arg1 then
|
||||
bucket.current[arg1] = true
|
||||
end
|
||||
end
|
||||
AceEvent.buckets[event][self].func = func
|
||||
if type(event) == "string" then
|
||||
AceEvent.RegisterEvent(self, event, func)
|
||||
else
|
||||
for _,v in ipairs(event) do
|
||||
AceEvent.RegisterEvent(self, v, func)
|
||||
end
|
||||
end
|
||||
if not bucketfunc then
|
||||
bucketfunc = function(bucket)
|
||||
local current = bucket.current
|
||||
local method = bucket.method
|
||||
local self = bucket.self
|
||||
if bucket.run then
|
||||
if type(method) == "string" then
|
||||
self[method](self, current)
|
||||
elseif method then -- function
|
||||
method(current)
|
||||
end
|
||||
for k in pairs(current) do
|
||||
current[k] = nil
|
||||
k = nil
|
||||
end
|
||||
bucket.run = false
|
||||
end
|
||||
end
|
||||
end
|
||||
bucket.id = AceEvent.ScheduleRepeatingEvent(self, bucketfunc, delay, bucket)
|
||||
end
|
||||
|
||||
function AceEvent:IsBucketEventRegistered(event)
|
||||
AceEvent:argCheck(event, 2, "string", "table")
|
||||
return AceEvent.buckets and AceEvent.buckets[event] and AceEvent.buckets[event][self]
|
||||
end
|
||||
|
||||
function AceEvent:UnregisterBucketEvent(event)
|
||||
AceEvent:argCheck(event, 2, "string", "table")
|
||||
if not AceEvent.buckets or not AceEvent.buckets[event] or not AceEvent.buckets[event][self] then
|
||||
AceEvent:error("Cannot unregister bucket event %q. %q is not registered with it.", event, self)
|
||||
end
|
||||
|
||||
local bucket = AceEvent.buckets[event][self]
|
||||
|
||||
if type(event) == "string" then
|
||||
AceEvent.UnregisterEvent(self, event)
|
||||
else
|
||||
for _,v in ipairs(event) do
|
||||
AceEvent.UnregisterEvent(self, v)
|
||||
end
|
||||
end
|
||||
AceEvent:CancelScheduledEvent(bucket.id)
|
||||
|
||||
del(bucket.current)
|
||||
AceEvent.buckets[event][self] = del(AceEvent.buckets[event][self])
|
||||
if not next(AceEvent.buckets[event]) then
|
||||
AceEvent.buckets[event] = del(AceEvent.buckets[event])
|
||||
end
|
||||
end
|
||||
|
||||
function AceEvent:UnregisterAllBucketEvents()
|
||||
if not AceEvent.buckets or not next(AceEvent.buckets) then
|
||||
return
|
||||
end
|
||||
for k,v in pairs(AceEvent.buckets) do
|
||||
if v == self then
|
||||
AceEvent.UnregisterBucketEvent(self, k)
|
||||
k = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceEvent:OnEmbedDisable(target)
|
||||
self.UnregisterAllEvents(target)
|
||||
|
||||
self.CancelAllScheduledEvents(target)
|
||||
|
||||
self.UnregisterAllBucketEvents(target)
|
||||
end
|
||||
|
||||
function AceEvent:EnableDebugging()
|
||||
if not self.debugTable then
|
||||
self.debugTable = new()
|
||||
|
||||
if delayRegistry then
|
||||
for k,v in pairs(self.delayRegistry) do
|
||||
if not v.mem then
|
||||
v.mem = 0
|
||||
v.count = 0
|
||||
v.timeSpent = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceEvent:IsFullyInitialized()
|
||||
return self.postInit or false
|
||||
end
|
||||
|
||||
function AceEvent:IsPostPlayerLogin()
|
||||
return self.playerLogin or false
|
||||
end
|
||||
|
||||
function AceEvent:activate(oldLib, oldDeactivate)
|
||||
AceEvent = self
|
||||
|
||||
if oldLib then
|
||||
self.onceRegistry = oldLib.onceRegistry
|
||||
self.throttleRegistry = oldLib.throttleRegistry
|
||||
self.delayRegistry = oldLib.delayRegistry
|
||||
self.buckets = oldLib.buckets
|
||||
self.registry = oldLib.registry
|
||||
self.frame = oldLib.frame
|
||||
self.debugTable = oldLib.debugTable
|
||||
self.playerLogin = oldLib.pew or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage and true
|
||||
self.postInit = oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true
|
||||
self.ALL_EVENTS = oldLib.ALL_EVENTS
|
||||
self.FAKE_NIL = oldLib.FAKE_NIL
|
||||
self.RATE = oldLib.RATE
|
||||
end
|
||||
if not self.registry then
|
||||
self.registry = {}
|
||||
end
|
||||
if not self.frame then
|
||||
self.frame = CreateFrame("Frame", "AceEvent20Frame")
|
||||
end
|
||||
if not self.ALL_EVENTS then
|
||||
self.ALL_EVENTS = {}
|
||||
end
|
||||
if not self.FAKE_NIL then
|
||||
self.FAKE_NIL = {}
|
||||
end
|
||||
if not self.RATE then
|
||||
self.RATE = {}
|
||||
end
|
||||
ALL_EVENTS = self.ALL_EVENTS
|
||||
FAKE_NIL = self.FAKE_NIL
|
||||
RATE = self.RATE
|
||||
local inPlw = false
|
||||
local blacklist = {
|
||||
UNIT_INVENTORY_CHANGED = true,
|
||||
BAG_UPDATE = true,
|
||||
ITEM_LOCK_CHANGED = true,
|
||||
ACTIONBAR_SLOT_CHANGED = true,
|
||||
}
|
||||
self.frame:SetScript("OnEvent", function()
|
||||
local event = event
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
inPlw = false
|
||||
elseif event == "PLAYER_LEAVING_WORLD" then
|
||||
inPlw = true
|
||||
end
|
||||
if event and (not inPlw or not blacklist[event]) then
|
||||
self:TriggerEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
|
||||
end
|
||||
end)
|
||||
if self.delayRegistry then
|
||||
delayRegistry = self.delayRegistry
|
||||
self.frame:SetScript("OnUpdate", OnUpdate)
|
||||
end
|
||||
|
||||
self:UnregisterAllEvents()
|
||||
self:CancelAllScheduledEvents()
|
||||
|
||||
registeringFromAceEvent = true
|
||||
self:RegisterEvent("LOOT_OPENED", function()
|
||||
SendAddonMessage("LOOT_OPENED", "", "RAID")
|
||||
end)
|
||||
registeringFromAceEvent = nil
|
||||
|
||||
if not self.playerLogin then
|
||||
registeringFromAceEvent = true
|
||||
self:RegisterEvent("PLAYER_LOGIN", function()
|
||||
self.playerLogin = true
|
||||
end, true)
|
||||
registeringFromAceEvent = nil
|
||||
end
|
||||
|
||||
if not self.postInit then
|
||||
local isReload = true
|
||||
local function func()
|
||||
self.postInit = true
|
||||
self:TriggerEvent("AceEvent_FullyInitialized")
|
||||
if self.registry["CHAT_MSG_CHANNEL_NOTICE"] and self.registry["CHAT_MSG_CHANNEL_NOTICE"][self] then
|
||||
self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE")
|
||||
end
|
||||
if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
|
||||
self:UnregisterEvent("MEETINGSTONE_CHANGED")
|
||||
end
|
||||
if self.registry["MINIMAP_ZONE_CHANGED"] and self.registry["MINIMAP_ZONE_CHANGED"][self] then
|
||||
self:UnregisterEvent("MINIMAP_ZONE_CHANGED")
|
||||
end
|
||||
if self.registry["LANGUAGE_LIST_CHANGED"] and self.registry["LANGUAGE_LIST_CHANGED"][self] then
|
||||
self:UnregisterEvent("LANGUAGE_LIST_CHANGED")
|
||||
end
|
||||
end
|
||||
registeringFromAceEvent = true
|
||||
local f = function()
|
||||
self.playerLogin = true
|
||||
self:ScheduleEvent("AceEvent_FullyInitialized", func, 1)
|
||||
end
|
||||
self:RegisterEvent("MEETINGSTONE_CHANGED", f, true)
|
||||
self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", function()
|
||||
self:ScheduleEvent("AceEvent_FullyInitialized", func, 0.05)
|
||||
end)
|
||||
self:RegisterEvent("LANGUAGE_LIST_CHANGED", function()
|
||||
if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
|
||||
registeringFromAceEvent = true
|
||||
self:UnregisterEvent("MEETINGSTONE_CHANGED")
|
||||
self:RegisterEvent("MINIMAP_ZONE_CHANGED", f, true)
|
||||
registeringFromAceEvent = nil
|
||||
end
|
||||
end)
|
||||
self:ScheduleEvent("AceEvent_FullyInitialized", func, 10)
|
||||
registeringFromAceEvent = nil
|
||||
end
|
||||
|
||||
self.super.activate(self, oldLib, oldDeactivate)
|
||||
if oldLib then
|
||||
oldDeactivate(oldLib)
|
||||
end
|
||||
end
|
||||
|
||||
AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, AceEvent.activate)
|
||||
AceEvent = AceLibrary(MAJOR_VERSION)
|
||||
@@ -0,0 +1,569 @@
|
||||
--[[
|
||||
Name: AceHook-2.1
|
||||
Revision: $Rev: 17638 $
|
||||
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/AceHook-2.1
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceHook-2.1
|
||||
Description: Mixin to allow for safe hooking of functions, methods, and scripts.
|
||||
Dependencies: AceLibrary, AceOO-2.0
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceHook-2.1"
|
||||
local MINOR_VERSION = "$Revision: 17638 $"
|
||||
|
||||
-- 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
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
|
||||
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
|
||||
|
||||
--[[---------------------------------------------------------------------------------
|
||||
Create the library object
|
||||
----------------------------------------------------------------------------------]]
|
||||
|
||||
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
|
||||
local AceHook = AceOO.Mixin {
|
||||
"Hook",
|
||||
"HookScript",
|
||||
"SecureHook",
|
||||
"Unhook",
|
||||
"UnhookAll",
|
||||
"HookReport",
|
||||
"IsHooked",
|
||||
}
|
||||
|
||||
--[[---------------------------------------------------------------------------------
|
||||
Library Definitions
|
||||
----------------------------------------------------------------------------------]]
|
||||
|
||||
local protFuncs = {
|
||||
CameraOrSelectOrMoveStart = true, CameraOrSelectOrMoveStop = true,
|
||||
TurnOrActionStart = true, TurnOrActionStop = true,
|
||||
PitchUpStart = true, PitchUpStop = true,
|
||||
PitchDownStart = true, PitchDownStop = true,
|
||||
MoveBackwardStart = true, MoveBackwardStop = true,
|
||||
MoveForwardStart = true, MoveForwardStop = true,
|
||||
Jump = true, StrafeLeftStart = true,
|
||||
StrafeLeftStop = true, StrafeRightStart = true,
|
||||
StrafeRightStop = true, ToggleMouseMove = true,
|
||||
ToggleRun = true, TurnLeftStart = true,
|
||||
TurnLeftStop = true, TurnRightStart = true,
|
||||
TurnRightStop = true,
|
||||
}
|
||||
|
||||
local function issecurevariable(x)
|
||||
if protFuncs[x] then
|
||||
return 1
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local _G = getfenv(0)
|
||||
|
||||
local function hooksecurefunc(arg1, arg2, arg3)
|
||||
if type(arg1) == "string" then
|
||||
arg1, arg2, arg3 = _G, arg1, arg2
|
||||
end
|
||||
local orig = arg1[arg2]
|
||||
arg1[arg2] = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
local x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20 = orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
|
||||
arg3(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
|
||||
return x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20
|
||||
end
|
||||
end
|
||||
|
||||
local protectedScripts = {
|
||||
OnClick = true,
|
||||
}
|
||||
|
||||
|
||||
local handlers, scripts, actives, registry
|
||||
|
||||
--[[---------------------------------------------------------------------------------
|
||||
Private definitions (Not exposed)
|
||||
----------------------------------------------------------------------------------]]
|
||||
|
||||
local new, del
|
||||
do
|
||||
local list = setmetatable({}, {__mode = "k"})
|
||||
function new()
|
||||
local t = next(list)
|
||||
if not t then
|
||||
return {}
|
||||
end
|
||||
list[t] = nil
|
||||
return t
|
||||
end
|
||||
|
||||
function del(t)
|
||||
setmetatable(t, nil)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
list[t] = true
|
||||
end
|
||||
end
|
||||
|
||||
local function createFunctionHook(self, func, handler, orig, secure)
|
||||
if not secure then
|
||||
if type(handler) == "string" then
|
||||
-- The handler is a method, need to self it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
else
|
||||
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
else
|
||||
-- The handler is a function, just call it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
else
|
||||
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
end
|
||||
else
|
||||
-- secure hooks don't call the original method
|
||||
if type(handler) == "string" then
|
||||
-- The handler is a method, need to self it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
else
|
||||
-- The handler is a function, just call it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function createMethodHook(self, object, method, handler, orig, secure, script)
|
||||
if script then
|
||||
if type(handler) == "string" then
|
||||
local uid
|
||||
uid = function()
|
||||
if actives[uid] then
|
||||
return self[handler](self, object)
|
||||
else
|
||||
return orig()
|
||||
end
|
||||
end
|
||||
return uid
|
||||
else
|
||||
-- The handler is a function, just call it
|
||||
local uid
|
||||
uid = function()
|
||||
if actives[uid] then
|
||||
return handler(object)
|
||||
else
|
||||
return orig()
|
||||
end
|
||||
end
|
||||
return uid
|
||||
end
|
||||
elseif not secure then
|
||||
if type(handler) == "string" then
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
else
|
||||
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
else
|
||||
-- The handler is a function, just call it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
else
|
||||
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
end
|
||||
else
|
||||
-- secure hooks don't call the original method
|
||||
if type(handler) == "string" then
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
else
|
||||
-- The handler is a function, just call it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function hookFunction(self, func, handler, secure)
|
||||
local orig = _G[func]
|
||||
|
||||
if not orig or type(orig) ~= "function" then
|
||||
AceHook:error("Attempt to hook a non-existant function %q", func)
|
||||
end
|
||||
|
||||
if not handler then
|
||||
handler = func
|
||||
end
|
||||
|
||||
if registry[self][func] then
|
||||
local uid = registry[self][func]
|
||||
|
||||
if actives[uid] then
|
||||
-- We have an active hook from this source. Don't multi-hook
|
||||
AceHook:error("%q already has an active hook from this source.", func)
|
||||
end
|
||||
|
||||
if handlers[uid] == handler then
|
||||
-- The hook is inactive, so reactivate it
|
||||
actives[uid] = true
|
||||
return
|
||||
else
|
||||
AceHook:error("There is a stale hook for %q can't hook or reactivate.", func)
|
||||
end
|
||||
end
|
||||
|
||||
if type(handler) == "string" then
|
||||
if type(self[handler]) ~= "function" then
|
||||
AceHook:error("Could not find the the handler %q when hooking function %q", handler, func)
|
||||
end
|
||||
elseif type(handler) ~= "function" then
|
||||
AceHook:error("Could not find the handler you supplied when hooking %q", func)
|
||||
end
|
||||
|
||||
local uid = createFunctionHook(self, func, handler, orig, secure)
|
||||
registry[self][func] = uid
|
||||
actives[uid] = true
|
||||
handlers[uid] = handler
|
||||
|
||||
if not secure then
|
||||
_G[func] = uid
|
||||
self.hooks[func] = orig
|
||||
else
|
||||
hooksecurefunc(func, uid)
|
||||
end
|
||||
end
|
||||
|
||||
local function unhookFunction(self, func)
|
||||
if not registry[self][func] then
|
||||
AceHook:error("Tried to unhook %q which is not currently hooked.", func)
|
||||
end
|
||||
|
||||
local uid = registry[self][func]
|
||||
|
||||
if actives[uid] then
|
||||
-- See if we own the global function
|
||||
if self.hooks[func] and _G[func] == uid then
|
||||
_G[func] = self.hooks[func]
|
||||
self.hooks[func] = nil
|
||||
registry[self][func] = nil
|
||||
handlers[uid] = nil
|
||||
scripts[uid] = nil
|
||||
actives[uid] = nil
|
||||
-- Magically all-done
|
||||
else
|
||||
actives[uid] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function hookMethod(self, obj, method, handler, script, secure)
|
||||
if not handler then
|
||||
handler = method
|
||||
end
|
||||
|
||||
if not obj or type(obj) ~= "table" then
|
||||
AceHook:error("The object you supplied could not be found, or isn't a table.")
|
||||
end
|
||||
|
||||
local uid = registry[self][obj] and registry[self][obj][method]
|
||||
if uid then
|
||||
if actives[uid] then
|
||||
-- We have an active hook from this source. Don't multi-hook
|
||||
AceHook:error("%q already has an active hook from this source.", method)
|
||||
end
|
||||
|
||||
if handlers[uid] == handler then
|
||||
-- The hook is inactive, reactivate it.
|
||||
actives[uid] = true
|
||||
return
|
||||
else
|
||||
AceHook:error("There is a stale hook for %q can't hook or reactivate.", method)
|
||||
end
|
||||
end
|
||||
|
||||
if type(handler) == "string" then
|
||||
if type(self[handler]) ~= "function" then
|
||||
AceHook:error("Could not find the handler %q you supplied when hooking method %q", handler, method)
|
||||
end
|
||||
elseif type(handler) ~= "function" then
|
||||
AceHook:error("Could not find the handler you supplied when hooking method %q", method)
|
||||
end
|
||||
|
||||
local orig
|
||||
if script then
|
||||
if not obj.GetScript then
|
||||
AceHook:error("The object you supplied does not have a GetScript method.")
|
||||
end
|
||||
if not obj:HasScript(method) then
|
||||
AceHook:error("The object you supplied doesn't allow the %q method.", method)
|
||||
end
|
||||
|
||||
orig = obj:GetScript(method)
|
||||
if type(orig) ~= "function" then
|
||||
-- Sometimes there is not a original function for a script.
|
||||
orig = function() end
|
||||
end
|
||||
else
|
||||
orig = obj[method]
|
||||
end
|
||||
if not orig then
|
||||
AceHook:error("Could not find the method or script %q you are trying to hook.", method)
|
||||
end
|
||||
|
||||
if not registry[self][obj] then
|
||||
registry[self][obj] = new()
|
||||
end
|
||||
|
||||
if not self.hooks[obj] then
|
||||
self.hooks[obj] = new()
|
||||
end
|
||||
|
||||
local uid = createMethodHook(self, obj, method, handler, orig, secure, script)
|
||||
registry[self][obj][method] = uid
|
||||
actives[uid] = true
|
||||
handlers[uid] = handler
|
||||
scripts[uid] = script and true or nil
|
||||
|
||||
if script then
|
||||
obj:SetScript(method, uid)
|
||||
self.hooks[obj][method] = orig
|
||||
elseif not secure then
|
||||
obj[method] = uid
|
||||
self.hooks[obj][method] = orig
|
||||
else
|
||||
hooksecurefunc(obj, method, uid)
|
||||
end
|
||||
end
|
||||
|
||||
local function unhookMethod(self, obj, method)
|
||||
if not registry[self][obj] or not registry[self][obj][method] then
|
||||
AceHook:error("Attempt to unhook a method %q that is not currently hooked.", method)
|
||||
return
|
||||
end
|
||||
|
||||
local uid = registry[self][obj][method]
|
||||
|
||||
if actives[uid] then
|
||||
if scripts[uid] then -- If this is a script
|
||||
if obj:GetScript(method) == uid then
|
||||
-- We own the script. Revert to normal.
|
||||
obj:SetScript(method, self.hooks[obj][method])
|
||||
self.hooks[obj][method] = nil
|
||||
registry[self][obj][method] = nil
|
||||
handlers[uid] = nil
|
||||
scripts[uid] = nil
|
||||
actives[uid] = nil
|
||||
else
|
||||
actives[uid] = nil
|
||||
end
|
||||
else
|
||||
if self.hooks[obj] and self.hooks[obj][method] and obj[method] == uid then
|
||||
-- We own the method. Revert to normal.
|
||||
obj[method] = self.hooks[obj][method]
|
||||
self.hooks[obj][method] = nil
|
||||
registry[self][obj][method] = nil
|
||||
handlers[uid] = nil
|
||||
actives[uid] = nil
|
||||
else
|
||||
actives[uid] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.hooks[obj] and not next(self.hooks[obj]) then
|
||||
self.hooks[obj] = del(self.hooks[obj])
|
||||
end
|
||||
if not next(registry[self][obj]) then
|
||||
registry[self][obj] = del(registry[self][obj])
|
||||
end
|
||||
end
|
||||
|
||||
-- ("function" [, handler] [, hookSecure]) or (object, "method" [, handler] [, hookSecure])
|
||||
function AceHook:Hook(object, method, handler, hookSecure)
|
||||
if type(object) == "string" then
|
||||
method, handler, hookSecure = object, method, handler
|
||||
if handler == true then
|
||||
handler, hookSecure = nil, true
|
||||
end
|
||||
if not hookSecure and issecurevariable(method) then
|
||||
AceHook:error("Attempt to hook secure function %q. Use `SecureHook' or add `true' to the argument list to override.", method)
|
||||
end
|
||||
AceHook:argCheck(handler, 3, "function", "string", "nil")
|
||||
AceHook:argCheck(hookSecure, 4, "boolean", "nil")
|
||||
hookFunction(self, method, handler, false)
|
||||
else
|
||||
if handler == true then
|
||||
handler, hookSecure = nil, true
|
||||
end
|
||||
if not hookSecure and issecurevariable(object, method) then
|
||||
AceHook:error("Attempt to hook secure method %q. Use `SecureHook' or add `true' to the argument list to override.", method)
|
||||
end
|
||||
AceHook:argCheck(object, 2, "table")
|
||||
AceHook:argCheck(method, 3, "string")
|
||||
AceHook:argCheck(handler, 4, "function", "string", "nil")
|
||||
AceHook:argCheck(hookSecure, 5, "boolean", "nil")
|
||||
hookMethod(self, object, method, handler, false, false)
|
||||
end
|
||||
end
|
||||
|
||||
-- ("function", handler) or (object, "method", handler)
|
||||
function AceHook:SecureHook(object, method, handler)
|
||||
if type(object) == "string" then
|
||||
method, handler = object, method
|
||||
AceHook:argCheck(handler, 3, "function", "string", "nil")
|
||||
hookFunction(self, method, handler, true)
|
||||
else
|
||||
AceHook:argCheck(object, 2, "table")
|
||||
AceHook:argCheck(method, 3, "string")
|
||||
AceHook:argCheck(handler, 4, "function", "string", "nil")
|
||||
hookMethod(self, object, method, handler, false, true)
|
||||
end
|
||||
end
|
||||
|
||||
function AceHook:HookScript(frame, script, handler)
|
||||
AceHook:argCheck(frame, 2, "table")
|
||||
if not frame[0] or type(frame.IsFrameType) ~= "function" then
|
||||
AceHook:error("Bad argument #2 to `HookScript'. Expected frame.")
|
||||
end
|
||||
AceHook:argCheck(script, 3, "string")
|
||||
AceHook:argCheck(handler, 4, "function", "string", "nil")
|
||||
hookMethod(self, frame, script, handler, true, false)
|
||||
end
|
||||
|
||||
-- ("function") or (object, "method")
|
||||
function AceHook:IsHooked(obj, method)
|
||||
if type(obj) == "string" then
|
||||
if registry[self][obj] and actives[registry[self][obj]] then
|
||||
return true, handlers[registry[self][obj]]
|
||||
end
|
||||
else
|
||||
AceHook:argCheck(obj, 2, "string", "table")
|
||||
AceHook:argCheck(method, 3, "string")
|
||||
if registry[self][obj] and registry[self][obj][method] and actives[registry[self][obj][method]] then
|
||||
return true, handlers[registry[self][obj][method]]
|
||||
end
|
||||
end
|
||||
|
||||
return false, nil
|
||||
end
|
||||
|
||||
-- ("function") or (object, "method")
|
||||
function AceHook:Unhook(obj, method)
|
||||
if type(obj) == "string" then
|
||||
unhookFunction(self, obj)
|
||||
else
|
||||
AceHook:argCheck(obj, 2, "string", "table")
|
||||
AceHook:argCheck(method, 3, "string")
|
||||
unhookMethod(self, obj, method)
|
||||
end
|
||||
end
|
||||
|
||||
function AceHook:UnhookAll()
|
||||
for key, value in pairs(registry[self]) do
|
||||
if type(key) == "table" then
|
||||
for method in pairs(value) do
|
||||
self:Unhook(key, method)
|
||||
end
|
||||
else
|
||||
self:Unhook(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceHook:HookReport()
|
||||
DEFAULT_CHAT_FRAME:AddMessage("This is a list of all active hooks for this object:")
|
||||
if not next(registry[self]) then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("No hooks")
|
||||
end
|
||||
|
||||
for key, value in pairs(registry[self]) do
|
||||
if type(value) == "table" then
|
||||
for method, uid in pairs(value) do
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("object: %s method: %q |cff%s|r%s", tostring(key), method, actives[uid] and "00ff00Active" or "ffff00Inactive", not self.hooks[key][method] and " |cff7f7fff-Secure-|r" or ""))
|
||||
end
|
||||
else
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("function: %q |cff%s|r%s", tostring(key), actives[value] and "00ff00Active" or "ffff00Inactive", not self.hooks[key] and " |cff7f7fff-Secure-|r" or ""))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceHook:OnInstanceInit(object)
|
||||
if not object.hooks then
|
||||
object.hooks = new()
|
||||
end
|
||||
if not registry[object] then
|
||||
registry[object] = new()
|
||||
end
|
||||
end
|
||||
|
||||
AceHook.OnManualEmbed = AceHook.OnInstanceInit
|
||||
|
||||
function AceHook:OnEmbedDisable(target)
|
||||
self.UnhookAll(target)
|
||||
end
|
||||
|
||||
local function activate(self, oldLib, oldDeactivate)
|
||||
AceHook = self
|
||||
|
||||
self.handlers = oldLib and oldLib.handlers or {}
|
||||
self.registry = oldLib and oldLib.registry or {}
|
||||
self.scripts = oldLib and oldLib.scripts or {}
|
||||
self.actives = oldLib and oldLib.actives or {}
|
||||
|
||||
handlers = self.handlers
|
||||
registry = self.registry
|
||||
scripts = self.scripts
|
||||
actives = self.actives
|
||||
|
||||
AceHook.super.activate(self, oldLib, oldDeactivate)
|
||||
|
||||
if oldDeactivate then
|
||||
oldDeactivate(oldLib)
|
||||
end
|
||||
end
|
||||
|
||||
AceLibrary:Register(AceHook, MAJOR_VERSION, MINOR_VERSION, activate)
|
||||
@@ -0,0 +1,757 @@
|
||||
--[[
|
||||
Name: AceLibrary
|
||||
Revision: $Rev: 17722 $
|
||||
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: 17722 $
|
||||
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
|
||||
]]
|
||||
|
||||
local ACELIBRARY_MAJOR = "AceLibrary"
|
||||
local ACELIBRARY_MINOR = "$Revision: 17722 $"
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary and AceLibrary:HasInstance(ACELIBRARY_MAJOR) then return end -- lua51 check
|
||||
local table_setn
|
||||
do
|
||||
local version = GetBuildInfo()
|
||||
if string.find(version, "^2%.") then
|
||||
-- 2.0.0
|
||||
table_setn = function() end
|
||||
else
|
||||
table_setn = table.setn
|
||||
end
|
||||
end
|
||||
|
||||
local string_gfind = string.gmatch or string.gfind
|
||||
|
||||
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,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
local success, err = pcall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
if not success then geterrorhandler()(err) end
|
||||
end
|
||||
|
||||
-- @table AceLibrary
|
||||
-- @brief System to handle all versioning of libraries.
|
||||
local AceLibrary = {}
|
||||
local AceLibrary_mt = {}
|
||||
setmetatable(AceLibrary, AceLibrary_mt)
|
||||
|
||||
local tmp
|
||||
local function error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if type(self) ~= "table" then
|
||||
_G.error(string.format("Bad argument #1 to `error' (table expected, got %s)", type(self)), 2)
|
||||
end
|
||||
if not tmp then
|
||||
tmp = {}
|
||||
else
|
||||
for k in pairs(tmp) do tmp[k] = nil end
|
||||
table_setn(tmp, 0)
|
||||
end
|
||||
|
||||
table.insert(tmp, a1)
|
||||
table.insert(tmp, a2)
|
||||
table.insert(tmp, a3)
|
||||
table.insert(tmp, a4)
|
||||
table.insert(tmp, a5)
|
||||
table.insert(tmp, a6)
|
||||
table.insert(tmp, a7)
|
||||
table.insert(tmp, a8)
|
||||
table.insert(tmp, a9)
|
||||
table.insert(tmp, a10)
|
||||
table.insert(tmp, a11)
|
||||
table.insert(tmp, a12)
|
||||
table.insert(tmp, a13)
|
||||
table.insert(tmp, a14)
|
||||
table.insert(tmp, a15)
|
||||
table.insert(tmp, a16)
|
||||
table.insert(tmp, a17)
|
||||
table.insert(tmp, a18)
|
||||
table.insert(tmp, a19)
|
||||
table.insert(tmp, a20)
|
||||
|
||||
local stack = debugstack()
|
||||
if not message then
|
||||
local _,_,second = string.find(stack, "\n(.-)\n")
|
||||
message = "error raised! " .. second
|
||||
else
|
||||
for i = 1,table.getn(tmp) do
|
||||
tmp[i] = tostring(tmp[i])
|
||||
end
|
||||
for i = 1,10 do
|
||||
table.insert(tmp, "nil")
|
||||
end
|
||||
message = string.format(message, unpack(tmp))
|
||||
end
|
||||
|
||||
if getmetatable(self) and getmetatable(self).__tostring then
|
||||
message = string.format("%s: %s", tostring(self), message)
|
||||
elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then
|
||||
message = string.format("%s: %s", self:GetLibraryVersion(), message)
|
||||
elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then
|
||||
message = string.format("%s: %s", self.class:GetLibraryVersion(), message)
|
||||
end
|
||||
|
||||
local first = string.gsub(stack, "\n.*", "")
|
||||
local file = string.gsub(first, ".*\\(.*).lua:%d+: .*", "%1")
|
||||
file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
|
||||
|
||||
local i = 0
|
||||
for s in string_gfind(stack, "\n([^\n]*)") do
|
||||
i = i + 1
|
||||
if not string.find(s, file .. "%.lua:%d+:") then
|
||||
file = string.gsub(s, "^.*\\(.*).lua:%d+: .*", "%1")
|
||||
file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
|
||||
break
|
||||
end
|
||||
end
|
||||
local j = 0
|
||||
for s in string_gfind(stack, "\n([^\n]*)") do
|
||||
j = j + 1
|
||||
if j > i and not string.find(s, file .. "%.lua:%d+:") then
|
||||
_G.error(message, j + 1)
|
||||
return
|
||||
end
|
||||
end
|
||||
_G.error(message, 2)
|
||||
return
|
||||
end
|
||||
|
||||
local function assert(self, condition, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not condition then
|
||||
if not message then
|
||||
local stack = debugstack()
|
||||
local _,_,second = string.find(stack, "\n(.-)\n")
|
||||
message = "assertion failed! " .. second
|
||||
end
|
||||
error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
return
|
||||
end
|
||||
return condition
|
||||
end
|
||||
|
||||
local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5)
|
||||
if type(num) ~= "number" then
|
||||
error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num))
|
||||
elseif type(kind) ~= "string" then
|
||||
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 _,_,func = string.find(debugstack(), "`argCheck'.-([`<].-['>])")
|
||||
if not func then
|
||||
_,_,func = string.find(debugstack(), "([`<].-['>])")
|
||||
end
|
||||
if kind5 then
|
||||
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
|
||||
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
|
||||
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
|
||||
error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg)
|
||||
else
|
||||
error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function pcall(self, func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = _G.pcall(func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not a1 then
|
||||
error(self, string.gsub(a2, ".-%.lua:%d-: ", ""))
|
||||
else
|
||||
return a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20
|
||||
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 string.find(text, "^%$Revision: (%d+) %$$") then
|
||||
return tonumber((string.gsub(text, "^%$Revision: (%d+) %$$", "%1")))
|
||||
elseif string.find(text, "^%$Rev: (%d+) %$$") then
|
||||
return tonumber((string.gsub(text, "^%$Rev: (%d+) %$$", "%1")))
|
||||
elseif string.find(text, "^%$LastChangedRevision: (%d+) %$$") then
|
||||
return tonumber((string.gsub(text, "^%$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
|
||||
table_setn(t, 0)
|
||||
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
|
||||
|
||||
local new, del
|
||||
do
|
||||
local tables = setmetatable({}, {__mode = "k"})
|
||||
|
||||
function new()
|
||||
local t = next(tables)
|
||||
if t then
|
||||
tables[t] = nil
|
||||
return t
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
function del(t, depth)
|
||||
if depth and depth > 0 then
|
||||
for k,v in pairs(t) do
|
||||
if type(v) == "table" and not isFrame(v) then
|
||||
del(v, depth - 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
destroyTable(t)
|
||||
tables[t] = true
|
||||
end
|
||||
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 = new()
|
||||
for k,v in pairs(from) do to[k] = v end
|
||||
table_setn(to, table.getn(from))
|
||||
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)
|
||||
local createdList
|
||||
if not list then
|
||||
createdList = true
|
||||
list = new()
|
||||
list2 = new()
|
||||
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
|
||||
table_setn(to, table.getn(from))
|
||||
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)
|
||||
if createdList then
|
||||
del(list)
|
||||
del(list2)
|
||||
end
|
||||
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(string.format("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate", 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(string.format("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate", 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(string.format("Cannot find a library instance of %s.", major), 2)
|
||||
return
|
||||
end
|
||||
if minor then
|
||||
if type(minor) == "string" then
|
||||
local m = svnRevisionToNumber(minor)
|
||||
if m then
|
||||
minor = m
|
||||
else
|
||||
_G.error(string.format("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
|
||||
end
|
||||
end
|
||||
argCheck(self, minor, 2, "number")
|
||||
if data.minor ~= minor then
|
||||
_G.error(string.format("Cannot find a library instance of %s, minor version %d.", major, minor), 2)
|
||||
return
|
||||
end
|
||||
end
|
||||
return data.instance
|
||||
end
|
||||
|
||||
-- Syntax sugar. AceLibrary("FooBar-1.0")
|
||||
AceLibrary_mt.__call = AceLibrary.GetInstance
|
||||
|
||||
local donothing
|
||||
|
||||
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 type(minor) == "string" then
|
||||
local m = svnRevisionToNumber(minor)
|
||||
if m then
|
||||
minor = m
|
||||
else
|
||||
_G.error(string.format("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate", 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
|
||||
if not donothing then
|
||||
donothing = function() end
|
||||
end
|
||||
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
|
||||
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(string.format("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", major, data.minor, minor, major, minor), 2)
|
||||
return
|
||||
end
|
||||
-- This is an update
|
||||
local oldInstance = new()
|
||||
|
||||
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)
|
||||
else
|
||||
safecall(oldDeactivateFunc, oldInstance)
|
||||
end
|
||||
del(oldInstance)
|
||||
|
||||
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)
|
||||
if not self.libs then
|
||||
if oldLib then
|
||||
self.libs = oldLib.libs
|
||||
self.scannedlibs = oldLib.scannedlibs
|
||||
end
|
||||
if not self.libs then
|
||||
self.libs = {}
|
||||
end
|
||||
if not self.scannedlibs then
|
||||
self.scannedlibs = {}
|
||||
end
|
||||
end
|
||||
if not self.positions then
|
||||
if oldLib then
|
||||
self.positions = oldLib.positions
|
||||
end
|
||||
if not self.positions then
|
||||
self.positions = setmetatable({}, { __mode = "k" })
|
||||
end
|
||||
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)
|
||||
@@ -0,0 +1,538 @@
|
||||
--[[
|
||||
Name: AceLocale-2.2
|
||||
Revision: $Rev: 17638 $
|
||||
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/AceLocale-2.2
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceLocale-2.2
|
||||
Description: Localization library for addons to use to handle proper
|
||||
localization and internationalization.
|
||||
Dependencies: AceLibrary
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceLocale-2.2"
|
||||
local MINOR_VERSION = "$Revision: 17639 $"
|
||||
|
||||
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
|
||||
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
|
||||
local AceLocale = {}
|
||||
|
||||
local DEFAULT_LOCALE = "enUS"
|
||||
local _G = getfenv(0)
|
||||
|
||||
local BASE_TRANSLATIONS, DEBUGGING, TRANSLATIONS, BASE_LOCALE, TRANSLATION_TABLES, REVERSE_TRANSLATIONS, STRICTNESS, DYNAMIC_LOCALES, CURRENT_LOCALE, NAME
|
||||
|
||||
local rawget = rawget
|
||||
local rawset = rawset
|
||||
local type = type
|
||||
|
||||
local newRegistries = {}
|
||||
local scheduleClear
|
||||
|
||||
local lastSelf
|
||||
local __index = function(self, key)
|
||||
lastSelf = self
|
||||
local value = (rawget(self, TRANSLATIONS) or AceLocale.prototype)[key]
|
||||
rawset(self, key, value)
|
||||
return value
|
||||
end
|
||||
|
||||
local __newindex = function(self, k, v)
|
||||
if type(v) ~= "function" and type(k) ~= "table" then
|
||||
AceLocale.error(self, "Cannot change the values of an AceLocale instance.")
|
||||
end
|
||||
rawset(self, k, v)
|
||||
end
|
||||
|
||||
local __tostring = function(self)
|
||||
if type(rawget(self, 'GetLibraryVersion')) == "function" then
|
||||
return self:GetLibraryVersion()
|
||||
else
|
||||
return "AceLocale(" .. self[NAME] .. ")"
|
||||
end
|
||||
end
|
||||
|
||||
local function clearCache(self)
|
||||
if not rawget(self, BASE_TRANSLATIONS) then
|
||||
return
|
||||
end
|
||||
|
||||
local cache = self[BASE_TRANSLATIONS]
|
||||
rawset(self, REVERSE_TRANSLATIONS, nil)
|
||||
|
||||
for k in pairs(self) do
|
||||
if rawget(cache, k) ~= nil then
|
||||
self[k] = nil
|
||||
end
|
||||
end
|
||||
rawset(self, 'tmp', true)
|
||||
self.tmp = nil
|
||||
end
|
||||
|
||||
local function refixInstance(instance)
|
||||
if getmetatable(instance) then
|
||||
setmetatable(instance, nil)
|
||||
end
|
||||
local translations = instance[TRANSLATIONS]
|
||||
if translations then
|
||||
if getmetatable(translations) then
|
||||
setmetatable(translations, nil)
|
||||
end
|
||||
local baseTranslations = instance[BASE_TRANSLATIONS]
|
||||
if getmetatable(baseTranslations) then
|
||||
setmetatable(baseTranslations, nil)
|
||||
end
|
||||
if translations == baseTranslations or instance[STRICTNESS] then
|
||||
setmetatable(instance, {
|
||||
__index = __index,
|
||||
__newindex = __newindex,
|
||||
__tostring = __tostring
|
||||
})
|
||||
|
||||
setmetatable(translations, {
|
||||
__index = AceLocale.prototype
|
||||
})
|
||||
else
|
||||
setmetatable(instance, {
|
||||
__index = __index,
|
||||
__newindex = __newindex,
|
||||
__tostring = __tostring
|
||||
})
|
||||
|
||||
setmetatable(translations, {
|
||||
__index = baseTranslations,
|
||||
})
|
||||
|
||||
setmetatable(baseTranslations, {
|
||||
__index = AceLocale.prototype,
|
||||
})
|
||||
end
|
||||
else
|
||||
setmetatable(instance, {
|
||||
__index = __index,
|
||||
__newindex = __newindex,
|
||||
__tostring = __tostring,
|
||||
})
|
||||
end
|
||||
clearCache(instance)
|
||||
newRegistries[instance] = true
|
||||
scheduleClear()
|
||||
return instance
|
||||
end
|
||||
|
||||
function AceLocale:new(name)
|
||||
self:argCheck(name, 2, "string")
|
||||
|
||||
if self.registry[name] and type(rawget(self.registry[name], 'GetLibraryVersion')) ~= "function" then
|
||||
return self.registry[name]
|
||||
end
|
||||
|
||||
AceLocale.registry[name] = refixInstance({
|
||||
[STRICTNESS] = false,
|
||||
[NAME] = name,
|
||||
})
|
||||
newRegistries[AceLocale.registry[name]] = true
|
||||
return AceLocale.registry[name]
|
||||
end
|
||||
|
||||
AceLocale.prototype = { class = AceLocale }
|
||||
|
||||
function AceLocale.prototype:EnableDebugging()
|
||||
if rawget(self, BASE_TRANSLATIONS) then
|
||||
AceLocale.error(self, "Cannot enable debugging after a translation has been registered.")
|
||||
end
|
||||
rawset(self, DEBUGGING, true)
|
||||
end
|
||||
|
||||
function AceLocale.prototype:EnableDynamicLocales(override)
|
||||
AceLocale.argCheck(self, override, 2, "boolean", "nil")
|
||||
if not override and rawget(self, BASE_TRANSLATIONS) then
|
||||
AceLocale.error(self, "Cannot enable dynamic locales after a translation has been registered.")
|
||||
end
|
||||
if not rawget(self, DYNAMIC_LOCALES) then
|
||||
rawset(self, DYNAMIC_LOCALES, true)
|
||||
if rawget(self, BASE_LOCALE) then
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
rawset(self, TRANSLATION_TABLES, {})
|
||||
end
|
||||
self[TRANSLATION_TABLES][self[BASE_LOCALE]] = self[BASE_TRANSLATIONS]
|
||||
self[TRANSLATION_TABLES][self[CURRENT_LOCALE]] = self[TRANSLATIONS]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceLocale.prototype:RegisterTranslations(locale, func)
|
||||
AceLocale.argCheck(self, locale, 2, "string")
|
||||
AceLocale.argCheck(self, func, 3, "function")
|
||||
|
||||
if locale == rawget(self, BASE_LOCALE) then
|
||||
AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale)
|
||||
end
|
||||
|
||||
if rawget(self, BASE_TRANSLATIONS) and GetLocale() ~= locale then
|
||||
if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
rawset(self, TRANSLATION_TABLES, {})
|
||||
end
|
||||
if self[TRANSLATION_TABLES][locale] then
|
||||
AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale)
|
||||
end
|
||||
local t = func()
|
||||
func = nil
|
||||
if type(t) ~= "table" then
|
||||
AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t))
|
||||
end
|
||||
self[TRANSLATION_TABLES][locale] = t
|
||||
t = nil
|
||||
end
|
||||
func = nil
|
||||
return
|
||||
end
|
||||
local t = func()
|
||||
func = nil
|
||||
if type(t) ~= "table" then
|
||||
AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t))
|
||||
end
|
||||
|
||||
rawset(self, TRANSLATIONS, t)
|
||||
if not rawget(self, BASE_TRANSLATIONS) then
|
||||
rawset(self, BASE_TRANSLATIONS, t)
|
||||
rawset(self, BASE_LOCALE, locale)
|
||||
for key,value in pairs(t) do
|
||||
if value == true then
|
||||
t[key] = key
|
||||
end
|
||||
end
|
||||
else
|
||||
for key, value in pairs(self[TRANSLATIONS]) do
|
||||
if not rawget(self[BASE_TRANSLATIONS], key) then
|
||||
AceLocale.error(self, "Improper translation exists. %q is likely misspelled for locale %s.", key, locale)
|
||||
end
|
||||
if value == true then
|
||||
AceLocale.error(self, "Can only accept true as a value on the base locale. %q is the base locale, %q is not.", rawget(self, BASE_LOCALE), locale)
|
||||
end
|
||||
end
|
||||
end
|
||||
rawset(self, CURRENT_LOCALE, locale)
|
||||
refixInstance(self)
|
||||
if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
rawset(self, TRANSLATION_TABLES, {})
|
||||
end
|
||||
self[TRANSLATION_TABLES][locale] = t
|
||||
end
|
||||
t = nil
|
||||
end
|
||||
|
||||
function AceLocale.prototype:SetLocale(locale)
|
||||
AceLocale.argCheck(self, locale, 2, "string", "boolean")
|
||||
if not rawget(self, DYNAMIC_LOCALES) then
|
||||
AceLocale.error(self, "Cannot call `SetLocale' without first calling `EnableDynamicLocales'.")
|
||||
end
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
AceLocale.error(self, "Cannot call `SetLocale' without first calling `RegisterTranslations'.")
|
||||
end
|
||||
if locale == true then
|
||||
locale = GetLocale()
|
||||
if not self[TRANSLATION_TABLES][locale] then
|
||||
locale = self[BASE_LOCALE]
|
||||
end
|
||||
end
|
||||
|
||||
if self[CURRENT_LOCALE] == locale then
|
||||
return
|
||||
end
|
||||
|
||||
if not self[TRANSLATION_TABLES][locale] then
|
||||
AceLocale.error(self, "Locale %q not registered.", locale)
|
||||
end
|
||||
|
||||
self[TRANSLATIONS] = self[TRANSLATION_TABLES][locale]
|
||||
self[CURRENT_LOCALE] = locale
|
||||
refixInstance(self)
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetLocale()
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
AceLocale.error(self, "Cannot call `GetLocale' without first calling `RegisterTranslations'.")
|
||||
end
|
||||
return self[CURRENT_LOCALE]
|
||||
end
|
||||
|
||||
local function iter(t, position)
|
||||
return (next(t, position))
|
||||
end
|
||||
|
||||
function AceLocale.prototype:IterateAvailableLocales()
|
||||
if not rawget(self, DYNAMIC_LOCALES) then
|
||||
AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `EnableDynamicLocales'.")
|
||||
end
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `RegisterTranslations'.")
|
||||
end
|
||||
return iter, self[TRANSLATION_TABLES], nil
|
||||
end
|
||||
|
||||
function AceLocale.prototype:HasLocale(locale)
|
||||
if not rawget(self, DYNAMIC_LOCALES) then
|
||||
AceLocale.error(self, "Cannot call `HasLocale' without first calling `EnableDynamicLocales'.")
|
||||
end
|
||||
AceLocale.argCheck(self, locale, 2, "string")
|
||||
return rawget(self, TRANSLATION_TABLES) and self[TRANSLATION_TABLES][locale] ~= nil
|
||||
end
|
||||
|
||||
function AceLocale.prototype:SetStrictness(strict)
|
||||
AceLocale.argCheck(self, strict, 2, "boolean")
|
||||
local mt = getmetatable(self)
|
||||
if not mt then
|
||||
AceLocale.error(self, "Cannot call `SetStrictness' without a metatable.")
|
||||
end
|
||||
if not rawget(self, TRANSLATIONS) then
|
||||
AceLocale.error(self, "No translations registered.")
|
||||
end
|
||||
rawset(self, STRICTNESS, strict)
|
||||
refixInstance(self)
|
||||
end
|
||||
|
||||
local function initReverse(self)
|
||||
rawset(self, REVERSE_TRANSLATIONS, {})
|
||||
local alpha = self[TRANSLATIONS]
|
||||
local bravo = self[REVERSE_TRANSLATIONS]
|
||||
for base, localized in pairs(alpha) do
|
||||
bravo[localized] = base
|
||||
end
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetTranslation(text)
|
||||
AceLocale.argCheck(self, text, 1, "string", "number")
|
||||
if not rawget(self, TRANSLATIONS) then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
return self[text]
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetStrictTranslation(text)
|
||||
AceLocale.argCheck(self, text, 1, "string", "number")
|
||||
local x = rawget(self, TRANSLATIONS)
|
||||
if not x then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
local value = rawget(x, text)
|
||||
if value == nil then
|
||||
AceLocale.error(self, "Translation %q does not exist for locale %s", text, self[CURRENT_LOCALE])
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetReverseTranslation(text)
|
||||
local x = rawget(self, REVERSE_TRANSLATIONS)
|
||||
if not x then
|
||||
if not rawget(self, TRANSLATIONS) then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
initReverse(self)
|
||||
x = self[REVERSE_TRANSLATIONS]
|
||||
end
|
||||
local translation = x[text]
|
||||
if not translation then
|
||||
AceLocale.error(self, "Reverse translation for %q does not exist", text)
|
||||
end
|
||||
return translation
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetIterator()
|
||||
local x = rawget(self, TRANSLATIONS)
|
||||
if not x then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
return next, x, nil
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetReverseIterator()
|
||||
local x = rawget(self, REVERSE_TRANSLATIONS)
|
||||
if not x then
|
||||
if not rawget(self, TRANSLATIONS) then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
initReverse(self)
|
||||
x = self[REVERSE_TRANSLATIONS]
|
||||
end
|
||||
return next, x, nil
|
||||
end
|
||||
|
||||
function AceLocale.prototype:HasTranslation(text)
|
||||
AceLocale.argCheck(self, text, 1, "string", "number")
|
||||
local x = rawget(self, TRANSLATIONS)
|
||||
if not x then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
return rawget(x, text) and true
|
||||
end
|
||||
|
||||
function AceLocale.prototype:HasReverseTranslation(text)
|
||||
local x = rawget(self, REVERSE_TRANSLATIONS)
|
||||
if not x then
|
||||
if not rawget(self, TRANSLATIONS) then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
initReverse(self)
|
||||
x = self[REVERSE_TRANSLATIONS]
|
||||
end
|
||||
return x[text] and true
|
||||
end
|
||||
|
||||
function AceLocale.prototype:Debug()
|
||||
if not rawget(self, DEBUGGING) then
|
||||
return
|
||||
end
|
||||
local words = {}
|
||||
local locales = {"enUS", "ruRU", "deDE", "frFR", "koKR", "zhCN", "zhTW", "esES"}
|
||||
local localizations = {}
|
||||
DEFAULT_CHAT_FRAME:AddMessage("--- AceLocale Debug ---")
|
||||
for _,locale in ipairs(locales) do
|
||||
if not self[TRANSLATION_TABLES][locale] then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q not found", locale))
|
||||
else
|
||||
localizations[locale] = self[TRANSLATION_TABLES][locale]
|
||||
end
|
||||
end
|
||||
local localeDebug = {}
|
||||
for locale, localization in pairs(localizations) do
|
||||
localeDebug[locale] = {}
|
||||
for word in pairs(localization) do
|
||||
if type(localization[word]) == "table" then
|
||||
if type(words[word]) ~= "table" then
|
||||
words[word] = {}
|
||||
end
|
||||
for bit in pairs(localization[word]) do
|
||||
if type(localization[word][bit]) == "string" then
|
||||
words[word][bit] = true
|
||||
end
|
||||
end
|
||||
elseif type(localization[word]) == "string" then
|
||||
words[word] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
for word in pairs(words) do
|
||||
if type(words[word]) == "table" then
|
||||
for bit in pairs(words[word]) do
|
||||
for locale, localization in pairs(localizations) do
|
||||
if not rawget(localization, word) or not localization[word][bit] then
|
||||
localeDebug[locale][word .. "::" .. bit] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
for locale, localization in pairs(localizations) do
|
||||
if not rawget(localization, word) then
|
||||
localeDebug[locale][word] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for locale, t in pairs(localeDebug) do
|
||||
if not next(t) then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q complete", locale))
|
||||
else
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q missing:", locale))
|
||||
for word in pairs(t) do
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format(" %q", word))
|
||||
end
|
||||
end
|
||||
end
|
||||
DEFAULT_CHAT_FRAME:AddMessage("--- End AceLocale Debug ---")
|
||||
end
|
||||
|
||||
setmetatable(AceLocale.prototype, {
|
||||
__index = function(self, k)
|
||||
if type(k) ~= "table" and k ~= 0 and k ~= "GetLibraryVersion" and k ~= "error" and k ~= "assert" and k ~= "argCheck" and k ~= "pcall" then -- HACK: remove "GetLibraryVersion" and such later.
|
||||
AceLocale.error(lastSelf or self, "Translation %q does not exist.", k)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
})
|
||||
|
||||
local function activate(self, oldLib, oldDeactivate)
|
||||
AceLocale = self
|
||||
|
||||
self.frame = oldLib and oldLib.frame or CreateFrame("Frame")
|
||||
self.registry = oldLib and oldLib.registry or {}
|
||||
self.BASE_TRANSLATIONS = oldLib and oldLib.BASE_TRANSLATIONS or {}
|
||||
self.DEBUGGING = oldLib and oldLib.DEBUGGING or {}
|
||||
self.TRANSLATIONS = oldLib and oldLib.TRANSLATIONS or {}
|
||||
self.BASE_LOCALE = oldLib and oldLib.BASE_LOCALE or {}
|
||||
self.TRANSLATION_TABLES = oldLib and oldLib.TRANSLATION_TABLES or {}
|
||||
self.REVERSE_TRANSLATIONS = oldLib and oldLib.REVERSE_TRANSLATIONS or {}
|
||||
self.STRICTNESS = oldLib and oldLib.STRICTNESS or {}
|
||||
self.NAME = oldLib and oldLib.NAME or {}
|
||||
self.DYNAMIC_LOCALES = oldLib and oldLib.DYNAMIC_LOCALES or {}
|
||||
self.CURRENT_LOCALE = oldLib and oldLib.CURRENT_LOCALE or {}
|
||||
|
||||
BASE_TRANSLATIONS = self.BASE_TRANSLATIONS
|
||||
DEBUGGING = self.DEBUGGING
|
||||
TRANSLATIONS = self.TRANSLATIONS
|
||||
BASE_LOCALE = self.BASE_LOCALE
|
||||
TRANSLATION_TABLES = self.TRANSLATION_TABLES
|
||||
REVERSE_TRANSLATIONS = self.REVERSE_TRANSLATIONS
|
||||
STRICTNESS = self.STRICTNESS
|
||||
NAME = self.NAME
|
||||
DYNAMIC_LOCALES = self.DYNAMIC_LOCALES
|
||||
CURRENT_LOCALE = self.CURRENT_LOCALE
|
||||
|
||||
|
||||
local GetTime = GetTime
|
||||
local timeUntilClear = GetTime() + 5
|
||||
scheduleClear = function()
|
||||
if next(newRegistries) then
|
||||
self.frame:Show()
|
||||
timeUntilClear = GetTime() + 5
|
||||
end
|
||||
end
|
||||
|
||||
if not self.registry then
|
||||
self.registry = {}
|
||||
else
|
||||
for name, instance in pairs(self.registry) do
|
||||
local name = name
|
||||
local mt = getmetatable(instance)
|
||||
setmetatable(instance, nil)
|
||||
instance[NAME] = name
|
||||
local strict
|
||||
if instance[STRICTNESS] ~= nil then
|
||||
strict = instance[STRICTNESS]
|
||||
elseif instance[TRANSLATIONS] ~= instance[BASE_TRANSLATIONS] then
|
||||
if getmetatable(instance[TRANSLATIONS]).__index == oldLib.prototype then
|
||||
strict = true
|
||||
end
|
||||
end
|
||||
instance[STRICTNESS] = strict and true or false
|
||||
refixInstance(instance)
|
||||
end
|
||||
end
|
||||
|
||||
self.frame:SetScript("OnEvent", scheduleClear)
|
||||
self.frame:SetScript("OnUpdate", function() -- (this, elapsed)
|
||||
if timeUntilClear - GetTime() <= 0 then
|
||||
self.frame:Hide()
|
||||
for k in pairs(newRegistries) do
|
||||
clearCache(k)
|
||||
newRegistries[k] = nil
|
||||
k = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
self.frame:UnregisterAllEvents()
|
||||
self.frame:RegisterEvent("ADDON_LOADED")
|
||||
self.frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
self.frame:Show()
|
||||
|
||||
if oldDeactivate then
|
||||
oldDeactivate(oldLib)
|
||||
end
|
||||
end
|
||||
|
||||
AceLibrary:Register(AceLocale, MAJOR_VERSION, MINOR_VERSION, activate)
|
||||
@@ -0,0 +1,462 @@
|
||||
--[[
|
||||
Name: AceModuleCore-2.0
|
||||
Revision: $Rev: 17998 $
|
||||
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/AceModuleCore-2.0
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceModuleCore-2.0
|
||||
Description: Mixin to provide a module system so that modules or plugins can
|
||||
use an addon as its core.
|
||||
Dependencies: AceLibrary, AceOO-2.0, AceAddon-2.0, AceEvent-2.0 (optional), Compost-2.0 (optional)
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceModuleCore-2.0"
|
||||
local MINOR_VERSION = "$Revision: 17998 $"
|
||||
|
||||
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
|
||||
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
|
||||
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
|
||||
|
||||
local function safecall(func,a,b,c,d,e,f,g)
|
||||
local success, err = pcall(func,a,b,c,d,e,f,g)
|
||||
if not success then geterrorhandler()(err) end
|
||||
end
|
||||
|
||||
local table_setn
|
||||
do
|
||||
local version = GetBuildInfo()
|
||||
if string.find(version, "^2%.") then
|
||||
-- 2.0.0
|
||||
table_setn = function() end
|
||||
else
|
||||
table_setn = table.setn
|
||||
end
|
||||
end
|
||||
|
||||
local new, del
|
||||
do
|
||||
local list = setmetatable({}, {__mode = 'k'})
|
||||
function new()
|
||||
local t = next(list)
|
||||
if t then
|
||||
list[t] = nil
|
||||
return t
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
function del(t)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
table_setn(t, 0)
|
||||
list[t] = true
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
|
||||
local AceModuleCore = AceOO.Mixin {
|
||||
"NewModule",
|
||||
"HasModule",
|
||||
"GetModule",
|
||||
"IsModule",
|
||||
"IterateModules",
|
||||
"SetModuleMixins",
|
||||
"SetModuleClass",
|
||||
"IsModuleActive",
|
||||
"ToggleModuleActive"
|
||||
}
|
||||
local AceEvent
|
||||
|
||||
local Compost = AceLibrary:HasInstance("Compost-2.0") and AceLibrary("Compost-2.0")
|
||||
|
||||
local function getlibrary(lib)
|
||||
if type(lib) == "string" then
|
||||
return AceLibrary(lib)
|
||||
else
|
||||
return lib
|
||||
end
|
||||
end
|
||||
|
||||
local tmp
|
||||
function AceModuleCore:NewModule(name, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not self.modules then
|
||||
AceModuleCore:error("CreatePrototype() must be called before attempting to create a new module.", 2)
|
||||
end
|
||||
AceModuleCore:argCheck(name, 2, "string")
|
||||
if string.len(name) == 0 then
|
||||
AceModuleCore:error("Bad argument #2 to `NewModule`, string must not be empty")
|
||||
end
|
||||
if self.modules[name] then
|
||||
AceModuleCore:error("The module %q has already been registered", name)
|
||||
end
|
||||
|
||||
if not tmp then
|
||||
tmp = {}
|
||||
end
|
||||
if a1 then table.insert(tmp, a1)
|
||||
if a2 then table.insert(tmp, a2)
|
||||
if a3 then table.insert(tmp, a3)
|
||||
if a4 then table.insert(tmp, a4)
|
||||
if a5 then table.insert(tmp, a5)
|
||||
if a6 then table.insert(tmp, a6)
|
||||
if a7 then table.insert(tmp, a7)
|
||||
if a8 then table.insert(tmp, a8)
|
||||
if a9 then table.insert(tmp, a9)
|
||||
if a10 then table.insert(tmp, a10)
|
||||
if a11 then table.insert(tmp, a11)
|
||||
if a12 then table.insert(tmp, a12)
|
||||
if a13 then table.insert(tmp, a13)
|
||||
if a14 then table.insert(tmp, a14)
|
||||
if a15 then table.insert(tmp, a15)
|
||||
if a16 then table.insert(tmp, a16)
|
||||
if a17 then table.insert(tmp, a17)
|
||||
if a18 then table.insert(tmp, a18)
|
||||
if a19 then table.insert(tmp, a19)
|
||||
if a20 then table.insert(tmp, a20)
|
||||
end end end end end end end end end end end end end end end end end end end end
|
||||
for k,v in ipairs(tmp) do
|
||||
tmp[k] = getlibrary(v)
|
||||
end
|
||||
|
||||
if self.moduleMixins then
|
||||
for _,mixin in ipairs(self.moduleMixins) do
|
||||
local exists = false
|
||||
for _,v in ipairs(tmp) do
|
||||
if mixin == v then
|
||||
exists = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not exists then
|
||||
table.insert(tmp, mixin)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local module = AceOO.Classpool(self.moduleClass, unpack(tmp)):new(name)
|
||||
self.modules[name] = module
|
||||
module.name = name
|
||||
module.title = name
|
||||
|
||||
AceModuleCore.totalModules[module] = self
|
||||
|
||||
if AceEvent then
|
||||
AceEvent:TriggerEvent("Ace2_ModuleCreated", module)
|
||||
end
|
||||
|
||||
for k in pairs(tmp) do
|
||||
tmp[k] = nil
|
||||
end
|
||||
table_setn(tmp, 0)
|
||||
return module
|
||||
end
|
||||
|
||||
function AceModuleCore:HasModule(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if a1 then if not self.modules[a1] then return false end
|
||||
if a2 then if not self.modules[a2] then return false end
|
||||
if a3 then if not self.modules[a3] then return false end
|
||||
if a4 then if not self.modules[a4] then return false end
|
||||
if a5 then if not self.modules[a5] then return false end
|
||||
if a6 then if not self.modules[a6] then return false end
|
||||
if a7 then if not self.modules[a7] then return false end
|
||||
if a8 then if not self.modules[a8] then return false end
|
||||
if a9 then if not self.modules[a9] then return false end
|
||||
if a10 then if not self.modules[a10] then return false end
|
||||
if a11 then if not self.modules[a11] then return false end
|
||||
if a12 then if not self.modules[a12] then return false end
|
||||
if a13 then if not self.modules[a13] then return false end
|
||||
if a14 then if not self.modules[a14] then return false end
|
||||
if a15 then if not self.modules[a15] then return false end
|
||||
if a16 then if not self.modules[a16] then return false end
|
||||
if a17 then if not self.modules[a17] then return false end
|
||||
if a18 then if not self.modules[a18] then return false end
|
||||
if a19 then if not self.modules[a19] then return false end
|
||||
if a20 then if not self.modules[a20] then return false end
|
||||
end end end end end end end end end end end end end end end end end end end end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function AceModuleCore:GetModule(name)
|
||||
if not self.modules then
|
||||
AceModuleCore:error("Error initializing class. Please report error.")
|
||||
end
|
||||
if not self.modules[name] then
|
||||
AceModuleCore:error("Cannot find module %q.", name)
|
||||
end
|
||||
return self.modules[name]
|
||||
end
|
||||
|
||||
function AceModuleCore:IsModule(module)
|
||||
if self == AceModuleCore then
|
||||
return AceModuleCore.totalModules[module]
|
||||
else
|
||||
for k,v in pairs(self.modules) do
|
||||
if v == module then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function AceModuleCore:IterateModules()
|
||||
local t = new()
|
||||
for k in pairs(self.modules) do
|
||||
table.insert(t, k)
|
||||
end
|
||||
table.sort(t)
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
local x = t[i]
|
||||
if x then
|
||||
return x, self.modules[x]
|
||||
else
|
||||
t = del(t)
|
||||
return nil
|
||||
end
|
||||
end, nil, nil
|
||||
end
|
||||
|
||||
function AceModuleCore:SetModuleMixins(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if self.moduleMixins then
|
||||
AceModuleCore:error('Cannot call "SetModuleMixins" twice')
|
||||
elseif not self.modules then
|
||||
AceModuleCore:error("Error initializing class. Please report error.")
|
||||
elseif next(self.modules) then
|
||||
AceModuleCore:error('Cannot call "SetModuleMixins" after "NewModule" has been called.')
|
||||
end
|
||||
|
||||
self.moduleMixins = Compost and Compost:Acquire(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) or {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20}
|
||||
for k,v in ipairs(self.moduleMixins) do
|
||||
self.moduleMixins[k] = getlibrary(v)
|
||||
end
|
||||
end
|
||||
|
||||
function AceModuleCore:SetModuleClass(class)
|
||||
class = getlibrary(class)
|
||||
AceModuleCore:assert(AceOO.inherits(class, AceOO.Class), "Bad argument #2 to `SetModuleClass' (Class expected)")
|
||||
if not self.modules then
|
||||
AceModuleCore:error("Error initializing class. Please report error.")
|
||||
end
|
||||
if self.customModuleClass then
|
||||
AceModuleCore:error("Cannot call `SetModuleClass' twice.")
|
||||
end
|
||||
self.customModuleClass = true
|
||||
self.moduleClass = class
|
||||
self.modulePrototype = class.prototype
|
||||
end
|
||||
|
||||
function AceModuleCore:ToggleModuleActive(module, state)
|
||||
AceModuleCore:argCheck(module, 2, "table", "string")
|
||||
AceModuleCore:argCheck(state, 3, "nil", "boolean")
|
||||
|
||||
if type(module) == "string" then
|
||||
if not self:HasModule(module) then
|
||||
AceModuleCore:error("Cannot find module %q", module)
|
||||
end
|
||||
module = self:GetModule(module)
|
||||
else
|
||||
if not self:IsModule(module) then
|
||||
AceModuleCore:error("%q is not a module", module)
|
||||
end
|
||||
end
|
||||
|
||||
local disable
|
||||
if state == nil then
|
||||
disable = self:IsModuleActive(module)
|
||||
else
|
||||
disable = not state
|
||||
if disable ~= self:IsModuleActive(module) then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if type(module.ToggleActive) == "function" then
|
||||
return module:ToggleActive(not disable)
|
||||
elseif AceOO.inherits(self, "AceDB-2.0") then
|
||||
if not self.db or not self.db.raw then
|
||||
AceModuleCore:error("Cannot toggle a module until `RegisterDB' has been called and `ADDON_LOADED' has been fireed.")
|
||||
end
|
||||
if type(self.db.raw.disabledModules) ~= "table" then
|
||||
self.db.raw.disabledModules = Compost and Compost:Acquire() or {}
|
||||
end
|
||||
local _,profile = self:GetProfile()
|
||||
if type(self.db.raw.disabledModules[profile]) ~= "table" then
|
||||
self.db.raw.disabledModules[profile] = Compost and Compost:Acquire() or {}
|
||||
end
|
||||
if type(self.db.raw.disabledModules[profile][module.name]) ~= "table" then
|
||||
self.db.raw.disabledModules[profile][module.name] = disable or nil
|
||||
end
|
||||
if not disable then
|
||||
if not next(self.db.raw.disabledModules[profile]) then
|
||||
if Compost then
|
||||
Compost:Reclaim(self.db.raw.disabledModules[profile])
|
||||
end
|
||||
self.db.raw.disabledModules[profile] = nil
|
||||
end
|
||||
if not next(self.db.raw.disabledModules) then
|
||||
if Compost then
|
||||
Compost:Reclaim(self.db.raw.disabledModules)
|
||||
end
|
||||
self.db.raw.disabledModules = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
if type(self.disabledModules) ~= "table" then
|
||||
self.disabledModules = Compost and Compost:Acquire() or {}
|
||||
end
|
||||
self.disabledModules[module.name] = disable or nil
|
||||
end
|
||||
if AceOO.inherits(module, "AceAddon-2.0") then
|
||||
local AceAddon = AceLibrary("AceAddon-2.0")
|
||||
if not AceAddon.addonsStarted[module] then
|
||||
return
|
||||
end
|
||||
end
|
||||
if not disable then
|
||||
local current = module.class
|
||||
while true do
|
||||
if current == AceOO.Class then
|
||||
break
|
||||
end
|
||||
if current.mixins then
|
||||
for mixin in pairs(current.mixins) do
|
||||
if type(mixin.OnEmbedEnable) == "function" then
|
||||
safecall(mixin.OnEmbedEnable, mixin, module)
|
||||
end
|
||||
end
|
||||
end
|
||||
current = current.super
|
||||
end
|
||||
if type(module.OnEnable) == "function" then
|
||||
safecall(module.OnEnable, module)
|
||||
end
|
||||
if AceEvent then
|
||||
AceEvent:TriggerEvent("Ace2_AddonEnabled", module)
|
||||
end
|
||||
else
|
||||
local current = module.class
|
||||
while true do
|
||||
if current == AceOO.Class then
|
||||
break
|
||||
end
|
||||
if current.mixins then
|
||||
for mixin in pairs(current.mixins) do
|
||||
if type(mixin.OnEmbedDisable) == "function" then
|
||||
safecall(mixin.OnEmbedDisable, mixin, module)
|
||||
end
|
||||
end
|
||||
end
|
||||
current = current.super
|
||||
end
|
||||
if type(module.OnDisable) == "function" then
|
||||
safecall(module.OnDisable, module)
|
||||
end
|
||||
if AceEvent then
|
||||
AceEvent:TriggerEvent("Ace2_AddonDisabled", module)
|
||||
end
|
||||
end
|
||||
return not disable
|
||||
end
|
||||
|
||||
function AceModuleCore:IsModuleActive(module)
|
||||
AceModuleCore:argCheck(module, 2, "table", "string")
|
||||
|
||||
if AceModuleCore == self then
|
||||
self:argCheck(module, 2, "table")
|
||||
|
||||
local core = AceModuleCore.totalModules[module]
|
||||
if not core then
|
||||
self:error("Bad argument #2 to `IsModuleActive'. Not a module")
|
||||
end
|
||||
return core:IsModuleActive(module)
|
||||
end
|
||||
|
||||
if type(module) == "string" then
|
||||
if not self:HasModule(module) then
|
||||
AceModuleCore:error("Cannot find module %q", module)
|
||||
end
|
||||
module = self:GetModule(module)
|
||||
else
|
||||
if not self:IsModule(module) then
|
||||
AceModuleCore:error("%q is not a module", module)
|
||||
end
|
||||
end
|
||||
|
||||
if type(module.IsActive) == "function" then
|
||||
return module:IsActive()
|
||||
elseif AceOO.inherits(self, "AceDB-2.0") then
|
||||
local _,profile = self:GetProfile()
|
||||
return not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[profile] or not self.db.raw.disabledModules[profile][module.name]
|
||||
else
|
||||
return not self.disabledModules or not self.disabledModules[module.name]
|
||||
end
|
||||
end
|
||||
|
||||
function AceModuleCore:OnInstanceInit(target)
|
||||
if target.modules then
|
||||
AceModuleCore:error("OnInstanceInit cannot be called twice")
|
||||
end
|
||||
target.modules = Compost and Compost:Acquire() or {}
|
||||
|
||||
target.moduleClass = AceOO.Class("AceAddon-2.0")
|
||||
target.modulePrototype = target.moduleClass.prototype
|
||||
end
|
||||
|
||||
AceModuleCore.OnManualEmbed = AceModuleCore.OnInstanceInit
|
||||
|
||||
function AceModuleCore.OnEmbedProfileDisable(AceModuleCore, self, newProfile)
|
||||
if not AceOO.inherits(self, "AceDB-2.0") then
|
||||
return
|
||||
end
|
||||
local _,currentProfile = self:GetProfile()
|
||||
for k, module in pairs(self.modules) do
|
||||
if type(module.IsActive) == "function" or type(module.ToggleActive) == "function" then
|
||||
-- continue
|
||||
else
|
||||
local currentActive = not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[currentProfile] or not self.db.raw.disabledModules[currentProfile][module.name]
|
||||
local newActive = not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[newProfile] or not self.db.raw.disabledModules[newProfile][module.name]
|
||||
if currentActive ~= newActive then
|
||||
self:ToggleModuleActive(module)
|
||||
if not self.db.raw.disabledModules then
|
||||
self.db.raw.disabledModules = {}
|
||||
end
|
||||
if not self.db.raw.disabledModules[currentProfile] then
|
||||
self.db.raw.disabledModules[currentProfile] = {}
|
||||
end
|
||||
self.db.raw.disabledModules[currentProfile][module.name] = not currentActive or nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function activate(self, oldLib, oldDeactivate)
|
||||
AceModuleCore = self
|
||||
|
||||
if oldLib then
|
||||
self.totalModules = oldLib.totalModules
|
||||
end
|
||||
if not self.totalModules then
|
||||
self.totalModules = {}
|
||||
end
|
||||
|
||||
self.super.activate(self, oldLib, oldDeactivate)
|
||||
end
|
||||
|
||||
local function external(self, major, instance)
|
||||
if major == "Compost-2.0" then
|
||||
Compost = instance
|
||||
elseif major == "AceEvent-2.0" then
|
||||
AceEvent = instance
|
||||
end
|
||||
end
|
||||
|
||||
AceLibrary:Register(AceModuleCore, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
|
||||
AceModuleCore = AceLibrary(MAJOR_VERSION)
|
||||
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
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,71 @@
|
||||
<Bindings>
|
||||
|
||||
<Binding name="UNITXPSP3NEARESTENEMY" header="UNITXPSP3TARGETING">
|
||||
UnitXP("target", "nearestEnemy");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3TARGETMOSTHP" header="UNITXPSP3TARGETING">
|
||||
UnitXP("target", "mostHP");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3WORLDBOSS">
|
||||
UnitXP("target", "worldBoss");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3NEXTMARKER">
|
||||
UnitXP("target", "nextMarkedEnemyInCycle");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3PREVIOUSMARKER">
|
||||
UnitXP("target", "previousMarkedEnemyInCycle");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3NEXTENEMY">
|
||||
UnitXP("target", "nextEnemyInCycle");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3PREVIOUSENEMY">
|
||||
UnitXP("target", "previousEnemyInCycle");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3NEXTMELEE">
|
||||
UnitXP("target", "nextEnemyConsideringDistance");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3PREVIOUSMELEE">
|
||||
UnitXP("target", "previousEnemyConsideringDistance");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3RAISECAMERA" header="UNITXPSP3UTILITIES">
|
||||
UnitXP_SP3_raiseCameraHeight();
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3LOWERCAMERA">
|
||||
UnitXP_SP3_lowerCameraHeight();
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3LEFTCAMERA">
|
||||
UnitXP_SP3_rightPlayer();
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3RIGHTCAMERA">
|
||||
UnitXP_SP3_leftPlayer();
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3RESETCAMERA">
|
||||
UnitXP_SP3_resetCamera();
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3TOGGLEMODERNNAMEPLATEDISTANCE">
|
||||
UnitXP_SP3_toggleModernNameplateDistance();
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3TOGGLEPRIORITIZETARGETNAMEPLATE">
|
||||
UnitXP_SP3_togglePrioritizeTargetNameplate();
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3TOGGLEPRIORITIZEMARKEDNAMEPLATE">
|
||||
UnitXP_SP3_togglePrioritizeMarkedNameplate();
|
||||
</Binding>
|
||||
|
||||
</Bindings>
|
||||
@@ -0,0 +1,2 @@
|
||||
# UnitXP_SP3_Addon
|
||||
Vanilla 1.12 Lua Addon for supporting [UnitXP SP3 mod](https://codeberg.org/konaka/UnitXP_SP3)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
||||
## Interface: 11200
|
||||
## Title: UnitXP SP3 Lua Addon
|
||||
## Notes: Menu to control UnitXP SP3 mod
|
||||
## Author: allfox and thankful community
|
||||
## DefaultState: enabled
|
||||
## OptionalDeps: none
|
||||
## SavedVariables: UnitXP_SP3_Addon, UnitXP_SP3_Icon
|
||||
libs\LibStub.lua
|
||||
libs\AceCore-3.0.lua
|
||||
libs\AceHook-3.0.lua
|
||||
libs\CallbackHandler-1.0.lua
|
||||
libs\LibDataBroker-1.1.lua
|
||||
libs\LibDBIcon-1.0.lua
|
||||
UnitXP_SP3_Addon.xml
|
||||
@@ -0,0 +1,856 @@
|
||||
<Ui
|
||||
xmlns="http://www.blizzard.com/wow/ui/"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<!-- $Id$ -->
|
||||
<!-- Script -->
|
||||
<Script file="UnitXP_SP3_Addon.lua"/>
|
||||
<!-- Frames -->
|
||||
<Frame name="xpsp3Frame" toplevel="true" movable="true" enableMouse="true" enableKeyboard="true" hidden="true">
|
||||
<TitleRegion setAllPoints="true"/>
|
||||
<Size>
|
||||
<AbsDimension x="550" y="430"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER" relativeTo="UIParent" />
|
||||
</Anchors>
|
||||
<Backdrop bgFile="Interface\DialogFrame\UI-DialogBox-Background" edgeFile="Interface\DialogFrame\UI-DialogBox-Border" tile="true">
|
||||
<BackgroundInsets>
|
||||
<AbsInset left="11" right="12" top="12" bottom="11"/>
|
||||
</BackgroundInsets>
|
||||
<TileSize>
|
||||
<AbsValue val="32"/>
|
||||
</TileSize>
|
||||
<EdgeSize>
|
||||
<AbsValue val="32"/>
|
||||
</EdgeSize>
|
||||
</Backdrop>
|
||||
<Layers>
|
||||
<Layer level="ARTWORK">
|
||||
<Texture name="xpsp3_title_texture" file="Interface\DialogFrame\UI-DialogBox-Header">
|
||||
<Size>
|
||||
<AbsDimension x="320" y="64"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOP">
|
||||
<Offset>
|
||||
<AbsDimension x="0" y="12"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Texture>
|
||||
<FontString inherits="GameFontNormal" text="UnitXP Service Pack 3">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="xpsp3_title_texture">
|
||||
<Offset>
|
||||
<AbsDimension x="0" y="-14"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString inherits="GameFontNormal" text="FPS Cap:" justifyH="LEFT" justifyV="TOP">
|
||||
<Size x="200" y="440" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="xpsp3Frame">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-40"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString inherits="GameFontNormal" text="Background:" justifyH="LEFT" justifyV="TOP">
|
||||
<Size x="200" y="440" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="xpsp3Frame">
|
||||
<Offset>
|
||||
<AbsDimension x="399" y="-40"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString name="xpsp3_fontString_combatTextSP3_fontSize" inherits="GameFontNormal" text="Size:" justifyH="LEFT" justifyV="TOP">
|
||||
<Size x="200" y="440" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="xpsp3Frame">
|
||||
<Offset>
|
||||
<AbsDimension x="295" y="-300"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString name="xpsp3_fontString_combatTextSP3_nameplateHeight" inherits="GameFontNormal" text="Elevation:" justifyH="LEFT" justifyV="TOP">
|
||||
<Size x="200" y="440" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="xpsp3Frame">
|
||||
<Offset>
|
||||
<AbsDimension x="413" y="-300"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString name="xpsp3_fontString_combatTextSP3_fontName" inherits="GameFontNormal" text="Font:" justifyH="LEFT" justifyV="TOP">
|
||||
<Size x="200" y="440" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="xpsp3Frame">
|
||||
<Offset>
|
||||
<AbsDimension x="295" y="-335"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString inherits="GameFontNormal" text="Targeting functions could be accessed via Key Bindings Menu" justifyH="CENTER" justifyV="TOP">
|
||||
<Size x="550" y="430" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="xpsp3Frame">
|
||||
<Offset>
|
||||
<AbsDimension x="0" y="-370"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<Button name="xpsp3_buttonCancel_close" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="Close">
|
||||
<Size>
|
||||
<AbsDimension x="100" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMRIGHT">
|
||||
<Offset>
|
||||
<AbsDimension x="-15" y="15"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="xpsp3_buttonCancel_resetCamera" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="Reset Camera">
|
||||
<Size>
|
||||
<AbsDimension x="100" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMRIGHT">
|
||||
<Offset>
|
||||
<AbsDimension x="-115" y="15"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<CheckButton name="xpsp3_checkButton_minimapButton" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-30"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Show Minimap Button");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Toggle visibility of the minimap button");
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_modernNameplate" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-70"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Proper Nameplates Occlusion");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Nameplates would also check camera's line of sight in addition to distance");
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_prioritizeTargetNameplate" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-100"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Prioritize Target Nameplate");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Other nameplates would disappear when a target is selected");
|
||||
xpsp3tooltip:AddLine("Need Proper Nameplates Occlusion to be also enabled",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_prioritizeMarkedNameplate" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-130"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Prioritize Marked Nameplate");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Other nameplates would disappear when some nameplates are raid-marked");
|
||||
xpsp3tooltip:AddLine("Need Proper Nameplates Occlusion to be also enabled",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_nameplateCombatFilter" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-160"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Hide Healthy Nameplates");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Aside from prioritized nameplates, only those in-combat/damaged/PvP-flagged nameplates would be shown");
|
||||
xpsp3tooltip:AddLine("Need Proper Nameplates Occlusion to be also enabled",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_hideCritterNameplate" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-190"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Hide Critter Nameplates");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Aside from prioritized nameplates, only those in-combat critters would have nameplates");
|
||||
xpsp3tooltip:AddLine("Need Proper Nameplates Occlusion to be also enabled",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_showInCombatNameplatesNearPlayer" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-220"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Show In-combat Nameplates Nearby");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("In-combat nameplates in small range would be shown regardless of occlusion");
|
||||
xpsp3tooltip:AddLine("Need Proper Nameplates Occlusion to be also enabled",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_notify_flashTaskbarIcon" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-260"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Taskbar Notification");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Flash operating system's taskbar icon when the game requires attention");
|
||||
xpsp3tooltip:AddLine("When the game is in background/minimized",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_notify_playSystemDefaultSound" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-290"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("System Sound Notification");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Play operating system's default sound when the game requires attention");
|
||||
xpsp3tooltip:AddLine("When the game is in background/minimized",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_perfectScreenshot" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-320"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Perfect Screenshot");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Generate PNG screenshot");
|
||||
xpsp3tooltip:AddLine("In-game screenshots would be in larger PNG files instead of JPEG files",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_cameraPinHeight" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-160"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Pin Camera Height");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Camera would keep its height during shapeshifting");
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_weatherAlwaysClear" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-190"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Disable Rain");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("This would fix FPS-drop at the cost of losing raining particles");
|
||||
xpsp3tooltip:AddLine("Toggling the switch would influence the next weather event but not the current one",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_combatTextSP3" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-260"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Anti-aliased Combat Text");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Alternative style for floating combat text");
|
||||
xpsp3tooltip:AddLine("It requires d3dx9_43.dll which is from DirectX End-User Runtimes",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_hideEXPtext" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-220"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Hide EXP Text");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Don't show that purple message when gaining experience point");
|
||||
xpsp3tooltip:AddLine("Some people don't like it...",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<EditBox name="xpsp3_editBox_combatTextSP3_fontSize" letters="2" autoFocus="false" numeric="true">
|
||||
<Size x="50" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="343" y="-290"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Left">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="LEFT">
|
||||
<Offset x="-17" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0" right="0.12" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Right">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT">
|
||||
<Offset x="-7" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0.88" right="1.0" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnEditFocusGained>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_LEFT");
|
||||
xpsp3tooltip:SetText("Adjust combat text font size");
|
||||
xpsp3tooltip:AddLine("Range from 10 to 99. Press Enter to confirm",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEditFocusGained>
|
||||
<OnEditFocusLost>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnEditFocusLost>
|
||||
<OnEnterPressed>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnEnterPressed>
|
||||
</Scripts>
|
||||
<FontString inherits="ChatFontNormal" />
|
||||
</EditBox>
|
||||
<EditBox name="xpsp3_editBox_combatTextSP3_nameplateHeight" letters="3" autoFocus="false" numeric="true">
|
||||
<Size x="50" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="490" y="-290"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Left">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="LEFT">
|
||||
<Offset x="-17" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0" right="0.12" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Right">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT">
|
||||
<Offset x="-7" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0.88" right="1.0" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnEditFocusGained>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_LEFT");
|
||||
xpsp3tooltip:SetText("Raise or lower combat text on enemies");
|
||||
xpsp3tooltip:AddLine("Range from 0 to 256. Press Enter to confirm",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEditFocusGained>
|
||||
<OnEditFocusLost>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnEditFocusLost>
|
||||
<OnEnterPressed>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnEnterPressed>
|
||||
</Scripts>
|
||||
<FontString inherits="ChatFontNormal" />
|
||||
</EditBox>
|
||||
<EditBox name="xpsp3_editBox_combatTextSP3_fontName" autoFocus="false" numeric="false">
|
||||
<Size x="150" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="336" y="-325"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Left">
|
||||
<Size x="100" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="LEFT">
|
||||
<Offset x="-10" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0" right="0.4" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Right">
|
||||
<Size x="100" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT">
|
||||
<Offset x="10" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0.6" right="1.0" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnEditFocusGained>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_LEFT");
|
||||
xpsp3tooltip:SetText("Change the font of combat text");
|
||||
xpsp3tooltip:AddLine("Input a font name in operating system. Press Enter to confirm",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEditFocusGained>
|
||||
<OnEditFocusLost>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnEditFocusLost>
|
||||
<OnEnterPressed>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnEnterPressed>
|
||||
</Scripts>
|
||||
<FontString inherits="ChatFontNormal" />
|
||||
</EditBox>
|
||||
<EditBox name="xpsp3_editBox_FPScap" letters="3" autoFocus="false" numeric="true">
|
||||
<Size x="50" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="343" y="-30"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Left">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="LEFT">
|
||||
<Offset x="-17" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0" right="0.12" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Right">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT">
|
||||
<Offset x="-7" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0.88" right="1.0" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnEditFocusGained>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_LEFT");
|
||||
xpsp3tooltip:SetText("Limit frames-per-second to the specific value");
|
||||
xpsp3tooltip:AddLine("Zero means no limit. Press Enter to confirm",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEditFocusGained>
|
||||
<OnEditFocusLost>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnEditFocusLost>
|
||||
<OnEnterPressed>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnEnterPressed>
|
||||
</Scripts>
|
||||
<FontString inherits="ChatFontNormal" />
|
||||
</EditBox>
|
||||
<EditBox name="xpsp3_editBox_backgroundFPScap" letters="3" autoFocus="false" numeric="true">
|
||||
<Size x="50" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="490" y="-30"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Left">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="LEFT">
|
||||
<Offset x="-17" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0" right="0.12" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Right">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT">
|
||||
<Offset x="-7" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0.88" right="1.0" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnEditFocusGained>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_LEFT");
|
||||
xpsp3tooltip:SetText("Limit background-frames-per-second to the specific value");
|
||||
xpsp3tooltip:AddLine("Zero means no limit. Press Enter to confirm",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEditFocusGained>
|
||||
<OnEditFocusLost>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnEditFocusLost>
|
||||
<OnEnterPressed>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnEnterPressed>
|
||||
</Scripts>
|
||||
<FontString inherits="ChatFontNormal" />
|
||||
</EditBox>
|
||||
<Button name="xpsp3_button_cameraHeight_raise" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="Raise Camera">
|
||||
<Size>
|
||||
<AbsDimension x="110" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-75"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="xpsp3_button_cameraHeight_lower" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="Lower Camera">
|
||||
<Size>
|
||||
<AbsDimension x="110" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="390" y="-75"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="xpsp3_button_cameraPitch_up" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="CamPitch Up">
|
||||
<Size>
|
||||
<AbsDimension x="110" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-105"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="xpsp3_button_cameraPitch_down" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="CamPitch Down">
|
||||
<Size>
|
||||
<AbsDimension x="110" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="390" y="-105"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="xpsp3_button_cameraHorizontalDisplacement_leftPlayer" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="Left Player">
|
||||
<Size>
|
||||
<AbsDimension x="110" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-135"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="xpsp3_button_cameraHorizontalDisplacement_rightPlayer" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="Right Player">
|
||||
<Size>
|
||||
<AbsDimension x="110" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="390" y="-135"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
UnitXP_SP3_OnLoad();
|
||||
</OnLoad>
|
||||
<OnEvent>
|
||||
UnitXP_SP3_OnEvent(event);
|
||||
</OnEvent>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
<GameTooltip name="xpsp3tooltip" frameStrata="TOOLTIP" hidden="true" parent="xpsp3Frame" inherits="GameTooltipTemplate" />
|
||||
</Ui>
|
||||
@@ -0,0 +1,209 @@
|
||||
local ACECORE_MAJOR, ACECORE_MINOR = "AceCore-3.0", 2
|
||||
local AceCore, oldminor = LibStub:NewLibrary(ACECORE_MAJOR, ACECORE_MINOR)
|
||||
|
||||
if not AceCore then return end -- No upgrade needed
|
||||
|
||||
AceCore._G = AceCore._G or getfenv()
|
||||
local _G = AceCore._G
|
||||
local strsub, strgsub, strfind = string.sub, string.gsub, string.find
|
||||
local tremove, tconcat = table.remove, table.concat
|
||||
local tgetn, tsetn = table.getn, table.setn
|
||||
|
||||
local new, del
|
||||
do
|
||||
local list = setmetatable({}, {__mode = "k"})
|
||||
function new()
|
||||
local t = next(list)
|
||||
if not t then
|
||||
return {}
|
||||
end
|
||||
list[t] = nil
|
||||
return t
|
||||
end
|
||||
|
||||
function del(t)
|
||||
setmetatable(t, nil)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
tsetn(t,0)
|
||||
list[t] = true
|
||||
end
|
||||
|
||||
print = print or function(text)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(text)
|
||||
end
|
||||
|
||||
-- debug
|
||||
function AceCore.listcount()
|
||||
local count = 0
|
||||
for k in list do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
end -- AceCore.new, AceCore.del
|
||||
AceCore.new, AceCore.del = new, del
|
||||
|
||||
local function errorhandler(err)
|
||||
return geterrorhandler()(err)
|
||||
end
|
||||
AceCore.errorhandler = errorhandler
|
||||
|
||||
local function CreateSafeDispatcher(argCount)
|
||||
local code = [[
|
||||
local errorhandler = LibStub("AceCore-3.0").errorhandler
|
||||
local method, UP_ARGS
|
||||
local function call()
|
||||
local func, ARGS = method, UP_ARGS
|
||||
method, UP_ARGS = nil, NILS
|
||||
return func(ARGS)
|
||||
end
|
||||
return function(func, ARGS)
|
||||
method, UP_ARGS = func, ARGS
|
||||
return xpcall(call, errorhandler)
|
||||
end
|
||||
]]
|
||||
local c = 4*argCount-1
|
||||
local s = "b01,b02,b03,b04,b05,b06,b07,b08,b09,b10,b11,b12,b13,b14,b15,b16,b17,b18,b19,b20"
|
||||
code = strgsub(code, "UP_ARGS", string.sub(s,1,c))
|
||||
s = "a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20"
|
||||
code = strgsub(code, "ARGS", string.sub(s,1,c))
|
||||
s = "nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil"
|
||||
code = strgsub(code, "NILS", string.sub(s,1,c))
|
||||
return assert(loadstring(code, "safecall SafeDispatcher["..tostring(argCount).."]"))()
|
||||
end
|
||||
|
||||
local SafeDispatchers = setmetatable({}, {__index=function(self, argCount)
|
||||
local dispatcher
|
||||
if not tonumber(argCount) then dbg(debugstack()) end
|
||||
if argCount > 0 then
|
||||
dispatcher = CreateSafeDispatcher(argCount)
|
||||
else
|
||||
dispatcher = function(func) return xpcall(func,errorhandler) end
|
||||
end
|
||||
rawset(self, argCount, dispatcher)
|
||||
return dispatcher
|
||||
end})
|
||||
|
||||
local function safecall(func,argc,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
-- we check to see if the func is passed is actually a function here and don't error when it isn't
|
||||
-- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not
|
||||
-- present execution should continue without hinderance
|
||||
if type(func) == "function" then
|
||||
return SafeDispatchers[argc](func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
AceCore.safecall = safecall
|
||||
|
||||
local function CreateDispatcher(argCount)
|
||||
local code = [[
|
||||
return function(func,ARGS)
|
||||
return func(ARGS)
|
||||
end
|
||||
]]
|
||||
local s = "a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20"
|
||||
code = strgsub(code, "ARGS", string.sub(s,1,4*argCount-1))
|
||||
return assert(loadstring(code, "call Dispatcher["..tostring(argCount).."]"))()
|
||||
end
|
||||
|
||||
AceCore.Dispatchers = setmetatable({}, {__index=function(self, argCount)
|
||||
local dispatcher
|
||||
if argCount > 0 then
|
||||
dispatcher = CreateDispatcher(argCount)
|
||||
else
|
||||
dispatcher = function(func) return func() end
|
||||
end
|
||||
rawset(self, argCount, dispatcher)
|
||||
return dispatcher
|
||||
end})
|
||||
|
||||
-- some string functions
|
||||
-- vanilla available string operations:
|
||||
-- sub, gfind, rep, gsub, char, dump, find, upper, len, format, byte, lower
|
||||
-- we will just replace every string.match with string.find in the code
|
||||
function AceCore.strtrim(s)
|
||||
return strgsub(s, "^%s*(.-)%s*$", "%1")
|
||||
end
|
||||
|
||||
local function strsplit(delim, s, n)
|
||||
if n and n < 2 then return s end
|
||||
beg = beg or 1
|
||||
local i,j = string.find(s,delim,beg)
|
||||
if not i then
|
||||
return s, nil
|
||||
end
|
||||
return string.sub(s,1,j-1), strsplit(delim, string.sub(s,j+1), n and n-1 or nil)
|
||||
end
|
||||
AceCore.strsplit = strsplit
|
||||
|
||||
-- Ace3v: fonctions copied from AceHook-2.1
|
||||
local protFuncs = {
|
||||
CameraOrSelectOrMoveStart = true, CameraOrSelectOrMoveStop = true,
|
||||
TurnOrActionStart = true, TurnOrActionStop = true,
|
||||
PitchUpStart = true, PitchUpStop = true,
|
||||
PitchDownStart = true, PitchDownStop = true,
|
||||
MoveBackwardStart = true, MoveBackwardStop = true,
|
||||
MoveForwardStart = true, MoveForwardStop = true,
|
||||
Jump = true, StrafeLeftStart = true,
|
||||
StrafeLeftStop = true, StrafeRightStart = true,
|
||||
StrafeRightStop = true, ToggleMouseMove = true,
|
||||
ToggleRun = true, TurnLeftStart = true,
|
||||
TurnLeftStop = true, TurnRightStart = true,
|
||||
TurnRightStop = true,
|
||||
}
|
||||
|
||||
local function issecurevariable(x)
|
||||
return protFuncs[x] and 1 or nil
|
||||
end
|
||||
AceCore.issecurevariable = issecurevariable
|
||||
|
||||
local function hooksecurefunc(arg1, arg2, arg3)
|
||||
if type(arg1) == "string" then
|
||||
arg1, arg2, arg3 = _G, arg1, arg2
|
||||
end
|
||||
local orig = arg1[arg2]
|
||||
if type(orig) ~= "function" then
|
||||
error("The function "..arg2.." does not exist", 2)
|
||||
end
|
||||
arg1[arg2] = function(...)
|
||||
local tmp = {orig(unpack(arg))}
|
||||
arg3(unpack(arg))
|
||||
return unpack(tmp)
|
||||
end
|
||||
end
|
||||
AceCore.hooksecurefunc = hooksecurefunc
|
||||
|
||||
-- pickfirstset() - picks the first non-nil value and returns it
|
||||
local function pickfirstset(argc,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
if (argc <= 1) or (a1 ~= nil) then
|
||||
return a1
|
||||
else
|
||||
return pickfirstset(argc-1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
end
|
||||
end
|
||||
AceCore.pickfirstset = pickfirstset
|
||||
|
||||
local function countargs(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
if (a1 == nil) then return 0 end
|
||||
return 1 + countargs(a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
end
|
||||
AceCore.countargs = countargs
|
||||
|
||||
-- wipe preserves metatable
|
||||
function AceCore.wipe(t)
|
||||
for k,v in pairs(t) do t[k] = nil end
|
||||
tsetn(t,0)
|
||||
return t
|
||||
end
|
||||
|
||||
function AceCore.truncate(t,e)
|
||||
e = e or tgetn(t)
|
||||
for i=1,e do
|
||||
if t[i] == nil then
|
||||
tsetn(t,i-1)
|
||||
return
|
||||
end
|
||||
end
|
||||
tsetn(t,e)
|
||||
end
|
||||
@@ -0,0 +1,540 @@
|
||||
--- **AceHook-3.0** offers safe Hooking/Unhooking of functions, methods and frame scripts.
|
||||
-- Using AceHook-3.0 is recommended when you need to unhook your hooks again, so the hook chain isn't broken
|
||||
-- when you manually restore the original function.
|
||||
--
|
||||
-- **AceHook-3.0** can be embeded into your addon, either explicitly by calling AceHook:Embed(MyAddon) or by
|
||||
-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
|
||||
-- and can be accessed directly, without having to explicitly call AceHook itself.\\
|
||||
-- It is recommended to embed AceHook, otherwise you'll have to specify a custom `self` on all calls you
|
||||
-- make into AceHook.
|
||||
-- @class file
|
||||
-- @name AceHook-3.0
|
||||
-- @release $Id: AceHook-3.0.lua 1118 2014-10-12 08:21:54Z nevcairiel $
|
||||
local ACEHOOK_MAJOR, ACEHOOK_MINOR = "AceHook-3.0", 8
|
||||
local AceHook, oldminor = LibStub:NewLibrary(ACEHOOK_MAJOR, ACEHOOK_MINOR)
|
||||
|
||||
if not AceHook then return end -- No upgrade needed
|
||||
|
||||
local AceCore = LibStub("AceCore-3.0")
|
||||
local new, del = AceCore.new, AceCore.del
|
||||
|
||||
AceHook.embeded = AceHook.embeded or {}
|
||||
AceHook.registry = AceHook.registry or setmetatable({}, {__index = function(tbl, key) tbl[key] = {} return tbl[key] end })
|
||||
AceHook.handlers = AceHook.handlers or {}
|
||||
AceHook.actives = AceHook.actives or {}
|
||||
AceHook.scripts = AceHook.scripts or {}
|
||||
AceHook.onceSecure = AceHook.onceSecure or {}
|
||||
AceHook.hooks = AceHook.hooks or {}
|
||||
|
||||
-- local upvalues
|
||||
local registry = AceHook.registry
|
||||
local handlers = AceHook.handlers
|
||||
local actives = AceHook.actives
|
||||
local scripts = AceHook.scripts
|
||||
local onceSecure = AceHook.onceSecure
|
||||
|
||||
-- Lua APIs
|
||||
local pairs, next, type = pairs, next, type
|
||||
local format = string.format
|
||||
local assert, error = assert, error
|
||||
|
||||
-- WoW APIs
|
||||
local hooksecurefunc, issecurevariable = AceCore.hooksecurefunc, AceCore.issecurevariable
|
||||
|
||||
local _G = AceCore._G
|
||||
|
||||
local protectedScripts = {
|
||||
OnClick = true,
|
||||
}
|
||||
|
||||
-- upgrading of embeded is done at the bottom of the file
|
||||
|
||||
local mixins = {
|
||||
"Hook", "SecureHook",
|
||||
"HookScript", "SecureHookScript",
|
||||
"Unhook", "UnhookAll",
|
||||
"IsHooked",
|
||||
"RawHook", "RawHookScript"
|
||||
}
|
||||
|
||||
-- AceHook:Embed( target )
|
||||
-- target (object) - target object to embed AceHook in
|
||||
--
|
||||
-- Embeds AceEevent into the target object making the functions from the mixins list available on target:..
|
||||
function AceHook:Embed( target )
|
||||
for k, v in pairs( mixins ) do
|
||||
target[v] = self[v]
|
||||
end
|
||||
self.embeded[target] = true
|
||||
-- inject the hooks table safely
|
||||
target.hooks = target.hooks or {}
|
||||
return target
|
||||
end
|
||||
|
||||
-- AceHook:OnEmbedDisable( target )
|
||||
-- target (object) - target object that is being disabled
|
||||
--
|
||||
-- Unhooks all hooks when the target disables.
|
||||
-- this method should be called by the target manually or by an addon framework
|
||||
function AceHook:OnEmbedDisable( target )
|
||||
target:UnhookAll()
|
||||
end
|
||||
|
||||
-- failsafe means even the hooking method fails, the original method will
|
||||
-- always be called
|
||||
local function createHook(self, handler, orig, secure, failsafe)
|
||||
local uid
|
||||
local method = type(handler) == "string"
|
||||
-- Ace3v: when this function called in the "hook" function, we have
|
||||
-- failsafe = not (raw or secure), so the first check condition
|
||||
-- is same as "if failsafe" or "if not raw and not secure"
|
||||
if failsafe and not secure then
|
||||
-- failsafe hook creation
|
||||
uid = function(...)
|
||||
if actives[uid] then
|
||||
if method then
|
||||
self[handler](self,unpack(arg))
|
||||
else
|
||||
handler(unpack(arg))
|
||||
end
|
||||
end
|
||||
return orig(unpack(arg))
|
||||
end
|
||||
-- /failsafe hook
|
||||
else
|
||||
-- all other hooks
|
||||
uid = function(...)
|
||||
if actives[uid] then
|
||||
if method then
|
||||
return self[handler](self,unpack(arg))
|
||||
else
|
||||
return handler(unpack(arg))
|
||||
end
|
||||
elseif not secure then -- backup on non secure
|
||||
return orig(unpack(arg))
|
||||
end
|
||||
end
|
||||
-- /hook
|
||||
end
|
||||
return uid
|
||||
end
|
||||
|
||||
local function donothing() end
|
||||
|
||||
-- @param self
|
||||
-- @param obj nil or a frame object, use with script = true
|
||||
-- @param method string, the name of a global function or the name of an object method
|
||||
-- @param handler function, or string when it's a method of 'self', if nil then will use the same value as method
|
||||
-- @param script boolean, must be true, if the hooked object is a frame
|
||||
-- @param secure boolean, if hooking a secure script, if true the original script will be called first, else later
|
||||
-- @param raw boolean, if raw hooking (replacing the original method)
|
||||
-- @param forceSecure boolean, if forcing to hook a secure method
|
||||
-- @param usage
|
||||
local function hook(self, obj, method, handler, script, secure, raw, forceSecure, usage)
|
||||
if not handler then handler = method end
|
||||
|
||||
-- These asserts make sure AceHooks's devs play by the rules.
|
||||
assert(not script or type(script) == "boolean")
|
||||
assert(not secure or type(secure) == "boolean")
|
||||
assert(not raw or type(raw) == "boolean")
|
||||
assert(not forceSecure or type(forceSecure) == "boolean")
|
||||
assert(usage)
|
||||
|
||||
-- Error checking Battery!
|
||||
if obj and type(obj) ~= "table" then
|
||||
error(format("%s: 'object' - nil or table expected got %s", usage, type(obj)), 3)
|
||||
end
|
||||
if type(method) ~= "string" then
|
||||
error(format("%s: 'method' - string expected got %s", usage, type(method)), 3)
|
||||
end
|
||||
if type(handler) ~= "string" and type(handler) ~= "function" then
|
||||
error(format("%s: 'handler' - nil, string, or function expected got %s", usage, type(handler)), 3)
|
||||
end
|
||||
if type(handler) == "string" and type(self[handler]) ~= "function" then
|
||||
error(format("%s: 'handler' - Handler specified does not exist at self[handler]", usage), 3)
|
||||
end
|
||||
if script then
|
||||
if not obj or not obj.GetScript or not obj:HasScript(method) then
|
||||
error(format("%s: You can only hook a script on a frame object", usage), 3)
|
||||
end
|
||||
if not secure and obj.IsProtected and obj:IsProtected() and protectedScripts[method] then
|
||||
error(format("Cannot hook secure script %q; Use SecureHookScript(obj, method, [handler]) instead.", method), 3)
|
||||
end
|
||||
else
|
||||
local issecure
|
||||
if obj then
|
||||
issecure = onceSecure[obj] and onceSecure[obj][method] or issecurevariable(obj, method)
|
||||
else
|
||||
issecure = onceSecure[method] or issecurevariable(method)
|
||||
end
|
||||
if issecure then
|
||||
if forceSecure then
|
||||
if obj then
|
||||
onceSecure[obj] = onceSecure[obj] or {}
|
||||
onceSecure[obj][method] = true
|
||||
else
|
||||
onceSecure[method] = true
|
||||
end
|
||||
elseif not secure then
|
||||
error(format("%s: Attempt to hook secure function %s. Use `SecureHook' or add `true' to the argument list to override.", usage, method), 3)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local uid
|
||||
if obj then
|
||||
uid = registry[self][obj] and registry[self][obj][method]
|
||||
else
|
||||
uid = registry[self][method]
|
||||
end
|
||||
|
||||
if uid then
|
||||
if actives[uid] then
|
||||
-- Only two sane choices exist here. We either a) error 100% of the time or b) always unhook and then hook
|
||||
-- choice b would likely lead to odd debuging conditions or other mysteries so we're going with a.
|
||||
error(format("Attempting to rehook already active hook %s.", method))
|
||||
end
|
||||
|
||||
if handlers[uid] == handler then -- turn on a decative hook, note enclosures break this ability, small memory leak
|
||||
actives[uid] = true
|
||||
return
|
||||
elseif obj then -- is there any reason not to call unhook instead of doing the following several lines?
|
||||
if self.hooks and self.hooks[obj] then
|
||||
self.hooks[obj][method] = nil
|
||||
end
|
||||
registry[self][obj][method] = nil
|
||||
else
|
||||
if self.hooks then
|
||||
self.hooks[method] = nil
|
||||
end
|
||||
registry[self][method] = nil
|
||||
end
|
||||
handlers[uid], actives[uid], scripts[uid] = nil, nil, nil
|
||||
uid = nil
|
||||
end
|
||||
|
||||
local orig
|
||||
if script then
|
||||
-- Sometimes there is not a original function for a script.
|
||||
orig = obj:GetScript(method) or donothing
|
||||
elseif obj then
|
||||
orig = obj[method]
|
||||
else
|
||||
orig = _G[method]
|
||||
end
|
||||
|
||||
if not orig then
|
||||
error(format("%s: Attempting to hook a non existing target", usage), 3)
|
||||
end
|
||||
|
||||
uid = createHook(self, handler, orig, secure, not (raw or secure))
|
||||
|
||||
if obj then
|
||||
registry[self][obj] = registry[self][obj] or new()
|
||||
registry[self][obj][method] = uid
|
||||
|
||||
if not secure then
|
||||
self.hooks[obj] = self.hooks[obj] or new()
|
||||
self.hooks[obj][method] = orig
|
||||
end
|
||||
|
||||
if script then
|
||||
if not secure then
|
||||
obj:SetScript(method, uid)
|
||||
else
|
||||
obj:SetScript(method, function(...)
|
||||
local tmp = {orig(unpack(arg))}
|
||||
uid(unpack(arg))
|
||||
return unpack(tmp)
|
||||
end)
|
||||
--obj:HookScript(method, uid) -- Ace3v: vanilla frame has no HookScript
|
||||
end
|
||||
else
|
||||
if not secure then
|
||||
obj[method] = uid
|
||||
else
|
||||
hooksecurefunc(obj, method, uid)
|
||||
end
|
||||
end
|
||||
else
|
||||
registry[self][method] = uid
|
||||
|
||||
if not secure then
|
||||
_G[method] = uid
|
||||
self.hooks[method] = orig
|
||||
else
|
||||
hooksecurefunc(method, uid)
|
||||
end
|
||||
end
|
||||
|
||||
actives[uid], handlers[uid], scripts[uid] = true, handler, script and true or nil
|
||||
end
|
||||
|
||||
--- Hook a function or a method on an object.
|
||||
-- The hook created will be a "safe hook", that means that your handler will be called
|
||||
-- before the hooked function ("Pre-Hook"), and you don't have to call the original function yourself,
|
||||
-- however you cannot stop the execution of the function, or modify any of the arguments/return values.\\
|
||||
-- This type of hook is typically used if you need to know if some function got called, and don't want to modify it.
|
||||
-- @paramsig [object], method, [handler], [hookSecure]
|
||||
-- @param object The object to hook a method from
|
||||
-- @param method If object was specified, the name of the method, or the name of the function to hook.
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked function)
|
||||
-- @param hookSecure If true, AceHook will allow hooking of secure functions.
|
||||
-- @usage
|
||||
-- -- create an addon with AceHook embeded
|
||||
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("HookDemo", "AceHook-3.0")
|
||||
--
|
||||
-- function MyAddon:OnEnable()
|
||||
-- -- Hook ActionButton_UpdateHotkeys, overwriting the secure status
|
||||
-- self:Hook("ActionButton_UpdateHotkeys", true)
|
||||
-- end
|
||||
--
|
||||
-- function MyAddon:ActionButton_UpdateHotkeys(button, type)
|
||||
-- print(button:GetName() .. " is updating its HotKey")
|
||||
-- end
|
||||
function AceHook:Hook(object, method, handler, hookSecure)
|
||||
if type(object) == "string" then
|
||||
method, handler, hookSecure, object = object, method, handler, nil
|
||||
end
|
||||
|
||||
if handler == true then
|
||||
handler, hookSecure = nil, true
|
||||
end
|
||||
|
||||
hook(self, object, method, handler, false, false, false, hookSecure or false, "Usage: Hook([object], method, [handler], [hookSecure])")
|
||||
end
|
||||
|
||||
--- RawHook a function or a method on an object.
|
||||
-- The hook created will be a "raw hook", that means that your handler will completly replace
|
||||
-- the original function, and your handler has to call the original function (or not, depending on your intentions).\\
|
||||
-- The original function will be stored in `self.hooks[object][method]` or `self.hooks[functionName]` respectively.\\
|
||||
-- This type of hook can be used for all purposes, and is usually the most common case when you need to modify arguments
|
||||
-- or want to control execution of the original function.
|
||||
-- @paramsig [object], method, [handler], [hookSecure]
|
||||
-- @param object The object to hook a method from
|
||||
-- @param method If object was specified, the name of the method, or the name of the function to hook.
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked function)
|
||||
-- @param hookSecure If true, AceHook will allow hooking of secure functions.
|
||||
-- @usage
|
||||
-- -- create an addon with AceHook embeded
|
||||
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("HookDemo", "AceHook-3.0")
|
||||
--
|
||||
-- function MyAddon:OnEnable()
|
||||
-- -- Hook ActionButton_UpdateHotkeys, overwriting the secure status
|
||||
-- self:RawHook("ActionButton_UpdateHotkeys", true)
|
||||
-- end
|
||||
--
|
||||
-- function MyAddon:ActionButton_UpdateHotkeys(button, type)
|
||||
-- if button:GetName() == "MyButton" then
|
||||
-- -- do stuff here
|
||||
-- else
|
||||
-- self.hooks.ActionButton_UpdateHotkeys(button, type)
|
||||
-- end
|
||||
-- end
|
||||
function AceHook:RawHook(object, method, handler, hookSecure)
|
||||
if type(object) == "string" then
|
||||
method, handler, hookSecure, object = object, method, handler, nil
|
||||
end
|
||||
|
||||
if handler == true then
|
||||
handler, hookSecure = nil, true
|
||||
end
|
||||
|
||||
hook(self, object, method, handler, false, false, true, hookSecure or false, "Usage: RawHook([object], method, [handler], [hookSecure])")
|
||||
end
|
||||
|
||||
--- SecureHook a function or a method on an object.
|
||||
-- This function is a wrapper around the `hooksecurefunc` function in the WoW API. Using AceHook
|
||||
-- extends the functionality of secure hooks, and adds the ability to unhook once the hook isn't
|
||||
-- required anymore, or the addon is being disabled.\\
|
||||
-- Secure Hooks should be used if the secure-status of the function is vital to its function,
|
||||
-- and taint would block execution. Secure Hooks are always called after the original function was called
|
||||
-- ("Post Hook"), and you cannot modify the arguments, return values or control the execution.
|
||||
-- @paramsig [object], method, [handler]
|
||||
-- @param object The object to hook a method from
|
||||
-- @param method If object was specified, the name of the method, or the name of the function to hook.
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked function)
|
||||
function AceHook:SecureHook(object, method, handler)
|
||||
if type(object) == "string" then
|
||||
method, handler, object = object, method, nil
|
||||
end
|
||||
|
||||
hook(self, object, method, handler, false, true, false, false, "Usage: SecureHook([object], method, [handler])")
|
||||
end
|
||||
|
||||
--- Hook a script handler on a frame.
|
||||
-- The hook created will be a "safe hook", that means that your handler will be called
|
||||
-- before the hooked script ("Pre-Hook"), and you don't have to call the original function yourself,
|
||||
-- however you cannot stop the execution of the function, or modify any of the arguments/return values.\\
|
||||
-- This is the frame script equivalent of the :Hook safe-hook. It would typically be used to be notified
|
||||
-- when a certain event happens to a frame.
|
||||
-- @paramsig frame, script, [handler]
|
||||
-- @param frame The Frame to hook the script on
|
||||
-- @param script The script to hook
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked script)
|
||||
-- @usage
|
||||
-- -- create an addon with AceHook embeded
|
||||
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("HookDemo", "AceHook-3.0")
|
||||
--
|
||||
-- function MyAddon:OnEnable()
|
||||
-- -- Hook the OnShow of FriendsFrame
|
||||
-- self:HookScript(FriendsFrame, "OnShow", "FriendsFrameOnShow")
|
||||
-- end
|
||||
--
|
||||
-- function MyAddon:FriendsFrameOnShow(frame)
|
||||
-- print("The FriendsFrame was shown!")
|
||||
-- end
|
||||
function AceHook:HookScript(frame, script, handler)
|
||||
hook(self, frame, script, handler, true, false, false, false, "Usage: HookScript(object, method, [handler])")
|
||||
end
|
||||
|
||||
--- RawHook a script handler on a frame.
|
||||
-- The hook created will be a "raw hook", that means that your handler will completly replace
|
||||
-- the original script, and your handler has to call the original script (or not, depending on your intentions).\\
|
||||
-- The original script will be stored in `self.hooks[frame][script]`.\\
|
||||
-- This type of hook can be used for all purposes, and is usually the most common case when you need to modify arguments
|
||||
-- or want to control execution of the original script.
|
||||
-- @paramsig frame, script, [handler]
|
||||
-- @param frame The Frame to hook the script on
|
||||
-- @param script The script to hook
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked script)
|
||||
-- @usage
|
||||
-- -- create an addon with AceHook embeded
|
||||
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("HookDemo", "AceHook-3.0")
|
||||
--
|
||||
-- function MyAddon:OnEnable()
|
||||
-- -- Hook the OnShow of FriendsFrame
|
||||
-- self:RawHookScript(FriendsFrame, "OnShow", "FriendsFrameOnShow")
|
||||
-- end
|
||||
--
|
||||
-- function MyAddon:FriendsFrameOnShow(frame)
|
||||
-- -- Call the original function
|
||||
-- self.hooks[frame].OnShow(frame)
|
||||
-- -- Do our processing
|
||||
-- -- .. stuff
|
||||
-- end
|
||||
function AceHook:RawHookScript(frame, script, handler)
|
||||
hook(self, frame, script, handler, true, false, true, false, "Usage: RawHookScript(object, method, [handler])")
|
||||
end
|
||||
|
||||
--- SecureHook a script handler on a frame.
|
||||
-- This function is a wrapper around the `frame:HookScript` function in the WoW API. Using AceHook
|
||||
-- extends the functionality of secure hooks, and adds the ability to unhook once the hook isn't
|
||||
-- required anymore, or the addon is being disabled.\\
|
||||
-- Secure Hooks should be used if the secure-status of the function is vital to its function,
|
||||
-- and taint would block execution. Secure Hooks are always called after the original function was called
|
||||
-- ("Post Hook"), and you cannot modify the arguments, return values or control the execution.
|
||||
-- @paramsig frame, script, [handler]
|
||||
-- @param frame The Frame to hook the script on
|
||||
-- @param script The script to hook
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked script)
|
||||
function AceHook:SecureHookScript(frame, script, handler)
|
||||
hook(self, frame, script, handler, true, true, false, false, "Usage: SecureHookScript(object, method, [handler])")
|
||||
end
|
||||
|
||||
--- Unhook from the specified function, method or script.
|
||||
-- @paramsig [obj], method
|
||||
-- @param obj The object or frame to unhook from
|
||||
-- @param method The name of the method, function or script to unhook from.
|
||||
function AceHook:Unhook(obj, method)
|
||||
local usage = "Usage: Unhook([obj], method)"
|
||||
if type(obj) == "string" then
|
||||
method, obj = obj, nil
|
||||
end
|
||||
|
||||
if obj and type(obj) ~= "table" then
|
||||
error(format("%s: 'obj' - expecting nil or table got %s", usage, type(obj)), 2)
|
||||
end
|
||||
if type(method) ~= "string" then
|
||||
error(format("%s: 'method' - expeting string got %s", usage, type(method)), 2)
|
||||
end
|
||||
|
||||
local uid
|
||||
if obj then
|
||||
uid = registry[self][obj] and registry[self][obj][method]
|
||||
else
|
||||
uid = registry[self][method]
|
||||
end
|
||||
|
||||
if not uid or not actives[uid] then
|
||||
-- Declining to error on an unneeded unhook since the end effect is the same and this would just be annoying.
|
||||
return false
|
||||
end
|
||||
|
||||
actives[uid], handlers[uid] = nil, nil
|
||||
|
||||
if obj then
|
||||
local tmp = registry[self][obj]
|
||||
tmp[method] = nil
|
||||
if not next(tmp) then
|
||||
del(tmp)
|
||||
registry[self][obj] = nil
|
||||
end
|
||||
|
||||
-- if the hook reference doesnt exist, then its a secure hook, just bail out and dont do any unhooking
|
||||
if not self.hooks[obj] or not self.hooks[obj][method] then return true end
|
||||
|
||||
if scripts[uid] and obj:GetScript(method) == uid then -- unhooks scripts
|
||||
obj:SetScript(method, self.hooks[obj][method] ~= donothing and self.hooks[obj][method] or nil)
|
||||
scripts[uid] = nil
|
||||
elseif obj and self.hooks[obj] and self.hooks[obj][method] and obj[method] == uid then -- unhooks methods
|
||||
obj[method] = self.hooks[obj][method]
|
||||
end
|
||||
|
||||
tmp = self.hooks[obj]
|
||||
tmp[method] = nil
|
||||
if not next(tmp) then
|
||||
del(tmp)
|
||||
self.hooks[obj] = nil
|
||||
end
|
||||
else
|
||||
registry[self][method] = nil
|
||||
|
||||
-- if self.hooks[method] doesn't exist, then this is a SecureHook, just bail out
|
||||
if not self.hooks[method] then return true end
|
||||
|
||||
if self.hooks[method] and _G[method] == uid then -- unhooks functions
|
||||
_G[method] = self.hooks[method]
|
||||
end
|
||||
|
||||
self.hooks[method] = nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Unhook all existing hooks for this addon.
|
||||
function AceHook:UnhookAll()
|
||||
for key, value in pairs(registry[self]) do
|
||||
if type(key) == "table" then
|
||||
for method in pairs(value) do
|
||||
self:Unhook(key, method)
|
||||
end
|
||||
else
|
||||
self:Unhook(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Check if the specific function, method or script is already hooked.
|
||||
-- @paramsig [obj], method
|
||||
-- @param obj The object or frame to unhook from
|
||||
-- @param method The name of the method, function or script to unhook from.
|
||||
function AceHook:IsHooked(obj, method)
|
||||
-- we don't check if registry[self] exists, this is done by evil magicks in the metatable
|
||||
if type(obj) == "string" then
|
||||
if registry[self][obj] and actives[registry[self][obj]] then
|
||||
return true, handlers[registry[self][obj]]
|
||||
end
|
||||
else
|
||||
if registry[self][obj] and registry[self][obj][method] and actives[registry[self][obj][method]] then
|
||||
return true, handlers[registry[self][obj][method]]
|
||||
end
|
||||
end
|
||||
|
||||
return false, nil
|
||||
end
|
||||
|
||||
--- Upgrade our old embeded
|
||||
for target, v in pairs( AceHook.embeded ) do
|
||||
AceHook:Embed( target )
|
||||
end
|
||||
@@ -0,0 +1,280 @@
|
||||
--[[ $Id: CallbackHandler-1.0.lua 1131 2015-06-04 07:29:24Z nevcairiel $ ]]
|
||||
local MAJOR, MINOR = "CallbackHandler-1.0", 6
|
||||
local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR)
|
||||
|
||||
if not CallbackHandler then return end -- No upgrade needed
|
||||
|
||||
-- Lua APIs
|
||||
local tconcat, tinsert, tgetn, tsetn = table.concat, table.insert, table.getn, table.setn
|
||||
local assert, error, loadstring = assert, error, loadstring
|
||||
local setmetatable, rawset, rawget = setmetatable, rawset, rawget
|
||||
local next, pairs, type, tostring = next, pairs, type, tostring
|
||||
local strgsub = string.gsub
|
||||
|
||||
local new, del
|
||||
do
|
||||
local list = setmetatable({}, {__mode = "k"})
|
||||
function new()
|
||||
local t = next(list)
|
||||
if not t then
|
||||
return {}
|
||||
end
|
||||
list[t] = nil
|
||||
return t
|
||||
end
|
||||
|
||||
function del(t)
|
||||
setmetatable(t, nil)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
tsetn(t,0)
|
||||
list[t] = true
|
||||
end
|
||||
end
|
||||
|
||||
local meta = {__index = function(tbl, key) rawset(tbl, key, new()) return tbl[key] end}
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: geterrorhandler
|
||||
|
||||
local function errorhandler(err)
|
||||
return geterrorhandler()(err)
|
||||
end
|
||||
CallbackHandler.errorhandler = errorhandler
|
||||
|
||||
local function CreateDispatcher(argCount)
|
||||
local code = [[
|
||||
local xpcall, errorhandler = xpcall, LibStub("CallbackHandler-1.0").errorhandler
|
||||
local method, UP_ARGS
|
||||
local function call()
|
||||
local func, ARGS = method, UP_ARGS
|
||||
method, UP_ARGS = nil, NILS
|
||||
return func(ARGS)
|
||||
end
|
||||
return function(handlers, ARGS)
|
||||
local index
|
||||
index, method = next(handlers)
|
||||
if not method then return end
|
||||
repeat
|
||||
UP_ARGS = ARGS
|
||||
xpcall(call, errorhandler)
|
||||
index, method = next(handlers, index)
|
||||
until not method
|
||||
end
|
||||
]]
|
||||
local c = 4*argCount-1
|
||||
local s = "b01,b02,b03,b04,b05,b06,b07,b08,b09,b10"
|
||||
code = strgsub(code, "UP_ARGS", string.sub(s,1,c))
|
||||
s = "a01,a02,a03,a04,a05,a06,a07,a08,a09,a10"
|
||||
code = strgsub(code, "ARGS", string.sub(s,1,c))
|
||||
s = "nil,nil,nil,nil,nil,nil,nil,nil,nil,nil"
|
||||
code = strgsub(code, "NILS", string.sub(s,1,c))
|
||||
return assert(loadstring(code, "safecall Dispatcher["..tostring(argCount).."]"))()
|
||||
end
|
||||
|
||||
local Dispatchers = setmetatable({}, {__index=function(self, argCount)
|
||||
local dispatcher = CreateDispatcher(argCount)
|
||||
rawset(self, argCount, dispatcher)
|
||||
return dispatcher
|
||||
end})
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
-- CallbackHandler:New
|
||||
--
|
||||
-- target - target object to embed public APIs in
|
||||
-- RegisterName - name of the callback registration API, default "RegisterCallback"
|
||||
-- UnregisterName - name of the callback unregistration API, default "UnregisterCallback"
|
||||
-- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API.
|
||||
function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName)
|
||||
|
||||
RegisterName = RegisterName or "RegisterCallback"
|
||||
UnregisterName = UnregisterName or "UnregisterCallback"
|
||||
if UnregisterAllName==nil then -- false is used to indicate "don't want this method"
|
||||
UnregisterAllName = "UnregisterAllCallbacks"
|
||||
end
|
||||
|
||||
-- we declare all objects and exported APIs inside this closure to quickly gain access
|
||||
-- to e.g. function names, the "target" parameter, etc
|
||||
|
||||
|
||||
-- Create the registry object
|
||||
local events = setmetatable({}, meta)
|
||||
local registry = { recurse=0, events=events }
|
||||
|
||||
-- registry:Fire() - fires the given event/message into the registry
|
||||
function registry:Fire(eventname, argc, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
|
||||
if not rawget(events, eventname) or not next(events[eventname]) then return end
|
||||
local oldrecurse = registry.recurse
|
||||
registry.recurse = oldrecurse + 1
|
||||
|
||||
argc = argc or 0
|
||||
Dispatchers[argc+1](events[eventname], eventname, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
|
||||
|
||||
registry.recurse = oldrecurse
|
||||
|
||||
if registry.insertQueue and oldrecurse==0 then
|
||||
-- Something in one of our callbacks wanted to register more callbacks; they got queued
|
||||
for eventname,callbacks in pairs(registry.insertQueue) do
|
||||
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
|
||||
for self,func in pairs(callbacks) do
|
||||
events[eventname][self] = func
|
||||
-- fire OnUsed callback?
|
||||
if first and registry.OnUsed then
|
||||
registry.OnUsed(registry, target, eventname)
|
||||
first = nil
|
||||
end
|
||||
end
|
||||
del(callbacks)
|
||||
end
|
||||
del(registry.insertQueue)
|
||||
registry.insertQueue = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Registration of a callback, handles:
|
||||
-- self["method"], leads to self["method"](self, ...)
|
||||
-- self with function ref, leads to functionref(...)
|
||||
-- "addonId" (instead of self) with function ref, leads to functionref(...)
|
||||
-- all with an optional arg, which, if present, gets passed as first argument (after self if present)
|
||||
target[RegisterName] = function(self, eventname, method, ...)
|
||||
if type(eventname) ~= "string" then
|
||||
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2)
|
||||
end
|
||||
|
||||
method = method or eventname
|
||||
|
||||
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
|
||||
|
||||
if type(method) ~= "string" and type(method) ~= "function" then
|
||||
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'method' - string or function expected.", 2)
|
||||
end
|
||||
|
||||
local regfunc
|
||||
local a1 = arg[1]
|
||||
|
||||
if type(method) == "string" then
|
||||
-- self["method"] calling style
|
||||
if type(self) ~= "table" then
|
||||
error("Usage: "..RegisterName.."(eventname, method[, arg]): self was not a table?", 2)
|
||||
elseif self==target then
|
||||
error("Usage: "..RegisterName.."(eventname, method[, arg]): do not use Library:"..RegisterName.."(), use your own 'self'.", 2)
|
||||
elseif type(self[method]) ~= "function" then
|
||||
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'method' - method '"..tostring(method).."' not found on 'self'.", 2)
|
||||
end
|
||||
|
||||
if tgetn(arg) >= 1 then
|
||||
regfunc = function (...) return self[method](self,a1,unpack(arg)) end
|
||||
else
|
||||
regfunc = function (...) return self[method](self,unpack(arg)) end
|
||||
end
|
||||
else
|
||||
-- function ref with self=object or self="addonId"
|
||||
if type(self)~="table" and type(self)~="string" then
|
||||
error("Usage: "..RegisterName.."(self or addonId, eventname, method[, arg]): 'self or addonId': table or string expected.", 2)
|
||||
end
|
||||
|
||||
if tgetn(arg) >= 1 then
|
||||
regfunc = function (...) return method(a1, unpack(arg)) end
|
||||
else
|
||||
regfunc = method
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if events[eventname][self] or registry.recurse<1 then
|
||||
-- if registry.recurse<1 then
|
||||
-- we're overwriting an existing entry, or not currently recursing. just set it.
|
||||
events[eventname][self] = regfunc
|
||||
-- fire OnUsed callback?
|
||||
if registry.OnUsed and first then
|
||||
registry.OnUsed(registry, target, eventname)
|
||||
end
|
||||
else
|
||||
-- we're currently processing a callback in this registry, so delay the registration of this new entry!
|
||||
-- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency
|
||||
registry.insertQueue = registry.insertQueue or setmetatable(new(),meta)
|
||||
registry.insertQueue[eventname][self] = regfunc
|
||||
end
|
||||
end
|
||||
|
||||
-- Unregister a callback
|
||||
target[UnregisterName] = function(self, eventname)
|
||||
if not self or self==target then
|
||||
error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2)
|
||||
end
|
||||
if type(eventname) ~= "string" then
|
||||
error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2)
|
||||
end
|
||||
if rawget(events, eventname) and events[eventname][self] then
|
||||
events[eventname][self] = nil
|
||||
|
||||
-- Fire OnUnused callback?
|
||||
if registry.OnUnused and not next(events[eventname]) then
|
||||
registry.OnUnused(registry, target, eventname)
|
||||
end
|
||||
|
||||
if rawget(events, eventname) and not next(events[eventname]) then
|
||||
del(events[eventname])
|
||||
events[eventname] = nil
|
||||
end
|
||||
end
|
||||
if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then
|
||||
registry.insertQueue[eventname][self] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- OPTIONAL: Unregister all callbacks for given selfs/addonIds
|
||||
if UnregisterAllName then
|
||||
target[UnregisterAllName] = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
if not a1 then
|
||||
error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or 'addonId' to unregister events for.", 2)
|
||||
end
|
||||
if a1 == target then
|
||||
error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or 'addonId'", 2)
|
||||
end
|
||||
|
||||
-- use our registry table as argument table
|
||||
registry[1] = a1
|
||||
registry[2] = a2
|
||||
registry[3] = a3
|
||||
registry[4] = a4
|
||||
registry[5] = a5
|
||||
registry[6] = a6
|
||||
registry[7] = a7
|
||||
registry[8] = a8
|
||||
registry[9] = a9
|
||||
registry[10] = a10
|
||||
for i=1,10 do
|
||||
local self = registry[i]
|
||||
registry[i] = nil
|
||||
if self then
|
||||
if registry.insertQueue then
|
||||
for eventname, callbacks in pairs(registry.insertQueue) do
|
||||
if callbacks[self] then
|
||||
callbacks[self] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
for eventname, callbacks in pairs(events) do
|
||||
if callbacks[self] then
|
||||
callbacks[self] = nil
|
||||
-- Fire OnUnused callback?
|
||||
if registry.OnUnused and not next(callbacks) then
|
||||
registry.OnUnused(registry, target, eventname)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return registry
|
||||
end
|
||||
|
||||
-- CallbackHandler purposefully does NOT do explicit embedding. Nor does it
|
||||
-- try to upgrade old implicit embeds since the system is selfcontained and
|
||||
-- relies on closures to work.
|
||||
|
||||
@@ -0,0 +1,487 @@
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- LibDBIcon-1.0
|
||||
--
|
||||
-- Allows addons to easily create a lightweight minimap icon as an alternative to heavier LDB displays.
|
||||
--
|
||||
|
||||
local DBICON10 = "LibDBIcon-1.0"
|
||||
local DBICON10_MINOR = 44 -- Bump on changes
|
||||
if not LibStub then error(DBICON10 .. " requires LibStub.") end
|
||||
local ldb = LibStub("LibDataBroker-1.1", true)
|
||||
if not ldb then error(DBICON10 .. " requires LibDataBroker-1.1.") end
|
||||
local lib = LibStub:NewLibrary(DBICON10, DBICON10_MINOR)
|
||||
if not lib then return end
|
||||
|
||||
LibStub("AceHook-3.0"):Embed(lib)
|
||||
lib.objects = lib.objects or {}
|
||||
lib.callbackRegistered = lib.callbackRegistered or nil
|
||||
lib.callbacks = lib.callbacks or LibStub("CallbackHandler-1.0"):New(lib)
|
||||
lib.notCreated = lib.notCreated or {}
|
||||
lib.radius = lib.radius or 5
|
||||
lib.tooltip = lib.tooltip or CreateFrame("GameTooltip", "LibDBIconTooltip", UIParent, "GameTooltipTemplate")
|
||||
local next, Minimap = next, Minimap
|
||||
local tgetn = table.getn
|
||||
local isDraggingButton = false
|
||||
|
||||
function lib:IconCallback(event, name, key, value)
|
||||
if lib.objects[name] then
|
||||
if key == "icon" then
|
||||
lib.objects[name].icon:SetTexture(value)
|
||||
elseif key == "iconCoords" then
|
||||
lib.objects[name].icon:UpdateCoord()
|
||||
elseif key == "iconR" then
|
||||
local _, g, b = lib.objects[name].icon:GetVertexColor()
|
||||
lib.objects[name].icon:SetVertexColor(value, g, b)
|
||||
elseif key == "iconG" then
|
||||
local r, _, b = lib.objects[name].icon:GetVertexColor()
|
||||
lib.objects[name].icon:SetVertexColor(r, value, b)
|
||||
elseif key == "iconB" then
|
||||
local r, g = lib.objects[name].icon:GetVertexColor()
|
||||
lib.objects[name].icon:SetVertexColor(r, g, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
if not lib.callbackRegistered then
|
||||
ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__icon", "IconCallback")
|
||||
ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconCoords", "IconCallback")
|
||||
ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconR", "IconCallback")
|
||||
ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconG", "IconCallback")
|
||||
ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconB", "IconCallback")
|
||||
lib.callbackRegistered = true
|
||||
end
|
||||
|
||||
local function getAnchors(frame)
|
||||
local x, y = frame:GetCenter()
|
||||
if not x or not y then return "CENTER" end
|
||||
local hhalf = (x > UIParent:GetWidth()*2/3) and "RIGHT" or (x < UIParent:GetWidth()/3) and "LEFT" or ""
|
||||
local vhalf = (y > UIParent:GetHeight()/2) and "TOP" or "BOTTOM"
|
||||
return vhalf..hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP")..hhalf
|
||||
end
|
||||
|
||||
local function onEnter()
|
||||
if isDraggingButton then return end
|
||||
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
--button.fadeOut:Stop()
|
||||
button:SetAlpha(1)
|
||||
end
|
||||
end
|
||||
|
||||
local obj = this.dataObject
|
||||
if obj.OnTooltipShow then
|
||||
lib.tooltip:SetOwner(this, "ANCHOR_NONE")
|
||||
lib.tooltip:SetPoint(getAnchors(this))
|
||||
obj.OnTooltipShow(lib.tooltip)
|
||||
lib.tooltip:Show()
|
||||
elseif obj.OnEnter then
|
||||
obj.OnEnter()
|
||||
end
|
||||
end
|
||||
|
||||
local function onLeave()
|
||||
lib.tooltip:Hide()
|
||||
|
||||
if not isDraggingButton then
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
--button.fadeOut:Play()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local obj = this.dataObject
|
||||
if obj.OnLeave then
|
||||
obj.OnLeave()
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local onDragStart, updatePosition
|
||||
|
||||
do
|
||||
local minimapShapes = {
|
||||
["ROUND"] = {true, true, true, true},
|
||||
["SQUARE"] = {false, false, false, false},
|
||||
["CORNER-TOPLEFT"] = {false, false, false, true},
|
||||
["CORNER-TOPRIGHT"] = {false, false, true, false},
|
||||
["CORNER-BOTTOMLEFT"] = {false, true, false, false},
|
||||
["CORNER-BOTTOMRIGHT"] = {true, false, false, false},
|
||||
["SIDE-LEFT"] = {false, true, false, true},
|
||||
["SIDE-RIGHT"] = {true, false, true, false},
|
||||
["SIDE-TOP"] = {false, false, true, true},
|
||||
["SIDE-BOTTOM"] = {true, true, false, false},
|
||||
["TRICORNER-TOPLEFT"] = {false, true, true, true},
|
||||
["TRICORNER-TOPRIGHT"] = {true, false, true, true},
|
||||
["TRICORNER-BOTTOMLEFT"] = {true, true, false, true},
|
||||
["TRICORNER-BOTTOMRIGHT"] = {true, true, true, false},
|
||||
}
|
||||
|
||||
local rad, cos, sin, sqrt, max, min = math.rad, math.cos, math.sin, math.sqrt, math.max, math.min
|
||||
function updatePosition(button, position)
|
||||
local angle = rad(position or 225)
|
||||
local x, y, q = cos(angle), sin(angle), 1
|
||||
if x < 0 then q = q + 1 end
|
||||
if y > 0 then q = q + 2 end
|
||||
local minimapShape = GetMinimapShape and GetMinimapShape() or "ROUND"
|
||||
local quadTable = minimapShapes[minimapShape]
|
||||
local w = (Minimap:GetWidth() / 2) + lib.radius
|
||||
local h = (Minimap:GetHeight() / 2) + lib.radius
|
||||
if quadTable[q] then
|
||||
x, y = x*w, y*h
|
||||
else
|
||||
local diagRadiusW = sqrt(2*(w)^2)-10
|
||||
local diagRadiusH = sqrt(2*(h)^2)-10
|
||||
x = max(-w, min(x*diagRadiusW, w))
|
||||
y = max(-h, min(y*diagRadiusH, h))
|
||||
end
|
||||
button:SetPoint("CENTER", Minimap, "CENTER", x, y)
|
||||
end
|
||||
end
|
||||
|
||||
local function onClick( )
|
||||
if this.dataObject.OnClick then
|
||||
this.dataObject.OnClick( this, arg1)
|
||||
end
|
||||
end
|
||||
|
||||
local function onMouseDown()
|
||||
this.isMouseDown = true
|
||||
this.icon:UpdateCoord()
|
||||
end
|
||||
|
||||
local function onMouseUp()
|
||||
this.isMouseDown = false
|
||||
this.icon:UpdateCoord()
|
||||
end
|
||||
|
||||
local fmod = function(x, y)
|
||||
return x - math.floor(x / y) * y
|
||||
end
|
||||
|
||||
do
|
||||
local deg, atan2 = math.deg, math.atan2
|
||||
local function onUpdate()
|
||||
local mx, my = Minimap:GetCenter()
|
||||
local px, py = GetCursorPosition()
|
||||
local scale = Minimap:GetEffectiveScale()
|
||||
px, py = px / scale, py / scale
|
||||
local pos = 225
|
||||
if this.db then
|
||||
pos = fmod(deg(atan2(py - my, px - mx)) , 360)
|
||||
this.db.minimapPos = pos
|
||||
else
|
||||
pos = fmod(deg(atan2(py - my, px - mx)) , 360)
|
||||
this.minimapPos = pos
|
||||
end
|
||||
updatePosition(this, pos)
|
||||
end
|
||||
|
||||
function onDragStart()
|
||||
this:LockHighlight()
|
||||
this.isMouseDown = true
|
||||
this.icon:UpdateCoord()
|
||||
this:SetScript("OnUpdate", onUpdate)
|
||||
isDraggingButton = true
|
||||
lib.tooltip:Hide()
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
--button.fadeOut:Stop()
|
||||
button:SetAlpha(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function onDragStop()
|
||||
this:SetScript("OnUpdate", nil)
|
||||
this.isMouseDown = false
|
||||
this.icon:UpdateCoord()
|
||||
this:UnlockHighlight()
|
||||
isDraggingButton = false
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
--button.fadeOut:Play()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local defaultCoords = {0, 1, 0, 1}
|
||||
local function updateCoord(self)
|
||||
local coords = self:GetParent().dataObject.iconCoords or defaultCoords
|
||||
local deltaX, deltaY = 0, 0
|
||||
if not self:GetParent().isMouseDown then
|
||||
deltaX = (coords[2] - coords[1]) * 0.05
|
||||
deltaY = (coords[4] - coords[3]) * 0.05
|
||||
end
|
||||
self:SetTexCoord(coords[1] + deltaX, coords[2] - deltaX, coords[3] + deltaY, coords[4] - deltaY)
|
||||
end
|
||||
|
||||
local function createButton(name, object, db)
|
||||
local button = CreateFrame("Button", "LibDBIcon10_"..name, Minimap)
|
||||
button.dataObject = object
|
||||
button.db = db
|
||||
button:SetFrameStrata("MEDIUM")
|
||||
button:SetWidth(31)
|
||||
button:SetHeight(31)
|
||||
button:SetFrameLevel(8)
|
||||
button:SetFrameStrata("HIGH")
|
||||
button:SetFrameLevel(7)
|
||||
button:EnableMouse(true)
|
||||
--button:EnableMouseWheel(true)
|
||||
button:SetMovable(true)
|
||||
button:RegisterForClicks("LeftButtonUp","RightButtonUp")
|
||||
button:RegisterForDrag("LeftButton")
|
||||
button:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight")
|
||||
local overlay = button:CreateTexture(nil, "OVERLAY")
|
||||
overlay:SetWidth(53)
|
||||
overlay:SetHeight(53)
|
||||
overlay:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder")
|
||||
overlay:SetPoint("TOPLEFT",0,0)
|
||||
local background = button:CreateTexture(nil, "BACKGROUND")
|
||||
background:SetWidth(20)
|
||||
background:SetHeight(20)
|
||||
background:SetTexture("Interface\\Minimap\\UI-Minimap-Background")
|
||||
background:SetPoint("TOPLEFT", 7, -5)
|
||||
local icon = button:CreateTexture(nil, "ARTWORK")
|
||||
icon:SetWidth(17)
|
||||
icon:SetHeight(17)
|
||||
icon:SetTexture(object.icon)
|
||||
icon:SetPoint("TOPLEFT", 7, -6)
|
||||
button.icon = icon
|
||||
icon.parent = button
|
||||
button.isMouseDown = false
|
||||
|
||||
local r, g, b = icon:GetVertexColor()
|
||||
icon:SetVertexColor(object.iconR or r, object.iconG or g, object.iconB or b)
|
||||
|
||||
icon.UpdateCoord = updateCoord
|
||||
icon:UpdateCoord()
|
||||
|
||||
button:SetScript("OnEnter", onEnter)
|
||||
button:SetScript("OnLeave", onLeave)
|
||||
button:SetScript("OnClick", onClick)
|
||||
if not db or not db.lock then
|
||||
button:SetScript("OnDragStart", onDragStart)
|
||||
button:SetScript("OnDragStop", onDragStop)
|
||||
end
|
||||
button:SetScript("OnMouseDown", onMouseDown)
|
||||
button:SetScript("OnMouseUp", onMouseUp)
|
||||
--[[
|
||||
button.fadeOut = button:CreateAnimationGroup()
|
||||
local animOut = button.fadeOut:CreateAnimation("Alpha")
|
||||
animOut:SetOrder(1)
|
||||
animOut:SetDuration(0.2)
|
||||
animOut:SetFromAlpha(1)
|
||||
animOut:SetToAlpha(0)
|
||||
animOut:SetStartDelay(1)
|
||||
button.fadeOut:SetToFinalAlpha(true)
|
||||
]]
|
||||
lib.objects[name] = button
|
||||
|
||||
if lib.loggedIn then
|
||||
updatePosition(button, db and db.minimapPos)
|
||||
if not db or not db.hide then
|
||||
button:Show()
|
||||
else
|
||||
button:Hide()
|
||||
end
|
||||
end
|
||||
lib.callbacks:Fire("LibDBIcon_IconCreated", button, name) -- Fire 'Icon Created' callback
|
||||
end
|
||||
|
||||
-- We could use a metatable.__index on lib.objects, but then we'd create
|
||||
-- the icons when checking things like :IsRegistered, which is not necessary.
|
||||
local function check(name)
|
||||
if lib.notCreated[name] then
|
||||
createButton(name, lib.notCreated[name][1], lib.notCreated[name][2])
|
||||
lib.notCreated[name] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Wait a bit with the initial positioning to let any GetMinimapShape addons
|
||||
-- load up.
|
||||
if not lib.loggedIn then
|
||||
local f = CreateFrame("Frame")
|
||||
f:SetScript("OnEvent", function()
|
||||
for _, button in next, lib.objects do
|
||||
updatePosition(button, button.db and button.db.minimapPos)
|
||||
if not button.db or not button.db.hide then
|
||||
button:Show()
|
||||
else
|
||||
button:Hide()
|
||||
end
|
||||
end
|
||||
lib.loggedIn = true
|
||||
this:SetScript("OnEvent", nil)
|
||||
end)
|
||||
f:RegisterEvent("PLAYER_LOGIN")
|
||||
end
|
||||
|
||||
local function getDatabase(name)
|
||||
return lib.notCreated[name] and lib.notCreated[name][2] or lib.objects[name].db
|
||||
end
|
||||
|
||||
function lib:Register(name, object, db)
|
||||
if not object.icon then error("Can't register LDB objects without icons set!") end
|
||||
if lib.objects[name] or lib.notCreated[name] then error(DBICON10.. ": Object '".. name .."' is already registered.") end
|
||||
if not db or not db.hide then
|
||||
createButton(name, object, db)
|
||||
else
|
||||
lib.notCreated[name] = {object, db}
|
||||
end
|
||||
end
|
||||
|
||||
function lib:Lock(name)
|
||||
if not lib:IsRegistered(name) then return end
|
||||
if lib.objects[name] then
|
||||
lib.objects[name]:SetScript("OnDragStart", nil)
|
||||
lib.objects[name]:SetScript("OnDragStop", nil)
|
||||
end
|
||||
local db = getDatabase(name)
|
||||
if db then
|
||||
db.lock = true
|
||||
end
|
||||
end
|
||||
|
||||
function lib:Unlock(name)
|
||||
if not lib:IsRegistered(name) then return end
|
||||
if lib.objects[name] then
|
||||
lib.objects[name]:SetScript("OnDragStart", onDragStart)
|
||||
lib.objects[name]:SetScript("OnDragStop", onDragStop)
|
||||
end
|
||||
local db = getDatabase(name)
|
||||
if db then
|
||||
db.lock = nil
|
||||
end
|
||||
end
|
||||
|
||||
function lib:Hide(name)
|
||||
if not lib.objects[name] then return end
|
||||
lib.objects[name]:Hide()
|
||||
end
|
||||
|
||||
function lib:Show(name)
|
||||
check(name)
|
||||
local button = lib.objects[name]
|
||||
if button then
|
||||
button:Show()
|
||||
updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
|
||||
end
|
||||
end
|
||||
|
||||
function lib:IsRegistered(name)
|
||||
return (lib.objects[name] or lib.notCreated[name]) and true or false
|
||||
end
|
||||
|
||||
function lib:Refresh(name, db)
|
||||
check(name)
|
||||
local button = lib.objects[name]
|
||||
if db then
|
||||
button.db = db
|
||||
end
|
||||
updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
|
||||
if not button.db or not button.db.hide then
|
||||
button:Show()
|
||||
else
|
||||
button:Hide()
|
||||
end
|
||||
if not button.db or not button.db.lock then
|
||||
button:SetScript("OnDragStart", onDragStart)
|
||||
button:SetScript("OnDragStop", onDragStop)
|
||||
else
|
||||
button:SetScript("OnDragStart", nil)
|
||||
button:SetScript("OnDragStop", nil)
|
||||
end
|
||||
end
|
||||
|
||||
function lib:GetMinimapButton(name)
|
||||
return lib.objects[name]
|
||||
end
|
||||
|
||||
do
|
||||
local function OnMinimapEnter()
|
||||
if isDraggingButton then return end
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
-- button.fadeOut:Stop()
|
||||
button:SetAlpha(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
local function OnMinimapLeave()
|
||||
if isDraggingButton then return end
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
-- button.fadeOut:Play()
|
||||
end
|
||||
end
|
||||
end
|
||||
lib:HookScript(Minimap, "OnEnter", OnMinimapEnter)
|
||||
lib:HookScript(Minimap, "OnLeave", OnMinimapLeave)
|
||||
|
||||
function lib:ShowOnEnter(name, value)
|
||||
local button = lib.objects[name]
|
||||
if button then
|
||||
if value then
|
||||
button.showOnMouseover = true
|
||||
-- button.fadeOut:Stop()
|
||||
button:SetAlpha(0)
|
||||
else
|
||||
button.showOnMouseover = false
|
||||
-- button.fadeOut:Stop()
|
||||
button:SetAlpha(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function lib:GetButtonList()
|
||||
local t = {}
|
||||
for name in next, lib.objects do
|
||||
tinsert(t, name)
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
function lib:SetButtonRadius(radius)
|
||||
if type(radius) == "number" then
|
||||
lib.radius = radius
|
||||
for _, button in next, lib.objects do
|
||||
updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function lib:SetButtonToPosition(button, position)
|
||||
updatePosition(lib.objects[button] or button, position)
|
||||
end
|
||||
|
||||
-- Upgrade!
|
||||
for name, button in next, lib.objects do
|
||||
local db = getDatabase(name)
|
||||
if not db or not db.lock then
|
||||
button:SetScript("OnDragStart", onDragStart)
|
||||
button:SetScript("OnDragStop", onDragStop)
|
||||
end
|
||||
button:SetScript("OnEnter", onEnter)
|
||||
button:SetScript("OnLeave", onLeave)
|
||||
button:SetScript("OnClick", onClick)
|
||||
button:SetScript("OnMouseDown", onMouseDown)
|
||||
button:SetScript("OnMouseUp", onMouseUp)
|
||||
--[[
|
||||
if not button.fadeOut then -- Upgrade to 39
|
||||
button.fadeOut = button:CreateAnimationGroup()
|
||||
local animOut = button.fadeOut:CreateAnimation("Alpha")
|
||||
animOut:SetOrder(1)
|
||||
animOut:SetDuration(0.2)
|
||||
animOut:SetFromAlpha(1)
|
||||
animOut:SetToAlpha(0)
|
||||
animOut:SetStartDelay(1)
|
||||
button.fadeOut:SetToFinalAlpha(true)
|
||||
end
|
||||
]]
|
||||
end
|
||||
lib:SetButtonRadius(lib.radius) -- Upgrade to 40
|
||||
@@ -0,0 +1,90 @@
|
||||
|
||||
assert(LibStub, "LibDataBroker-1.1 requires LibStub")
|
||||
assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0")
|
||||
|
||||
local lib, oldminor = LibStub:NewLibrary("LibDataBroker-1.1", 4)
|
||||
if not lib then return end
|
||||
oldminor = oldminor or 0
|
||||
|
||||
|
||||
lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib)
|
||||
lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {}
|
||||
local attributestorage, namestorage, callbacks = lib.attributestorage, lib.namestorage, lib.callbacks
|
||||
|
||||
if oldminor < 2 then
|
||||
lib.domt = {
|
||||
__metatable = "access denied",
|
||||
__index = function(self, key) return attributestorage[self] and attributestorage[self][key] end,
|
||||
}
|
||||
end
|
||||
|
||||
if oldminor < 3 then
|
||||
lib.domt.__newindex = function(self, key, value)
|
||||
if not attributestorage[self] then attributestorage[self] = {} end
|
||||
if attributestorage[self][key] == value then return end
|
||||
attributestorage[self][key] = value
|
||||
local name = namestorage[self]
|
||||
if not name then return end
|
||||
callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value, self)
|
||||
callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value, self)
|
||||
callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value, self)
|
||||
callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value, self)
|
||||
end
|
||||
end
|
||||
|
||||
if oldminor < 2 then
|
||||
function lib:NewDataObject(name, dataobj)
|
||||
if self.proxystorage[name] then return end
|
||||
|
||||
if dataobj then
|
||||
assert(type(dataobj) == "table", "Invalid dataobj, must be nil or a table")
|
||||
self.attributestorage[dataobj] = {}
|
||||
for i,v in pairs(dataobj) do
|
||||
self.attributestorage[dataobj][i] = v
|
||||
dataobj[i] = nil
|
||||
end
|
||||
end
|
||||
dataobj = setmetatable(dataobj or {}, self.domt)
|
||||
self.proxystorage[name], self.namestorage[dataobj] = dataobj, name
|
||||
self.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj)
|
||||
return dataobj
|
||||
end
|
||||
end
|
||||
|
||||
if oldminor < 1 then
|
||||
function lib:DataObjectIterator()
|
||||
return pairs(self.proxystorage)
|
||||
end
|
||||
|
||||
function lib:GetDataObjectByName(dataobjectname)
|
||||
return self.proxystorage[dataobjectname]
|
||||
end
|
||||
|
||||
function lib:GetNameByDataObject(dataobject)
|
||||
return self.namestorage[dataobject]
|
||||
end
|
||||
end
|
||||
|
||||
if oldminor < 4 then
|
||||
local next = pairs(attributestorage)
|
||||
function lib:pairs(dataobject_or_name)
|
||||
local t = type(dataobject_or_name)
|
||||
assert(t == "string" or t == "table", "Usage: ldb:pairs('dataobjectname') or ldb:pairs(dataobject)")
|
||||
|
||||
local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
|
||||
assert(attributestorage[dataobj], "Data object not found")
|
||||
|
||||
return next, attributestorage[dataobj], nil
|
||||
end
|
||||
|
||||
local ipairs_iter = ipairs(attributestorage)
|
||||
function lib:ipairs(dataobject_or_name)
|
||||
local t = type(dataobject_or_name)
|
||||
assert(t == "string" or t == "table", "Usage: ldb:ipairs('dataobjectname') or ldb:ipairs(dataobject)")
|
||||
|
||||
local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
|
||||
assert(attributestorage[dataobj], "Data object not found")
|
||||
|
||||
return ipairs_iter, attributestorage[dataobj], 0
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
|
||||
-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
|
||||
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
|
||||
local _G = getfenv()
|
||||
local strfind, strfmt = string.find, string.format
|
||||
local LibStub = _G[LIBSTUB_MAJOR]
|
||||
|
||||
if not LibStub or LibStub.minor < LIBSTUB_MINOR then
|
||||
LibStub = LibStub or { libs = {}, minors = {} }
|
||||
_G[LIBSTUB_MAJOR] = LibStub
|
||||
LibStub.minor = LIBSTUB_MINOR
|
||||
|
||||
function LibStub:NewLibrary(major, minor)
|
||||
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
|
||||
local _,_,num = strfind(minor, "(%d+)")
|
||||
minor = assert(tonumber(num), "Minor version must either be a number or contain a number.")
|
||||
|
||||
local oldminor = self.minors[major]
|
||||
if oldminor and oldminor >= minor then return nil end
|
||||
self.minors[major], self.libs[major] = minor, self.libs[major] or {}
|
||||
return self.libs[major], oldminor
|
||||
end
|
||||
|
||||
function LibStub:GetLibrary(major, silent)
|
||||
if not self.libs[major] and not silent then
|
||||
error(strfmt("Cannot find a library instance of %q.", tostring(major)), 2)
|
||||
end
|
||||
return self.libs[major], self.minors[major]
|
||||
end
|
||||
|
||||
function LibStub:IterateLibraries() return pairs(self.libs) end
|
||||
setmetatable(LibStub, { __call = LibStub.GetLibrary })
|
||||
end
|
||||
@@ -0,0 +1,371 @@
|
||||
local L = AceLibrary("AceLocale-2.2"):new("NampowerSettings")
|
||||
|
||||
L:RegisterTranslations("enUS", function()
|
||||
return {
|
||||
["|cffffcc00Nampower v2|cffffaaaa not present hiding settings."] = true,
|
||||
|
||||
["update available"] = "|cffffcc00Nampower update v2.20.0 available.|cffffcc00 Some settings may be hidden until you update. Replace your existing nampower.dll with the latest from https://gitea.com/avitasia/nampower/releases",
|
||||
|
||||
["Superwow required to display queued spells."] = true,
|
||||
|
||||
["Enable Per Character Settings"] = true,
|
||||
["Whether to use per character settings for all of the NP_ settings. This will cause settings saved in your character's NampowerSettings.lua to override any global settings in Config.wtf."] = true,
|
||||
|
||||
["Queue Cast Time Spells"] = true,
|
||||
["Whether to enable spell queuing for spells with a cast time"] = true,
|
||||
|
||||
["Queue Instant Spells"] = true,
|
||||
["Whether to enable spell queuing for instant cast spells tied to gcd"] = true,
|
||||
|
||||
["Queue On Swing Spells"] = true,
|
||||
["Whether to enable on swing spell queuing"] = true,
|
||||
|
||||
["Queue Channeling Spells"] = true,
|
||||
["Whether to enable channeling spell queuing"] = true,
|
||||
|
||||
["Queue Targeting Spells"] = true,
|
||||
["Whether to enable terrain targeting spell queuing"] = true,
|
||||
|
||||
["Queue Spells Coming Off Cooldown"] = true,
|
||||
["Whether to enable spell queuing for spells coming off cooldown"] = true,
|
||||
|
||||
["Queue Windows"] = true,
|
||||
["How much time in ms you have before a cast ends to queue different types of spells"] = true,
|
||||
|
||||
["Spell Queue Window (ms)"] = true,
|
||||
["The window in ms before a cast finishes where the next will get queued"] = true,
|
||||
|
||||
["On Swing Buffer Cooldown (ms)"] = true,
|
||||
["The cooldown time in ms after an on swing spell before you can queue on swing spells"] = true,
|
||||
|
||||
["Channel Queue Window (ms)"] = true,
|
||||
["The window in ms before a channel finishes where the next will get queued"] = true,
|
||||
|
||||
["Targeting Queue Window (ms)"] = true,
|
||||
["The window in ms before a terrain targeting spell finishes where the next will get queued"] = true,
|
||||
|
||||
["Cooldown Queue Window (ms)"] = true,
|
||||
["The window in ms before a spell coming off cooldown finishes where the next will get queued"] = true,
|
||||
|
||||
["Cast Options"] = true,
|
||||
["Options for controlling casting behavior"] = true,
|
||||
|
||||
["QOL Options"] = true,
|
||||
["Quality of life options to prevent common issues"] = true,
|
||||
["Chat Bubbles"] = true,
|
||||
["Chat bubble options"] = true,
|
||||
["Chat Bubbles (Say/Yell)"] = true,
|
||||
["Whether to enable chat bubbles for /say and /yell messages."] = true,
|
||||
["Chat Bubbles (Party)"] = true,
|
||||
["Whether to enable chat bubbles for /party messages."] = true,
|
||||
["Chat Bubbles (Whisper)"] = true,
|
||||
["Whether to enable chat bubbles for /whisper messages."] = true,
|
||||
["Chat Bubbles (Raid)"] = true,
|
||||
["Whether to enable chat bubbles for /raid messages."] = true,
|
||||
["Chat Bubbles (Battleground)"] = true,
|
||||
["Whether to enable chat bubbles for battleground messages."] = true,
|
||||
["Chat Bubble Distance"] = true,
|
||||
["The distance in yards to show chat bubbles"] = true,
|
||||
|
||||
["Advanced options"] = true,
|
||||
["Collection of various advanced options"] = true,
|
||||
["Spell Events"] = true,
|
||||
["Controls optional spell and combat event CVars."] = true,
|
||||
|
||||
["Double Cast to End Channel Early"] = true,
|
||||
["Whether to allow double casting a spell within 350ms to end channeling on the next tick. Takes into account your ChannelLatencyReductionPercentage."] = true,
|
||||
|
||||
["Quickcast Targeting Spells on Double Cast"] = true,
|
||||
["Allows casting targeting spells by attempting to cast them twice as opposed to the default client behavior which cancels the targeting indicator"] = true,
|
||||
|
||||
["Interrupt Channels Outside Queue Window"] = true,
|
||||
["Whether to allow interrupting channels (the original client behavior) when trying to cast a spell outside the channeling queue window"] = true,
|
||||
|
||||
["Retry Server Rejected Spells"] = true,
|
||||
["Whether to retry spells that are rejected by the server for these reasons: SPELL_FAILED_ITEM_NOT_READY, SPELL_FAILED_NOT_READY, SPELL_FAILED_SPELL_IN_PROGRESS"] = true,
|
||||
|
||||
["Quickcast Targeting Spells"] = true,
|
||||
["Whether to enable quick casting for ALL spells with terrain targeting"] = true,
|
||||
|
||||
["Replace Matching Non GCD Category"] = true,
|
||||
["Whether to replace any queued non gcd spell when a new non gcd spell with the same non zero StartRecoveryCategory is cast. Most trinkets and spells are category 0 which are ignored by this setting. The primary use case is to switch which potion you have queued."] = true,
|
||||
|
||||
["Optimize Buffer Using Packet Timings"] = true,
|
||||
["Whether to attempt to optimize your buffer using your latency and server packet timings"] = true,
|
||||
|
||||
["Minimum Buffer Time (ms)"] = true,
|
||||
["The minimum buffer delay in ms added to each cast"] = true,
|
||||
|
||||
["Non GCD Buffer Time (ms)"] = true,
|
||||
["The buffer delay in ms added AFTER each cast that is not tied to the gcd"] = true,
|
||||
|
||||
["Max Buffer Increase (ms)"] = true,
|
||||
["The maximum amount of time in ms to increase the buffer by when the server rejects a cast"] = true,
|
||||
|
||||
["Channel Latency Reduction (%)"] = true,
|
||||
["The percentage of your latency to subtract from the end of a channel duration to optimize cast time while hopefully not losing any ticks"] = true,
|
||||
|
||||
["Queued Spell Display Options"] = true,
|
||||
["Options for displaying an icon for the queued spell"] = true,
|
||||
|
||||
["Display queued spell icon"] = true,
|
||||
["Whether to display an icon of the queued spell"] = true,
|
||||
|
||||
["Icon size"] = true,
|
||||
["Change the spell icon size"] = true,
|
||||
|
||||
["Allow dragging"] = true,
|
||||
["Whether to allow interaction with the queued spell icon so you can move it around"] = true,
|
||||
|
||||
["Reset Position"] = true,
|
||||
["Reset the position of the queued spell icon"] = true,
|
||||
|
||||
["Show/Hide for positioning"] = true,
|
||||
["Test the queued spell icon and position it to your liking"] = true,
|
||||
|
||||
["Prevent Right Click Target Change"] = true,
|
||||
["Whether to prevent right-clicking from changing your current target when in combat. If you don't have a target right click will still change your target even with this on. This is mainly to prevent accidentally changing targets in combat when trying to adjust your camera."] = true,
|
||||
|
||||
["Nameplate Distance"] = true,
|
||||
["The distance in yards to show nameplates"] = true,
|
||||
|
||||
["Spam Protection"] = true,
|
||||
["Whether to enable spam protection functionality that blocks spamming spells while waiting for the server to respond to your initial cast due to issues spamming can cause"] = true,
|
||||
|
||||
["Prevent Right Click PvP Attack"] = true,
|
||||
["Whether to prevent right-clicking on PvP flagged players to avoid accidental PvP attacks"] = true,
|
||||
|
||||
["Prevent Mounting When Buff Capped"] = true,
|
||||
["Whether to prevent mounting when you have 32 buffs (buff capped) and are not already mounted. This prevents the issue where you mount but cannot dismount because the mount aura fails to apply due to the buff cap. When blocked, displays an error message."] = true,
|
||||
|
||||
["Enable Enhanced Tooltips"] = true,
|
||||
["Whether to enable nampower's tooltip additions such as spell proc chance, ICD, and item cooldown text."] = true,
|
||||
|
||||
["Enable Aura Cast Events"] = true,
|
||||
["Whether to enable AURA_CAST_ON_SELF and AURA_CAST_ON_OTHER events."] = true,
|
||||
|
||||
["Enable Auto Attack Events"] = true,
|
||||
["Whether to enable AUTO_ATTACK_SELF and AUTO_ATTACK_OTHER events."] = true,
|
||||
|
||||
["Enable Spell Start Events"] = true,
|
||||
["Whether to enable SPELL_START_SELF and SPELL_START_OTHER events."] = true,
|
||||
|
||||
["Enable Spell Go Events"] = true,
|
||||
["Whether to enable SPELL_GO_SELF and SPELL_GO_OTHER events."] = true,
|
||||
|
||||
["Enable Spell Heal Events"] = true,
|
||||
["Whether to enable SPELL_HEAL_BY_SELF, SPELL_HEAL_BY_OTHER, and SPELL_HEAL_ON_SELF events."] = true,
|
||||
|
||||
["Enable Spell Energize Events"] = true,
|
||||
["Whether to enable SPELL_ENERGIZE_BY_SELF, SPELL_ENERGIZE_BY_OTHER, and SPELL_ENERGIZE_ON_SELF events."] = true,
|
||||
|
||||
["Unit Event Filters"] = true,
|
||||
["Controls which unit identifiers trigger unit events (UNIT_HEALTH, UNIT_COMBAT, etc.). In the base game the same event can fire multiple times for the same unit, once per identifying string (e.g. 'party1', 'raid1', 'mouseover'). Disabling unused identifiers reduces redundant event calls. Note: 'player', 'target', and 'pet' (your own pet) always trigger regardless of these settings, as they are critical."] = true,
|
||||
|
||||
["Enable Pet Unit Events"] = true,
|
||||
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for party and raid pet identifiers ('party1pet', 'raid1pet', etc.). Party pets additionally require Enable Party Unit Events to be on; raid pets additionally require Enable Raid Unit Events to be on. Your own pet ('pet') always fires events regardless of this setting."] = true,
|
||||
|
||||
["Enable Party Unit Events"] = true,
|
||||
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for party member identifiers ('party1', 'party2', 'party3', 'party4'). Also required alongside Enable Pet Unit Events for party pet identifiers to fire."] = true,
|
||||
|
||||
["Enable Raid Unit Events"] = true,
|
||||
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for raid member identifiers ('raid1' through 'raid40'). Also required alongside Enable Pet Unit Events for raid pet identifiers to fire."] = true,
|
||||
|
||||
["Enable Mouseover Unit Events"] = true,
|
||||
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for the 'mouseover' unit identifier."] = true,
|
||||
|
||||
["Enable GUID Unit Events"] = true,
|
||||
["Whether to fire unit events (UNIT_HEALTH, UNIT_MANA, UNIT_AURA, etc.) using the raw GUID as the unit token, mirroring SuperWoW behavior. Fires for every unit the client tracks — not just named tokens like 'player', 'party1', 'raid1' — which can cause significant event spam in raids, BGs, and crowded zones. Older addons (e.g. pfUI) written for standard named tokens may have performance issues receiving GUID-based events. Addons needing GUID tracking (e.g. Automarker, Cursive) should use the new dedicated UNIT_HEALTH_GUID, UNIT_MANA_GUID, etc. events instead, allowing this to be safely disabled."] = true,
|
||||
|
||||
["Enable GUID Unit Event Filtering"] = true,
|
||||
["When enabled, suppresses high-frequency GUID events that cause spam in older addons — specifically UNIT_AURA, UNIT_HEALTH, UNIT_MANA, and similar events below UNIT_COMBAT, plus UNIT_NAME_UPDATE, UNIT_PORTRAIT_UPDATE, UNIT_INVENTORY_CHANGED, and PLAYER_GUILD_UPDATE. UNIT_COMBAT_GUID and other less frequent GUID events are still fired. Has no effect if Enable GUID Unit Events is disabled. This is a direct replacement for the 'Filter GUID Events' option in PerfBoost."] = true,
|
||||
|
||||
["Preserve Greater Demon Autocast"] = true,
|
||||
["Whether to remember and restore Felguard/Doomguard autocast preferences when swapping or resummoning those greater demons."] = true,
|
||||
}
|
||||
end)
|
||||
|
||||
L:RegisterTranslations("zhCN", function()
|
||||
return {
|
||||
["|cffffcc00Nampower v2|cffffaaaa not present hiding settings."] = "|cffffcc00Nampower v2|cffffaaaa 未安装,隐藏设置项。",
|
||||
|
||||
["update available"] = "|cffffcc00Nampower v2.20.0 更新可用。|cffffcc00 更新前部分设置可能隐藏。请从 https://gitea.com/avitasia/nampower/releases 下载最新 nampower.dll 替换当前版本。",
|
||||
|
||||
["Superwow required to display queued spells."] = "需 Superwow 插件显示队列中法术。",
|
||||
|
||||
["Enable Per Character Settings"] = "启用角色独立设置",
|
||||
["Whether to use per character settings for all of the NP_ settings. This will cause settings saved in your character's NampowerSettings.lua to override any global settings in Config.wtf."] = "是否为所有 NP_ 设置启用角色独立配置。启用后,角色 NampowerSettings.lua 中的设置将覆盖 Config.wtf 中的全局设置。",
|
||||
|
||||
["Queue Cast Time Spells"] = "队列施法时间法术",
|
||||
["Whether to enable spell queuing for spells with a cast time"] = "是否对施法时间类法术启用队列功能",
|
||||
|
||||
["Queue Instant Spells"] = "队列瞬发法术",
|
||||
["Whether to enable spell queuing for instant cast spells tied to gcd"] = "是否对涉及公共冷却的瞬发法术启用队列功能",
|
||||
|
||||
["Queue On Swing Spells"] = "队列特殊攻击法术",
|
||||
["Whether to enable on swing spell queuing"] = "是否对特殊攻击类法术(如战士压制)启用队列功能",
|
||||
|
||||
["Queue Channeling Spells"] = "队列引导法术",
|
||||
["Whether to enable channeling spell queuing"] = "是否对引导类法术(如暴风雪)启用队列功能",
|
||||
|
||||
["Queue Targeting Spells"] = "队列目标区域法术",
|
||||
["Whether to enable terrain targeting spell queuing"] = "是否对目标区域类法术(如暴风雪)启用队列功能",
|
||||
|
||||
["Queue Spells Coming Off Cooldown"] = "队列冷却结束法术",
|
||||
["Whether to enable spell queuing for spells coming off cooldown"] = "是否对即将结束冷却的法术启用队列功能",
|
||||
|
||||
["Queue Windows"] = "队列窗口设置",
|
||||
["How much time in ms you have before a cast ends to queue different types of spells"] = "各种法术类型在施法结束前多少毫秒(ms)开始进入队列",
|
||||
|
||||
["Spell Queue Window (ms)"] = "法术队列窗口(ms)",
|
||||
["The window in ms before a cast finishes where the next will get queued"] = "当前施法结束前多少毫秒(ms)可将下一个法术加入队列",
|
||||
|
||||
["On Swing Buffer Cooldown (ms)"] = "特殊攻击缓冲冷却(ms)",
|
||||
["The cooldown time in ms after an on swing spell before you can queue on swing spells"] = "特殊攻击法术施放后,多少毫秒(ms)内无法再次将同类法术加入队列",
|
||||
|
||||
["Channel Queue Window (ms)"] = "引导法术队列窗口(ms)",
|
||||
["The window in ms before a channel finishes where the next will get queued"] = "引导法术结束前多少毫秒(ms)可将下一个法术加入队列",
|
||||
|
||||
["Targeting Queue Window (ms)"] = "目标区域法术队列窗口(ms)",
|
||||
["The window in ms before a terrain targeting spell finishes where the next will get queued"] = "目标区域法术结束前多少毫秒(ms)可将下一个法术加入队列",
|
||||
|
||||
["Cooldown Queue Window (ms)"] = "冷却结束队列窗口(ms)",
|
||||
["The window in ms before a spell coming off cooldown finishes where the next will get queued"] = "法术冷却结束前多少毫秒(ms)可将该法术加入队列",
|
||||
|
||||
["Cast Options"] = "施法选项",
|
||||
["Options for controlling casting behavior"] = "控制施法行为的选项",
|
||||
|
||||
["QOL Options"] = "便利性选项",
|
||||
["Quality of life options to prevent common issues"] = "用于防止常见错误的便利性选项",
|
||||
["Chat Bubbles"] = "聊天气泡",
|
||||
["Chat bubble options"] = "聊天气泡相关选项",
|
||||
["Chat Bubbles (Say/Yell)"] = "聊天气泡(说话/大喊)",
|
||||
["Whether to enable chat bubbles for /say and /yell messages."] = "是否为 /say 与 /yell 消息启用聊天气泡。",
|
||||
["Chat Bubbles (Party)"] = "聊天气泡(队伍)",
|
||||
["Whether to enable chat bubbles for /party messages."] = "是否为 /party 消息启用聊天气泡。",
|
||||
["Chat Bubbles (Whisper)"] = "聊天气泡(密语)",
|
||||
["Whether to enable chat bubbles for /whisper messages."] = "是否为 /whisper 消息启用聊天气泡。",
|
||||
["Chat Bubbles (Raid)"] = "聊天气泡(团队)",
|
||||
["Whether to enable chat bubbles for /raid messages."] = "是否为 /raid 消息启用聊天气泡。",
|
||||
["Chat Bubbles (Battleground)"] = "聊天气泡(战场)",
|
||||
["Whether to enable chat bubbles for battleground messages."] = "是否为战场消息启用聊天气泡。",
|
||||
["Chat Bubble Distance"] = "聊天气泡距离",
|
||||
["The distance in yards to show chat bubbles"] = "显示聊天气泡的最大距离(码)",
|
||||
|
||||
["Advanced options"] = "高级选项",
|
||||
["Collection of various advanced options"] = "各类高级选项集合",
|
||||
["Spell Events"] = "法术事件",
|
||||
["Controls optional spell and combat event CVars."] = "控制可选的法术和战斗事件 CVar。",
|
||||
|
||||
["Double Cast to End Channel Early"] = "双重施法提前结束引导",
|
||||
["Whether to allow double casting a spell within 350ms to end channeling on the next tick. Takes into account your ChannelLatencyReductionPercentage."] = "是否允许350毫秒内双重施法以在下一个伤害周期提前结束引导。受引导延迟削减比例影响。",
|
||||
|
||||
["Quickcast Targeting Spells on Double Cast"] = "双重施法快速施放目标区域法术",
|
||||
["Allows casting targeting spells by attempting to cast them twice as opposed to the default client behavior which cancels the targeting indicator"] = "允许通过双次尝试施法来释放目标区域法术,而非默认客户端行为(取消目标指示器)",
|
||||
|
||||
["Interrupt Channels Outside Queue Window"] = "队列窗口外允许打断引导",
|
||||
["Whether to allow interrupting channels (the original client behavior) when trying to cast a spell outside the channeling queue window"] = "在引导法术队列窗口外尝试施法时,是否允许打断引导(默认客户端行为)",
|
||||
|
||||
["Retry Server Rejected Spells"] = "重试服务器拒绝的法术",
|
||||
["Whether to retry spells that are rejected by the server for these reasons: SPELL_FAILED_ITEM_NOT_READY, SPELL_FAILED_NOT_READY, SPELL_FAILED_SPELL_IN_PROGRESS"] = "是否对因以下原因被服务器拒绝的法术进行重试:未准备就绪、未冷却、已有法术施放中",
|
||||
|
||||
["Quickcast Targeting Spells"] = "快速施放目标区域法术",
|
||||
["Whether to enable quick casting for ALL spells with terrain targeting"] = "是否为所有目标区域法术启用快速施放功能",
|
||||
|
||||
["Replace Matching Non GCD Category"] = "替换同类型非公共冷却法术",
|
||||
["Whether to replace any queued non gcd spell when a new non gcd spell with the same non zero StartRecoveryCategory is cast. Most trinkets and spells are category 0 which are ignored by this setting. The primary use case is to switch which potion you have queued."] = "当施放新的非公共冷却法术且其启动类别相同时,是否替换队列中的同类法术。饰品和多数法术属类别0(不受影响),主要用于替换已队列的药水。",
|
||||
|
||||
["Optimize Buffer Using Packet Timings"] = "基于封包时间优化缓冲",
|
||||
["Whether to attempt to optimize your buffer using your latency and server packet timings"] = "是否根据延迟和服务器封包时间优化法术缓冲机制",
|
||||
|
||||
["Minimum Buffer Time (ms)"] = "最低缓冲时间(ms)",
|
||||
["The minimum buffer delay in ms added to each cast"] = "为每次施法添加的最小缓冲延迟(毫秒)",
|
||||
|
||||
["Non GCD Buffer Time (ms)"] = "非公共冷却缓冲时间(ms)",
|
||||
["The buffer delay in ms added AFTER each cast that is not tied to the gcd"] = "在非公共冷却法术施放后添加的缓冲延迟(毫秒)",
|
||||
|
||||
["Max Buffer Increase (ms)"] = "最大缓冲增加量(ms)",
|
||||
["The maximum amount of time in ms to increase the buffer by when the server rejects a cast"] = "当法术被服务器拒绝时,缓冲时间可增加的最大毫秒数",
|
||||
|
||||
["Channel Latency Reduction (%)"] = "引导延迟削减比例(%)",
|
||||
["The percentage of your latency to subtract from the end of a channel duration to optimize cast time while hopefully not losing any ticks"] = "为优化施法时间,从引导结束时序中扣除延迟的百分比(避免丢失伤害周期)",
|
||||
|
||||
["Queued Spell Display Options"] = "队列法术显示选项",
|
||||
["Options for displaying an icon for the queued spell"] = "队列中法术图标的显示设置",
|
||||
|
||||
["Display queued spell icon"] = "显示队列法术图标",
|
||||
["Whether to display an icon of the queued spell"] = "是否显示队列中法术的图标",
|
||||
|
||||
["Icon size"] = "图标尺寸",
|
||||
["Change the spell icon size"] = "修改法术图标大小",
|
||||
|
||||
["Allow dragging"] = "允许拖动",
|
||||
["Whether to allow interaction with the queued spell icon so you can move it around"] = "是否允许通过拖动交互调整队列法术图标位置",
|
||||
|
||||
["Reset Position"] = "重置位置",
|
||||
["Reset the position of the queued spell icon"] = "重置队列法术图标位置",
|
||||
|
||||
["Show/Hide for positioning"] = "显示/隐藏(用于定位)",
|
||||
["Test the queued spell icon and position it to your liking"] = "测试队列法术图标位置,按需调整",
|
||||
|
||||
["Prevent Right Click Target Change"] = "阻止右键切换目标",
|
||||
["Whether to prevent right-clicking from changing your current target when in combat. If you don't have a target right click will still change your target even with this on. This is mainly to prevent accidentally changing targets in combat when trying to adjust your camera."] = "战斗中右键点击是否不改变当前目标(无目标时仍可切换)。主要用于调整镜头时避免误切目标。",
|
||||
|
||||
["Nameplate Distance"] = "姓名板显示距离",
|
||||
["The distance in yards to show nameplates"] = "显示姓名板的最大距离(码)",
|
||||
|
||||
["Spam Protection"] = "防连点保护",
|
||||
["Whether to enable spam protection functionality that blocks spamming spells while waiting for the server to respond to your initial cast due to issues spamming can cause"] = "是否启用防连点保护功能,避免在等待服务器响应期间连点施法导致的问题",
|
||||
|
||||
["Prevent Right Click PvP Attack"] = "阻止右键PvP攻击",
|
||||
["Whether to prevent right-clicking on PvP flagged players to avoid accidental PvP attacks"] = "是否防止右键点击PvP标记玩家以避免误触发PvP攻击",
|
||||
|
||||
["Prevent Mounting When Buff Capped"] = "Buff满时阻止上坐骑",
|
||||
["Whether to prevent mounting when you have 32 buffs (buff capped) and are not already mounted. This prevents the issue where you mount but cannot dismount because the mount aura fails to apply due to the buff cap. When blocked, displays an error message."] = "当已有32个增益(Buff已满)且未在坐骑状态时,是否阻止上坐骑,以避免坐骑光环因Buff上限无法施加导致无法下马的问题。被阻止时会显示错误提示。",
|
||||
|
||||
["Enable Enhanced Tooltips"] = "启用增强工具提示",
|
||||
["Whether to enable nampower's tooltip additions such as spell proc chance, ICD, and item cooldown text."] = "是否启用 nampower 的工具提示增强内容,例如法术触发几率、内置冷却时间(ICD)和物品冷却文本。",
|
||||
|
||||
["Enable Aura Cast Events"] = "启用光环施放事件",
|
||||
["Whether to enable AURA_CAST_ON_SELF and AURA_CAST_ON_OTHER events."] = "是否启用 AURA_CAST_ON_SELF 和 AURA_CAST_ON_OTHER 事件。",
|
||||
|
||||
["Enable Auto Attack Events"] = "启用自动攻击事件",
|
||||
["Whether to enable AUTO_ATTACK_SELF and AUTO_ATTACK_OTHER events."] = "是否启用 AUTO_ATTACK_SELF 和 AUTO_ATTACK_OTHER 事件。",
|
||||
|
||||
["Enable Spell Start Events"] = "启用法术开始事件",
|
||||
["Whether to enable SPELL_START_SELF and SPELL_START_OTHER events."] = "是否启用 SPELL_START_SELF 和 SPELL_START_OTHER 事件。",
|
||||
|
||||
["Enable Spell Go Events"] = "启用法术释放事件",
|
||||
["Whether to enable SPELL_GO_SELF and SPELL_GO_OTHER events."] = "是否启用 SPELL_GO_SELF 和 SPELL_GO_OTHER 事件。",
|
||||
|
||||
["Enable Spell Heal Events"] = "启用法术治疗事件",
|
||||
["Whether to enable SPELL_HEAL_BY_SELF, SPELL_HEAL_BY_OTHER, and SPELL_HEAL_ON_SELF events."] = "是否启用 SPELL_HEAL_BY_SELF、SPELL_HEAL_BY_OTHER 和 SPELL_HEAL_ON_SELF 事件。",
|
||||
|
||||
["Enable Spell Energize Events"] = "启用法术回能事件",
|
||||
["Whether to enable SPELL_ENERGIZE_BY_SELF, SPELL_ENERGIZE_BY_OTHER, and SPELL_ENERGIZE_ON_SELF events."] = "是否启用 SPELL_ENERGIZE_BY_SELF、SPELL_ENERGIZE_BY_OTHER 和 SPELL_ENERGIZE_ON_SELF 事件。",
|
||||
|
||||
["Unit Event Filters"] = "单位事件过滤器",
|
||||
["Controls which unit identifiers trigger unit events (UNIT_HEALTH, UNIT_COMBAT, etc.). In the base game the same event can fire multiple times for the same unit, once per identifying string (e.g. 'party1', 'raid1', 'mouseover'). Disabling unused identifiers reduces redundant event calls. Note: 'player', 'target', and 'pet' (your own pet) always trigger regardless of these settings, as they are critical."] = "控制哪些单位标识符触发单位事件(UNIT_HEALTH、UNIT_COMBAT 等)。在原版游戏中,同一单位可能因多个标识符(如 'party1'、'raid1'、'mouseover')重复触发同一事件。禁用不需要的标识符可减少冗余事件调用。注意:'player'(自身)、'target'(目标)和 'pet'(自己的宠物)始终会触发,不受此处设置影响,因为它们是关键标识符。",
|
||||
|
||||
["Enable Pet Unit Events"] = "启用宠物单位事件",
|
||||
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for party and raid pet identifiers ('party1pet', 'raid1pet', etc.). Party pets additionally require Enable Party Unit Events to be on; raid pets additionally require Enable Raid Unit Events to be on. Your own pet ('pet') always fires events regardless of this setting."] = "是否为队伍和团队宠物标识符('party1pet'、'raid1pet' 等)触发单位事件(UNIT_HEALTH、UNIT_COMBAT 等)。队伍宠物还需同时开启「启用队伍单位事件」;团队宠物还需同时开启「启用团队单位事件」。自己的宠物('pet')始终触发事件,不受此设置影响。",
|
||||
|
||||
["Enable Party Unit Events"] = "启用队伍单位事件",
|
||||
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for party member identifiers ('party1', 'party2', 'party3', 'party4'). Also required alongside Enable Pet Unit Events for party pet identifiers to fire."] = "是否为队伍成员标识符('party1'、'party2'、'party3'、'party4')触发单位事件(UNIT_HEALTH、UNIT_COMBAT 等)。同时也是队伍宠物标识符触发的必要条件(需与「启用宠物单位事件」共同开启)。",
|
||||
|
||||
["Enable Raid Unit Events"] = "启用团队单位事件",
|
||||
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for raid member identifiers ('raid1' through 'raid40'). Also required alongside Enable Pet Unit Events for raid pet identifiers to fire."] = "是否为团队成员标识符('raid1' 至 'raid40')触发单位事件(UNIT_HEALTH、UNIT_COMBAT 等)。同时也是团队宠物标识符触发的必要条件(需与「启用宠物单位事件」共同开启)。",
|
||||
|
||||
["Enable Mouseover Unit Events"] = "启用鼠标悬停单位事件",
|
||||
["Whether to fire unit events (UNIT_HEALTH, UNIT_COMBAT, etc.) for the 'mouseover' unit identifier."] = "是否为 'mouseover'(鼠标悬停)单位标识符触发单位事件(UNIT_HEALTH、UNIT_COMBAT 等)。",
|
||||
|
||||
["Enable GUID Unit Events"] = "启用GUID单位事件",
|
||||
["Whether to fire unit events (UNIT_HEALTH, UNIT_MANA, UNIT_AURA, etc.) using the raw GUID as the unit token, mirroring SuperWoW behavior. Fires for every unit the client tracks — not just named tokens like 'player', 'party1', 'raid1' — which can cause significant event spam in raids, BGs, and crowded zones. Older addons (e.g. pfUI) written for standard named tokens may have performance issues receiving GUID-based events. Addons needing GUID tracking (e.g. Automarker, Cursive) should use the new dedicated UNIT_HEALTH_GUID, UNIT_MANA_GUID, etc. events instead, allowing this to be safely disabled."] = "是否使用原始 GUID 作为单位标识符触发单位事件(UNIT_HEALTH、UNIT_MANA、UNIT_AURA 等),模拟 SuperWoW 行为。此功能会对客户端跟踪的所有单位触发事件——不仅限于 'player'、'party1'、'raid1' 等命名标识符——在团队、战场和人口密集区域可能产生大量事件。为标准命名标识符设计的旧版插件(如 pfUI)在收到 GUID 事件时可能出现性能问题。需要 GUID 跟踪的插件(如 Automarker、Cursive)应改用专用的 UNIT_HEALTH_GUID、UNIT_MANA_GUID 等事件,届时可安全禁用此选项。",
|
||||
|
||||
["Enable GUID Unit Event Filtering"] = "启用GUID单位事件过滤",
|
||||
["When enabled, suppresses high-frequency GUID events that cause spam in older addons — specifically UNIT_AURA, UNIT_HEALTH, UNIT_MANA, and similar events below UNIT_COMBAT, plus UNIT_NAME_UPDATE, UNIT_PORTRAIT_UPDATE, UNIT_INVENTORY_CHANGED, and PLAYER_GUILD_UPDATE. UNIT_COMBAT_GUID and other less frequent GUID events are still fired. Has no effect if Enable GUID Unit Events is disabled. This is a direct replacement for the 'Filter GUID Events' option in PerfBoost."] = "启用后,将屏蔽对旧版插件造成刷屏的高频 GUID 事件——具体包括 UNIT_COMBAT 以下的事件(如 UNIT_AURA、UNIT_HEALTH、UNIT_MANA 等),以及 UNIT_NAME_UPDATE、UNIT_PORTRAIT_UPDATE、UNIT_INVENTORY_CHANGED 和 PLAYER_GUILD_UPDATE。UNIT_COMBAT_GUID 及其他低频 GUID 事件仍会正常触发。若已禁用「启用GUID单位事件」,则此设置无效。此选项直接替代 PerfBoost 中的「过滤GUID事件」功能。",
|
||||
|
||||
["Preserve Greater Demon Autocast"] = "保留大型恶魔自动施法",
|
||||
["Whether to remember and restore Felguard/Doomguard autocast preferences when swapping or resummoning those greater demons."] = "切换或重新召唤地狱守卫/末日守卫时,是否记忆并恢复其自动施法设置。",
|
||||
}
|
||||
end)
|
||||
@@ -0,0 +1,27 @@
|
||||
## Interface: 11200
|
||||
## Title: Nampower Settings
|
||||
## Notes: the companion compatibility addon to the mod of the same name.
|
||||
## Title-zhCN:|CFFEE7600[模组增强]|R |cff00ffffNampower设置|R|cffff1450[需Nampower模组]
|
||||
## Notes-zhCN: Nampower模组配套的设置工具,能够根据自己的网络和需要设置该模组内部的各种参数。
|
||||
## SavedVariablesPerCharacter: NampowerSettingsDB
|
||||
## X-Embeds: Ace2, FuBarPlugin-2.0, CompostLib, DewDropLib, TabletLib
|
||||
|
||||
libs\AceLibrary\AceLibrary.lua
|
||||
|
||||
libs\AceOO-2.0\AceOO-2.0.lua
|
||||
libs\AceAddon-2.0\AceAddon-2.0.lua
|
||||
libs\AceConsole-2.0\AceConsole-2.0.lua
|
||||
libs\AceHook-2.1\AceHook-2.1.lua
|
||||
libs\AceDB-2.0\AceDB-2.0.lua
|
||||
libs\AceDebug-2.0\AceDebug-2.0.lua
|
||||
libs\AceEvent-2.0\AceEvent-2.0.lua
|
||||
libs\AceLocale-2.2\AceLocale-2.2.lua
|
||||
libs\AceModuleCore-2.0\AceModuleCore-2.0.lua
|
||||
|
||||
libs\Dewdrop\Dewdrop-2.0.lua
|
||||
libs\Tablet\Tablet-2.0.lua
|
||||
libs\FuBarPlugin\FuBarPlugin-2.0.lua
|
||||
|
||||
Localization.lua
|
||||
settings.lua
|
||||
helper.lua
|
||||
@@ -0,0 +1,12 @@
|
||||
### Nampower settings
|
||||
See https://gitea.com/avitasia/nampower or in game tooltip for description of each setting.
|
||||
Access settings in game by right clicking the minimap icon.
|
||||
|
||||
### Per Character settings
|
||||
You can now use different nampower settings for each character. When you enable this option it will save all of your current nampower settings into
|
||||
WTF\Account\YOUR_ACCOUNT\Nordanaar\YOUR_CHARACTER\SavedVariables\NampowerSettings.lua. Then every time the game loads the settings in that file will overwrite both the nampower defaults and whatever cvars were saved in your main Config.wtf. If you make changes to any settings while this option is enabled it will save to that character's settings file AS WELL AS the main Config.wtf.
|
||||
|
||||
#### Queued spell icon display
|
||||
If you have superwow there is an option (default off) to display an icon for the most recently queued spell (the mini fireball in the castbar). You can resize and position it anywhere.
|
||||
|
||||

|
||||
@@ -0,0 +1,952 @@
|
||||
if not Nampower then
|
||||
return
|
||||
end
|
||||
|
||||
if not Nampower:HasMinimumVersion(2, 14, 0) then
|
||||
return
|
||||
end
|
||||
|
||||
local function GetSpellMod(num)
|
||||
local modifierTypes = {
|
||||
[0] = "DAMAGE",
|
||||
[1] = "DURATION",
|
||||
[2] = "THREAT",
|
||||
[3] = "ATTACK_POWER",
|
||||
[4] = "CHARGES",
|
||||
[5] = "RANGE",
|
||||
[6] = "RADIUS",
|
||||
[7] = "CRITICAL_CHANCE",
|
||||
[8] = "ALL_EFFECTS",
|
||||
[9] = "NOT_LOSE_CASTING_TIME",
|
||||
[10] = "CASTING_TIME",
|
||||
[11] = "COOLDOWN",
|
||||
[12] = "SPEED",
|
||||
[14] = "COST",
|
||||
[15] = "CRIT_DAMAGE_BONUS",
|
||||
[16] = "RESIST_MISS_CHANCE",
|
||||
[17] = "JUMP_TARGETS",
|
||||
[18] = "CHANCE_OF_SUCCESS",
|
||||
[19] = "ACTIVATION_TIME",
|
||||
[20] = "EFFECT_PAST_FIRST",
|
||||
[21] = "CASTING_TIME_OLD",
|
||||
[22] = "DOT",
|
||||
[23] = "HASTE",
|
||||
[24] = "SPELL_BONUS_DAMAGE",
|
||||
[27] = "MULTIPLE_VALUE",
|
||||
[28] = "RESIST_DISPEL_CHANCE"
|
||||
}
|
||||
return modifierTypes[num] or "UNKNOWN"
|
||||
end
|
||||
|
||||
function PrintTable(tbl, indent)
|
||||
indent = indent or 0
|
||||
local prefix = string.rep(" ", indent)
|
||||
|
||||
for k, v in pairs(tbl) do
|
||||
if type(v) == "table" then
|
||||
print(prefix .. tostring(k) .. ":")
|
||||
PrintTable(v, indent + 1)
|
||||
else
|
||||
print(prefix .. tostring(k) .. ": " .. tostring(v))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if CombatLogAdd then
|
||||
function PrintTableToCombatLog(tbl, indent)
|
||||
indent = indent or 0
|
||||
local prefix = string.rep(" ", indent)
|
||||
|
||||
for k, v in pairs(tbl) do
|
||||
if type(v) == "table" then
|
||||
CombatLogAdd(prefix .. tostring(k) .. ":")
|
||||
PrintTableToCombatLog(v, indent + 1)
|
||||
else
|
||||
CombatLogAdd(prefix .. tostring(k) .. ": " .. tostring(v))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function LogUnitData(unit)
|
||||
local data = GetUnitData(unit)
|
||||
if data then
|
||||
PrintTableToCombatLog(data)
|
||||
else
|
||||
print("No unit data available")
|
||||
end
|
||||
end
|
||||
|
||||
function LogSpellData(spellId)
|
||||
local data = GetSpellRec(spellId)
|
||||
if data then
|
||||
PrintTableToCombatLog(data)
|
||||
else
|
||||
print("No spell data available")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PrintBuffs(unit)
|
||||
for i = 1, 32 do
|
||||
local texture, stacks, spellId = UnitBuff(unit, i)
|
||||
if not texture then
|
||||
break
|
||||
end
|
||||
print(tostring(i) .. " " .. texture .. " " .. tostring(spellId))
|
||||
end
|
||||
end
|
||||
|
||||
function PrintAuras(unit)
|
||||
local auras = GetUnitField(unit, "aura")
|
||||
local applications = GetUnitField(unit, "auraApplications")
|
||||
if not auras then return end
|
||||
for i, auraId in ipairs(auras) do
|
||||
local stacks = -1
|
||||
if applications then
|
||||
local stacks = (applications[i] or 0) + 1
|
||||
end
|
||||
if auraId > 0 then
|
||||
print(tostring(i) .. " " .. tostring(auraId) .. " x" .. tostring(stacks))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PrintUnitData(unit)
|
||||
local data = GetUnitData(unit)
|
||||
if data then
|
||||
PrintTable(data)
|
||||
else
|
||||
print("No unit data available")
|
||||
end
|
||||
end
|
||||
|
||||
function PrintSpellMods(spellId)
|
||||
for i = 0, 28 do
|
||||
local flatMod, percentMod, ret = GetSpellModifiers(spellId, i)
|
||||
if ret > 0 then
|
||||
print(GetSpellMod(i) .. " (" .. tostring(i) .. "): flat:" .. tostring(flatMod) .. " percent:" .. tostring(percentMod))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PrintSpellData(spellId)
|
||||
local data = GetSpellRec(spellId)
|
||||
if data then
|
||||
PrintTable(data)
|
||||
else
|
||||
print("No spell data available")
|
||||
end
|
||||
end
|
||||
|
||||
if not Nampower:HasMinimumVersion(2, 16, 0) then
|
||||
return
|
||||
end
|
||||
|
||||
function PrintItemLocation(itemNameOrId)
|
||||
local bagIndex, slot = FindPlayerItemSlot(itemNameOrId)
|
||||
print(tostring(bagIndex) .. " " .. tostring(slot))
|
||||
end
|
||||
|
||||
function PrintEquippedItems()
|
||||
local target = "player"
|
||||
if UnitExists("target") then
|
||||
target = "target"
|
||||
end
|
||||
local data = GetEquippedItems(target)
|
||||
PrintTable(data)
|
||||
end
|
||||
|
||||
function LogEquippedItems()
|
||||
local target = "player"
|
||||
if UnitExists("target") then
|
||||
target = "target"
|
||||
end
|
||||
local data = GetEquippedItems(target)
|
||||
PrintTableToCombatLog(data)
|
||||
end
|
||||
|
||||
function PrintBagItems()
|
||||
local data = GetBagItems()
|
||||
PrintTable(data)
|
||||
end
|
||||
|
||||
function LogBagItems()
|
||||
local data = GetBagItems()
|
||||
PrintTableToCombatLog(data)
|
||||
end
|
||||
|
||||
function TestBagItems(bagIndex)
|
||||
local items = GetBagItems(bagIndex)
|
||||
local numItemsChecked = 0
|
||||
for slot, itemData in pairs(items) do
|
||||
local itemLink = GetContainerItemLink(bagIndex, slot)
|
||||
if not itemLink then
|
||||
print(tostring(bagIndex) .. " " .. tostring(slot))
|
||||
PrintTable(itemData)
|
||||
end
|
||||
local _, _, itemId = strfind(itemLink, "(%d+):")
|
||||
numItemsChecked = numItemsChecked + 1
|
||||
if (tostring(itemData.itemId) ~= itemId) then
|
||||
print("Item mismatch at " .. tostring(bagIndex) .. " " .. tostring(slot) .. " " .. tostring(itemData.itemId) .. " " .. tostring(itemId))
|
||||
print(itemLink)
|
||||
end
|
||||
end
|
||||
print("checked " .. tostring(numItemsChecked) .. " items")
|
||||
end
|
||||
|
||||
function TestAllBagItems()
|
||||
local data = GetBagItems()
|
||||
local numItemsChecked = 0
|
||||
for bagIndex, items in pairs(data) do
|
||||
for slot, itemData in pairs(items) do
|
||||
local itemLink = GetContainerItemLink(bagIndex, slot)
|
||||
if not itemLink then
|
||||
print(tostring(bagIndex) .. " " .. tostring(slot))
|
||||
PrintTable(itemData)
|
||||
end
|
||||
local _, _, itemId = strfind(itemLink, "(%d+):")
|
||||
numItemsChecked = numItemsChecked + 1
|
||||
if (tostring(itemData.itemId) ~= itemId) then
|
||||
print("Item mismatch at " .. tostring(bagIndex) .. " " .. tostring(slot) .. " " .. tostring(itemData.itemId) .. " " .. tostring(itemId))
|
||||
print(itemLink)
|
||||
end
|
||||
end
|
||||
end
|
||||
print("checked " .. tostring(numItemsChecked) .. " items")
|
||||
end
|
||||
|
||||
function TestTrinkets()
|
||||
local data = GetTrinkets()
|
||||
for index, trinketData in pairs(data) do
|
||||
local bagIndex = trinketData.bagIndex
|
||||
local slot = trinketData.slotIndex
|
||||
local itemLink
|
||||
if bagIndex then
|
||||
itemLink = GetContainerItemLink(bagIndex, slot)
|
||||
else
|
||||
itemLink = GetInventoryItemLink("player", slot)
|
||||
end
|
||||
local _, _, itemId = strfind(itemLink, "(%d+):")
|
||||
if (tostring(trinketData.itemId) ~= itemId) then
|
||||
print("Trinket mismatch at " .. tostring(bagIndex) .. " " .. tostring(slot) .. " " .. tostring(itemData.itemId) .. " " .. tostring(itemId))
|
||||
print(itemLink)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not Nampower:HasMinimumVersion(2, 17, 0) then
|
||||
return
|
||||
end
|
||||
|
||||
function PrintCastInfo()
|
||||
local data = GetCastInfo()
|
||||
if data then
|
||||
PrintTable(data)
|
||||
end
|
||||
end
|
||||
|
||||
function PrintSpellCooldown(spellId)
|
||||
local data = GetSpellIdCooldown(spellId)
|
||||
if data then
|
||||
PrintTable(data)
|
||||
end
|
||||
end
|
||||
|
||||
function PrintItemCooldown(itemId)
|
||||
local data = GetItemIdCooldown(itemId)
|
||||
if data then
|
||||
PrintTable(data)
|
||||
end
|
||||
end
|
||||
|
||||
function PrintTrinketCooldown(trinketId)
|
||||
local data = GetTrinketCooldown(trinketId)
|
||||
if data then
|
||||
PrintTable(data)
|
||||
end
|
||||
end
|
||||
|
||||
function PrintTrinkets()
|
||||
local data = GetTrinkets(1)
|
||||
if data then
|
||||
PrintTable(data)
|
||||
end
|
||||
end
|
||||
|
||||
if not Nampower:HasMinimumVersion(2, 24, 0) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Auto Attack Event Constants
|
||||
local HITINFO_NORMALSWING = 0
|
||||
local HITINFO_UNK0 = 1
|
||||
local HITINFO_AFFECTS_VICTIM = 2
|
||||
local HITINFO_LEFTSWING = 4
|
||||
local HITINFO_UNK3 = 8
|
||||
local HITINFO_MISS = 16
|
||||
local HITINFO_ABSORB = 32
|
||||
local HITINFO_RESIST = 64
|
||||
local HITINFO_CRITICALHIT = 128
|
||||
local HITINFO_UNK8 = 256
|
||||
local HITINFO_UNK9 = 8192
|
||||
local HITINFO_GLANCING = 16384
|
||||
local HITINFO_CRUSHING = 32768
|
||||
local HITINFO_NOACTION = 65536
|
||||
local HITINFO_SWINGNOHITSOUND = 524288
|
||||
|
||||
local VICTIMSTATE_UNAFFECTED = 0
|
||||
local VICTIMSTATE_NORMAL = 1
|
||||
local VICTIMSTATE_DODGE = 2
|
||||
local VICTIMSTATE_PARRY = 3
|
||||
local VICTIMSTATE_INTERRUPT = 4
|
||||
local VICTIMSTATE_BLOCKS = 5
|
||||
local VICTIMSTATE_EVADES = 6
|
||||
local VICTIMSTATE_IS_IMMUNE = 7
|
||||
local VICTIMSTATE_DEFLECTS = 8
|
||||
|
||||
local victimStateNames = {
|
||||
[0] = "Unaffected",
|
||||
[1] = "Normal",
|
||||
[2] = "Dodged",
|
||||
[3] = "Parried",
|
||||
[4] = "Interrupted",
|
||||
[5] = "Blocked",
|
||||
[6] = "Evaded",
|
||||
[7] = "Immune",
|
||||
[8] = "Deflected"
|
||||
}
|
||||
|
||||
function GetHitType(hitInfo)
|
||||
local isCrit = bit.band(hitInfo, HITINFO_CRITICALHIT) ~= 0
|
||||
local isGlancing = bit.band(hitInfo, HITINFO_GLANCING) ~= 0
|
||||
local isCrushing = bit.band(hitInfo, HITINFO_CRUSHING) ~= 0
|
||||
local isMiss = bit.band(hitInfo, HITINFO_MISS) ~= 0
|
||||
local isOffHand = bit.band(hitInfo, HITINFO_LEFTSWING) ~= 0
|
||||
|
||||
local hitType = "Normal"
|
||||
if isMiss then
|
||||
hitType = "Miss"
|
||||
elseif isCrit then
|
||||
hitType = "Critical"
|
||||
elseif isGlancing then
|
||||
hitType = "Glancing"
|
||||
elseif isCrushing then
|
||||
hitType = "Crushing"
|
||||
end
|
||||
|
||||
if isOffHand then
|
||||
hitType = hitType .. " (Off-hand)"
|
||||
end
|
||||
|
||||
return hitType
|
||||
end
|
||||
|
||||
function GetVictimStateName(victimState)
|
||||
return victimStateNames[victimState] or "Unknown"
|
||||
end
|
||||
|
||||
local function onAutoAttack(attackerGuid, targetGuid, totalDamage, hitInfo, victimState, subDamageCount, blockedAmount, totalAbsorb, totalResist)
|
||||
local hitType = GetHitType(hitInfo)
|
||||
local victimStateName = GetVictimStateName(victimState)
|
||||
|
||||
print(string.format(
|
||||
"Auto Attack: %s -> %s | %d damage (%s) | State: %s | SubDmg: %d | Absorbed: %d | Resisted: %d | Blocked: %d",
|
||||
attackerGuid, targetGuid, totalDamage, hitType, victimStateName,
|
||||
subDamageCount, totalAbsorb, totalResist, blockedAmount
|
||||
))
|
||||
end
|
||||
|
||||
-- helpful event hooks for debugging
|
||||
--Nampower:RegisterEvent("UNIT_CASTEVENT", function(casterGuid, targetGuid, event, spellID, castDuration)
|
||||
-- if(casterGuid=="0x00000000001CD43C") then
|
||||
-- print("CASTEVENT: " .. tostring(casterGuid) .. " -> " .. tostring(targetGuid) .. " event: " .. tostring(event) .. " spellID: " .. tostring(spellID) .. " castDuration: " .. tostring(castDuration))
|
||||
-- end
|
||||
--end)
|
||||
|
||||
--Nampower:RegisterEvent("AURA_CAST_ON_OTHER", function(spellId, caster, target, effect, effectname)
|
||||
-- local unitName = target and UnitName(target) or ""
|
||||
-- print("AURA_CAST_ON_OTHER: " .. tostring(spellId) .. " " .. tostring(GetSpellRecField(spellId, "name")) .. " by " .. tostring(UnitName(caster)) .. " on " .. unitName .. " effect: " .. tostring(effect) .. " effectname: " .. tostring(effectname))
|
||||
--end)
|
||||
--
|
||||
--Nampower:RegisterEvent("AURA_CAST_ON_SELF", function(spellId, caster, target, effect, effectname)
|
||||
-- local unitName = target and UnitName(target) or ""
|
||||
-- print("AURA_CAST_ON_SELF: " .. tostring(spellId) .. " " .. tostring(GetSpellRecField(spellId, "name")) .. " by " .. tostring(UnitName(caster)) .. " on " .. unitName .. " effect: " .. tostring(effect) .. " effectname: " .. tostring(effectname))
|
||||
--end)
|
||||
--
|
||||
|
||||
--Nampower:RegisterEvent("AUTO_ATTACK_SELF", onAutoAttack)
|
||||
--Nampower:RegisterEvent("AUTO_ATTACK_OTHER", onAutoAttack)
|
||||
|
||||
if not Nampower:HasMinimumVersion(2, 25, 0) then
|
||||
return
|
||||
end
|
||||
|
||||
local spellTypeNames = {
|
||||
[0] = "Normal",
|
||||
[1] = "Channeling",
|
||||
[2] = "Autorepeating",
|
||||
}
|
||||
|
||||
local function onSpellStart(itemId, spellId, casterGuid, targetGuid, castFlags, castTime, duration, spellType, corpseOwnerGuid)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
|
||||
local spellTypeName = spellTypeNames[spellType] or "Unknown"
|
||||
local caster = UnitName(casterGuid) or tostring(casterGuid)
|
||||
local target = UnitName(targetGuid) or tostring(targetGuid)
|
||||
local corpse = nil
|
||||
if corpseOwnerGuid then
|
||||
corpse = UnitName(corpseOwnerGuid) or tostring(corpseOwnerGuid)
|
||||
end
|
||||
print(string.format(
|
||||
"SPELL_START: %s (id:%d) | Caster: %s -> Target: %s | ItemId: %d | Flags: %d | CastTime: %dms | Duration: %dms | Type: %s | CorpseOwner: %s",
|
||||
tostring(spellName), spellId, caster, target, itemId, castFlags, castTime, duration, spellTypeName, tostring(corpse)
|
||||
))
|
||||
end
|
||||
|
||||
local function onSpellGo(itemId, spellId, casterGuid, targetGuid, castFlags, numTargetsHit, numTargetsMissed, corpseOwnerGuid)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
|
||||
local caster = UnitName(casterGuid) or tostring(casterGuid)
|
||||
local target = UnitName(targetGuid) or tostring(targetGuid)
|
||||
local corpse = nil
|
||||
if corpseOwnerGuid then
|
||||
corpse = UnitName(corpseOwnerGuid) or tostring(corpseOwnerGuid)
|
||||
end
|
||||
print(string.format(
|
||||
"SPELL_GO: %s (id:%d) | Caster: %s -> Target: %s | ItemId: %d | Flags: %d | Hit: %d | Missed: %d | CorpseOwner: %s",
|
||||
tostring(spellName), spellId, caster, target, itemId, castFlags, numTargetsHit, numTargetsMissed, tostring(corpse)
|
||||
))
|
||||
end
|
||||
|
||||
local function onSpellFailedSelf(spellId, spellResult, failedByServer)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
|
||||
local source = failedByServer == 1 and "Server" or "Client"
|
||||
print(string.format(
|
||||
"SPELL_FAILED_SELF: %s (id:%d) | Result: %d | Source: %s",
|
||||
tostring(spellName), spellId, spellResult, source
|
||||
))
|
||||
end
|
||||
|
||||
local function onSpellFailedOther(casterGuid, spellId)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
|
||||
print(string.format(
|
||||
"SPELL_FAILED_OTHER: %s (id:%d) | Caster: %s",
|
||||
tostring(spellName), spellId, casterGuid
|
||||
))
|
||||
end
|
||||
|
||||
local function onSpellDelayed(casterGuid, delayMs)
|
||||
print(string.format(
|
||||
"SPELL_DELAYED: Caster: %s | Delay: %dms",
|
||||
casterGuid, delayMs
|
||||
))
|
||||
end
|
||||
|
||||
local function onSpellChannelStart(spellId, targetGuid, durationMs)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
|
||||
print(string.format(
|
||||
"SPELL_CHANNEL_START: %s (id:%d) | Target: %s | Duration: %dms",
|
||||
tostring(spellName), spellId, targetGuid, durationMs
|
||||
))
|
||||
end
|
||||
|
||||
local function onSpellChannelUpdate(spellId, targetGuid, remainingMs)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
|
||||
print(string.format(
|
||||
"SPELL_CHANNEL_UPDATE: %s (id:%d) | Target: %s | Remaining: %dms",
|
||||
tostring(spellName), spellId, targetGuid, remainingMs
|
||||
))
|
||||
end
|
||||
|
||||
--Nampower:RegisterEvent("SPELL_START_SELF", onSpellStart)
|
||||
--Nampower:RegisterEvent("SPELL_START_OTHER", onSpellStart)
|
||||
--Nampower:RegisterEvent("SPELL_GO_SELF", onSpellGo)
|
||||
--Nampower:RegisterEvent("SPELL_GO_OTHER", onSpellGo)
|
||||
--Nampower:RegisterEvent("SPELL_FAILED_SELF", onSpellFailedSelf)
|
||||
--Nampower:RegisterEvent("SPELL_FAILED_OTHER", onSpellFailedOther)
|
||||
--Nampower:RegisterEvent("SPELL_DELAYED_SELF", onSpellDelayed)
|
||||
--Nampower:RegisterEvent("SPELL_DELAYED_OTHER", onSpellDelayed)
|
||||
--Nampower:RegisterEvent("SPELL_CHANNEL_START", onSpellChannelStart)
|
||||
--Nampower:RegisterEvent("SPELL_CHANNEL_UPDATE", onSpellChannelUpdate)
|
||||
|
||||
if not Nampower:HasMinimumVersion(2, 26, 0) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Spell Heal Events (gated behind NP_EnableSpellHealEvents CVar, default 0)
|
||||
-- SPELL_HEAL_BY_SELF - Fires when the active player is the caster (you healed someone)
|
||||
-- SPELL_HEAL_BY_OTHER - Fires when someone other than the active player is the caster (someone else healed someone)
|
||||
-- SPELL_HEAL_ON_SELF - Fires when the active player is the target (you received a heal)
|
||||
-- Note: SPELL_HEAL_BY_SELF and SPELL_HEAL_ON_SELF can both fire for the same heal if you heal yourself
|
||||
|
||||
local function createSpellHealHandler(eventName)
|
||||
return function(targetGuid, casterGuid, spellId, amount, critical, periodic)
|
||||
local critText = critical == 1 and " (CRIT)" or ""
|
||||
local periodicText = periodic == 1 and " (PERIODIC)" or ""
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
|
||||
print(string.format(
|
||||
"%s: %s healed %s for %d with %s%s%s",
|
||||
eventName, UnitName(casterGuid), UnitName(targetGuid), amount, tostring(spellName), critText, periodicText
|
||||
))
|
||||
end
|
||||
end
|
||||
|
||||
-- Spell Energize Events (gated behind NP_EnableSpellEnergizeEvents CVar, default 0)
|
||||
-- SPELL_ENERGIZE_BY_SELF - Fires when the active player is the caster (you restored power to someone)
|
||||
-- SPELL_ENERGIZE_BY_OTHER - Fires when someone other than the active player is the caster (someone else restored power)
|
||||
-- SPELL_ENERGIZE_ON_SELF - Fires when the active player is the target (you received power)
|
||||
-- Note: SPELL_ENERGIZE_BY_SELF and SPELL_ENERGIZE_ON_SELF can both fire for the same energize if you restore power to yourself
|
||||
|
||||
-- Power Type Constants
|
||||
local POWER_MANA = 0
|
||||
local POWER_RAGE = 1
|
||||
local POWER_FOCUS = 2
|
||||
local POWER_ENERGY = 3
|
||||
local POWER_HAPPINESS = 4
|
||||
local POWER_HEALTH = -2 -- 0xFFFFFFFE as unsigned
|
||||
|
||||
local POWER_NAMES = {
|
||||
[0] = "Mana",
|
||||
[1] = "Rage",
|
||||
[2] = "Focus",
|
||||
[3] = "Energy",
|
||||
[4] = "Happiness",
|
||||
[-2] = "Health",
|
||||
}
|
||||
|
||||
function GetPowerTypeName(powerType)
|
||||
return POWER_NAMES[powerType] or "Unknown"
|
||||
end
|
||||
|
||||
local function createSpellEnergizeHandler(eventName)
|
||||
return function(targetGuid, casterGuid, spellId, powerType, amount, periodic)
|
||||
local powerName = GetPowerTypeName(powerType)
|
||||
local periodicText = periodic == 1 and " (PERIODIC)" or ""
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
|
||||
print(string.format(
|
||||
"%s: %s restored %d %s to %s with %s%s",
|
||||
eventName, UnitName(casterGuid), amount, powerName, UnitName(targetGuid), tostring(spellName), periodicText
|
||||
))
|
||||
end
|
||||
end
|
||||
|
||||
--Nampower:RegisterEvent("SPELL_HEAL_BY_SELF", createSpellHealHandler("SPELL_HEAL_BY_SELF"))
|
||||
--Nampower:RegisterEvent("SPELL_HEAL_BY_OTHER", createSpellHealHandler("SPELL_HEAL_BY_OTHER"))
|
||||
--Nampower:RegisterEvent("SPELL_HEAL_ON_SELF", createSpellHealHandler("SPELL_HEAL_ON_SELF"))
|
||||
--Nampower:RegisterEvent("SPELL_ENERGIZE_BY_SELF", createSpellEnergizeHandler("SPELL_ENERGIZE_BY_SELF"))
|
||||
--Nampower:RegisterEvent("SPELL_ENERGIZE_BY_OTHER", createSpellEnergizeHandler("SPELL_ENERGIZE_BY_OTHER"))
|
||||
--Nampower:RegisterEvent("SPELL_ENERGIZE_ON_SELF", createSpellEnergizeHandler("SPELL_ENERGIZE_ON_SELF"))
|
||||
|
||||
if not Nampower:HasMinimumVersion(2, 30, 0) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Aura Duration Update Events
|
||||
-- BUFF_UPDATE_DURATION_SELF - aura slot 0-31
|
||||
-- DEBUFF_UPDATE_DURATION_SELF - aura slot 32-47
|
||||
-- Parameters: auraSlot, durationMs, expirationTimeMs
|
||||
|
||||
local function createAuraDurationHandler(eventName)
|
||||
return function(auraSlot, durationMs, expirationTimeMs)
|
||||
print(string.format(
|
||||
"%s: slot %d | Duration: %dms | Expires: %d",
|
||||
eventName, auraSlot, durationMs, expirationTimeMs
|
||||
))
|
||||
end
|
||||
end
|
||||
--
|
||||
--Nampower:RegisterEvent("DEBUFF_UPDATE_DURATION_SELF", createAuraDurationHandler("DEBUFF_UPDATE_DURATION_SELF"))
|
||||
--Nampower:RegisterEvent("BUFF_UPDATE_DURATION_SELF", createAuraDurationHandler("BUFF_UPDATE_DURATION_SELF"))
|
||||
|
||||
-- Aura Added/Removed Events
|
||||
-- BUFF_ADDED_SELF, BUFF_REMOVED_SELF, BUFF_ADDED_OTHER, BUFF_REMOVED_OTHER
|
||||
-- DEBUFF_ADDED_SELF, DEBUFF_REMOVED_SELF, DEBUFF_ADDED_OTHER, DEBUFF_REMOVED_OTHER
|
||||
-- Parameters: guid, luaSlot, spellId, stackCount, auraLevel, auraSlot
|
||||
|
||||
local function createAuraChangeHandler(eventName)
|
||||
return function(guid, luaSlot, spellId, stackCount, auraLevel, auraSlot)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
|
||||
print(string.format(
|
||||
"%s: %s | luaSlot %d | %s (id:%d) | Stacks: %d | Level: %d | auraSlot: %d",
|
||||
eventName, guid, luaSlot, tostring(spellName), spellId, stackCount, auraLevel, auraSlot
|
||||
))
|
||||
end
|
||||
end
|
||||
|
||||
--Nampower:RegisterEvent("BUFF_ADDED_SELF", createAuraChangeHandler("BUFF_ADDED_SELF"))
|
||||
--Nampower:RegisterEvent("BUFF_REMOVED_SELF", createAuraChangeHandler("BUFF_REMOVED_SELF"))
|
||||
--Nampower:RegisterEvent("BUFF_ADDED_OTHER", createAuraChangeHandler("BUFF_ADDED_OTHER"))
|
||||
--Nampower:RegisterEvent("BUFF_REMOVED_OTHER", createAuraChangeHandler("BUFF_REMOVED_OTHER"))
|
||||
--Nampower:RegisterEvent("DEBUFF_ADDED_SELF", createAuraChangeHandler("DEBUFF_ADDED_SELF"))
|
||||
--Nampower:RegisterEvent("DEBUFF_REMOVED_SELF", createAuraChangeHandler("DEBUFF_REMOVED_SELF"))
|
||||
--Nampower:RegisterEvent("DEBUFF_ADDED_OTHER", createAuraChangeHandler("DEBUFF_ADDED_OTHER"))
|
||||
--Nampower:RegisterEvent("DEBUFF_REMOVED_OTHER", createAuraChangeHandler("DEBUFF_REMOVED_OTHER"))
|
||||
|
||||
-- Spell Damage Events (gated behind NP_EnableSpellDamageEvents CVar, default 0)
|
||||
-- SPELL_DAMAGE_EVENT_SELF - Fires when the active player deals spell damage
|
||||
-- SPELL_DAMAGE_EVENT_OTHER - Fires when someone other than the active player deals spell damage
|
||||
|
||||
-- Spell Damage Hit Info Constants
|
||||
local SPELL_DAMAGE_HIT_NORMAL = 0
|
||||
local SPELL_DAMAGE_HIT_CRIT = 2
|
||||
|
||||
-- Spell School Constants
|
||||
local SPELL_SCHOOL_NORMAL = 0
|
||||
local SPELL_SCHOOL_HOLY = 1
|
||||
local SPELL_SCHOOL_FIRE = 2
|
||||
local SPELL_SCHOOL_NATURE = 3
|
||||
local SPELL_SCHOOL_FROST = 4
|
||||
local SPELL_SCHOOL_SHADOW = 5
|
||||
local SPELL_SCHOOL_ARCANE = 6
|
||||
|
||||
local SPELL_SCHOOL_NAMES = {
|
||||
[0] = "Physical",
|
||||
[1] = "Holy",
|
||||
[2] = "Fire",
|
||||
[3] = "Nature",
|
||||
[4] = "Frost",
|
||||
[5] = "Shadow",
|
||||
[6] = "Arcane",
|
||||
}
|
||||
|
||||
function GetSpellSchoolName(spellSchool)
|
||||
return SPELL_SCHOOL_NAMES[spellSchool] or "Unknown"
|
||||
end
|
||||
|
||||
local function createSpellDamageHandler(eventName)
|
||||
return function(targetGuid, casterGuid, spellId, amount, mitigationStr, hitInfo, spellSchool, effectAuraStr)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
|
||||
local schoolName = GetSpellSchoolName(spellSchool)
|
||||
local critText = hitInfo == SPELL_DAMAGE_HIT_CRIT and " (CRIT)" or ""
|
||||
print(string.format(
|
||||
"%s: %s -> %s | %s (id:%d) | %d %s damage%s | Mitigation: %s | HitInfo: %d | Effects: %s",
|
||||
eventName, casterGuid, targetGuid, tostring(spellName), spellId, amount, schoolName, critText, mitigationStr, hitInfo, effectAuraStr
|
||||
))
|
||||
end
|
||||
end
|
||||
|
||||
--Nampower:RegisterEvent("SPELL_DAMAGE_EVENT_SELF", createSpellDamageHandler("SPELL_DAMAGE_EVENT_SELF"))
|
||||
--Nampower:RegisterEvent("SPELL_DAMAGE_EVENT_OTHER", createSpellDamageHandler("SPELL_DAMAGE_EVENT_OTHER"))
|
||||
|
||||
if not Nampower:HasMinimumVersion(2, 31, 0) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Spell Miss Events (gated behind NP_EnableSpellMissEvents CVar, default 0)
|
||||
-- SPELL_MISS_SELF - Fires when the active player's spell misses/resists/is immune/etc
|
||||
-- SPELL_MISS_OTHER - Fires when someone else's spell misses/resists/is immune/etc
|
||||
-- Triggered by SMSG_SPELLLOGMISS, SMSG_PROCRESIST, and SMSG_SPELLORDAMAGE_IMMUNE
|
||||
|
||||
-- SpellMissInfo Constants
|
||||
local SPELL_MISS_NONE = 0
|
||||
local SPELL_MISS_MISS = 1
|
||||
local SPELL_MISS_RESIST = 2
|
||||
local SPELL_MISS_DODGE = 3
|
||||
local SPELL_MISS_PARRY = 4
|
||||
local SPELL_MISS_BLOCK = 5
|
||||
local SPELL_MISS_EVADE = 6
|
||||
local SPELL_MISS_IMMUNE = 7
|
||||
local SPELL_MISS_IMMUNE2 = 8
|
||||
local SPELL_MISS_DEFLECT = 9
|
||||
local SPELL_MISS_ABSORB = 10
|
||||
local SPELL_MISS_REFLECT = 11
|
||||
|
||||
local SPELL_MISS_NAMES = {
|
||||
[0] = "None",
|
||||
[1] = "Miss",
|
||||
[2] = "Resist",
|
||||
[3] = "Dodge",
|
||||
[4] = "Parry",
|
||||
[5] = "Block",
|
||||
[6] = "Evade",
|
||||
[7] = "Immune",
|
||||
[8] = "Immune",
|
||||
[9] = "Deflect",
|
||||
[10] = "Absorb",
|
||||
[11] = "Reflect",
|
||||
}
|
||||
|
||||
function GetSpellMissName(missInfo)
|
||||
return SPELL_MISS_NAMES[missInfo] or "Unknown"
|
||||
end
|
||||
|
||||
local function createSpellMissHandler(eventName)
|
||||
return function(casterGuid, targetGuid, spellId, missInfo)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
|
||||
local missName = GetSpellMissName(missInfo)
|
||||
print(string.format(
|
||||
"%s: %s -> %s | %s (id:%d) | %s",
|
||||
eventName, UnitName(casterGuid), UnitName(targetGuid), tostring(spellName), spellId, missName
|
||||
))
|
||||
end
|
||||
end
|
||||
|
||||
--Nampower:RegisterEvent("SPELL_MISS_SELF", createSpellMissHandler("SPELL_MISS_SELF"))
|
||||
--Nampower:RegisterEvent("SPELL_MISS_OTHER", createSpellMissHandler("SPELL_MISS_OTHER"))
|
||||
|
||||
if not Nampower:HasMinimumVersion(2, 39, 0) then
|
||||
return
|
||||
end
|
||||
|
||||
-- UNIT_INVENTORY_CHANGED - fires when a unit's inventory changes (standard named token variant)
|
||||
-- Parameters: unit (string) - unit token like "player", "target", "party1", etc.
|
||||
|
||||
-- UNIT_INVENTORY_CHANGED_GUID - fires when a unit's inventory changes (GUID variant)
|
||||
-- Parameters:
|
||||
-- guid (string) - unit GUID e.g. "0xF5300000000000A5"
|
||||
-- isPlayer (int) - 1 if the unit is the active player, 0 otherwise
|
||||
-- isTarget (int) - 1 if the unit is the current locked target, 0 otherwise
|
||||
-- isMouseover (int) - 1 if the unit is the current mouseover, 0 otherwise
|
||||
-- isPet (int) - 1 if the unit is the active player's pet, 0 otherwise
|
||||
-- partyIndex (int) - party slot (1-4) if the unit is a party member, 0 otherwise
|
||||
-- raidIndex (int) - raid slot (1-40) if the unit is a raid member, 0 otherwise
|
||||
|
||||
local function onUnitInventoryChanged(unit)
|
||||
print(string.format("UNIT_MODEL_CHANGED: %s", unit))
|
||||
end
|
||||
|
||||
local function onUnitInventoryChangedGuid(guid, isPlayer, isTarget, isMouseover, isPet, partyIndex, raidIndex)
|
||||
print(string.format(
|
||||
"UNIT_MODEL_CHANGED_GUID: %s | isPlayer:%d isTarget:%d isMouseover:%d isPet:%d party:%d raid:%d",
|
||||
guid, isPlayer, isTarget, isMouseover, isPet, partyIndex, raidIndex
|
||||
))
|
||||
end
|
||||
|
||||
--Nampower:RegisterEvent("UNIT_MODEL_CHANGED", onUnitInventoryChanged)
|
||||
--Nampower:RegisterEvent("UNIT_MODEL_CHANGED_GUID", onUnitInventoryChangedGuid)
|
||||
|
||||
-- UNIT_MANA - fires when a unit's mana changes (standard named token variant)
|
||||
-- Parameters: unit (string) - unit token like "player", "target", "party1", etc.
|
||||
|
||||
-- UNIT_MANA_GUID - fires when a unit's mana changes (GUID variant)
|
||||
-- Parameters:
|
||||
-- guid (string) - unit GUID e.g. "0xF5300000000000A5"
|
||||
-- isPlayer (int) - 1 if the unit is the active player, 0 otherwise
|
||||
-- isTarget (int) - 1 if the unit is the current locked target, 0 otherwise
|
||||
-- isMouseover (int) - 1 if the unit is the current mouseover, 0 otherwise
|
||||
-- isPet (int) - 1 if the unit is the active player's pet, 0 otherwise
|
||||
-- partyIndex (int) - party slot (1-4) if the unit is a party member, 0 otherwise
|
||||
-- raidIndex (int) - raid slot (1-40) if the unit is a raid member, 0 otherwise
|
||||
|
||||
local function onUnitMana(unit)
|
||||
print(string.format("UNIT_MANA: %s", unit))
|
||||
end
|
||||
|
||||
local function onUnitManaGuid(guid, isPlayer, isTarget, isMouseover, isPet, partyIndex, raidIndex)
|
||||
if UnitName(guid) == "Tankeboy" or UnitName(guid) == "Icansummon" then
|
||||
print(string.format(
|
||||
"UNIT_MANA_GUID: %s | isPlayer:%d isTarget:%d isMouseover:%d isPet:%d party:%d raid:%d",
|
||||
UnitName(guid), isPlayer, isTarget, isMouseover, isPet, partyIndex, raidIndex
|
||||
))
|
||||
end
|
||||
end
|
||||
|
||||
--Nampower:RegisterEvent("UNIT_MANA", onUnitMana)
|
||||
--Nampower:RegisterEvent("UNIT_MANA_GUID", onUnitManaGuid)
|
||||
|
||||
-- UNIT_COMBAT - fires when a unit participates in combat (standard named token variant)
|
||||
-- Parameters:
|
||||
-- unit (string) - unit token like "player", "target", "party1", etc.
|
||||
-- action (string) - e.g. "WOUND", "MISS", "DODGE", "PARRY", "BLOCK", "EVADE", "IMMUNE", "REFLECT", "ABSORB", "INTERRUPT"
|
||||
-- critical (string) - "CRITICAL" if a crit, "" otherwise
|
||||
-- damage (int) - raw damage amount (0 for non-damaging outcomes)
|
||||
-- school (int) - damage school (0=Physical, 1=Holy, 2=Fire, 3=Nature, 4=Frost, 5=Shadow, 6=Arcane)
|
||||
|
||||
-- UNIT_COMBAT_GUID - fires once per combat feedback event, identified by GUID
|
||||
-- Unlike other GUID events, carries full combat detail rather than unit membership flags.
|
||||
-- Parameters:
|
||||
-- guid (string) - unit GUID e.g. "0xF5300000000000A5"
|
||||
-- action (string) - e.g. "WOUND", "MISS", "DODGE", "PARRY", "BLOCK", "EVADE", "IMMUNE", "REFLECT", "ABSORB", "INTERRUPT"
|
||||
-- damage (int) - raw damage amount (0 for non-damaging outcomes like dodge/miss)
|
||||
-- school (int) - damage school (0=Physical, 1=Holy, 2=Fire, 3=Nature, 4=Frost, 5=Shadow, 6=Arcane)
|
||||
-- hitInfo (int) - hit info bitfield
|
||||
-- isInteract (int) - 1 if the unit matches the current interact target, 0 otherwise
|
||||
-- isPet (int) - 1 if the unit is the active player's pet, 0 otherwise
|
||||
-- partyIndex (int) - party slot (1-4) if the unit is a party member, 0 otherwise
|
||||
-- raidIndex (int) - raid slot (1-40) if the unit is a raid member, 0 otherwise
|
||||
|
||||
local function onUnitCombat(unit, action, critical, damage, school)
|
||||
local schoolName = GetSpellSchoolName(school)
|
||||
local critText = critical == "CRITICAL" and " (CRIT)" or ""
|
||||
print(string.format(
|
||||
"UNIT_COMBAT: %s | %s | %d %s damage%s",
|
||||
unit, action, damage, schoolName, critText
|
||||
))
|
||||
end
|
||||
|
||||
local function onUnitCombatGuid(guid, action, damage, school, hitInfo, isInteract, isPet, partyIndex, raidIndex)
|
||||
local name = UnitName(guid)
|
||||
local schoolName = GetSpellSchoolName(school)
|
||||
print(string.format(
|
||||
"UNIT_COMBAT_GUID: %s | %s | %d %s | hitInfo:%d isInteract:%d isPet:%d party:%d raid:%d",
|
||||
name, action, damage, schoolName, hitInfo, isInteract, isPet, partyIndex, raidIndex
|
||||
))
|
||||
end
|
||||
|
||||
--Nampower:RegisterEvent("UNIT_COMBAT", onUnitCombat)
|
||||
--Nampower:RegisterEvent("UNIT_COMBAT_GUID", onUnitCombatGuid)
|
||||
|
||||
-- SPELL_CAST_EVENT - fires when you (or certain pets) initiate a spell cast
|
||||
-- Triggered client-side before the cast is sent to the server.
|
||||
-- Only fires for spells you initiated.
|
||||
-- Parameters:
|
||||
-- success (int) - 1 if cast succeeded, 0 if failed
|
||||
-- spellId (int) - spell ID
|
||||
-- castType (int) - see Cast Type Constants below
|
||||
-- targetGuid (string) - guid string like "0xF5300000000000A5"
|
||||
-- itemId (int) - item ID that triggered the spell, 0 if not item-triggered
|
||||
|
||||
-- Cast Type Constants
|
||||
local CAST_TYPE_NORMAL = 0
|
||||
local CAST_TYPE_NON_GCD = 1
|
||||
local CAST_TYPE_ON_SWING = 2
|
||||
local CAST_TYPE_CHANNEL = 3
|
||||
local CAST_TYPE_TARGETING = 4
|
||||
local CAST_TYPE_TARGETING_NON_GCD = 5
|
||||
|
||||
local CAST_TYPE_NAMES = {
|
||||
[0] = "Normal",
|
||||
[1] = "NonGCD",
|
||||
[2] = "OnSwing",
|
||||
[3] = "Channel",
|
||||
[4] = "Targeting",
|
||||
[5] = "TargetingNonGCD",
|
||||
}
|
||||
|
||||
function GetCastTypeName(castType)
|
||||
return CAST_TYPE_NAMES[castType] or "Unknown"
|
||||
end
|
||||
|
||||
local function onSpellCastEvent(success, spellId, castType, targetGuid, itemId)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
|
||||
local castTypeName = GetCastTypeName(castType)
|
||||
local successText = success == 1 and "OK" or "FAILED"
|
||||
local itemText = itemId ~= 0 and (" | Item: " .. tostring(itemId)) or ""
|
||||
print(string.format(
|
||||
"SPELL_CAST_EVENT: [%s] %s (id:%d) | Type: %s | Target: %s%s",
|
||||
successText, tostring(spellName), spellId, castTypeName, tostring(targetGuid), itemText
|
||||
))
|
||||
end
|
||||
|
||||
--Nampower:RegisterEvent("SPELL_CAST_EVENT", onSpellCastEvent)
|
||||
|
||||
-- UNIT_AURA_GUID - fires when a unit's aura state changes (GUID variant)
|
||||
-- All GUID events share the same parameter format:
|
||||
-- guid (string) - unit guid like "0xF5300000000000A5"
|
||||
-- isPlayer (int) - 1 if the unit is the active player, 0 otherwise
|
||||
-- isTarget (int) - 1 if the unit is the current locked target, 0 otherwise
|
||||
-- isMouseover (int) - 1 if the unit is the current mouseover, 0 otherwise
|
||||
-- isPet (int) - 1 if the unit is the active player's pet, 0 otherwise
|
||||
-- partyIndex (int) - party slot (1-4) if the unit is a party member, 0 otherwise
|
||||
-- raidIndex (int) - raid slot (1-40) if the unit is a raid member, 0 otherwise
|
||||
local function onUnitGuid(guid, isPlayer, isTarget, isMouseover, isPet, partyIndex, raidIndex)
|
||||
isPlayer = isPlayer or 0
|
||||
isTarget = isTarget or 0
|
||||
isMouseover = isMouseover or 0
|
||||
isPet = isPet or 0
|
||||
partyIndex = partyIndex or 0
|
||||
raidIndex = raidIndex or 0
|
||||
print(string.format(
|
||||
"%s: %s | isPlayer:%d isTarget:%d isMouseover:%d isPet:%d party:%d raid:%d",
|
||||
"UNIT_AURA_GUID", tostring(UnitName(guid) or guid), isPlayer, isTarget, isMouseover, isPet, partyIndex, raidIndex
|
||||
))
|
||||
end
|
||||
|
||||
--Nampower:RegisterEvent("UNIT_AURA_GUID", onUnitGuid)
|
||||
|
||||
if not Nampower:HasMinimumVersion(3, 4, 0) then
|
||||
return
|
||||
end
|
||||
|
||||
-- ENVIRONMENTAL_DMG_SELF / ENVIRONMENTAL_DMG_OTHER
|
||||
-- Parameters:
|
||||
-- unitGuid (string) - guid of the unit that took damage
|
||||
-- dmgType (int) - environmental damage type
|
||||
-- damage (int) - amount of damage taken
|
||||
-- absorb (int) - amount of damage absorbed
|
||||
-- resist (int) - amount of damage resisted
|
||||
local ENV_DMG_NAMES = {
|
||||
[0] = "Exhausted",
|
||||
[1] = "Drowning",
|
||||
[2] = "Fall",
|
||||
[3] = "Lava",
|
||||
[4] = "Slime",
|
||||
[5] = "Fire",
|
||||
[6] = "FallToVoid",
|
||||
}
|
||||
|
||||
local function onEnvironmentalDmg(unitGuid, dmgType, damage, absorb, resist)
|
||||
dmgType = dmgType or -1
|
||||
damage = damage or 0
|
||||
absorb = absorb or 0
|
||||
resist = resist or 0
|
||||
print(string.format(
|
||||
"ENVIRONMENTAL_DMG: %s took %d %s damage (absorbed:%d resisted:%d)",
|
||||
tostring(unitGuid), damage, ENV_DMG_NAMES[dmgType] or "Unknown", absorb, resist
|
||||
))
|
||||
end
|
||||
|
||||
--Nampower:RegisterEvent("ENVIRONMENTAL_DMG_SELF", onEnvironmentalDmg)
|
||||
--Nampower:RegisterEvent("ENVIRONMENTAL_DMG_OTHER", onEnvironmentalDmg)
|
||||
|
||||
-- DAMAGE_SHIELD_SELF / DAMAGE_SHIELD_OTHER
|
||||
-- Parameters:
|
||||
-- unitGuid (string) - guid of shield owner
|
||||
-- targetGuid (string) - guid of attacker taking reflected damage
|
||||
-- damage (int) - amount of shield damage dealt
|
||||
-- spellSchool (int) - school of shield damage
|
||||
local SCHOOL_NAMES = {
|
||||
[0] = "Physical",
|
||||
[1] = "Holy",
|
||||
[2] = "Fire",
|
||||
[3] = "Nature",
|
||||
[4] = "Frost",
|
||||
[5] = "Shadow",
|
||||
[6] = "Arcane",
|
||||
}
|
||||
|
||||
local function onDamageShield(unitGuid, targetGuid, damage, spellSchool)
|
||||
damage = damage or 0
|
||||
spellSchool = spellSchool or -1
|
||||
print(string.format(
|
||||
"DAMAGE_SHIELD: %s dealt %d %s damage to %s",
|
||||
tostring(unitGuid), damage, SCHOOL_NAMES[spellSchool] or "Unknown", tostring(targetGuid)
|
||||
))
|
||||
end
|
||||
|
||||
--Nampower:RegisterEvent("DAMAGE_SHIELD_SELF", onDamageShield)
|
||||
--Nampower:RegisterEvent("DAMAGE_SHIELD_OTHER", onDamageShield)
|
||||
|
||||
-- SPELL_DISPEL_BY_SELF / SPELL_DISPEL_BY_OTHER
|
||||
-- Parameters:
|
||||
-- casterGuid (string) - guid of dispel caster
|
||||
-- targetGuid (string) - guid of dispelled unit
|
||||
-- spellId (int) - dispelled spell id
|
||||
local function onSpellDispel(casterGuid, targetGuid, spellId)
|
||||
spellId = spellId or 0
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or spellId
|
||||
print(string.format(
|
||||
"SPELL_DISPEL_BY: %s dispelled %s (id:%d) from %s",
|
||||
tostring(casterGuid), tostring(spellName), spellId, tostring(targetGuid)
|
||||
))
|
||||
end
|
||||
|
||||
--Nampower:RegisterEvent("SPELL_DISPEL_BY_SELF", onSpellDispel)
|
||||
--Nampower:RegisterEvent("SPELL_DISPEL_BY_OTHER", onSpellDispel)
|
||||
|
||||
-- PrintSpellRange(rangeIndex)
|
||||
-- Prints the SpellRange DBC record for the given index using GetSpellRangeData.
|
||||
-- GetSpellRangeData returns: minRange, maxRange, flags, name
|
||||
function PrintSpellRange(rangeIndex)
|
||||
if not GetSpellRangeData then
|
||||
print("GetSpellRangeData is not available")
|
||||
return
|
||||
end
|
||||
local minRange, maxRange, flags, name = GetSpellRangeData(rangeIndex)
|
||||
if minRange == nil then
|
||||
print(string.format("PrintSpellRange(%d): no record found", rangeIndex))
|
||||
return
|
||||
end
|
||||
print(string.format(
|
||||
"SpellRange[%d]: name=%q min=%.2f max=%.2f flags=0x%08X",
|
||||
rangeIndex, tostring(name), minRange, maxRange, flags
|
||||
))
|
||||
end
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
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
@@ -0,0 +1,245 @@
|
||||
--[[
|
||||
Name: AceDebug-2.0
|
||||
Revision: $Rev: 17638 $
|
||||
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/AceDebug-2.0
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceDebug-2.0
|
||||
Description: Mixin to allow for simple debugging capabilities.
|
||||
Dependencies: AceLibrary, AceOO-2.0
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceDebug-2.0"
|
||||
local MINOR_VERSION = "$Revision: 17639 $"
|
||||
|
||||
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
|
||||
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
|
||||
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
|
||||
|
||||
local DEBUGGING, TOGGLE_DEBUGGING
|
||||
|
||||
if GetLocale() == "frFR" then
|
||||
DEBUGGING = "D\195\169boguage"
|
||||
TOGGLE_DEBUGGING = "Activer/d\195\169sactiver le d\195\169boguage"
|
||||
elseif GetLocale() == "esES" then
|
||||
DEBUGGING = "Depurar"
|
||||
TOGGLE_DEBUGGING = "Activar/desactivar depurar"
|
||||
elseif GetLocale() == "deDE" then
|
||||
DEBUGGING = "Debuggen"
|
||||
TOGGLE_DEBUGGING = "Aktiviert/Deaktiviert Debugging"
|
||||
elseif GetLocale() == "koKR" then
|
||||
DEBUGGING = "디버깅"
|
||||
TOGGLE_DEBUGGING = "디버깅 기능 사용함/사용안함"
|
||||
elseif GetLocale() == "zhTW" then
|
||||
DEBUGGING = "除錯"
|
||||
TOGGLE_DEBUGGING = "啟用/停用除錯功能"
|
||||
elseif GetLocale() == "zhCN" then
|
||||
DEBUGGING = "\232\176\131\232\175\149"
|
||||
TOGGLE_DEBUGGING = "\229\144\175\231\148\168/\231\166\129\231\148\168 \232\176\131\232\175\149"
|
||||
elseif GetLocale() == "ruRU" then
|
||||
DEBUGGING = "Отладка"
|
||||
TOGGLE_DEBUGGING = "Вкл/Выкл отладку для этого аддона."
|
||||
else -- enUS
|
||||
DEBUGGING = "Debugging"
|
||||
TOGGLE_DEBUGGING = "Enable/disable debugging"
|
||||
end
|
||||
|
||||
local table_setn
|
||||
do
|
||||
local version = GetBuildInfo()
|
||||
if string.find(version, "^2%.") then
|
||||
-- 2.0.0
|
||||
table_setn = function() end
|
||||
else
|
||||
table_setn = table.setn
|
||||
end
|
||||
end
|
||||
|
||||
local math_mod = math.mod or math.fmod
|
||||
|
||||
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
|
||||
local AceDebug = AceOO.Mixin {"Debug", "CustomDebug", "IsDebugging", "SetDebugging", "SetDebugLevel", "LevelDebug", "CustomLevelDebug", "GetDebugLevel"}
|
||||
|
||||
local function print(text, r, g, b, frame, delay)
|
||||
(frame or DEFAULT_CHAT_FRAME):AddMessage(text, r, g, b, 1, delay or 5)
|
||||
end
|
||||
|
||||
local tmp
|
||||
|
||||
function AceDebug:CustomDebug(r, g, b, frame, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not self.debugging then return end
|
||||
|
||||
local output = string.format("|cff7fff7f(DEBUG) %s:[%s%3d]|r", tostring(self), date("%H:%M:%S"), math_mod(GetTime(), 1) * 1000)
|
||||
|
||||
if string.find(tostring(a1), "%%") then
|
||||
output = output .. " " .. string.format(tostring(a1), tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20))
|
||||
else
|
||||
if not tmp then
|
||||
tmp = {}
|
||||
end
|
||||
|
||||
-- This block dynamically rebuilds the tmp array stopping on the first nil.
|
||||
table.insert(tmp, output)
|
||||
|
||||
table.insert(tmp, tostring(a1))
|
||||
table.insert(tmp, a2)
|
||||
table.insert(tmp, a3)
|
||||
table.insert(tmp, a4)
|
||||
table.insert(tmp, a5)
|
||||
table.insert(tmp, a6)
|
||||
table.insert(tmp, a7)
|
||||
table.insert(tmp, a8)
|
||||
table.insert(tmp, a9)
|
||||
table.insert(tmp, a10)
|
||||
table.insert(tmp, a11)
|
||||
table.insert(tmp, a12)
|
||||
table.insert(tmp, a13)
|
||||
table.insert(tmp, a14)
|
||||
table.insert(tmp, a15)
|
||||
table.insert(tmp, a16)
|
||||
table.insert(tmp, a17)
|
||||
table.insert(tmp, a18)
|
||||
table.insert(tmp, a19)
|
||||
table.insert(tmp, a20)
|
||||
while tmp[table.getn(tmp)] == nil do
|
||||
table.remove(tmp)
|
||||
end
|
||||
for k = 1, table.getn(tmp) do
|
||||
tmp[k] = tostring(tmp[k])
|
||||
end
|
||||
|
||||
output = table.concat(tmp, " ")
|
||||
|
||||
for k,v in pairs(tmp) do
|
||||
tmp[k] = nil
|
||||
end
|
||||
table_setn(tmp, 0)
|
||||
end
|
||||
|
||||
print(output, r, g, b, frame or self.debugFrame, delay)
|
||||
end
|
||||
|
||||
function AceDebug:Debug(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
AceDebug.CustomDebug(self, nil, nil, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
|
||||
function AceDebug:IsDebugging()
|
||||
return self.debugging
|
||||
end
|
||||
|
||||
function AceDebug:SetDebugging(debugging)
|
||||
self.debugging = debugging
|
||||
end
|
||||
|
||||
-- Takes a number 1-3
|
||||
-- Level 1: Critical messages that every user should receive
|
||||
-- Level 2: Should be used for local debugging (function calls, etc)
|
||||
-- Level 3: Very verbose debugging, will dump everything and anything
|
||||
-- If set to nil, you will receive no debug information
|
||||
function AceDebug:SetDebugLevel(level)
|
||||
AceDebug:argCheck(level, 1, "number", "nil")
|
||||
if not level then
|
||||
self.debuglevel = nil
|
||||
return
|
||||
end
|
||||
if level < 1 or level > 3 then
|
||||
AceDebug:error("Bad argument #1 to `SetDebugLevel`, must be a number 1-3")
|
||||
end
|
||||
self.debuglevel = level
|
||||
end
|
||||
|
||||
function AceDebug:GetDebugLevel()
|
||||
return self.debuglevel
|
||||
end
|
||||
|
||||
function AceDebug:CustomLevelDebug(level, r, g, b, frame, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not self.debugging or not self.debuglevel then return end
|
||||
AceDebug:argCheck(level, 1, "number")
|
||||
if level < 1 or level > 3 then
|
||||
AceDebug:error("Bad argument #1 to `LevelDebug`, must be a number 1-3")
|
||||
end
|
||||
if level > self.debuglevel then return end
|
||||
|
||||
local output = string.format("|cff7fff7f(DEBUG) %s:[%s.%3d]|r", tostring(self), date("%H:%M:%S"), math_mod(GetTime(), 1) * 1000)
|
||||
|
||||
if string.find(tostring(a1), "%%") then
|
||||
output = output .. " " .. string.format(tostring(a1), tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20))
|
||||
else
|
||||
if not tmp then
|
||||
tmp = {}
|
||||
end
|
||||
|
||||
-- This block dynamically rebuilds the tmp array stopping on the first nil.
|
||||
table.insert(tmp, output)
|
||||
|
||||
table.insert(tmp, tostring(a1))
|
||||
table.insert(tmp, a2)
|
||||
table.insert(tmp, a3)
|
||||
table.insert(tmp, a4)
|
||||
table.insert(tmp, a5)
|
||||
table.insert(tmp, a6)
|
||||
table.insert(tmp, a7)
|
||||
table.insert(tmp, a8)
|
||||
table.insert(tmp, a9)
|
||||
table.insert(tmp, a10)
|
||||
table.insert(tmp, a11)
|
||||
table.insert(tmp, a12)
|
||||
table.insert(tmp, a13)
|
||||
table.insert(tmp, a14)
|
||||
table.insert(tmp, a15)
|
||||
table.insert(tmp, a16)
|
||||
table.insert(tmp, a17)
|
||||
table.insert(tmp, a18)
|
||||
table.insert(tmp, a19)
|
||||
table.insert(tmp, a20)
|
||||
while tmp[table.getn(tmp)] == nil do
|
||||
table.remove(tmp)
|
||||
end
|
||||
for k = 1, table.getn(tmp) do
|
||||
tmp[k] = tostring(tmp[k])
|
||||
end
|
||||
|
||||
output = table.concat(tmp, " ")
|
||||
|
||||
for k,v in pairs(tmp) do
|
||||
tmp[k] = nil
|
||||
end
|
||||
table_setn(tmp, 0)
|
||||
end
|
||||
|
||||
print(output, r, g, b, frame or self.debugFrame, delay)
|
||||
end
|
||||
|
||||
function AceDebug:LevelDebug(level, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not self.debugging or not self.debuglevel then return end
|
||||
AceDebug:argCheck(level, 1, "number")
|
||||
if level < 1 or level > 3 then
|
||||
AceDebug:error("Bad argument #1 to `LevelDebug`, must be a number 1-3")
|
||||
end
|
||||
if level > self.debuglevel then return end
|
||||
|
||||
AceDebug.CustomLevelDebug(self, level, nil, nil, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
|
||||
|
||||
local options
|
||||
function AceDebug:GetAceOptionsDataTable(target)
|
||||
if not options then
|
||||
options = {
|
||||
debug = {
|
||||
name = DEBUGGING,
|
||||
desc = TOGGLE_DEBUGGING,
|
||||
type = "toggle",
|
||||
get = "IsDebugging",
|
||||
set = "SetDebugging",
|
||||
order = -2,
|
||||
}
|
||||
}
|
||||
end
|
||||
return options
|
||||
end
|
||||
AceLibrary:Register(AceDebug, MAJOR_VERSION, MINOR_VERSION, AceDebug.activate)
|
||||
AceDebug = AceLibrary(MAJOR_VERSION)
|
||||
@@ -0,0 +1,973 @@
|
||||
--[[
|
||||
Name: AceEvent-2.0
|
||||
Revision: $Rev: 17803 $
|
||||
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/AceEvent-2.0
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceEvent-2.0
|
||||
Description: Mixin to allow for event handling, scheduling, and inter-addon
|
||||
communication.
|
||||
Dependencies: AceLibrary, AceOO-2.0
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceEvent-2.0"
|
||||
local MINOR_VERSION = "$Revision: 17803 $"
|
||||
|
||||
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
|
||||
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
|
||||
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
|
||||
|
||||
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
|
||||
local Mixin = AceOO.Mixin
|
||||
local AceEvent = Mixin {
|
||||
"RegisterEvent",
|
||||
"RegisterAllEvents",
|
||||
"UnregisterEvent",
|
||||
"UnregisterAllEvents",
|
||||
"TriggerEvent",
|
||||
"ScheduleEvent",
|
||||
"ScheduleRepeatingEvent",
|
||||
"CancelScheduledEvent",
|
||||
"CancelAllScheduledEvents",
|
||||
"IsEventRegistered",
|
||||
"IsEventScheduled",
|
||||
"RegisterBucketEvent",
|
||||
"UnregisterBucketEvent",
|
||||
"UnregisterAllBucketEvents",
|
||||
"IsBucketEventRegistered",
|
||||
}
|
||||
|
||||
local table_setn
|
||||
do
|
||||
local version = GetBuildInfo()
|
||||
if string.find(version, "^2%.") then
|
||||
-- 2.0.0
|
||||
table_setn = function() end
|
||||
else
|
||||
table_setn = table.setn
|
||||
end
|
||||
end
|
||||
|
||||
local weakKey = {__mode="k"}
|
||||
local new, del
|
||||
do
|
||||
local list = setmetatable({}, weakKey)
|
||||
function new()
|
||||
local t = next(list)
|
||||
if t then
|
||||
list[t] = nil
|
||||
return t
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
function del(t)
|
||||
setmetatable(t, nil)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
list[t] = true
|
||||
end
|
||||
end
|
||||
|
||||
local FAKE_NIL
|
||||
local RATE
|
||||
|
||||
local eventsWhichHappenOnce = {
|
||||
PLAYER_LOGIN = true,
|
||||
AceEvent_FullyInitialized = true,
|
||||
VARIABLES_LOADED = true,
|
||||
PLAYER_LOGOUT = true,
|
||||
}
|
||||
|
||||
local registeringFromAceEvent
|
||||
function AceEvent:RegisterEvent(event, method, once)
|
||||
AceEvent:argCheck(event, 2, "string")
|
||||
if self == AceEvent and not registeringFromAceEvent then
|
||||
AceEvent:argCheck(method, 3, "function")
|
||||
self = method
|
||||
else
|
||||
AceEvent:argCheck(method, 3, "string", "function", "nil", "boolean", "number")
|
||||
if type(method) == "boolean" or type(method) == "number" then
|
||||
AceEvent:argCheck(once, 4, "nil")
|
||||
once, method = method, event
|
||||
end
|
||||
end
|
||||
AceEvent:argCheck(once, 4, "number", "boolean", "nil")
|
||||
if eventsWhichHappenOnce[event] then
|
||||
once = true
|
||||
end
|
||||
local throttleRate
|
||||
if type(once) == "number" then
|
||||
throttleRate, once = once
|
||||
end
|
||||
if not method then
|
||||
method = event
|
||||
end
|
||||
if type(method) == "string" and type(self[method]) ~= "function" then
|
||||
AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
|
||||
else
|
||||
assert(type(method) == "function" or type(method) == "string")
|
||||
end
|
||||
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if not AceEvent_registry[event] then
|
||||
AceEvent_registry[event] = new()
|
||||
AceEvent.frame:RegisterEvent(event)
|
||||
end
|
||||
|
||||
local remember = true
|
||||
if AceEvent_registry[event][self] then
|
||||
remember = false
|
||||
end
|
||||
AceEvent_registry[event][self] = method
|
||||
|
||||
local AceEvent_onceRegistry = AceEvent.onceRegistry
|
||||
if once then
|
||||
if not AceEvent_onceRegistry then
|
||||
AceEvent.onceRegistry = new()
|
||||
AceEvent_onceRegistry = AceEvent.onceRegistry
|
||||
end
|
||||
if not AceEvent_onceRegistry[event] then
|
||||
AceEvent_onceRegistry[event] = new()
|
||||
end
|
||||
AceEvent_onceRegistry[event][self] = true
|
||||
else
|
||||
if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
|
||||
AceEvent_onceRegistry[event][self] = nil
|
||||
if not next(AceEvent_onceRegistry[event]) then
|
||||
AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local AceEvent_throttleRegistry = AceEvent.throttleRegistry
|
||||
if throttleRate then
|
||||
if not AceEvent_throttleRegistry then
|
||||
AceEvent.throttleRegistry = new()
|
||||
AceEvent_throttleRegistry = AceEvent.throttleRegistry
|
||||
end
|
||||
if not AceEvent_throttleRegistry[event] then
|
||||
AceEvent_throttleRegistry[event] = new()
|
||||
end
|
||||
if AceEvent_throttleRegistry[event][self] then
|
||||
AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
|
||||
end
|
||||
AceEvent_throttleRegistry[event][self] = setmetatable(new(), weakKey)
|
||||
local t = AceEvent_throttleRegistry[event][self]
|
||||
t[RATE] = throttleRate
|
||||
else
|
||||
if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] then
|
||||
if AceEvent_throttleRegistry[event][self] then
|
||||
AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
|
||||
end
|
||||
if not next(AceEvent_throttleRegistry[event]) then
|
||||
AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if remember then
|
||||
AceEvent:TriggerEvent("AceEvent_EventRegistered", self, event)
|
||||
end
|
||||
end
|
||||
|
||||
local ALL_EVENTS
|
||||
|
||||
function AceEvent:RegisterAllEvents(method)
|
||||
if self == AceEvent then
|
||||
AceEvent:argCheck(method, 1, "function")
|
||||
self = method
|
||||
else
|
||||
AceEvent:argCheck(method, 1, "string", "function")
|
||||
if type(method) == "string" and type(self[method]) ~= "function" then
|
||||
AceEvent:error("Cannot register all events to method %q, it does not exist", method)
|
||||
end
|
||||
end
|
||||
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if not AceEvent_registry[ALL_EVENTS] then
|
||||
AceEvent_registry[ALL_EVENTS] = new()
|
||||
AceEvent.frame:RegisterAllEvents()
|
||||
end
|
||||
|
||||
AceEvent_registry[ALL_EVENTS][self] = method
|
||||
end
|
||||
|
||||
local _G = getfenv(0)
|
||||
local memstack, timestack = {}, {}
|
||||
local memdiff, timediff
|
||||
function AceEvent:TriggerEvent(event, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
AceEvent:argCheck(event, 2, "string")
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if (not AceEvent_registry[event] or not next(AceEvent_registry[event])) and (not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS])) then
|
||||
return
|
||||
end
|
||||
local _G_event = _G.event
|
||||
_G.event = event
|
||||
local lastEvent = AceEvent.currentEvent
|
||||
AceEvent.currentEvent = event
|
||||
|
||||
local AceEvent_onceRegistry = AceEvent.onceRegistry
|
||||
local AceEvent_debugTable = AceEvent.debugTable
|
||||
if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
|
||||
local tmp = new()
|
||||
for obj, method in pairs(AceEvent_onceRegistry[event]) do
|
||||
tmp[obj] = AceEvent_registry[event] and AceEvent_registry[event][obj] or nil
|
||||
end
|
||||
local obj = next(tmp)
|
||||
while obj do
|
||||
local mem, time
|
||||
if AceEvent_debugTable then
|
||||
if not AceEvent_debugTable[event] then
|
||||
AceEvent_debugTable[event] = new()
|
||||
end
|
||||
if not AceEvent_debugTable[event][obj] then
|
||||
AceEvent_debugTable[event][obj] = new()
|
||||
AceEvent_debugTable[event][obj].mem = 0
|
||||
AceEvent_debugTable[event][obj].time = 0
|
||||
AceEvent_debugTable[event][obj].count = 0
|
||||
end
|
||||
if memdiff then
|
||||
table.insert(memstack, memdiff)
|
||||
table.insert(timestack, timediff)
|
||||
end
|
||||
memdiff, timediff = 0, 0
|
||||
mem, time = gcinfo(), GetTime()
|
||||
end
|
||||
local method = tmp[obj]
|
||||
AceEvent.UnregisterEvent(obj, event)
|
||||
if type(method) == "string" then
|
||||
local obj_method = obj[method]
|
||||
if obj_method then
|
||||
obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
elseif method then -- function
|
||||
method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
if AceEvent_debugTable then
|
||||
local dmem, dtime = memdiff, timediff
|
||||
mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
|
||||
AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
|
||||
AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
|
||||
AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1
|
||||
|
||||
memdiff, timediff = table.remove(memstack), table.remove(timestack)
|
||||
if memdiff then
|
||||
memdiff = memdiff + mem + dmem
|
||||
timediff = timediff + time + dtime
|
||||
end
|
||||
end
|
||||
tmp[obj] = nil
|
||||
obj = next(tmp)
|
||||
end
|
||||
del(tmp)
|
||||
end
|
||||
|
||||
local AceEvent_throttleRegistry = AceEvent.throttleRegistry
|
||||
local throttleTable = AceEvent_throttleRegistry and AceEvent_throttleRegistry[event]
|
||||
if AceEvent_registry[event] then
|
||||
local tmp = new()
|
||||
for obj, method in pairs(AceEvent_registry[event]) do
|
||||
tmp[obj] = method
|
||||
end
|
||||
local obj = next(tmp)
|
||||
while obj do
|
||||
local method = tmp[obj]
|
||||
local continue = false
|
||||
if throttleTable and throttleTable[obj] then
|
||||
local a1 = a1
|
||||
if a1 == nil then
|
||||
a1 = FAKE_NIL
|
||||
end
|
||||
if not throttleTable[obj][a1] or GetTime() - throttleTable[obj][a1] >= throttleTable[obj][RATE] then
|
||||
throttleTable[obj][a1] = GetTime()
|
||||
else
|
||||
continue = true
|
||||
end
|
||||
end
|
||||
if not continue then
|
||||
local mem, time
|
||||
if AceEvent_debugTable then
|
||||
if not AceEvent_debugTable[event] then
|
||||
AceEvent_debugTable[event] = new()
|
||||
end
|
||||
if not AceEvent_debugTable[event][obj] then
|
||||
AceEvent_debugTable[event][obj] = new()
|
||||
AceEvent_debugTable[event][obj].mem = 0
|
||||
AceEvent_debugTable[event][obj].time = 0
|
||||
AceEvent_debugTable[event][obj].count = 0
|
||||
end
|
||||
if memdiff then
|
||||
table.insert(memstack, memdiff)
|
||||
table.insert(timestack, timediff)
|
||||
end
|
||||
memdiff, timediff = 0, 0
|
||||
mem, time = gcinfo(), GetTime()
|
||||
end
|
||||
if type(method) == "string" then
|
||||
local obj_method = obj[method]
|
||||
if obj_method then
|
||||
obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
elseif method then -- function
|
||||
method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
if AceEvent_debugTable then
|
||||
local dmem, dtime = memdiff, timediff
|
||||
mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
|
||||
AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
|
||||
AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
|
||||
AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1
|
||||
|
||||
memdiff, timediff = table.remove(memstack), table.remove(timestack)
|
||||
if memdiff then
|
||||
memdiff = memdiff + mem + dmem
|
||||
timediff = timediff + time + dtime
|
||||
end
|
||||
end
|
||||
end
|
||||
tmp[obj] = nil
|
||||
obj = next(tmp)
|
||||
end
|
||||
del(tmp)
|
||||
end
|
||||
if AceEvent_registry[ALL_EVENTS] then
|
||||
local tmp = new()
|
||||
for obj, method in pairs(AceEvent_registry[ALL_EVENTS]) do
|
||||
tmp[obj] = method
|
||||
end
|
||||
local obj = next(tmp)
|
||||
while obj do
|
||||
local method = tmp[obj]
|
||||
local mem, time
|
||||
if AceEvent_debugTable then
|
||||
if not AceEvent_debugTable[event] then
|
||||
AceEvent_debugTable[event] = new()
|
||||
end
|
||||
if not AceEvent_debugTable[event][obj] then
|
||||
AceEvent_debugTable[event][obj] = new()
|
||||
AceEvent_debugTable[event][obj].mem = 0
|
||||
AceEvent_debugTable[event][obj].time = 0
|
||||
AceEvent_debugTable[event][obj].count = 0
|
||||
end
|
||||
if memdiff then
|
||||
table.insert(memstack, memdiff)
|
||||
table.insert(timestack, timediff)
|
||||
end
|
||||
memdiff, timediff = 0, 0
|
||||
mem, time = gcinfo(), GetTime()
|
||||
end
|
||||
if type(method) == "string" then
|
||||
local obj_method = obj[method]
|
||||
if obj_method then
|
||||
obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
elseif method then -- function
|
||||
method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
if AceEvent_debugTable then
|
||||
local dmem, dtime = memdiff, timediff
|
||||
mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
|
||||
AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
|
||||
AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
|
||||
AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1
|
||||
|
||||
memdiff, timediff = table.remove(memstack), table.remove(timestack)
|
||||
if memdiff then
|
||||
memdiff = memdiff + mem + dmem
|
||||
timediff = timediff + time + dtime
|
||||
end
|
||||
end
|
||||
tmp[obj] = nil
|
||||
obj = next(tmp)
|
||||
end
|
||||
del(tmp)
|
||||
end
|
||||
_G.event = _G_event
|
||||
AceEvent.currentEvent = lastEvent
|
||||
end
|
||||
|
||||
-- local accessors
|
||||
local getn = table.getn
|
||||
local tinsert = table.insert
|
||||
local tremove = table.remove
|
||||
local floor = math.floor
|
||||
local GetTime = GetTime
|
||||
local next = next
|
||||
local pairs = pairs
|
||||
local unpack = unpack
|
||||
|
||||
local delayRegistry
|
||||
local tmp = {}
|
||||
local function OnUpdate()
|
||||
local t = GetTime()
|
||||
for k,v in pairs(delayRegistry) do
|
||||
tmp[k] = true
|
||||
end
|
||||
for k in pairs(tmp) do
|
||||
local v = delayRegistry[k]
|
||||
if v then
|
||||
local v_time = v.time
|
||||
if not v_time then
|
||||
delayRegistry[k] = del(v)
|
||||
elseif v_time <= t then
|
||||
local v_repeatDelay = v.repeatDelay
|
||||
if v_repeatDelay then
|
||||
-- use the event time, not the current time, else timing inaccuracies add up over time
|
||||
v.time = v_time + v_repeatDelay
|
||||
end
|
||||
local event = v.event
|
||||
local mem, time
|
||||
if AceEvent_debugTable then
|
||||
mem, time = gcinfo(), GetTime()
|
||||
end
|
||||
if type(event) == "function" then
|
||||
event(unpack(v))
|
||||
else
|
||||
AceEvent:TriggerEvent(event, unpack(v))
|
||||
end
|
||||
if AceEvent_debugTable then
|
||||
mem, time = gcinfo() - mem, GetTime() - time
|
||||
v.mem = v.mem + mem
|
||||
v.timeSpent = v.timeSpent + time
|
||||
v.count = v.count + 1
|
||||
end
|
||||
if not v_repeatDelay then
|
||||
local x = delayRegistry[k]
|
||||
if x and x.time == v_time then -- check if it was manually reset
|
||||
delayRegistry[k] = del(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for k in pairs(tmp) do
|
||||
tmp[k] = nil
|
||||
end
|
||||
if not next(delayRegistry) then
|
||||
AceEvent.frame:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local function ScheduleEvent(self, repeating, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
local id
|
||||
if type(event) == "string" or type(event) == "table" then
|
||||
if type(event) == "table" then
|
||||
if not delayRegistry or not delayRegistry[event] then
|
||||
AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
|
||||
end
|
||||
end
|
||||
if type(delay) ~= "number" then
|
||||
id, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20
|
||||
AceEvent:argCheck(event, 3, "string", "function", --[[ so message is right ]] "number")
|
||||
AceEvent:argCheck(delay, 4, "number")
|
||||
self:CancelScheduledEvent(id)
|
||||
end
|
||||
else
|
||||
AceEvent:argCheck(event, 2, "string", "function")
|
||||
AceEvent:argCheck(delay, 3, "number")
|
||||
end
|
||||
|
||||
if not delayRegistry then
|
||||
AceEvent.delayRegistry = new()
|
||||
delayRegistry = AceEvent.delayRegistry
|
||||
AceEvent.frame:SetScript("OnUpdate", OnUpdate)
|
||||
end
|
||||
local t
|
||||
if type(id) == "table" then
|
||||
for k in pairs(id) do
|
||||
id[k] = nil
|
||||
end
|
||||
t = id
|
||||
else
|
||||
t = new()
|
||||
end
|
||||
t[1] = a1
|
||||
t[2] = a2
|
||||
t[3] = a3
|
||||
t[4] = a4
|
||||
t[5] = a5
|
||||
t[6] = a6
|
||||
t[7] = a7
|
||||
t[8] = a8
|
||||
t[9] = a9
|
||||
t[10] = a10
|
||||
t[11] = a11
|
||||
t[12] = a12
|
||||
t[13] = a13
|
||||
t[14] = a14
|
||||
t[15] = a15
|
||||
t[16] = a16
|
||||
t[17] = a17
|
||||
t[18] = a18
|
||||
t[19] = a19
|
||||
t[20] = a20
|
||||
table_setn(t, 20)
|
||||
t.event = event
|
||||
t.time = GetTime() + delay
|
||||
t.self = self
|
||||
t.id = id or t
|
||||
t.repeatDelay = repeating and delay
|
||||
if AceEvent_debugTable then
|
||||
t.mem = 0
|
||||
t.count = 0
|
||||
t.timeSpent = 0
|
||||
end
|
||||
delayRegistry[t.id] = t
|
||||
AceEvent.frame:Show()
|
||||
return t.id
|
||||
end
|
||||
|
||||
function AceEvent:ScheduleEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if type(event) == "string" or type(event) == "table" then
|
||||
if type(event) == "table" then
|
||||
if not delayRegistry or not delayRegistry[event] then
|
||||
AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
|
||||
end
|
||||
end
|
||||
if type(delay) ~= "number" then
|
||||
AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
|
||||
AceEvent:argCheck(a1, 4, "number")
|
||||
end
|
||||
else
|
||||
AceEvent:argCheck(event, 2, "string", "function")
|
||||
AceEvent:argCheck(delay, 3, "number")
|
||||
end
|
||||
|
||||
return ScheduleEvent(self, false, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
|
||||
function AceEvent:ScheduleRepeatingEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if type(event) == "string" or type(event) == "table" then
|
||||
if type(event) == "table" then
|
||||
if not delayRegistry or not delayRegistry[event] then
|
||||
AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
|
||||
end
|
||||
end
|
||||
if type(delay) ~= "number" then
|
||||
AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
|
||||
AceEvent:argCheck(a1, 4, "number")
|
||||
end
|
||||
else
|
||||
AceEvent:argCheck(event, 2, "string", "function")
|
||||
AceEvent:argCheck(delay, 3, "number")
|
||||
end
|
||||
|
||||
return ScheduleEvent(self, true, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
|
||||
function AceEvent:CancelScheduledEvent(t)
|
||||
AceEvent:argCheck(t, 2, "string", "table")
|
||||
if delayRegistry then
|
||||
local v = delayRegistry[t]
|
||||
if v then
|
||||
delayRegistry[t] = del(v)
|
||||
if not next(delayRegistry) then
|
||||
AceEvent.frame:Hide()
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function AceEvent:IsEventScheduled(t)
|
||||
AceEvent:argCheck(t, 2, "string", "table")
|
||||
if delayRegistry then
|
||||
local v = delayRegistry[t]
|
||||
if v then
|
||||
return true, v.time - GetTime()
|
||||
end
|
||||
end
|
||||
return false, nil
|
||||
end
|
||||
|
||||
function AceEvent:UnregisterEvent(event)
|
||||
AceEvent:argCheck(event, 2, "string")
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if AceEvent_registry[event] and AceEvent_registry[event][self] then
|
||||
AceEvent_registry[event][self] = nil
|
||||
local AceEvent_onceRegistry = AceEvent.onceRegistry
|
||||
if AceEvent_onceRegistry and AceEvent_onceRegistry[event] and AceEvent_onceRegistry[event][self] then
|
||||
AceEvent_onceRegistry[event][self] = nil
|
||||
if not next(AceEvent_onceRegistry[event]) then
|
||||
AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
|
||||
end
|
||||
end
|
||||
local AceEvent_throttleRegistry = AceEvent.throttleRegistry
|
||||
if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] and AceEvent_throttleRegistry[event][self] then
|
||||
AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
|
||||
if not next(AceEvent_throttleRegistry[event]) then
|
||||
AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
|
||||
end
|
||||
end
|
||||
if not next(AceEvent_registry[event]) then
|
||||
AceEvent_registry[event] = del(AceEvent_registry[event])
|
||||
if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then
|
||||
AceEvent.frame:UnregisterEvent(event)
|
||||
end
|
||||
end
|
||||
else
|
||||
if self == AceEvent then
|
||||
error(string.format("Cannot unregister event %q. Improperly unregistering from AceEvent-2.0.", event), 2)
|
||||
else
|
||||
AceEvent:error("Cannot unregister event %q. %q is not registered with it.", event, self)
|
||||
end
|
||||
end
|
||||
AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
|
||||
end
|
||||
|
||||
function AceEvent:UnregisterAllEvents()
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then
|
||||
AceEvent_registry[ALL_EVENTS][self] = nil
|
||||
if not next(AceEvent_registry[ALL_EVENTS]) then
|
||||
del(AceEvent_registry[ALL_EVENTS])
|
||||
AceEvent.frame:UnregisterAllEvents()
|
||||
for k,v in pairs(AceEvent_registry) do
|
||||
if k ~= ALL_EVENTS then
|
||||
AceEvent.frame:RegisterEvent(k)
|
||||
end
|
||||
end
|
||||
AceEvent_registry[event] = nil
|
||||
end
|
||||
end
|
||||
local first = true
|
||||
for event, data in pairs(AceEvent_registry) do
|
||||
if first then
|
||||
if AceEvent_registry.AceEvent_EventUnregistered then
|
||||
event = "AceEvent_EventUnregistered"
|
||||
else
|
||||
first = false
|
||||
end
|
||||
end
|
||||
local x = data[self]
|
||||
data[self] = nil
|
||||
if x and event ~= ALL_EVENTS then
|
||||
if not next(data) then
|
||||
del(data)
|
||||
if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then
|
||||
AceEvent.frame:UnregisterEvent(event)
|
||||
end
|
||||
AceEvent_registry[event] = nil
|
||||
end
|
||||
AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
|
||||
end
|
||||
if first then
|
||||
event = nil
|
||||
end
|
||||
end
|
||||
if AceEvent.onceRegistry then
|
||||
for event, data in pairs(AceEvent.onceRegistry) do
|
||||
data[self] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceEvent:CancelAllScheduledEvents()
|
||||
if delayRegistry then
|
||||
for k,v in pairs(delayRegistry) do
|
||||
if v.self == self then
|
||||
delayRegistry[k] = del(v)
|
||||
end
|
||||
end
|
||||
if not next(delayRegistry) then
|
||||
AceEvent.frame:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceEvent:IsEventRegistered(event)
|
||||
AceEvent:argCheck(event, 2, "string")
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if self == AceEvent then
|
||||
return AceEvent_registry[event] and next(AceEvent_registry[event]) and true or false
|
||||
end
|
||||
if AceEvent_registry[event] and AceEvent_registry[event][self] then
|
||||
return true, AceEvent_registry[event][self]
|
||||
end
|
||||
return false, nil
|
||||
end
|
||||
|
||||
local bucketfunc
|
||||
function AceEvent:RegisterBucketEvent(event, delay, method)
|
||||
AceEvent:argCheck(event, 2, "string", "table")
|
||||
if type(event) == "table" then
|
||||
for k,v in pairs(event) do
|
||||
if type(k) ~= "number" then
|
||||
AceEvent:error("All keys to argument #2 to `RegisterBucketEvent' must be numbers.")
|
||||
elseif type(v) ~= "string" then
|
||||
AceEvent:error("All values to argument #2 to `RegisterBucketEvent' must be strings.")
|
||||
end
|
||||
end
|
||||
end
|
||||
AceEvent:argCheck(delay, 3, "number")
|
||||
if AceEvent == self then
|
||||
AceEvent:argCheck(method, 4, "function")
|
||||
self = method
|
||||
else
|
||||
if type(event) == "string" then
|
||||
AceEvent:argCheck(method, 4, "string", "function", "nil")
|
||||
if not method then
|
||||
method = event
|
||||
end
|
||||
else
|
||||
AceEvent:argCheck(method, 4, "string", "function")
|
||||
end
|
||||
|
||||
if type(method) == "string" and type(self[method]) ~= "function" then
|
||||
AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
|
||||
end
|
||||
end
|
||||
if not AceEvent.buckets then
|
||||
AceEvent.buckets = new()
|
||||
end
|
||||
if not AceEvent.buckets[event] then
|
||||
AceEvent.buckets[event] = new()
|
||||
end
|
||||
if not AceEvent.buckets[event][self] then
|
||||
AceEvent.buckets[event][self] = new()
|
||||
AceEvent.buckets[event][self].current = new()
|
||||
AceEvent.buckets[event][self].self = self
|
||||
else
|
||||
AceEvent.CancelScheduledEvent(self, AceEvent.buckets[event][self].id)
|
||||
end
|
||||
local bucket = AceEvent.buckets[event][self]
|
||||
bucket.method = method
|
||||
|
||||
local func = function(arg1)
|
||||
bucket.run = true
|
||||
if arg1 then
|
||||
bucket.current[arg1] = true
|
||||
end
|
||||
end
|
||||
AceEvent.buckets[event][self].func = func
|
||||
if type(event) == "string" then
|
||||
AceEvent.RegisterEvent(self, event, func)
|
||||
else
|
||||
for _,v in ipairs(event) do
|
||||
AceEvent.RegisterEvent(self, v, func)
|
||||
end
|
||||
end
|
||||
if not bucketfunc then
|
||||
bucketfunc = function(bucket)
|
||||
local current = bucket.current
|
||||
local method = bucket.method
|
||||
local self = bucket.self
|
||||
if bucket.run then
|
||||
if type(method) == "string" then
|
||||
self[method](self, current)
|
||||
elseif method then -- function
|
||||
method(current)
|
||||
end
|
||||
for k in pairs(current) do
|
||||
current[k] = nil
|
||||
k = nil
|
||||
end
|
||||
bucket.run = false
|
||||
end
|
||||
end
|
||||
end
|
||||
bucket.id = AceEvent.ScheduleRepeatingEvent(self, bucketfunc, delay, bucket)
|
||||
end
|
||||
|
||||
function AceEvent:IsBucketEventRegistered(event)
|
||||
AceEvent:argCheck(event, 2, "string", "table")
|
||||
return AceEvent.buckets and AceEvent.buckets[event] and AceEvent.buckets[event][self]
|
||||
end
|
||||
|
||||
function AceEvent:UnregisterBucketEvent(event)
|
||||
AceEvent:argCheck(event, 2, "string", "table")
|
||||
if not AceEvent.buckets or not AceEvent.buckets[event] or not AceEvent.buckets[event][self] then
|
||||
AceEvent:error("Cannot unregister bucket event %q. %q is not registered with it.", event, self)
|
||||
end
|
||||
|
||||
local bucket = AceEvent.buckets[event][self]
|
||||
|
||||
if type(event) == "string" then
|
||||
AceEvent.UnregisterEvent(self, event)
|
||||
else
|
||||
for _,v in ipairs(event) do
|
||||
AceEvent.UnregisterEvent(self, v)
|
||||
end
|
||||
end
|
||||
AceEvent:CancelScheduledEvent(bucket.id)
|
||||
|
||||
del(bucket.current)
|
||||
AceEvent.buckets[event][self] = del(AceEvent.buckets[event][self])
|
||||
if not next(AceEvent.buckets[event]) then
|
||||
AceEvent.buckets[event] = del(AceEvent.buckets[event])
|
||||
end
|
||||
end
|
||||
|
||||
function AceEvent:UnregisterAllBucketEvents()
|
||||
if not AceEvent.buckets or not next(AceEvent.buckets) then
|
||||
return
|
||||
end
|
||||
for k,v in pairs(AceEvent.buckets) do
|
||||
if v == self then
|
||||
AceEvent.UnregisterBucketEvent(self, k)
|
||||
k = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceEvent:OnEmbedDisable(target)
|
||||
self.UnregisterAllEvents(target)
|
||||
|
||||
self.CancelAllScheduledEvents(target)
|
||||
|
||||
self.UnregisterAllBucketEvents(target)
|
||||
end
|
||||
|
||||
function AceEvent:EnableDebugging()
|
||||
if not self.debugTable then
|
||||
self.debugTable = new()
|
||||
|
||||
if delayRegistry then
|
||||
for k,v in pairs(self.delayRegistry) do
|
||||
if not v.mem then
|
||||
v.mem = 0
|
||||
v.count = 0
|
||||
v.timeSpent = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceEvent:IsFullyInitialized()
|
||||
return self.postInit or false
|
||||
end
|
||||
|
||||
function AceEvent:IsPostPlayerLogin()
|
||||
return self.playerLogin or false
|
||||
end
|
||||
|
||||
function AceEvent:activate(oldLib, oldDeactivate)
|
||||
AceEvent = self
|
||||
|
||||
if oldLib then
|
||||
self.onceRegistry = oldLib.onceRegistry
|
||||
self.throttleRegistry = oldLib.throttleRegistry
|
||||
self.delayRegistry = oldLib.delayRegistry
|
||||
self.buckets = oldLib.buckets
|
||||
self.registry = oldLib.registry
|
||||
self.frame = oldLib.frame
|
||||
self.debugTable = oldLib.debugTable
|
||||
self.playerLogin = oldLib.pew or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage and true
|
||||
self.postInit = oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true
|
||||
self.ALL_EVENTS = oldLib.ALL_EVENTS
|
||||
self.FAKE_NIL = oldLib.FAKE_NIL
|
||||
self.RATE = oldLib.RATE
|
||||
end
|
||||
if not self.registry then
|
||||
self.registry = {}
|
||||
end
|
||||
if not self.frame then
|
||||
self.frame = CreateFrame("Frame", "AceEvent20Frame")
|
||||
end
|
||||
if not self.ALL_EVENTS then
|
||||
self.ALL_EVENTS = {}
|
||||
end
|
||||
if not self.FAKE_NIL then
|
||||
self.FAKE_NIL = {}
|
||||
end
|
||||
if not self.RATE then
|
||||
self.RATE = {}
|
||||
end
|
||||
ALL_EVENTS = self.ALL_EVENTS
|
||||
FAKE_NIL = self.FAKE_NIL
|
||||
RATE = self.RATE
|
||||
local inPlw = false
|
||||
local blacklist = {
|
||||
UNIT_INVENTORY_CHANGED = true,
|
||||
BAG_UPDATE = true,
|
||||
ITEM_LOCK_CHANGED = true,
|
||||
ACTIONBAR_SLOT_CHANGED = true,
|
||||
}
|
||||
self.frame:SetScript("OnEvent", function()
|
||||
local event = event
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
inPlw = false
|
||||
elseif event == "PLAYER_LEAVING_WORLD" then
|
||||
inPlw = true
|
||||
end
|
||||
if event and (not inPlw or not blacklist[event]) then
|
||||
self:TriggerEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
|
||||
end
|
||||
end)
|
||||
if self.delayRegistry then
|
||||
delayRegistry = self.delayRegistry
|
||||
self.frame:SetScript("OnUpdate", OnUpdate)
|
||||
end
|
||||
|
||||
self:UnregisterAllEvents()
|
||||
self:CancelAllScheduledEvents()
|
||||
|
||||
registeringFromAceEvent = true
|
||||
self:RegisterEvent("LOOT_OPENED", function()
|
||||
SendAddonMessage("LOOT_OPENED", "", "RAID")
|
||||
end)
|
||||
registeringFromAceEvent = nil
|
||||
|
||||
if not self.playerLogin then
|
||||
registeringFromAceEvent = true
|
||||
self:RegisterEvent("PLAYER_LOGIN", function()
|
||||
self.playerLogin = true
|
||||
end, true)
|
||||
registeringFromAceEvent = nil
|
||||
end
|
||||
|
||||
if not self.postInit then
|
||||
local isReload = true
|
||||
local function func()
|
||||
self.postInit = true
|
||||
self:TriggerEvent("AceEvent_FullyInitialized")
|
||||
if self.registry["CHAT_MSG_CHANNEL_NOTICE"] and self.registry["CHAT_MSG_CHANNEL_NOTICE"][self] then
|
||||
self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE")
|
||||
end
|
||||
if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
|
||||
self:UnregisterEvent("MEETINGSTONE_CHANGED")
|
||||
end
|
||||
if self.registry["MINIMAP_ZONE_CHANGED"] and self.registry["MINIMAP_ZONE_CHANGED"][self] then
|
||||
self:UnregisterEvent("MINIMAP_ZONE_CHANGED")
|
||||
end
|
||||
if self.registry["LANGUAGE_LIST_CHANGED"] and self.registry["LANGUAGE_LIST_CHANGED"][self] then
|
||||
self:UnregisterEvent("LANGUAGE_LIST_CHANGED")
|
||||
end
|
||||
end
|
||||
registeringFromAceEvent = true
|
||||
local f = function()
|
||||
self.playerLogin = true
|
||||
self:ScheduleEvent("AceEvent_FullyInitialized", func, 1)
|
||||
end
|
||||
self:RegisterEvent("MEETINGSTONE_CHANGED", f, true)
|
||||
self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", function()
|
||||
self:ScheduleEvent("AceEvent_FullyInitialized", func, 0.05)
|
||||
end)
|
||||
self:RegisterEvent("LANGUAGE_LIST_CHANGED", function()
|
||||
if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
|
||||
registeringFromAceEvent = true
|
||||
self:UnregisterEvent("MEETINGSTONE_CHANGED")
|
||||
self:RegisterEvent("MINIMAP_ZONE_CHANGED", f, true)
|
||||
registeringFromAceEvent = nil
|
||||
end
|
||||
end)
|
||||
self:ScheduleEvent("AceEvent_FullyInitialized", func, 10)
|
||||
registeringFromAceEvent = nil
|
||||
end
|
||||
|
||||
self.super.activate(self, oldLib, oldDeactivate)
|
||||
if oldLib then
|
||||
oldDeactivate(oldLib)
|
||||
end
|
||||
end
|
||||
|
||||
AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, AceEvent.activate)
|
||||
AceEvent = AceLibrary(MAJOR_VERSION)
|
||||
@@ -0,0 +1,569 @@
|
||||
--[[
|
||||
Name: AceHook-2.1
|
||||
Revision: $Rev: 17638 $
|
||||
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/AceHook-2.1
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceHook-2.1
|
||||
Description: Mixin to allow for safe hooking of functions, methods, and scripts.
|
||||
Dependencies: AceLibrary, AceOO-2.0
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceHook-2.1"
|
||||
local MINOR_VERSION = "$Revision: 17638 $"
|
||||
|
||||
-- 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
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
|
||||
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
|
||||
|
||||
--[[---------------------------------------------------------------------------------
|
||||
Create the library object
|
||||
----------------------------------------------------------------------------------]]
|
||||
|
||||
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
|
||||
local AceHook = AceOO.Mixin {
|
||||
"Hook",
|
||||
"HookScript",
|
||||
"SecureHook",
|
||||
"Unhook",
|
||||
"UnhookAll",
|
||||
"HookReport",
|
||||
"IsHooked",
|
||||
}
|
||||
|
||||
--[[---------------------------------------------------------------------------------
|
||||
Library Definitions
|
||||
----------------------------------------------------------------------------------]]
|
||||
|
||||
local protFuncs = {
|
||||
CameraOrSelectOrMoveStart = true, CameraOrSelectOrMoveStop = true,
|
||||
TurnOrActionStart = true, TurnOrActionStop = true,
|
||||
PitchUpStart = true, PitchUpStop = true,
|
||||
PitchDownStart = true, PitchDownStop = true,
|
||||
MoveBackwardStart = true, MoveBackwardStop = true,
|
||||
MoveForwardStart = true, MoveForwardStop = true,
|
||||
Jump = true, StrafeLeftStart = true,
|
||||
StrafeLeftStop = true, StrafeRightStart = true,
|
||||
StrafeRightStop = true, ToggleMouseMove = true,
|
||||
ToggleRun = true, TurnLeftStart = true,
|
||||
TurnLeftStop = true, TurnRightStart = true,
|
||||
TurnRightStop = true,
|
||||
}
|
||||
|
||||
local function issecurevariable(x)
|
||||
if protFuncs[x] then
|
||||
return 1
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local _G = getfenv(0)
|
||||
|
||||
local function hooksecurefunc(arg1, arg2, arg3)
|
||||
if type(arg1) == "string" then
|
||||
arg1, arg2, arg3 = _G, arg1, arg2
|
||||
end
|
||||
local orig = arg1[arg2]
|
||||
arg1[arg2] = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
local x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20 = orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
|
||||
arg3(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
|
||||
return x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20
|
||||
end
|
||||
end
|
||||
|
||||
local protectedScripts = {
|
||||
OnClick = true,
|
||||
}
|
||||
|
||||
|
||||
local handlers, scripts, actives, registry
|
||||
|
||||
--[[---------------------------------------------------------------------------------
|
||||
Private definitions (Not exposed)
|
||||
----------------------------------------------------------------------------------]]
|
||||
|
||||
local new, del
|
||||
do
|
||||
local list = setmetatable({}, {__mode = "k"})
|
||||
function new()
|
||||
local t = next(list)
|
||||
if not t then
|
||||
return {}
|
||||
end
|
||||
list[t] = nil
|
||||
return t
|
||||
end
|
||||
|
||||
function del(t)
|
||||
setmetatable(t, nil)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
list[t] = true
|
||||
end
|
||||
end
|
||||
|
||||
local function createFunctionHook(self, func, handler, orig, secure)
|
||||
if not secure then
|
||||
if type(handler) == "string" then
|
||||
-- The handler is a method, need to self it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
else
|
||||
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
else
|
||||
-- The handler is a function, just call it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
else
|
||||
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
end
|
||||
else
|
||||
-- secure hooks don't call the original method
|
||||
if type(handler) == "string" then
|
||||
-- The handler is a method, need to self it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
else
|
||||
-- The handler is a function, just call it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function createMethodHook(self, object, method, handler, orig, secure, script)
|
||||
if script then
|
||||
if type(handler) == "string" then
|
||||
local uid
|
||||
uid = function()
|
||||
if actives[uid] then
|
||||
return self[handler](self, object)
|
||||
else
|
||||
return orig()
|
||||
end
|
||||
end
|
||||
return uid
|
||||
else
|
||||
-- The handler is a function, just call it
|
||||
local uid
|
||||
uid = function()
|
||||
if actives[uid] then
|
||||
return handler(object)
|
||||
else
|
||||
return orig()
|
||||
end
|
||||
end
|
||||
return uid
|
||||
end
|
||||
elseif not secure then
|
||||
if type(handler) == "string" then
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
else
|
||||
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
else
|
||||
-- The handler is a function, just call it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
else
|
||||
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
end
|
||||
else
|
||||
-- secure hooks don't call the original method
|
||||
if type(handler) == "string" then
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
else
|
||||
-- The handler is a function, just call it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function hookFunction(self, func, handler, secure)
|
||||
local orig = _G[func]
|
||||
|
||||
if not orig or type(orig) ~= "function" then
|
||||
AceHook:error("Attempt to hook a non-existant function %q", func)
|
||||
end
|
||||
|
||||
if not handler then
|
||||
handler = func
|
||||
end
|
||||
|
||||
if registry[self][func] then
|
||||
local uid = registry[self][func]
|
||||
|
||||
if actives[uid] then
|
||||
-- We have an active hook from this source. Don't multi-hook
|
||||
AceHook:error("%q already has an active hook from this source.", func)
|
||||
end
|
||||
|
||||
if handlers[uid] == handler then
|
||||
-- The hook is inactive, so reactivate it
|
||||
actives[uid] = true
|
||||
return
|
||||
else
|
||||
AceHook:error("There is a stale hook for %q can't hook or reactivate.", func)
|
||||
end
|
||||
end
|
||||
|
||||
if type(handler) == "string" then
|
||||
if type(self[handler]) ~= "function" then
|
||||
AceHook:error("Could not find the the handler %q when hooking function %q", handler, func)
|
||||
end
|
||||
elseif type(handler) ~= "function" then
|
||||
AceHook:error("Could not find the handler you supplied when hooking %q", func)
|
||||
end
|
||||
|
||||
local uid = createFunctionHook(self, func, handler, orig, secure)
|
||||
registry[self][func] = uid
|
||||
actives[uid] = true
|
||||
handlers[uid] = handler
|
||||
|
||||
if not secure then
|
||||
_G[func] = uid
|
||||
self.hooks[func] = orig
|
||||
else
|
||||
hooksecurefunc(func, uid)
|
||||
end
|
||||
end
|
||||
|
||||
local function unhookFunction(self, func)
|
||||
if not registry[self][func] then
|
||||
AceHook:error("Tried to unhook %q which is not currently hooked.", func)
|
||||
end
|
||||
|
||||
local uid = registry[self][func]
|
||||
|
||||
if actives[uid] then
|
||||
-- See if we own the global function
|
||||
if self.hooks[func] and _G[func] == uid then
|
||||
_G[func] = self.hooks[func]
|
||||
self.hooks[func] = nil
|
||||
registry[self][func] = nil
|
||||
handlers[uid] = nil
|
||||
scripts[uid] = nil
|
||||
actives[uid] = nil
|
||||
-- Magically all-done
|
||||
else
|
||||
actives[uid] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function hookMethod(self, obj, method, handler, script, secure)
|
||||
if not handler then
|
||||
handler = method
|
||||
end
|
||||
|
||||
if not obj or type(obj) ~= "table" then
|
||||
AceHook:error("The object you supplied could not be found, or isn't a table.")
|
||||
end
|
||||
|
||||
local uid = registry[self][obj] and registry[self][obj][method]
|
||||
if uid then
|
||||
if actives[uid] then
|
||||
-- We have an active hook from this source. Don't multi-hook
|
||||
AceHook:error("%q already has an active hook from this source.", method)
|
||||
end
|
||||
|
||||
if handlers[uid] == handler then
|
||||
-- The hook is inactive, reactivate it.
|
||||
actives[uid] = true
|
||||
return
|
||||
else
|
||||
AceHook:error("There is a stale hook for %q can't hook or reactivate.", method)
|
||||
end
|
||||
end
|
||||
|
||||
if type(handler) == "string" then
|
||||
if type(self[handler]) ~= "function" then
|
||||
AceHook:error("Could not find the handler %q you supplied when hooking method %q", handler, method)
|
||||
end
|
||||
elseif type(handler) ~= "function" then
|
||||
AceHook:error("Could not find the handler you supplied when hooking method %q", method)
|
||||
end
|
||||
|
||||
local orig
|
||||
if script then
|
||||
if not obj.GetScript then
|
||||
AceHook:error("The object you supplied does not have a GetScript method.")
|
||||
end
|
||||
if not obj:HasScript(method) then
|
||||
AceHook:error("The object you supplied doesn't allow the %q method.", method)
|
||||
end
|
||||
|
||||
orig = obj:GetScript(method)
|
||||
if type(orig) ~= "function" then
|
||||
-- Sometimes there is not a original function for a script.
|
||||
orig = function() end
|
||||
end
|
||||
else
|
||||
orig = obj[method]
|
||||
end
|
||||
if not orig then
|
||||
AceHook:error("Could not find the method or script %q you are trying to hook.", method)
|
||||
end
|
||||
|
||||
if not registry[self][obj] then
|
||||
registry[self][obj] = new()
|
||||
end
|
||||
|
||||
if not self.hooks[obj] then
|
||||
self.hooks[obj] = new()
|
||||
end
|
||||
|
||||
local uid = createMethodHook(self, obj, method, handler, orig, secure, script)
|
||||
registry[self][obj][method] = uid
|
||||
actives[uid] = true
|
||||
handlers[uid] = handler
|
||||
scripts[uid] = script and true or nil
|
||||
|
||||
if script then
|
||||
obj:SetScript(method, uid)
|
||||
self.hooks[obj][method] = orig
|
||||
elseif not secure then
|
||||
obj[method] = uid
|
||||
self.hooks[obj][method] = orig
|
||||
else
|
||||
hooksecurefunc(obj, method, uid)
|
||||
end
|
||||
end
|
||||
|
||||
local function unhookMethod(self, obj, method)
|
||||
if not registry[self][obj] or not registry[self][obj][method] then
|
||||
AceHook:error("Attempt to unhook a method %q that is not currently hooked.", method)
|
||||
return
|
||||
end
|
||||
|
||||
local uid = registry[self][obj][method]
|
||||
|
||||
if actives[uid] then
|
||||
if scripts[uid] then -- If this is a script
|
||||
if obj:GetScript(method) == uid then
|
||||
-- We own the script. Revert to normal.
|
||||
obj:SetScript(method, self.hooks[obj][method])
|
||||
self.hooks[obj][method] = nil
|
||||
registry[self][obj][method] = nil
|
||||
handlers[uid] = nil
|
||||
scripts[uid] = nil
|
||||
actives[uid] = nil
|
||||
else
|
||||
actives[uid] = nil
|
||||
end
|
||||
else
|
||||
if self.hooks[obj] and self.hooks[obj][method] and obj[method] == uid then
|
||||
-- We own the method. Revert to normal.
|
||||
obj[method] = self.hooks[obj][method]
|
||||
self.hooks[obj][method] = nil
|
||||
registry[self][obj][method] = nil
|
||||
handlers[uid] = nil
|
||||
actives[uid] = nil
|
||||
else
|
||||
actives[uid] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.hooks[obj] and not next(self.hooks[obj]) then
|
||||
self.hooks[obj] = del(self.hooks[obj])
|
||||
end
|
||||
if not next(registry[self][obj]) then
|
||||
registry[self][obj] = del(registry[self][obj])
|
||||
end
|
||||
end
|
||||
|
||||
-- ("function" [, handler] [, hookSecure]) or (object, "method" [, handler] [, hookSecure])
|
||||
function AceHook:Hook(object, method, handler, hookSecure)
|
||||
if type(object) == "string" then
|
||||
method, handler, hookSecure = object, method, handler
|
||||
if handler == true then
|
||||
handler, hookSecure = nil, true
|
||||
end
|
||||
if not hookSecure and issecurevariable(method) then
|
||||
AceHook:error("Attempt to hook secure function %q. Use `SecureHook' or add `true' to the argument list to override.", method)
|
||||
end
|
||||
AceHook:argCheck(handler, 3, "function", "string", "nil")
|
||||
AceHook:argCheck(hookSecure, 4, "boolean", "nil")
|
||||
hookFunction(self, method, handler, false)
|
||||
else
|
||||
if handler == true then
|
||||
handler, hookSecure = nil, true
|
||||
end
|
||||
if not hookSecure and issecurevariable(object, method) then
|
||||
AceHook:error("Attempt to hook secure method %q. Use `SecureHook' or add `true' to the argument list to override.", method)
|
||||
end
|
||||
AceHook:argCheck(object, 2, "table")
|
||||
AceHook:argCheck(method, 3, "string")
|
||||
AceHook:argCheck(handler, 4, "function", "string", "nil")
|
||||
AceHook:argCheck(hookSecure, 5, "boolean", "nil")
|
||||
hookMethod(self, object, method, handler, false, false)
|
||||
end
|
||||
end
|
||||
|
||||
-- ("function", handler) or (object, "method", handler)
|
||||
function AceHook:SecureHook(object, method, handler)
|
||||
if type(object) == "string" then
|
||||
method, handler = object, method
|
||||
AceHook:argCheck(handler, 3, "function", "string", "nil")
|
||||
hookFunction(self, method, handler, true)
|
||||
else
|
||||
AceHook:argCheck(object, 2, "table")
|
||||
AceHook:argCheck(method, 3, "string")
|
||||
AceHook:argCheck(handler, 4, "function", "string", "nil")
|
||||
hookMethod(self, object, method, handler, false, true)
|
||||
end
|
||||
end
|
||||
|
||||
function AceHook:HookScript(frame, script, handler)
|
||||
AceHook:argCheck(frame, 2, "table")
|
||||
if not frame[0] or type(frame.IsFrameType) ~= "function" then
|
||||
AceHook:error("Bad argument #2 to `HookScript'. Expected frame.")
|
||||
end
|
||||
AceHook:argCheck(script, 3, "string")
|
||||
AceHook:argCheck(handler, 4, "function", "string", "nil")
|
||||
hookMethod(self, frame, script, handler, true, false)
|
||||
end
|
||||
|
||||
-- ("function") or (object, "method")
|
||||
function AceHook:IsHooked(obj, method)
|
||||
if type(obj) == "string" then
|
||||
if registry[self][obj] and actives[registry[self][obj]] then
|
||||
return true, handlers[registry[self][obj]]
|
||||
end
|
||||
else
|
||||
AceHook:argCheck(obj, 2, "string", "table")
|
||||
AceHook:argCheck(method, 3, "string")
|
||||
if registry[self][obj] and registry[self][obj][method] and actives[registry[self][obj][method]] then
|
||||
return true, handlers[registry[self][obj][method]]
|
||||
end
|
||||
end
|
||||
|
||||
return false, nil
|
||||
end
|
||||
|
||||
-- ("function") or (object, "method")
|
||||
function AceHook:Unhook(obj, method)
|
||||
if type(obj) == "string" then
|
||||
unhookFunction(self, obj)
|
||||
else
|
||||
AceHook:argCheck(obj, 2, "string", "table")
|
||||
AceHook:argCheck(method, 3, "string")
|
||||
unhookMethod(self, obj, method)
|
||||
end
|
||||
end
|
||||
|
||||
function AceHook:UnhookAll()
|
||||
for key, value in pairs(registry[self]) do
|
||||
if type(key) == "table" then
|
||||
for method in pairs(value) do
|
||||
self:Unhook(key, method)
|
||||
end
|
||||
else
|
||||
self:Unhook(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceHook:HookReport()
|
||||
DEFAULT_CHAT_FRAME:AddMessage("This is a list of all active hooks for this object:")
|
||||
if not next(registry[self]) then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("No hooks")
|
||||
end
|
||||
|
||||
for key, value in pairs(registry[self]) do
|
||||
if type(value) == "table" then
|
||||
for method, uid in pairs(value) do
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("object: %s method: %q |cff%s|r%s", tostring(key), method, actives[uid] and "00ff00Active" or "ffff00Inactive", not self.hooks[key][method] and " |cff7f7fff-Secure-|r" or ""))
|
||||
end
|
||||
else
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("function: %q |cff%s|r%s", tostring(key), actives[value] and "00ff00Active" or "ffff00Inactive", not self.hooks[key] and " |cff7f7fff-Secure-|r" or ""))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceHook:OnInstanceInit(object)
|
||||
if not object.hooks then
|
||||
object.hooks = new()
|
||||
end
|
||||
if not registry[object] then
|
||||
registry[object] = new()
|
||||
end
|
||||
end
|
||||
|
||||
AceHook.OnManualEmbed = AceHook.OnInstanceInit
|
||||
|
||||
function AceHook:OnEmbedDisable(target)
|
||||
self.UnhookAll(target)
|
||||
end
|
||||
|
||||
local function activate(self, oldLib, oldDeactivate)
|
||||
AceHook = self
|
||||
|
||||
self.handlers = oldLib and oldLib.handlers or {}
|
||||
self.registry = oldLib and oldLib.registry or {}
|
||||
self.scripts = oldLib and oldLib.scripts or {}
|
||||
self.actives = oldLib and oldLib.actives or {}
|
||||
|
||||
handlers = self.handlers
|
||||
registry = self.registry
|
||||
scripts = self.scripts
|
||||
actives = self.actives
|
||||
|
||||
AceHook.super.activate(self, oldLib, oldDeactivate)
|
||||
|
||||
if oldDeactivate then
|
||||
oldDeactivate(oldLib)
|
||||
end
|
||||
end
|
||||
|
||||
AceLibrary:Register(AceHook, MAJOR_VERSION, MINOR_VERSION, activate)
|
||||
@@ -0,0 +1,757 @@
|
||||
--[[
|
||||
Name: AceLibrary
|
||||
Revision: $Rev: 17722 $
|
||||
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: 17722 $
|
||||
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
|
||||
]]
|
||||
|
||||
local ACELIBRARY_MAJOR = "AceLibrary"
|
||||
local ACELIBRARY_MINOR = "$Revision: 17722 $"
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary and AceLibrary:HasInstance(ACELIBRARY_MAJOR) then return end -- lua51 check
|
||||
local table_setn
|
||||
do
|
||||
local version = GetBuildInfo()
|
||||
if string.find(version, "^2%.") then
|
||||
-- 2.0.0
|
||||
table_setn = function() end
|
||||
else
|
||||
table_setn = table.setn
|
||||
end
|
||||
end
|
||||
|
||||
local string_gfind = string.gmatch or string.gfind
|
||||
|
||||
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,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
local success, err = pcall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
if not success then geterrorhandler()(err) end
|
||||
end
|
||||
|
||||
-- @table AceLibrary
|
||||
-- @brief System to handle all versioning of libraries.
|
||||
local AceLibrary = {}
|
||||
local AceLibrary_mt = {}
|
||||
setmetatable(AceLibrary, AceLibrary_mt)
|
||||
|
||||
local tmp
|
||||
local function error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if type(self) ~= "table" then
|
||||
_G.error(string.format("Bad argument #1 to `error' (table expected, got %s)", type(self)), 2)
|
||||
end
|
||||
if not tmp then
|
||||
tmp = {}
|
||||
else
|
||||
for k in pairs(tmp) do tmp[k] = nil end
|
||||
table_setn(tmp, 0)
|
||||
end
|
||||
|
||||
table.insert(tmp, a1)
|
||||
table.insert(tmp, a2)
|
||||
table.insert(tmp, a3)
|
||||
table.insert(tmp, a4)
|
||||
table.insert(tmp, a5)
|
||||
table.insert(tmp, a6)
|
||||
table.insert(tmp, a7)
|
||||
table.insert(tmp, a8)
|
||||
table.insert(tmp, a9)
|
||||
table.insert(tmp, a10)
|
||||
table.insert(tmp, a11)
|
||||
table.insert(tmp, a12)
|
||||
table.insert(tmp, a13)
|
||||
table.insert(tmp, a14)
|
||||
table.insert(tmp, a15)
|
||||
table.insert(tmp, a16)
|
||||
table.insert(tmp, a17)
|
||||
table.insert(tmp, a18)
|
||||
table.insert(tmp, a19)
|
||||
table.insert(tmp, a20)
|
||||
|
||||
local stack = debugstack()
|
||||
if not message then
|
||||
local _,_,second = string.find(stack, "\n(.-)\n")
|
||||
message = "error raised! " .. second
|
||||
else
|
||||
for i = 1,table.getn(tmp) do
|
||||
tmp[i] = tostring(tmp[i])
|
||||
end
|
||||
for i = 1,10 do
|
||||
table.insert(tmp, "nil")
|
||||
end
|
||||
message = string.format(message, unpack(tmp))
|
||||
end
|
||||
|
||||
if getmetatable(self) and getmetatable(self).__tostring then
|
||||
message = string.format("%s: %s", tostring(self), message)
|
||||
elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then
|
||||
message = string.format("%s: %s", self:GetLibraryVersion(), message)
|
||||
elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then
|
||||
message = string.format("%s: %s", self.class:GetLibraryVersion(), message)
|
||||
end
|
||||
|
||||
local first = string.gsub(stack, "\n.*", "")
|
||||
local file = string.gsub(first, ".*\\(.*).lua:%d+: .*", "%1")
|
||||
file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
|
||||
|
||||
local i = 0
|
||||
for s in string_gfind(stack, "\n([^\n]*)") do
|
||||
i = i + 1
|
||||
if not string.find(s, file .. "%.lua:%d+:") then
|
||||
file = string.gsub(s, "^.*\\(.*).lua:%d+: .*", "%1")
|
||||
file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
|
||||
break
|
||||
end
|
||||
end
|
||||
local j = 0
|
||||
for s in string_gfind(stack, "\n([^\n]*)") do
|
||||
j = j + 1
|
||||
if j > i and not string.find(s, file .. "%.lua:%d+:") then
|
||||
_G.error(message, j + 1)
|
||||
return
|
||||
end
|
||||
end
|
||||
_G.error(message, 2)
|
||||
return
|
||||
end
|
||||
|
||||
local function assert(self, condition, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not condition then
|
||||
if not message then
|
||||
local stack = debugstack()
|
||||
local _,_,second = string.find(stack, "\n(.-)\n")
|
||||
message = "assertion failed! " .. second
|
||||
end
|
||||
error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
return
|
||||
end
|
||||
return condition
|
||||
end
|
||||
|
||||
local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5)
|
||||
if type(num) ~= "number" then
|
||||
error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num))
|
||||
elseif type(kind) ~= "string" then
|
||||
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 _,_,func = string.find(debugstack(), "`argCheck'.-([`<].-['>])")
|
||||
if not func then
|
||||
_,_,func = string.find(debugstack(), "([`<].-['>])")
|
||||
end
|
||||
if kind5 then
|
||||
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
|
||||
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
|
||||
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
|
||||
error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg)
|
||||
else
|
||||
error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function pcall(self, func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = _G.pcall(func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not a1 then
|
||||
error(self, string.gsub(a2, ".-%.lua:%d-: ", ""))
|
||||
else
|
||||
return a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20
|
||||
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 string.find(text, "^%$Revision: (%d+) %$$") then
|
||||
return tonumber((string.gsub(text, "^%$Revision: (%d+) %$$", "%1")))
|
||||
elseif string.find(text, "^%$Rev: (%d+) %$$") then
|
||||
return tonumber((string.gsub(text, "^%$Rev: (%d+) %$$", "%1")))
|
||||
elseif string.find(text, "^%$LastChangedRevision: (%d+) %$$") then
|
||||
return tonumber((string.gsub(text, "^%$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
|
||||
table_setn(t, 0)
|
||||
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
|
||||
|
||||
local new, del
|
||||
do
|
||||
local tables = setmetatable({}, {__mode = "k"})
|
||||
|
||||
function new()
|
||||
local t = next(tables)
|
||||
if t then
|
||||
tables[t] = nil
|
||||
return t
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
function del(t, depth)
|
||||
if depth and depth > 0 then
|
||||
for k,v in pairs(t) do
|
||||
if type(v) == "table" and not isFrame(v) then
|
||||
del(v, depth - 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
destroyTable(t)
|
||||
tables[t] = true
|
||||
end
|
||||
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 = new()
|
||||
for k,v in pairs(from) do to[k] = v end
|
||||
table_setn(to, table.getn(from))
|
||||
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)
|
||||
local createdList
|
||||
if not list then
|
||||
createdList = true
|
||||
list = new()
|
||||
list2 = new()
|
||||
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
|
||||
table_setn(to, table.getn(from))
|
||||
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)
|
||||
if createdList then
|
||||
del(list)
|
||||
del(list2)
|
||||
end
|
||||
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(string.format("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate", 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(string.format("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate", 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(string.format("Cannot find a library instance of %s.", major), 2)
|
||||
return
|
||||
end
|
||||
if minor then
|
||||
if type(minor) == "string" then
|
||||
local m = svnRevisionToNumber(minor)
|
||||
if m then
|
||||
minor = m
|
||||
else
|
||||
_G.error(string.format("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
|
||||
end
|
||||
end
|
||||
argCheck(self, minor, 2, "number")
|
||||
if data.minor ~= minor then
|
||||
_G.error(string.format("Cannot find a library instance of %s, minor version %d.", major, minor), 2)
|
||||
return
|
||||
end
|
||||
end
|
||||
return data.instance
|
||||
end
|
||||
|
||||
-- Syntax sugar. AceLibrary("FooBar-1.0")
|
||||
AceLibrary_mt.__call = AceLibrary.GetInstance
|
||||
|
||||
local donothing
|
||||
|
||||
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 type(minor) == "string" then
|
||||
local m = svnRevisionToNumber(minor)
|
||||
if m then
|
||||
minor = m
|
||||
else
|
||||
_G.error(string.format("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate", 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
|
||||
if not donothing then
|
||||
donothing = function() end
|
||||
end
|
||||
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
|
||||
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(string.format("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", major, data.minor, minor, major, minor), 2)
|
||||
return
|
||||
end
|
||||
-- This is an update
|
||||
local oldInstance = new()
|
||||
|
||||
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)
|
||||
else
|
||||
safecall(oldDeactivateFunc, oldInstance)
|
||||
end
|
||||
del(oldInstance)
|
||||
|
||||
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)
|
||||
if not self.libs then
|
||||
if oldLib then
|
||||
self.libs = oldLib.libs
|
||||
self.scannedlibs = oldLib.scannedlibs
|
||||
end
|
||||
if not self.libs then
|
||||
self.libs = {}
|
||||
end
|
||||
if not self.scannedlibs then
|
||||
self.scannedlibs = {}
|
||||
end
|
||||
end
|
||||
if not self.positions then
|
||||
if oldLib then
|
||||
self.positions = oldLib.positions
|
||||
end
|
||||
if not self.positions then
|
||||
self.positions = setmetatable({}, { __mode = "k" })
|
||||
end
|
||||
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)
|
||||
@@ -0,0 +1,538 @@
|
||||
--[[
|
||||
Name: AceLocale-2.2
|
||||
Revision: $Rev: 17638 $
|
||||
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/AceLocale-2.2
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceLocale-2.2
|
||||
Description: Localization library for addons to use to handle proper
|
||||
localization and internationalization.
|
||||
Dependencies: AceLibrary
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceLocale-2.2"
|
||||
local MINOR_VERSION = "$Revision: 17639 $"
|
||||
|
||||
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
|
||||
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
|
||||
local AceLocale = {}
|
||||
|
||||
local DEFAULT_LOCALE = "enUS"
|
||||
local _G = getfenv(0)
|
||||
|
||||
local BASE_TRANSLATIONS, DEBUGGING, TRANSLATIONS, BASE_LOCALE, TRANSLATION_TABLES, REVERSE_TRANSLATIONS, STRICTNESS, DYNAMIC_LOCALES, CURRENT_LOCALE, NAME
|
||||
|
||||
local rawget = rawget
|
||||
local rawset = rawset
|
||||
local type = type
|
||||
|
||||
local newRegistries = {}
|
||||
local scheduleClear
|
||||
|
||||
local lastSelf
|
||||
local __index = function(self, key)
|
||||
lastSelf = self
|
||||
local value = (rawget(self, TRANSLATIONS) or AceLocale.prototype)[key]
|
||||
rawset(self, key, value)
|
||||
return value
|
||||
end
|
||||
|
||||
local __newindex = function(self, k, v)
|
||||
if type(v) ~= "function" and type(k) ~= "table" then
|
||||
AceLocale.error(self, "Cannot change the values of an AceLocale instance.")
|
||||
end
|
||||
rawset(self, k, v)
|
||||
end
|
||||
|
||||
local __tostring = function(self)
|
||||
if type(rawget(self, 'GetLibraryVersion')) == "function" then
|
||||
return self:GetLibraryVersion()
|
||||
else
|
||||
return "AceLocale(" .. self[NAME] .. ")"
|
||||
end
|
||||
end
|
||||
|
||||
local function clearCache(self)
|
||||
if not rawget(self, BASE_TRANSLATIONS) then
|
||||
return
|
||||
end
|
||||
|
||||
local cache = self[BASE_TRANSLATIONS]
|
||||
rawset(self, REVERSE_TRANSLATIONS, nil)
|
||||
|
||||
for k in pairs(self) do
|
||||
if rawget(cache, k) ~= nil then
|
||||
self[k] = nil
|
||||
end
|
||||
end
|
||||
rawset(self, 'tmp', true)
|
||||
self.tmp = nil
|
||||
end
|
||||
|
||||
local function refixInstance(instance)
|
||||
if getmetatable(instance) then
|
||||
setmetatable(instance, nil)
|
||||
end
|
||||
local translations = instance[TRANSLATIONS]
|
||||
if translations then
|
||||
if getmetatable(translations) then
|
||||
setmetatable(translations, nil)
|
||||
end
|
||||
local baseTranslations = instance[BASE_TRANSLATIONS]
|
||||
if getmetatable(baseTranslations) then
|
||||
setmetatable(baseTranslations, nil)
|
||||
end
|
||||
if translations == baseTranslations or instance[STRICTNESS] then
|
||||
setmetatable(instance, {
|
||||
__index = __index,
|
||||
__newindex = __newindex,
|
||||
__tostring = __tostring
|
||||
})
|
||||
|
||||
setmetatable(translations, {
|
||||
__index = AceLocale.prototype
|
||||
})
|
||||
else
|
||||
setmetatable(instance, {
|
||||
__index = __index,
|
||||
__newindex = __newindex,
|
||||
__tostring = __tostring
|
||||
})
|
||||
|
||||
setmetatable(translations, {
|
||||
__index = baseTranslations,
|
||||
})
|
||||
|
||||
setmetatable(baseTranslations, {
|
||||
__index = AceLocale.prototype,
|
||||
})
|
||||
end
|
||||
else
|
||||
setmetatable(instance, {
|
||||
__index = __index,
|
||||
__newindex = __newindex,
|
||||
__tostring = __tostring,
|
||||
})
|
||||
end
|
||||
clearCache(instance)
|
||||
newRegistries[instance] = true
|
||||
scheduleClear()
|
||||
return instance
|
||||
end
|
||||
|
||||
function AceLocale:new(name)
|
||||
self:argCheck(name, 2, "string")
|
||||
|
||||
if self.registry[name] and type(rawget(self.registry[name], 'GetLibraryVersion')) ~= "function" then
|
||||
return self.registry[name]
|
||||
end
|
||||
|
||||
AceLocale.registry[name] = refixInstance({
|
||||
[STRICTNESS] = false,
|
||||
[NAME] = name,
|
||||
})
|
||||
newRegistries[AceLocale.registry[name]] = true
|
||||
return AceLocale.registry[name]
|
||||
end
|
||||
|
||||
AceLocale.prototype = { class = AceLocale }
|
||||
|
||||
function AceLocale.prototype:EnableDebugging()
|
||||
if rawget(self, BASE_TRANSLATIONS) then
|
||||
AceLocale.error(self, "Cannot enable debugging after a translation has been registered.")
|
||||
end
|
||||
rawset(self, DEBUGGING, true)
|
||||
end
|
||||
|
||||
function AceLocale.prototype:EnableDynamicLocales(override)
|
||||
AceLocale.argCheck(self, override, 2, "boolean", "nil")
|
||||
if not override and rawget(self, BASE_TRANSLATIONS) then
|
||||
AceLocale.error(self, "Cannot enable dynamic locales after a translation has been registered.")
|
||||
end
|
||||
if not rawget(self, DYNAMIC_LOCALES) then
|
||||
rawset(self, DYNAMIC_LOCALES, true)
|
||||
if rawget(self, BASE_LOCALE) then
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
rawset(self, TRANSLATION_TABLES, {})
|
||||
end
|
||||
self[TRANSLATION_TABLES][self[BASE_LOCALE]] = self[BASE_TRANSLATIONS]
|
||||
self[TRANSLATION_TABLES][self[CURRENT_LOCALE]] = self[TRANSLATIONS]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceLocale.prototype:RegisterTranslations(locale, func)
|
||||
AceLocale.argCheck(self, locale, 2, "string")
|
||||
AceLocale.argCheck(self, func, 3, "function")
|
||||
|
||||
if locale == rawget(self, BASE_LOCALE) then
|
||||
AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale)
|
||||
end
|
||||
|
||||
if rawget(self, BASE_TRANSLATIONS) and GetLocale() ~= locale then
|
||||
if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
rawset(self, TRANSLATION_TABLES, {})
|
||||
end
|
||||
if self[TRANSLATION_TABLES][locale] then
|
||||
AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale)
|
||||
end
|
||||
local t = func()
|
||||
func = nil
|
||||
if type(t) ~= "table" then
|
||||
AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t))
|
||||
end
|
||||
self[TRANSLATION_TABLES][locale] = t
|
||||
t = nil
|
||||
end
|
||||
func = nil
|
||||
return
|
||||
end
|
||||
local t = func()
|
||||
func = nil
|
||||
if type(t) ~= "table" then
|
||||
AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t))
|
||||
end
|
||||
|
||||
rawset(self, TRANSLATIONS, t)
|
||||
if not rawget(self, BASE_TRANSLATIONS) then
|
||||
rawset(self, BASE_TRANSLATIONS, t)
|
||||
rawset(self, BASE_LOCALE, locale)
|
||||
for key,value in pairs(t) do
|
||||
if value == true then
|
||||
t[key] = key
|
||||
end
|
||||
end
|
||||
else
|
||||
for key, value in pairs(self[TRANSLATIONS]) do
|
||||
if not rawget(self[BASE_TRANSLATIONS], key) then
|
||||
AceLocale.error(self, "Improper translation exists. %q is likely misspelled for locale %s.", key, locale)
|
||||
end
|
||||
if value == true then
|
||||
AceLocale.error(self, "Can only accept true as a value on the base locale. %q is the base locale, %q is not.", rawget(self, BASE_LOCALE), locale)
|
||||
end
|
||||
end
|
||||
end
|
||||
rawset(self, CURRENT_LOCALE, locale)
|
||||
refixInstance(self)
|
||||
if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
rawset(self, TRANSLATION_TABLES, {})
|
||||
end
|
||||
self[TRANSLATION_TABLES][locale] = t
|
||||
end
|
||||
t = nil
|
||||
end
|
||||
|
||||
function AceLocale.prototype:SetLocale(locale)
|
||||
AceLocale.argCheck(self, locale, 2, "string", "boolean")
|
||||
if not rawget(self, DYNAMIC_LOCALES) then
|
||||
AceLocale.error(self, "Cannot call `SetLocale' without first calling `EnableDynamicLocales'.")
|
||||
end
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
AceLocale.error(self, "Cannot call `SetLocale' without first calling `RegisterTranslations'.")
|
||||
end
|
||||
if locale == true then
|
||||
locale = GetLocale()
|
||||
if not self[TRANSLATION_TABLES][locale] then
|
||||
locale = self[BASE_LOCALE]
|
||||
end
|
||||
end
|
||||
|
||||
if self[CURRENT_LOCALE] == locale then
|
||||
return
|
||||
end
|
||||
|
||||
if not self[TRANSLATION_TABLES][locale] then
|
||||
AceLocale.error(self, "Locale %q not registered.", locale)
|
||||
end
|
||||
|
||||
self[TRANSLATIONS] = self[TRANSLATION_TABLES][locale]
|
||||
self[CURRENT_LOCALE] = locale
|
||||
refixInstance(self)
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetLocale()
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
AceLocale.error(self, "Cannot call `GetLocale' without first calling `RegisterTranslations'.")
|
||||
end
|
||||
return self[CURRENT_LOCALE]
|
||||
end
|
||||
|
||||
local function iter(t, position)
|
||||
return (next(t, position))
|
||||
end
|
||||
|
||||
function AceLocale.prototype:IterateAvailableLocales()
|
||||
if not rawget(self, DYNAMIC_LOCALES) then
|
||||
AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `EnableDynamicLocales'.")
|
||||
end
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `RegisterTranslations'.")
|
||||
end
|
||||
return iter, self[TRANSLATION_TABLES], nil
|
||||
end
|
||||
|
||||
function AceLocale.prototype:HasLocale(locale)
|
||||
if not rawget(self, DYNAMIC_LOCALES) then
|
||||
AceLocale.error(self, "Cannot call `HasLocale' without first calling `EnableDynamicLocales'.")
|
||||
end
|
||||
AceLocale.argCheck(self, locale, 2, "string")
|
||||
return rawget(self, TRANSLATION_TABLES) and self[TRANSLATION_TABLES][locale] ~= nil
|
||||
end
|
||||
|
||||
function AceLocale.prototype:SetStrictness(strict)
|
||||
AceLocale.argCheck(self, strict, 2, "boolean")
|
||||
local mt = getmetatable(self)
|
||||
if not mt then
|
||||
AceLocale.error(self, "Cannot call `SetStrictness' without a metatable.")
|
||||
end
|
||||
if not rawget(self, TRANSLATIONS) then
|
||||
AceLocale.error(self, "No translations registered.")
|
||||
end
|
||||
rawset(self, STRICTNESS, strict)
|
||||
refixInstance(self)
|
||||
end
|
||||
|
||||
local function initReverse(self)
|
||||
rawset(self, REVERSE_TRANSLATIONS, {})
|
||||
local alpha = self[TRANSLATIONS]
|
||||
local bravo = self[REVERSE_TRANSLATIONS]
|
||||
for base, localized in pairs(alpha) do
|
||||
bravo[localized] = base
|
||||
end
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetTranslation(text)
|
||||
AceLocale.argCheck(self, text, 1, "string", "number")
|
||||
if not rawget(self, TRANSLATIONS) then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
return self[text]
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetStrictTranslation(text)
|
||||
AceLocale.argCheck(self, text, 1, "string", "number")
|
||||
local x = rawget(self, TRANSLATIONS)
|
||||
if not x then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
local value = rawget(x, text)
|
||||
if value == nil then
|
||||
AceLocale.error(self, "Translation %q does not exist for locale %s", text, self[CURRENT_LOCALE])
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetReverseTranslation(text)
|
||||
local x = rawget(self, REVERSE_TRANSLATIONS)
|
||||
if not x then
|
||||
if not rawget(self, TRANSLATIONS) then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
initReverse(self)
|
||||
x = self[REVERSE_TRANSLATIONS]
|
||||
end
|
||||
local translation = x[text]
|
||||
if not translation then
|
||||
AceLocale.error(self, "Reverse translation for %q does not exist", text)
|
||||
end
|
||||
return translation
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetIterator()
|
||||
local x = rawget(self, TRANSLATIONS)
|
||||
if not x then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
return next, x, nil
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetReverseIterator()
|
||||
local x = rawget(self, REVERSE_TRANSLATIONS)
|
||||
if not x then
|
||||
if not rawget(self, TRANSLATIONS) then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
initReverse(self)
|
||||
x = self[REVERSE_TRANSLATIONS]
|
||||
end
|
||||
return next, x, nil
|
||||
end
|
||||
|
||||
function AceLocale.prototype:HasTranslation(text)
|
||||
AceLocale.argCheck(self, text, 1, "string", "number")
|
||||
local x = rawget(self, TRANSLATIONS)
|
||||
if not x then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
return rawget(x, text) and true
|
||||
end
|
||||
|
||||
function AceLocale.prototype:HasReverseTranslation(text)
|
||||
local x = rawget(self, REVERSE_TRANSLATIONS)
|
||||
if not x then
|
||||
if not rawget(self, TRANSLATIONS) then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
initReverse(self)
|
||||
x = self[REVERSE_TRANSLATIONS]
|
||||
end
|
||||
return x[text] and true
|
||||
end
|
||||
|
||||
function AceLocale.prototype:Debug()
|
||||
if not rawget(self, DEBUGGING) then
|
||||
return
|
||||
end
|
||||
local words = {}
|
||||
local locales = {"enUS", "ruRU", "deDE", "frFR", "koKR", "zhCN", "zhTW", "esES"}
|
||||
local localizations = {}
|
||||
DEFAULT_CHAT_FRAME:AddMessage("--- AceLocale Debug ---")
|
||||
for _,locale in ipairs(locales) do
|
||||
if not self[TRANSLATION_TABLES][locale] then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q not found", locale))
|
||||
else
|
||||
localizations[locale] = self[TRANSLATION_TABLES][locale]
|
||||
end
|
||||
end
|
||||
local localeDebug = {}
|
||||
for locale, localization in pairs(localizations) do
|
||||
localeDebug[locale] = {}
|
||||
for word in pairs(localization) do
|
||||
if type(localization[word]) == "table" then
|
||||
if type(words[word]) ~= "table" then
|
||||
words[word] = {}
|
||||
end
|
||||
for bit in pairs(localization[word]) do
|
||||
if type(localization[word][bit]) == "string" then
|
||||
words[word][bit] = true
|
||||
end
|
||||
end
|
||||
elseif type(localization[word]) == "string" then
|
||||
words[word] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
for word in pairs(words) do
|
||||
if type(words[word]) == "table" then
|
||||
for bit in pairs(words[word]) do
|
||||
for locale, localization in pairs(localizations) do
|
||||
if not rawget(localization, word) or not localization[word][bit] then
|
||||
localeDebug[locale][word .. "::" .. bit] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
for locale, localization in pairs(localizations) do
|
||||
if not rawget(localization, word) then
|
||||
localeDebug[locale][word] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for locale, t in pairs(localeDebug) do
|
||||
if not next(t) then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q complete", locale))
|
||||
else
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q missing:", locale))
|
||||
for word in pairs(t) do
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format(" %q", word))
|
||||
end
|
||||
end
|
||||
end
|
||||
DEFAULT_CHAT_FRAME:AddMessage("--- End AceLocale Debug ---")
|
||||
end
|
||||
|
||||
setmetatable(AceLocale.prototype, {
|
||||
__index = function(self, k)
|
||||
if type(k) ~= "table" and k ~= 0 and k ~= "GetLibraryVersion" and k ~= "error" and k ~= "assert" and k ~= "argCheck" and k ~= "pcall" then -- HACK: remove "GetLibraryVersion" and such later.
|
||||
AceLocale.error(lastSelf or self, "Translation %q does not exist.", k)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
})
|
||||
|
||||
local function activate(self, oldLib, oldDeactivate)
|
||||
AceLocale = self
|
||||
|
||||
self.frame = oldLib and oldLib.frame or CreateFrame("Frame")
|
||||
self.registry = oldLib and oldLib.registry or {}
|
||||
self.BASE_TRANSLATIONS = oldLib and oldLib.BASE_TRANSLATIONS or {}
|
||||
self.DEBUGGING = oldLib and oldLib.DEBUGGING or {}
|
||||
self.TRANSLATIONS = oldLib and oldLib.TRANSLATIONS or {}
|
||||
self.BASE_LOCALE = oldLib and oldLib.BASE_LOCALE or {}
|
||||
self.TRANSLATION_TABLES = oldLib and oldLib.TRANSLATION_TABLES or {}
|
||||
self.REVERSE_TRANSLATIONS = oldLib and oldLib.REVERSE_TRANSLATIONS or {}
|
||||
self.STRICTNESS = oldLib and oldLib.STRICTNESS or {}
|
||||
self.NAME = oldLib and oldLib.NAME or {}
|
||||
self.DYNAMIC_LOCALES = oldLib and oldLib.DYNAMIC_LOCALES or {}
|
||||
self.CURRENT_LOCALE = oldLib and oldLib.CURRENT_LOCALE or {}
|
||||
|
||||
BASE_TRANSLATIONS = self.BASE_TRANSLATIONS
|
||||
DEBUGGING = self.DEBUGGING
|
||||
TRANSLATIONS = self.TRANSLATIONS
|
||||
BASE_LOCALE = self.BASE_LOCALE
|
||||
TRANSLATION_TABLES = self.TRANSLATION_TABLES
|
||||
REVERSE_TRANSLATIONS = self.REVERSE_TRANSLATIONS
|
||||
STRICTNESS = self.STRICTNESS
|
||||
NAME = self.NAME
|
||||
DYNAMIC_LOCALES = self.DYNAMIC_LOCALES
|
||||
CURRENT_LOCALE = self.CURRENT_LOCALE
|
||||
|
||||
|
||||
local GetTime = GetTime
|
||||
local timeUntilClear = GetTime() + 5
|
||||
scheduleClear = function()
|
||||
if next(newRegistries) then
|
||||
self.frame:Show()
|
||||
timeUntilClear = GetTime() + 5
|
||||
end
|
||||
end
|
||||
|
||||
if not self.registry then
|
||||
self.registry = {}
|
||||
else
|
||||
for name, instance in pairs(self.registry) do
|
||||
local name = name
|
||||
local mt = getmetatable(instance)
|
||||
setmetatable(instance, nil)
|
||||
instance[NAME] = name
|
||||
local strict
|
||||
if instance[STRICTNESS] ~= nil then
|
||||
strict = instance[STRICTNESS]
|
||||
elseif instance[TRANSLATIONS] ~= instance[BASE_TRANSLATIONS] then
|
||||
if getmetatable(instance[TRANSLATIONS]).__index == oldLib.prototype then
|
||||
strict = true
|
||||
end
|
||||
end
|
||||
instance[STRICTNESS] = strict and true or false
|
||||
refixInstance(instance)
|
||||
end
|
||||
end
|
||||
|
||||
self.frame:SetScript("OnEvent", scheduleClear)
|
||||
self.frame:SetScript("OnUpdate", function() -- (this, elapsed)
|
||||
if timeUntilClear - GetTime() <= 0 then
|
||||
self.frame:Hide()
|
||||
for k in pairs(newRegistries) do
|
||||
clearCache(k)
|
||||
newRegistries[k] = nil
|
||||
k = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
self.frame:UnregisterAllEvents()
|
||||
self.frame:RegisterEvent("ADDON_LOADED")
|
||||
self.frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
self.frame:Show()
|
||||
|
||||
if oldDeactivate then
|
||||
oldDeactivate(oldLib)
|
||||
end
|
||||
end
|
||||
|
||||
AceLibrary:Register(AceLocale, MAJOR_VERSION, MINOR_VERSION, activate)
|
||||
+462
@@ -0,0 +1,462 @@
|
||||
--[[
|
||||
Name: AceModuleCore-2.0
|
||||
Revision: $Rev: 17998 $
|
||||
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/AceModuleCore-2.0
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceModuleCore-2.0
|
||||
Description: Mixin to provide a module system so that modules or plugins can
|
||||
use an addon as its core.
|
||||
Dependencies: AceLibrary, AceOO-2.0, AceAddon-2.0, AceEvent-2.0 (optional), Compost-2.0 (optional)
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceModuleCore-2.0"
|
||||
local MINOR_VERSION = "$Revision: 17998 $"
|
||||
|
||||
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
|
||||
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
|
||||
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
|
||||
|
||||
local function safecall(func,a,b,c,d,e,f,g)
|
||||
local success, err = pcall(func,a,b,c,d,e,f,g)
|
||||
if not success then geterrorhandler()(err) end
|
||||
end
|
||||
|
||||
local table_setn
|
||||
do
|
||||
local version = GetBuildInfo()
|
||||
if string.find(version, "^2%.") then
|
||||
-- 2.0.0
|
||||
table_setn = function() end
|
||||
else
|
||||
table_setn = table.setn
|
||||
end
|
||||
end
|
||||
|
||||
local new, del
|
||||
do
|
||||
local list = setmetatable({}, {__mode = 'k'})
|
||||
function new()
|
||||
local t = next(list)
|
||||
if t then
|
||||
list[t] = nil
|
||||
return t
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
function del(t)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
table_setn(t, 0)
|
||||
list[t] = true
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
|
||||
local AceModuleCore = AceOO.Mixin {
|
||||
"NewModule",
|
||||
"HasModule",
|
||||
"GetModule",
|
||||
"IsModule",
|
||||
"IterateModules",
|
||||
"SetModuleMixins",
|
||||
"SetModuleClass",
|
||||
"IsModuleActive",
|
||||
"ToggleModuleActive"
|
||||
}
|
||||
local AceEvent
|
||||
|
||||
local Compost = AceLibrary:HasInstance("Compost-2.0") and AceLibrary("Compost-2.0")
|
||||
|
||||
local function getlibrary(lib)
|
||||
if type(lib) == "string" then
|
||||
return AceLibrary(lib)
|
||||
else
|
||||
return lib
|
||||
end
|
||||
end
|
||||
|
||||
local tmp
|
||||
function AceModuleCore:NewModule(name, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not self.modules then
|
||||
AceModuleCore:error("CreatePrototype() must be called before attempting to create a new module.", 2)
|
||||
end
|
||||
AceModuleCore:argCheck(name, 2, "string")
|
||||
if string.len(name) == 0 then
|
||||
AceModuleCore:error("Bad argument #2 to `NewModule`, string must not be empty")
|
||||
end
|
||||
if self.modules[name] then
|
||||
AceModuleCore:error("The module %q has already been registered", name)
|
||||
end
|
||||
|
||||
if not tmp then
|
||||
tmp = {}
|
||||
end
|
||||
if a1 then table.insert(tmp, a1)
|
||||
if a2 then table.insert(tmp, a2)
|
||||
if a3 then table.insert(tmp, a3)
|
||||
if a4 then table.insert(tmp, a4)
|
||||
if a5 then table.insert(tmp, a5)
|
||||
if a6 then table.insert(tmp, a6)
|
||||
if a7 then table.insert(tmp, a7)
|
||||
if a8 then table.insert(tmp, a8)
|
||||
if a9 then table.insert(tmp, a9)
|
||||
if a10 then table.insert(tmp, a10)
|
||||
if a11 then table.insert(tmp, a11)
|
||||
if a12 then table.insert(tmp, a12)
|
||||
if a13 then table.insert(tmp, a13)
|
||||
if a14 then table.insert(tmp, a14)
|
||||
if a15 then table.insert(tmp, a15)
|
||||
if a16 then table.insert(tmp, a16)
|
||||
if a17 then table.insert(tmp, a17)
|
||||
if a18 then table.insert(tmp, a18)
|
||||
if a19 then table.insert(tmp, a19)
|
||||
if a20 then table.insert(tmp, a20)
|
||||
end end end end end end end end end end end end end end end end end end end end
|
||||
for k,v in ipairs(tmp) do
|
||||
tmp[k] = getlibrary(v)
|
||||
end
|
||||
|
||||
if self.moduleMixins then
|
||||
for _,mixin in ipairs(self.moduleMixins) do
|
||||
local exists = false
|
||||
for _,v in ipairs(tmp) do
|
||||
if mixin == v then
|
||||
exists = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not exists then
|
||||
table.insert(tmp, mixin)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local module = AceOO.Classpool(self.moduleClass, unpack(tmp)):new(name)
|
||||
self.modules[name] = module
|
||||
module.name = name
|
||||
module.title = name
|
||||
|
||||
AceModuleCore.totalModules[module] = self
|
||||
|
||||
if AceEvent then
|
||||
AceEvent:TriggerEvent("Ace2_ModuleCreated", module)
|
||||
end
|
||||
|
||||
for k in pairs(tmp) do
|
||||
tmp[k] = nil
|
||||
end
|
||||
table_setn(tmp, 0)
|
||||
return module
|
||||
end
|
||||
|
||||
function AceModuleCore:HasModule(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if a1 then if not self.modules[a1] then return false end
|
||||
if a2 then if not self.modules[a2] then return false end
|
||||
if a3 then if not self.modules[a3] then return false end
|
||||
if a4 then if not self.modules[a4] then return false end
|
||||
if a5 then if not self.modules[a5] then return false end
|
||||
if a6 then if not self.modules[a6] then return false end
|
||||
if a7 then if not self.modules[a7] then return false end
|
||||
if a8 then if not self.modules[a8] then return false end
|
||||
if a9 then if not self.modules[a9] then return false end
|
||||
if a10 then if not self.modules[a10] then return false end
|
||||
if a11 then if not self.modules[a11] then return false end
|
||||
if a12 then if not self.modules[a12] then return false end
|
||||
if a13 then if not self.modules[a13] then return false end
|
||||
if a14 then if not self.modules[a14] then return false end
|
||||
if a15 then if not self.modules[a15] then return false end
|
||||
if a16 then if not self.modules[a16] then return false end
|
||||
if a17 then if not self.modules[a17] then return false end
|
||||
if a18 then if not self.modules[a18] then return false end
|
||||
if a19 then if not self.modules[a19] then return false end
|
||||
if a20 then if not self.modules[a20] then return false end
|
||||
end end end end end end end end end end end end end end end end end end end end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function AceModuleCore:GetModule(name)
|
||||
if not self.modules then
|
||||
AceModuleCore:error("Error initializing class. Please report error.")
|
||||
end
|
||||
if not self.modules[name] then
|
||||
AceModuleCore:error("Cannot find module %q.", name)
|
||||
end
|
||||
return self.modules[name]
|
||||
end
|
||||
|
||||
function AceModuleCore:IsModule(module)
|
||||
if self == AceModuleCore then
|
||||
return AceModuleCore.totalModules[module]
|
||||
else
|
||||
for k,v in pairs(self.modules) do
|
||||
if v == module then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function AceModuleCore:IterateModules()
|
||||
local t = new()
|
||||
for k in pairs(self.modules) do
|
||||
table.insert(t, k)
|
||||
end
|
||||
table.sort(t)
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
local x = t[i]
|
||||
if x then
|
||||
return x, self.modules[x]
|
||||
else
|
||||
t = del(t)
|
||||
return nil
|
||||
end
|
||||
end, nil, nil
|
||||
end
|
||||
|
||||
function AceModuleCore:SetModuleMixins(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if self.moduleMixins then
|
||||
AceModuleCore:error('Cannot call "SetModuleMixins" twice')
|
||||
elseif not self.modules then
|
||||
AceModuleCore:error("Error initializing class. Please report error.")
|
||||
elseif next(self.modules) then
|
||||
AceModuleCore:error('Cannot call "SetModuleMixins" after "NewModule" has been called.')
|
||||
end
|
||||
|
||||
self.moduleMixins = Compost and Compost:Acquire(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) or {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20}
|
||||
for k,v in ipairs(self.moduleMixins) do
|
||||
self.moduleMixins[k] = getlibrary(v)
|
||||
end
|
||||
end
|
||||
|
||||
function AceModuleCore:SetModuleClass(class)
|
||||
class = getlibrary(class)
|
||||
AceModuleCore:assert(AceOO.inherits(class, AceOO.Class), "Bad argument #2 to `SetModuleClass' (Class expected)")
|
||||
if not self.modules then
|
||||
AceModuleCore:error("Error initializing class. Please report error.")
|
||||
end
|
||||
if self.customModuleClass then
|
||||
AceModuleCore:error("Cannot call `SetModuleClass' twice.")
|
||||
end
|
||||
self.customModuleClass = true
|
||||
self.moduleClass = class
|
||||
self.modulePrototype = class.prototype
|
||||
end
|
||||
|
||||
function AceModuleCore:ToggleModuleActive(module, state)
|
||||
AceModuleCore:argCheck(module, 2, "table", "string")
|
||||
AceModuleCore:argCheck(state, 3, "nil", "boolean")
|
||||
|
||||
if type(module) == "string" then
|
||||
if not self:HasModule(module) then
|
||||
AceModuleCore:error("Cannot find module %q", module)
|
||||
end
|
||||
module = self:GetModule(module)
|
||||
else
|
||||
if not self:IsModule(module) then
|
||||
AceModuleCore:error("%q is not a module", module)
|
||||
end
|
||||
end
|
||||
|
||||
local disable
|
||||
if state == nil then
|
||||
disable = self:IsModuleActive(module)
|
||||
else
|
||||
disable = not state
|
||||
if disable ~= self:IsModuleActive(module) then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if type(module.ToggleActive) == "function" then
|
||||
return module:ToggleActive(not disable)
|
||||
elseif AceOO.inherits(self, "AceDB-2.0") then
|
||||
if not self.db or not self.db.raw then
|
||||
AceModuleCore:error("Cannot toggle a module until `RegisterDB' has been called and `ADDON_LOADED' has been fireed.")
|
||||
end
|
||||
if type(self.db.raw.disabledModules) ~= "table" then
|
||||
self.db.raw.disabledModules = Compost and Compost:Acquire() or {}
|
||||
end
|
||||
local _,profile = self:GetProfile()
|
||||
if type(self.db.raw.disabledModules[profile]) ~= "table" then
|
||||
self.db.raw.disabledModules[profile] = Compost and Compost:Acquire() or {}
|
||||
end
|
||||
if type(self.db.raw.disabledModules[profile][module.name]) ~= "table" then
|
||||
self.db.raw.disabledModules[profile][module.name] = disable or nil
|
||||
end
|
||||
if not disable then
|
||||
if not next(self.db.raw.disabledModules[profile]) then
|
||||
if Compost then
|
||||
Compost:Reclaim(self.db.raw.disabledModules[profile])
|
||||
end
|
||||
self.db.raw.disabledModules[profile] = nil
|
||||
end
|
||||
if not next(self.db.raw.disabledModules) then
|
||||
if Compost then
|
||||
Compost:Reclaim(self.db.raw.disabledModules)
|
||||
end
|
||||
self.db.raw.disabledModules = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
if type(self.disabledModules) ~= "table" then
|
||||
self.disabledModules = Compost and Compost:Acquire() or {}
|
||||
end
|
||||
self.disabledModules[module.name] = disable or nil
|
||||
end
|
||||
if AceOO.inherits(module, "AceAddon-2.0") then
|
||||
local AceAddon = AceLibrary("AceAddon-2.0")
|
||||
if not AceAddon.addonsStarted[module] then
|
||||
return
|
||||
end
|
||||
end
|
||||
if not disable then
|
||||
local current = module.class
|
||||
while true do
|
||||
if current == AceOO.Class then
|
||||
break
|
||||
end
|
||||
if current.mixins then
|
||||
for mixin in pairs(current.mixins) do
|
||||
if type(mixin.OnEmbedEnable) == "function" then
|
||||
safecall(mixin.OnEmbedEnable, mixin, module)
|
||||
end
|
||||
end
|
||||
end
|
||||
current = current.super
|
||||
end
|
||||
if type(module.OnEnable) == "function" then
|
||||
safecall(module.OnEnable, module)
|
||||
end
|
||||
if AceEvent then
|
||||
AceEvent:TriggerEvent("Ace2_AddonEnabled", module)
|
||||
end
|
||||
else
|
||||
local current = module.class
|
||||
while true do
|
||||
if current == AceOO.Class then
|
||||
break
|
||||
end
|
||||
if current.mixins then
|
||||
for mixin in pairs(current.mixins) do
|
||||
if type(mixin.OnEmbedDisable) == "function" then
|
||||
safecall(mixin.OnEmbedDisable, mixin, module)
|
||||
end
|
||||
end
|
||||
end
|
||||
current = current.super
|
||||
end
|
||||
if type(module.OnDisable) == "function" then
|
||||
safecall(module.OnDisable, module)
|
||||
end
|
||||
if AceEvent then
|
||||
AceEvent:TriggerEvent("Ace2_AddonDisabled", module)
|
||||
end
|
||||
end
|
||||
return not disable
|
||||
end
|
||||
|
||||
function AceModuleCore:IsModuleActive(module)
|
||||
AceModuleCore:argCheck(module, 2, "table", "string")
|
||||
|
||||
if AceModuleCore == self then
|
||||
self:argCheck(module, 2, "table")
|
||||
|
||||
local core = AceModuleCore.totalModules[module]
|
||||
if not core then
|
||||
self:error("Bad argument #2 to `IsModuleActive'. Not a module")
|
||||
end
|
||||
return core:IsModuleActive(module)
|
||||
end
|
||||
|
||||
if type(module) == "string" then
|
||||
if not self:HasModule(module) then
|
||||
AceModuleCore:error("Cannot find module %q", module)
|
||||
end
|
||||
module = self:GetModule(module)
|
||||
else
|
||||
if not self:IsModule(module) then
|
||||
AceModuleCore:error("%q is not a module", module)
|
||||
end
|
||||
end
|
||||
|
||||
if type(module.IsActive) == "function" then
|
||||
return module:IsActive()
|
||||
elseif AceOO.inherits(self, "AceDB-2.0") then
|
||||
local _,profile = self:GetProfile()
|
||||
return not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[profile] or not self.db.raw.disabledModules[profile][module.name]
|
||||
else
|
||||
return not self.disabledModules or not self.disabledModules[module.name]
|
||||
end
|
||||
end
|
||||
|
||||
function AceModuleCore:OnInstanceInit(target)
|
||||
if target.modules then
|
||||
AceModuleCore:error("OnInstanceInit cannot be called twice")
|
||||
end
|
||||
target.modules = Compost and Compost:Acquire() or {}
|
||||
|
||||
target.moduleClass = AceOO.Class("AceAddon-2.0")
|
||||
target.modulePrototype = target.moduleClass.prototype
|
||||
end
|
||||
|
||||
AceModuleCore.OnManualEmbed = AceModuleCore.OnInstanceInit
|
||||
|
||||
function AceModuleCore.OnEmbedProfileDisable(AceModuleCore, self, newProfile)
|
||||
if not AceOO.inherits(self, "AceDB-2.0") then
|
||||
return
|
||||
end
|
||||
local _,currentProfile = self:GetProfile()
|
||||
for k, module in pairs(self.modules) do
|
||||
if type(module.IsActive) == "function" or type(module.ToggleActive) == "function" then
|
||||
-- continue
|
||||
else
|
||||
local currentActive = not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[currentProfile] or not self.db.raw.disabledModules[currentProfile][module.name]
|
||||
local newActive = not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[newProfile] or not self.db.raw.disabledModules[newProfile][module.name]
|
||||
if currentActive ~= newActive then
|
||||
self:ToggleModuleActive(module)
|
||||
if not self.db.raw.disabledModules then
|
||||
self.db.raw.disabledModules = {}
|
||||
end
|
||||
if not self.db.raw.disabledModules[currentProfile] then
|
||||
self.db.raw.disabledModules[currentProfile] = {}
|
||||
end
|
||||
self.db.raw.disabledModules[currentProfile][module.name] = not currentActive or nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function activate(self, oldLib, oldDeactivate)
|
||||
AceModuleCore = self
|
||||
|
||||
if oldLib then
|
||||
self.totalModules = oldLib.totalModules
|
||||
end
|
||||
if not self.totalModules then
|
||||
self.totalModules = {}
|
||||
end
|
||||
|
||||
self.super.activate(self, oldLib, oldDeactivate)
|
||||
end
|
||||
|
||||
local function external(self, major, instance)
|
||||
if major == "Compost-2.0" then
|
||||
Compost = instance
|
||||
elseif major == "AceEvent-2.0" then
|
||||
AceEvent = instance
|
||||
end
|
||||
end
|
||||
|
||||
AceLibrary:Register(AceModuleCore, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
|
||||
AceModuleCore = AceLibrary(MAJOR_VERSION)
|
||||
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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
||||
<Bindings>
|
||||
<Binding name="PERFBOOSTENABLE" header="PERFBOOST">
|
||||
SlashCmdList.PBENABLE()
|
||||
</Binding>
|
||||
|
||||
<Binding name="PBHIDEALLPLAYERS" header="PERFBOOST">
|
||||
SlashCmdList.PBHIDEALLPLAYERS()
|
||||
</Binding>
|
||||
|
||||
<Binding name="PBTOGGLEPLAYERRENDERIST" header="PERFBOOST">
|
||||
SlashCmdList.PBTOGGLEPLAYERRENDERIST()
|
||||
</Binding>
|
||||
|
||||
<Binding name="PBSHOWALLPLAYERS" header="PERFBOOST">
|
||||
SlashCmdList.PBSHOWALLPLAYERS()
|
||||
</Binding>
|
||||
|
||||
<Binding name="PBCYCLEPLAYERVISIBILITY" header="PERFBOOST">
|
||||
SlashCmdList.PBCYCLEPLAYERVISIBILITY()
|
||||
</Binding>
|
||||
|
||||
<Binding name="PBCYCLEDOODADVISIBILITY" header="PERFBOOST">
|
||||
SlashCmdList.PBCYCLEDOODADVISIBILITY()
|
||||
</Binding>
|
||||
|
||||
</Bindings>
|
||||
@@ -0,0 +1,82 @@
|
||||
# Installation
|
||||
- Add the dll from the lastest https://gitea.com/avitasia/perf_boost/releases next to WoW.exe
|
||||
- Enable perf_boost.dll in the launcher if you use it or add perf_boost.dll to dlls.txt
|
||||
|
||||
|
||||
- Install https://gitea.com/avitasia/PerfBoostSettings like any other addon
|
||||
|
||||
# General
|
||||
- Any distance set to -1 just means it is disabled and that category won't ever be hidden.
|
||||
- Setting a distance to 0 will always hide that category
|
||||
- If you want different settings on different characters enable `Per character settings` otherwise settings will be shared with all your characters
|
||||
- Play around with the settings to best fit your role (tanks and healers likely don't want to hide as much stuff as ranged dps)
|
||||
- If you don't want to hide players just turning off their spell effects will still be a large performance boost
|
||||
- Check out the readme for more detailed information on settings and commands https://gitea.com/avitasia/PerfBoostSettings/
|
||||
|
||||
# 1. Setup keybinds or a macro to turn off perf boost quickly when needed
|
||||
If you go to Esc -> Keybinds and scroll down you should see a section for PerfBoostSettings with these options:
|
||||
|
||||

|
||||
|
||||
The 2 most common ways to use the keybinds:
|
||||
|
||||
- Have all/most players shown by default and temporarily hide everyone using "Toggle 0 player render distance" when you need the extra fps or want to be able to see better
|
||||
- Have all/most players hidden by default using a low player render distance and temporarily show everyone using "Toggle show all players" for fights that require it.
|
||||
|
||||
Or can macro these commands if you prefer
|
||||
```
|
||||
- `/pbenable` or `/perfboostenable` - Toggle performance boost on/off. Can be keybound also.
|
||||
- `/pbshowallplayers` or `/pbsap` - Toggle show all players on/off. Can be keybound also.
|
||||
- `/pbhideallplayers` or `/pbhap` - Toggle hide all players on/off. Can be keybound also.
|
||||
- `/pbcycleplayervisibility` or `/pbcycle` - Cycle through player visibility modes: normal → show all → hide all → repeat. Can be keybound also.
|
||||
- `/pbtoggleplayerrenderist` or `/pbtrd` - Toggle player render distance between 0 and previous value. Can be keybound also.
|
||||
```
|
||||
|
||||
# 2. Suggested rendering settings
|
||||
## Initial Recommended
|
||||
- Default player render distance -1 (you'll see everyone out of combat)
|
||||
- In combat player render distance - 10yd
|
||||
- In combat trash distance 60yd
|
||||
- Pet distance 0 yd
|
||||
- Totem/Guardian distance 0 yd
|
||||
- Corpse distance 0 yd
|
||||
- In cities distance 5yd
|
||||
- Doodad render distance 60yd
|
||||
|
||||
## Max Performance
|
||||
- Set either Default or In combat (when most people suffer fps drops) player render distance to 0 yd
|
||||
- Whitelist tanks you want to see (if any) by adding their names to Always Render Players
|
||||
- In combat trash distance 60yd
|
||||
- Pet distance 0 yd
|
||||
- Totem/Guardian distance 0 yd
|
||||
- Corpse distance 0 yd
|
||||
- In cities distance 5yd
|
||||
- Doodad render distance 15yd
|
||||
|
||||
## Player whitelist/blacklist commands
|
||||
If you are using small player render distances you might find it useful to set tanks to always render.
|
||||
Alternatively, if you use a normal render player distance you find it useful to selectively hide like characters like taurens.
|
||||
|
||||
- `/pbalwaysrender` or `/pbar` - Add current target to always render list
|
||||
- `/pbalwaysrenderremove` or `/pbarr` - Remove current target from always render list
|
||||
- `/pbalwaysrenderclear` or `/pbarc` - Clear entire always render list
|
||||
- `/pbneverrender` or `/pbnr` - Add current target to never render list
|
||||
- `/pbneverrenderremove` or `/pbnrr` - Remove current target from never render list
|
||||
- `/pbneverrenderclear` or `/pbnrc` - Clear entire never render list
|
||||
|
||||
# 3. Suggested spell visual settings
|
||||
## Initial Recommended
|
||||
- Defaults are fine (will just hide spells cast by hidden players)
|
||||
|
||||
## Max Performance
|
||||
- Spell visuals off
|
||||
- Ground effects off
|
||||
- Unit auras off
|
||||
- Player auras off
|
||||
|
||||
- Whitelist specific spells by adding them to Always Shown Spell Ids
|
||||
|
||||
# 4. Filter GUID Events
|
||||
`Filters out generally unnecessary superwow GUID-based events to reduce event spam and improve performance. Blocks events like UNIT_AURA, UNIT_HEALTH, UNIT_MANA when triggered with a guid instead of a string like 'player' or 'raid1', while preserving commonly used guid events like UNIT_COMBAT and UNIT_MODEL_CHANGED.`
|
||||
|
||||
If you use superwow you most likely can turn this on to reduce event spam. I think very few addons will be negatively affected by turning this on whereas many addons are affected by the repeat event spam. If you notice something no longer happening in one of your addons just disable this setting.
|
||||
@@ -0,0 +1,23 @@
|
||||
## Interface: 11200
|
||||
## Title: Perf_boost Settings v2.0.0
|
||||
## Notes: the companion compatibility addon to the mod of the same name.
|
||||
## SavedVariablesPerCharacter: PerfBoostSettingsDB
|
||||
## X-Embeds: Ace2, FuBarPlugin-2.0, CompostLib, DewDropLib, TabletLib
|
||||
|
||||
libs\AceLibrary\AceLibrary.lua
|
||||
|
||||
libs\AceOO-2.0\AceOO-2.0.lua
|
||||
libs\AceAddon-2.0\AceAddon-2.0.lua
|
||||
libs\AceConsole-2.0\AceConsole-2.0.lua
|
||||
libs\AceHook-2.1\AceHook-2.1.lua
|
||||
libs\AceDB-2.0\AceDB-2.0.lua
|
||||
libs\AceDebug-2.0\AceDebug-2.0.lua
|
||||
libs\AceEvent-2.0\AceEvent-2.0.lua
|
||||
libs\AceLocale-2.2\AceLocale-2.2.lua
|
||||
libs\AceModuleCore-2.0\AceModuleCore-2.0.lua
|
||||
|
||||
libs\Dewdrop\Dewdrop-2.0.lua
|
||||
libs\Tablet\Tablet-2.0.lua
|
||||
libs\FuBarPlugin\FuBarPlugin-2.0.lua
|
||||
|
||||
settings.lua
|
||||
@@ -0,0 +1,95 @@
|
||||
# <b>If you manually download zip make sure you rename the folder after extracting to PerfBoostSettings instead of perfboostsettings or it won't work</b>
|
||||
|
||||
|
||||
### PerfBoost Settings
|
||||
|
||||
A configuration addon for the perf_boost.dll mod https://gitea.com/avitasia/perf_boost that provides an easy-to-use interface for adjusting performance optimization settings.
|
||||
|
||||
### [Guide for new users](GUIDE.md)
|
||||
|
||||
## Settings
|
||||
|
||||
### Master Control
|
||||
- **Enable Performance Boost** - Enable/disable all performance boost features
|
||||
|
||||
<b>You can keybind this in your keybinds — I highly recommend doing this in case you need to see hidden units in an emergency!<b>
|
||||
|
||||
### Rendering Settings
|
||||
- **Always Render Raid Marks** - Whether to always render raid marks regardless of distance/combat/etc
|
||||
- **Always Render PVP Players** - Whether to always render players flagged for PVP
|
||||
- **Always Render Players With Aggro** - Whether to always render players who have aggro from enemies
|
||||
- **Always Render Players** - Comma-separated list of player names to always render regardless of other settings
|
||||
- **Never Render Players** - Comma-separated list of player names to never render regardless of other settings
|
||||
- **Show All Players** - Show all players regardless of distance and other render settings
|
||||
- **Hide All Players** - Hide all players regardless of other render settings
|
||||
|
||||
**Priority:** Settings are applied from top to bottom - earlier settings override later ones.
|
||||
|
||||
**Note:** The addon will scan for players in your always/never render lists once per minute to cache their GUIDs. For better performance, it's recommended to clear out old names from these lists when players are no longer relevant.
|
||||
|
||||
### Player Render Distance
|
||||
- **Default Player Render Distance** - Max distance to render players when not in combat or cities (-1 to disable, 0-100 yards)
|
||||
- **In Combat Player Render Distance** - Max distance to render players when in combat (-1 to disable, 0-100 yards)
|
||||
- **In Cities Player Render Distance** - Max distance to render players when in cities (-1 to disable, 0-100 yards)
|
||||
|
||||
### Unit Render Distance
|
||||
- **Default Trash Unit Render Distance** - Max distance to render trash units (level < 63) when not in combat (-1 to disable, 0-100 yards)
|
||||
- **In Combat Trash Unit Render Distance** - Max distance to render trash units (level < 63) when in combat (-1 to disable, 0-100 yards)
|
||||
|
||||
### Pet Render Distance
|
||||
- **Default Pet Render Distance** - Max distance to render pets when not in combat (-1 to disable, 0-100 yards)
|
||||
- **In Combat Pet Render Distance** - Max distance to render pets when in combat (-1 to disable, 0-100 yards)
|
||||
|
||||
### Totem/Guardian Render Distance
|
||||
- **Default Totem/Guardian Render Distance** - Max distance to render unnamed summons when not in combat (-1 to disable, 0-100 yards)
|
||||
- **In Combat Totem/Guardian Render Distance** - Max distance to render unnamed summons when in combat (-1 to disable, 0-100 yards)
|
||||
|
||||
### Other Rendering
|
||||
- **Corpse Render Distance** - Max distance to render corpses (-1 to disable, 0-100 yards)
|
||||
- **Doodad Render Distance** - Max distance to render doodads (trees, crates, lamps, etc.). Interactable game objects are not hidden. Ignored if you are PvP flagged. (-1 to disable, 0-100 yards)
|
||||
|
||||
### Spell Visual Settings
|
||||
- **Show Other Player Spell Visuals** - Whether to show spell visual effects from other players (spell casts, ranged weapon projectiles, wanding, etc.)
|
||||
- **Show Other Player Ground Effects** - Whether to show ground effects from players
|
||||
- **Show Other Player Aura Visuals** - Whether to show aura visual effects on players
|
||||
- **Show Unit Aura Visuals** - Whether to show aura visual effects on units (NPCs, mobs)
|
||||
- **Hide Spells for Hidden Players** - Hide spell visuals from players that are hidden due to render distance or other settings
|
||||
- **Hide Laggy Boss Spells on Others** - Curated list of spells for Sapphiron, Gnarlmoon, and Anomalus that will be hidden on other players/units. If you aren't displaying unit auras most of these will already be hidden.
|
||||
- **Always Hidden Spell IDs** - Comma-separated list of spell IDs to hide visuals for (e.g., 1234,5678)
|
||||
- **Always Shown Spell IDs** - Comma-separated list of spell IDs to always show visuals for, overriding other settings
|
||||
- **Apply Hidden Spell IDs to Me** - Whether to apply the hidden spell IDs list to your own character's spells
|
||||
- **Debug Shown Spells** - Prints to the log information about each spell being displayed
|
||||
|
||||
**Note:** Some important debuff auras will always be shown regardless of settings:
|
||||
- Mark of the Highlord
|
||||
- Corruption of Medivh
|
||||
- Living Bomb
|
||||
- Plague
|
||||
|
||||
## Distance Priority
|
||||
|
||||
The addon will prioritize render distances in the following order:
|
||||
1. **Combat Distance** - Used when in combat (highest priority)
|
||||
2. **City Distance** - Used when in cities (for players only)
|
||||
3. **Default Distance** - Used in all other situations (lowest priority)
|
||||
|
||||
## Slash Commands
|
||||
|
||||
### General Commands
|
||||
- `/pbenable` or `/perfboostenable` - Toggle performance boost on/off. Can be keybound also.
|
||||
- `/pbshowallplayers` or `/pbsap` - Toggle show all players on/off. Can be keybound also.
|
||||
- `/pbhideallplayers` or `/pbhap` - Toggle hide all players on/off. Can be keybound also.
|
||||
- `/pbcycleplayervisibility` or `/pbcycle` - Cycle through player visibility modes: normal → show all → hide all → repeat. Can be keybound also.
|
||||
- `/pbtoggleplayerrenderist` or `/pbtrd` - Toggle both normal and combat player render distances between 0 and their previous values. Can be keybound also.
|
||||
|
||||
### Player Render List Commands
|
||||
- `/pbalwaysrender` or `/pbar` - Add current target to always render list
|
||||
- `/pbalwaysrenderremove` or `/pbarr` - Remove current target from always render list
|
||||
- `/pbalwaysrenderclear` or `/pbarc` - Clear entire always render list
|
||||
- `/pbneverrender` or `/pbnr` - Add current target to never render list
|
||||
- `/pbneverrenderremove` or `/pbnrr` - Remove current target from never render list
|
||||
- `/pbneverrenderclear` or `/pbnrc` - Clear entire never render list
|
||||
|
||||
## Per-Character Settings
|
||||
|
||||
Enable "Per Character Settings" to save different configurations for each character. When enabled, settings are saved to your character's saved variables file and will override whatever is in the global Config.wtf file.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 116 KiB |
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
@@ -0,0 +1,245 @@
|
||||
--[[
|
||||
Name: AceDebug-2.0
|
||||
Revision: $Rev: 17638 $
|
||||
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/AceDebug-2.0
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceDebug-2.0
|
||||
Description: Mixin to allow for simple debugging capabilities.
|
||||
Dependencies: AceLibrary, AceOO-2.0
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceDebug-2.0"
|
||||
local MINOR_VERSION = "$Revision: 17639 $"
|
||||
|
||||
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
|
||||
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
|
||||
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
|
||||
|
||||
local DEBUGGING, TOGGLE_DEBUGGING
|
||||
|
||||
if GetLocale() == "frFR" then
|
||||
DEBUGGING = "D\195\169boguage"
|
||||
TOGGLE_DEBUGGING = "Activer/d\195\169sactiver le d\195\169boguage"
|
||||
elseif GetLocale() == "esES" then
|
||||
DEBUGGING = "Depurar"
|
||||
TOGGLE_DEBUGGING = "Activar/desactivar depurar"
|
||||
elseif GetLocale() == "deDE" then
|
||||
DEBUGGING = "Debuggen"
|
||||
TOGGLE_DEBUGGING = "Aktiviert/Deaktiviert Debugging"
|
||||
elseif GetLocale() == "koKR" then
|
||||
DEBUGGING = "디버깅"
|
||||
TOGGLE_DEBUGGING = "디버깅 기능 사용함/사용안함"
|
||||
elseif GetLocale() == "zhTW" then
|
||||
DEBUGGING = "除錯"
|
||||
TOGGLE_DEBUGGING = "啟用/停用除錯功能"
|
||||
elseif GetLocale() == "zhCN" then
|
||||
DEBUGGING = "\232\176\131\232\175\149"
|
||||
TOGGLE_DEBUGGING = "\229\144\175\231\148\168/\231\166\129\231\148\168 \232\176\131\232\175\149"
|
||||
elseif GetLocale() == "ruRU" then
|
||||
DEBUGGING = "Отладка"
|
||||
TOGGLE_DEBUGGING = "Вкл/Выкл отладку для этого аддона."
|
||||
else -- enUS
|
||||
DEBUGGING = "Debugging"
|
||||
TOGGLE_DEBUGGING = "Enable/disable debugging"
|
||||
end
|
||||
|
||||
local table_setn
|
||||
do
|
||||
local version = GetBuildInfo()
|
||||
if string.find(version, "^2%.") then
|
||||
-- 2.0.0
|
||||
table_setn = function() end
|
||||
else
|
||||
table_setn = table.setn
|
||||
end
|
||||
end
|
||||
|
||||
local math_mod = math.mod or math.fmod
|
||||
|
||||
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
|
||||
local AceDebug = AceOO.Mixin {"Debug", "CustomDebug", "IsDebugging", "SetDebugging", "SetDebugLevel", "LevelDebug", "CustomLevelDebug", "GetDebugLevel"}
|
||||
|
||||
local function print(text, r, g, b, frame, delay)
|
||||
(frame or DEFAULT_CHAT_FRAME):AddMessage(text, r, g, b, 1, delay or 5)
|
||||
end
|
||||
|
||||
local tmp
|
||||
|
||||
function AceDebug:CustomDebug(r, g, b, frame, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not self.debugging then return end
|
||||
|
||||
local output = string.format("|cff7fff7f(DEBUG) %s:[%s%3d]|r", tostring(self), date("%H:%M:%S"), math_mod(GetTime(), 1) * 1000)
|
||||
|
||||
if string.find(tostring(a1), "%%") then
|
||||
output = output .. " " .. string.format(tostring(a1), tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20))
|
||||
else
|
||||
if not tmp then
|
||||
tmp = {}
|
||||
end
|
||||
|
||||
-- This block dynamically rebuilds the tmp array stopping on the first nil.
|
||||
table.insert(tmp, output)
|
||||
|
||||
table.insert(tmp, tostring(a1))
|
||||
table.insert(tmp, a2)
|
||||
table.insert(tmp, a3)
|
||||
table.insert(tmp, a4)
|
||||
table.insert(tmp, a5)
|
||||
table.insert(tmp, a6)
|
||||
table.insert(tmp, a7)
|
||||
table.insert(tmp, a8)
|
||||
table.insert(tmp, a9)
|
||||
table.insert(tmp, a10)
|
||||
table.insert(tmp, a11)
|
||||
table.insert(tmp, a12)
|
||||
table.insert(tmp, a13)
|
||||
table.insert(tmp, a14)
|
||||
table.insert(tmp, a15)
|
||||
table.insert(tmp, a16)
|
||||
table.insert(tmp, a17)
|
||||
table.insert(tmp, a18)
|
||||
table.insert(tmp, a19)
|
||||
table.insert(tmp, a20)
|
||||
while tmp[table.getn(tmp)] == nil do
|
||||
table.remove(tmp)
|
||||
end
|
||||
for k = 1, table.getn(tmp) do
|
||||
tmp[k] = tostring(tmp[k])
|
||||
end
|
||||
|
||||
output = table.concat(tmp, " ")
|
||||
|
||||
for k,v in pairs(tmp) do
|
||||
tmp[k] = nil
|
||||
end
|
||||
table_setn(tmp, 0)
|
||||
end
|
||||
|
||||
print(output, r, g, b, frame or self.debugFrame, delay)
|
||||
end
|
||||
|
||||
function AceDebug:Debug(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
AceDebug.CustomDebug(self, nil, nil, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
|
||||
function AceDebug:IsDebugging()
|
||||
return self.debugging
|
||||
end
|
||||
|
||||
function AceDebug:SetDebugging(debugging)
|
||||
self.debugging = debugging
|
||||
end
|
||||
|
||||
-- Takes a number 1-3
|
||||
-- Level 1: Critical messages that every user should receive
|
||||
-- Level 2: Should be used for local debugging (function calls, etc)
|
||||
-- Level 3: Very verbose debugging, will dump everything and anything
|
||||
-- If set to nil, you will receive no debug information
|
||||
function AceDebug:SetDebugLevel(level)
|
||||
AceDebug:argCheck(level, 1, "number", "nil")
|
||||
if not level then
|
||||
self.debuglevel = nil
|
||||
return
|
||||
end
|
||||
if level < 1 or level > 3 then
|
||||
AceDebug:error("Bad argument #1 to `SetDebugLevel`, must be a number 1-3")
|
||||
end
|
||||
self.debuglevel = level
|
||||
end
|
||||
|
||||
function AceDebug:GetDebugLevel()
|
||||
return self.debuglevel
|
||||
end
|
||||
|
||||
function AceDebug:CustomLevelDebug(level, r, g, b, frame, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not self.debugging or not self.debuglevel then return end
|
||||
AceDebug:argCheck(level, 1, "number")
|
||||
if level < 1 or level > 3 then
|
||||
AceDebug:error("Bad argument #1 to `LevelDebug`, must be a number 1-3")
|
||||
end
|
||||
if level > self.debuglevel then return end
|
||||
|
||||
local output = string.format("|cff7fff7f(DEBUG) %s:[%s.%3d]|r", tostring(self), date("%H:%M:%S"), math_mod(GetTime(), 1) * 1000)
|
||||
|
||||
if string.find(tostring(a1), "%%") then
|
||||
output = output .. " " .. string.format(tostring(a1), tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20))
|
||||
else
|
||||
if not tmp then
|
||||
tmp = {}
|
||||
end
|
||||
|
||||
-- This block dynamically rebuilds the tmp array stopping on the first nil.
|
||||
table.insert(tmp, output)
|
||||
|
||||
table.insert(tmp, tostring(a1))
|
||||
table.insert(tmp, a2)
|
||||
table.insert(tmp, a3)
|
||||
table.insert(tmp, a4)
|
||||
table.insert(tmp, a5)
|
||||
table.insert(tmp, a6)
|
||||
table.insert(tmp, a7)
|
||||
table.insert(tmp, a8)
|
||||
table.insert(tmp, a9)
|
||||
table.insert(tmp, a10)
|
||||
table.insert(tmp, a11)
|
||||
table.insert(tmp, a12)
|
||||
table.insert(tmp, a13)
|
||||
table.insert(tmp, a14)
|
||||
table.insert(tmp, a15)
|
||||
table.insert(tmp, a16)
|
||||
table.insert(tmp, a17)
|
||||
table.insert(tmp, a18)
|
||||
table.insert(tmp, a19)
|
||||
table.insert(tmp, a20)
|
||||
while tmp[table.getn(tmp)] == nil do
|
||||
table.remove(tmp)
|
||||
end
|
||||
for k = 1, table.getn(tmp) do
|
||||
tmp[k] = tostring(tmp[k])
|
||||
end
|
||||
|
||||
output = table.concat(tmp, " ")
|
||||
|
||||
for k,v in pairs(tmp) do
|
||||
tmp[k] = nil
|
||||
end
|
||||
table_setn(tmp, 0)
|
||||
end
|
||||
|
||||
print(output, r, g, b, frame or self.debugFrame, delay)
|
||||
end
|
||||
|
||||
function AceDebug:LevelDebug(level, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not self.debugging or not self.debuglevel then return end
|
||||
AceDebug:argCheck(level, 1, "number")
|
||||
if level < 1 or level > 3 then
|
||||
AceDebug:error("Bad argument #1 to `LevelDebug`, must be a number 1-3")
|
||||
end
|
||||
if level > self.debuglevel then return end
|
||||
|
||||
AceDebug.CustomLevelDebug(self, level, nil, nil, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
|
||||
|
||||
local options
|
||||
function AceDebug:GetAceOptionsDataTable(target)
|
||||
if not options then
|
||||
options = {
|
||||
debug = {
|
||||
name = DEBUGGING,
|
||||
desc = TOGGLE_DEBUGGING,
|
||||
type = "toggle",
|
||||
get = "IsDebugging",
|
||||
set = "SetDebugging",
|
||||
order = -2,
|
||||
}
|
||||
}
|
||||
end
|
||||
return options
|
||||
end
|
||||
AceLibrary:Register(AceDebug, MAJOR_VERSION, MINOR_VERSION, AceDebug.activate)
|
||||
AceDebug = AceLibrary(MAJOR_VERSION)
|
||||
@@ -0,0 +1,973 @@
|
||||
--[[
|
||||
Name: AceEvent-2.0
|
||||
Revision: $Rev: 17803 $
|
||||
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/AceEvent-2.0
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceEvent-2.0
|
||||
Description: Mixin to allow for event handling, scheduling, and inter-addon
|
||||
communication.
|
||||
Dependencies: AceLibrary, AceOO-2.0
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceEvent-2.0"
|
||||
local MINOR_VERSION = "$Revision: 17803 $"
|
||||
|
||||
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
|
||||
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
|
||||
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
|
||||
|
||||
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
|
||||
local Mixin = AceOO.Mixin
|
||||
local AceEvent = Mixin {
|
||||
"RegisterEvent",
|
||||
"RegisterAllEvents",
|
||||
"UnregisterEvent",
|
||||
"UnregisterAllEvents",
|
||||
"TriggerEvent",
|
||||
"ScheduleEvent",
|
||||
"ScheduleRepeatingEvent",
|
||||
"CancelScheduledEvent",
|
||||
"CancelAllScheduledEvents",
|
||||
"IsEventRegistered",
|
||||
"IsEventScheduled",
|
||||
"RegisterBucketEvent",
|
||||
"UnregisterBucketEvent",
|
||||
"UnregisterAllBucketEvents",
|
||||
"IsBucketEventRegistered",
|
||||
}
|
||||
|
||||
local table_setn
|
||||
do
|
||||
local version = GetBuildInfo()
|
||||
if string.find(version, "^2%.") then
|
||||
-- 2.0.0
|
||||
table_setn = function() end
|
||||
else
|
||||
table_setn = table.setn
|
||||
end
|
||||
end
|
||||
|
||||
local weakKey = {__mode="k"}
|
||||
local new, del
|
||||
do
|
||||
local list = setmetatable({}, weakKey)
|
||||
function new()
|
||||
local t = next(list)
|
||||
if t then
|
||||
list[t] = nil
|
||||
return t
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
function del(t)
|
||||
setmetatable(t, nil)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
list[t] = true
|
||||
end
|
||||
end
|
||||
|
||||
local FAKE_NIL
|
||||
local RATE
|
||||
|
||||
local eventsWhichHappenOnce = {
|
||||
PLAYER_LOGIN = true,
|
||||
AceEvent_FullyInitialized = true,
|
||||
VARIABLES_LOADED = true,
|
||||
PLAYER_LOGOUT = true,
|
||||
}
|
||||
|
||||
local registeringFromAceEvent
|
||||
function AceEvent:RegisterEvent(event, method, once)
|
||||
AceEvent:argCheck(event, 2, "string")
|
||||
if self == AceEvent and not registeringFromAceEvent then
|
||||
AceEvent:argCheck(method, 3, "function")
|
||||
self = method
|
||||
else
|
||||
AceEvent:argCheck(method, 3, "string", "function", "nil", "boolean", "number")
|
||||
if type(method) == "boolean" or type(method) == "number" then
|
||||
AceEvent:argCheck(once, 4, "nil")
|
||||
once, method = method, event
|
||||
end
|
||||
end
|
||||
AceEvent:argCheck(once, 4, "number", "boolean", "nil")
|
||||
if eventsWhichHappenOnce[event] then
|
||||
once = true
|
||||
end
|
||||
local throttleRate
|
||||
if type(once) == "number" then
|
||||
throttleRate, once = once
|
||||
end
|
||||
if not method then
|
||||
method = event
|
||||
end
|
||||
if type(method) == "string" and type(self[method]) ~= "function" then
|
||||
AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
|
||||
else
|
||||
assert(type(method) == "function" or type(method) == "string")
|
||||
end
|
||||
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if not AceEvent_registry[event] then
|
||||
AceEvent_registry[event] = new()
|
||||
AceEvent.frame:RegisterEvent(event)
|
||||
end
|
||||
|
||||
local remember = true
|
||||
if AceEvent_registry[event][self] then
|
||||
remember = false
|
||||
end
|
||||
AceEvent_registry[event][self] = method
|
||||
|
||||
local AceEvent_onceRegistry = AceEvent.onceRegistry
|
||||
if once then
|
||||
if not AceEvent_onceRegistry then
|
||||
AceEvent.onceRegistry = new()
|
||||
AceEvent_onceRegistry = AceEvent.onceRegistry
|
||||
end
|
||||
if not AceEvent_onceRegistry[event] then
|
||||
AceEvent_onceRegistry[event] = new()
|
||||
end
|
||||
AceEvent_onceRegistry[event][self] = true
|
||||
else
|
||||
if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
|
||||
AceEvent_onceRegistry[event][self] = nil
|
||||
if not next(AceEvent_onceRegistry[event]) then
|
||||
AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local AceEvent_throttleRegistry = AceEvent.throttleRegistry
|
||||
if throttleRate then
|
||||
if not AceEvent_throttleRegistry then
|
||||
AceEvent.throttleRegistry = new()
|
||||
AceEvent_throttleRegistry = AceEvent.throttleRegistry
|
||||
end
|
||||
if not AceEvent_throttleRegistry[event] then
|
||||
AceEvent_throttleRegistry[event] = new()
|
||||
end
|
||||
if AceEvent_throttleRegistry[event][self] then
|
||||
AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
|
||||
end
|
||||
AceEvent_throttleRegistry[event][self] = setmetatable(new(), weakKey)
|
||||
local t = AceEvent_throttleRegistry[event][self]
|
||||
t[RATE] = throttleRate
|
||||
else
|
||||
if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] then
|
||||
if AceEvent_throttleRegistry[event][self] then
|
||||
AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
|
||||
end
|
||||
if not next(AceEvent_throttleRegistry[event]) then
|
||||
AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if remember then
|
||||
AceEvent:TriggerEvent("AceEvent_EventRegistered", self, event)
|
||||
end
|
||||
end
|
||||
|
||||
local ALL_EVENTS
|
||||
|
||||
function AceEvent:RegisterAllEvents(method)
|
||||
if self == AceEvent then
|
||||
AceEvent:argCheck(method, 1, "function")
|
||||
self = method
|
||||
else
|
||||
AceEvent:argCheck(method, 1, "string", "function")
|
||||
if type(method) == "string" and type(self[method]) ~= "function" then
|
||||
AceEvent:error("Cannot register all events to method %q, it does not exist", method)
|
||||
end
|
||||
end
|
||||
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if not AceEvent_registry[ALL_EVENTS] then
|
||||
AceEvent_registry[ALL_EVENTS] = new()
|
||||
AceEvent.frame:RegisterAllEvents()
|
||||
end
|
||||
|
||||
AceEvent_registry[ALL_EVENTS][self] = method
|
||||
end
|
||||
|
||||
local _G = getfenv(0)
|
||||
local memstack, timestack = {}, {}
|
||||
local memdiff, timediff
|
||||
function AceEvent:TriggerEvent(event, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
AceEvent:argCheck(event, 2, "string")
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if (not AceEvent_registry[event] or not next(AceEvent_registry[event])) and (not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS])) then
|
||||
return
|
||||
end
|
||||
local _G_event = _G.event
|
||||
_G.event = event
|
||||
local lastEvent = AceEvent.currentEvent
|
||||
AceEvent.currentEvent = event
|
||||
|
||||
local AceEvent_onceRegistry = AceEvent.onceRegistry
|
||||
local AceEvent_debugTable = AceEvent.debugTable
|
||||
if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
|
||||
local tmp = new()
|
||||
for obj, method in pairs(AceEvent_onceRegistry[event]) do
|
||||
tmp[obj] = AceEvent_registry[event] and AceEvent_registry[event][obj] or nil
|
||||
end
|
||||
local obj = next(tmp)
|
||||
while obj do
|
||||
local mem, time
|
||||
if AceEvent_debugTable then
|
||||
if not AceEvent_debugTable[event] then
|
||||
AceEvent_debugTable[event] = new()
|
||||
end
|
||||
if not AceEvent_debugTable[event][obj] then
|
||||
AceEvent_debugTable[event][obj] = new()
|
||||
AceEvent_debugTable[event][obj].mem = 0
|
||||
AceEvent_debugTable[event][obj].time = 0
|
||||
AceEvent_debugTable[event][obj].count = 0
|
||||
end
|
||||
if memdiff then
|
||||
table.insert(memstack, memdiff)
|
||||
table.insert(timestack, timediff)
|
||||
end
|
||||
memdiff, timediff = 0, 0
|
||||
mem, time = gcinfo(), GetTime()
|
||||
end
|
||||
local method = tmp[obj]
|
||||
AceEvent.UnregisterEvent(obj, event)
|
||||
if type(method) == "string" then
|
||||
local obj_method = obj[method]
|
||||
if obj_method then
|
||||
obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
elseif method then -- function
|
||||
method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
if AceEvent_debugTable then
|
||||
local dmem, dtime = memdiff, timediff
|
||||
mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
|
||||
AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
|
||||
AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
|
||||
AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1
|
||||
|
||||
memdiff, timediff = table.remove(memstack), table.remove(timestack)
|
||||
if memdiff then
|
||||
memdiff = memdiff + mem + dmem
|
||||
timediff = timediff + time + dtime
|
||||
end
|
||||
end
|
||||
tmp[obj] = nil
|
||||
obj = next(tmp)
|
||||
end
|
||||
del(tmp)
|
||||
end
|
||||
|
||||
local AceEvent_throttleRegistry = AceEvent.throttleRegistry
|
||||
local throttleTable = AceEvent_throttleRegistry and AceEvent_throttleRegistry[event]
|
||||
if AceEvent_registry[event] then
|
||||
local tmp = new()
|
||||
for obj, method in pairs(AceEvent_registry[event]) do
|
||||
tmp[obj] = method
|
||||
end
|
||||
local obj = next(tmp)
|
||||
while obj do
|
||||
local method = tmp[obj]
|
||||
local continue = false
|
||||
if throttleTable and throttleTable[obj] then
|
||||
local a1 = a1
|
||||
if a1 == nil then
|
||||
a1 = FAKE_NIL
|
||||
end
|
||||
if not throttleTable[obj][a1] or GetTime() - throttleTable[obj][a1] >= throttleTable[obj][RATE] then
|
||||
throttleTable[obj][a1] = GetTime()
|
||||
else
|
||||
continue = true
|
||||
end
|
||||
end
|
||||
if not continue then
|
||||
local mem, time
|
||||
if AceEvent_debugTable then
|
||||
if not AceEvent_debugTable[event] then
|
||||
AceEvent_debugTable[event] = new()
|
||||
end
|
||||
if not AceEvent_debugTable[event][obj] then
|
||||
AceEvent_debugTable[event][obj] = new()
|
||||
AceEvent_debugTable[event][obj].mem = 0
|
||||
AceEvent_debugTable[event][obj].time = 0
|
||||
AceEvent_debugTable[event][obj].count = 0
|
||||
end
|
||||
if memdiff then
|
||||
table.insert(memstack, memdiff)
|
||||
table.insert(timestack, timediff)
|
||||
end
|
||||
memdiff, timediff = 0, 0
|
||||
mem, time = gcinfo(), GetTime()
|
||||
end
|
||||
if type(method) == "string" then
|
||||
local obj_method = obj[method]
|
||||
if obj_method then
|
||||
obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
elseif method then -- function
|
||||
method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
if AceEvent_debugTable then
|
||||
local dmem, dtime = memdiff, timediff
|
||||
mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
|
||||
AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
|
||||
AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
|
||||
AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1
|
||||
|
||||
memdiff, timediff = table.remove(memstack), table.remove(timestack)
|
||||
if memdiff then
|
||||
memdiff = memdiff + mem + dmem
|
||||
timediff = timediff + time + dtime
|
||||
end
|
||||
end
|
||||
end
|
||||
tmp[obj] = nil
|
||||
obj = next(tmp)
|
||||
end
|
||||
del(tmp)
|
||||
end
|
||||
if AceEvent_registry[ALL_EVENTS] then
|
||||
local tmp = new()
|
||||
for obj, method in pairs(AceEvent_registry[ALL_EVENTS]) do
|
||||
tmp[obj] = method
|
||||
end
|
||||
local obj = next(tmp)
|
||||
while obj do
|
||||
local method = tmp[obj]
|
||||
local mem, time
|
||||
if AceEvent_debugTable then
|
||||
if not AceEvent_debugTable[event] then
|
||||
AceEvent_debugTable[event] = new()
|
||||
end
|
||||
if not AceEvent_debugTable[event][obj] then
|
||||
AceEvent_debugTable[event][obj] = new()
|
||||
AceEvent_debugTable[event][obj].mem = 0
|
||||
AceEvent_debugTable[event][obj].time = 0
|
||||
AceEvent_debugTable[event][obj].count = 0
|
||||
end
|
||||
if memdiff then
|
||||
table.insert(memstack, memdiff)
|
||||
table.insert(timestack, timediff)
|
||||
end
|
||||
memdiff, timediff = 0, 0
|
||||
mem, time = gcinfo(), GetTime()
|
||||
end
|
||||
if type(method) == "string" then
|
||||
local obj_method = obj[method]
|
||||
if obj_method then
|
||||
obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
elseif method then -- function
|
||||
method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
if AceEvent_debugTable then
|
||||
local dmem, dtime = memdiff, timediff
|
||||
mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
|
||||
AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
|
||||
AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
|
||||
AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1
|
||||
|
||||
memdiff, timediff = table.remove(memstack), table.remove(timestack)
|
||||
if memdiff then
|
||||
memdiff = memdiff + mem + dmem
|
||||
timediff = timediff + time + dtime
|
||||
end
|
||||
end
|
||||
tmp[obj] = nil
|
||||
obj = next(tmp)
|
||||
end
|
||||
del(tmp)
|
||||
end
|
||||
_G.event = _G_event
|
||||
AceEvent.currentEvent = lastEvent
|
||||
end
|
||||
|
||||
-- local accessors
|
||||
local getn = table.getn
|
||||
local tinsert = table.insert
|
||||
local tremove = table.remove
|
||||
local floor = math.floor
|
||||
local GetTime = GetTime
|
||||
local next = next
|
||||
local pairs = pairs
|
||||
local unpack = unpack
|
||||
|
||||
local delayRegistry
|
||||
local tmp = {}
|
||||
local function OnUpdate()
|
||||
local t = GetTime()
|
||||
for k,v in pairs(delayRegistry) do
|
||||
tmp[k] = true
|
||||
end
|
||||
for k in pairs(tmp) do
|
||||
local v = delayRegistry[k]
|
||||
if v then
|
||||
local v_time = v.time
|
||||
if not v_time then
|
||||
delayRegistry[k] = del(v)
|
||||
elseif v_time <= t then
|
||||
local v_repeatDelay = v.repeatDelay
|
||||
if v_repeatDelay then
|
||||
-- use the event time, not the current time, else timing inaccuracies add up over time
|
||||
v.time = v_time + v_repeatDelay
|
||||
end
|
||||
local event = v.event
|
||||
local mem, time
|
||||
if AceEvent_debugTable then
|
||||
mem, time = gcinfo(), GetTime()
|
||||
end
|
||||
if type(event) == "function" then
|
||||
event(unpack(v))
|
||||
else
|
||||
AceEvent:TriggerEvent(event, unpack(v))
|
||||
end
|
||||
if AceEvent_debugTable then
|
||||
mem, time = gcinfo() - mem, GetTime() - time
|
||||
v.mem = v.mem + mem
|
||||
v.timeSpent = v.timeSpent + time
|
||||
v.count = v.count + 1
|
||||
end
|
||||
if not v_repeatDelay then
|
||||
local x = delayRegistry[k]
|
||||
if x and x.time == v_time then -- check if it was manually reset
|
||||
delayRegistry[k] = del(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for k in pairs(tmp) do
|
||||
tmp[k] = nil
|
||||
end
|
||||
if not next(delayRegistry) then
|
||||
AceEvent.frame:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local function ScheduleEvent(self, repeating, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
local id
|
||||
if type(event) == "string" or type(event) == "table" then
|
||||
if type(event) == "table" then
|
||||
if not delayRegistry or not delayRegistry[event] then
|
||||
AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
|
||||
end
|
||||
end
|
||||
if type(delay) ~= "number" then
|
||||
id, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20
|
||||
AceEvent:argCheck(event, 3, "string", "function", --[[ so message is right ]] "number")
|
||||
AceEvent:argCheck(delay, 4, "number")
|
||||
self:CancelScheduledEvent(id)
|
||||
end
|
||||
else
|
||||
AceEvent:argCheck(event, 2, "string", "function")
|
||||
AceEvent:argCheck(delay, 3, "number")
|
||||
end
|
||||
|
||||
if not delayRegistry then
|
||||
AceEvent.delayRegistry = new()
|
||||
delayRegistry = AceEvent.delayRegistry
|
||||
AceEvent.frame:SetScript("OnUpdate", OnUpdate)
|
||||
end
|
||||
local t
|
||||
if type(id) == "table" then
|
||||
for k in pairs(id) do
|
||||
id[k] = nil
|
||||
end
|
||||
t = id
|
||||
else
|
||||
t = new()
|
||||
end
|
||||
t[1] = a1
|
||||
t[2] = a2
|
||||
t[3] = a3
|
||||
t[4] = a4
|
||||
t[5] = a5
|
||||
t[6] = a6
|
||||
t[7] = a7
|
||||
t[8] = a8
|
||||
t[9] = a9
|
||||
t[10] = a10
|
||||
t[11] = a11
|
||||
t[12] = a12
|
||||
t[13] = a13
|
||||
t[14] = a14
|
||||
t[15] = a15
|
||||
t[16] = a16
|
||||
t[17] = a17
|
||||
t[18] = a18
|
||||
t[19] = a19
|
||||
t[20] = a20
|
||||
table_setn(t, 20)
|
||||
t.event = event
|
||||
t.time = GetTime() + delay
|
||||
t.self = self
|
||||
t.id = id or t
|
||||
t.repeatDelay = repeating and delay
|
||||
if AceEvent_debugTable then
|
||||
t.mem = 0
|
||||
t.count = 0
|
||||
t.timeSpent = 0
|
||||
end
|
||||
delayRegistry[t.id] = t
|
||||
AceEvent.frame:Show()
|
||||
return t.id
|
||||
end
|
||||
|
||||
function AceEvent:ScheduleEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if type(event) == "string" or type(event) == "table" then
|
||||
if type(event) == "table" then
|
||||
if not delayRegistry or not delayRegistry[event] then
|
||||
AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
|
||||
end
|
||||
end
|
||||
if type(delay) ~= "number" then
|
||||
AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
|
||||
AceEvent:argCheck(a1, 4, "number")
|
||||
end
|
||||
else
|
||||
AceEvent:argCheck(event, 2, "string", "function")
|
||||
AceEvent:argCheck(delay, 3, "number")
|
||||
end
|
||||
|
||||
return ScheduleEvent(self, false, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
|
||||
function AceEvent:ScheduleRepeatingEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if type(event) == "string" or type(event) == "table" then
|
||||
if type(event) == "table" then
|
||||
if not delayRegistry or not delayRegistry[event] then
|
||||
AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
|
||||
end
|
||||
end
|
||||
if type(delay) ~= "number" then
|
||||
AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
|
||||
AceEvent:argCheck(a1, 4, "number")
|
||||
end
|
||||
else
|
||||
AceEvent:argCheck(event, 2, "string", "function")
|
||||
AceEvent:argCheck(delay, 3, "number")
|
||||
end
|
||||
|
||||
return ScheduleEvent(self, true, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
end
|
||||
|
||||
function AceEvent:CancelScheduledEvent(t)
|
||||
AceEvent:argCheck(t, 2, "string", "table")
|
||||
if delayRegistry then
|
||||
local v = delayRegistry[t]
|
||||
if v then
|
||||
delayRegistry[t] = del(v)
|
||||
if not next(delayRegistry) then
|
||||
AceEvent.frame:Hide()
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function AceEvent:IsEventScheduled(t)
|
||||
AceEvent:argCheck(t, 2, "string", "table")
|
||||
if delayRegistry then
|
||||
local v = delayRegistry[t]
|
||||
if v then
|
||||
return true, v.time - GetTime()
|
||||
end
|
||||
end
|
||||
return false, nil
|
||||
end
|
||||
|
||||
function AceEvent:UnregisterEvent(event)
|
||||
AceEvent:argCheck(event, 2, "string")
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if AceEvent_registry[event] and AceEvent_registry[event][self] then
|
||||
AceEvent_registry[event][self] = nil
|
||||
local AceEvent_onceRegistry = AceEvent.onceRegistry
|
||||
if AceEvent_onceRegistry and AceEvent_onceRegistry[event] and AceEvent_onceRegistry[event][self] then
|
||||
AceEvent_onceRegistry[event][self] = nil
|
||||
if not next(AceEvent_onceRegistry[event]) then
|
||||
AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
|
||||
end
|
||||
end
|
||||
local AceEvent_throttleRegistry = AceEvent.throttleRegistry
|
||||
if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] and AceEvent_throttleRegistry[event][self] then
|
||||
AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
|
||||
if not next(AceEvent_throttleRegistry[event]) then
|
||||
AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
|
||||
end
|
||||
end
|
||||
if not next(AceEvent_registry[event]) then
|
||||
AceEvent_registry[event] = del(AceEvent_registry[event])
|
||||
if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then
|
||||
AceEvent.frame:UnregisterEvent(event)
|
||||
end
|
||||
end
|
||||
else
|
||||
if self == AceEvent then
|
||||
error(string.format("Cannot unregister event %q. Improperly unregistering from AceEvent-2.0.", event), 2)
|
||||
else
|
||||
AceEvent:error("Cannot unregister event %q. %q is not registered with it.", event, self)
|
||||
end
|
||||
end
|
||||
AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
|
||||
end
|
||||
|
||||
function AceEvent:UnregisterAllEvents()
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then
|
||||
AceEvent_registry[ALL_EVENTS][self] = nil
|
||||
if not next(AceEvent_registry[ALL_EVENTS]) then
|
||||
del(AceEvent_registry[ALL_EVENTS])
|
||||
AceEvent.frame:UnregisterAllEvents()
|
||||
for k,v in pairs(AceEvent_registry) do
|
||||
if k ~= ALL_EVENTS then
|
||||
AceEvent.frame:RegisterEvent(k)
|
||||
end
|
||||
end
|
||||
AceEvent_registry[event] = nil
|
||||
end
|
||||
end
|
||||
local first = true
|
||||
for event, data in pairs(AceEvent_registry) do
|
||||
if first then
|
||||
if AceEvent_registry.AceEvent_EventUnregistered then
|
||||
event = "AceEvent_EventUnregistered"
|
||||
else
|
||||
first = false
|
||||
end
|
||||
end
|
||||
local x = data[self]
|
||||
data[self] = nil
|
||||
if x and event ~= ALL_EVENTS then
|
||||
if not next(data) then
|
||||
del(data)
|
||||
if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then
|
||||
AceEvent.frame:UnregisterEvent(event)
|
||||
end
|
||||
AceEvent_registry[event] = nil
|
||||
end
|
||||
AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
|
||||
end
|
||||
if first then
|
||||
event = nil
|
||||
end
|
||||
end
|
||||
if AceEvent.onceRegistry then
|
||||
for event, data in pairs(AceEvent.onceRegistry) do
|
||||
data[self] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceEvent:CancelAllScheduledEvents()
|
||||
if delayRegistry then
|
||||
for k,v in pairs(delayRegistry) do
|
||||
if v.self == self then
|
||||
delayRegistry[k] = del(v)
|
||||
end
|
||||
end
|
||||
if not next(delayRegistry) then
|
||||
AceEvent.frame:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceEvent:IsEventRegistered(event)
|
||||
AceEvent:argCheck(event, 2, "string")
|
||||
local AceEvent_registry = AceEvent.registry
|
||||
if self == AceEvent then
|
||||
return AceEvent_registry[event] and next(AceEvent_registry[event]) and true or false
|
||||
end
|
||||
if AceEvent_registry[event] and AceEvent_registry[event][self] then
|
||||
return true, AceEvent_registry[event][self]
|
||||
end
|
||||
return false, nil
|
||||
end
|
||||
|
||||
local bucketfunc
|
||||
function AceEvent:RegisterBucketEvent(event, delay, method)
|
||||
AceEvent:argCheck(event, 2, "string", "table")
|
||||
if type(event) == "table" then
|
||||
for k,v in pairs(event) do
|
||||
if type(k) ~= "number" then
|
||||
AceEvent:error("All keys to argument #2 to `RegisterBucketEvent' must be numbers.")
|
||||
elseif type(v) ~= "string" then
|
||||
AceEvent:error("All values to argument #2 to `RegisterBucketEvent' must be strings.")
|
||||
end
|
||||
end
|
||||
end
|
||||
AceEvent:argCheck(delay, 3, "number")
|
||||
if AceEvent == self then
|
||||
AceEvent:argCheck(method, 4, "function")
|
||||
self = method
|
||||
else
|
||||
if type(event) == "string" then
|
||||
AceEvent:argCheck(method, 4, "string", "function", "nil")
|
||||
if not method then
|
||||
method = event
|
||||
end
|
||||
else
|
||||
AceEvent:argCheck(method, 4, "string", "function")
|
||||
end
|
||||
|
||||
if type(method) == "string" and type(self[method]) ~= "function" then
|
||||
AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
|
||||
end
|
||||
end
|
||||
if not AceEvent.buckets then
|
||||
AceEvent.buckets = new()
|
||||
end
|
||||
if not AceEvent.buckets[event] then
|
||||
AceEvent.buckets[event] = new()
|
||||
end
|
||||
if not AceEvent.buckets[event][self] then
|
||||
AceEvent.buckets[event][self] = new()
|
||||
AceEvent.buckets[event][self].current = new()
|
||||
AceEvent.buckets[event][self].self = self
|
||||
else
|
||||
AceEvent.CancelScheduledEvent(self, AceEvent.buckets[event][self].id)
|
||||
end
|
||||
local bucket = AceEvent.buckets[event][self]
|
||||
bucket.method = method
|
||||
|
||||
local func = function(arg1)
|
||||
bucket.run = true
|
||||
if arg1 then
|
||||
bucket.current[arg1] = true
|
||||
end
|
||||
end
|
||||
AceEvent.buckets[event][self].func = func
|
||||
if type(event) == "string" then
|
||||
AceEvent.RegisterEvent(self, event, func)
|
||||
else
|
||||
for _,v in ipairs(event) do
|
||||
AceEvent.RegisterEvent(self, v, func)
|
||||
end
|
||||
end
|
||||
if not bucketfunc then
|
||||
bucketfunc = function(bucket)
|
||||
local current = bucket.current
|
||||
local method = bucket.method
|
||||
local self = bucket.self
|
||||
if bucket.run then
|
||||
if type(method) == "string" then
|
||||
self[method](self, current)
|
||||
elseif method then -- function
|
||||
method(current)
|
||||
end
|
||||
for k in pairs(current) do
|
||||
current[k] = nil
|
||||
k = nil
|
||||
end
|
||||
bucket.run = false
|
||||
end
|
||||
end
|
||||
end
|
||||
bucket.id = AceEvent.ScheduleRepeatingEvent(self, bucketfunc, delay, bucket)
|
||||
end
|
||||
|
||||
function AceEvent:IsBucketEventRegistered(event)
|
||||
AceEvent:argCheck(event, 2, "string", "table")
|
||||
return AceEvent.buckets and AceEvent.buckets[event] and AceEvent.buckets[event][self]
|
||||
end
|
||||
|
||||
function AceEvent:UnregisterBucketEvent(event)
|
||||
AceEvent:argCheck(event, 2, "string", "table")
|
||||
if not AceEvent.buckets or not AceEvent.buckets[event] or not AceEvent.buckets[event][self] then
|
||||
AceEvent:error("Cannot unregister bucket event %q. %q is not registered with it.", event, self)
|
||||
end
|
||||
|
||||
local bucket = AceEvent.buckets[event][self]
|
||||
|
||||
if type(event) == "string" then
|
||||
AceEvent.UnregisterEvent(self, event)
|
||||
else
|
||||
for _,v in ipairs(event) do
|
||||
AceEvent.UnregisterEvent(self, v)
|
||||
end
|
||||
end
|
||||
AceEvent:CancelScheduledEvent(bucket.id)
|
||||
|
||||
del(bucket.current)
|
||||
AceEvent.buckets[event][self] = del(AceEvent.buckets[event][self])
|
||||
if not next(AceEvent.buckets[event]) then
|
||||
AceEvent.buckets[event] = del(AceEvent.buckets[event])
|
||||
end
|
||||
end
|
||||
|
||||
function AceEvent:UnregisterAllBucketEvents()
|
||||
if not AceEvent.buckets or not next(AceEvent.buckets) then
|
||||
return
|
||||
end
|
||||
for k,v in pairs(AceEvent.buckets) do
|
||||
if v == self then
|
||||
AceEvent.UnregisterBucketEvent(self, k)
|
||||
k = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceEvent:OnEmbedDisable(target)
|
||||
self.UnregisterAllEvents(target)
|
||||
|
||||
self.CancelAllScheduledEvents(target)
|
||||
|
||||
self.UnregisterAllBucketEvents(target)
|
||||
end
|
||||
|
||||
function AceEvent:EnableDebugging()
|
||||
if not self.debugTable then
|
||||
self.debugTable = new()
|
||||
|
||||
if delayRegistry then
|
||||
for k,v in pairs(self.delayRegistry) do
|
||||
if not v.mem then
|
||||
v.mem = 0
|
||||
v.count = 0
|
||||
v.timeSpent = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceEvent:IsFullyInitialized()
|
||||
return self.postInit or false
|
||||
end
|
||||
|
||||
function AceEvent:IsPostPlayerLogin()
|
||||
return self.playerLogin or false
|
||||
end
|
||||
|
||||
function AceEvent:activate(oldLib, oldDeactivate)
|
||||
AceEvent = self
|
||||
|
||||
if oldLib then
|
||||
self.onceRegistry = oldLib.onceRegistry
|
||||
self.throttleRegistry = oldLib.throttleRegistry
|
||||
self.delayRegistry = oldLib.delayRegistry
|
||||
self.buckets = oldLib.buckets
|
||||
self.registry = oldLib.registry
|
||||
self.frame = oldLib.frame
|
||||
self.debugTable = oldLib.debugTable
|
||||
self.playerLogin = oldLib.pew or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage and true
|
||||
self.postInit = oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true
|
||||
self.ALL_EVENTS = oldLib.ALL_EVENTS
|
||||
self.FAKE_NIL = oldLib.FAKE_NIL
|
||||
self.RATE = oldLib.RATE
|
||||
end
|
||||
if not self.registry then
|
||||
self.registry = {}
|
||||
end
|
||||
if not self.frame then
|
||||
self.frame = CreateFrame("Frame", "AceEvent20Frame")
|
||||
end
|
||||
if not self.ALL_EVENTS then
|
||||
self.ALL_EVENTS = {}
|
||||
end
|
||||
if not self.FAKE_NIL then
|
||||
self.FAKE_NIL = {}
|
||||
end
|
||||
if not self.RATE then
|
||||
self.RATE = {}
|
||||
end
|
||||
ALL_EVENTS = self.ALL_EVENTS
|
||||
FAKE_NIL = self.FAKE_NIL
|
||||
RATE = self.RATE
|
||||
local inPlw = false
|
||||
local blacklist = {
|
||||
UNIT_INVENTORY_CHANGED = true,
|
||||
BAG_UPDATE = true,
|
||||
ITEM_LOCK_CHANGED = true,
|
||||
ACTIONBAR_SLOT_CHANGED = true,
|
||||
}
|
||||
self.frame:SetScript("OnEvent", function()
|
||||
local event = event
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
inPlw = false
|
||||
elseif event == "PLAYER_LEAVING_WORLD" then
|
||||
inPlw = true
|
||||
end
|
||||
if event and (not inPlw or not blacklist[event]) then
|
||||
self:TriggerEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
|
||||
end
|
||||
end)
|
||||
if self.delayRegistry then
|
||||
delayRegistry = self.delayRegistry
|
||||
self.frame:SetScript("OnUpdate", OnUpdate)
|
||||
end
|
||||
|
||||
self:UnregisterAllEvents()
|
||||
self:CancelAllScheduledEvents()
|
||||
|
||||
registeringFromAceEvent = true
|
||||
self:RegisterEvent("LOOT_OPENED", function()
|
||||
SendAddonMessage("LOOT_OPENED", "", "RAID")
|
||||
end)
|
||||
registeringFromAceEvent = nil
|
||||
|
||||
if not self.playerLogin then
|
||||
registeringFromAceEvent = true
|
||||
self:RegisterEvent("PLAYER_LOGIN", function()
|
||||
self.playerLogin = true
|
||||
end, true)
|
||||
registeringFromAceEvent = nil
|
||||
end
|
||||
|
||||
if not self.postInit then
|
||||
local isReload = true
|
||||
local function func()
|
||||
self.postInit = true
|
||||
self:TriggerEvent("AceEvent_FullyInitialized")
|
||||
if self.registry["CHAT_MSG_CHANNEL_NOTICE"] and self.registry["CHAT_MSG_CHANNEL_NOTICE"][self] then
|
||||
self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE")
|
||||
end
|
||||
if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
|
||||
self:UnregisterEvent("MEETINGSTONE_CHANGED")
|
||||
end
|
||||
if self.registry["MINIMAP_ZONE_CHANGED"] and self.registry["MINIMAP_ZONE_CHANGED"][self] then
|
||||
self:UnregisterEvent("MINIMAP_ZONE_CHANGED")
|
||||
end
|
||||
if self.registry["LANGUAGE_LIST_CHANGED"] and self.registry["LANGUAGE_LIST_CHANGED"][self] then
|
||||
self:UnregisterEvent("LANGUAGE_LIST_CHANGED")
|
||||
end
|
||||
end
|
||||
registeringFromAceEvent = true
|
||||
local f = function()
|
||||
self.playerLogin = true
|
||||
self:ScheduleEvent("AceEvent_FullyInitialized", func, 1)
|
||||
end
|
||||
self:RegisterEvent("MEETINGSTONE_CHANGED", f, true)
|
||||
self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", function()
|
||||
self:ScheduleEvent("AceEvent_FullyInitialized", func, 0.05)
|
||||
end)
|
||||
self:RegisterEvent("LANGUAGE_LIST_CHANGED", function()
|
||||
if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
|
||||
registeringFromAceEvent = true
|
||||
self:UnregisterEvent("MEETINGSTONE_CHANGED")
|
||||
self:RegisterEvent("MINIMAP_ZONE_CHANGED", f, true)
|
||||
registeringFromAceEvent = nil
|
||||
end
|
||||
end)
|
||||
self:ScheduleEvent("AceEvent_FullyInitialized", func, 10)
|
||||
registeringFromAceEvent = nil
|
||||
end
|
||||
|
||||
self.super.activate(self, oldLib, oldDeactivate)
|
||||
if oldLib then
|
||||
oldDeactivate(oldLib)
|
||||
end
|
||||
end
|
||||
|
||||
AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, AceEvent.activate)
|
||||
AceEvent = AceLibrary(MAJOR_VERSION)
|
||||
@@ -0,0 +1,569 @@
|
||||
--[[
|
||||
Name: AceHook-2.1
|
||||
Revision: $Rev: 17638 $
|
||||
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/AceHook-2.1
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceHook-2.1
|
||||
Description: Mixin to allow for safe hooking of functions, methods, and scripts.
|
||||
Dependencies: AceLibrary, AceOO-2.0
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceHook-2.1"
|
||||
local MINOR_VERSION = "$Revision: 17638 $"
|
||||
|
||||
-- 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
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
|
||||
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
|
||||
|
||||
--[[---------------------------------------------------------------------------------
|
||||
Create the library object
|
||||
----------------------------------------------------------------------------------]]
|
||||
|
||||
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
|
||||
local AceHook = AceOO.Mixin {
|
||||
"Hook",
|
||||
"HookScript",
|
||||
"SecureHook",
|
||||
"Unhook",
|
||||
"UnhookAll",
|
||||
"HookReport",
|
||||
"IsHooked",
|
||||
}
|
||||
|
||||
--[[---------------------------------------------------------------------------------
|
||||
Library Definitions
|
||||
----------------------------------------------------------------------------------]]
|
||||
|
||||
local protFuncs = {
|
||||
CameraOrSelectOrMoveStart = true, CameraOrSelectOrMoveStop = true,
|
||||
TurnOrActionStart = true, TurnOrActionStop = true,
|
||||
PitchUpStart = true, PitchUpStop = true,
|
||||
PitchDownStart = true, PitchDownStop = true,
|
||||
MoveBackwardStart = true, MoveBackwardStop = true,
|
||||
MoveForwardStart = true, MoveForwardStop = true,
|
||||
Jump = true, StrafeLeftStart = true,
|
||||
StrafeLeftStop = true, StrafeRightStart = true,
|
||||
StrafeRightStop = true, ToggleMouseMove = true,
|
||||
ToggleRun = true, TurnLeftStart = true,
|
||||
TurnLeftStop = true, TurnRightStart = true,
|
||||
TurnRightStop = true,
|
||||
}
|
||||
|
||||
local function issecurevariable(x)
|
||||
if protFuncs[x] then
|
||||
return 1
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local _G = getfenv(0)
|
||||
|
||||
local function hooksecurefunc(arg1, arg2, arg3)
|
||||
if type(arg1) == "string" then
|
||||
arg1, arg2, arg3 = _G, arg1, arg2
|
||||
end
|
||||
local orig = arg1[arg2]
|
||||
arg1[arg2] = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
local x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20 = orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
|
||||
arg3(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
|
||||
return x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20
|
||||
end
|
||||
end
|
||||
|
||||
local protectedScripts = {
|
||||
OnClick = true,
|
||||
}
|
||||
|
||||
|
||||
local handlers, scripts, actives, registry
|
||||
|
||||
--[[---------------------------------------------------------------------------------
|
||||
Private definitions (Not exposed)
|
||||
----------------------------------------------------------------------------------]]
|
||||
|
||||
local new, del
|
||||
do
|
||||
local list = setmetatable({}, {__mode = "k"})
|
||||
function new()
|
||||
local t = next(list)
|
||||
if not t then
|
||||
return {}
|
||||
end
|
||||
list[t] = nil
|
||||
return t
|
||||
end
|
||||
|
||||
function del(t)
|
||||
setmetatable(t, nil)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
list[t] = true
|
||||
end
|
||||
end
|
||||
|
||||
local function createFunctionHook(self, func, handler, orig, secure)
|
||||
if not secure then
|
||||
if type(handler) == "string" then
|
||||
-- The handler is a method, need to self it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
else
|
||||
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
else
|
||||
-- The handler is a function, just call it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
else
|
||||
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
end
|
||||
else
|
||||
-- secure hooks don't call the original method
|
||||
if type(handler) == "string" then
|
||||
-- The handler is a method, need to self it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
else
|
||||
-- The handler is a function, just call it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function createMethodHook(self, object, method, handler, orig, secure, script)
|
||||
if script then
|
||||
if type(handler) == "string" then
|
||||
local uid
|
||||
uid = function()
|
||||
if actives[uid] then
|
||||
return self[handler](self, object)
|
||||
else
|
||||
return orig()
|
||||
end
|
||||
end
|
||||
return uid
|
||||
else
|
||||
-- The handler is a function, just call it
|
||||
local uid
|
||||
uid = function()
|
||||
if actives[uid] then
|
||||
return handler(object)
|
||||
else
|
||||
return orig()
|
||||
end
|
||||
end
|
||||
return uid
|
||||
end
|
||||
elseif not secure then
|
||||
if type(handler) == "string" then
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
else
|
||||
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
else
|
||||
-- The handler is a function, just call it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
else
|
||||
return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
end
|
||||
else
|
||||
-- secure hooks don't call the original method
|
||||
if type(handler) == "string" then
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
else
|
||||
-- The handler is a function, just call it
|
||||
local uid
|
||||
uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
if actives[uid] then
|
||||
return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
return uid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function hookFunction(self, func, handler, secure)
|
||||
local orig = _G[func]
|
||||
|
||||
if not orig or type(orig) ~= "function" then
|
||||
AceHook:error("Attempt to hook a non-existant function %q", func)
|
||||
end
|
||||
|
||||
if not handler then
|
||||
handler = func
|
||||
end
|
||||
|
||||
if registry[self][func] then
|
||||
local uid = registry[self][func]
|
||||
|
||||
if actives[uid] then
|
||||
-- We have an active hook from this source. Don't multi-hook
|
||||
AceHook:error("%q already has an active hook from this source.", func)
|
||||
end
|
||||
|
||||
if handlers[uid] == handler then
|
||||
-- The hook is inactive, so reactivate it
|
||||
actives[uid] = true
|
||||
return
|
||||
else
|
||||
AceHook:error("There is a stale hook for %q can't hook or reactivate.", func)
|
||||
end
|
||||
end
|
||||
|
||||
if type(handler) == "string" then
|
||||
if type(self[handler]) ~= "function" then
|
||||
AceHook:error("Could not find the the handler %q when hooking function %q", handler, func)
|
||||
end
|
||||
elseif type(handler) ~= "function" then
|
||||
AceHook:error("Could not find the handler you supplied when hooking %q", func)
|
||||
end
|
||||
|
||||
local uid = createFunctionHook(self, func, handler, orig, secure)
|
||||
registry[self][func] = uid
|
||||
actives[uid] = true
|
||||
handlers[uid] = handler
|
||||
|
||||
if not secure then
|
||||
_G[func] = uid
|
||||
self.hooks[func] = orig
|
||||
else
|
||||
hooksecurefunc(func, uid)
|
||||
end
|
||||
end
|
||||
|
||||
local function unhookFunction(self, func)
|
||||
if not registry[self][func] then
|
||||
AceHook:error("Tried to unhook %q which is not currently hooked.", func)
|
||||
end
|
||||
|
||||
local uid = registry[self][func]
|
||||
|
||||
if actives[uid] then
|
||||
-- See if we own the global function
|
||||
if self.hooks[func] and _G[func] == uid then
|
||||
_G[func] = self.hooks[func]
|
||||
self.hooks[func] = nil
|
||||
registry[self][func] = nil
|
||||
handlers[uid] = nil
|
||||
scripts[uid] = nil
|
||||
actives[uid] = nil
|
||||
-- Magically all-done
|
||||
else
|
||||
actives[uid] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function hookMethod(self, obj, method, handler, script, secure)
|
||||
if not handler then
|
||||
handler = method
|
||||
end
|
||||
|
||||
if not obj or type(obj) ~= "table" then
|
||||
AceHook:error("The object you supplied could not be found, or isn't a table.")
|
||||
end
|
||||
|
||||
local uid = registry[self][obj] and registry[self][obj][method]
|
||||
if uid then
|
||||
if actives[uid] then
|
||||
-- We have an active hook from this source. Don't multi-hook
|
||||
AceHook:error("%q already has an active hook from this source.", method)
|
||||
end
|
||||
|
||||
if handlers[uid] == handler then
|
||||
-- The hook is inactive, reactivate it.
|
||||
actives[uid] = true
|
||||
return
|
||||
else
|
||||
AceHook:error("There is a stale hook for %q can't hook or reactivate.", method)
|
||||
end
|
||||
end
|
||||
|
||||
if type(handler) == "string" then
|
||||
if type(self[handler]) ~= "function" then
|
||||
AceHook:error("Could not find the handler %q you supplied when hooking method %q", handler, method)
|
||||
end
|
||||
elseif type(handler) ~= "function" then
|
||||
AceHook:error("Could not find the handler you supplied when hooking method %q", method)
|
||||
end
|
||||
|
||||
local orig
|
||||
if script then
|
||||
if not obj.GetScript then
|
||||
AceHook:error("The object you supplied does not have a GetScript method.")
|
||||
end
|
||||
if not obj:HasScript(method) then
|
||||
AceHook:error("The object you supplied doesn't allow the %q method.", method)
|
||||
end
|
||||
|
||||
orig = obj:GetScript(method)
|
||||
if type(orig) ~= "function" then
|
||||
-- Sometimes there is not a original function for a script.
|
||||
orig = function() end
|
||||
end
|
||||
else
|
||||
orig = obj[method]
|
||||
end
|
||||
if not orig then
|
||||
AceHook:error("Could not find the method or script %q you are trying to hook.", method)
|
||||
end
|
||||
|
||||
if not registry[self][obj] then
|
||||
registry[self][obj] = new()
|
||||
end
|
||||
|
||||
if not self.hooks[obj] then
|
||||
self.hooks[obj] = new()
|
||||
end
|
||||
|
||||
local uid = createMethodHook(self, obj, method, handler, orig, secure, script)
|
||||
registry[self][obj][method] = uid
|
||||
actives[uid] = true
|
||||
handlers[uid] = handler
|
||||
scripts[uid] = script and true or nil
|
||||
|
||||
if script then
|
||||
obj:SetScript(method, uid)
|
||||
self.hooks[obj][method] = orig
|
||||
elseif not secure then
|
||||
obj[method] = uid
|
||||
self.hooks[obj][method] = orig
|
||||
else
|
||||
hooksecurefunc(obj, method, uid)
|
||||
end
|
||||
end
|
||||
|
||||
local function unhookMethod(self, obj, method)
|
||||
if not registry[self][obj] or not registry[self][obj][method] then
|
||||
AceHook:error("Attempt to unhook a method %q that is not currently hooked.", method)
|
||||
return
|
||||
end
|
||||
|
||||
local uid = registry[self][obj][method]
|
||||
|
||||
if actives[uid] then
|
||||
if scripts[uid] then -- If this is a script
|
||||
if obj:GetScript(method) == uid then
|
||||
-- We own the script. Revert to normal.
|
||||
obj:SetScript(method, self.hooks[obj][method])
|
||||
self.hooks[obj][method] = nil
|
||||
registry[self][obj][method] = nil
|
||||
handlers[uid] = nil
|
||||
scripts[uid] = nil
|
||||
actives[uid] = nil
|
||||
else
|
||||
actives[uid] = nil
|
||||
end
|
||||
else
|
||||
if self.hooks[obj] and self.hooks[obj][method] and obj[method] == uid then
|
||||
-- We own the method. Revert to normal.
|
||||
obj[method] = self.hooks[obj][method]
|
||||
self.hooks[obj][method] = nil
|
||||
registry[self][obj][method] = nil
|
||||
handlers[uid] = nil
|
||||
actives[uid] = nil
|
||||
else
|
||||
actives[uid] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.hooks[obj] and not next(self.hooks[obj]) then
|
||||
self.hooks[obj] = del(self.hooks[obj])
|
||||
end
|
||||
if not next(registry[self][obj]) then
|
||||
registry[self][obj] = del(registry[self][obj])
|
||||
end
|
||||
end
|
||||
|
||||
-- ("function" [, handler] [, hookSecure]) or (object, "method" [, handler] [, hookSecure])
|
||||
function AceHook:Hook(object, method, handler, hookSecure)
|
||||
if type(object) == "string" then
|
||||
method, handler, hookSecure = object, method, handler
|
||||
if handler == true then
|
||||
handler, hookSecure = nil, true
|
||||
end
|
||||
if not hookSecure and issecurevariable(method) then
|
||||
AceHook:error("Attempt to hook secure function %q. Use `SecureHook' or add `true' to the argument list to override.", method)
|
||||
end
|
||||
AceHook:argCheck(handler, 3, "function", "string", "nil")
|
||||
AceHook:argCheck(hookSecure, 4, "boolean", "nil")
|
||||
hookFunction(self, method, handler, false)
|
||||
else
|
||||
if handler == true then
|
||||
handler, hookSecure = nil, true
|
||||
end
|
||||
if not hookSecure and issecurevariable(object, method) then
|
||||
AceHook:error("Attempt to hook secure method %q. Use `SecureHook' or add `true' to the argument list to override.", method)
|
||||
end
|
||||
AceHook:argCheck(object, 2, "table")
|
||||
AceHook:argCheck(method, 3, "string")
|
||||
AceHook:argCheck(handler, 4, "function", "string", "nil")
|
||||
AceHook:argCheck(hookSecure, 5, "boolean", "nil")
|
||||
hookMethod(self, object, method, handler, false, false)
|
||||
end
|
||||
end
|
||||
|
||||
-- ("function", handler) or (object, "method", handler)
|
||||
function AceHook:SecureHook(object, method, handler)
|
||||
if type(object) == "string" then
|
||||
method, handler = object, method
|
||||
AceHook:argCheck(handler, 3, "function", "string", "nil")
|
||||
hookFunction(self, method, handler, true)
|
||||
else
|
||||
AceHook:argCheck(object, 2, "table")
|
||||
AceHook:argCheck(method, 3, "string")
|
||||
AceHook:argCheck(handler, 4, "function", "string", "nil")
|
||||
hookMethod(self, object, method, handler, false, true)
|
||||
end
|
||||
end
|
||||
|
||||
function AceHook:HookScript(frame, script, handler)
|
||||
AceHook:argCheck(frame, 2, "table")
|
||||
if not frame[0] or type(frame.IsFrameType) ~= "function" then
|
||||
AceHook:error("Bad argument #2 to `HookScript'. Expected frame.")
|
||||
end
|
||||
AceHook:argCheck(script, 3, "string")
|
||||
AceHook:argCheck(handler, 4, "function", "string", "nil")
|
||||
hookMethod(self, frame, script, handler, true, false)
|
||||
end
|
||||
|
||||
-- ("function") or (object, "method")
|
||||
function AceHook:IsHooked(obj, method)
|
||||
if type(obj) == "string" then
|
||||
if registry[self][obj] and actives[registry[self][obj]] then
|
||||
return true, handlers[registry[self][obj]]
|
||||
end
|
||||
else
|
||||
AceHook:argCheck(obj, 2, "string", "table")
|
||||
AceHook:argCheck(method, 3, "string")
|
||||
if registry[self][obj] and registry[self][obj][method] and actives[registry[self][obj][method]] then
|
||||
return true, handlers[registry[self][obj][method]]
|
||||
end
|
||||
end
|
||||
|
||||
return false, nil
|
||||
end
|
||||
|
||||
-- ("function") or (object, "method")
|
||||
function AceHook:Unhook(obj, method)
|
||||
if type(obj) == "string" then
|
||||
unhookFunction(self, obj)
|
||||
else
|
||||
AceHook:argCheck(obj, 2, "string", "table")
|
||||
AceHook:argCheck(method, 3, "string")
|
||||
unhookMethod(self, obj, method)
|
||||
end
|
||||
end
|
||||
|
||||
function AceHook:UnhookAll()
|
||||
for key, value in pairs(registry[self]) do
|
||||
if type(key) == "table" then
|
||||
for method in pairs(value) do
|
||||
self:Unhook(key, method)
|
||||
end
|
||||
else
|
||||
self:Unhook(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceHook:HookReport()
|
||||
DEFAULT_CHAT_FRAME:AddMessage("This is a list of all active hooks for this object:")
|
||||
if not next(registry[self]) then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("No hooks")
|
||||
end
|
||||
|
||||
for key, value in pairs(registry[self]) do
|
||||
if type(value) == "table" then
|
||||
for method, uid in pairs(value) do
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("object: %s method: %q |cff%s|r%s", tostring(key), method, actives[uid] and "00ff00Active" or "ffff00Inactive", not self.hooks[key][method] and " |cff7f7fff-Secure-|r" or ""))
|
||||
end
|
||||
else
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("function: %q |cff%s|r%s", tostring(key), actives[value] and "00ff00Active" or "ffff00Inactive", not self.hooks[key] and " |cff7f7fff-Secure-|r" or ""))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceHook:OnInstanceInit(object)
|
||||
if not object.hooks then
|
||||
object.hooks = new()
|
||||
end
|
||||
if not registry[object] then
|
||||
registry[object] = new()
|
||||
end
|
||||
end
|
||||
|
||||
AceHook.OnManualEmbed = AceHook.OnInstanceInit
|
||||
|
||||
function AceHook:OnEmbedDisable(target)
|
||||
self.UnhookAll(target)
|
||||
end
|
||||
|
||||
local function activate(self, oldLib, oldDeactivate)
|
||||
AceHook = self
|
||||
|
||||
self.handlers = oldLib and oldLib.handlers or {}
|
||||
self.registry = oldLib and oldLib.registry or {}
|
||||
self.scripts = oldLib and oldLib.scripts or {}
|
||||
self.actives = oldLib and oldLib.actives or {}
|
||||
|
||||
handlers = self.handlers
|
||||
registry = self.registry
|
||||
scripts = self.scripts
|
||||
actives = self.actives
|
||||
|
||||
AceHook.super.activate(self, oldLib, oldDeactivate)
|
||||
|
||||
if oldDeactivate then
|
||||
oldDeactivate(oldLib)
|
||||
end
|
||||
end
|
||||
|
||||
AceLibrary:Register(AceHook, MAJOR_VERSION, MINOR_VERSION, activate)
|
||||
@@ -0,0 +1,757 @@
|
||||
--[[
|
||||
Name: AceLibrary
|
||||
Revision: $Rev: 17722 $
|
||||
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: 17722 $
|
||||
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
|
||||
]]
|
||||
|
||||
local ACELIBRARY_MAJOR = "AceLibrary"
|
||||
local ACELIBRARY_MINOR = "$Revision: 17722 $"
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary and AceLibrary:HasInstance(ACELIBRARY_MAJOR) then return end -- lua51 check
|
||||
local table_setn
|
||||
do
|
||||
local version = GetBuildInfo()
|
||||
if string.find(version, "^2%.") then
|
||||
-- 2.0.0
|
||||
table_setn = function() end
|
||||
else
|
||||
table_setn = table.setn
|
||||
end
|
||||
end
|
||||
|
||||
local string_gfind = string.gmatch or string.gfind
|
||||
|
||||
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,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
local success, err = pcall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
if not success then geterrorhandler()(err) end
|
||||
end
|
||||
|
||||
-- @table AceLibrary
|
||||
-- @brief System to handle all versioning of libraries.
|
||||
local AceLibrary = {}
|
||||
local AceLibrary_mt = {}
|
||||
setmetatable(AceLibrary, AceLibrary_mt)
|
||||
|
||||
local tmp
|
||||
local function error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if type(self) ~= "table" then
|
||||
_G.error(string.format("Bad argument #1 to `error' (table expected, got %s)", type(self)), 2)
|
||||
end
|
||||
if not tmp then
|
||||
tmp = {}
|
||||
else
|
||||
for k in pairs(tmp) do tmp[k] = nil end
|
||||
table_setn(tmp, 0)
|
||||
end
|
||||
|
||||
table.insert(tmp, a1)
|
||||
table.insert(tmp, a2)
|
||||
table.insert(tmp, a3)
|
||||
table.insert(tmp, a4)
|
||||
table.insert(tmp, a5)
|
||||
table.insert(tmp, a6)
|
||||
table.insert(tmp, a7)
|
||||
table.insert(tmp, a8)
|
||||
table.insert(tmp, a9)
|
||||
table.insert(tmp, a10)
|
||||
table.insert(tmp, a11)
|
||||
table.insert(tmp, a12)
|
||||
table.insert(tmp, a13)
|
||||
table.insert(tmp, a14)
|
||||
table.insert(tmp, a15)
|
||||
table.insert(tmp, a16)
|
||||
table.insert(tmp, a17)
|
||||
table.insert(tmp, a18)
|
||||
table.insert(tmp, a19)
|
||||
table.insert(tmp, a20)
|
||||
|
||||
local stack = debugstack()
|
||||
if not message then
|
||||
local _,_,second = string.find(stack, "\n(.-)\n")
|
||||
message = "error raised! " .. second
|
||||
else
|
||||
for i = 1,table.getn(tmp) do
|
||||
tmp[i] = tostring(tmp[i])
|
||||
end
|
||||
for i = 1,10 do
|
||||
table.insert(tmp, "nil")
|
||||
end
|
||||
message = string.format(message, unpack(tmp))
|
||||
end
|
||||
|
||||
if getmetatable(self) and getmetatable(self).__tostring then
|
||||
message = string.format("%s: %s", tostring(self), message)
|
||||
elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then
|
||||
message = string.format("%s: %s", self:GetLibraryVersion(), message)
|
||||
elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then
|
||||
message = string.format("%s: %s", self.class:GetLibraryVersion(), message)
|
||||
end
|
||||
|
||||
local first = string.gsub(stack, "\n.*", "")
|
||||
local file = string.gsub(first, ".*\\(.*).lua:%d+: .*", "%1")
|
||||
file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
|
||||
|
||||
local i = 0
|
||||
for s in string_gfind(stack, "\n([^\n]*)") do
|
||||
i = i + 1
|
||||
if not string.find(s, file .. "%.lua:%d+:") then
|
||||
file = string.gsub(s, "^.*\\(.*).lua:%d+: .*", "%1")
|
||||
file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
|
||||
break
|
||||
end
|
||||
end
|
||||
local j = 0
|
||||
for s in string_gfind(stack, "\n([^\n]*)") do
|
||||
j = j + 1
|
||||
if j > i and not string.find(s, file .. "%.lua:%d+:") then
|
||||
_G.error(message, j + 1)
|
||||
return
|
||||
end
|
||||
end
|
||||
_G.error(message, 2)
|
||||
return
|
||||
end
|
||||
|
||||
local function assert(self, condition, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not condition then
|
||||
if not message then
|
||||
local stack = debugstack()
|
||||
local _,_,second = string.find(stack, "\n(.-)\n")
|
||||
message = "assertion failed! " .. second
|
||||
end
|
||||
error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
return
|
||||
end
|
||||
return condition
|
||||
end
|
||||
|
||||
local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5)
|
||||
if type(num) ~= "number" then
|
||||
error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num))
|
||||
elseif type(kind) ~= "string" then
|
||||
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 _,_,func = string.find(debugstack(), "`argCheck'.-([`<].-['>])")
|
||||
if not func then
|
||||
_,_,func = string.find(debugstack(), "([`<].-['>])")
|
||||
end
|
||||
if kind5 then
|
||||
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
|
||||
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
|
||||
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
|
||||
error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg)
|
||||
else
|
||||
error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function pcall(self, func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = _G.pcall(func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not a1 then
|
||||
error(self, string.gsub(a2, ".-%.lua:%d-: ", ""))
|
||||
else
|
||||
return a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20
|
||||
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 string.find(text, "^%$Revision: (%d+) %$$") then
|
||||
return tonumber((string.gsub(text, "^%$Revision: (%d+) %$$", "%1")))
|
||||
elseif string.find(text, "^%$Rev: (%d+) %$$") then
|
||||
return tonumber((string.gsub(text, "^%$Rev: (%d+) %$$", "%1")))
|
||||
elseif string.find(text, "^%$LastChangedRevision: (%d+) %$$") then
|
||||
return tonumber((string.gsub(text, "^%$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
|
||||
table_setn(t, 0)
|
||||
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
|
||||
|
||||
local new, del
|
||||
do
|
||||
local tables = setmetatable({}, {__mode = "k"})
|
||||
|
||||
function new()
|
||||
local t = next(tables)
|
||||
if t then
|
||||
tables[t] = nil
|
||||
return t
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
function del(t, depth)
|
||||
if depth and depth > 0 then
|
||||
for k,v in pairs(t) do
|
||||
if type(v) == "table" and not isFrame(v) then
|
||||
del(v, depth - 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
destroyTable(t)
|
||||
tables[t] = true
|
||||
end
|
||||
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 = new()
|
||||
for k,v in pairs(from) do to[k] = v end
|
||||
table_setn(to, table.getn(from))
|
||||
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)
|
||||
local createdList
|
||||
if not list then
|
||||
createdList = true
|
||||
list = new()
|
||||
list2 = new()
|
||||
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
|
||||
table_setn(to, table.getn(from))
|
||||
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)
|
||||
if createdList then
|
||||
del(list)
|
||||
del(list2)
|
||||
end
|
||||
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(string.format("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate", 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(string.format("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate", 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(string.format("Cannot find a library instance of %s.", major), 2)
|
||||
return
|
||||
end
|
||||
if minor then
|
||||
if type(minor) == "string" then
|
||||
local m = svnRevisionToNumber(minor)
|
||||
if m then
|
||||
minor = m
|
||||
else
|
||||
_G.error(string.format("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
|
||||
end
|
||||
end
|
||||
argCheck(self, minor, 2, "number")
|
||||
if data.minor ~= minor then
|
||||
_G.error(string.format("Cannot find a library instance of %s, minor version %d.", major, minor), 2)
|
||||
return
|
||||
end
|
||||
end
|
||||
return data.instance
|
||||
end
|
||||
|
||||
-- Syntax sugar. AceLibrary("FooBar-1.0")
|
||||
AceLibrary_mt.__call = AceLibrary.GetInstance
|
||||
|
||||
local donothing
|
||||
|
||||
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 type(minor) == "string" then
|
||||
local m = svnRevisionToNumber(minor)
|
||||
if m then
|
||||
minor = m
|
||||
else
|
||||
_G.error(string.format("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate", 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
|
||||
if not donothing then
|
||||
donothing = function() end
|
||||
end
|
||||
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
|
||||
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(string.format("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", major, data.minor, minor, major, minor), 2)
|
||||
return
|
||||
end
|
||||
-- This is an update
|
||||
local oldInstance = new()
|
||||
|
||||
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)
|
||||
else
|
||||
safecall(oldDeactivateFunc, oldInstance)
|
||||
end
|
||||
del(oldInstance)
|
||||
|
||||
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)
|
||||
if not self.libs then
|
||||
if oldLib then
|
||||
self.libs = oldLib.libs
|
||||
self.scannedlibs = oldLib.scannedlibs
|
||||
end
|
||||
if not self.libs then
|
||||
self.libs = {}
|
||||
end
|
||||
if not self.scannedlibs then
|
||||
self.scannedlibs = {}
|
||||
end
|
||||
end
|
||||
if not self.positions then
|
||||
if oldLib then
|
||||
self.positions = oldLib.positions
|
||||
end
|
||||
if not self.positions then
|
||||
self.positions = setmetatable({}, { __mode = "k" })
|
||||
end
|
||||
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)
|
||||
@@ -0,0 +1,538 @@
|
||||
--[[
|
||||
Name: AceLocale-2.2
|
||||
Revision: $Rev: 17638 $
|
||||
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/AceLocale-2.2
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceLocale-2.2
|
||||
Description: Localization library for addons to use to handle proper
|
||||
localization and internationalization.
|
||||
Dependencies: AceLibrary
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceLocale-2.2"
|
||||
local MINOR_VERSION = "$Revision: 17639 $"
|
||||
|
||||
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
|
||||
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
|
||||
local AceLocale = {}
|
||||
|
||||
local DEFAULT_LOCALE = "enUS"
|
||||
local _G = getfenv(0)
|
||||
|
||||
local BASE_TRANSLATIONS, DEBUGGING, TRANSLATIONS, BASE_LOCALE, TRANSLATION_TABLES, REVERSE_TRANSLATIONS, STRICTNESS, DYNAMIC_LOCALES, CURRENT_LOCALE, NAME
|
||||
|
||||
local rawget = rawget
|
||||
local rawset = rawset
|
||||
local type = type
|
||||
|
||||
local newRegistries = {}
|
||||
local scheduleClear
|
||||
|
||||
local lastSelf
|
||||
local __index = function(self, key)
|
||||
lastSelf = self
|
||||
local value = (rawget(self, TRANSLATIONS) or AceLocale.prototype)[key]
|
||||
rawset(self, key, value)
|
||||
return value
|
||||
end
|
||||
|
||||
local __newindex = function(self, k, v)
|
||||
if type(v) ~= "function" and type(k) ~= "table" then
|
||||
AceLocale.error(self, "Cannot change the values of an AceLocale instance.")
|
||||
end
|
||||
rawset(self, k, v)
|
||||
end
|
||||
|
||||
local __tostring = function(self)
|
||||
if type(rawget(self, 'GetLibraryVersion')) == "function" then
|
||||
return self:GetLibraryVersion()
|
||||
else
|
||||
return "AceLocale(" .. self[NAME] .. ")"
|
||||
end
|
||||
end
|
||||
|
||||
local function clearCache(self)
|
||||
if not rawget(self, BASE_TRANSLATIONS) then
|
||||
return
|
||||
end
|
||||
|
||||
local cache = self[BASE_TRANSLATIONS]
|
||||
rawset(self, REVERSE_TRANSLATIONS, nil)
|
||||
|
||||
for k in pairs(self) do
|
||||
if rawget(cache, k) ~= nil then
|
||||
self[k] = nil
|
||||
end
|
||||
end
|
||||
rawset(self, 'tmp', true)
|
||||
self.tmp = nil
|
||||
end
|
||||
|
||||
local function refixInstance(instance)
|
||||
if getmetatable(instance) then
|
||||
setmetatable(instance, nil)
|
||||
end
|
||||
local translations = instance[TRANSLATIONS]
|
||||
if translations then
|
||||
if getmetatable(translations) then
|
||||
setmetatable(translations, nil)
|
||||
end
|
||||
local baseTranslations = instance[BASE_TRANSLATIONS]
|
||||
if getmetatable(baseTranslations) then
|
||||
setmetatable(baseTranslations, nil)
|
||||
end
|
||||
if translations == baseTranslations or instance[STRICTNESS] then
|
||||
setmetatable(instance, {
|
||||
__index = __index,
|
||||
__newindex = __newindex,
|
||||
__tostring = __tostring
|
||||
})
|
||||
|
||||
setmetatable(translations, {
|
||||
__index = AceLocale.prototype
|
||||
})
|
||||
else
|
||||
setmetatable(instance, {
|
||||
__index = __index,
|
||||
__newindex = __newindex,
|
||||
__tostring = __tostring
|
||||
})
|
||||
|
||||
setmetatable(translations, {
|
||||
__index = baseTranslations,
|
||||
})
|
||||
|
||||
setmetatable(baseTranslations, {
|
||||
__index = AceLocale.prototype,
|
||||
})
|
||||
end
|
||||
else
|
||||
setmetatable(instance, {
|
||||
__index = __index,
|
||||
__newindex = __newindex,
|
||||
__tostring = __tostring,
|
||||
})
|
||||
end
|
||||
clearCache(instance)
|
||||
newRegistries[instance] = true
|
||||
scheduleClear()
|
||||
return instance
|
||||
end
|
||||
|
||||
function AceLocale:new(name)
|
||||
self:argCheck(name, 2, "string")
|
||||
|
||||
if self.registry[name] and type(rawget(self.registry[name], 'GetLibraryVersion')) ~= "function" then
|
||||
return self.registry[name]
|
||||
end
|
||||
|
||||
AceLocale.registry[name] = refixInstance({
|
||||
[STRICTNESS] = false,
|
||||
[NAME] = name,
|
||||
})
|
||||
newRegistries[AceLocale.registry[name]] = true
|
||||
return AceLocale.registry[name]
|
||||
end
|
||||
|
||||
AceLocale.prototype = { class = AceLocale }
|
||||
|
||||
function AceLocale.prototype:EnableDebugging()
|
||||
if rawget(self, BASE_TRANSLATIONS) then
|
||||
AceLocale.error(self, "Cannot enable debugging after a translation has been registered.")
|
||||
end
|
||||
rawset(self, DEBUGGING, true)
|
||||
end
|
||||
|
||||
function AceLocale.prototype:EnableDynamicLocales(override)
|
||||
AceLocale.argCheck(self, override, 2, "boolean", "nil")
|
||||
if not override and rawget(self, BASE_TRANSLATIONS) then
|
||||
AceLocale.error(self, "Cannot enable dynamic locales after a translation has been registered.")
|
||||
end
|
||||
if not rawget(self, DYNAMIC_LOCALES) then
|
||||
rawset(self, DYNAMIC_LOCALES, true)
|
||||
if rawget(self, BASE_LOCALE) then
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
rawset(self, TRANSLATION_TABLES, {})
|
||||
end
|
||||
self[TRANSLATION_TABLES][self[BASE_LOCALE]] = self[BASE_TRANSLATIONS]
|
||||
self[TRANSLATION_TABLES][self[CURRENT_LOCALE]] = self[TRANSLATIONS]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceLocale.prototype:RegisterTranslations(locale, func)
|
||||
AceLocale.argCheck(self, locale, 2, "string")
|
||||
AceLocale.argCheck(self, func, 3, "function")
|
||||
|
||||
if locale == rawget(self, BASE_LOCALE) then
|
||||
AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale)
|
||||
end
|
||||
|
||||
if rawget(self, BASE_TRANSLATIONS) and GetLocale() ~= locale then
|
||||
if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
rawset(self, TRANSLATION_TABLES, {})
|
||||
end
|
||||
if self[TRANSLATION_TABLES][locale] then
|
||||
AceLocale.error(self, "Cannot provide the same locale more than once. %q provided twice.", locale)
|
||||
end
|
||||
local t = func()
|
||||
func = nil
|
||||
if type(t) ~= "table" then
|
||||
AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t))
|
||||
end
|
||||
self[TRANSLATION_TABLES][locale] = t
|
||||
t = nil
|
||||
end
|
||||
func = nil
|
||||
return
|
||||
end
|
||||
local t = func()
|
||||
func = nil
|
||||
if type(t) ~= "table" then
|
||||
AceLocale.error(self, "Bad argument #3 to `RegisterTranslations'. function did not return a table. (expected table, got %s)", type(t))
|
||||
end
|
||||
|
||||
rawset(self, TRANSLATIONS, t)
|
||||
if not rawget(self, BASE_TRANSLATIONS) then
|
||||
rawset(self, BASE_TRANSLATIONS, t)
|
||||
rawset(self, BASE_LOCALE, locale)
|
||||
for key,value in pairs(t) do
|
||||
if value == true then
|
||||
t[key] = key
|
||||
end
|
||||
end
|
||||
else
|
||||
for key, value in pairs(self[TRANSLATIONS]) do
|
||||
if not rawget(self[BASE_TRANSLATIONS], key) then
|
||||
AceLocale.error(self, "Improper translation exists. %q is likely misspelled for locale %s.", key, locale)
|
||||
end
|
||||
if value == true then
|
||||
AceLocale.error(self, "Can only accept true as a value on the base locale. %q is the base locale, %q is not.", rawget(self, BASE_LOCALE), locale)
|
||||
end
|
||||
end
|
||||
end
|
||||
rawset(self, CURRENT_LOCALE, locale)
|
||||
refixInstance(self)
|
||||
if rawget(self, DEBUGGING) or rawget(self, DYNAMIC_LOCALES) then
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
rawset(self, TRANSLATION_TABLES, {})
|
||||
end
|
||||
self[TRANSLATION_TABLES][locale] = t
|
||||
end
|
||||
t = nil
|
||||
end
|
||||
|
||||
function AceLocale.prototype:SetLocale(locale)
|
||||
AceLocale.argCheck(self, locale, 2, "string", "boolean")
|
||||
if not rawget(self, DYNAMIC_LOCALES) then
|
||||
AceLocale.error(self, "Cannot call `SetLocale' without first calling `EnableDynamicLocales'.")
|
||||
end
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
AceLocale.error(self, "Cannot call `SetLocale' without first calling `RegisterTranslations'.")
|
||||
end
|
||||
if locale == true then
|
||||
locale = GetLocale()
|
||||
if not self[TRANSLATION_TABLES][locale] then
|
||||
locale = self[BASE_LOCALE]
|
||||
end
|
||||
end
|
||||
|
||||
if self[CURRENT_LOCALE] == locale then
|
||||
return
|
||||
end
|
||||
|
||||
if not self[TRANSLATION_TABLES][locale] then
|
||||
AceLocale.error(self, "Locale %q not registered.", locale)
|
||||
end
|
||||
|
||||
self[TRANSLATIONS] = self[TRANSLATION_TABLES][locale]
|
||||
self[CURRENT_LOCALE] = locale
|
||||
refixInstance(self)
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetLocale()
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
AceLocale.error(self, "Cannot call `GetLocale' without first calling `RegisterTranslations'.")
|
||||
end
|
||||
return self[CURRENT_LOCALE]
|
||||
end
|
||||
|
||||
local function iter(t, position)
|
||||
return (next(t, position))
|
||||
end
|
||||
|
||||
function AceLocale.prototype:IterateAvailableLocales()
|
||||
if not rawget(self, DYNAMIC_LOCALES) then
|
||||
AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `EnableDynamicLocales'.")
|
||||
end
|
||||
if not rawget(self, TRANSLATION_TABLES) then
|
||||
AceLocale.error(self, "Cannot call `IterateAvailableLocales' without first calling `RegisterTranslations'.")
|
||||
end
|
||||
return iter, self[TRANSLATION_TABLES], nil
|
||||
end
|
||||
|
||||
function AceLocale.prototype:HasLocale(locale)
|
||||
if not rawget(self, DYNAMIC_LOCALES) then
|
||||
AceLocale.error(self, "Cannot call `HasLocale' without first calling `EnableDynamicLocales'.")
|
||||
end
|
||||
AceLocale.argCheck(self, locale, 2, "string")
|
||||
return rawget(self, TRANSLATION_TABLES) and self[TRANSLATION_TABLES][locale] ~= nil
|
||||
end
|
||||
|
||||
function AceLocale.prototype:SetStrictness(strict)
|
||||
AceLocale.argCheck(self, strict, 2, "boolean")
|
||||
local mt = getmetatable(self)
|
||||
if not mt then
|
||||
AceLocale.error(self, "Cannot call `SetStrictness' without a metatable.")
|
||||
end
|
||||
if not rawget(self, TRANSLATIONS) then
|
||||
AceLocale.error(self, "No translations registered.")
|
||||
end
|
||||
rawset(self, STRICTNESS, strict)
|
||||
refixInstance(self)
|
||||
end
|
||||
|
||||
local function initReverse(self)
|
||||
rawset(self, REVERSE_TRANSLATIONS, {})
|
||||
local alpha = self[TRANSLATIONS]
|
||||
local bravo = self[REVERSE_TRANSLATIONS]
|
||||
for base, localized in pairs(alpha) do
|
||||
bravo[localized] = base
|
||||
end
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetTranslation(text)
|
||||
AceLocale.argCheck(self, text, 1, "string", "number")
|
||||
if not rawget(self, TRANSLATIONS) then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
return self[text]
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetStrictTranslation(text)
|
||||
AceLocale.argCheck(self, text, 1, "string", "number")
|
||||
local x = rawget(self, TRANSLATIONS)
|
||||
if not x then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
local value = rawget(x, text)
|
||||
if value == nil then
|
||||
AceLocale.error(self, "Translation %q does not exist for locale %s", text, self[CURRENT_LOCALE])
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetReverseTranslation(text)
|
||||
local x = rawget(self, REVERSE_TRANSLATIONS)
|
||||
if not x then
|
||||
if not rawget(self, TRANSLATIONS) then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
initReverse(self)
|
||||
x = self[REVERSE_TRANSLATIONS]
|
||||
end
|
||||
local translation = x[text]
|
||||
if not translation then
|
||||
AceLocale.error(self, "Reverse translation for %q does not exist", text)
|
||||
end
|
||||
return translation
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetIterator()
|
||||
local x = rawget(self, TRANSLATIONS)
|
||||
if not x then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
return next, x, nil
|
||||
end
|
||||
|
||||
function AceLocale.prototype:GetReverseIterator()
|
||||
local x = rawget(self, REVERSE_TRANSLATIONS)
|
||||
if not x then
|
||||
if not rawget(self, TRANSLATIONS) then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
initReverse(self)
|
||||
x = self[REVERSE_TRANSLATIONS]
|
||||
end
|
||||
return next, x, nil
|
||||
end
|
||||
|
||||
function AceLocale.prototype:HasTranslation(text)
|
||||
AceLocale.argCheck(self, text, 1, "string", "number")
|
||||
local x = rawget(self, TRANSLATIONS)
|
||||
if not x then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
return rawget(x, text) and true
|
||||
end
|
||||
|
||||
function AceLocale.prototype:HasReverseTranslation(text)
|
||||
local x = rawget(self, REVERSE_TRANSLATIONS)
|
||||
if not x then
|
||||
if not rawget(self, TRANSLATIONS) then
|
||||
AceLocale.error(self, "No translations registered")
|
||||
end
|
||||
initReverse(self)
|
||||
x = self[REVERSE_TRANSLATIONS]
|
||||
end
|
||||
return x[text] and true
|
||||
end
|
||||
|
||||
function AceLocale.prototype:Debug()
|
||||
if not rawget(self, DEBUGGING) then
|
||||
return
|
||||
end
|
||||
local words = {}
|
||||
local locales = {"enUS", "ruRU", "deDE", "frFR", "koKR", "zhCN", "zhTW", "esES"}
|
||||
local localizations = {}
|
||||
DEFAULT_CHAT_FRAME:AddMessage("--- AceLocale Debug ---")
|
||||
for _,locale in ipairs(locales) do
|
||||
if not self[TRANSLATION_TABLES][locale] then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q not found", locale))
|
||||
else
|
||||
localizations[locale] = self[TRANSLATION_TABLES][locale]
|
||||
end
|
||||
end
|
||||
local localeDebug = {}
|
||||
for locale, localization in pairs(localizations) do
|
||||
localeDebug[locale] = {}
|
||||
for word in pairs(localization) do
|
||||
if type(localization[word]) == "table" then
|
||||
if type(words[word]) ~= "table" then
|
||||
words[word] = {}
|
||||
end
|
||||
for bit in pairs(localization[word]) do
|
||||
if type(localization[word][bit]) == "string" then
|
||||
words[word][bit] = true
|
||||
end
|
||||
end
|
||||
elseif type(localization[word]) == "string" then
|
||||
words[word] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
for word in pairs(words) do
|
||||
if type(words[word]) == "table" then
|
||||
for bit in pairs(words[word]) do
|
||||
for locale, localization in pairs(localizations) do
|
||||
if not rawget(localization, word) or not localization[word][bit] then
|
||||
localeDebug[locale][word .. "::" .. bit] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
for locale, localization in pairs(localizations) do
|
||||
if not rawget(localization, word) then
|
||||
localeDebug[locale][word] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for locale, t in pairs(localeDebug) do
|
||||
if not next(t) then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q complete", locale))
|
||||
else
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("Locale %q missing:", locale))
|
||||
for word in pairs(t) do
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format(" %q", word))
|
||||
end
|
||||
end
|
||||
end
|
||||
DEFAULT_CHAT_FRAME:AddMessage("--- End AceLocale Debug ---")
|
||||
end
|
||||
|
||||
setmetatable(AceLocale.prototype, {
|
||||
__index = function(self, k)
|
||||
if type(k) ~= "table" and k ~= 0 and k ~= "GetLibraryVersion" and k ~= "error" and k ~= "assert" and k ~= "argCheck" and k ~= "pcall" then -- HACK: remove "GetLibraryVersion" and such later.
|
||||
AceLocale.error(lastSelf or self, "Translation %q does not exist.", k)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
})
|
||||
|
||||
local function activate(self, oldLib, oldDeactivate)
|
||||
AceLocale = self
|
||||
|
||||
self.frame = oldLib and oldLib.frame or CreateFrame("Frame")
|
||||
self.registry = oldLib and oldLib.registry or {}
|
||||
self.BASE_TRANSLATIONS = oldLib and oldLib.BASE_TRANSLATIONS or {}
|
||||
self.DEBUGGING = oldLib and oldLib.DEBUGGING or {}
|
||||
self.TRANSLATIONS = oldLib and oldLib.TRANSLATIONS or {}
|
||||
self.BASE_LOCALE = oldLib and oldLib.BASE_LOCALE or {}
|
||||
self.TRANSLATION_TABLES = oldLib and oldLib.TRANSLATION_TABLES or {}
|
||||
self.REVERSE_TRANSLATIONS = oldLib and oldLib.REVERSE_TRANSLATIONS or {}
|
||||
self.STRICTNESS = oldLib and oldLib.STRICTNESS or {}
|
||||
self.NAME = oldLib and oldLib.NAME or {}
|
||||
self.DYNAMIC_LOCALES = oldLib and oldLib.DYNAMIC_LOCALES or {}
|
||||
self.CURRENT_LOCALE = oldLib and oldLib.CURRENT_LOCALE or {}
|
||||
|
||||
BASE_TRANSLATIONS = self.BASE_TRANSLATIONS
|
||||
DEBUGGING = self.DEBUGGING
|
||||
TRANSLATIONS = self.TRANSLATIONS
|
||||
BASE_LOCALE = self.BASE_LOCALE
|
||||
TRANSLATION_TABLES = self.TRANSLATION_TABLES
|
||||
REVERSE_TRANSLATIONS = self.REVERSE_TRANSLATIONS
|
||||
STRICTNESS = self.STRICTNESS
|
||||
NAME = self.NAME
|
||||
DYNAMIC_LOCALES = self.DYNAMIC_LOCALES
|
||||
CURRENT_LOCALE = self.CURRENT_LOCALE
|
||||
|
||||
|
||||
local GetTime = GetTime
|
||||
local timeUntilClear = GetTime() + 5
|
||||
scheduleClear = function()
|
||||
if next(newRegistries) then
|
||||
self.frame:Show()
|
||||
timeUntilClear = GetTime() + 5
|
||||
end
|
||||
end
|
||||
|
||||
if not self.registry then
|
||||
self.registry = {}
|
||||
else
|
||||
for name, instance in pairs(self.registry) do
|
||||
local name = name
|
||||
local mt = getmetatable(instance)
|
||||
setmetatable(instance, nil)
|
||||
instance[NAME] = name
|
||||
local strict
|
||||
if instance[STRICTNESS] ~= nil then
|
||||
strict = instance[STRICTNESS]
|
||||
elseif instance[TRANSLATIONS] ~= instance[BASE_TRANSLATIONS] then
|
||||
if getmetatable(instance[TRANSLATIONS]).__index == oldLib.prototype then
|
||||
strict = true
|
||||
end
|
||||
end
|
||||
instance[STRICTNESS] = strict and true or false
|
||||
refixInstance(instance)
|
||||
end
|
||||
end
|
||||
|
||||
self.frame:SetScript("OnEvent", scheduleClear)
|
||||
self.frame:SetScript("OnUpdate", function() -- (this, elapsed)
|
||||
if timeUntilClear - GetTime() <= 0 then
|
||||
self.frame:Hide()
|
||||
for k in pairs(newRegistries) do
|
||||
clearCache(k)
|
||||
newRegistries[k] = nil
|
||||
k = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
self.frame:UnregisterAllEvents()
|
||||
self.frame:RegisterEvent("ADDON_LOADED")
|
||||
self.frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
self.frame:Show()
|
||||
|
||||
if oldDeactivate then
|
||||
oldDeactivate(oldLib)
|
||||
end
|
||||
end
|
||||
|
||||
AceLibrary:Register(AceLocale, MAJOR_VERSION, MINOR_VERSION, activate)
|
||||
+462
@@ -0,0 +1,462 @@
|
||||
--[[
|
||||
Name: AceModuleCore-2.0
|
||||
Revision: $Rev: 17998 $
|
||||
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/AceModuleCore-2.0
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceModuleCore-2.0
|
||||
Description: Mixin to provide a module system so that modules or plugins can
|
||||
use an addon as its core.
|
||||
Dependencies: AceLibrary, AceOO-2.0, AceAddon-2.0, AceEvent-2.0 (optional), Compost-2.0 (optional)
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceModuleCore-2.0"
|
||||
local MINOR_VERSION = "$Revision: 17998 $"
|
||||
|
||||
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
|
||||
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
|
||||
|
||||
if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
|
||||
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
|
||||
|
||||
local function safecall(func,a,b,c,d,e,f,g)
|
||||
local success, err = pcall(func,a,b,c,d,e,f,g)
|
||||
if not success then geterrorhandler()(err) end
|
||||
end
|
||||
|
||||
local table_setn
|
||||
do
|
||||
local version = GetBuildInfo()
|
||||
if string.find(version, "^2%.") then
|
||||
-- 2.0.0
|
||||
table_setn = function() end
|
||||
else
|
||||
table_setn = table.setn
|
||||
end
|
||||
end
|
||||
|
||||
local new, del
|
||||
do
|
||||
local list = setmetatable({}, {__mode = 'k'})
|
||||
function new()
|
||||
local t = next(list)
|
||||
if t then
|
||||
list[t] = nil
|
||||
return t
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
function del(t)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
table_setn(t, 0)
|
||||
list[t] = true
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local AceOO = AceLibrary:GetInstance("AceOO-2.0")
|
||||
local AceModuleCore = AceOO.Mixin {
|
||||
"NewModule",
|
||||
"HasModule",
|
||||
"GetModule",
|
||||
"IsModule",
|
||||
"IterateModules",
|
||||
"SetModuleMixins",
|
||||
"SetModuleClass",
|
||||
"IsModuleActive",
|
||||
"ToggleModuleActive"
|
||||
}
|
||||
local AceEvent
|
||||
|
||||
local Compost = AceLibrary:HasInstance("Compost-2.0") and AceLibrary("Compost-2.0")
|
||||
|
||||
local function getlibrary(lib)
|
||||
if type(lib) == "string" then
|
||||
return AceLibrary(lib)
|
||||
else
|
||||
return lib
|
||||
end
|
||||
end
|
||||
|
||||
local tmp
|
||||
function AceModuleCore:NewModule(name, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if not self.modules then
|
||||
AceModuleCore:error("CreatePrototype() must be called before attempting to create a new module.", 2)
|
||||
end
|
||||
AceModuleCore:argCheck(name, 2, "string")
|
||||
if string.len(name) == 0 then
|
||||
AceModuleCore:error("Bad argument #2 to `NewModule`, string must not be empty")
|
||||
end
|
||||
if self.modules[name] then
|
||||
AceModuleCore:error("The module %q has already been registered", name)
|
||||
end
|
||||
|
||||
if not tmp then
|
||||
tmp = {}
|
||||
end
|
||||
if a1 then table.insert(tmp, a1)
|
||||
if a2 then table.insert(tmp, a2)
|
||||
if a3 then table.insert(tmp, a3)
|
||||
if a4 then table.insert(tmp, a4)
|
||||
if a5 then table.insert(tmp, a5)
|
||||
if a6 then table.insert(tmp, a6)
|
||||
if a7 then table.insert(tmp, a7)
|
||||
if a8 then table.insert(tmp, a8)
|
||||
if a9 then table.insert(tmp, a9)
|
||||
if a10 then table.insert(tmp, a10)
|
||||
if a11 then table.insert(tmp, a11)
|
||||
if a12 then table.insert(tmp, a12)
|
||||
if a13 then table.insert(tmp, a13)
|
||||
if a14 then table.insert(tmp, a14)
|
||||
if a15 then table.insert(tmp, a15)
|
||||
if a16 then table.insert(tmp, a16)
|
||||
if a17 then table.insert(tmp, a17)
|
||||
if a18 then table.insert(tmp, a18)
|
||||
if a19 then table.insert(tmp, a19)
|
||||
if a20 then table.insert(tmp, a20)
|
||||
end end end end end end end end end end end end end end end end end end end end
|
||||
for k,v in ipairs(tmp) do
|
||||
tmp[k] = getlibrary(v)
|
||||
end
|
||||
|
||||
if self.moduleMixins then
|
||||
for _,mixin in ipairs(self.moduleMixins) do
|
||||
local exists = false
|
||||
for _,v in ipairs(tmp) do
|
||||
if mixin == v then
|
||||
exists = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not exists then
|
||||
table.insert(tmp, mixin)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local module = AceOO.Classpool(self.moduleClass, unpack(tmp)):new(name)
|
||||
self.modules[name] = module
|
||||
module.name = name
|
||||
module.title = name
|
||||
|
||||
AceModuleCore.totalModules[module] = self
|
||||
|
||||
if AceEvent then
|
||||
AceEvent:TriggerEvent("Ace2_ModuleCreated", module)
|
||||
end
|
||||
|
||||
for k in pairs(tmp) do
|
||||
tmp[k] = nil
|
||||
end
|
||||
table_setn(tmp, 0)
|
||||
return module
|
||||
end
|
||||
|
||||
function AceModuleCore:HasModule(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if a1 then if not self.modules[a1] then return false end
|
||||
if a2 then if not self.modules[a2] then return false end
|
||||
if a3 then if not self.modules[a3] then return false end
|
||||
if a4 then if not self.modules[a4] then return false end
|
||||
if a5 then if not self.modules[a5] then return false end
|
||||
if a6 then if not self.modules[a6] then return false end
|
||||
if a7 then if not self.modules[a7] then return false end
|
||||
if a8 then if not self.modules[a8] then return false end
|
||||
if a9 then if not self.modules[a9] then return false end
|
||||
if a10 then if not self.modules[a10] then return false end
|
||||
if a11 then if not self.modules[a11] then return false end
|
||||
if a12 then if not self.modules[a12] then return false end
|
||||
if a13 then if not self.modules[a13] then return false end
|
||||
if a14 then if not self.modules[a14] then return false end
|
||||
if a15 then if not self.modules[a15] then return false end
|
||||
if a16 then if not self.modules[a16] then return false end
|
||||
if a17 then if not self.modules[a17] then return false end
|
||||
if a18 then if not self.modules[a18] then return false end
|
||||
if a19 then if not self.modules[a19] then return false end
|
||||
if a20 then if not self.modules[a20] then return false end
|
||||
end end end end end end end end end end end end end end end end end end end end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function AceModuleCore:GetModule(name)
|
||||
if not self.modules then
|
||||
AceModuleCore:error("Error initializing class. Please report error.")
|
||||
end
|
||||
if not self.modules[name] then
|
||||
AceModuleCore:error("Cannot find module %q.", name)
|
||||
end
|
||||
return self.modules[name]
|
||||
end
|
||||
|
||||
function AceModuleCore:IsModule(module)
|
||||
if self == AceModuleCore then
|
||||
return AceModuleCore.totalModules[module]
|
||||
else
|
||||
for k,v in pairs(self.modules) do
|
||||
if v == module then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function AceModuleCore:IterateModules()
|
||||
local t = new()
|
||||
for k in pairs(self.modules) do
|
||||
table.insert(t, k)
|
||||
end
|
||||
table.sort(t)
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
local x = t[i]
|
||||
if x then
|
||||
return x, self.modules[x]
|
||||
else
|
||||
t = del(t)
|
||||
return nil
|
||||
end
|
||||
end, nil, nil
|
||||
end
|
||||
|
||||
function AceModuleCore:SetModuleMixins(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
|
||||
if self.moduleMixins then
|
||||
AceModuleCore:error('Cannot call "SetModuleMixins" twice')
|
||||
elseif not self.modules then
|
||||
AceModuleCore:error("Error initializing class. Please report error.")
|
||||
elseif next(self.modules) then
|
||||
AceModuleCore:error('Cannot call "SetModuleMixins" after "NewModule" has been called.')
|
||||
end
|
||||
|
||||
self.moduleMixins = Compost and Compost:Acquire(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) or {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20}
|
||||
for k,v in ipairs(self.moduleMixins) do
|
||||
self.moduleMixins[k] = getlibrary(v)
|
||||
end
|
||||
end
|
||||
|
||||
function AceModuleCore:SetModuleClass(class)
|
||||
class = getlibrary(class)
|
||||
AceModuleCore:assert(AceOO.inherits(class, AceOO.Class), "Bad argument #2 to `SetModuleClass' (Class expected)")
|
||||
if not self.modules then
|
||||
AceModuleCore:error("Error initializing class. Please report error.")
|
||||
end
|
||||
if self.customModuleClass then
|
||||
AceModuleCore:error("Cannot call `SetModuleClass' twice.")
|
||||
end
|
||||
self.customModuleClass = true
|
||||
self.moduleClass = class
|
||||
self.modulePrototype = class.prototype
|
||||
end
|
||||
|
||||
function AceModuleCore:ToggleModuleActive(module, state)
|
||||
AceModuleCore:argCheck(module, 2, "table", "string")
|
||||
AceModuleCore:argCheck(state, 3, "nil", "boolean")
|
||||
|
||||
if type(module) == "string" then
|
||||
if not self:HasModule(module) then
|
||||
AceModuleCore:error("Cannot find module %q", module)
|
||||
end
|
||||
module = self:GetModule(module)
|
||||
else
|
||||
if not self:IsModule(module) then
|
||||
AceModuleCore:error("%q is not a module", module)
|
||||
end
|
||||
end
|
||||
|
||||
local disable
|
||||
if state == nil then
|
||||
disable = self:IsModuleActive(module)
|
||||
else
|
||||
disable = not state
|
||||
if disable ~= self:IsModuleActive(module) then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if type(module.ToggleActive) == "function" then
|
||||
return module:ToggleActive(not disable)
|
||||
elseif AceOO.inherits(self, "AceDB-2.0") then
|
||||
if not self.db or not self.db.raw then
|
||||
AceModuleCore:error("Cannot toggle a module until `RegisterDB' has been called and `ADDON_LOADED' has been fireed.")
|
||||
end
|
||||
if type(self.db.raw.disabledModules) ~= "table" then
|
||||
self.db.raw.disabledModules = Compost and Compost:Acquire() or {}
|
||||
end
|
||||
local _,profile = self:GetProfile()
|
||||
if type(self.db.raw.disabledModules[profile]) ~= "table" then
|
||||
self.db.raw.disabledModules[profile] = Compost and Compost:Acquire() or {}
|
||||
end
|
||||
if type(self.db.raw.disabledModules[profile][module.name]) ~= "table" then
|
||||
self.db.raw.disabledModules[profile][module.name] = disable or nil
|
||||
end
|
||||
if not disable then
|
||||
if not next(self.db.raw.disabledModules[profile]) then
|
||||
if Compost then
|
||||
Compost:Reclaim(self.db.raw.disabledModules[profile])
|
||||
end
|
||||
self.db.raw.disabledModules[profile] = nil
|
||||
end
|
||||
if not next(self.db.raw.disabledModules) then
|
||||
if Compost then
|
||||
Compost:Reclaim(self.db.raw.disabledModules)
|
||||
end
|
||||
self.db.raw.disabledModules = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
if type(self.disabledModules) ~= "table" then
|
||||
self.disabledModules = Compost and Compost:Acquire() or {}
|
||||
end
|
||||
self.disabledModules[module.name] = disable or nil
|
||||
end
|
||||
if AceOO.inherits(module, "AceAddon-2.0") then
|
||||
local AceAddon = AceLibrary("AceAddon-2.0")
|
||||
if not AceAddon.addonsStarted[module] then
|
||||
return
|
||||
end
|
||||
end
|
||||
if not disable then
|
||||
local current = module.class
|
||||
while true do
|
||||
if current == AceOO.Class then
|
||||
break
|
||||
end
|
||||
if current.mixins then
|
||||
for mixin in pairs(current.mixins) do
|
||||
if type(mixin.OnEmbedEnable) == "function" then
|
||||
safecall(mixin.OnEmbedEnable, mixin, module)
|
||||
end
|
||||
end
|
||||
end
|
||||
current = current.super
|
||||
end
|
||||
if type(module.OnEnable) == "function" then
|
||||
safecall(module.OnEnable, module)
|
||||
end
|
||||
if AceEvent then
|
||||
AceEvent:TriggerEvent("Ace2_AddonEnabled", module)
|
||||
end
|
||||
else
|
||||
local current = module.class
|
||||
while true do
|
||||
if current == AceOO.Class then
|
||||
break
|
||||
end
|
||||
if current.mixins then
|
||||
for mixin in pairs(current.mixins) do
|
||||
if type(mixin.OnEmbedDisable) == "function" then
|
||||
safecall(mixin.OnEmbedDisable, mixin, module)
|
||||
end
|
||||
end
|
||||
end
|
||||
current = current.super
|
||||
end
|
||||
if type(module.OnDisable) == "function" then
|
||||
safecall(module.OnDisable, module)
|
||||
end
|
||||
if AceEvent then
|
||||
AceEvent:TriggerEvent("Ace2_AddonDisabled", module)
|
||||
end
|
||||
end
|
||||
return not disable
|
||||
end
|
||||
|
||||
function AceModuleCore:IsModuleActive(module)
|
||||
AceModuleCore:argCheck(module, 2, "table", "string")
|
||||
|
||||
if AceModuleCore == self then
|
||||
self:argCheck(module, 2, "table")
|
||||
|
||||
local core = AceModuleCore.totalModules[module]
|
||||
if not core then
|
||||
self:error("Bad argument #2 to `IsModuleActive'. Not a module")
|
||||
end
|
||||
return core:IsModuleActive(module)
|
||||
end
|
||||
|
||||
if type(module) == "string" then
|
||||
if not self:HasModule(module) then
|
||||
AceModuleCore:error("Cannot find module %q", module)
|
||||
end
|
||||
module = self:GetModule(module)
|
||||
else
|
||||
if not self:IsModule(module) then
|
||||
AceModuleCore:error("%q is not a module", module)
|
||||
end
|
||||
end
|
||||
|
||||
if type(module.IsActive) == "function" then
|
||||
return module:IsActive()
|
||||
elseif AceOO.inherits(self, "AceDB-2.0") then
|
||||
local _,profile = self:GetProfile()
|
||||
return not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[profile] or not self.db.raw.disabledModules[profile][module.name]
|
||||
else
|
||||
return not self.disabledModules or not self.disabledModules[module.name]
|
||||
end
|
||||
end
|
||||
|
||||
function AceModuleCore:OnInstanceInit(target)
|
||||
if target.modules then
|
||||
AceModuleCore:error("OnInstanceInit cannot be called twice")
|
||||
end
|
||||
target.modules = Compost and Compost:Acquire() or {}
|
||||
|
||||
target.moduleClass = AceOO.Class("AceAddon-2.0")
|
||||
target.modulePrototype = target.moduleClass.prototype
|
||||
end
|
||||
|
||||
AceModuleCore.OnManualEmbed = AceModuleCore.OnInstanceInit
|
||||
|
||||
function AceModuleCore.OnEmbedProfileDisable(AceModuleCore, self, newProfile)
|
||||
if not AceOO.inherits(self, "AceDB-2.0") then
|
||||
return
|
||||
end
|
||||
local _,currentProfile = self:GetProfile()
|
||||
for k, module in pairs(self.modules) do
|
||||
if type(module.IsActive) == "function" or type(module.ToggleActive) == "function" then
|
||||
-- continue
|
||||
else
|
||||
local currentActive = not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[currentProfile] or not self.db.raw.disabledModules[currentProfile][module.name]
|
||||
local newActive = not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[newProfile] or not self.db.raw.disabledModules[newProfile][module.name]
|
||||
if currentActive ~= newActive then
|
||||
self:ToggleModuleActive(module)
|
||||
if not self.db.raw.disabledModules then
|
||||
self.db.raw.disabledModules = {}
|
||||
end
|
||||
if not self.db.raw.disabledModules[currentProfile] then
|
||||
self.db.raw.disabledModules[currentProfile] = {}
|
||||
end
|
||||
self.db.raw.disabledModules[currentProfile][module.name] = not currentActive or nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function activate(self, oldLib, oldDeactivate)
|
||||
AceModuleCore = self
|
||||
|
||||
if oldLib then
|
||||
self.totalModules = oldLib.totalModules
|
||||
end
|
||||
if not self.totalModules then
|
||||
self.totalModules = {}
|
||||
end
|
||||
|
||||
self.super.activate(self, oldLib, oldDeactivate)
|
||||
end
|
||||
|
||||
local function external(self, major, instance)
|
||||
if major == "Compost-2.0" then
|
||||
Compost = instance
|
||||
elseif major == "AceEvent-2.0" then
|
||||
AceEvent = instance
|
||||
end
|
||||
end
|
||||
|
||||
AceLibrary:Register(AceModuleCore, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
|
||||
AceModuleCore = AceLibrary(MAJOR_VERSION)
|
||||
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
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,982 @@
|
||||
-- No PerfBoost v2, no need for settings
|
||||
local has_perf_boost = pcall(GetCVar, "PB_Enabled")
|
||||
if not has_perf_boost then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost|cffffaaaa not present hiding settings.")
|
||||
return
|
||||
end
|
||||
|
||||
PerfBoost = AceLibrary("AceAddon-2.0"):new("AceEvent-2.0", "AceDebug-2.0", "AceModuleCore-2.0", "AceConsole-2.0", "AceDB-2.0", "AceHook-2.1")
|
||||
PerfBoost:RegisterDB("PerfBoostSettingsDB")
|
||||
PerfBoost:RegisterDefaults("profile", {
|
||||
per_character_settings = false,
|
||||
previous_player_render_dist = nil,
|
||||
previous_player_render_dist_in_combat = nil,
|
||||
previous_doodad_render_dist = nil,
|
||||
})
|
||||
PerfBoost.frame = CreateFrame("Frame", "PerfBoost", UIParent)
|
||||
|
||||
BINDING_HEADER_PERFBOOST = "PerfBoost";
|
||||
BINDING_NAME_PERFBOOSTENABLE = "Toggles whether perfboot is enabled/disabled";
|
||||
BINDING_NAME_PBHIDEALLPLAYERS = "Toggle hide all players (overrides other settings)";
|
||||
BINDING_NAME_PBTOGGLEPLAYERRENDERIST = "Toggle 0 player render distance (hides all players respecting other settings)";
|
||||
BINDING_NAME_PBSHOWALLPLAYERS = "Toggle show all players (overrides other settings)";
|
||||
BINDING_NAME_PBCYCLEPLAYERVISIBILITY = "Cycle player visibility: show all -> hide all -> normal";
|
||||
BINDING_NAME_PBCYCLEDOODADVISIBILITY = "Cycle doodad visibility: current -> hidden -> always render";
|
||||
|
||||
-- used when turning on per character settings
|
||||
function PerfBoost:SavePerCharacterSettings()
|
||||
local function saveNestedSettings(args)
|
||||
for settingKey, settingData in pairs(args) do
|
||||
if string.find(settingKey, "PB_") == 1 and settingData.get and settingData.set then
|
||||
settingData.set(settingData.get()) -- trigger the set function for each setting with the current value
|
||||
elseif settingData.args then
|
||||
saveNestedSettings(settingData.args)
|
||||
end
|
||||
end
|
||||
end
|
||||
saveNestedSettings(PerfBoost.cmdtable.args)
|
||||
end
|
||||
|
||||
function PerfBoost:ApplySavedSettings()
|
||||
local function applyNestedSettings(args)
|
||||
for settingKey, settingData in pairs(args) do
|
||||
if string.find(settingKey, "PB_") == 1 and settingData.set then
|
||||
if PerfBoost.db.profile[settingKey] ~= nil then
|
||||
settingData.set(PerfBoost.db.profile[settingKey])
|
||||
end
|
||||
elseif settingData.args then
|
||||
applyNestedSettings(settingData.args)
|
||||
end
|
||||
end
|
||||
end
|
||||
applyNestedSettings(PerfBoost.cmdtable.args)
|
||||
end
|
||||
|
||||
function PerfBoost:OnEnable()
|
||||
-- if per character settings are enabled, apply them
|
||||
if PerfBoost.db.profile.per_character_settings then
|
||||
PerfBoost:ApplySavedSettings()
|
||||
end
|
||||
end
|
||||
|
||||
PerfBoost.cmdtable = {
|
||||
type = "group",
|
||||
handler = PerfBoost,
|
||||
args = {
|
||||
per_character_settings = {
|
||||
type = "toggle",
|
||||
name = "Enable Per Character Settings",
|
||||
desc = "Whether to use per character settings for all of the PB_ settings. This will cause settings saved in your character's PerfBoostSettings.lua to override any global settings in Config.wtf.",
|
||||
order = 1,
|
||||
get = function()
|
||||
return PerfBoost.db.profile.per_character_settings
|
||||
end,
|
||||
set = function(v)
|
||||
if v ~= PerfBoost.db.profile.per_character_settings then
|
||||
PerfBoost.db.profile.per_character_settings = v
|
||||
if v == true then
|
||||
PerfBoost:SavePerCharacterSettings()
|
||||
end
|
||||
end
|
||||
end,
|
||||
},
|
||||
PB_Enabled = {
|
||||
type = "toggle",
|
||||
name = "Enable Performance Boost",
|
||||
desc = "Enable/disable all performance boost features",
|
||||
order = 5,
|
||||
get = function()
|
||||
return GetCVar("PB_Enabled") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_Enabled = v
|
||||
if v == true then
|
||||
SetCVar("PB_Enabled", "1")
|
||||
else
|
||||
SetCVar("PB_Enabled", "0")
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
PB_ShowAllPlayers = {
|
||||
type = "toggle",
|
||||
name = "Show All Players (Overrides other settings)",
|
||||
desc = "Show all players regardless of distance and other render settings. Has priority over hide all players.",
|
||||
order = 6,
|
||||
get = function()
|
||||
return GetCVar("PB_ShowAllPlayers") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_ShowAllPlayers = v
|
||||
if v == true then
|
||||
SetCVar("PB_ShowAllPlayers", "1")
|
||||
else
|
||||
SetCVar("PB_ShowAllPlayers", "0")
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
PB_HideAllPlayers = {
|
||||
type = "toggle",
|
||||
name = "Hide All Players (Overrides other settings)",
|
||||
desc = "Hide all players regardless of other render settings",
|
||||
order = 7,
|
||||
get = function()
|
||||
return GetCVar("PB_HideAllPlayers") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_HideAllPlayers = v
|
||||
if v == true then
|
||||
SetCVar("PB_HideAllPlayers", "1")
|
||||
else
|
||||
SetCVar("PB_HideAllPlayers", "0")
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
spacera = {
|
||||
type = "header",
|
||||
name = " ",
|
||||
order = 10,
|
||||
},
|
||||
|
||||
rendering = {
|
||||
type = "group",
|
||||
name = "Rendering Settings",
|
||||
desc = "Configure all rendering and visibility options",
|
||||
order = 15,
|
||||
args = {
|
||||
PB_AlwaysRenderRaidMarks = {
|
||||
type = "toggle",
|
||||
name = "Always Render Raid Marks",
|
||||
desc = "Whether to always render raid marks",
|
||||
order = 1,
|
||||
get = function()
|
||||
return GetCVar("PB_AlwaysRenderRaidMarks") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_AlwaysRenderRaidMarks = v
|
||||
if v == true then
|
||||
SetCVar("PB_AlwaysRenderRaidMarks", "1")
|
||||
else
|
||||
SetCVar("PB_AlwaysRenderRaidMarks", "0")
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
PB_AlwaysRenderPVP = {
|
||||
type = "toggle",
|
||||
name = "Always Render PVP Players",
|
||||
desc = "Whether to always render players flagged for PVP",
|
||||
order = 2,
|
||||
get = function()
|
||||
return GetCVar("PB_AlwaysRenderPVP") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_AlwaysRenderPVP = v
|
||||
if v == true then
|
||||
SetCVar("PB_AlwaysRenderPVP", "1")
|
||||
else
|
||||
SetCVar("PB_AlwaysRenderPVP", "0")
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
PB_AlwaysRenderPlayersWithAggro = {
|
||||
type = "toggle",
|
||||
name = "Always Render Players With Aggro",
|
||||
desc = "Whether to always render players who have aggro from enemies",
|
||||
order = 3,
|
||||
get = function()
|
||||
return GetCVar("PB_AlwaysRenderPlayersWithAggro") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_AlwaysRenderPlayersWithAggro = v
|
||||
if v == true then
|
||||
SetCVar("PB_AlwaysRenderPlayersWithAggro", "1")
|
||||
else
|
||||
SetCVar("PB_AlwaysRenderPlayersWithAggro", "0")
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
PB_AlwaysRenderPlayers = {
|
||||
type = "text",
|
||||
name = "Always Render Players (ENTER to save)",
|
||||
desc = "Comma separated list of playernames to always render regardless of other settings",
|
||||
usage = "Name1,Name2,etc",
|
||||
order = 4,
|
||||
get = function()
|
||||
return GetCVar("PB_AlwaysRenderPlayers") or ""
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_AlwaysRenderPlayers = v
|
||||
SetCVar("PB_AlwaysRenderPlayers", v)
|
||||
end,
|
||||
},
|
||||
|
||||
PB_NeverRenderPlayers = {
|
||||
type = "text",
|
||||
name = "Never Render Players (ENTER to save)",
|
||||
desc = "Comma separated list of playernames to never render regardless of other settings",
|
||||
usage = "Name1,Name2,etc",
|
||||
order = 5,
|
||||
get = function()
|
||||
return GetCVar("PB_NeverRenderPlayers") or ""
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_NeverRenderPlayers = v
|
||||
SetCVar("PB_NeverRenderPlayers", v)
|
||||
end,
|
||||
},
|
||||
|
||||
spacer_player = {
|
||||
type = "header",
|
||||
name = "Player Distances",
|
||||
order = 10,
|
||||
},
|
||||
|
||||
PB_PlayerRenderDist = {
|
||||
type = "range",
|
||||
name = "Default Player Render Distance",
|
||||
desc = "Max distance to render players when not in combat",
|
||||
order = 11,
|
||||
min = -1,
|
||||
max = 100,
|
||||
step = 1,
|
||||
get = function()
|
||||
local val = GetCVar("PB_PlayerRenderDist")
|
||||
return val and tonumber(val) or 0
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_PlayerRenderDist = v
|
||||
SetCVar("PB_PlayerRenderDist", tostring(v))
|
||||
end,
|
||||
},
|
||||
PB_PlayerRenderDistInCombat = {
|
||||
type = "range",
|
||||
name = "In Combat Player Render Distance",
|
||||
desc = "Max distance to render players when in combat",
|
||||
order = 12,
|
||||
min = -1,
|
||||
max = 100,
|
||||
step = 1,
|
||||
get = function()
|
||||
local val = GetCVar("PB_PlayerRenderDistInCombat")
|
||||
return val and tonumber(val) or 0
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_PlayerRenderDistInCombat = v
|
||||
SetCVar("PB_PlayerRenderDistInCombat", tostring(v))
|
||||
end,
|
||||
},
|
||||
PB_PlayerRenderDistInCities = {
|
||||
type = "range",
|
||||
name = "In Cities Player Render Distance",
|
||||
desc = "Max distance to render players when in cities",
|
||||
order = 13,
|
||||
min = -1,
|
||||
max = 100,
|
||||
step = 1,
|
||||
get = function()
|
||||
local val = GetCVar("PB_PlayerRenderDistInCities")
|
||||
return val and tonumber(val) or 0
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_PlayerRenderDistInCities = v
|
||||
SetCVar("PB_PlayerRenderDistInCities", tostring(v))
|
||||
end,
|
||||
},
|
||||
|
||||
spacer_trash = {
|
||||
type = "header",
|
||||
name = "Trash Units",
|
||||
order = 20,
|
||||
},
|
||||
|
||||
PB_TrashUnitRenderDist = {
|
||||
type = "range",
|
||||
name = "Default Trash Unit (lvl < 63) Render Distance",
|
||||
desc = "Max distance to render trash units when not in combat",
|
||||
order = 21,
|
||||
min = -1,
|
||||
max = 100,
|
||||
step = 1,
|
||||
get = function()
|
||||
local val = GetCVar("PB_TrashUnitRenderDist")
|
||||
return val and tonumber(val) or 0
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_TrashUnitRenderDist = v
|
||||
SetCVar("PB_TrashUnitRenderDist", tostring(v))
|
||||
end,
|
||||
},
|
||||
PB_TrashUnitRenderDistInCombat = {
|
||||
type = "range",
|
||||
name = "In Combat Trash Unit (lvl < 63) Render Distance",
|
||||
desc = "Max distance to render trash units when in combat",
|
||||
order = 22,
|
||||
min = -1,
|
||||
max = 100,
|
||||
step = 1,
|
||||
get = function()
|
||||
local val = GetCVar("PB_TrashUnitRenderDistInCombat")
|
||||
return val and tonumber(val) or 0
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_TrashUnitRenderDistInCombat = v
|
||||
SetCVar("PB_TrashUnitRenderDistInCombat", tostring(v))
|
||||
end,
|
||||
},
|
||||
|
||||
spacer_pets = {
|
||||
type = "header",
|
||||
name = "Pets & Summons",
|
||||
order = 30,
|
||||
},
|
||||
|
||||
PB_PetRenderDist = {
|
||||
type = "range",
|
||||
name = "Default Pet Render Distance",
|
||||
desc = "Max distance to render pets when not in combat. Your own pet is always shown.",
|
||||
order = 31,
|
||||
min = -1,
|
||||
max = 100,
|
||||
step = 1,
|
||||
get = function()
|
||||
local val = GetCVar("PB_PetRenderDist")
|
||||
return val and tonumber(val) or 0
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_PetRenderDist = v
|
||||
SetCVar("PB_PetRenderDist", tostring(v))
|
||||
end,
|
||||
},
|
||||
PB_PetRenderDistInCombat = {
|
||||
type = "range",
|
||||
name = "In Combat Pet Render Distance",
|
||||
desc = "Max distance to render pets when in combat. Your own pet is always shown.",
|
||||
order = 32,
|
||||
min = -1,
|
||||
max = 100,
|
||||
step = 1,
|
||||
get = function()
|
||||
local val = GetCVar("PB_PetRenderDistInCombat")
|
||||
return val and tonumber(val) or 0
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_PetRenderDistInCombat = v
|
||||
SetCVar("PB_PetRenderDistInCombat", tostring(v))
|
||||
end,
|
||||
},
|
||||
|
||||
PB_SummonRenderDist = {
|
||||
type = "range",
|
||||
name = "Default Totem/Guardian Render Distance",
|
||||
desc = "Max distance to render unnamed summons when not in combat. Your own totems and guardians are always shown.",
|
||||
order = 33,
|
||||
min = -1,
|
||||
max = 100,
|
||||
step = 1,
|
||||
get = function()
|
||||
local val = GetCVar("PB_SummonRenderDist")
|
||||
return val and tonumber(val) or 0
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_SummonRenderDist = v
|
||||
SetCVar("PB_SummonRenderDist", tostring(v))
|
||||
end,
|
||||
},
|
||||
PB_SummonRenderDistInCombat = {
|
||||
type = "range",
|
||||
name = "In Combat Totem/Guardian Render Distance",
|
||||
desc = "Max distance to render unnamed summons when in combat. Your own totems and guardians are always shown.",
|
||||
order = 34,
|
||||
min = -1,
|
||||
max = 100,
|
||||
step = 1,
|
||||
get = function()
|
||||
local val = GetCVar("PB_SummonRenderDistInCombat")
|
||||
return val and tonumber(val) or 0
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_SummonRenderDistInCombat = v
|
||||
SetCVar("PB_SummonRenderDistInCombat", tostring(v))
|
||||
end,
|
||||
},
|
||||
|
||||
spacer_corpse = {
|
||||
type = "header",
|
||||
name = "Other Objects",
|
||||
order = 40,
|
||||
},
|
||||
|
||||
PB_CorpseRenderDist = {
|
||||
type = "range",
|
||||
name = "Corpse Render Distance (see desc)",
|
||||
desc = "Max distance to render corpses. Won't hide corpses for units >= lvl 63 or any corpses if you have group loot, need before greed or round robin set.",
|
||||
order = 41,
|
||||
min = -1,
|
||||
max = 100,
|
||||
step = 1,
|
||||
get = function()
|
||||
local val = GetCVar("PB_CorpseRenderDist")
|
||||
return val and tonumber(val) or 0
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_CorpseRenderDist = v
|
||||
SetCVar("PB_CorpseRenderDist", tostring(v))
|
||||
end,
|
||||
},
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
spells = {
|
||||
type = "group",
|
||||
name = "Spell Visual Settings",
|
||||
desc = "Configure spell visual effects and filtering",
|
||||
order = 20,
|
||||
args = {
|
||||
PB_ShowPlayerSpellVisuals = {
|
||||
type = "toggle",
|
||||
name = "Show Other Player Spell Visuals",
|
||||
desc = "Whether to show spell visual effects from other players. This includes spell casts, ranged weapon projectiles, wanding, not sure what else.",
|
||||
order = 1,
|
||||
get = function()
|
||||
return GetCVar("PB_ShowPlayerSpellVisuals") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_ShowPlayerSpellVisuals = v
|
||||
if v == true then
|
||||
SetCVar("PB_ShowPlayerSpellVisuals", "1")
|
||||
else
|
||||
SetCVar("PB_ShowPlayerSpellVisuals", "0")
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
PB_ShowPlayerGroundEffects = {
|
||||
type = "toggle",
|
||||
name = "Show Other Player Ground Effects",
|
||||
desc = "Whether to show ground effects from players",
|
||||
order = 2,
|
||||
get = function()
|
||||
return GetCVar("PB_ShowPlayerGroundEffects") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_ShowPlayerGroundEffects = v
|
||||
if v == true then
|
||||
SetCVar("PB_ShowPlayerGroundEffects", "1")
|
||||
else
|
||||
SetCVar("PB_ShowPlayerGroundEffects", "0")
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
PB_ShowPlayerAuraVisuals = {
|
||||
type = "toggle",
|
||||
name = "Show Other Player Aura Visuals",
|
||||
desc = "Whether to show aura visual effects on players",
|
||||
order = 3,
|
||||
get = function()
|
||||
return GetCVar("PB_ShowPlayerAuraVisuals") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_ShowPlayerAuraVisuals = v
|
||||
if v == true then
|
||||
SetCVar("PB_ShowPlayerAuraVisuals", "1")
|
||||
else
|
||||
SetCVar("PB_ShowPlayerAuraVisuals", "0")
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
PB_ShowUnitAuraVisuals = {
|
||||
type = "toggle",
|
||||
name = "Show Unit Aura Visuals",
|
||||
desc = "Whether to show aura visual effects on units (NPCs, mobs)",
|
||||
order = 4,
|
||||
get = function()
|
||||
return GetCVar("PB_ShowUnitAuraVisuals") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_ShowUnitAuraVisuals = v
|
||||
if v == true then
|
||||
SetCVar("PB_ShowUnitAuraVisuals", "1")
|
||||
else
|
||||
SetCVar("PB_ShowUnitAuraVisuals", "0")
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
PB_HideSpellsForHiddenPlayers = {
|
||||
type = "toggle",
|
||||
name = "Hide Spells for Hidden Players",
|
||||
desc = "Hide spell visuals from players that are hidden due to render distance or other settings",
|
||||
order = 5,
|
||||
get = function()
|
||||
return GetCVar("PB_HideSpellsForHiddenPlayers") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_HideSpellsForHiddenPlayers = v
|
||||
if v == true then
|
||||
SetCVar("PB_HideSpellsForHiddenPlayers", "1")
|
||||
else
|
||||
SetCVar("PB_HideSpellsForHiddenPlayers", "0")
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
spacer_spells = {
|
||||
type = "header",
|
||||
order = 10,
|
||||
},
|
||||
|
||||
PB_HiddenSpellIds = {
|
||||
type = "text",
|
||||
name = "Always Hidden Spell IDs (ENTER to save)",
|
||||
desc = "Comma separated list of spell IDs to hide visuals for",
|
||||
usage = "1234,5678,etc",
|
||||
order = 11,
|
||||
get = function()
|
||||
return GetCVar("PB_HiddenSpellIds") or ""
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_HiddenSpellIds = v
|
||||
SetCVar("PB_HiddenSpellIds", v)
|
||||
end,
|
||||
},
|
||||
|
||||
PB_AlwaysShownSpellIds = {
|
||||
type = "text",
|
||||
name = "Always Shown Spell IDs (ENTER to save)",
|
||||
desc = "Comma separated list of spell IDs to always show visuals for, overriding other settings",
|
||||
usage = "1234,5678,etc",
|
||||
order = 12,
|
||||
get = function()
|
||||
return GetCVar("PB_AlwaysShownSpellIds") or ""
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_AlwaysShownSpellIds = v
|
||||
SetCVar("PB_AlwaysShownSpellIds", v)
|
||||
end,
|
||||
},
|
||||
|
||||
PB_ApplyHiddenSpellIdsToMe = {
|
||||
type = "toggle",
|
||||
name = "Apply Hidden Spell IDs to Me",
|
||||
desc = "Whether to apply the hidden spell IDs list to your own character's spells",
|
||||
order = 13,
|
||||
get = function()
|
||||
return GetCVar("PB_ApplyHiddenSpellIdsToMe") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_ApplyHiddenSpellIdsToMe = v
|
||||
if v == true then
|
||||
SetCVar("PB_ApplyHiddenSpellIdsToMe", "1")
|
||||
else
|
||||
SetCVar("PB_ApplyHiddenSpellIdsToMe", "0")
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
spacer_debug = {
|
||||
type = "header",
|
||||
name = " ",
|
||||
order = 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
local deuce = PerfBoost:NewModule("PerfBoost Options Menu")
|
||||
deuce.hasFuBar = IsAddOnLoaded("FuBar") and FuBar
|
||||
deuce.consoleCmd = not deuce.hasFuBar
|
||||
|
||||
PerfBoostOptions = AceLibrary("AceAddon-2.0"):new("AceDB-2.0", "FuBarPlugin-2.0")
|
||||
PerfBoostOptions.name = "FuBar - PerfBoost"
|
||||
PerfBoostOptions.hasIcon = "Interface\\Icons\\Spell_Nature_Strength"
|
||||
PerfBoostOptions.defaultMinimapPosition = 180
|
||||
PerfBoostOptions.independentProfile = true
|
||||
PerfBoostOptions.hideWithoutStandby = false
|
||||
|
||||
PerfBoostOptions.OnMenuRequest = PerfBoost.cmdtable
|
||||
local args = AceLibrary("FuBarPlugin-2.0"):GetAceOptionsDataTable(PerfBoostOptions)
|
||||
for k, v in pairs(args) do
|
||||
if PerfBoostOptions.OnMenuRequest.args[k] == nil then
|
||||
PerfBoostOptions.OnMenuRequest.args[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
SLASH_PBENABLE1, SLASH_PBENABLE2 = '/pbenable', '/perfboostenable'
|
||||
function SlashCmdList.PBENABLE(msg, editbox)
|
||||
local v = not (GetCVar("PB_Enabled") == "1")
|
||||
PerfBoost.db.profile.PB_Enabled = v
|
||||
if v == true then
|
||||
SetCVar("PB_Enabled", "1")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r enabled")
|
||||
else
|
||||
SetCVar("PB_Enabled", "0")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r disabled")
|
||||
end
|
||||
end
|
||||
|
||||
SLASH_PBALWAYSRENDER1, SLASH_PBALWAYSRENDER2 = '/pbalwaysrender', '/pbar'
|
||||
function SlashCmdList.PBALWAYSRENDER(msg, editbox)
|
||||
local target = UnitName("target")
|
||||
if not target then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r No target selected")
|
||||
return
|
||||
end
|
||||
|
||||
local current = GetCVar("PB_AlwaysRenderPlayers") or ""
|
||||
local names = {}
|
||||
if current ~= "" then
|
||||
for name in string.gfind(current, "([^,]+)") do
|
||||
name = string.gsub(name, "^%s*(.-)%s*$", "%1")
|
||||
if name ~= "" then
|
||||
names[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if names[target] then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r " .. target .. " is already in always render list")
|
||||
return
|
||||
end
|
||||
|
||||
names[target] = true
|
||||
local newList = ""
|
||||
for name in pairs(names) do
|
||||
if newList == "" then
|
||||
newList = name
|
||||
else
|
||||
newList = newList .. "," .. name
|
||||
end
|
||||
end
|
||||
|
||||
PerfBoost.db.profile.PB_AlwaysRenderPlayers = newList
|
||||
SetCVar("PB_AlwaysRenderPlayers", newList)
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Added " .. target .. " to always render list")
|
||||
end
|
||||
|
||||
SLASH_PBNEVERRENDER1, SLASH_PBNEVERRENDER2 = '/pbneverrender', '/pbnr'
|
||||
function SlashCmdList.PBNEVERRENDER(msg, editbox)
|
||||
local target = UnitName("target")
|
||||
if not target then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r No target selected")
|
||||
return
|
||||
end
|
||||
|
||||
local current = GetCVar("PB_NeverRenderPlayers") or ""
|
||||
local names = {}
|
||||
if current ~= "" then
|
||||
for name in string.gfind(current, "([^,]+)") do
|
||||
name = string.gsub(name, "^%s*(.-)%s*$", "%1")
|
||||
if name ~= "" then
|
||||
names[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if names[target] then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r " .. target .. " is already in never render list")
|
||||
return
|
||||
end
|
||||
|
||||
names[target] = true
|
||||
local newList = ""
|
||||
for name in pairs(names) do
|
||||
if newList == "" then
|
||||
newList = name
|
||||
else
|
||||
newList = newList .. "," .. name
|
||||
end
|
||||
end
|
||||
|
||||
PerfBoost.db.profile.PB_NeverRenderPlayers = newList
|
||||
SetCVar("PB_NeverRenderPlayers", newList)
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Added " .. target .. " to never render list")
|
||||
end
|
||||
|
||||
SLASH_PBALWAYSRENDERREMOVE1, SLASH_PBALWAYSRENDERREMOVE2 = '/pbalwaysrenderremove', '/pbarr'
|
||||
function SlashCmdList.PBALWAYSRENDERREMOVE(msg, editbox)
|
||||
local target = UnitName("target")
|
||||
if not target then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r No target selected")
|
||||
return
|
||||
end
|
||||
|
||||
local current = GetCVar("PB_AlwaysRenderPlayers") or ""
|
||||
local names = {}
|
||||
if current ~= "" then
|
||||
for name in string.gfind(current, "([^,]+)") do
|
||||
name = string.gsub(name, "^%s*(.-)%s*$", "%1")
|
||||
if name ~= "" and name ~= target then
|
||||
names[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local newList = ""
|
||||
for name in pairs(names) do
|
||||
if newList == "" then
|
||||
newList = name
|
||||
else
|
||||
newList = newList .. "," .. name
|
||||
end
|
||||
end
|
||||
|
||||
PerfBoost.db.profile.PB_AlwaysRenderPlayers = newList
|
||||
SetCVar("PB_AlwaysRenderPlayers", newList)
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Removed " .. target .. " from always render list")
|
||||
end
|
||||
|
||||
SLASH_PBNEVERRENDERREMOVE1, SLASH_PBNEVERRENDERREMOVE2 = '/pbneverrenderremove', '/pbnrr'
|
||||
function SlashCmdList.PBNEVERRENDERREMOVE(msg, editbox)
|
||||
local target = UnitName("target")
|
||||
if not target then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r No target selected")
|
||||
return
|
||||
end
|
||||
|
||||
local current = GetCVar("PB_NeverRenderPlayers") or ""
|
||||
local names = {}
|
||||
if current ~= "" then
|
||||
for name in string.gfind(current, "([^,]+)") do
|
||||
name = string.gsub(name, "^%s*(.-)%s*$", "%1")
|
||||
if name ~= "" and name ~= target then
|
||||
names[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local newList = ""
|
||||
for name in pairs(names) do
|
||||
if newList == "" then
|
||||
newList = name
|
||||
else
|
||||
newList = newList .. "," .. name
|
||||
end
|
||||
end
|
||||
|
||||
PerfBoost.db.profile.PB_NeverRenderPlayers = newList
|
||||
SetCVar("PB_NeverRenderPlayers", newList)
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Removed " .. target .. " from never render list")
|
||||
end
|
||||
|
||||
SLASH_PBALWAYSRENDERCLEAR1, SLASH_PBALWAYSRENDERCLEAR2 = '/pbalwaysrenderclear', '/pbarc'
|
||||
function SlashCmdList.PBALWAYSRENDERCLEAR(msg, editbox)
|
||||
PerfBoost.db.profile.PB_AlwaysRenderPlayers = ""
|
||||
SetCVar("PB_AlwaysRenderPlayers", "")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Cleared always render list")
|
||||
end
|
||||
|
||||
SLASH_PBNEVERRENDERCLEAR1, SLASH_PBNEVERRENDERCLEAR2 = '/pbneverrenderclear', '/pbnrc'
|
||||
function SlashCmdList.PBNEVERRENDERCLEAR(msg, editbox)
|
||||
PerfBoost.db.profile.PB_NeverRenderPlayers = ""
|
||||
SetCVar("PB_NeverRenderPlayers", "")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Cleared never render list")
|
||||
end
|
||||
|
||||
SLASH_PBHIDEALLPLAYERS1, SLASH_PBHIDEALLPLAYERS2 = '/pbhideallplayers', '/pbhap'
|
||||
function SlashCmdList.PBHIDEALLPLAYERS(msg, editbox)
|
||||
local v = not (GetCVar("PB_HideAllPlayers") == "1")
|
||||
PerfBoost.db.profile.PB_HideAllPlayers = v
|
||||
if v == true then
|
||||
SetCVar("PB_HideAllPlayers", "1")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Hide all players enabled")
|
||||
else
|
||||
SetCVar("PB_HideAllPlayers", "0")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Hide all players disabled")
|
||||
end
|
||||
end
|
||||
|
||||
SLASH_PBSHOWALLPLAYERS1, SLASH_PBSHOWALLPLAYERS2 = '/pbshowallplayers', '/pbsap'
|
||||
function SlashCmdList.PBSHOWALLPLAYERS(msg, editbox)
|
||||
local v = not (GetCVar("PB_ShowAllPlayers") == "1")
|
||||
PerfBoost.db.profile.PB_ShowAllPlayers = v
|
||||
if v == true then
|
||||
SetCVar("PB_ShowAllPlayers", "1")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Show all players enabled")
|
||||
else
|
||||
SetCVar("PB_ShowAllPlayers", "0")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Show all players disabled")
|
||||
end
|
||||
end
|
||||
|
||||
SLASH_PBCYCLEPLAYERVISIBILITY1, SLASH_PBCYCLEPLAYERVISIBILITY2 = '/pbcycleplayervisibility', '/pbcycle'
|
||||
function SlashCmdList.PBCYCLEPLAYERVISIBILITY(msg, editbox)
|
||||
local showAll = GetCVar("PB_ShowAllPlayers") == "1"
|
||||
local hideAll = GetCVar("PB_HideAllPlayers") == "1"
|
||||
|
||||
if not showAll and not hideAll then
|
||||
-- Normal -> Show All
|
||||
PerfBoost.db.profile.PB_ShowAllPlayers = true
|
||||
PerfBoost.db.profile.PB_HideAllPlayers = false
|
||||
SetCVar("PB_ShowAllPlayers", "1")
|
||||
SetCVar("PB_HideAllPlayers", "0")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Show all players enabled")
|
||||
elseif showAll and not hideAll then
|
||||
-- Show All -> Hide All
|
||||
PerfBoost.db.profile.PB_ShowAllPlayers = false
|
||||
PerfBoost.db.profile.PB_HideAllPlayers = true
|
||||
SetCVar("PB_ShowAllPlayers", "0")
|
||||
SetCVar("PB_HideAllPlayers", "1")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Hide all players enabled")
|
||||
else
|
||||
-- Hide All (or both) -> Normal
|
||||
PerfBoost.db.profile.PB_ShowAllPlayers = false
|
||||
PerfBoost.db.profile.PB_HideAllPlayers = false
|
||||
SetCVar("PB_ShowAllPlayers", "0")
|
||||
SetCVar("PB_HideAllPlayers", "0")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Normal player rendering enabled")
|
||||
end
|
||||
end
|
||||
|
||||
SLASH_PBTOGGLEPLAYERRENDERIST1, SLASH_PBTOGGLEPLAYERRENDERIST2 = '/pbtoggleplayerrenderist', '/pbtrd'
|
||||
function SlashCmdList.PBTOGGLEPLAYERRENDERIST(msg, editbox)
|
||||
local currentDist = PerfBoost.db.profile.PB_PlayerRenderDist
|
||||
local currentDistInCombat = PerfBoost.db.profile.PB_PlayerRenderDistInCombat
|
||||
|
||||
if PerfBoost.db.profile.previous_player_render_dist ~= nil then
|
||||
-- restore previous values
|
||||
local restoreValue = PerfBoost.db.profile.previous_player_render_dist
|
||||
local restoreValueInCombat = PerfBoost.db.profile.previous_player_render_dist_in_combat
|
||||
PerfBoost.db.profile.PB_PlayerRenderDist = restoreValue
|
||||
PerfBoost.db.profile.PB_PlayerRenderDistInCombat = restoreValueInCombat
|
||||
SetCVar("PB_PlayerRenderDist", tostring(restoreValue))
|
||||
SetCVar("PB_PlayerRenderDistInCombat", tostring(restoreValueInCombat))
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Player render distances restored to " .. tostring(restoreValue) .. " (normal) and " .. tostring(restoreValueInCombat) .. " (combat)")
|
||||
PerfBoost.db.profile.previous_player_render_dist = nil
|
||||
PerfBoost.db.profile.previous_player_render_dist_in_combat = nil
|
||||
else
|
||||
-- Save current values and set both to 0
|
||||
PerfBoost.db.profile.previous_player_render_dist = currentDist
|
||||
PerfBoost.db.profile.previous_player_render_dist_in_combat = currentDistInCombat
|
||||
PerfBoost.db.profile.PB_PlayerRenderDist = 0
|
||||
PerfBoost.db.profile.PB_PlayerRenderDistInCombat = 0
|
||||
SetCVar("PB_PlayerRenderDist", "0")
|
||||
SetCVar("PB_PlayerRenderDistInCombat", "0")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Player render distances set to 0 (saved: " .. tostring(currentDist) .. " normal, " .. tostring(currentDistInCombat) .. " combat)")
|
||||
end
|
||||
end
|
||||
|
||||
SLASH_PBCYCLEDOODADVISIBILITY1, SLASH_PBCYCLEDOODADVISIBILITY2 = '/pbcycledoodadvisibility', '/pbcycledoodads'
|
||||
function SlashCmdList.PBCYCLEDOODADVISIBILITY(msg, editbox)
|
||||
local has_doodad = pcall(GetCVar, "PB_DoodadRenderDist")
|
||||
if not has_doodad then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r PB_DoodadRenderDist not available")
|
||||
return
|
||||
end
|
||||
|
||||
local currentDist = tonumber(GetCVar("PB_DoodadRenderDist")) or 0
|
||||
|
||||
if PerfBoost.db.profile.previous_doodad_render_dist == nil then
|
||||
-- Normal -> 0 (hide doodads)
|
||||
PerfBoost.db.profile.previous_doodad_render_dist = currentDist
|
||||
PerfBoost.db.profile.PB_DoodadRenderDist = 0
|
||||
SetCVar("PB_DoodadRenderDist", "0")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Doodad render distance set to 0 (hidden)")
|
||||
elseif currentDist == 0 then
|
||||
-- 0 -> -1 (always render)
|
||||
PerfBoost.db.profile.PB_DoodadRenderDist = -1
|
||||
SetCVar("PB_DoodadRenderDist", "-1")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Doodad render distance set to -1 (always render)")
|
||||
else
|
||||
-- -1 (or any other state) -> restore previous
|
||||
local restoreValue = PerfBoost.db.profile.previous_doodad_render_dist
|
||||
PerfBoost.db.profile.PB_DoodadRenderDist = restoreValue
|
||||
SetCVar("PB_DoodadRenderDist", tostring(restoreValue))
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00PerfBoost:|r Doodad render distance restored to " .. tostring(restoreValue))
|
||||
PerfBoost.db.profile.previous_doodad_render_dist = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Conditionally add new CVars if they exist (for compatibility with older DLL versions)
|
||||
local has_laggy_boss_spells = pcall(GetCVar, "PB_HideLaggyBossSpellsOnOthers")
|
||||
if has_laggy_boss_spells then
|
||||
PerfBoost.cmdtable.args.spells.args.PB_HideLaggyBossSpellsOnOthers = {
|
||||
type = "toggle",
|
||||
name = "Hide Laggy Boss Spells on Others",
|
||||
desc = "Curated list of spells for Sapphiron, Gnarlmoon, and Anomalus that will be hidden on other players/units. If you aren't displaying unit auras most of these will already be hidden.",
|
||||
order = 6,
|
||||
get = function()
|
||||
return GetCVar("PB_HideLaggyBossSpellsOnOthers") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_HideLaggyBossSpellsOnOthers = v
|
||||
if v == true then
|
||||
SetCVar("PB_HideLaggyBossSpellsOnOthers", "1")
|
||||
else
|
||||
SetCVar("PB_HideLaggyBossSpellsOnOthers", "0")
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
local has_debug_shown_spells = pcall(GetCVar, "PB_DebugShownSpells")
|
||||
if has_debug_shown_spells then
|
||||
PerfBoost.cmdtable.args.spells.args.PB_DebugShownSpells = {
|
||||
type = "toggle",
|
||||
name = "Debug Shown Spells",
|
||||
desc = "Prints to the log information about each spell being displayed.",
|
||||
order = 21,
|
||||
get = function()
|
||||
return GetCVar("PB_DebugShownSpells") == "1"
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_DebugShownSpells = v
|
||||
if v == true then
|
||||
SetCVar("PB_DebugShownSpells", "1")
|
||||
else
|
||||
SetCVar("PB_DebugShownSpells", "0")
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
local has_doodad_render_dist = pcall(GetCVar, "PB_DoodadRenderDist")
|
||||
if has_doodad_render_dist then
|
||||
PerfBoost.cmdtable.args.rendering.args.PB_DoodadRenderDist = {
|
||||
type = "range",
|
||||
name = "Doodad/Game Object Render Distance",
|
||||
desc = "Max distance to render doodads. Interactable game objects (chests, doors, quest objects, etc.) are not hidden. Ignored if you are PvP flagged.",
|
||||
order = 42,
|
||||
min = -1,
|
||||
max = 100,
|
||||
step = 1,
|
||||
get = function()
|
||||
local val = GetCVar("PB_DoodadRenderDist")
|
||||
return val and tonumber(val) or 0
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_DoodadRenderDist = v
|
||||
SetCVar("PB_DoodadRenderDist", tostring(v))
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
local has_doodad_render_dist_in_cities_and_raids = pcall(GetCVar, "PB_DoodadRenderDistInCitiesAndRaids")
|
||||
if has_doodad_render_dist_in_cities_and_raids then
|
||||
PerfBoost.cmdtable.args.rendering.args.PB_DoodadRenderDistInCitiesAndRaids = {
|
||||
type = "range",
|
||||
name = "Doodad/Game Object Render Distance in Cities and Raids",
|
||||
desc = "Max distance to render doodads while in cities and 40 man raids. Takes priority. Interactable game objects (chests, doors, quest objects, etc.) are not hidden. Ignored if you are PvP flagged.",
|
||||
order = 43,
|
||||
min = -1,
|
||||
max = 100,
|
||||
step = 1,
|
||||
get = function()
|
||||
local val = GetCVar("PB_DoodadRenderDistInCitiesAndRaids")
|
||||
return val and tonumber(val) or 0
|
||||
end,
|
||||
set = function(v)
|
||||
PerfBoost.db.profile.PB_DoodadRenderDistInCitiesAndRaids = v
|
||||
SetCVar("PB_DoodadRenderDistInCitiesAndRaids", tostring(v))
|
||||
end,
|
||||
}
|
||||
end
|
||||
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.
@@ -0,0 +1,87 @@
|
||||
# -------------------------------------------------------------------------
|
||||
# TURTLE WOW (1.12) DXVK CONFIGURATION
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# --- WINDOWING & DISPLAY STABILITY ---
|
||||
# "False" disables Exclusive Fullscreen. This is critical for screen capture
|
||||
# reliability (OBS/Discord) and ensures the game behaves like a borderless
|
||||
# window, allowing seamless alt-tabbing.
|
||||
dxvk.allowFse = False
|
||||
|
||||
# Forces Vulkan to handle vertical sync logic within the window container.
|
||||
# This prevents screen tearing in windowed modes without the heavy input
|
||||
# lag penalty typically associated with standard V-Sync.
|
||||
dxvk.tearFree = True
|
||||
|
||||
# Defers the creation of the rendering surface until the window is fully
|
||||
# initialized. This fixes the "Black Screen" or "White Screen" hangs
|
||||
# often seen when launching the 1.12 client or alt-tabbing back into it.
|
||||
d3d9.deferSurfaceCreation = True
|
||||
|
||||
# --- STABILITY (CRITICAL FOR 32-BIT CLIENTS) ---
|
||||
# The 1.12 client uses a signed 32-bit integer to query VRAM.
|
||||
# On GPUs with high VRAM, this can overflow into negative values, causing
|
||||
# crashes. Capping the report to 4096MB prevents integer overflow errors.
|
||||
# Options: Any integer (MB). 0 = Autodetect (unsafe for 1.12).
|
||||
d3d9.maxAvailableMemory = 4096
|
||||
|
||||
# Aggressively evicts texture data from System RAM once uploaded to the GPU.
|
||||
# This significantly reduces the Virtual Memory footprint of the 32-bit
|
||||
# process, helping to prevent "Error #132" (Out of Memory) crashes.
|
||||
d3d9.evictManagedOnUnlock = True
|
||||
|
||||
# --- VISUAL FIDELITY ---
|
||||
# Forces 4x MSAA (Multisample Anti-Aliasing) via the Vulkan backend.
|
||||
# This provides much cleaner edge smoothing than the native WoW 1.12
|
||||
# implementation. (Note: Set WoW's internal "Multisampling" to 1x/Off).
|
||||
# Options: -1 (Off), 0 (Off), 2, 4, 8, 16.
|
||||
d3d9.forceSwapchainMSAA = 16
|
||||
|
||||
# Forces 16x Anisotropic Filtering via Vulkan.
|
||||
# Overrides the legacy 1.12 filtering implementation, providing sharper
|
||||
# textures on angled surfaces (roads, hills) with negligible performance cost.
|
||||
# Options: -1 (Game Default), 0 (Off), 1, 2, 4, 8, 16.
|
||||
d3d9.samplerAnisotropy = 16
|
||||
|
||||
# Prevents texture shimmering or "noise" when the camera is moving
|
||||
# by clamping negative Level of Detail bias.
|
||||
d3d9.clampNegativeLodBias = True
|
||||
|
||||
# Forces the use of the hardware cursor.
|
||||
# This decouples the cursor from the game's render loop, preventing mouse
|
||||
# lag or disappearance during framerate dips.
|
||||
d3d9.cursor = True
|
||||
|
||||
# --- PERFORMANCE & SMOOTHNESS ---
|
||||
# Enables "Graphics Pipeline Library" (Fast Linking).
|
||||
# "Auto" uses fast linking to compile shaders instantly (eliminating stutter)
|
||||
# while simultaneously optimizing them on a background thread.
|
||||
# Options: Auto (Recommended), True (Force On), False (Force Off).
|
||||
dxvk.enableGraphicsPipelineLibrary = auto
|
||||
|
||||
# Explicitly enables the state cache to ensure compiled shaders are saved
|
||||
# to disk. This prevents micro-stutters on subsequent game launches.
|
||||
dxvk.stateCache = True
|
||||
|
||||
# Sets the number of frame buffers.
|
||||
# Set to 2 to minimize input latency for reactive combat. The "tearFree"
|
||||
# setting above handles the smoothness, so 3 buffers are unnecessary and
|
||||
# only add input lag.
|
||||
# Options: 1 (Double Buffering), 2 (Triple Buffering), 3+ (Extra Buffering).
|
||||
dxvk.numBackBuffers = 2
|
||||
|
||||
# Limits the number of frames the CPU can queue ahead of the GPU.
|
||||
# Keeping this at 1 ensures input remains as snappy as possible,
|
||||
# counteracting latency from windowed mode presentation.
|
||||
# Options: 1-16 (Lower = Less Input Lag, Higher = Smoother Pacing).
|
||||
d3d9.maxFrameLatency = 1
|
||||
|
||||
# --- SYSTEM INTERACTION ---
|
||||
# Disables logging to minimize disk I/O and unnecessary CPU overhead.
|
||||
# Options: none, error, warn, info, debug.
|
||||
dxvk.logLevel = none
|
||||
|
||||
# Disables High DPI awareness in DXVK.
|
||||
# Prevents double-scaling issues if the operating system is using
|
||||
# display scaling (e.g., 125% or 150%).
|
||||
d3d9.dpiAware = False
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 260 KiB |
+703
@@ -0,0 +1,703 @@
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
import math
|
||||
import tkinter as tk
|
||||
import webbrowser
|
||||
from tkinter import ttk, filedialog, messagebox
|
||||
|
||||
def get_base_path():
|
||||
"""Gets the correct directory whether running as a script or a compiled .exe"""
|
||||
if getattr(sys, 'frozen', False):
|
||||
# Running as a compiled PyInstaller executable
|
||||
return sys._MEIPASS
|
||||
# Running as a normal Python script
|
||||
return os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
class ToolTip:
|
||||
"""Creates a hover-tooltip for a given tkinter widget."""
|
||||
def __init__(self, widget, text):
|
||||
self.widget = widget
|
||||
self.text = text
|
||||
self.tw = None
|
||||
self.id = None
|
||||
self.widget.bind("<Enter>", self.enter)
|
||||
self.widget.bind("<Leave>", self.leave)
|
||||
|
||||
def enter(self, event=None):
|
||||
self.schedule()
|
||||
|
||||
def schedule(self):
|
||||
self.unschedule()
|
||||
self.id = self.widget.after(400, self.showtip) # 400ms delay before showing
|
||||
|
||||
def unschedule(self):
|
||||
if self.id:
|
||||
self.widget.after_cancel(self.id)
|
||||
self.id = None
|
||||
|
||||
def showtip(self, event=None):
|
||||
x, y, cx, cy = self.widget.bbox("insert")
|
||||
x += self.widget.winfo_rootx() + 25
|
||||
y += self.widget.winfo_rooty() + 20
|
||||
self.tw = tk.Toplevel(self.widget)
|
||||
self.tw.wm_overrideredirect(True)
|
||||
self.tw.wm_geometry(f"+{x}+{y}")
|
||||
label = tk.Label(self.tw, text=self.text, justify='left',
|
||||
background="#ffffe0", relief='solid', borderwidth=1,
|
||||
font=("Segoe UI", "9", "normal"), wraplength=350)
|
||||
label.pack(ipadx=6, ipady=4)
|
||||
|
||||
def leave(self, event=None):
|
||||
self.unschedule()
|
||||
if self.tw:
|
||||
self.tw.destroy()
|
||||
self.tw = None
|
||||
|
||||
class WowSetupTool:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.root.title("WoW Vanilla 1.12 Modernization Tool")
|
||||
|
||||
# Lock window size exactly as requested
|
||||
self.root.geometry("680x610")
|
||||
self.root.resizable(False, False)
|
||||
|
||||
icon_path = os.path.join(get_base_path(), "PurpleWowLogo.ico")
|
||||
if os.path.exists(icon_path):
|
||||
self.root.iconbitmap(icon_path)
|
||||
|
||||
# Dictionary containing all tooltip explanations
|
||||
self.descriptions = {
|
||||
# Setup & General
|
||||
"autologin": "Automates the authentication process. Bypasses the standard login screen by securely passing your credentials so you drop directly into the character selection screen.",
|
||||
"dxvk_standard": "Includes DXVK v2.6.2. Translates legacy DirectX 9 calls into modern Vulkan API, dramatically improving frame pacing on modern NVIDIA and Intel GPUs.",
|
||||
"dxvk_amd": "Uses DXVK v2.5.3, an older stable release that prevents crashing issues specific to AMD graphics cards.",
|
||||
|
||||
# Core DLLs
|
||||
"nampower.dll": "Nampower 4.2.0: Implements ping compensation. Bypasses a 1.12 client flaw to enable queueing spell casts, enabling tighter spell batching for players with network latency.",
|
||||
"no1600x1200.dll": "Removes the hardcoded 1600x1200 resolution limit. Natively unlocks widescreen and ultrawidescreen resolutions in the game settings.",
|
||||
"perf_boost.dll": "Provides dynamic render distance controls (culling). Stabilizes framerates in crowded environments by lowering the rendering priority of non-essential entities.",
|
||||
"UnitXP_SP3.dll": "Engine-level optimizations replacing legacy assembly. Introduces improved network handling, better Tab-targeting, true line-of-sight checks via Lua, and modern nameplate support.",
|
||||
"VanillaHelpers.dll": "Expands engine limits. Raises the client memory allocator from 2GB to 4GB to prevent Out-of-Memory crashes and introduces modern high-resolution texture support.",
|
||||
"SuperWoWhook.dll": "Injects a massively expanded Lua API. Increases macro limit, enables castbars on nameplates, and provides hooks for modern UI addons.",
|
||||
"transmogfix.dll": "Eliminates FPS drops caused by rapid equipment visual updates when transmogged items lose durability.",
|
||||
"weirdperformance.dll": "Engine-level optimizations: SIMD math replacements, faster data decompression (modern zlib), MPQ file caching, timer calibration, and Lua runtime GC improvements.",
|
||||
|
||||
# WeirdUtils
|
||||
"bigcursor.dll": "Upscales the hardware cursor for improved visibility on modern resolutions without losing sharpness (up to 4.0x scale via CVar).",
|
||||
"customassets.dll": "Enables loading loose game asset files from the Data/ directory mirroring internal paths without needing to repack MPQ archives.",
|
||||
"logsessions.dll": "Organizes combat and chat logs into clean, per-character, per-day files automatically upon login.",
|
||||
"minimapicons.dll": "Adds TBC/WotLK-style minimap tracking icons for NPCs and objects, combined into a new native tracking dropdown.",
|
||||
"pngscreenshots.dll": "Saves screenshots as compressed PNG files on a background thread to completely eliminate frame drops when taking pictures.",
|
||||
"worldmarkers.dll": "Place up to 5 animated colored markers (Cataclysm style) in the world for raid positioning. Syncs automatically with other users.",
|
||||
|
||||
# Tweaks Tab
|
||||
"fov": "Calculates horizontal Field of View mathematically scaled to maintain vertical aspect space based on your screen ratio.",
|
||||
"farclip": "Increases the maximum terrain render distance. Vanilla default is 777. Tweaks default is 1500.",
|
||||
"frill": "Changes the ground clutter (grass) render distance. Vanilla default is 70. Tweaks default is 300.",
|
||||
"nameplate": "Increases the distance at which enemy nameplates become visible. Vanilla default is 20. Tweaks default is 41.",
|
||||
"cam": "Increases the maximum camera zoom-out distance. Vanilla default is 50. Max safe limit is 100.",
|
||||
"sound": "Increases the maximum number of simultaneous audio channels. Values above 64 may cause crashes.",
|
||||
"loot": "Reverses the auto-loot behavior so you always auto-loot, and hold Shift for manual looting.",
|
||||
"bg_sound": "Allows game sounds to continue playing while the game is minimized or in the background.",
|
||||
"laa": "Patches the executable to be Large Address Aware, allowing the 32-bit client to utilize up to 4GB of RAM (Essential for HD Mods).",
|
||||
"cam_fix": "Fixes a bug where right-clicking and dragging to rotate the camera occasionally snaps your view in a random direction.",
|
||||
"dep_fix": "Disables Data Execution Prevention (DEP) and EmulateAtlThunks for WoW_Tweaked.exe. Prevents Windows from force-closing the game due to memory hooks. (Prompts for Admin Privileges)."
|
||||
}
|
||||
|
||||
# Basic Setup Variables
|
||||
self.wow_dir = tk.StringVar()
|
||||
self.gpu_type = tk.StringVar(value=self.detect_gpu())
|
||||
self.install_autologin = tk.BooleanVar(value=True)
|
||||
|
||||
self.screen_w = self.root.winfo_screenwidth()
|
||||
self.screen_h = self.root.winfo_screenheight()
|
||||
self.detected_ratio = self.screen_w / self.screen_h
|
||||
|
||||
self.ratio_options = {
|
||||
f"Auto-detected ({self.screen_w}x{self.screen_h})": self.detected_ratio,
|
||||
"4:3 (Standard)": 4.0/3.0,
|
||||
"16:9 (Widescreen)": 16.0/9.0,
|
||||
"16:10 (Widescreen)": 16.0/10.0,
|
||||
"21:9 (Ultrawide)": 21.0/9.0,
|
||||
"32:9 (Super Ultrawide)": 32.0/9.0
|
||||
}
|
||||
|
||||
# Core Plugins (These files must be in the base Payload/ folder)
|
||||
self.core_plugins = {
|
||||
"nampower.dll": tk.BooleanVar(value=True),
|
||||
"no1600x1200.dll": tk.BooleanVar(value=True),
|
||||
"perf_boost.dll": tk.BooleanVar(value=True),
|
||||
"SuperWoWhook.dll": tk.BooleanVar(value=True),
|
||||
"transmogfix.dll": tk.BooleanVar(value=True),
|
||||
"UnitXP_SP3.dll": tk.BooleanVar(value=True),
|
||||
"VanillaHelpers.dll": tk.BooleanVar(value=True),
|
||||
"weirdperformance.dll": tk.BooleanVar(value=True)
|
||||
}
|
||||
|
||||
# Optional WeirdUtils (These files must be in the Payload/WeirdUtils/ folder)
|
||||
self.optional_plugins = {
|
||||
"bigcursor.dll": tk.BooleanVar(value=False),
|
||||
"customassets.dll": tk.BooleanVar(value=False),
|
||||
"logsessions.dll": tk.BooleanVar(value=False),
|
||||
"minimapicons.dll": tk.BooleanVar(value=False),
|
||||
"pngscreenshots.dll": tk.BooleanVar(value=False),
|
||||
"worldmarkers.dll": tk.BooleanVar(value=False)
|
||||
}
|
||||
|
||||
self.addon_dependencies = {
|
||||
"nampower.dll": "nampowersettings",
|
||||
"perf_boost.dll": "perfboostsettings",
|
||||
"UnitXP_SP3.dll": "UnitXP_SP3_Addon",
|
||||
"SuperWoWhook.dll": "SuperAPI"
|
||||
}
|
||||
|
||||
# Vanilla Tweaks Variables
|
||||
self.vt_fov = tk.DoubleVar()
|
||||
self.ratio_var = tk.StringVar(value=list(self.ratio_options.keys())[0])
|
||||
self.vt_farclip = tk.IntVar(value=1500)
|
||||
self.vt_frill = tk.IntVar(value=300)
|
||||
self.vt_nameplate = tk.IntVar(value=41)
|
||||
self.vt_soundchan = tk.IntVar(value=64)
|
||||
self.vt_maxcam = tk.IntVar(value=100)
|
||||
|
||||
# Tweak Toggles
|
||||
self.vt_quickloot = tk.BooleanVar(value=True)
|
||||
self.vt_bg_sound = tk.BooleanVar(value=True)
|
||||
self.vt_laa = tk.BooleanVar(value=True)
|
||||
self.vt_cam_fix = tk.BooleanVar(value=True)
|
||||
self.vt_dep_fix = tk.BooleanVar(value=True)
|
||||
|
||||
# Safety Limit Toggle
|
||||
self.safety_override = tk.BooleanVar(value=False)
|
||||
self.slider_widgets =[]
|
||||
|
||||
self.on_ratio_change()
|
||||
self.build_ui()
|
||||
|
||||
def detect_gpu(self):
|
||||
try:
|
||||
output = subprocess.check_output("wmic path win32_VideoController get name", shell=True).decode()
|
||||
if "AMD" in output.upper() or "RADEON" in output.upper():
|
||||
return "AMD"
|
||||
return "Standard"
|
||||
except Exception:
|
||||
return "Standard"
|
||||
|
||||
def on_ratio_change(self, event=None):
|
||||
selection = self.ratio_var.get()
|
||||
ratio = self.ratio_options.get(selection, 4.0/3.0)
|
||||
default_ar, default_fov = 4.0 / 3.0, 1.570796
|
||||
fov = 2 * math.atan((ratio / default_ar) * math.tan(default_fov / 2))
|
||||
self.vt_fov.set(round(fov, 4))
|
||||
|
||||
def toggle_safety_limits(self):
|
||||
override = self.safety_override.get()
|
||||
for scale, safe_max, extreme_max, var in self.slider_widgets:
|
||||
if override:
|
||||
scale.configure(to=extreme_max)
|
||||
else:
|
||||
scale.configure(to=safe_max)
|
||||
try:
|
||||
if var.get() > safe_max:
|
||||
var.set(safe_max)
|
||||
except tk.TclError:
|
||||
pass
|
||||
|
||||
def create_slider_row(self, parent, row, label_text, var, min_val, safe_max, extreme_max, desc_key):
|
||||
lbl = ttk.Label(parent, text=label_text)
|
||||
lbl.grid(row=row, column=0, sticky='w', pady=5)
|
||||
ToolTip(lbl, self.descriptions[desc_key])
|
||||
|
||||
current_max = extreme_max if self.safety_override.get() else safe_max
|
||||
|
||||
scale = ttk.Scale(parent, from_=min_val, to=current_max, orient='horizontal', variable=var,
|
||||
command=lambda s, v=var: v.set(int(float(s))))
|
||||
scale.grid(row=row, column=1, sticky='ew', padx=15, pady=5)
|
||||
|
||||
ttk.Entry(parent, textvariable=var, width=8).grid(row=row, column=2, sticky='e', pady=5)
|
||||
|
||||
self.slider_widgets.append((scale, safe_max, extreme_max, var))
|
||||
|
||||
def build_ui(self):
|
||||
ttk.Label(self.root, text="💡 Hover your mouse over any setting or plugin for a detailed explanation.", foreground="#005A9E", font=("Segoe UI", 9, "bold")).pack(pady=(8, 0))
|
||||
|
||||
notebook = ttk.Notebook(self.root)
|
||||
notebook.pack(fill='both', expand=True, padx=10, pady=(5, 10))
|
||||
|
||||
tab_main = ttk.Frame(notebook)
|
||||
tab_plugins = ttk.Frame(notebook)
|
||||
tab_tweaks = ttk.Frame(notebook)
|
||||
tab_credits = ttk.Frame(notebook)
|
||||
|
||||
notebook.add(tab_main, text="Setup & DXVK")
|
||||
notebook.add(tab_plugins, text="Plugins")
|
||||
notebook.add(tab_tweaks, text="Vanilla Tweaks")
|
||||
notebook.add(tab_credits, text="Credits & Sources")
|
||||
|
||||
self.build_main_tab(tab_main)
|
||||
self.build_plugins_tab(tab_plugins)
|
||||
self.build_tweaks_tab(tab_tweaks)
|
||||
self.build_credits_tab(tab_credits)
|
||||
|
||||
ttk.Button(self.root, text="Apply Setup & Tweaks", command=self.run_installation, style="Accent.TButton").pack(pady=10, fill='x', padx=20)
|
||||
|
||||
def build_main_tab(self, parent):
|
||||
ttk.Label(parent, text="Vanilla 1.12 Installation Directory:").pack(anchor='w', pady=(10, 0), padx=10)
|
||||
dir_frame = ttk.Frame(parent)
|
||||
dir_frame.pack(fill='x', padx=10, pady=5)
|
||||
ttk.Entry(dir_frame, textvariable=self.wow_dir).pack(side='left', fill='x', expand=True)
|
||||
ttk.Button(dir_frame, text="Browse...", command=lambda: self.wow_dir.set(filedialog.askdirectory())).pack(side='left', padx=(5,0))
|
||||
|
||||
ttk.Label(parent, text="DXVK Version (GPU Detected):").pack(anchor='w', pady=(20, 0), padx=10)
|
||||
|
||||
rb_std = ttk.Radiobutton(parent, text="Standard (NVIDIA / Intel)", variable=self.gpu_type, value="Standard")
|
||||
rb_std.pack(anchor='w', padx=20, pady=2)
|
||||
ToolTip(rb_std, self.descriptions["dxvk_standard"])
|
||||
|
||||
rb_amd = ttk.Radiobutton(parent, text="AMD Specific (Fixes AMD crashes)", variable=self.gpu_type, value="AMD")
|
||||
rb_amd.pack(anchor='w', padx=20, pady=2)
|
||||
ToolTip(rb_amd, self.descriptions["dxvk_amd"])
|
||||
|
||||
ttk.Label(parent, text="Optional Mods:").pack(anchor='w', pady=(20, 0), padx=10)
|
||||
|
||||
cb_login = ttk.Checkbutton(parent, text="Install Auto Login Mod (Data/Interface/GlueXML)", variable=self.install_autologin)
|
||||
cb_login.pack(anchor='w', padx=20, pady=2)
|
||||
ToolTip(cb_login, self.descriptions["autologin"])
|
||||
|
||||
def build_plugins_tab(self, parent):
|
||||
container = ttk.Frame(parent)
|
||||
container.pack(fill='both', expand=True, padx=10, pady=10)
|
||||
|
||||
left_frame = ttk.LabelFrame(container, text="Recommended Core")
|
||||
left_frame.pack(side='left', fill='both', expand=True, padx=(0, 5))
|
||||
|
||||
ttk.Label(left_frame, text="These are highly recommended for performance\nand client stability.", font=("", 8, "italic")).pack(anchor='w', padx=10, pady=10)
|
||||
|
||||
for dll, var in self.core_plugins.items():
|
||||
cb = ttk.Checkbutton(left_frame, text=dll, variable=var)
|
||||
cb.pack(anchor='w', padx=10, pady=4)
|
||||
ToolTip(cb, self.descriptions.get(dll, ""))
|
||||
|
||||
right_frame = ttk.LabelFrame(container, text="Optional WeirdUtils")
|
||||
right_frame.pack(side='right', fill='both', expand=True, padx=(5, 0))
|
||||
|
||||
ttk.Label(right_frame, text="Additional quality-of-life adjustments.", font=("", 8, "italic")).pack(anchor='w', padx=10, pady=10)
|
||||
|
||||
for dll, var in self.optional_plugins.items():
|
||||
cb = ttk.Checkbutton(right_frame, text=dll, variable=var)
|
||||
cb.pack(anchor='w', padx=10, pady=4)
|
||||
ToolTip(cb, self.descriptions.get(dll, ""))
|
||||
|
||||
def build_tweaks_tab(self, parent):
|
||||
fov_frame = ttk.LabelFrame(parent, text="Field of View (FoV) Calculator")
|
||||
fov_frame.pack(fill='x', padx=10, pady=5)
|
||||
|
||||
ttk.Label(fov_frame, text="Screen Aspect Ratio:").grid(row=0, column=0, padx=5, pady=5, sticky='w')
|
||||
ratio_combo = ttk.Combobox(fov_frame, textvariable=self.ratio_var, values=list(self.ratio_options.keys()), state="readonly", width=28)
|
||||
ratio_combo.grid(row=0, column=1, padx=5, pady=5, sticky='w')
|
||||
ratio_combo.bind("<<ComboboxSelected>>", self.on_ratio_change)
|
||||
|
||||
fov_lbl = ttk.Label(fov_frame, text="Calculated FoV (Radians) [Safe Max: 2.268]:")
|
||||
fov_lbl.grid(row=1, column=0, padx=5, pady=5, sticky='w')
|
||||
ToolTip(fov_lbl, self.descriptions["fov"])
|
||||
|
||||
ttk.Entry(fov_frame, textvariable=self.vt_fov, width=15).grid(row=1, column=1, padx=5, pady=5, sticky='w')
|
||||
|
||||
warn_text = "* Note: If SuperWoW is active, it natively overrides FoV scaling. This setting is a backup for Vanilla Tweaks."
|
||||
ttk.Label(fov_frame, text=warn_text, font=("", 8, "italic"), wraplength=520).grid(row=2, column=0, columnspan=2, padx=5, pady=(0, 5), sticky='w')
|
||||
|
||||
style = ttk.Style()
|
||||
style.configure("Warning.TCheckbutton", foreground="red")
|
||||
ttk.Checkbutton(parent, text="Disable Safety Limits (Warning: Exceeding max limits may cause game instability)",
|
||||
variable=self.safety_override, style="Warning.TCheckbutton",
|
||||
command=self.toggle_safety_limits).pack(anchor='w', padx=15, pady=(0, 5))
|
||||
|
||||
frame_nums = ttk.Frame(parent)
|
||||
frame_nums.pack(fill='x', padx=15, pady=0)
|
||||
frame_nums.columnconfigure(1, weight=1)
|
||||
|
||||
self.create_slider_row(frame_nums, 0, "Render distance (Farclip) [Safe Max: 1500]:", self.vt_farclip, 777, 1500, 10000, "farclip")
|
||||
self.create_slider_row(frame_nums, 1, "Ground clutter (Frilldistance) [Safe Max: 300]:", self.vt_frill, 70, 300, 1000, "frill")
|
||||
self.create_slider_row(frame_nums, 2, "Nameplate range [Safe Max: 41]:", self.vt_nameplate, 20, 41, 150, "nameplate")
|
||||
self.create_slider_row(frame_nums, 3, "Camera distance [Safe Max: 100]:", self.vt_maxcam, 50, 100, 250, "cam")
|
||||
self.create_slider_row(frame_nums, 4, "Sound Channels [Safe Max: 64]:", self.vt_soundchan, 12, 64, 128, "sound")
|
||||
|
||||
ttk.Label(parent, text="Patch Toggles:").pack(anchor='w', pady=(5,0), padx=10)
|
||||
|
||||
toggles_frame = ttk.Frame(parent)
|
||||
toggles_frame.pack(fill='x', padx=10)
|
||||
|
||||
cb_loot = ttk.Checkbutton(toggles_frame, text="Always auto-loot", variable=self.vt_quickloot)
|
||||
cb_loot.grid(row=0, column=0, sticky='w', padx=10, pady=2)
|
||||
ToolTip(cb_loot, self.descriptions["loot"])
|
||||
|
||||
cb_bg = ttk.Checkbutton(toggles_frame, text="Background sounds", variable=self.vt_bg_sound)
|
||||
cb_bg.grid(row=0, column=1, sticky='w', padx=10, pady=2)
|
||||
ToolTip(cb_bg, self.descriptions["bg_sound"])
|
||||
|
||||
cb_laa = ttk.Checkbutton(toggles_frame, text="Large Address Aware (LAA)", variable=self.vt_laa)
|
||||
cb_laa.grid(row=1, column=0, sticky='w', padx=10, pady=2)
|
||||
ToolTip(cb_laa, self.descriptions["laa"])
|
||||
|
||||
cb_cam = ttk.Checkbutton(toggles_frame, text="Fix Camera Skip Glitch", variable=self.vt_cam_fix)
|
||||
cb_cam.grid(row=1, column=1, sticky='w', padx=10, pady=2)
|
||||
ToolTip(cb_cam, self.descriptions["cam_fix"])
|
||||
|
||||
cb_dep = ttk.Checkbutton(toggles_frame, text="Disable DEP (Requires Admin)", variable=self.vt_dep_fix)
|
||||
cb_dep.grid(row=2, column=0, sticky='w', padx=10, pady=2)
|
||||
ToolTip(cb_dep, self.descriptions["dep_fix"])
|
||||
|
||||
|
||||
def build_credits_tab(self, parent):
|
||||
# Create a frame with a scrollbar
|
||||
frame = ttk.Frame(parent)
|
||||
frame.pack(fill='both', expand=True, padx=10, pady=10)
|
||||
|
||||
scrollbar = ttk.Scrollbar(frame)
|
||||
scrollbar.pack(side='right', fill='y')
|
||||
|
||||
bg_color = self.root.cget('bg')
|
||||
text_area = tk.Text(frame, wrap='word', yscrollcommand=scrollbar.set, bg=bg_color, relief='flat', font=("Segoe UI", 9))
|
||||
text_area.pack(side='left', fill='both', expand=True)
|
||||
scrollbar.config(command=text_area.yview)
|
||||
|
||||
# Configure Text Tags for formatting
|
||||
text_area.tag_configure("header", font=("Segoe UI", 10, "bold"), spacing1=10, spacing3=5, foreground="#333333")
|
||||
text_area.tag_configure("bold", font=("Segoe UI", 9, "bold"))
|
||||
|
||||
# Helper function to create clickable links
|
||||
def insert_link(text, url):
|
||||
tag_name = f"link_{url}"
|
||||
text_area.tag_configure(tag_name, foreground="#005A9E", underline=True)
|
||||
text_area.tag_bind(tag_name, "<Button-1>", lambda e, u=url: webbrowser.open_new(u))
|
||||
text_area.tag_bind(tag_name, "<Enter>", lambda e: text_area.config(cursor="hand2"))
|
||||
text_area.tag_bind(tag_name, "<Leave>", lambda e: text_area.config(cursor="arrow"))
|
||||
text_area.insert("end", text, tag_name)
|
||||
|
||||
# Content Insertion
|
||||
text_area.insert("end", "This modernization tool packages the incredible work of several open-source developers. If you wish to manually compile these tools or learn more about how they work, please visit their respective repositories below.\n", "")
|
||||
|
||||
# Core Engine & Performance Modifications
|
||||
text_area.insert("end", "\nCore Engine & Performance Modifications\n", "header")
|
||||
text_area.insert("end", "• VanillaFixes: ", "bold")
|
||||
insert_link("Source Repository", "https://github.com/hannesmann/vanillafixes")
|
||||
text_area.insert("end", "\n• VanillaHelpers: ", "bold")
|
||||
insert_link("Source Repository", "https://github.com/isfir/VanillaHelpers")
|
||||
text_area.insert("end", "\n• PerfBoost: ", "bold")
|
||||
insert_link("Mod Source", "https://gitea.com/avitasia/perf_boost")
|
||||
text_area.insert("end", " | ")
|
||||
insert_link("Addon Source", "https://gitea.com/avitasia/PerfBoostSettings")
|
||||
text_area.insert("end", "\n• UnitXP_SP3: ", "bold")
|
||||
insert_link("Mod Source", "https://codeberg.org/konaka/UnitXP_SP3")
|
||||
text_area.insert("end", " | ")
|
||||
insert_link("Addon Source", "https://codeberg.org/konaka/UnitXP_SP3_Addon")
|
||||
text_area.insert("end", "\n• no1600x1200: ", "bold")
|
||||
text_area.insert("end", "Legacy File\n")
|
||||
|
||||
# Gameplay & Interface Enhancements
|
||||
text_area.insert("end", "\nGameplay & Interface Enhancements\n", "header")
|
||||
text_area.insert("end", "• Nampower v4.2.0: ", "bold")
|
||||
insert_link("Mod Source", "https://gitea.com/avitasia/nampower")
|
||||
text_area.insert("end", " | ")
|
||||
insert_link("Addon Source", "https://gitea.com/avitasia/NampowerSettings")
|
||||
text_area.insert("end", "\n• SuperWoW: ", "bold")
|
||||
insert_link("Mod Source", "https://github.com/balakethelock/SuperWoW")
|
||||
text_area.insert("end", " | ")
|
||||
insert_link("Addon Source", "https://github.com/balakethelock/SuperAPI")
|
||||
text_area.insert("end", "\n• Vanilla-Autologin: ", "bold")
|
||||
insert_link("Source Repository", "https://github.com/MarcelineVQ/turtle-autologin")
|
||||
|
||||
# WeirdUtils Module Breakdown
|
||||
text_area.insert("end", "\n\nWeirdUtils Suite\n", "header")
|
||||
text_area.insert("end", "A modular suite of advanced quality-of-life scripts handling World Markers, Minimap Icons, PNG Screenshots, Transmog Fixing, and deep-engine optimizations. Due to the deeply rooted memory hooks used in these files, they are distributed as pre-compiled, closed-source binaries to prevent trivial exploitation by bad actors.\n\n")
|
||||
text_area.insert("end", "• WeirdUtils Documentation & Releases: ", "bold")
|
||||
insert_link("Source Repository", "https://codeberg.org/MarcelineVQ/WeirdUtils")
|
||||
text_area.insert("end", "\n")
|
||||
|
||||
# Lock text area to prevent the user from typing in it
|
||||
text_area.config(state='disabled')
|
||||
|
||||
|
||||
def validate_installation_dir(self, target_dir):
|
||||
"""Checks if the directory exists and contains necessary WoW components."""
|
||||
if not target_dir:
|
||||
messagebox.showerror("Directory Error", "Please select a Vanilla 1.12 installation directory.")
|
||||
return False
|
||||
|
||||
if not os.path.exists(os.path.join(target_dir, "WoW.exe")) or \
|
||||
not os.path.isdir(os.path.join(target_dir, "Data")) or \
|
||||
not os.path.isdir(os.path.join(target_dir, "Interface")):
|
||||
|
||||
msg = ("This does not look like a valid Vanilla 1.12 installation directory.\n\n"
|
||||
"Please make sure you are selecting the directory that has the WoW.exe in it.")
|
||||
messagebox.showerror("Invalid Directory", msg)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def validate_limits(self):
|
||||
if self.safety_override.get():
|
||||
return True
|
||||
|
||||
try:
|
||||
if self.vt_farclip.get() > 1500:
|
||||
messagebox.showerror("Limit Exceeded", "Render distance (Farclip) exceeds the safe limit of 1500.\n\nPlease lower it, or check 'Disable Safety Limits' to bypass.")
|
||||
return False
|
||||
if self.vt_frill.get() > 300:
|
||||
messagebox.showerror("Limit Exceeded", "Ground clutter (Frilldistance) exceeds the safe limit of 300.\n\nPlease lower it, or check 'Disable Safety Limits' to bypass.")
|
||||
return False
|
||||
if self.vt_nameplate.get() > 41:
|
||||
messagebox.showerror("Limit Exceeded", "Nameplate range exceeds the safe limit of 41.\n\nPlease lower it, or check 'Disable Safety Limits' to bypass.")
|
||||
return False
|
||||
if self.vt_maxcam.get() > 100:
|
||||
messagebox.showerror("Limit Exceeded", "Camera distance exceeds the safe limit of 100.\n\nPlease lower it, or check 'Disable Safety Limits' to bypass.")
|
||||
return False
|
||||
if self.vt_soundchan.get() > 64:
|
||||
messagebox.showerror("Limit Exceeded", "Sound Channels exceed the safe limit of 64.\n\nPlease lower it, or check 'Disable Safety Limits' to bypass.")
|
||||
return False
|
||||
if self.vt_fov.get() > 2.2689:
|
||||
messagebox.showerror("Limit Exceeded", "Field of View exceeds the safe limit of 130 degrees (2.2689 radians).\n\nPlease lower it, or check 'Disable Safety Limits' to bypass.")
|
||||
return False
|
||||
except tk.TclError:
|
||||
messagebox.showerror("Input Error", "Please ensure all Tweak fields contain valid numbers.")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def clean_unselected_files(self, target):
|
||||
"""Removes managed files/folders if they were explicitly unselected by the user."""
|
||||
|
||||
# 1. Clean AutoLogin files if unselected
|
||||
if not self.install_autologin.get():
|
||||
glue_dir = os.path.join(target, "Data", "Interface", "GlueXML")
|
||||
for file_name in ["AutoLogin.lua", "AutoLogin.xml", "GlueXML.toc"]:
|
||||
file_path = os.path.join(glue_dir, file_name)
|
||||
if os.path.exists(file_path):
|
||||
try: os.remove(file_path)
|
||||
except: pass
|
||||
if os.path.exists(glue_dir) and not os.listdir(glue_dir):
|
||||
try: os.rmdir(glue_dir)
|
||||
except: pass
|
||||
|
||||
# 2. Clean unselected Core Plugins and their dependent AddOns
|
||||
for dll_name, var in self.core_plugins.items():
|
||||
if not var.get():
|
||||
dll_path = os.path.join(target, dll_name)
|
||||
if os.path.exists(dll_path):
|
||||
try: os.remove(dll_path)
|
||||
except: pass
|
||||
|
||||
addon_folder = self.addon_dependencies.get(dll_name)
|
||||
if addon_folder:
|
||||
addon_path = os.path.join(target, "Interface", "AddOns", addon_folder)
|
||||
if os.path.exists(addon_path):
|
||||
shutil.rmtree(addon_path, ignore_errors=True)
|
||||
|
||||
# 3. Clean unselected Optional WeirdUtils Plugins (Now checks the root directory)
|
||||
for dll_name, var in self.optional_plugins.items():
|
||||
if not var.get():
|
||||
dll_path = os.path.join(target, dll_name)
|
||||
if os.path.exists(dll_path):
|
||||
try: os.remove(dll_path)
|
||||
except: pass
|
||||
|
||||
def apply_process_mitigations(self):
|
||||
"""Runs the Set-ProcessMitigation PowerShell command with a UAC prompt if needed."""
|
||||
if not self.vt_dep_fix.get():
|
||||
return
|
||||
|
||||
ps_cmd = "Set-ProcessMitigation -Name WoW_Tweaked.exe -Disable DEP, EmulateAtlThunks"
|
||||
|
||||
# Start-Process with '-Verb RunAs' triggers the Windows Administrator UAC prompt automatically
|
||||
full_cmd = f"Start-Process powershell -WindowStyle Hidden -Verb RunAs -ArgumentList \"-Command {ps_cmd}\""
|
||||
|
||||
try:
|
||||
# Execute the elevation request
|
||||
subprocess.run(["powershell", "-Command", full_cmd], creationflags=0x08000000)
|
||||
except Exception:
|
||||
pass # Fail silently if the user clicks "No" on the Administrator prompt
|
||||
|
||||
def run_installation(self):
|
||||
target_dir = self.wow_dir.get()
|
||||
|
||||
# 1. Validate Directory
|
||||
if not self.validate_installation_dir(target_dir):
|
||||
return
|
||||
|
||||
# 2. Validate Bounds
|
||||
if not self.validate_limits():
|
||||
return
|
||||
|
||||
try:
|
||||
self.clean_unselected_files(target_dir)
|
||||
self.copy_base_files(target_dir)
|
||||
self.configure_dxvk(target_dir)
|
||||
self.configure_plugins(target_dir)
|
||||
self.run_vanilla_tweaks(target_dir)
|
||||
self.apply_process_mitigations()
|
||||
|
||||
# Generate the seamless launcher shortcut
|
||||
self.create_launcher_shortcut(target_dir)
|
||||
|
||||
messagebox.showinfo("Success", "Installation and patching complete!\n\nUse the new 'Play Modernized WoW' shortcut in your directory to launch the game.")
|
||||
except Exception as e:
|
||||
messagebox.showerror("Installation Error", str(e))
|
||||
|
||||
def create_launcher_shortcut(self, target_dir):
|
||||
"""Automates creating the VanillaFixes shortcut targeting WoW_Tweaked.exe"""
|
||||
shortcut_path = os.path.join(target_dir, "Play Modernized WoW.lnk")
|
||||
vanilla_fixes_exe = os.path.join(target_dir, "VanillaFixes.exe")
|
||||
|
||||
# Check for the icon in the setup tool's root directory
|
||||
source_icon = os.path.join(get_base_path(), "PurpleWowLogo.ico")
|
||||
target_icon = os.path.join(target_dir, "PurpleWowLogo.ico")
|
||||
icon_vbs_line = ""
|
||||
|
||||
# If the icon exists, copy it to the WoW folder and add it to the shortcut
|
||||
if os.path.exists(source_icon):
|
||||
try:
|
||||
shutil.copy2(source_icon, target_icon)
|
||||
icon_vbs_line = f'oLink.IconLocation = "{target_icon}, 0"'
|
||||
except Exception:
|
||||
pass # Fail silently on icon copy if there's a permission issue
|
||||
|
||||
vbs_script = f"""
|
||||
Set oWS = WScript.CreateObject("WScript.Shell")
|
||||
sLinkFile = "{shortcut_path}"
|
||||
Set oLink = oWS.CreateShortcut(sLinkFile)
|
||||
oLink.TargetPath = "{vanilla_fixes_exe}"
|
||||
oLink.Arguments = "WoW_Tweaked.exe"
|
||||
oLink.WorkingDirectory = "{target_dir}"
|
||||
oLink.Description = "Launch Vanilla WoW with VanillaFixes and Tweaks"
|
||||
{icon_vbs_line}
|
||||
oLink.Save
|
||||
"""
|
||||
vbs_path = os.path.join(target_dir, "create_shortcut.vbs")
|
||||
with open(vbs_path, "w") as f:
|
||||
f.write(vbs_script)
|
||||
|
||||
try:
|
||||
subprocess.run(["cscript", "//nologo", vbs_path], creationflags=0x08000000)
|
||||
finally:
|
||||
if os.path.exists(vbs_path):
|
||||
os.remove(vbs_path)
|
||||
|
||||
def copy_base_files(self, target):
|
||||
payload_dir = os.path.join(get_base_path(), "Payload")
|
||||
if not os.path.exists(payload_dir): return
|
||||
|
||||
if self.install_autologin.get() and os.path.exists(os.path.join(payload_dir, "Data")):
|
||||
shutil.copytree(os.path.join(payload_dir, "Data"), os.path.join(target, "Data"), dirs_exist_ok=True)
|
||||
|
||||
if os.path.exists(os.path.join(payload_dir, "Interface")):
|
||||
shutil.copytree(os.path.join(payload_dir, "Interface"), os.path.join(target, "Interface"), dirs_exist_ok=True)
|
||||
|
||||
# Removed SuperWoWhook.dll from this list!
|
||||
for file in ["VanillaFixes.exe", "VfPatcher.dll", "dxvk.conf"]:
|
||||
source_file = os.path.join(payload_dir, file)
|
||||
if os.path.exists(source_file): shutil.copy2(source_file, target)
|
||||
|
||||
def configure_dxvk(self, target):
|
||||
payload_dir = os.path.join(get_base_path(), "Payload")
|
||||
dxvk_folder = "DXVK_AMD" if self.gpu_type.get() == "AMD" else "DXVK_Standard"
|
||||
d3d9_src = os.path.join(payload_dir, dxvk_folder, "d3d9.dll")
|
||||
if os.path.exists(d3d9_src): shutil.copy2(d3d9_src, target)
|
||||
|
||||
def configure_plugins(self, target):
|
||||
payload_base = os.path.join(get_base_path(), "Payload")
|
||||
payload_weirdu = os.path.join(payload_base, "WeirdUtils")
|
||||
|
||||
# Start the list with dxvk to ensure DXVK is always loaded first
|
||||
dlls_text_lines = ["dxvk"]
|
||||
|
||||
# Process Core Plugins
|
||||
for dll_name, var in self.core_plugins.items():
|
||||
if var.get():
|
||||
source_dll = os.path.join(payload_base, dll_name)
|
||||
if os.path.exists(source_dll):
|
||||
shutil.copy2(source_dll, target)
|
||||
dlls_text_lines.append(dll_name)
|
||||
|
||||
# Clean up corresponding addons if their core DLL was UNCHECKED
|
||||
for dll_name, addon_folder in self.addon_dependencies.items():
|
||||
if not self.core_plugins.get(dll_name, tk.BooleanVar(value=True)).get():
|
||||
addon_path = os.path.join(target, "Interface", "AddOns", addon_folder)
|
||||
if os.path.exists(addon_path):
|
||||
shutil.rmtree(addon_path, ignore_errors=True)
|
||||
|
||||
# Process Optional Plugins (WeirdUtils) - Now installed to ROOT folder
|
||||
for dll_name, var in self.optional_plugins.items():
|
||||
if var.get():
|
||||
source_dll = os.path.join(payload_weirdu, dll_name)
|
||||
if os.path.exists(source_dll):
|
||||
shutil.copy2(source_dll, target)
|
||||
dlls_text_lines.append(dll_name)
|
||||
|
||||
# Write active dlls to dlls.txt
|
||||
with open(os.path.join(target, "dlls.txt"), "w") as f:
|
||||
f.write("\n".join(dlls_text_lines))
|
||||
|
||||
def run_vanilla_tweaks(self, target):
|
||||
wow_exe = os.path.join(target, "WoW.exe")
|
||||
tweaks_exe = os.path.join(get_base_path(), "vanilla-tweaks.exe")
|
||||
|
||||
if not os.path.exists(tweaks_exe):
|
||||
raise FileNotFoundError("vanilla-tweaks.exe not found alongside this setup tool.")
|
||||
|
||||
args = [tweaks_exe]
|
||||
|
||||
# --- SMART BYPASS LOGIC ---
|
||||
# If a slider is set to the base Vanilla value, explicitly pass the --no flag.
|
||||
# This prevents Vanilla Tweaks from injecting unnecessary code caves that corrupt WMO rendering.
|
||||
|
||||
# Field of View (Vanilla: ~1.5708)
|
||||
if abs(self.vt_fov.get() - 1.5708) < 0.0001:
|
||||
args.append("--no-fov")
|
||||
else:
|
||||
args.extend(["--fov", str(self.vt_fov.get())])
|
||||
|
||||
# Farclip (Vanilla: 777)
|
||||
if self.vt_farclip.get() == 777:
|
||||
args.append("--no-farclip")
|
||||
else:
|
||||
args.extend(["--farclip", str(self.vt_farclip.get())])
|
||||
|
||||
# Frilldistance (Vanilla: 70)
|
||||
if self.vt_frill.get() == 70:
|
||||
args.append("--no-frilldistance")
|
||||
else:
|
||||
args.extend(["--frilldistance", str(self.vt_frill.get())])
|
||||
|
||||
# Nameplate Distance (Vanilla: 20)
|
||||
if self.vt_nameplate.get() == 20:
|
||||
args.append("--no-nameplatedistance")
|
||||
else:
|
||||
args.extend(["--nameplatedistance", str(self.vt_nameplate.get())])
|
||||
|
||||
# Sound Channels (Vanilla: 12)
|
||||
if self.vt_soundchan.get() == 12:
|
||||
args.append("--no-soundchannels")
|
||||
else:
|
||||
args.extend(["--soundchannels", str(self.vt_soundchan.get())])
|
||||
|
||||
# Max Camera Distance (Vanilla: 50 - Unchanged by default in Tweaks)
|
||||
if self.vt_maxcam.get() != 50:
|
||||
args.extend(["--maxcameradistance", str(self.vt_maxcam.get())])
|
||||
|
||||
# --- BOOLEAN TOGGLES ---
|
||||
if not self.vt_quickloot.get(): args.append("--no-quickloot")
|
||||
if not self.vt_bg_sound.get(): args.append("--no-sound-in-background")
|
||||
if not self.vt_laa.get(): args.append("--no-largeaddressaware")
|
||||
if not self.vt_cam_fix.get(): args.append("--no-cameraskipfix")
|
||||
|
||||
args.extend(["-o", os.path.join(target, "WoW_Tweaked.exe")])
|
||||
args.append(wow_exe)
|
||||
subprocess.run(args, check=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
root = tk.Tk()
|
||||
app = WowSetupTool(root)
|
||||
root.mainloop()
|
||||
Binary file not shown.
Reference in New Issue
Block a user