Compare commits

...

5 Commits

Author SHA1 Message Date
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
13 changed files with 722 additions and 439 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"
]
}

View File

@@ -3,9 +3,10 @@
## Version: 1.0.7
## Notes: Manage, save, and restore action bar profiles
## Author: Voodoomoose
## SavedVariables: ActionBarSaverReloaded
## SavedVariables: ActionBarSaverReloaded, KeybindSaverReloaded
B64.lua
Constants.lua
Main.lua
Actions.lua
KeybindSaver.lua

BIN
ActionBarSaverReloaded.zip (Stored with Git LFS)

Binary file not shown.

View File

@@ -6,7 +6,7 @@ local typeMap = {
macro = "M",
companion = "C",
equipmentset = "E",
summonmount = "U"
summonmount = "U",
}
local inverseTypeMap = {
S = "spell",
@@ -14,7 +14,7 @@ local inverseTypeMap = {
M = "macro",
C = "companion",
E = "equipmentset",
U = "summonmount"
U = "summonmount",
}
local function PickupEquipmentSet(setName)
@@ -23,9 +23,7 @@ local function PickupEquipmentSet(setName)
for i = 1, C_EquipmentSet.GetNumEquipmentSets() do
local sn = C_EquipmentSet.GetEquipmentSetInfo(i)
if sn == setName then
setIndex = i
end
if sn == setName then setIndex = i end
end
C_EquipmentSet.PickupEquipmentSet(setIndex)
@@ -37,7 +35,7 @@ local pickupActionButton = {
macro = PickupMacro,
companion = PickupSpell,
equipmentset = PickupEquipmentSet,
summonmount = C_MountJournal.Pickup
summonmount = C_MountJournal.Pickup,
}
local function RestoreActionButton(self, index, actionButton)
@@ -46,9 +44,7 @@ local function RestoreActionButton(self, index, actionButton)
ClearCursor()
end
if not actionButton then
return true, nil
end
if not actionButton then return true, nil end
local aliases = ActionBarSaverReloaded.spellAliases[actionButton.id] or {}
table.insert(aliases, actionButton.id)
@@ -67,9 +63,7 @@ local function RestoreActionButton(self, index, actionButton)
return false
end
local function IsMacro(actionButton)
return actionButton and actionButton.type == "macro"
end
local function IsMacro(actionButton) return actionButton and actionButton.type == "macro" end
local function GetMacroDuplicates()
local t = {}
@@ -92,8 +86,10 @@ local function GetMacroDuplicates()
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))
table.insert(
warnings,
string.format("Warning: Found %d macros named '%s'. Consider renaming them to avoid issues", usages, macroName)
)
end
function SaveSet(setName)
@@ -112,17 +108,13 @@ function SaveSet(setName)
if type == "macro" then
-- use macro name as the ID
id = GetMacroInfo(id)
if duplicates[id] then
AddWarning(warnings, id, duplicates[id])
end
if duplicates[id] then AddWarning(warnings, id, duplicates[id]) end
end
if type and id then
set[i] = type and {
if type and id then set[i] = type and {
type = type,
id = id
}
end
id = id,
} end
end
ActionBarSaverReloaded.sets[setName] = set
@@ -164,12 +156,25 @@ function RestoreSet(setName)
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))
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))
table.insert(
messages,
string.format(
"Info: Restored spell %d (%s) in place of spell %d",
restoredID,
GetSpellInfo(restoredID),
actionButton.id
)
)
end
end
@@ -260,9 +265,12 @@ function ListAliases()
return
end
Dict.iter(ActionBarSaverReloaded.spellAliases, function(spellID, aliases)
Dict.iter(
ActionBarSaverReloaded.spellAliases,
function(spellID, aliases)
print(string.format("Spell %d is aliased by: %s", spellID, table.concat(aliases, ", ")))
end)
end
)
end
---@param text string
@@ -282,16 +290,12 @@ local function Partition(text, size, deliminer)
if #currentChunk + #word + 1 <= size then
currentChunk = currentChunk .. deliminer .. word
else
if #currentChunk > 0 then
ret[#ret + 1] = currentChunk
end
if #currentChunk > 0 then ret[#ret + 1] = currentChunk end
currentChunk = word
end
end
if #currentChunk > 0 then
ret[#ret + 1] = currentChunk
end
if #currentChunk > 0 then ret[#ret + 1] = currentChunk end
return ret
end
@@ -314,8 +318,8 @@ importExportFrame:SetBackdrop({
left = 4,
right = 4,
top = 4,
bottom = 4
}
bottom = 4,
},
})
importExportFrame:SetBackdropColor(0, 0, 0, 0.8)
importExportFrame:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
@@ -323,15 +327,9 @@ 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: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 importExportFrameTextBox = CreateFrame("EditBox", "ABSImportExportFrameTextBox", importExportFrame)
@@ -346,12 +344,10 @@ importExportFrameTextBox:SetMaxLetters(1000000)
importExportFrameTextBox:SetScript("OnEscapePressed", function(self)
importExportFrame:Hide()
if importingSet then
local lines = {strsplit("\n", self:GetText())}
local lines = { strsplit("\n", self:GetText()) }
for _, line in ipairs(lines) do
line = strtrim(line)
if line ~= "" then
ImportSet(importingSet, line)
end
if line ~= "" then ImportSet(importingSet, line) end
end
importingSet = nil
end
@@ -371,13 +367,11 @@ function ExportSet(setName)
print(string.format("Unknown action type '%s' in set '%s'", action.type, setName))
return
end
stringified[#stringified + 1] = string.format("%s\\%s\\%s", tostring(slot), tostring(action.id),
tostring(typeChar))
stringified[#stringified + 1] =
string.format("%s\\%s\\%s", tostring(slot), tostring(action.id), tostring(typeChar))
if typeChar == "M" then
local _, _, macro = GetMacroInfo(action.id)
if macro then
macros[action.id] = macro
end
if macro then macros[action.id] = macro end
end
end
@@ -394,9 +388,7 @@ function ExportSet(setName)
end
function ParseAction(action)
if not action or action == "" then
return nil
end
if not action or action == "" then return nil end
action = strtrim(action)
local slot, id, typeChar = string.match(action, "([^\\]+)\\([^\\]+)\\([^\\]+)")
if not typeChar then
@@ -424,7 +416,7 @@ function ParseAction(action)
return {
slot = slot,
id = id,
type = type
type = type,
}
end
@@ -470,15 +462,13 @@ function ImportSet(setName, str)
-- end
str = strtrim(str)
local data = {strsplit("ž", str)}
local data = { strsplit("ž", str) }
for _, action in ipairs(data) do
local paction = ParseAction(action)
if paction then
set[paction.slot] = {
if paction then set[paction.slot] = {
type = paction.type,
id = paction.id
}
end
id = paction.id,
} end
end
-- /dump ActionBarSaverReloaded.sets["havoc"]
@@ -512,39 +502,19 @@ function PrintUsage()
end
SlashCmdList["ABS"] = function(argv)
local args = {strsplit(" ", 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 == "export" then
ExportSet(args[2])
end
if cmd == "import" then
ImportSetDialogue(args[2])
end
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 == "export" then ExportSet(args[2]) end
if cmd == "import" then ImportSetDialogue(args[2]) end
if cmd == "" or not cmd then
PrintUsage()
end
if cmd == "" or not cmd then PrintUsage() end
end
SLASH_ABS1 = "/abs"

12
B64.lua
View File

@@ -1,11 +1,9 @@
if not B64 then
B64 = {}
end
if not B64 then B64 = {} end
local encode, decode = {}, {
[strbyte("=")] = false
[strbyte("=")] = false,
}
for value = 0, 63 do
local char = strsub('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', value + 1, value + 1)
local char = strsub("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", value + 1, value + 1)
encode[value] = char
decode[strbyte(char)] = value
end
@@ -26,9 +24,7 @@ end
function B64.Decode(str)
local j = 1
if strlen(str) % 4 ~= 0 then
str = str .. string.rep("=", 4 - strlen(str) % 4)
end
if strlen(str) % 4 ~= 0 then str = str .. string.rep("=", 4 - strlen(str) % 4) end
assert(strlen(str) % 4 == 0, format("invalid data length: %d", strlen(str)))
for i = 1, strlen(str), 4 do
local ba, bb, bc, bd = strbyte(str, i, i + 3)

227
KeybindSaver.lua Normal file
View File

@@ -0,0 +1,227 @@
local ADDON_NAME, shared = ...
-- 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
KeybindSaverReloaded = KeybindSaverReloaded or {}
KeybindSaverReloaded.sets = KeybindSaverReloaded.sets or {}
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 ImportSet(importingSet, text) end
importingSet = nil
end
end)
local function SaveSet(setName)
if not setName or setName == "" then
print("Set name cannot be empty")
return
end
local set = {}
local numBindings = GetNumBindings()
for i = 1, numBindings do
local command, category, key1, key2 = GetBinding(i)
if key1 or key2 then set[command] = {
key1 = key1,
key2 = key2,
} end
end
KeybindSaverReloaded.sets[setName] = set
print(string.format("Saved keybind set '%s'!", setName))
end
local function RestoreSet(setName)
if not setName or setName == "" then
print("Set name cannot be empty")
return
end
local set = KeybindSaverReloaded.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
if keys.key1 then SetBinding(keys.key1, command) end
if keys.key2 then SetBinding(keys.key2, command) end
end
-- Save the changes
SaveBindings(GetCurrentBindingSet())
print(string.format("Restored keybind set '%s'", setName))
end
local function DeleteSet(setName)
if not setName or setName == "" then
print("Set name cannot be empty")
return
end
if not KeybindSaverReloaded.sets[setName] then
print(string.format("No set with the name '%s' exists", setName))
return
end
KeybindSaverReloaded.sets[setName] = nil
print(string.format("Deleted keybind set '%s'", setName))
end
local function ListSets()
local sets = {}
for setName in pairs(KeybindSaverReloaded.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
local function ExportSet(setName)
local set = KeybindSaverReloaded.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 keys.key1 then table.insert(export, string.format("%s:%s", command, keys.key1)) end
if keys.key2 then table.insert(export, string.format("%s:%s", command, keys.key2)) end
end
local exportStr = table.concat(export, "\n")
importExportFrame:Show()
importExportFrameTextBox:SetText(exportStr)
importExportFrameTextBox:SetFocus()
end
local function ImportSet(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 = strsplit(":", line)
if command and key then
if not set[command] then set[command] = {} end
if not set[command].key1 then
set[command].key1 = key
else
set[command].key2 = key
end
end
end
KeybindSaverReloaded.sets[setName] = set
print(string.format("Imported keybind set '%s'", setName))
end
local function ImportSetDialogue(setName)
if not setName or setName == "" then
print("Set name cannot be empty")
return
end
importingSet = setName
importExportFrameTextBox:SetText("")
importExportFrame:Show()
importExportFrameTextBox:SetFocus()
end
local function PrintUsage()
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
-- Register slash commands
SLASH_KBS1 = "/kbs"
SlashCmdList["KBS"] = function(argv)
local args = { strsplit(" ", argv) }
local cmd = args[1]
if cmd == "save" then
SaveSet(args[2])
elseif cmd == "restore" then
RestoreSet(args[2])
elseif cmd == "delete" then
DeleteSet(args[2])
elseif cmd == "list" then
ListSets()
elseif cmd == "export" then
ExportSet(args[2])
elseif cmd == "import" then
ImportSetDialogue(args[2])
else
PrintUsage()
end
end

View File

@@ -3,9 +3,7 @@ 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
if addon ~= ADDON_NAME then return end
ActionBarSaverReloaded = ActionBarSaverReloaded or {}
ActionBarSaverReloaded.spellAliases = ActionBarSaverReloaded.spellAliases or {}

1
Meta Submodule

Submodule Meta added at 90f71c5c56

61
release.sh Normal file
View File

@@ -0,0 +1,61 @@
#!/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/" Heimdall.toc
sed -i "s/local VERSION = .*/local VERSION = \"$TAG\"/" Heimdall.lua
git add Heimdall.toc Heimdall.lua
git commit -m "Release $TAG"
git tag -f $TAG
git push origin $TAG
rm Heimdall-${TAG}.zip
mkdir Heimdall
cp *.lua *.toc Heimdall
cp -r Modules Heimdall
cp -r Sounds Heimdall
cp -r Texture Heimdall
7z a Heimdall-${TAG}.zip Heimdall
rm -rf Heimdall
echo "Creating a release..."
TOKEN="$GITEA_API_KEY"
GITEA="https://git.site.quack-lab.dev"
REPO="dave/wow-ABS"
# 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=@Heimdall-${TAG}.zip" \
"$GITEA/api/v1/repos/$REPO/releases/${RELEASE_ID}/assets?name=Heimdall-${TAG}.zip"
rm Heimdall-${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