25 Commits

Author SHA1 Message Date
1759ebfab6 Release 1.2.3 2025-05-08 14:55:05 +02:00
798043c7aa Try to save keybinds when restoring 2025-05-08 14:54:58 +02:00
396ef0437f Release 1.2.2 2025-05-07 21:40:10 +02:00
d92fe23d8a Update meta 2025-05-07 21:40:07 +02:00
1b09f262d5 Do more hacky shit
I hate this api
2025-05-07 21:39:49 +02:00
f17078c8a8 Release 1.2.0 2025-05-07 21:30:45 +02:00
3e85b9933e Add support for CLICK keybinds
What a cancer API FUCK YOU BLIZZARD
2025-05-07 21:30:38 +02:00
53e201e414 Add machined russian translation of readme 2025-05-06 23:09:23 +02:00
4f7a07c337 FIX the fucking release
I forgot to rename repo OOPS
2025-05-06 23:07:29 +02:00
0637832bdd Release 1.1.2 2025-05-06 23:07:17 +02:00
1b4e3dd688 Fix the god damn git add 2025-05-06 23:06:57 +02:00
717a8ee015 Release 1.1.1 2025-05-06 23:06:40 +02:00
b9a671b0f2 Release 1.1.0 2025-05-06 23:05:55 +02:00
b43608b724 Fix importing actionbars 2025-05-06 23:04:20 +02:00
f5b9b973e3 Fix import and export 2025-05-06 23:00:44 +02:00
0638896bc3 Rename all functions to reduce duplication 2025-05-06 22:52:46 +02:00
7df8ecb582 Update meta 2025-05-06 22:43:32 +02:00
3bd12c8ca8 Thoroughly refuck everything 2025-05-06 22:42:57 +02:00
38b5dbccb1 Clean up the code in general a bit 2025-05-06 22:15:08 +02:00
24e56078bd Rename from Reloaded to Daved
Because now it's in my hands!
2025-05-06 22:09:52 +02:00
10d35da8e0 Update 2025-05-06 22:02:53 +02:00
b7a184b26b Add release script 2025-05-06 22:00:54 +02:00
f1b1a384d4 Code format 2025-05-06 22:00:50 +02:00
4c3913ca3f Add meta 2025-05-06 21:58:41 +02:00
2b1baaca36 Implement keybind saver 2025-05-06 21:58:40 +02:00
17 changed files with 908 additions and 545 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.zip

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "Meta"]
path = Meta
url = https://git.site.quack-lab.dev/dave/wow_Meta

5
.luacheckrc Symbolic link
View File

@@ -0,0 +1,5 @@
globals = { "CykaPersistentData", "CreateFrame", "GetItemInfo" }
unused_args = false
max_line_length = 150
exclude_files = { "Meta/" }
global = false

11
.luarc.json Symbolic link
View File

@@ -0,0 +1,11 @@
{
"workspace": {
"library": [
"./Meta"
]
},
"diagnostics.disable": [
"unused-local",
"unused-vararg"
]
}

12
ActionBarSaverDaved.toc Normal file
View File

@@ -0,0 +1,12 @@
## Interface: 70300
## Title: ActionBarSaver: Daved
## Version: 1.2.3
## Notes: Manage, save, restore, import and export action bar and keybind profiles
## Author: Phat phuck dave
## SavedVariables: ActionBarSaverDaved, KeybindSaverDaved
B64.lua
Constants.lua
Main.lua
Actions.lua
KeybindSaver.lua

View File

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

BIN
ActionBarSaverReloaded.zip (Stored with Git LFS)

Binary file not shown.

View File

@@ -1,299 +1,313 @@
local ADDON_LOADED, shared = ...
local typeMap = { local typeMap = {
spell = "S", spell = "S",
item = "I", item = "I",
macro = "M", macro = "M",
companion = "C", companion = "C",
equipmentset = "E", equipmentset = "E",
summonmount = "U" summonmount = "U",
} }
local inverseTypeMap = { local inverseTypeMap = {
S = "spell", S = "spell",
I = "item", I = "item",
M = "macro", M = "macro",
C = "companion", C = "companion",
E = "equipmentset", E = "equipmentset",
U = "summonmount" U = "summonmount",
} }
---@param setName string
---@return nil
local function PickupEquipmentSet(setName) local function PickupEquipmentSet(setName)
local setIndex = 0 local setIndex = 0
for i = 1, C_EquipmentSet.GetNumEquipmentSets() do for i = 1, C_EquipmentSet.GetNumEquipmentSets() do
local sn = C_EquipmentSet.GetEquipmentSetInfo(i) local sn = C_EquipmentSet.GetEquipmentSetInfo(i)
if sn == setName then if sn == setName then setIndex = i end
setIndex = i end
end
end
C_EquipmentSet.PickupEquipmentSet(setIndex) C_EquipmentSet.PickupEquipmentSet(setIndex)
end end
local pickupActionButton = { local pickupActionButton = {
item = PickupItem, item = PickupItem,
spell = PickupSpell, spell = PickupSpell,
macro = PickupMacro, macro = PickupMacro,
companion = PickupSpell, companion = PickupSpell,
equipmentset = PickupEquipmentSet, equipmentset = PickupEquipmentSet,
summonmount = C_MountJournal.Pickup summonmount = C_MountJournal.Pickup,
} }
local function RestoreActionButton(self, index, actionButton) ---@param index number
if GetActionInfo(index) then ---@return nil
PickupAction(index) local function ClearActionButton(index)
ClearCursor() if GetActionInfo(index) then
end PickupAction(index)
ClearCursor()
if not actionButton then end
return true, nil
end
local aliases = ActionBarSaverReloaded.spellAliases[actionButton.id] or {}
table.insert(aliases, actionButton.id)
for _, id in ipairs(aliases) do
pickupActionButton[actionButton.type](id)
if GetCursorInfo() == actionButton.type then
PlaceAction(index)
return true, id
end
ClearCursor()
end
return false
end end
local function IsMacro(actionButton) ---@param index number
return actionButton and actionButton.type == "macro" ---@param actionButton {type: string, id: number|string}
---@return boolean, number?
local function RestoreActionButton(index, actionButton)
ClearActionButton(index)
if not actionButton then return true, nil end
local aliases = ActionBarSaverDaved.spellAliases[actionButton.id] or {}
table.insert(aliases, actionButton.id)
for _, id in ipairs(aliases) do
pickupActionButton[actionButton.type](id)
if GetCursorInfo() == actionButton.type then
PlaceAction(index)
return true, id
end
ClearCursor()
end
return false
end end
---@param index number
---@return boolean
local function ClearActionButton(index)
if not index then return false end
PickupAction(index)
ClearCursor()
return true
end
---@param actionButton {type: string, id: number|string}
---@return boolean
local function IsMacro(actionButton) return actionButton and actionButton.type == "macro" end
---@return table<string, number>
local function GetMacroDuplicates() local function GetMacroDuplicates()
local t = {} local t = {}
local duplicates = {} local duplicates = {}
for i = 1, MAX_MACROS do for i = 1, MAX_MACROS do
local macroName = GetMacroInfo(i) local macroName = GetMacroInfo(i)
if macroName then if macroName then
if not t[macroName] then if not t[macroName] then
t[macroName] = 1 t[macroName] = 1
else else
t[macroName] = t[macroName] + 1 t[macroName] = t[macroName] + 1
duplicates[macroName] = t[macroName] duplicates[macroName] = t[macroName]
end end
end end
end end
return duplicates return duplicates
end end
---@param warnings table<string, number>
---@param macroName string
---@param usages number
---@return nil
local function AddWarning(warnings, macroName, usages) local function AddWarning(warnings, macroName, usages)
table.insert(warnings, string.format("Warning: Found %d macros named '%s'. Consider renaming them to avoid issues", table.insert(
usages, macroName)) warnings,
string.format("Warning: Found %d macros named '%s'. Consider renaming them to avoid issues", usages, macroName)
)
end end
function SaveSet(setName) ---@param setName string
if not setName or setName == "" then ---@return nil
print("Set name cannot be empty") function SaveActionbarSet(setName)
return if not setName or setName == "" then
end print("Set name cannot be empty")
return
end
local duplicates = GetMacroDuplicates() local duplicates = GetMacroDuplicates()
local set = {} ---@type table<number, {type: string, id: number|string}>
local warnings = {} local set = {}
---@type string[]
local warnings = {}
for i = 1, MAX_ACTION_BUTTONS do for i = 1, MAX_ACTION_BUTTONS do
local type, id = GetActionInfo(i) local type, id = GetActionInfo(i)
if type == "macro" then if type == "macro" then
-- use macro name as the ID -- use macro name as the ID
id = GetMacroInfo(id) id = GetMacroInfo(id)
if duplicates[id] then if duplicates[id] then AddWarning(warnings, id, duplicates[id]) end
AddWarning(warnings, id, duplicates[id]) end
end
end
if type and id then if type and id then set[i] = type and {
set[i] = type and { type = type,
type = type, id = id,
id = id } end
} end
end
end
ActionBarSaverReloaded.sets[setName] = set ActionBarSaverDaved.sets[setName] = set
print(string.format("Saved set '%s'!", setName)) print(string.format("Saved set '%s'!", setName))
for _, warning in ipairs(warnings) do for _, warning in ipairs(warnings) do
print(warning) print(warning)
end end
end end
function RestoreSet(setName) ---@param setName string
if not setName or setName == "" then ---@return nil
print("Set name cannot be empty") function RestoreActionbarSet(setName)
return if not setName or setName == "" then
end print("Set name cannot be empty")
return
end
local set = ActionBarSaverReloaded.sets[setName] local set = ActionBarSaverDaved.sets[setName]
if not set then if not set then
print(string.format("No set with the name '%s' exists", setName)) print(string.format("No set with the name '%s' exists", setName))
return return
end end
if InCombatLockdown() then if InCombatLockdown() then
print("Cannot restore sets while in combat") print("Cannot restore sets while in combat")
return return
end end
local duplicates = GetMacroDuplicates() local duplicates = GetMacroDuplicates()
local messages = {} local messages = {}
-- Start with an empty cursor -- Start with an empty cursor
ClearCursor() ClearCursor()
for i = 1, MAX_ACTION_BUTTONS do for i = 1, MAX_ACTION_BUTTONS do
local actionButton = set[i] local actionButton = set[i]
ClearActionButton(i)
if actionButton then
if IsMacro(actionButton) and duplicates[actionButton.id] then
---@cast actionButton {type: string, id: string}
AddWarning(messages, actionButton.id, duplicates[actionButton.id])
end
if IsMacro(actionButton) and duplicates[actionButton.id] then local succeeded, restoredID = RestoreActionButton(i, actionButton)
AddWarning(messages, actionButton.id, duplicates[actionButton.id]) if not succeeded or not restoredID then
end 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
else
ClearActionButton(i)
end
end
local succeeded, restoredID = RestoreActionButton(self, i, actionButton) print(string.format("Restored set '%s'", setName))
if not succeeded then for _, warning in ipairs(messages) do
table.insert(messages, string.format("Error: Unable to restore %s with id [%s] to slot %d", print(warning)
actionButton.type, actionButton.id or "", i)) end
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
print(string.format("Restored set '%s'", setName))
for _, warning in ipairs(messages) do
print(warning)
end
end end
function DeleteSet(setName) ---@param setName string
if not setName or setName == "" then ---@return nil
print("Set name cannot be empty") function DeleteActionbarSet(setName)
return if not setName or setName == "" then
end print("Set name cannot be empty")
return
end
if not ActionBarSaverReloaded.sets[setName] then if not ActionBarSaverDaved.sets[setName] then
print(string.format("No set with the name '%s' exists", setName)) print(string.format("No set with the name '%s' exists", setName))
return return
end end
ActionBarSaverReloaded.sets[setName] = nil ActionBarSaverDaved.sets[setName] = nil
print(string.format("Deleted set '%s'", setName)) print(string.format("Deleted set '%s'", setName))
end end
function ListSets() ---@return nil
local sets = {} function ListActionbarSets()
for setName, foo in pairs(ActionBarSaverReloaded.sets) do local sets = {}
sets[#sets + 1] = setName for setName, foo in pairs(ActionBarSaverDaved.sets) do
end sets[#sets + 1] = setName
table.sort(sets) end
local setsStr = table.concat(sets, ", ") table.sort(sets)
local setsStr = table.concat(sets, ", ")
print(not (not setsStr or setsStr == "") and setsStr or "No sets found") print(not (not setsStr or setsStr == "") and setsStr or "No sets found")
end end
function AliasSpell(args) ---@param of string
if not args or args == "" then ---@param to string
print("Must provide args in the format 'spellID aliasID'") ---@return nil
return function AliasSpell(of, to)
end if not of or not to then
local spellID, aliasID = string.match(args, "(%d+)%s+(%d+)") print("Must provide args in the format 'spellID aliasID'")
return
end
local ofId = tonumber(of)
local toId = tonumber(to)
spellID = tonumber(spellID) if not (ofId and toId) then
aliasID = tonumber(aliasID) print(string.format("Could not parse %s and/or %s to numbers", of, to))
return
end
if not (spellID and aliasID) then local aliases = ActionBarSaverDaved.spellAliases[ofId] or {}
print(string.format("Could not parse spellID and aliasID from '%s'", args))
return
end
local aliases = ActionBarSaverReloaded.spellAliases[spellID] or {} for _, id in ipairs(aliases) do
if id == toId then
print(string.format("Spell %d is already aliased by %d", ofId, toId))
return
end
end
for _, id in ipairs(aliases) do table.insert(ActionBarSaverDaved.spellAliases[ofId], toId)
if id == aliasID then print(string.format("Added %d as an alias for %d", toId, ofId))
print(string.format("Spell %d is already aliased by %d", spellID, aliasID))
return
end
end
table.insert(ActionBarSaverReloaded.spellAliases[spellID], aliasID)
print(string.format("Added %d as an alias for %d", aliasID, spellID))
end end
function DeleteSpellAliases(spellID) ---@param of string
if not spellID or spellID == "" then ---@return nil
print("Must provide a valid spellID") function DeleteSpellAliases(of)
return if not of then
end print("Must provide a valid spellID")
return
end
spellID = tonumber(spellID) local ofId = tonumber(of)
if not ActionBarSaverReloaded.spellAliases[spellID] then if not ofId then
print(string.format("No aliases to remove for spell with ID %d", spellID)) print(string.format("Could not parse spellID from '%s'", of))
return return
end end
ActionBarSaverReloaded.spellAliases[spellID] = nil if not ActionBarSaverDaved.spellAliases[ofId] then
print(string.format("No aliases to remove for spell with ID %d", ofId))
return
end
print(string.format("Removed all aliases for spell with ID %d", spellID)) ActionBarSaverDaved.spellAliases[ofId] = nil
print(string.format("Removed all aliases for spell with ID %d", ofId))
end end
---@return nil
function ListAliases() function ListAliases()
local aliases = ActionBarSaverReloaded.spellAliases for spellID, spellAliases in pairs(ActionBarSaverDaved.spellAliases) do
print(string.format("Spell %d is aliased by: %s", spellID, table.concat(spellAliases, ", ")))
if Dict.isEmpty(aliases) then end
print("No aliases found")
return
end
Dict.iter(ActionBarSaverReloaded.spellAliases, function(spellID, aliases)
print(string.format("Spell %d is aliased by: %s", spellID, table.concat(aliases, ", ")))
end)
end
---@param text string
---@param size number
---@param deliminer string
---@return string[]
local function Partition(text, size, deliminer)
local words = {}
for word in text:gmatch("[^" .. deliminer .. "]+") do
words[#words + 1] = word
end
local ret = {}
local currentChunk = ""
for _, word in ipairs(words) do
if #currentChunk + #word + 1 <= size then
currentChunk = currentChunk .. deliminer .. word
else
if #currentChunk > 0 then
ret[#ret + 1] = currentChunk
end
currentChunk = word
end
end
if #currentChunk > 0 then
ret[#ret + 1] = currentChunk
end
return ret
end end
local importingSet = nil local importingSet = nil
@@ -305,17 +319,17 @@ importExportFrame:EnableMouse(true)
importExportFrame:SetMovable(true) importExportFrame:SetMovable(true)
importExportFrame:SetResizable(false) importExportFrame:SetResizable(false)
importExportFrame:SetBackdrop({ importExportFrame:SetBackdrop({
bgFile = "Interface/Tooltips/UI-Tooltip-Background", bgFile = "Interface/Tooltips/UI-Tooltip-Background",
edgeFile = "Interface/Tooltips/UI-Tooltip-Border", edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
tile = true, tile = true,
tileSize = 4, tileSize = 4,
edgeSize = 4, edgeSize = 4,
insets = { insets = {
left = 4, left = 4,
right = 4, right = 4,
top = 4, top = 4,
bottom = 4 bottom = 4,
} },
}) })
importExportFrame:SetBackdropColor(0, 0, 0, 0.8) importExportFrame:SetBackdropColor(0, 0, 0, 0.8)
importExportFrame:SetBackdropBorderColor(0.5, 0.5, 0.5, 1) importExportFrame:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
@@ -323,15 +337,9 @@ importExportFrame:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
importExportFrame:SetMovable(true) importExportFrame:SetMovable(true)
importExportFrame:EnableMouse(true) importExportFrame:EnableMouse(true)
importExportFrame:RegisterForDrag("LeftButton") importExportFrame:RegisterForDrag("LeftButton")
importExportFrame:SetScript("OnDragStart", function(self) importExportFrame:SetScript("OnDragStart", function(self) self:StartMoving() end)
self:StartMoving() importExportFrame:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end)
end) importExportFrame:SetScript("OnShow", function(self) self:SetScale(1) end)
importExportFrame:SetScript("OnDragStop", function(self)
self:StopMovingOrSizing()
end)
importExportFrame:SetScript("OnShow", function(self)
self:SetScale(1)
end)
importExportFrame:Hide() importExportFrame:Hide()
local importExportFrameTextBox = CreateFrame("EditBox", "ABSImportExportFrameTextBox", importExportFrame) local importExportFrameTextBox = CreateFrame("EditBox", "ABSImportExportFrameTextBox", importExportFrame)
@@ -344,207 +352,198 @@ importExportFrameTextBox:SetMultiLine(true)
importExportFrameTextBox:SetAutoFocus(true) importExportFrameTextBox:SetAutoFocus(true)
importExportFrameTextBox:SetMaxLetters(1000000) importExportFrameTextBox:SetMaxLetters(1000000)
importExportFrameTextBox:SetScript("OnEscapePressed", function(self) importExportFrameTextBox:SetScript("OnEscapePressed", function(self)
importExportFrame:Hide() importExportFrame:Hide()
if importingSet then if importingSet then
local lines = {strsplit("\n", self:GetText())} local lines = { strsplit("\n", self:GetText()) }
for _, line in ipairs(lines) do for _, line in ipairs(lines) do
line = strtrim(line) line = strtrim(line)
if line ~= "" then if line ~= "" then ImportActionbarSet(importingSet, line) end
ImportSet(importingSet, line) end
end importingSet = nil
end end
importingSet = nil
end
end) end)
function ExportSet(setName) ---@param setName string
local set = ActionBarSaverReloaded.sets[setName] ---@return nil
if not set then function ExportActionbarSet(setName)
print(string.format("No set with the name '%s' exists", setName)) local set = ActionBarSaverDaved.sets[setName]
return if not set then
end print(string.format("No set with the name '%s' exists", setName))
local macros = {} return
local stringified = {} end
for slot, action in pairs(set) do local macros = {}
local typeChar = typeMap[action.type] local stringified = {}
if not typeChar then for slot, action in pairs(set) do
print(string.format("Unknown action type '%s' in set '%s'", action.type, setName)) local typeChar = typeMap[action.type]
return if not typeChar then
end print(string.format("Unknown action type '%s' in set '%s'", action.type, setName))
stringified[#stringified + 1] = string.format("%s\\%s\\%s", tostring(slot), tostring(action.id), return
tostring(typeChar)) end
if typeChar == "M" then stringified[#stringified + 1] =
local _, _, macro = GetMacroInfo(action.id) string.format("%s\\%s\\%s", tostring(slot), tostring(action.id), tostring(typeChar))
if macro then if typeChar == "M" then
macros[action.id] = macro local _, _, macro = GetMacroInfo(action.id)
end if macro then macros[action.id] = macro end
end end
end end
local export = {} local export = {}
for name, macro in pairs(macros) do for name, macro in pairs(macros) do
local content = B64.Encode(macro) local content = B64.Encode(macro)
export[#export + 1] = string.format("Mž%sž%s", name, content) export[#export + 1] = string.format("Mž%sž%s", name, content)
end end
export[#export + 1] = table.concat(stringified, "ž") export[#export + 1] = table.concat(stringified, "ž")
local str = table.concat(export, "\n") local str = table.concat(export, "\n")
importExportFrame:Show() importExportFrame:Show()
importExportFrameTextBox:SetText(str) importExportFrameTextBox:SetText(str)
importExportFrameTextBox:SetFocus() importExportFrameTextBox:SetFocus()
end end
---@param action {slot: number, id: number|string, type: string}
---@return string
function FormatAction(action) return string.format("slot: %d, id: %s, type: %s", action.slot, action.id, action.type) end
---@param action string
---@return {slot: number, id: number|string, type: string}
function ParseAction(action) function ParseAction(action)
if not action or action == "" then local ret = {
return nil slot = 0,
end id = 0,
action = strtrim(action) type = "",
local slot, id, typeChar = string.match(action, "([^\\]+)\\([^\\]+)\\([^\\]+)") }
if not typeChar then
print(string.format("Unknown action type '%s' in set '%s'", tostring(typeChar), tostring(setName)))
return
end
local type = inverseTypeMap[typeChar]
if not type then
print(string.format("Unknown action type '%s' in set '%s'", tostring(typeChar), tostring(setName)))
return
end
slot = tonumber(slot) if not action or action == "" then return ret end
action = strtrim(action)
---@type string, string, string
local slot, id, typeChar = string.match(action, "([^\\]+)\\([^\\]+)\\([^\\]+)")
if not typeChar then
print(string.format("Unknown action type '%s' for action '%s'", tostring(typeChar), FormatAction(ret)))
return ret
end
local type = inverseTypeMap[typeChar]
if not type then
print(string.format("Unknown action type '%s' for action '%s'", tostring(typeChar), FormatAction(ret)))
return ret
end
if not slot then local slotNum = tonumber(slot)
print(string.format("Unknown slot '%s' in set '%s'", tostring(slot), tostring(setName))) if not slotNum then
return print(string.format("Unknown slot '%s' for action '%s'", tostring(slot), FormatAction(ret)))
end return ret
end
if not id then if not id then
print(string.format("Unknown id '%s' in set '%s'", tostring(id), tostring(setName))) print(string.format("Unknown id '%s' for action '%s'", tostring(id), FormatAction(ret)))
return return ret
end end
return { return {
slot = slot, slot = slotNum,
id = id, id = id,
type = type type = type,
} }
end end
---@param importString string
---@return nil
function ImportMacro(importString) function ImportMacro(importString)
if not importString or importString == "" then if not importString or importString == "" then
print("Must provide a valid macro string") print("Must provide a valid macro string")
return return
end end
importString = strtrim(importString) importString = strtrim(importString)
local name, content = string.match(importString, "^Mž([^ž]+)ž([^ž]+)") local name, content = string.match(importString, "^Mž([^ž]+)ž([^ž]+)")
if not name or not content then if not name or not content then
print("Error: Invalid macro part format") print("Error: Invalid macro part format")
return return
end end
content = strtrim(content) content = strtrim(content)
name = strtrim(name) name = strtrim(name)
local reconstructed = B64.Decode(content) local reconstructed = B64.Decode(content)
local macroIdx = GetMacroIndexByName(name) local macroIdx = GetMacroIndexByName(name)
if macroIdx == 0 then if macroIdx == 0 then
macroIdx = CreateMacro(name, "Inv_misc_questionmark", "") CreateMacro(name, "Inv_misc_questionmark", "")
macroIdx = GetMacroIndexByName(name) macroIdx = GetMacroIndexByName(name)
end end
EditMacro(macroIdx, name, nil, reconstructed) EditMacro(macroIdx, name, nil, reconstructed)
print(string.format("Imported macro '%s' with index %d and content '%s'", name, macroIdx, reconstructed)) print(string.format("Imported macro '%s' with index %d and content '%s'", name, macroIdx, reconstructed))
return
end end
function ImportSet(setName, str) ---@param setName string
if not setName or setName == "" then ---@param str string
print("Must provide a valid set name") ---@return nil
return function ImportActionbarSet(setName, str)
end if not setName or setName == "" then
print("Must provide a valid set name")
return
end
if string.find(str, "^Mž") then if string.find(str, "^Mž") then
ImportMacro(str) ImportMacro(str)
return return
end end
local set = ActionBarSaverReloaded.sets[setName] or {} local set = ActionBarSaverDaved.sets[setName] or {}
-- if set then -- if set then
-- print(string.format("Set '%s' already exists", setName)) -- print(string.format("Set '%s' already exists", setName))
-- return -- return
-- end -- end
str = strtrim(str) str = strtrim(str)
local data = {strsplit("ž", str)} local data = { strsplit("ž", str) }
for _, action in ipairs(data) do for _, action in ipairs(data) do
local paction = ParseAction(action) local paction = ParseAction(action)
if paction then if paction then set[paction.slot] = {
set[paction.slot] = { type = paction.type,
type = paction.type, id = paction.id,
id = paction.id } end
} end
end
end
-- /dump ActionBarSaverReloaded.sets["havoc"] ActionBarSaverDaved.sets[setName] = set
-- /dump ActionBarSaverReloaded.sets["havoc2"] print(string.format("Imported set '%s'", setName))
ActionBarSaverReloaded.sets[setName] = set
print(string.format("Imported set '%s'", setName))
end end
function ImportSetDialogue(setName) ---@param setName string
if not setName or setName == "" then ---@return nil
print("Must provide a valid set name") function ImportActionbarSetDialogue(setName)
return if not setName or setName == "" then
end print("Must provide a valid set name")
importingSet = setName return
importExportFrameTextBox:SetText("") end
importExportFrame:Show() importingSet = setName
importExportFrameTextBox:SetFocus() importExportFrameTextBox:SetText("")
importExportFrame:Show()
importExportFrameTextBox:SetFocus()
end end
function PrintUsage() ---@return nil
print("ABS Slash commands") function PrintActionbarUsage()
print("/abs save <set> - Saves your current action bar setup under the given <set>") print("ABS Slash commands")
print("/abs restore <set> - Restores the saved <set>") print("/abs save <set> - Saves your current action bar setup under the given <set>")
print("/abs delete <set> - Deletes the saved <set>") print("/abs restore <set> - Restores the saved <set>")
print("/abs list - Lists all saved sets") print("/abs delete <set> - Deletes the saved <set>")
print("/abs alias <spellID> <aliasID> - Adds an alias with <aliasID> to <spellID>") print("/abs list - Lists all saved sets")
print("/abs unalias <spellID> - Removes all aliases associated with <spellID>") print("/abs alias <spellID> <aliasID> - Adds an alias with <aliasID> to <spellID>")
print("/abs aliases - List all spell aliases") print("/abs unalias <spellID> - Removes all aliases associated with <spellID>")
print("/abs export <set> - Brings up a dialog to export the given <set>") print("/abs aliases - List all spell aliases")
print("/abs import <set> - Brings up a dialog to import the given <set>") print("/abs export <set> - Brings up a dialog to export the given <set>")
print("/abs import <set> - Brings up a dialog to import the given <set>")
end end
SlashCmdList["ABS"] = function(argv) SlashCmdList["ABS"] = function(argv)
local args = {strsplit(" ", argv)} local args = { strsplit(" ", argv) }
local cmd = args[1] local cmd = args[1]
if cmd == "save" then if cmd == "save" then SaveActionbarSet(args[2]) end
SaveSet(args[2]) if cmd == "restore" then RestoreActionbarSet(args[2]) end
end if cmd == "delete" then DeleteActionbarSet(args[2]) end
if cmd == "restore" then if cmd == "list" then ListActionbarSets() end
RestoreSet(args[2]) if cmd == "alias" then AliasSpell(args[2], args[3]) end
end if cmd == "unalias" then DeleteSpellAliases(args[2]) end
if cmd == "delete" then if cmd == "aliases" then ListAliases() end
DeleteSet(args[2]) if cmd == "export" then ExportActionbarSet(args[2]) end
end if cmd == "import" then ImportActionbarSetDialogue(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 == "export" then
ExportSet(args[2])
end
if cmd == "import" then
ImportSetDialogue(args[2])
end
if cmd == "" or not cmd then if cmd == "" or not cmd then PrintActionbarUsage() end
PrintUsage()
end
end end
SLASH_ABS1 = "/abs" SLASH_ABS1 = "/abs"

66
B64.lua
View File

@@ -1,46 +1,42 @@
if not B64 then if not B64 then B64 = {} end
B64 = {}
end
local encode, decode = {}, { local encode, decode = {}, {
[strbyte("=")] = false [strbyte("=")] = false,
} }
for value = 0, 63 do for value = 0, 63 do
local char = strsub('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', value + 1, value + 1) local char = strsub("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", value + 1, value + 1)
encode[value] = char encode[value] = char
decode[strbyte(char)] = value decode[strbyte(char)] = value
end end
local t = {} local t = {}
function B64.Encode(str) function B64.Encode(str)
local j = 1 local j = 1
for i = 1, strlen(str), 3 do for i = 1, strlen(str), 3 do
local a, b, c = strbyte(str, i, i + 2) local a, b, c = strbyte(str, i, i + 2)
t[j] = encode[bit.rshift(a, 2)] t[j] = encode[bit.rshift(a, 2)]
t[j + 1] = encode[bit.band(bit.lshift(a, 4) + bit.rshift(b or 0, 4), 0x3F)] t[j + 1] = encode[bit.band(bit.lshift(a, 4) + bit.rshift(b or 0, 4), 0x3F)]
t[j + 2] = b and encode[bit.band(bit.lshift(b, 2) + bit.rshift(c or 0, 6), 0x3F)] or "=" t[j + 2] = b and encode[bit.band(bit.lshift(b, 2) + bit.rshift(c or 0, 6), 0x3F)] or "="
t[j + 3] = c and encode[bit.band(c, 0x3F)] or "=" t[j + 3] = c and encode[bit.band(c, 0x3F)] or "="
j = j + 4 j = j + 4
end end
return table.concat(t, "", 1, j - 1) return table.concat(t, "", 1, j - 1)
end end
function B64.Decode(str) function B64.Decode(str)
local j = 1 local j = 1
if strlen(str) % 4 ~= 0 then if strlen(str) % 4 ~= 0 then str = str .. string.rep("=", 4 - strlen(str) % 4) end
str = str .. string.rep("=", 4 - strlen(str) % 4) assert(strlen(str) % 4 == 0, format("invalid data length: %d", strlen(str)))
end for i = 1, strlen(str), 4 do
assert(strlen(str) % 4 == 0, format("invalid data length: %d", strlen(str))) local ba, bb, bc, bd = strbyte(str, i, i + 3)
for i = 1, strlen(str), 4 do local a, b, c, d = decode[ba], decode[bb], decode[bc], decode[bd]
local ba, bb, bc, bd = strbyte(str, i, i + 3) assert(a ~= nil, format("invalid data at position %d: '%s'", i, ba))
local a, b, c, d = decode[ba], decode[bb], decode[bc], decode[bd] assert(b ~= nil, format("invalid data at position %d: '%s'", i + 1, bb))
assert(a ~= nil, format("invalid data at position %d: '%s'", i, ba)) assert(c ~= nil, format("invalid data at position %d: '%s'", i + 2, bc))
assert(b ~= nil, format("invalid data at position %d: '%s'", i + 1, bb)) assert(d ~= nil, format("invalid data at position %d: '%s'", i + 3, bd))
assert(c ~= nil, format("invalid data at position %d: '%s'", i + 2, bc)) t[j] = strchar(bit.lshift(a, 2) + bit.rshift(b, 4))
assert(d ~= nil, format("invalid data at position %d: '%s'", i + 3, bd)) t[j + 1] = c and strchar(bit.band(bit.lshift(b, 4) + bit.rshift(c, 2), 0xFF)) or ""
t[j] = strchar(bit.lshift(a, 2) + bit.rshift(b, 4)) t[j + 2] = d and strchar(bit.band(bit.lshift(c, 6) + d, 0xFF)) or ""
t[j + 1] = c and strchar(bit.band(bit.lshift(b, 4) + bit.rshift(c, 2), 0xFF)) or "" j = j + 3
t[j + 2] = d and strchar(bit.band(bit.lshift(c, 6) + d, 0xFF)) or "" end
j = j + 3 return table.concat(t, "", 1, j - 1)
end
return table.concat(t, "", 1, j - 1)
end end

278
KeybindSaver.lua Normal file
View File

@@ -0,0 +1,278 @@
local ADDON_NAME = ...
-- Initialize saved variables
local frame = CreateFrame("Frame")
frame:RegisterEvent("ADDON_LOADED")
frame:SetScript("OnEvent", function(self, event, addon)
if addon ~= ADDON_NAME then return end
KeybindSaverDaved = KeybindSaverDaved or {}
KeybindSaverDaved.sets = KeybindSaverDaved.sets or {}
end)
---@param setName string
---@return nil
local function SaveKeybindSet(setName)
if not setName or setName == "" then
print("Set name cannot be empty")
return
end
---@type table<string, {key1: string, key2: string?}>
local set = {}
local numBindings = GetNumBindings()
for i = 1, numBindings do
local command, _, key1, key2 = GetBinding(i)
if string.find(command, "CLICK ") then
-- What a stupid fucking api, so fucking idiotic, retarded
command, key2 = strsplit(":", command)
end
if key1 or key2 then set[command] = {
key1 = key1,
key2 = key2,
} end
end
KeybindSaverDaved.sets[setName] = set
print(string.format("Saved keybind set '%s'!", setName))
end
---@param command string
---@return nil
local function UnbindCommand(command)
if not command or command == "" then
print("Cannot unbind an empty command")
return
end
local keys = { GetBindingKey(command) }
for _, key in ipairs(keys) do
-- print("Unbinding", key)
SetBinding(key)
end
end
---@param setName string
---@return nil
local function RestoreKeybindSet(setName)
if not setName or setName == "" then
print("Set name cannot be empty")
return
end
local set = KeybindSaverDaved.sets[setName]
if not set then
print(string.format("No set with the name '%s' exists", setName))
return
end
-- Clear all current bindings first
ClearOverrideBindings(frame)
-- Restore saved bindings
for command, keys in pairs(set) do
UnbindCommand(command)
-- This fucking catastrophe binds a KEY(2) to CLICK A BUTTON (command) as a MOUSE BUTTON (key1)
if string.find(command, "CLICK ") and keys.key2 then
command = string.gsub(command, "CLICK ", "")
local ok = SetBindingClick(keys.key2, command, keys.key1)
if not ok then print("Failed to bind click", keys.key1, command) end
else
if keys.key1 then
local ok = SetBinding(keys.key1, command)
if not ok then print("Failed to bind", keys.key1, command) end
end
if keys.key2 then
local ok = SetBinding(keys.key2, command)
if not ok then print("Failed to bind", keys.key2, command) end
end
end
end
-- Save the changes
SaveBindings(GetCurrentBindingSet())
SaveBindings(1)
print(string.format("Restored keybind set '%s'", setName))
end
---@param setName string
---@return nil
local function DeleteKeybindSet(setName)
if not setName or setName == "" then
print("Set name cannot be empty")
return
end
if not KeybindSaverDaved.sets[setName] then
print(string.format("No set with the name '%s' exists", setName))
return
end
KeybindSaverDaved.sets[setName] = nil
print(string.format("Deleted keybind set '%s'", setName))
end
---@return nil
local function ListKeybindSets()
local sets = {}
for setName in pairs(KeybindSaverDaved.sets) do
table.insert(sets, setName)
end
table.sort(sets)
if #sets == 0 then
print("No keybind sets found")
else
print("Saved keybind sets:")
print(table.concat(sets, ", "))
end
end
---@param setName string
---@param importStr string
---@return nil
local function ImportKeybindSet(setName, importStr)
if not setName or setName == "" then
print("Set name cannot be empty")
return
end
local set = {}
local lines = { strsplit("\n", importStr) }
for _, line in ipairs(lines) do
local command, key, key2 = strsplit(":", line)
if command and key then
if string.find(command, "CLICK ") then
-- I can't believe we have to do this bullshit...
local tmp = key
key = key2
key2 = tmp
end
if not set[command] then set[command] = {
key1 = key,
key2 = key2,
} end
end
end
KeybindSaverDaved.sets[setName] = set
print(string.format("Imported keybind set '%s'", setName))
end
-- Create the import/export frame
local importExportFrame = CreateFrame("Frame", "KBSImportExportFrame", UIParent)
importExportFrame:SetSize(512, 512)
importExportFrame:SetPoint("CENTER")
importExportFrame:SetFrameStrata("HIGH")
importExportFrame:EnableMouse(true)
importExportFrame:SetMovable(true)
importExportFrame:SetResizable(false)
importExportFrame:SetBackdrop({
bgFile = "Interface/Tooltips/UI-Tooltip-Background",
edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
tile = true,
tileSize = 4,
edgeSize = 4,
insets = {
left = 4,
right = 4,
top = 4,
bottom = 4,
},
})
importExportFrame:SetBackdropColor(0, 0, 0, 0.8)
importExportFrame:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
importExportFrame:SetMovable(true)
importExportFrame:EnableMouse(true)
importExportFrame:RegisterForDrag("LeftButton")
importExportFrame:SetScript("OnDragStart", function(self) self:StartMoving() end)
importExportFrame:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end)
importExportFrame:SetScript("OnShow", function(self) self:SetScale(1) end)
importExportFrame:Hide()
local importingSet = nil
local importExportFrameTextBox = CreateFrame("EditBox", "KBSImportExportFrameTextBox", importExportFrame)
importExportFrameTextBox:SetSize(512, 512)
importExportFrameTextBox:SetPoint("TOPLEFT", importExportFrame, "TOPLEFT", 0, 0)
importExportFrameTextBox:SetFont("Fonts\\FRIZQT__.ttf", 12)
importExportFrameTextBox:SetTextColor(1, 1, 1, 1)
importExportFrameTextBox:SetTextInsets(20, 20, 20, 20)
importExportFrameTextBox:SetMultiLine(true)
importExportFrameTextBox:SetAutoFocus(true)
importExportFrameTextBox:SetMaxLetters(1000000)
importExportFrameTextBox:SetScript("OnEscapePressed", function(self)
importExportFrame:Hide()
if importingSet then
local text = self:GetText()
if text and text ~= "" then ImportKeybindSet(importingSet, text) end
importingSet = nil
end
end)
---@param setName string
---@return nil
local function ExportKeybindSet(setName)
local set = KeybindSaverDaved.sets[setName]
if not set then
print(string.format("No set with the name '%s' exists", setName))
return
end
local export = {}
for command, keys in pairs(set) do
if command and keys and (keys.key1 or keys.key2) then
local formatted = command
if keys.key1 then formatted = formatted .. ":" .. keys.key1 end
if keys.key2 then formatted = formatted .. ":" .. keys.key2 end
export[#export + 1] = formatted
end
end
table.sort(export)
local exportStr = table.concat(export, "\n")
importExportFrame:Show()
importExportFrameTextBox:SetText(exportStr)
importExportFrameTextBox:SetFocus()
end
---@param setName string
---@return nil
local function ImportKeybindSetDialogue(setName)
if not setName or setName == "" then
print("Set name cannot be empty")
return
end
importingSet = setName
importExportFrameTextBox:SetText("")
importExportFrame:Show()
importExportFrameTextBox:SetFocus()
end
---@return nil
local function PrintKeybindUsage()
print("Keybind Saver Slash Commands:")
print("/kbs save <set> - Saves your current keybinds under the given <set>")
print("/kbs restore <set> - Restores the saved <set>")
print("/kbs delete <set> - Deletes the saved <set>")
print("/kbs list - Lists all saved sets")
print("/kbs export <set> - Opens a window to export the given <set>")
print("/kbs import <set> - Opens a window to import a keybind set")
end
SLASH_KBS1 = "/kbs"
SlashCmdList["KBS"] = function(argv)
local args = { strsplit(" ", argv) }
local cmd = args[1]
if cmd == "save" then SaveKeybindSet(args[2]) end
if cmd == "restore" then RestoreKeybindSet(args[2]) end
if cmd == "delete" then DeleteKeybindSet(args[2]) end
if cmd == "list" then ListKeybindSets() end
if cmd == "export" then ExportKeybindSet(args[2]) end
if cmd == "import" then ImportKeybindSetDialogue(args[2]) end
if cmd == "" or not cmd then PrintKeybindUsage() end
end

View File

@@ -1,32 +1,21 @@
local ADDON_NAME, shared = ... local ADDON_NAME = ...
local frame = CreateFrame("Frame") local frame = CreateFrame("Frame")
frame:RegisterEvent("ADDON_LOADED") frame:RegisterEvent("ADDON_LOADED")
frame:SetScript("OnEvent", function(self, event, addon) frame:SetScript("OnEvent", function(self, event, addon)
if addon ~= ADDON_NAME then if addon ~= ADDON_NAME then return end
return
end
ActionBarSaverReloaded = ActionBarSaverReloaded or {} ---@class ActionBarSaverDaved
ActionBarSaverReloaded.spellAliases = ActionBarSaverReloaded.spellAliases or {} ---@field spellAliases table<number, number[]>
ActionBarSaverReloaded.sets = ActionBarSaverReloaded.sets or {} ---@field sets table<string, table<number, {type: string, id: number|string}>>
ActionBarSaverDaved = ActionBarSaverDaved or {}
ActionBarSaverDaved.spellAliases = ActionBarSaverDaved.spellAliases or {}
ActionBarSaverDaved.sets = ActionBarSaverDaved.sets or {}
---@class KeybindSaverDaved
---@field sets table<string, table<string, {key1: string, key2: string?}>>
KeybindSaverDaved = KeybindSaverDaved or {}
KeybindSaverDaved.sets = KeybindSaverDaved.sets or {}
end) end)
-- function ABS:OnInitialize() -- TODO: Fix equipment sets (saving reloading exporting importing...)
-- 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

1
Meta Submodule

Submodule Meta added at 9aae83307e

View File

@@ -1,16 +1,12 @@
# ActionBarSaver:Reloaded # ActionBarSaver:Daved
## Overview ## 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. ActionBarSaver:Daved is an addon for saving and restoring action bar profiles and keybindings. 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. 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. ## Action Bar Saver Usage
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 save <set>` - Saves your current action bar setup under the given <set>\
`/abs restore <set>` - Restores the saved <set>\ `/abs restore <set>` - Restores the saved <set>\
@@ -18,8 +14,56 @@ A new feature has been added for setting up aliases for spells. A common use cas
`/abs list` - Lists all saved sets\ `/abs list` - Lists all saved sets\
`/abs alias <spellID> <aliasID>` - Adds an alias with <aliasID> to <spellID>\ `/abs alias <spellID> <aliasID>` - Adds an alias with <aliasID> to <spellID>\
`/abs unalias <spellID>` - Removes all aliases associated with <spellID>\ `/abs unalias <spellID>` - Removes all aliases associated with <spellID>\
`/abs aliases` - List all spell aliases `/abs aliases` - List all spell aliases\
`/abs export <set>` - Opens a window to export the given <set>\
`/abs import <set>` - Opens a window to import an action bar set
## Keybind Saver Usage
The addon also includes a keybind saver that allows you to save and restore your keybindings. This is useful for maintaining different keybind setups for different specs or situations.
`/kbs save <set>` - Saves your current keybinds under the given <set>\
`/kbs restore <set>` - Restores the saved <set>\
`/kbs delete <set>` - Deletes the saved <set>\
`/kbs list` - Lists all saved sets\
`/kbs export <set>` - Opens a window to export the given <set>\
`/kbs import <set>` - Opens a window to import a keybind set
## Known Issues ## 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. * 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.
---
## Обзор
ActionBarSaver:Daved - это аддон для сохранения и восстановления профилей панели действий и привязок клавиш. Это полная переработка оригинального аддона ActionBarSaver.
Все наборы сохраняются по классу, а не по персонажу. Кроме того, при просмотре профилей вы будете видеть только профили, относящиеся к вашему классу.
## Использование ActionBarSaver
`/abs save <set>` - Сохраняет ваш текущий набор панели действий под данным <set>\
`/abs restore <set>` - Восстанавливает сохраненный <set>\
`/abs delete <set>` - Удаляет сохраненный <set>\
`/abs list` - Списывает все сохраненные наборы\
`/abs alias <spellID> <aliasID>` - Добавляет псевдоним с <aliasID> к <spellID>\
`/abs unalias <spellID>` - Удаляет все псевдонимы, связанные с <spellID>\
`/abs aliases` - Списывает все псевдонимы заклинаний\
`/abs export <set>` - Открывает окно для экспорта данного <set>\
`/abs import <set>` - Открывает окно для импорта набора панели действий
## Использование KeybindSaver
Аддон также включает в себя сохранитель привязок, который позволяет сохранять и восстанавливать ваши привязки. Это полезно для поддержания различных настроек привязок для разных специализаций или ситуаций.
`/kbs save <set>` - Сохраняет ваши текущие привязки под данным <set>\
`/kbs restore <set>` - Восстанавливает сохраненный <set>\
`/kbs delete <set>` - Удаляет сохраненный <set>\
`/kbs list` - Списывает все сохраненные наборы\
`/kbs export <set>` - Открывает окно для экспорта данного <set>\
`/kbs import <set>` - Открывает окно для импорта набора привязок
## Известные проблемы
* Псевдонимы не работают в обе стороны. Если вы дадите псевдоним `Berserking` как `War Stomp` и затем сохраните набор, содержащий `Berserking`, он будет работать должным образом, если вы восстановите этот набор на таурене. Однако, если вы сохраните набор, содержащий `War Stomp`, и попытаетесь восстановить его на тролле, он не сработает. Это будет исправлено в будущей версии.

View File

@@ -1,31 +0,0 @@
## 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

View File

@@ -1,5 +1,5 @@
mkdir ActionBarSaverReloaded mkdir ABS
cp -r *.lua ActionBarSaverReloaded cp -r *.lua ABS
cp -r *.toc ActionBarSaverReloaded cp -r *.toc ABS
7z a ActionBarSaverReloaded.zip ActionBarSaverReloaded 7z a ABS.zip ABS
rm -rf ActionBarSaverReloaded rm -rf ABS

57
release.sh Normal file
View File

@@ -0,0 +1,57 @@
#!/bin/bash
echo "Figuring out the tag..."
TAG=$(git describe --tags --exact-match 2>/dev/null || echo "")
if [ -z "$TAG" ]; then
# Get the latest tag
LATEST_TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
# Increment the patch version
IFS='.' read -r -a VERSION_PARTS <<< "$LATEST_TAG"
VERSION_PARTS[2]=$((VERSION_PARTS[2]+1))
TAG="${VERSION_PARTS[0]}.${VERSION_PARTS[1]}.${VERSION_PARTS[2]}"
# Create a new tag
git tag $TAG
fi
echo "Tag: $TAG"
echo "Building the thing..."
sed -i "s/## Version: .*/## Version: $TAG/" ActionBarSaverDaved.toc
git add ActionBarSaverDaved.toc
git commit -m "Release $TAG"
git tag -f $TAG
git push origin $TAG
rm ActionBarSaverDaved-${TAG}.zip
mkdir ActionBarSaverDaved
cp *.lua *.toc ActionBarSaverDaved
7z a ActionBarSaverDaved-${TAG}.zip ActionBarSaverDaved
rm -rf ActionBarSaverDaved
echo "Creating a release..."
TOKEN="$GITEA_API_KEY"
GITEA="https://git.site.quack-lab.dev"
REPO="dave/wow-ActionBarSaverDaved"
# Create a release
RELEASE_RESPONSE=$(curl -s -X POST \
-H "Authorization: token $TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"tag_name": "'"$TAG"'",
"name": "'"$TAG"'",
"draft": false,
"prerelease": false
}' \
$GITEA/api/v1/repos/$REPO/releases)
# Extract the release ID
echo $RELEASE_RESPONSE
RELEASE_ID=$(echo $RELEASE_RESPONSE | awk -F'"id":' '{print $2+0; exit}')
echo "Release ID: $RELEASE_ID"
echo "Uploading the things..."
curl -X POST \
-H "Authorization: token $TOKEN" \
-F "attachment=@ActionBarSaverDaved-${TAG}.zip" \
"$GITEA/api/v1/repos/$REPO/releases/${RELEASE_ID}/assets?name=ActionBarSaverDaved-${TAG}.zip"
rm ActionBarSaverDaved-${TAG}.zip

12
stylua.toml Symbolic link
View File

@@ -0,0 +1,12 @@
syntax = "All" # Specify a disambiguation for the style of Lua syntax being formatted. Possible options: All (default), Lua51, Lua52, Lua53, Lua54, LuaJIT, Luau, CfxLua
column_width = 120 # Approximate line length for printing. Used as a guide for line wrapping - this is not a hard requirement: lines may fall under or over the limit.
line_endings = "Windows" # Line endings type. Possible options: Unix (LF) or Windows (CRLF)
indent_type = "Tabs" # Indent type. Possible options: Tabs or Spaces
indent_width = 4 # Character size of single indentation. If indent_type is set to Tabs, this option is used as a heuristic to determine column width only.
quote_style = "AutoPreferDouble" # Quote style for string literals. Possible options: AutoPreferDouble, AutoPreferSingle, ForceDouble, ForceSingle. AutoPrefer styles will prefer the specified quote style, but fall back to the alternative if it has fewer string escapes. Force styles always use the specified style regardless of escapes.
call_parentheses = "Always" # Whether parentheses should be applied on function calls with a single string/table argument. Possible options: Always, NoSingleString, NoSingleTable, None, Input. Always applies parentheses in all cases. NoSingleString omits parentheses on calls with a single string argument. Similarly, NoSingleTable omits parentheses on calls with a single table argument. None omits parentheses in both cases. Note: parentheses are still kept in situations where removal can lead to obscurity (e.g. foo "bar".setup -> foo("bar").setup, since the index is on the call result, not the string). Input removes all automation and preserves parentheses only if they were present in input code: consistency is not enforced.
space_after_function_names = "Never" # Specify whether to add a space between the function name and parentheses. Possible options: Never, Definitions, Calls, or Always
collapse_simple_statement = "Always" # Specify whether to collapse simple statements. Possible options: Never, FunctionOnly, ConditionalOnly, or Always
[sort_requires]
enabled = false