Add optimized TokensWorth chat and loot modules
Add Block NPC Spam, World Chat Hider, and Loot Quality to Extras with shared chat filtering, event-driven loot quality handling, runtime safety fixes, and updated documentation/provenance.
This commit is contained in:
@@ -43,8 +43,11 @@ Settings: **Esc → Advanced Options**.
|
||||
- Buy Em All uses ClassicAPI merchant, item count and bag-family data, including specialty bags.
|
||||
- Added modifier-aware Key-Down Casting with Shift/Ctrl/Alt binding support and an independent Alt Self-Cast option.
|
||||
- Added Metric Range for metre labels in range tooltips without changing numeric values.
|
||||
- Added optimized modules adapted from TokensWorth/ShaguTweaks-mods: Mouseover Right bars, Hide Macro Text, Unit Frame Abbreviated Names, Cursor Tooltip, Hide Combat Tooltip and MiniMap Framerate & Latency.
|
||||
- Added optimized modules adapted from TokensWorth/ShaguTweaks-mods: Mouseover Right bars, Hide Macro Text, Unit Frame Abbreviated Names, Cursor Tooltip, Hide Combat Tooltip, MiniMap Framerate & Latency, Block NPC Spam, World Chat Hider and Loot Quality.
|
||||
- MiniMap Framerate & Latency updates both counters through one ClassicAPI ticker instead of permanent per-frame handlers.
|
||||
- Block NPC Spam and World Chat Hider share one on-demand chat filter dispatcher instead of stacking separate global chat wrappers.
|
||||
- World Chat Hider suppresses World messages locally in instances and never leaves or rejoins the channel.
|
||||
- Loot Quality is event-driven and checks all current loot slots instead of only visible loot buttons.
|
||||
- Hide Combat Tooltip uses ClassicAPI modifier-state events instead of permanent per-frame modifier polling.
|
||||
- Various stability fixes across legacy ShaguTweaks Extras modules.
|
||||
|
||||
@@ -96,6 +99,10 @@ Other modules can also benefit indirectly from ClassicAPI through shared ShaguTw
|
||||
- Auction Alt-Buy
|
||||
- Buy Em All
|
||||
|
||||
### Loot
|
||||
|
||||
- Loot Quality
|
||||
|
||||
### Tooltip & Items
|
||||
|
||||
- Metric Range
|
||||
@@ -113,6 +120,8 @@ Other modules can also benefit indirectly from ClassicAPI through shared ShaguTw
|
||||
|
||||
### Chat
|
||||
|
||||
- Block NPC Spam
|
||||
- World Chat Hider
|
||||
- Chat Timestamps
|
||||
- Center Text Input Box
|
||||
- Enable Text Shadow
|
||||
|
||||
@@ -39,6 +39,9 @@ mods\bag-search.lua
|
||||
mods\auction-alt-buy.lua
|
||||
mods\buy-em-all.lua
|
||||
|
||||
# loot
|
||||
mods\loot-quality.lua
|
||||
|
||||
# tooltip & items
|
||||
mods\metric-range.lua
|
||||
mods\cursor-tooltip.lua
|
||||
@@ -56,6 +59,9 @@ mods\macro-icons.lua
|
||||
mods\macro-tweaks.lua
|
||||
|
||||
# chat
|
||||
mods\chat-filter-common.lua
|
||||
mods\chat-npc-spam.lua
|
||||
mods\world-chat-hider.lua
|
||||
mods\chat-input-center.lua
|
||||
mods\chat-text-shadow.lua
|
||||
mods\chat-timestamps.lua
|
||||
|
||||
@@ -38,6 +38,9 @@ The following modules in this fork contain code adapted from
|
||||
- `mods/cursor-tooltip.lua`
|
||||
- `mods/hide-combat-tooltip.lua`
|
||||
- `mods/minimap-framerate-latency.lua`
|
||||
- `mods/chat-npc-spam.lua`
|
||||
- `mods/world-chat-hider.lua`
|
||||
- `mods/loot-quality.lua`
|
||||
|
||||
Source:
|
||||
- https://github.com/TokensWorth/ShaguTweaks-mods
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
-- Shared chat-filter dispatcher for Extras modules.
|
||||
-- Installs one ChatFrame_OnEvent wrapper only when a filter is enabled.
|
||||
|
||||
local Filter = ShaguTweaks.ExtrasChatFilter
|
||||
|
||||
if not Filter then
|
||||
Filter = {
|
||||
handlers = {},
|
||||
order = {},
|
||||
known = {},
|
||||
installed = false,
|
||||
installer = nil,
|
||||
}
|
||||
ShaguTweaks.ExtrasChatFilter = Filter
|
||||
end
|
||||
|
||||
function Filter:Install()
|
||||
if self.installed then return end
|
||||
|
||||
self.installed = true
|
||||
self.original = ChatFrame_OnEvent
|
||||
|
||||
ChatFrame_OnEvent = function(event)
|
||||
for i = 1, table.getn(Filter.order) do
|
||||
local current = Filter.handlers[Filter.order[i]]
|
||||
if current and current(event) then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if Filter.original then
|
||||
return Filter.original(event)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Filter:ScheduleInstall()
|
||||
if self.installed or self.installer then return end
|
||||
|
||||
-- ShaguTweaks enables modules during VARIABLES_LOADED in unspecified table
|
||||
-- order. Install on PLAYER_ENTERING_WORLD so this dispatcher becomes the
|
||||
-- outer wrapper after the core Chat Spam Filter regardless of module order.
|
||||
-- That prevents locally hidden World/NPC messages from entering its cache.
|
||||
self.installer = CreateFrame("Frame")
|
||||
self.installer:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
self.installer:SetScript("OnEvent", function()
|
||||
Filter:Install()
|
||||
this:UnregisterAllEvents()
|
||||
this:Hide()
|
||||
Filter.installer = nil
|
||||
end)
|
||||
end
|
||||
|
||||
function Filter:Register(key, handler)
|
||||
if type(key) ~= "string" or type(handler) ~= "function" then return end
|
||||
|
||||
if not self.known[key] then
|
||||
self.known[key] = true
|
||||
self.order[table.getn(self.order) + 1] = key
|
||||
end
|
||||
|
||||
self.handlers[key] = handler
|
||||
self:ScheduleInstall()
|
||||
end
|
||||
|
||||
function Filter:Unregister(key)
|
||||
self.handlers[key] = nil
|
||||
end
|
||||
@@ -0,0 +1,92 @@
|
||||
-- Adapted from TokensWorth/ShaguTweaks-mods (MIT, original copyright GryllsAddons).
|
||||
|
||||
local T = ShaguTweaks.T
|
||||
local Filter = ShaguTweaks.ExtrasChatFilter
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Block NPC Spam"],
|
||||
description = T["Blocks known repetitive NPC say/yell spam messages."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Chat"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
local blockedEvents = {
|
||||
["CHAT_MSG_MONSTER_SAY"] = true,
|
||||
["CHAT_MSG_MONSTER_YELL"] = true,
|
||||
}
|
||||
|
||||
local blockedNPCs = {}
|
||||
local npcNames = {
|
||||
-- enUS
|
||||
"Tansy Sparkpen",
|
||||
"Fara Boltbreaker",
|
||||
"Shellcoin Promoter",
|
||||
|
||||
-- ruRU
|
||||
"Зазывала ярмарки Новолуния",
|
||||
"Томас Миллер",
|
||||
"Уильям",
|
||||
"Донна",
|
||||
"Жюстина Демалье",
|
||||
"Мерлис Малаган",
|
||||
"Лиана Пирс",
|
||||
"Сюзанна",
|
||||
"Джейни Аншип",
|
||||
}
|
||||
|
||||
for _, name in pairs(npcNames) do
|
||||
blockedNPCs[name] = true
|
||||
blockedNPCs[string.lower(name)] = true
|
||||
end
|
||||
|
||||
local blockedPhrases = {
|
||||
{ "shellcoin", "invest" },
|
||||
{ "shells", "trade" },
|
||||
{ "shells", "money" },
|
||||
}
|
||||
|
||||
local function IsBlockedNPC(sender)
|
||||
if not sender then return false end
|
||||
return blockedNPCs[sender] or blockedNPCs[string.lower(sender)] or false
|
||||
end
|
||||
|
||||
local function IsBlockedPhrase(message)
|
||||
if not message then return false end
|
||||
|
||||
local normalized = string.lower(string.gsub(message, "[^A-Za-z0-9]", ""))
|
||||
|
||||
for _, phrase in pairs(blockedPhrases) do
|
||||
local matched = true
|
||||
|
||||
for _, word in pairs(phrase) do
|
||||
if not string.find(normalized, word, 1, true) then
|
||||
matched = false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if matched then return true end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function FilterNPCSpam(event)
|
||||
if not blockedEvents[event] then return false end
|
||||
if not arg1 or not arg2 then return false end
|
||||
|
||||
return IsBlockedNPC(arg2) or IsBlockedPhrase(arg1)
|
||||
end
|
||||
|
||||
module.enable = function(self)
|
||||
if Filter then
|
||||
Filter:Register("BlockNPCSpam", FilterNPCSpam)
|
||||
end
|
||||
end
|
||||
|
||||
module.disable = function(self)
|
||||
if Filter then
|
||||
Filter:Unregister("BlockNPCSpam")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,128 @@
|
||||
-- Adapted from TokensWorth/ShaguTweaks-mods (MIT, original copyright GryllsAddons).
|
||||
|
||||
local T = ShaguTweaks.T
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Loot Quality"],
|
||||
description = T["Colors the loot frame using the highest item quality in the current loot window."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Loot"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
local MIN_QUALITY = 2 -- uncommon / green
|
||||
|
||||
local function FindLootTitle()
|
||||
local regions = { LootFrame:GetRegions() }
|
||||
|
||||
for i = 1, table.getn(regions) do
|
||||
local region = regions[i]
|
||||
-- Some Vanilla/Turtle regions expose GetText without being FontStrings.
|
||||
-- Only keep a region that can actually be recolored.
|
||||
if region
|
||||
and type(region.GetText) == "function"
|
||||
and type(region.SetTextColor) == "function"
|
||||
and region:GetText() == ITEMS then
|
||||
return region
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module.enable = function(self)
|
||||
if not self.border then
|
||||
self.border = LootFrame:CreateTexture(nil, "OVERLAY")
|
||||
self.border:SetTexture("Interface\\LootFrame\\UI-LootPanel")
|
||||
self.border:SetPoint("TOPLEFT", LootFrame, "TOPLEFT", -4, 3)
|
||||
self.border:SetPoint("BOTTOMRIGHT", LootFrame, "BOTTOMRIGHT", 5, -3)
|
||||
self.border:SetDrawLayer("ARTWORK")
|
||||
|
||||
self.highlight = LootFrame:CreateTexture(nil, "OVERLAY")
|
||||
self.highlight:SetTexture("Interface\\LootFrame\\UI-LootPanel")
|
||||
self.highlight:SetAllPoints(self.border)
|
||||
self.highlight:SetDrawLayer("ARTWORK")
|
||||
self.highlight:SetBlendMode("ADD")
|
||||
end
|
||||
|
||||
self.title = self.title or FindLootTitle()
|
||||
|
||||
if self.title
|
||||
and not self.titleColor
|
||||
and type(self.title.GetTextColor) == "function" then
|
||||
local r, g, b, a = self.title:GetTextColor()
|
||||
self.titleColor = { r or 1, g or .82, b or 0, a or 1 }
|
||||
end
|
||||
|
||||
local function RestoreTitle()
|
||||
if not self.title or type(self.title.SetTextColor) ~= "function" then return end
|
||||
|
||||
local color = self.titleColor
|
||||
if color then
|
||||
self.title:SetTextColor(color[1], color[2], color[3], color[4])
|
||||
else
|
||||
self.title:SetTextColor(1, .82, 0)
|
||||
end
|
||||
end
|
||||
|
||||
local function ResetVisuals()
|
||||
self.border:Hide()
|
||||
self.highlight:Hide()
|
||||
RestoreTitle()
|
||||
end
|
||||
|
||||
local function UpdateQuality()
|
||||
local highestQuality = 0
|
||||
local count = GetNumLootItems() or 0
|
||||
|
||||
for i = 1, count do
|
||||
if LootSlotIsItem(i) then
|
||||
local _, _, _, quality = GetLootSlotInfo(i)
|
||||
quality = tonumber(quality) or 0
|
||||
if quality > highestQuality then
|
||||
highestQuality = quality
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local color = ITEM_QUALITY_COLORS and ITEM_QUALITY_COLORS[highestQuality]
|
||||
if highestQuality < MIN_QUALITY or not color then
|
||||
ResetVisuals()
|
||||
return
|
||||
end
|
||||
|
||||
self.border:SetVertexColor(color.r, color.g, color.b)
|
||||
self.highlight:SetVertexColor(color.r, color.g, color.b)
|
||||
self.border:Show()
|
||||
self.highlight:Show()
|
||||
|
||||
if self.title and type(self.title.SetTextColor) == "function" then
|
||||
self.title:SetTextColor(color.r, color.g, color.b)
|
||||
end
|
||||
end
|
||||
|
||||
self.ResetVisuals = ResetVisuals
|
||||
self.events = self.events or CreateFrame("Frame")
|
||||
self.events:UnregisterAllEvents()
|
||||
self.events:RegisterEvent("LOOT_OPENED")
|
||||
self.events:RegisterEvent("LOOT_SLOT_CLEARED")
|
||||
self.events:RegisterEvent("LOOT_CLOSED")
|
||||
|
||||
self.events:SetScript("OnEvent", function()
|
||||
if event == "LOOT_CLOSED" then
|
||||
ResetVisuals()
|
||||
else
|
||||
UpdateQuality()
|
||||
end
|
||||
end)
|
||||
|
||||
ResetVisuals()
|
||||
end
|
||||
|
||||
module.disable = function(self)
|
||||
if self.events then
|
||||
self.events:UnregisterAllEvents()
|
||||
end
|
||||
|
||||
if self.ResetVisuals then
|
||||
self.ResetVisuals()
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,59 @@
|
||||
-- Adapted from TokensWorth/ShaguTweaks-mods (MIT, original copyright GryllsAddons).
|
||||
|
||||
local T = ShaguTweaks.T
|
||||
local Filter = ShaguTweaks.ExtrasChatFilter
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["World Chat Hider"],
|
||||
description = T["Hides World channel messages while inside an instance without leaving the channel."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Chat"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
local function IsInsideInstance()
|
||||
if type(IsInInstance) ~= "function" then return false end
|
||||
|
||||
local inside = IsInInstance()
|
||||
return inside == 1 or inside == true
|
||||
end
|
||||
|
||||
local function IsWorldChannel()
|
||||
-- Vanilla 1.12 CHAT_MSG_CHANNEL exposes the base channel name in arg9.
|
||||
-- Custom channel names are not localized, so comparing the actual event is
|
||||
-- both cheaper and safer than querying GetChannelName("world") every time.
|
||||
if arg9 and string.lower(arg9) == "world" then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Conservative fallback for clients that omit arg9: arg4 is the numbered
|
||||
-- channel label (for example "5. World").
|
||||
if arg4 then
|
||||
local full = string.lower(arg4)
|
||||
if full == "world" then return true end
|
||||
|
||||
local _, _, base = string.find(full, "^%d+%.%s*(.+)$")
|
||||
if base == "world" then return true end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function FilterWorldChat(event)
|
||||
if event ~= "CHAT_MSG_CHANNEL" then return false end
|
||||
if not IsInsideInstance() then return false end
|
||||
|
||||
return IsWorldChannel()
|
||||
end
|
||||
|
||||
module.enable = function(self)
|
||||
if Filter then
|
||||
Filter:Register("WorldChatHider", FilterWorldChat)
|
||||
end
|
||||
end
|
||||
|
||||
module.disable = function(self)
|
||||
if Filter then
|
||||
Filter:Unregister("WorldChatHider")
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user