You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

378 lines
12 KiB

-- VgerCore by Vger-Azjol-Nerub
-- www.vgermods.com
-- © 2006-2021 Travis Spomer. This mod is released under the Creative Commons Attribution-NonCommercial-NoDerivs 3.0 license.
--
-- Version 1.0.14 -- IndexOf
local VgerCoreThisVersion = 1.14
--
-- VgerCore contains functionality that is shared by Vger's mods.
-- It can be used as a standalone add-on, or embedded within other mods.
--
------------------------------------------------------------
local InitializeOrUpgrade = (not VgerCore) or (not VgerCore.Version) or (VgerCore.Version < VgerCoreThisVersion)
-- If the currently loaded version of VgerCore isn't as good as this one, load the new one.
if InitializeOrUpgrade then
VgerCore = {}
VgerCore.Version = VgerCoreThisVersion
-- What version is this?
VgerCore.IsClassic = (WOW_PROJECT_ID == WOW_PROJECT_CLASSIC)
VgerCore.IsBurningCrusade = (WOW_PROJECT_ID == WOW_PROJECT_BURNING_CRUSADE_CLASSIC) -- includes pre-patch
VgerCore.IsShadowlands = (select(4, GetBuildInfo()) >= 90000)
VgerCore.DeathKnightsExist = VgerCore.IsShadowlands
VgerCore.MonksExist = VgerCore.IsShadowlands
VgerCore.DemonHuntersExist = VgerCore.IsShadowlands
VgerCore.SpecsExist = VgerCore.IsShadowlands
VgerCore.RangedSlotExists = VgerCore.IsClassic or VgerCore.IsBurningCrusade
VgerCore.ArtifactsExist = VgerCore.IsShadowlands
VgerCore.EquipmentSetsExist = VgerCore.IsShadowlands
-- Common colors
VgerCore.Color = {}
VgerCore.Color.Reset = "|r"
VgerCore.Color.Blue = "|cff8ec3e6"
VgerCore.Color.BlueR = 142 / 255
VgerCore.Color.BlueG = 195 / 255
VgerCore.Color.BlueB = 230 / 255
VgerCore.Color.DarkBlue = "|cff6a92ac"
VgerCore.Color.DarkBlueR = 106 / 255
VgerCore.Color.DarkBlueG = 146 / 255
VgerCore.Color.DarkBlueB = 172 / 255
VgerCore.Color.Green = "|cffb4fe2c"
VgerCore.Color.GreenR = 180 / 255
VgerCore.Color.GreenG = 255 / 255
VgerCore.Color.GreenB = 44 / 255
VgerCore.Color.Orange = "|cfffecf38"
VgerCore.Color.OrangeR = 255 / 255
VgerCore.Color.OrangeG = 207 / 255
VgerCore.Color.OrangeB = 56 / 255
VgerCore.Color.Lemon = "|cfffffdd0"
VgerCore.Color.LemonR = 255 / 255
VgerCore.Color.LemonG = 253 / 255
VgerCore.Color.LemonB = 208 / 255
VgerCore.Color.Salmon = "|cfffe8460"
VgerCore.Color.SalmonR = 255 / 255
VgerCore.Color.SalmonG = 132 / 255
VgerCore.Color.SalmonB = 96 / 255
VgerCore.Color.Beige = "|cffe0dec8"
VgerCore.Color.BeigeR = 224 / 255
VgerCore.Color.BeigeG = 222 / 255
VgerCore.Color.BeigeB = 200 / 255
VgerCore.Color.White = "|cffffffff"
VgerCore.Color.WhiteR = 255 / 255
VgerCore.Color.WhiteG = 255 / 255
VgerCore.Color.WhiteB = 255 / 255
VgerCore.Color.Grey = "|cff909090"
VgerCore.Color.GreyR = 144 / 255
VgerCore.Color.GreyG = 144 / 255
VgerCore.Color.GreyB = 144 / 255
VgerCore.Color.Silver = "|cffc0c0c0"
VgerCore.Color.SilverR = 192 / 255
VgerCore.Color.SilverG = 192 / 255
VgerCore.Color.SilverB = 192 / 255
VgerCore.Color.Black= "|cff000000"
VgerCore.Color.BlackR = 0 / 255
VgerCore.Color.BlackG = 0 / 255
VgerCore.Color.BlackB = 0 / 255
VgerCore.MoneyColor = {}
VgerCore.MoneyColor.Gold = "|cffecda90"
VgerCore.MoneyColor.Silver = "|cffd7d5d8"
VgerCore.MoneyColor.Copper = "|cffe2ad8e"
-- Displays a standard VgerCore message.
function VgerCore.Message(Text)
if DEFAULT_CHAT_FRAME then
DEFAULT_CHAT_FRAME:AddMessage(VgerCore.Color.Orange .. tostring(Text))
else
message(VgerCore.Color.Orange .. tostring(Text))
end
end
-- Displays a bunch of messages from one string, separated by newlines.
-- Notes:
-- * Colors specified at the beginning of Text will not propagate to subsequent lines of Text.
-- Use the optional Color parameter instead.
-- * Empty lines will be skipped. Add a space to the line if you want it to be printed.
function VgerCore.MultilineMessage(Text, Color)
local Line
local ColorString = Color
if not ColorString then ColorString = "" end
for Line in string.gmatch(Text, "[^\r\n]+") do
VgerCore.Message(ColorString .. Line)
end
end
-- Displays a large VgerCore message.
function VgerCore.BigMessage(Text)
if UIErrorsFrame then
UIErrorsFrame:AddMessage(tostring(Text), VgerCore.Color.GreenR, VgerCore.Color.GreenG, VgerCore.Color.GreenB, 1.0, 4.0)
end
if DEFAULT_CHAT_FRAME then
DEFAULT_CHAT_FRAME:AddMessage(VgerCore.Color.Green .. tostring(Text))
end
end
-- Displays a VgerCore error message if the condition is false.
function VgerCore.Assert(Condition, Message)
-- Possibility: call the assert() function to get a callstack and integrate with mods such as Swatter.
if not Condition then VgerCore.Fail(Message) end
end
-- Displays a VgerCore error message.
function VgerCore.Fail(Message)
VgerCore.Message(VgerCore.Color.Salmon .. "ERROR: " .. VgerCore.Color.White .. tostring(Message))
end
-- Hooks an insecure function. Similar to the base WoW API's hooksecurefunc. The hook function will be run
-- after the original function to be hooked, unless Pre is true, in which case the hook will be run first.
-- Valid usage:
-- VgerCore.HookInsecureFunction(Object, FunctionName, Hook, Pre)
-- VgerCore.HookInsecureFunction(FunctionName, Hook, Pre)
function VgerCore.HookInsecureFunction(arg1, arg2, arg3, arg4)
local TypeOfObject = type(arg1)
local OldFunction
if TypeOfObject == "table" then -- Object, FunctionName, Hook, Pre
OldFunction = arg1[arg2]
if OldFunction then
arg1[arg2] = VgerCore.CreateHookFunction(OldFunction, arg3, arg4)
else
VgerCore.Fail("VgerCore.HookInsecureFunction: could not find member function '" .. arg2 .. "'.")
end
elseif TypeOfObject == "string" then -- FunctionName, Hook, Pre
OldFunction = getglobal(arg1)
if OldFunction then
local Environment = getfenv()
Environment[arg1] = VgerCore.CreateHookFunction(OldFunction, arg2, arg3)
else
VgerCore.Fail("VgerCore.HookInsecureFunction: could not find function '" .. arg1 .. "'.")
end
else
VgerCore.Fail("VgerCore.HookInsecureFunction argument 1 must be table or string, not " .. TypeOfObject .. ".")
end
end
-- Hooks an insecure script handler. Works just like HookInsecureFunction(Object, FunctionName, Hook, Pre), except that
-- instead of a function name, a script name is passed.
function VgerCore.HookInsecureScript(Object, ScriptName, Hook, Pre)
local OldFunction = Object:GetScript(ScriptName)
if OldFunction then
Object:SetScript(ScriptName, VgerCore.CreateHookFunction(OldFunction, Hook, Pre))
else
Object:SetScript(ScriptName, Hook)
end
end
-- Internal function used by HookInsecureFunction.
function VgerCore.CreateHookFunction(OldFunction, Hook, Pre)
if Pre then
return function(...)
Hook(...)
return OldFunction(...)
end
else
return function(...)
local ReturnValue = OldFunction(...)
Hook(...)
return ReturnValue
end
end
end
-- Executes a chat command just as if it were typed in the chat window.
-- Returns true if successful, or false if not (primarily if the command is a secure function, such as /cast).
function VgerCore.ExecuteChatCommand(Command)
local EditBox = DEFAULT_CHAT_FRAME.editBox
if not EditBox then return false end
-- First, make sure that this command is okay.
local _, _, SlashCommand = strfind(Command, "^(/%w+) ")
if SlashCommand then
if IsSecureCmd(SlashCommand) then
VgerCore.Fail(SlashCommand .. " is a secure command and cannot be run automatically.")
return false
end
end
-- Now, execute the chat command.
local PreviousText = EditBox:GetText()
EditBox:SetText(Command)
ChatEdit_SendText(EditBox)
EditBox:SetText(PreviousText)
return true
end
-- Runs a macro.
-- Returns true if successful, or false if not.
function VgerCore.RunMacro(MacroName)
-- First, get the text of the macro.
local _, _, Script, _ = GetMacroInfo(MacroName)
if not Script then return false end
-- Then, execute each line individually. Ignore comments marked with # or -.
local Line
for Line in string.gmatch(Script, "[^\n]+") do
local FirstChar = strsub(Line, 1, 1)
if FirstChar ~= "#" and FirstChar ~= "-" then
VgerCore.ExecuteChatCommand(Line)
end
end
return true
end
-- Returns true if the user is in a Battleground, or false if not.
function VgerCore.IsInBattleground()
local Battleground
for Battleground = 1, GetMaxBattlefieldID() do
if GetBattlefieldStatus(Battleground) == "active" then return true end
end
return false
end
-- Comparer function for use in table.sort that sorts strings alphabetically, ignoring case.
function VgerCore.CaseInsensitiveComparer(a, b)
return strlower(a) < strlower(b)
end
-- Returns a six-digit hex string for three RGB values 0-1.
function VgerCore.RGBToHex(r, g, b)
r = r <= 1 and r >= 0 and r or 0
g = g <= 1 and g >= 0 and g or 0
b = b <= 1 and b >= 0 and b or 0
return format("%02x%02x%02x", r * 255, g * 255, b * 255)
end
-- Returns RGB values 0-1 for a six-digit hex string, or nil if unsuccessful.
function VgerCore.HexToRGB(hex)
if not hex or strlen(hex) ~= 6 then return end
local r, g, b = strsub(hex, 1, 2), strsub(hex, 3, 4), strsub(hex, 5, 6)
r, g, b = r or 0, g or 0, b or 0
return tonumber(r, 16) / 255, tonumber(g, 16) / 255, tonumber(b, 16) / 255
end
-- Same as strfind, but finds the last occurrence of a substring. The substring to find must be
-- no more than two characters.
function VgerCore.StringFindReverse(str, find)
local FindLen = strlen(find)
VgerCore.Assert(FindLen == 1 or FindLen == 2, "The substring to find must be one or two characters in length.")
local FindByte, FindByte2 = strbyte(find, 1, 2)
local StringLength = strlen(str)
local i
for i = StringLength, 1, -1 do
if strbyte(str, i) == FindByte and (FindByte2 == nil or strbyte(str, i + 1) == FindByte2) then return i end
end
return nil
end
-- Returns a string representation of a number to a maximum of one decimal place. If the number passed is nil, nil is returned.
function VgerCore.FormatShortDecimal(Number)
if Number == nil then
return nil
elseif Number >= 1000 then
return BreakUpLargeNumbers(floor(Number + .5))
elseif abs(Number - floor(Number)) < .0001 then
return tostring(Number)
else
return format("%.1f", Number)
end
end
-- Returns a string representation of a number to a maximum of one decimal place. If the number is long, it is shortened using
-- an abbreviation like "12.3k" so that it fits in a small space. If the number passed is nil, nil is returned.
function VgerCore.FormatCompactDecimal(Number)
if Number == nil then
return nil
elseif Number >= 1000000000 then
return format("%.1fb", Number / 1000000000)
elseif Number >= 1000000 then
return format("%.1fm", Number / 1000000)
elseif Number >= 10000 then
return format("%.1fk", Number / 1000)
elseif Number >= 1000 then
return BreakUpLargeNumbers(floor(Number + .5))
elseif abs(Number - floor(Number)) < .0001 then
return tostring(Number)
else
return format("%.1f", Number)
end
end
-- Returns a string representation of a number with no decimal places. If the number passed is nil, nil is returned.
function VgerCore.FormatInteger(Number)
if Number == nil then
return nil
else
return BreakUpLargeNumbers(floor(Number + .5))
end
end
function VgerCore.ThrottleDelayCore(Duration, Immediate, Func, arg1)
if arg1 ~= nil then
VgerCore.Fail("Throttle and Delay don't support functions with arguments!")
end
local ThrottleUntil, WasThrottled
local TimerCallback = function()
Func()
ThrottleUntil = nil
WasThrottled = nil
end
local Throttled = function()
if (not Immediate) or (ThrottleUntil ~= nil and GetTime() < ThrottleUntil) then
-- We were called too soon.
-- Or, if Immediate is true, then delay the first call too.
if not WasThrottled then
if ThrottleUntil == nil then ThrottleUntil = GetTime() + Duration end
C_Timer.After(Duration, TimerCallback)
WasThrottled = true
end
else
-- It's been long enough, or this is the first call of the sequence.
ThrottleUntil = GetTime() + Duration
Func()
end
end
return Throttled
end
-- Returns a version of Function that is called no more often than Duration (in sec).
function VgerCore.Throttle(Duration, Func, arg1)
return VgerCore.ThrottleDelayCore(Duration, true, Func, arg1)
end
-- Returns a version of Function that delays its own execution by Duration (in sec), and also
-- prevents execution more than once in the same duration.
function VgerCore.Delay(Duration, Func, arg1)
return VgerCore.ThrottleDelayCore(Duration, false, Func, arg1)
end
-- Returns the index of an item in a table, or nil if it's not in the table.
-- (Keep in mind that this requires iteration!)
function VgerCore.IndexOf(Table, Item)
if not Item then
VgerCore.Fail("Can't find the index of nothing.")
end
local ThisIndex, ThisItem
for ThisIndex, ThisItem in pairs(Table) do
if ThisItem == Item then return ThisIndex end
end
end
------------------------------------------------------------
end -- if InitializeOrUpgrade