Add tested modules and organize Extras categories

This commit is contained in:
2026-08-30 16:05:21 +02:00
parent 2c594d2506
commit 531b3feefb
9 changed files with 1141 additions and 13 deletions
+27 -6
View File
@@ -39,12 +39,19 @@ Settings: **Esc → Advanced Options**.
- Reagent Counter now uses ClassicAPI spell reagent and item count data instead of tooltip scanning.
- Reveal World Map now uses live ClassicAPI map overlay data instead of bundled static map tables.
- Better Turtle WoW-like server compatibility for items, auras and UI behavior.
- Added Auction Alt-Buy and Buy Em All as optional merchant and auction conveniences.
- Added modifier-aware Key-Down Casting with an independent Alt Self-Cast option.
- Added Metric Range for metre labels in range tooltips without changing numeric values.
- Various stability fixes across legacy ShaguTweaks Extras modules.
## 🔷 Mods using ClassicAPI integration
- Auction Alt-Buy
- Bag Item Click
- Bag Search Bar
- Buy Em All
- Key-Down Casting
- Alt Self-Cast
- Macro Icons
- Macro Tweaks
- Raid Frames
@@ -66,6 +73,26 @@ Other modules can also benefit indirectly from ClassicAPI through shared ShaguTw
- Reagent Counter
- Show Bags
- Show Micro Menu
- Key-Down Casting
- Alt Self-Cast
### Bags & Inventory
- Bag Item Click
- Bag Search Bar
### Merchant & Auction
- Auction Alt-Buy
- Buy Em All
### Tooltip & Items
- Metric Range
### World Map
- Reveal World Map
### Chat
@@ -73,12 +100,6 @@ Other modules can also benefit indirectly from ClassicAPI through shared ShaguTw
- Center Text Input Box
- Enable Text Shadow
### Bags & World Map
- Bag Item Click
- Bag Search Bar
- Reveal World Map
### Macro
- Macro Icons
+19 -7
View File
@@ -18,17 +18,33 @@ locales\translations_zhTW.lua
# core
main.lua
# general
mods\macro-icons.lua
mods\macro-tweaks.lua
# action bar
mods\actionbar-df-gryphon.lua
mods\actionbar-right-center.lua
mods\actionbar-reagents.lua
mods\actionbar-float.lua
mods\classic-snowfall.lua
mods\reduced-actionbar-bags.lua
mods\reduced-actionbar-micromenu.lua
# bags & inventory
mods\bag-item-click.lua
mods\bag-search.lua
# merchant & auction
mods\auction-alt-buy.lua
mods\buy-em-all.lua
# tooltip & items
mods\metric-range.lua
# world map
mods\worldmap-reveal.lua
# macro
mods\macro-icons.lua
mods\macro-tweaks.lua
# chat
mods\chat-input-center.lua
mods\chat-text-shadow.lua
@@ -44,7 +60,3 @@ mods\raid-aggro-indicator.lua
mods\raid-healing-predictions.lua
mods\raid-compact-layout.lua
mods\raid-hide-party.lua
# reduced actionbar size
mods\reduced-actionbar-bags.lua
mods\reduced-actionbar-micromenu.lua
+65
View File
@@ -0,0 +1,65 @@
local _G = ShaguTweaks.GetGlobalEnv()
local T = ShaguTweaks.T
local API = ShaguTweaks.API
local hooksecurefunc = ShaguTweaks.hooksecurefunc
local module = ShaguTweaks:register({
title = T["Auction Alt-Buy"],
description = T["Alt-click an auction to buy it out instantly, or Alt-click your own auction to cancel it."],
expansions = { ["vanilla"] = true },
category = T["Merchant & Auction"],
enabled = true,
})
module.enable = function(self)
-- Do not double-hook when migrating from the standalone addon.
if IsAddOnLoaded("AuctionAltBuy") then return end
local installed
local function AltDown()
return API and API.IsAltKeyDown and API.IsAltKeyDown()
end
local function InstallHooks()
if installed then return end
if type(_G.BrowseButton_OnClick) ~= "function"
or type(_G.AuctionsButton_OnClick) ~= "function" then
return
end
-- Run after the native handler so the clicked row has already become the
-- selected auction. Read the selected row itself instead of trusting a
-- cached frame-level buyout value.
hooksecurefunc("BrowseButton_OnClick", function()
if not AltDown() then return end
if not AuctionFrame or not AuctionFrame.type then return end
local index = GetSelectedAuctionItem(AuctionFrame.type)
if not index or index <= 0 then return end
local _, _, _, _, _, _, _, _, buyout = GetAuctionItemInfo(AuctionFrame.type, index)
if buyout and buyout > 0 then
PlaceAuctionBid(AuctionFrame.type, index, buyout)
end
end)
hooksecurefunc("AuctionsButton_OnClick", function()
if not AltDown() then return end
local index = GetSelectedAuctionItem("owner")
if index and index > 0 then
CancelAuction(index)
end
end)
installed = true
end
local frame = CreateFrame("Frame", nil, UIParent)
frame:RegisterEvent("AUCTION_HOUSE_SHOW")
frame:SetScript("OnEvent", InstallHooks)
-- Some 1.12 clients keep auction functions resident.
InstallHooks()
end
+1
View File
@@ -7,6 +7,7 @@ local module = ShaguTweaks:register({
title = T["Bag Item Click"],
description = T["Send items to trade window or auction house search via right click."],
expansions = { ["vanilla"] = true, ["tbc"] = nil },
category = T["Bags & Inventory"],
maintainer = "@shagu (GitHub)",
enabled = true,
})
+1
View File
@@ -6,6 +6,7 @@ local module = ShaguTweaks:register({
title = T["Bag Search Bar"],
description = T["Adds a search field to the bag which allows you to search bag, keyring and bank slots."],
expansions = { ["vanilla"] = true, ["tbc"] = nil },
category = T["Bags & Inventory"],
maintainer = "@shagu (GitHub)",
enabled = true,
})
+662
View File
@@ -0,0 +1,662 @@
local _G = ShaguTweaks.GetGlobalEnv()
local T = ShaguTweaks.T
local API = ShaguTweaks.API
local HookScript = ShaguTweaks.HookScript
local floor = math.floor
local ceil = math.ceil
local min = math.min
local mod = math.mod or mod
STBuyEmAll = STBuyEmAll or {}
local Buy = STBuyEmAll
local strings = {
enUS = {
["Max"] = "Max",
["Stack"] = "Stack",
["Are you sure you want to buy\n %d × %s?"] = "Are you sure you want to buy\n %d × %s?",
["Stack purchase"] = "Stack purchase",
["Stack size"] = "Stack size",
["Partial stack"] = "Partial stack",
["Maximum purchase"] = "Maximum purchase",
["You can fit"] = "You can fit",
["You can afford"] = "You can afford",
["Vendor has"] = "Vendor has",
},
deDE = {
["Are you sure you want to buy\n %d × %s?"] = "Bist du sicher das du\n %d × %s kaufen willst?",
["Stack size"] = "Stack größe",
["Maximum purchase"] = "Größt möglicher Einkauf",
["You can fit"] = "Du hast Platz für",
["You can afford"] = "Du kannst dir leisten",
["Vendor has"] = "Der Verkäufer hat",
},
frFR = {
["Stack"] = "Pile",
["Are you sure you want to buy\n %d × %s?"] = "Voulez-vous vraiment acheter\n %d × %s?",
["Stack size"] = "Taille de pile",
["Maximum purchase"] = "Achat maximum",
["You can fit"] = "Vous pouvez transporter",
["You can afford"] = "Vous pouvez payer",
["Vendor has"] = "Le marchand a",
},
}
local locale = strings[GetLocale()] or strings.enUS
local function L(key)
return locale[key] or strings.enUS[key] or key
end
BUYEMALL_MAX = L("Max")
BUYEMALL_STACK = L("Stack")
local module = ShaguTweaks:register({
title = T["Buy Em All"],
description = T["Shift-click vendor items to choose stack, maximum, or a custom purchase amount."],
expansions = { ["vanilla"] = true },
category = T["Merchant & Auction"],
enabled = true,
})
local function FamilyMatches(itemFamily, bagFamily)
bagFamily = tonumber(bagFamily) or 0
itemFamily = tonumber(itemFamily) or 0
if bagFamily == 0 then return true end
if itemFamily == 0 then return false end
-- Bag families are power-of-two bit flags. Avoid requiring an external bit
-- library on 1.12 while still supporting Turtle custom specialty families.
return mod(floor(itemFamily / bagFamily), 2) == 1
end
function Buy:CountPurchaseItem()
if not self.purchaseItemID then return nil end
if API and API.GetItemCount then
return API.GetItemCount(self.purchaseItemID, false, false)
end
return nil
end
function Buy:StopPurchaseQueue()
if self.purchaseFrame then self.purchaseFrame:Hide() end
self.purchaseItemIndex = nil
self.purchaseItemID = nil
self.purchasePreset = 1
self.purchaseLoops = 0
self.purchaseAmount = 0
self.purchaseLeftover = 0
self.purchaseElapsed = 0
self.purchaseWaiting = nil
self.purchaseExpectedCount = nil
self.purchasePendingKind = nil
end
function Buy:AbortPurchaseQueue()
self:StopPurchaseQueue()
DEFAULT_CHAT_FRAME:AddMessage("Buy Em All: purchase queue stopped because the previous stack was not confirmed. No further stacks were sent.")
end
function Buy:ConfirmPendingPurchase()
if not self.purchaseWaiting or not self.purchaseExpectedCount then return false end
local count = self:CountPurchaseItem()
if count == nil or count < self.purchaseExpectedCount then return false end
if self.purchasePendingKind == "loop" then
self.purchaseLoops = self.purchaseLoops - 1
elseif self.purchasePendingKind == "leftover" then
self.purchaseLeftover = 0
end
self.purchaseWaiting = nil
self.purchaseExpectedCount = nil
self.purchasePendingKind = nil
self.purchaseElapsed = 0
return true
end
function Buy:Purchase_OnEvent()
self:ConfirmPendingPurchase()
end
function Buy:SendPurchase(quantity, kind)
local before = self:CountPurchaseItem()
if before == nil then
self:AbortPurchaseQueue()
return
end
self.purchaseWaiting = true
self.purchasePendingKind = kind
self.purchaseExpectedCount = before + quantity * (self.purchasePreset or 1)
self.purchaseElapsed = 0
BuyMerchantItem(self.purchaseItemIndex, quantity)
end
function Buy:Purchase_OnUpdate(elapsed)
self.purchaseElapsed = (self.purchaseElapsed or 0) + (elapsed or 0)
if self.purchaseWaiting then
if self.purchaseElapsed >= self.purchaseTimeout then
if not self:ConfirmPendingPurchase() then self:AbortPurchaseQueue() end
end
return
end
if self.purchaseElapsed < self.purchaseDelay then return end
self.purchaseElapsed = 0
if self.purchaseLoops and self.purchaseLoops > 0 then
self:SendPurchase(self.purchaseAmount, "loop")
return
end
if self.purchaseLeftover and self.purchaseLeftover > 0 then
self:SendPurchase(self.purchaseLeftover, "leftover")
return
end
self:StopPurchaseQueue()
end
function Buy:DoPurchase(amount)
if not amount or amount <= 0 or not self.itemIndex or not self.preset or not self.stack then return end
if STBuyEmAllFrame then STBuyEmAllFrame:Hide() end
local numLoops, purchaseAmount, leftover
if self.preset > 1 then
numLoops = floor(amount / self.preset)
purchaseAmount = 1
leftover = 0
else
numLoops = floor(amount / self.stack)
purchaseAmount = self.stack
leftover = mod(amount, self.stack)
end
self:StopPurchaseQueue()
self.purchaseItemIndex = self.itemIndex
self.purchaseItemID = self.itemID
self.purchasePreset = self.preset
self.purchaseLoops = numLoops
self.purchaseAmount = purchaseAmount
self.purchaseLeftover = leftover
self.purchaseElapsed = self.purchaseDelay
if self.purchaseLoops > 0 or self.purchaseLeftover > 0 then
self.purchaseFrame:Show()
end
end
function Buy:DoConfirmation(amount)
self.confirmAmount = amount
local dialog = StaticPopup_Show("STBUYEMALL_CONFIRM", amount, self.itemName)
if dialog then dialog.data = amount end
end
function Buy:VerifyPurchase(amount)
amount = amount or self.split
if not amount or amount <= 0 or not self.preset or self.preset <= 0 then return end
amount = ceil(amount / self.preset) * self.preset
if amount > self.max then amount = self.max end
if amount > self.stack and amount > self.defaultStack then
self:DoConfirmation(amount)
else
self:DoPurchase(amount)
end
end
function Buy:SetStackClick()
if not self.stack or self.stack <= 0 then
self.stackClick = self.max or self.split or 1
return
end
local targetRemainder = self.partialFit or 0
if targetRemainder == 0 then targetRemainder = self.stack end
local currentRemainder = mod(self.split, self.stack)
local increase = targetRemainder - currentRemainder
if increase <= 0 then increase = increase + self.stack end
self.stackClick = self.split + increase
end
function Buy:UpdateDisplay(amount)
if not self.preset or not self.price or not self.split then return end
local purchase = ceil((amount or self.split) / self.preset)
local cost = purchase * self.price
if STBuyEmAllMoneyFrame then
MoneyFrame_Update("STBuyEmAllMoneyFrame", cost)
end
if not amount and not self.isUpdating then
self.isUpdating = true
STBuyEmAllText:SetText(self.split)
STBuyEmAllLeftButton:Enable()
STBuyEmAllRightButton:Enable()
if self.split >= self.max then
STBuyEmAllRightButton:Disable()
elseif self.split <= self.preset then
STBuyEmAllLeftButton:Disable()
end
self:SetStackClick()
STBuyEmAllStackButton:Enable()
if not self.stackClick or self.max < self.stackClick then
STBuyEmAllStackButton:Disable()
end
self.isUpdating = nil
end
end
function Buy:Show(anchor)
self.typing = 0
STBuyEmAllLeftButton:Disable()
STBuyEmAllRightButton:Enable()
self:SetStackClick()
STBuyEmAllStackButton:Enable()
if not self.stackClick or self.max < self.stackClick then STBuyEmAllStackButton:Disable() end
STBuyEmAllFrame:ClearAllPoints()
STBuyEmAllFrame:SetPoint("BOTTOMLEFT", anchor, "TOPLEFT", 0, 4)
STBuyEmAllFrame:Show()
self:UpdateDisplay()
end
function Buy:OnClick()
if this == STBuyEmAllOkayButton then
self:VerifyPurchase()
elseif this == STBuyEmAllCancelButton then
STBuyEmAllFrame:Hide()
elseif this == STBuyEmAllStackButton then
if self.stackClick and self.stackClick <= self.max then
self.split = self.stackClick
self:UpdateDisplay()
if this:IsEnabled() == 1 then self:OnEnter() else GameTooltip:Hide() end
end
elseif this == STBuyEmAllMaxButton then
self.split = self.max
self:UpdateDisplay()
end
end
function Buy:OnChar()
local digit = tonumber(arg1)
if digit == nil then return end
if self.typing == 0 then
self.typing = 1
self.split = 0
end
local split = (self.split * 10) + digit
if split <= self.max then
self.split = split
end
if self.split <= 0 then self.split = 1 end
self:UpdateDisplay()
end
function Buy:OnKeyDown()
if arg1 == "BACKSPACE" or arg1 == "DELETE" then
if self.typing == 0 or self.split == 1 then return end
self.split = floor(self.split / 10)
if self.split <= 1 then
self.split = 1
self.typing = 0
end
self:UpdateDisplay()
elseif arg1 == "ENTER" then
self:VerifyPurchase()
elseif arg1 == "ESCAPE" then
STBuyEmAllFrame:Hide()
elseif arg1 == "LEFT" or arg1 == "DOWN" then
self:Left_Click()
elseif arg1 == "RIGHT" or arg1 == "UP" then
self:Right_Click()
end
end
function Buy:Left_Click()
if self.split <= self.preset then return end
local decrease = mod(self.split, self.preset)
decrease = decrease == 0 and self.preset or decrease
self.split = self.split - decrease
self:UpdateDisplay()
end
function Buy:Right_Click()
local increase = self.preset - mod(self.split, self.preset)
if self.split + increase > self.max then return end
self.split = self.split + increase
self:UpdateDisplay()
end
Buy.lines = {
stack = {
label = L("Stack purchase"),
field = "stackClick",
{ label = L("Stack size"), field = "stack" },
{ label = L("Partial stack"), field = "partialFit" },
},
max = {
label = L("Maximum purchase"),
field = "max",
{ label = L("You can fit"), field = "fit" },
{ label = L("You can afford"), field = "afford" },
{
label = L("Vendor has"),
field = "available",
Hide = function()
return not Buy.available or Buy.available <= 1
end,
},
},
}
function Buy:CreateTooltip(lines)
GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT")
GameTooltip:SetText(lines.label .. "|cFFFFFFFF - |r" .. GREEN_FONT_COLOR_CODE .. (lines.amount or 0) .. "|r")
for _, line in ipairs(lines) do
if not (line.Hide and line.Hide()) then
local color = line.amount == lines.amount and GREEN_FONT_COLOR or HIGHLIGHT_FONT_COLOR
GameTooltip:AddDoubleLine(line.label, line.amount or 0, 1, 1, 1, color.r, color.g, color.b)
end
end
GameTooltip:Show()
end
function Buy:OnEnter()
local lines = self.lines[this == STBuyEmAllMaxButton and "max" or "stack"]
lines.amount = self[lines.field]
for _, line in ipairs(lines) do line.amount = self[line.field] end
self:CreateTooltip(lines)
self:UpdateDisplay(lines.amount)
end
function Buy:OnLeave()
self:UpdateDisplay()
GameTooltip:Hide()
end
function Buy:OnHide()
StaticPopup_Hide("STBUYEMALL_CONFIRM")
end
function Buy:FreeBagSpace(itemID)
if not itemID then return 0, 1 end
local stackSize = API.GetItemMaxStackSizeByID(itemID) or 1
if stackSize < 1 then stackSize = 1 end
local itemFamily = API.GetItemFamily(itemID) or 0
local capacity = 0
for bag = 0, 4 do
local freeSlots, bagFamily = API.GetContainerNumFreeSlots(bag)
if FamilyMatches(itemFamily, bagFamily) then
capacity = capacity + (freeSlots or 0) * stackSize
end
local numSlots = GetContainerNumSlots(bag)
for slot = 1, numSlots do
local bagItemID = API.GetContainerItemID(bag, slot)
if bagItemID == itemID then
local itemCount = API.GetContainerItemStackCount(bag, slot) or 0
if itemCount < stackSize then
capacity = capacity + stackSize - itemCount
end
end
end
end
return capacity, stackSize
end
function Buy:GetMerchantData(index)
local info = API.GetMerchantItemInfo and API.GetMerchantItemInfo(index)
if info and info.itemID then
local name = API.GetItemNameByID(info.itemID)
if not name then
name = GetMerchantItemInfo(index)
end
return name, info.itemID, info.price or 0, info.stackCount or 1,
info.numAvailable or -1
end
local name, _, price, quantity, numAvailable = GetMerchantItemInfo(index)
local itemID = API.GetMerchantItemID and API.GetMerchantItemID(index)
return name, itemID, price, quantity, numAvailable
end
function Buy:HandleMerchantClick(anchor, button, ignoreModifiers)
if not anchor or not anchor.GetID then return false end
if MerchantFrame.selectedTab ~= 1
or not API.IsShiftKeyDown()
or API.IsControlKeyDown()
or ignoreModifiers
or (ChatFrameEditBox:IsVisible() and button == "LeftButton") then
return false
end
self.itemIndex = anchor:GetID()
local name, itemID, price, quantity, numAvailable = self:GetMerchantData(self.itemIndex)
if not name or not itemID or not price or not quantity or quantity < 1 then return false end
self.preset = quantity
self.price = price
self.itemName = name
self.itemID = itemID
self.available = numAvailable
local bagCapacity, stack = self:FreeBagSpace(itemID)
self.stack = stack
self.fit = floor(bagCapacity / quantity) * quantity
if price > 0 then
self.afford = floor(GetMoney() / price) * quantity
else
self.afford = self.fit
end
self.max = min(self.fit, self.afford)
if numAvailable and numAvailable > -1 then
self.max = min(self.max, numAvailable * quantity)
end
if self.max <= 0 then return true end
if self.max == 1 then
if self.originalMerchantItemButton_OnClick then
self.originalMerchantItemButton_OnClick("LeftButton", 1)
end
return true
end
self.defaultStack = quantity
if self.defaultStack > self.max then self.defaultStack = self.max end
self.split = self.defaultStack
self.partialFit = mod(self.fit, stack)
self:SetStackClick()
self:Show(anchor)
return true
end
function Buy:CreateUI()
if STBuyEmAllFrame then return end
local frame = CreateFrame("Frame", "STBuyEmAllFrame", MerchantFrame)
frame:SetWidth(210)
frame:SetHeight(110)
frame:SetFrameStrata("DIALOG")
frame:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 8, edgeSize = 16,
insets = { left = 4, right = 4, top = 4, bottom = 4 },
})
frame:SetBackdropColor(0, 0, 0, .9)
frame:EnableKeyboard(true)
frame:SetScript("OnChar", function() Buy:OnChar() end)
frame:SetScript("OnKeyDown", function() Buy:OnKeyDown() end)
frame:SetScript("OnHide", function() Buy:OnHide() end)
frame:Hide()
local title = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
title:SetPoint("TOP", frame, "TOP", 0, -10)
title:SetText(T["Buy Em All"])
local amount = frame:CreateFontString("STBuyEmAllText", "ARTWORK", "GameFontHighlight")
amount:SetWidth(50)
amount:SetPoint("TOP", frame, "TOP", 0, -29)
amount:SetText("1")
local left = CreateFrame("Button", "STBuyEmAllLeftButton", frame, "UIPanelButtonTemplate")
left:SetWidth(28)
left:SetHeight(22)
left:SetPoint("RIGHT", amount, "LEFT", -4, 0)
left:SetText("-")
left:SetScript("OnClick", function() Buy:Left_Click() end)
local right = CreateFrame("Button", "STBuyEmAllRightButton", frame, "UIPanelButtonTemplate")
right:SetWidth(28)
right:SetHeight(22)
right:SetPoint("LEFT", amount, "RIGHT", 4, 0)
right:SetText("+")
right:SetScript("OnClick", function() Buy:Right_Click() end)
local stack = CreateFrame("Button", "STBuyEmAllStackButton", frame, "UIPanelButtonTemplate")
stack:SetWidth(58)
stack:SetHeight(22)
stack:SetPoint("TOPLEFT", frame, "TOPLEFT", 12, -50)
stack:SetText(BUYEMALL_STACK)
stack:SetScript("OnClick", function() Buy:OnClick() end)
stack:SetScript("OnEnter", function() Buy:OnEnter() end)
stack:SetScript("OnLeave", function() Buy:OnLeave() end)
local maxButton = CreateFrame("Button", "STBuyEmAllMaxButton", frame, "UIPanelButtonTemplate")
maxButton:SetWidth(58)
maxButton:SetHeight(22)
maxButton:SetPoint("LEFT", stack, "RIGHT", 4, 0)
maxButton:SetText(BUYEMALL_MAX)
maxButton:SetScript("OnClick", function() Buy:OnClick() end)
maxButton:SetScript("OnEnter", function() Buy:OnEnter() end)
maxButton:SetScript("OnLeave", function() Buy:OnLeave() end)
local okay = CreateFrame("Button", "STBuyEmAllOkayButton", frame, "UIPanelButtonTemplate")
okay:SetWidth(58)
okay:SetHeight(22)
okay:SetPoint("LEFT", maxButton, "RIGHT", 4, 0)
okay:SetText(OKAY)
okay:SetScript("OnClick", function() Buy:OnClick() end)
local cancel = CreateFrame("Button", "STBuyEmAllCancelButton", frame, "UIPanelButtonTemplate")
cancel:SetWidth(58)
cancel:SetHeight(22)
cancel:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -12, -78)
cancel:SetText(CANCEL)
cancel:SetScript("OnClick", function() Buy:OnClick() end)
local money = CreateFrame("Frame", "STBuyEmAllMoneyFrame", frame, "SmallMoneyFrameTemplate")
money:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 12, 11)
MoneyTypeInfo["STBUYEMALL"] = {
UpdateFunc = function() return 0 end,
showSmallerCoins = "Backpack",
fixedWidth = 1,
collapse = 1,
}
money.info = MoneyTypeInfo["STBUYEMALL"]
money.moneyType = "STBUYEMALL"
money.small = 1
end
module.enable = function(self)
if IsAddOnLoaded("BuyEmAll") then return end
local required = {
"GetItemCount", "GetItemMaxStackSizeByID", "GetItemFamily",
"GetContainerNumFreeSlots", "GetContainerItemID",
"GetContainerItemStackCount", "GetMerchantItemInfo",
"GetMerchantItemID", "IsShiftKeyDown", "IsControlKeyDown",
}
for _, name in ipairs(required) do
if not API or type(API[name]) ~= "function" then
DEFAULT_CHAT_FRAME:AddMessage("|cffff5555ShaguTweaks Extras:|r Buy Em All requires the current ShaguTweaks-ClassicAPI.")
return
end
end
Buy:CreateUI()
Buy.purchaseDelay = 0.05
Buy.purchaseTimeout = 3.0
Buy.purchaseFrame = Buy.purchaseFrame or CreateFrame("Frame", "STBuyEmAllPurchaseQueue", UIParent)
Buy.purchaseFrame:Hide()
Buy.purchaseFrame:RegisterEvent("BAG_UPDATE")
Buy.purchaseFrame:RegisterEvent("MERCHANT_UPDATE")
Buy.purchaseFrame:SetScript("OnEvent", function()
Buy:Purchase_OnEvent()
end)
Buy.purchaseFrame:SetScript("OnUpdate", function()
Buy:Purchase_OnUpdate(arg1)
end)
StaticPopupDialogs["STBUYEMALL_CONFIRM"] = {
text = L("Are you sure you want to buy\n %d × %s?"),
button1 = YES,
button2 = NO,
OnAccept = function()
Buy:DoPurchase(Buy.confirmAmount)
end,
timeout = 0,
hideOnEscape = true,
}
HookScript(MerchantFrame, "OnHide", function()
STBuyEmAllFrame:Hide()
Buy:StopPurchaseQueue()
end)
local function MerchantItemButton_OnClick_Wrapper(button, ignoreModifiers)
local anchor = this
if Buy:HandleMerchantClick(anchor, button, ignoreModifiers) then return end
if Buy.originalMerchantItemButton_OnClick then
return Buy.originalMerchantItemButton_OnClick(button, ignoreModifiers)
end
end
local function InstallHook()
local current = _G.MerchantItemButton_OnClick
if type(current) ~= "function" then return end
if current == MerchantItemButton_OnClick_Wrapper then return end
Buy.originalMerchantItemButton_OnClick = current
_G.MerchantItemButton_OnClick = MerchantItemButton_OnClick_Wrapper
end
InstallHook()
local frame = CreateFrame("Frame", nil, UIParent)
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("MERCHANT_SHOW")
frame:SetScript("OnEvent", InstallHook)
end
+295
View File
@@ -0,0 +1,295 @@
local _G = ShaguTweaks.GetGlobalEnv()
local T = ShaguTweaks.T
local API = ShaguTweaks.API
local selfcastTitle = T["Alt Self-Cast"]
local selfcast = ShaguTweaks:register({
title = selfcastTitle,
description = T["When Key-Down Casting is enabled, hold Alt to cast eligible actions on yourself."],
expansions = { ["vanilla"] = true },
category = T["Action Bar"],
enabled = nil,
})
selfcast.enable = function(self)
-- Runtime behavior is handled by Key-Down Casting below.
end
local module = ShaguTweaks:register({
title = T["Key-Down Casting"],
description = T["Activates action-bar and pet actions when the key is pressed instead of when it is released."],
expansions = { ["vanilla"] = true },
category = T["Action Bar"],
enabled = true,
})
module.enable = function(self)
-- Keep this feature independent from No Toggle Auto-Attack.
if IsAddOnLoaded("ClassicSnowFall") or IsAddOnLoaded("ClassicSnowfall") then return end
local function SelfCast()
if ShaguTweaks_config and ShaguTweaks_config[selfcastTitle] == 1 then
return API.IsAltKeyDown() and 1 or 0
end
return 0
end
-- Preserve the original slash command while using a unique registry key.
SLASH_STCLASSICSNOWFALLSELFCAST1 = "/csselfcast"
SlashCmdList["STCLASSICSNOWFALLSELFCAST"] = function()
ShaguTweaks_config = ShaguTweaks_config or {}
if ShaguTweaks_config[selfcastTitle] == 1 then
ShaguTweaks_config[selfcastTitle] = 0
DEFAULT_CHAT_FRAME:AddMessage("Classic Snowfall ALT SelfCast now disabled.")
else
ShaguTweaks_config[selfcastTitle] = 1
DEFAULT_CHAT_FRAME:AddMessage("Classic Snowfall ALT SelfCast now enabled.")
end
end
local function IsBongosReady()
return _G.BActionButton and type(_G.BActionButton.GetPagedID) == "function"
end
local function ST_ActionButtonDown(id)
local button, pagedID
if IsBongosReady() then
button = _G["BActionButton" .. id]
pagedID = _G.BActionButton.GetPagedID(id)
if button and button:GetButtonState() == "NORMAL" then
button:SetButtonState("PUSHED")
end
if pagedID then UseAction(pagedID, 0, SelfCast()) end
return
end
if BonusActionBarFrame and BonusActionBarFrame:IsShown() then
button = _G["BonusActionButton" .. id]
else
button = _G["ActionButton" .. id]
end
if button and button:GetButtonState() == "NORMAL" then
button:SetButtonState("PUSHED")
UseAction(ActionButton_GetPagedID(button), 0, SelfCast())
end
end
local function ST_ActionButtonUp(id)
local button, pagedID
if IsBongosReady() then
button = _G["BActionButton" .. id]
pagedID = _G.BActionButton.GetPagedID(id)
if button and button:GetButtonState() == "PUSHED" then
button:SetButtonState("NORMAL")
if MacroFrame_SaveMacro then MacroFrame_SaveMacro() end
if pagedID and IsCurrentAction(pagedID) then
button:SetChecked(1)
else
button:SetChecked(0)
end
end
return
end
if BonusActionBarFrame and BonusActionBarFrame:IsShown() then
button = _G["BonusActionButton" .. id]
else
button = _G["ActionButton" .. id]
end
if button and button:GetButtonState() == "PUSHED" then
button:SetButtonState("NORMAL")
if MacroFrame_SaveMacro then MacroFrame_SaveMacro() end
if IsCurrentAction(ActionButton_GetPagedID(button)) then
button:SetChecked(1)
else
button:SetChecked(0)
end
end
end
local function ST_MultiActionButtonDown(bar, id)
local button = _G[bar .. "Button" .. id]
if button and button:GetButtonState() == "NORMAL" then
button:SetButtonState("PUSHED")
UseAction(ActionButton_GetPagedID(button), 0, SelfCast())
end
end
local function ST_MultiActionButtonUp(bar, id)
local button = _G[bar .. "Button" .. id]
if button and button:GetButtonState() == "PUSHED" then
button:SetButtonState("NORMAL")
if MacroFrame_SaveMacro then MacroFrame_SaveMacro() end
if IsCurrentAction(ActionButton_GetPagedID(button)) then
button:SetChecked(1)
else
button:SetChecked(0)
end
end
end
local function ST_PetActionButtonDown(id)
local button = _G["PetActionButton" .. id]
if button and button:GetButtonState() == "NORMAL" then
button:SetButtonState("PUSHED")
CastPetAction(id)
end
end
local function ST_PetActionButtonUp(id)
local button = _G["PetActionButton" .. id]
if button and button:GetButtonState() == "PUSHED" then
button:SetButtonState("NORMAL")
end
end
-- Vanilla's BONUSACTIONBUTTON bindings are aliases for the pet-action
-- handlers. Keep the same relationship while moving the cast to key-down.
local function ST_BonusActionButtonDown(id)
ST_PetActionButtonDown(id)
end
local function ST_BonusActionButtonUp(id)
ST_PetActionButtonUp(id)
end
local function Install()
_G.ActionButtonDown = ST_ActionButtonDown
_G.ActionButtonUp = ST_ActionButtonUp
_G.MultiActionButtonDown = ST_MultiActionButtonDown
_G.MultiActionButtonUp = ST_MultiActionButtonUp
_G.PetActionButtonDown = ST_PetActionButtonDown
_G.PetActionButtonUp = ST_PetActionButtonUp
if type(_G.BonusActionButtonDown) == "function" then
_G.BonusActionButtonDown = ST_BonusActionButtonDown
end
if type(_G.BonusActionButtonUp) == "function" then
_G.BonusActionButtonUp = ST_BonusActionButtonUp
end
end
local function HandlersAreInstalled()
return _G.ActionButtonDown == ST_ActionButtonDown
and _G.ActionButtonUp == ST_ActionButtonUp
and _G.MultiActionButtonDown == ST_MultiActionButtonDown
and _G.MultiActionButtonUp == ST_MultiActionButtonUp
and _G.PetActionButtonDown == ST_PetActionButtonDown
and _G.PetActionButtonUp == ST_PetActionButtonUp
end
-- Modified bindings (SHIFT-*, CTRL-*, ALT-*) are routed through ClassicAPI's
-- normalized override-binding layer. Vanilla's runOnUp binding path is not
-- reliable for every modified-key combination on custom 1.12 clients.
-- Normal unmodified keys continue through the original ClassicSnowfall-style
-- global handlers above.
local bindingOwner = CreateFrame("Frame", "ShaguTweaksKeyDownBindingOwner", UIParent)
local proxies = {}
local specs = {}
local function AddSpec(command, kind, a, b)
table.insert(specs, { command = command, kind = kind, a = a, b = b })
end
for i = 1, 12 do
AddSpec("ACTIONBUTTON" .. i, "action", i)
AddSpec("MULTIACTIONBAR1BUTTON" .. i, "multi", "MultiBarBottomLeft", i)
AddSpec("MULTIACTIONBAR2BUTTON" .. i, "multi", "MultiBarBottomRight", i)
AddSpec("MULTIACTIONBAR3BUTTON" .. i, "multi", "MultiBarRight", i)
AddSpec("MULTIACTIONBAR4BUTTON" .. i, "multi", "MultiBarLeft", i)
AddSpec("BONUSACTIONBUTTON" .. i, "bonus", i)
end
local function IsModifiedKey(key)
if type(key) ~= "string" then return false end
local upper = string.upper(key)
return string.find(upper, "SHIFT-", 1, true)
or string.find(upper, "CTRL-", 1, true)
or string.find(upper, "ALT-", 1, true)
end
local function GetProxy(spec, index)
local name = "ShaguTweaksKeyDownProxy" .. index
if proxies[index] then return proxies[index] end
local button = CreateFrame("Button", name, bindingOwner)
button.spec = spec
button:SetScript("OnClick", function()
local s = this.spec
if s.kind == "action" then
ST_ActionButtonDown(s.a)
ST_ActionButtonUp(s.a)
elseif s.kind == "multi" then
ST_MultiActionButtonDown(s.a, s.b)
ST_MultiActionButtonUp(s.a, s.b)
elseif s.kind == "bonus" then
ST_BonusActionButtonDown(s.a)
ST_BonusActionButtonUp(s.a)
end
end)
proxies[index] = button
return button
end
local refreshingBindings
local function RefreshModifiedBindings()
if refreshingBindings then return end
if not API or not API.overridebindings then return end
refreshingBindings = true
API.ClearOverrideBindings(bindingOwner)
for index, spec in ipairs(specs) do
local key1, key2 = GetBindingKey(spec.command)
local proxy = GetProxy(spec, index)
if IsModifiedKey(key1) then
API.SetOverrideBindingClick(bindingOwner, true, key1, proxy:GetName())
end
if IsModifiedKey(key2) then
API.SetOverrideBindingClick(bindingOwner, true, key2, proxy:GetName())
end
end
refreshingBindings = nil
end
Install()
RefreshModifiedBindings()
local frame = CreateFrame("Frame", nil, UIParent)
frame:RegisterEvent("PLAYER_LOGIN")
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("UPDATE_BINDINGS")
frame.elapsed = 0
frame.guardUntil = 0
local function GuardOnUpdate()
if not frame.guardUntil or frame.guardUntil <= GetTime() then
frame:SetScript("OnUpdate", nil)
return
end
frame.elapsed = frame.elapsed + (arg1 or 0)
if frame.elapsed < 0.25 then return end
frame.elapsed = 0
if not HandlersAreInstalled() then Install() end
end
frame:SetScript("OnEvent", function()
Install()
RefreshModifiedBindings()
if event == "PLAYER_LOGIN" or event == "PLAYER_ENTERING_WORLD" then
frame.guardUntil = GetTime() + 8
frame.elapsed = 0
frame:SetScript("OnUpdate", GuardOnUpdate)
end
end)
end
+70
View File
@@ -0,0 +1,70 @@
local _G = ShaguTweaks.GetGlobalEnv()
local T = ShaguTweaks.T
local HookScript = ShaguTweaks.HookScript
local module = ShaguTweaks:register({
title = T["Metric Range"],
description = T["Displays tooltip range units in metres instead of yards."],
expansions = { ["vanilla"] = true, ["tbc"] = nil },
category = T["Tooltip & Items"],
enabled = true,
})
module.enable = function(self)
-- Avoid double processing while the old standalone addon is still enabled.
if IsAddOnLoaded("MetricRange") then return end
local function ConvertText(text)
if type(text) ~= "string" then return text end
-- Intentionally keep the original behavior: only replace the displayed
-- unit label. Do not numerically convert the range value.
text = string.gsub(text, " yards", " metres")
text = string.gsub(text, " yard", " metre")
text = string.gsub(text, " yd", " m")
return text
end
local function ConvertTooltip(tooltip)
if not tooltip or not tooltip.GetName then return end
local name = tooltip:GetName()
if not name then return end
for i = 1, 30 do
local left = _G[name .. "TextLeft" .. i]
if left then
local old = left:GetText()
if old then
local new = ConvertText(old)
if new ~= old then left:SetText(new) end
end
end
local right = _G[name .. "TextRight" .. i]
if right then
local old = right:GetText()
if old then
local new = ConvertText(old)
if new ~= old then right:SetText(new) end
end
end
end
end
local function HookTooltip(tooltip)
if not tooltip then return end
HookScript(tooltip, "OnShow", function()
ConvertTooltip(tooltip)
end)
end
HookTooltip(GameTooltip)
HookTooltip(ItemRefTooltip)
HookTooltip(ShoppingTooltip1)
HookTooltip(ShoppingTooltip2)
HookTooltip(ShoppingTooltip3)
if GameTooltip and GameTooltip:IsVisible() then ConvertTooltip(GameTooltip) end
if ItemRefTooltip and ItemRefTooltip:IsVisible() then ConvertTooltip(ItemRefTooltip) end
end
+1
View File
@@ -7,6 +7,7 @@ local module = ShaguTweaks:register({
title = T["Reveal World Map"],
description = T["Reveals unexplored world map areas and shows exploration hints."],
expansions = { ["vanilla"] = true, ["tbc"] = false },
category = T["World Map"],
maintainer = "@shagu (GitHub)",
enabled = true,
config = {