Initial commit

This commit is contained in:
2025-01-04 13:47:04 +01:00
commit 19a7052f75
8 changed files with 471 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
## Interface: 40400
## Title: ActionBarSaver: Reloaded
## Version: 1.0.7
## Notes: Manage, save, and restore action bar profiles
## Author: Voodoomoose
## SavedVariables: ActionBarSaverReloaded
Constants.lua
StringHelpers.lua
TableHelpers.lua
Main.lua
Actions.lua

277
Actions.lua Normal file
View File

@@ -0,0 +1,277 @@
local ADDON_LOADED, shared = ...
local function PickupEquipmentSet(setName)
local setIndex = 0
for i=1,C_EquipmentSet.GetNumEquipmentSets() do
local sn = C_EquipmentSet.GetEquipmentSetInfo(i)
if sn == setName then
setIndex = i
end
end
C_EquipmentSet.PickupEquipmentSet(setIndex)
end
local pickupActionButton = {
item = PickupItem,
spell = PickupSpell,
macro = PickupMacro,
companion = PickupSpell,
equipmentset = PickupEquipmentSet,
}
local function RestoreActionButton(self, index, actionButton)
-- Clear the slot
if GetActionInfo(index) then
PickupAction(index)
ClearCursor()
end
if not actionButton then return true, nil end
local aliases = self.db.class.spellAliases[actionButton.id] or {}
local ids = Array.insert(aliases, actionButton.id, 1)
for _, id in ipairs(ids) do
pickupActionButton[actionButton.type](id)
if GetCursorInfo() == actionButton.type then
PlaceAction(index)
return true, id
end
ClearCursor()
end
return false
end
local function IsMacro(actionButton) return actionButton and actionButton.type == "macro" end
local function GetMacroDuplicates()
local t = {}
local duplicates = {}
for i = 1, MAX_MACROS do
local macroName = GetMacroInfo(i)
if macroName then
if not t[macroName] then
t[macroName] = 1
else
t[macroName] = t[macroName] + 1
duplicates[macroName] = t[macroName]
end
end
end
return duplicates
end
local function AddWarning(warnings, macroName, usages)
table.insert(
warnings,
string.format(
"Warning: Found %d macros named '%s'. Consider renaming them to avoid issues",
usages,
macroName))
end
function SaveSet(setName)
if Str.nullOrEmpty(setName) then
self:Print("Set name cannot be empty")
return
end
local duplicates = GetMacroDuplicates()
local set = {}
local warnings = {}
for i = 1, MAX_ACTION_BUTTONS do
local type, id = GetActionInfo(i)
if type == "macro" then
-- use macro name as the ID
id = GetMacroInfo(id)
if duplicates[id] then AddWarning(warnings, id, duplicates[id]) end
end
if type and id then
set[i] = type and {
type = type,
id = id
}
end
end
self.db.class.sets[setName] = set
self:Print(string.format("Saved set '%s'!", setName))
Array.iter(warnings, function(warning) self:Print(warning) end)
end
function RestoreSet(setName)
if Str.nullOrEmpty(setName) then
self:Print("Set name cannot be empty")
return
end
local set = self.db.class.sets[setName]
if not set then
self:Print(string.format("No set with the name '%s' exists", setName))
return
end
if InCombatLockdown() then
self:Print("Cannot restore sets while in combat")
return
end
local duplicates = GetMacroDuplicates()
local messages = {}
-- Start with an empty cursor
ClearCursor()
for i = 1, MAX_ACTION_BUTTONS do
local actionButton = set[i]
if IsMacro(actionButton) and duplicates[actionButton.id] then AddWarning(messages, actionButton.id, duplicates[actionButton.id]) end
local succeeded, restoredID = RestoreActionButton(self, i, actionButton)
if not succeeded then
table.insert(messages, string.format("Error: Unable to restore %s with id [%s] to slot %d", actionButton.type, actionButton.id or "", i))
elseif actionButton and restoredID ~= actionButton.id then
table.insert(messages, string.format("Info: Restored spell %d (%s) in place of spell %d", restoredID, GetSpellInfo(restoredID), actionButton.id))
end
end
self:Print(string.format("Restored set '%s'", setName))
Array.iter(messages, function(warning) self:Print(warning) end)
end
function DeleteSet(setName)
if Str.nullOrEmpty(setName) then
self:Print("Set name cannot be empty")
return
end
if not self.db.class.sets[setName] then
self:Print(string.format("No set with the name '%s' exists", setName))
return
end
self.db.class.sets[setName] = nil
self:Print(string.format("Deleted set '%s'", setName))
end
function ListSets()
local sets = Dict.keysAsArray(self.db.class.sets)
table.sort(sets)
local setsStr = table.concat(sets, ", ")
self:Print(not Str.nullOrEmpty(setsStr) and setsStr or "No sets found")
end
function AliasSpell(args)
if Str.nullOrEmpty(args) then
self:Print("Must provide args in the format 'spellID aliasID'")
return
end
local spellID, aliasID = string.match(args, "(%d+)%s+(%d+)")
spellID = tonumber(spellID)
aliasID = tonumber(aliasID)
if not (spellID and aliasID) then
self:Print(string.format("Could not parse spellID and aliasID from '%s'", args))
return
end
local aliases = self.db.class.spellAliases[spellID] or {}
if Array.contains(aliases, aliasID) then
self:Print(string.format("Spell %d is already aliased by %d", spellID, aliasID))
return
end
table.insert(aliases, aliasID)
self.db.class.spellAliases[spellID] = aliases
self:Print(string.format("Added %d as an alias for %d", aliasID, spellID))
end
function DeleteSpellAliases(spellID)
if Str.nullOrEmpty(spellID) then
self:Print("Must provide a valid spellID")
return
end
spellID = tonumber(spellID)
if not self.db.class.spellAliases[spellID] then
self:Print(string.format("No aliases to remove for spell with ID %d", spellID))
return
end
self.db.class.spellAliases[spellID] = nil
self:Print(string.format("Removed all aliases for spell with ID %d", spellID))
end
function ListAliases()
local aliases = self.db.class.spellAliases
if Dict.isEmpty(aliases) then
self:Print("No aliases found")
return
end
Dict.iter(self.db.class.spellAliases, function(spellID, aliases)
self:Print(string.format("Spell %d is aliased by: %s", spellID, table.concat(aliases, ", ")))
end)
end
function PrintUsage()
self:Print("ABS Slash commands")
self:Print("/abs save <set> - Saves your current action bar setup under the given <set>")
self:Print("/abs restore <set> - Restores the saved <set>")
self:Print("/abs delete <set> - Deletes the saved <set>")
self:Print("/abs list - Lists all saved sets")
self:Print("/abs alias <spellID> <aliasID> - Adds an alias with <aliasID> to <spellID>")
self:Print("/abs unalias <spellID> - Removes all aliases associated with <spellID>")
self:Print("/abs aliases - List all spell aliases")
end
SlashCmdList["ABS"] = function(argv)
local args = strsplit(" ", argv)
local cmd = args[1]
if cmd == "save" then
SaveSet(args[2])
end
if cmd == "restore" then
RestoreSet(args[2])
end
if cmd == "delete" then
DeleteSet(args[2])
end
if cmd == "list" then
ListSets()
end
if cmd == "alias" then
AliasSpell(args[2], args[3])
end
if cmd == "unalias" then
DeleteSpellAliases(args[2])
end
if cmd == "aliases" then
ListAliases()
end
if cmd == "" then
PrintUsage()
end
end
SLASH_ABS1 = "/abs"

30
Main.lua Normal file
View File

@@ -0,0 +1,30 @@
local ADDON_NAME, shared = ...
local frame = CreateFrame("Frame")
frame:RegisterEvent("ADDON_LOADED")
frame:SetScript("OnEvent", function(self, event, addon)
if addon ~= ADDON_NAME then return end
ActionBarSaverReloaded = ActionBarSaverReloaded or {}
ActionBarSaverReloaded.spellAliases = ActionBarSaverReloaded.spellAliases or {}
ActionBarSaverReloaded.sets = ActionBarSaverReloaded.sets or {}
end)
--function ABS:OnInitialize()
-- self.commands = {
-- save = self.actions.SaveSet,
-- restore = self.actions.RestoreSet,
-- delete = self.actions.DeleteSet,
-- list = self.actions.ListSets,
-- alias = self.actions.AliasSpell,
-- unalias = self.actions.DeleteSpellAliases,
-- aliases = self.actions.ListAliases,
-- }
--end
--
--function ABS:HandleCommands(input)
-- local cmd, args = Str.split(input, " ", 2)
-- local fn = self.commands[Str.toLower(cmd)]
--
-- if fn then fn(self, args) else self.actions.PrintUsage(self) end
--end

25
README.md Normal file
View File

@@ -0,0 +1,25 @@
# ActionBarSaver:Reloaded
## Overview
ActionBarSaver:Reloaded is an addon for saving and restoring action bar profiles. It is based on the original ActionBarSaver addon but is a full re-write.
All sets are saved by class rather than by character. Additionally, when you list profiles, you will only see profiles that pertain to your class.
Features such as rename have been deleted for simplicity. To perform a rename, simply save the set with a new name and delete the old set. Additionally, restoring a set will no longer try to re-create macros that do not exist. It will simply notify you of the missing macro and restore nothing for that slot. ABS:R will not work properly if you have multiple macros with the same name, and will warn you of potential issues if you restore a set that has a macro with a shared name.
A new feature has been added for setting up aliases for spells. A common use case for this would be restoring a single set for two characters that share a class but have a different race. For example, you could create a set on a troll shaman that contains `Berserking` and then add an alias for `War Stomp` using `/abs alias 20554 20549`. If you do this, when you restore a set it will first try to restore the proper spell but will also try each alias that you set up. A spell can have as many aliases as you want.
## Usage
`/abs save <set>` - Saves your current action bar setup under the given <set>\
`/abs restore <set>` - Restores the saved <set>\
`/abs delete <set>` - Deletes the saved <set>\
`/abs list` - Lists all saved sets\
`/abs alias <spellID> <aliasID>` - Adds an alias with <aliasID> to <spellID>\
`/abs unalias <spellID>` - Removes all aliases associated with <spellID>\
`/abs aliases` - List all spell aliases
## Known Issues
* Aliases do not work both ways. If you alias `Berserking` as `War Stomp` and then save a set that contains `Berserking`, it will work properly if you restore that set on a tauren. However, if you save a set that contains `War Stomp` and try to restore it on a troll, it will fail. This will be addressed in a future version.

31
RELEASE_NOTES.md Normal file
View File

@@ -0,0 +1,31 @@
## 1.0.7 (2024-06-30)
- Updated for Cata classic
## 1.0.6 (2023-08-29)
- TOC bump for ICC patch
## 1.0.5 (2023-06-20)
- TOC bump for ToGC patch
## 1.0.4 (2023-01-18)
- TOC bump for WOTLK patch
## 1.0.3 (2022-11-09)
- Fixed bug with equipment sets not being restored properly
## 1.0.2 (2022-10-27)
- Fixed bug where totem sets were not being saved or restored
## 1.0.1 (2022-09-26)
- Fixed bug related to restoring sets containing companions and mounts
## 1.0.0 (2022-07-15)
- Initial re-write of the addon

9
StringHelpers.lua Normal file
View File

@@ -0,0 +1,9 @@
Str = {}
function Str.toLower(s) return string.lower(s or "") end
function Str.nullOrEmpty(s) return not s or s == "" end
function Str.split(s, delimiter, max)
---@diagnostic disable-next-line: undefined-field
return string.split(delimiter, s, max)
end

84
TableHelpers.lua Normal file
View File

@@ -0,0 +1,84 @@
Array = {}
Dict = {}
function Array.iter(arr, fn)
for _,v in ipairs(arr) do
fn(v)
end
end
function Array.map(arr, fn)
local t = {}
for _,v in ipairs(arr) do
table.insert(t, fn(v))
end
return t
end
function Array.mapRange(start, stop, fn)
local arr = {}
for i = start, stop do
arr[i] = fn(i)
end
return arr
end
function Array.iterRange(start, stop, fn)
for i = start, stop do
fn(i)
end
end
function Array.contains(arr, value)
for _,v in ipairs(arr) do
if v == value then return true end
end
return false
end
function Array.insert(arr, value, index)
local t = {}
for _, v in ipairs(arr) do
table.insert(t, v)
end
table.insert(t, index, value)
return t
end
function Dict.iter(t, fn)
for k,v in pairs(t) do
fn(k,v)
end
end
function Dict.map(t, fn)
local nt = {}
for k,v in pairs(t) do
nt[k] = fn(k,v)
end
return nt
end
function Dict.keysAsArray(t)
local nt = {}
for k,_ in pairs(t) do
table.insert(nt, k)
end
return nt
end
function Dict.isEmpty(t)
return next(t) == nil
end

3
constants.lua Normal file
View File

@@ -0,0 +1,3 @@
ADDON_NAME = "ActionBarSaverReloaded"
MAX_MACROS = 138
MAX_ACTION_BUTTONS = 132