Author:ptrace Comitter:ptrace Date:2026-06-18 14:23:29 UTC

init

diff --git a/README.md b/README.md new file mode 100644 index 0000000..634bae6 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ # NSWA This addon collection solves some problems I've had with the WoW UI: - hide talking head - hide error messages - temporarily display which M+ dungeon I joined - alert me, when the group is considered full - alert group when the dungeon difficulty is not set to mythic It's entirely tailored to my needs, so it might not suit your needs.  But you're welcome to use this code however you like.   Note: You call this addon ingame via `/nswa` or `/wa`. The latter will  conflict with Weakauras.  diff --git a/assets/circle.png b/assets/circle.png new file mode 100644 index 0000000..1ae7063 Binary files /dev/null and b/assets/circle.png differ diff --git a/assets/okletsgo.ogg b/assets/okletsgo.ogg new file mode 100755 index 0000000..c7c563f Binary files /dev/null and b/assets/okletsgo.ogg differ diff --git a/assets/triangle.png b/assets/triangle.png new file mode 100644 index 0000000..2c05485 Binary files /dev/null and b/assets/triangle.png differ diff --git a/desc.gt b/desc.gt new file mode 100644 index 0000000..babf269 --- /dev/null +++ b/desc.gt @@ -0,0 +1 @@ WoW addon which removes annoyances and adds QoL (for me) diff --git a/nswa.toc b/nswa.toc new file mode 100644 index 0000000..c5dfdf2 --- /dev/null +++ b/nswa.toc @@ -0,0 +1,25 @@ ## Interface: 120000, 120001 ## Title: NSWA ## Author: https://github.com/worstprgr ## Version: 1337.0 ## Notes: Non Sucking WoW Addon ## DefaultState: Enabled ## LoadOnDemand: 0 ## SavedVariables: nswa_db_global ## SavedVariablesPerCharacter: nswa_db nswa_load.lua nswa_hide_cringe.lua nswa_portals.lua nswa_which_dungeon.lua nswa_dungeon_difficulty.lua nswa_group_is_full.lua nswa_hello.lua nswa_repair.lua nswa_cursor.lua nswa_gossip.lua nswa_main.lua diff --git a/nswa_cursor.lua b/nswa_cursor.lua new file mode 100644 index 0000000..1169e1f --- /dev/null +++ b/nswa_cursor.lua @@ -0,0 +1,47 @@ local SIZE      = 95 local THROTTLE  = 0.01 local OFFSET_X  = 0 local OFFSET_Y  = 0 local COLOR = { r = 0, g = 1, b = 0.3, a = 0.8 } local circle = CreateFrame("Frame", "MouseCombatCircle", UIParent) circle:SetSize(SIZE, SIZE) circle:SetFrameStrata("TOOLTIP") circle:Hide() local tex = circle:CreateTexture(nil, "OVERLAY") tex:SetAllPoints() tex:SetTexture("Interface\\AddOns\\nswa\\assets\\circle.png") tex:SetVertexColor(COLOR.r, COLOR.g, COLOR.b, COLOR.a) local elapsed = 0 circle:SetScript("OnUpdate", function(self, delta)     elapsed = elapsed + delta     if elapsed < THROTTLE then return end     elapsed = 0     local x, y = GetCursorPosition()     local scale = UIParent:GetScale()     x = (x / scale) + OFFSET_X     y = (y / scale) + OFFSET_Y     self:ClearAllPoints()     self:SetPoint("CENTER", UIParent, "BOTTOMLEFT", x, y) end) local f = CreateFrame("Frame") f:RegisterEvent("PLAYER_REGEN_DISABLED") f:RegisterEvent("PLAYER_REGEN_ENABLED") f:SetScript("OnEvent", function(_, event)     if event == "PLAYER_REGEN_DISABLED" then         circle:Show()     elseif event == "PLAYER_REGEN_ENABLED" then         circle:Hide()     end end) diff --git a/nswa_dungeon_difficulty.lua b/nswa_dungeon_difficulty.lua new file mode 100644 index 0000000..ba44a5b --- /dev/null +++ b/nswa_dungeon_difficulty.lua @@ -0,0 +1,48 @@ local already_warned = false local function check_dungeon_difficulty()     if IsInRaid() then return end     if not IsInGroup() then return end     if already_warned then return end     local difficulty_id = GetDungeonDifficultyID()     -- 23 = Mythic     if difficulty_id ~= 23 then         local text = "[Alert]: Dungeon difficulty is currently not set to Mythic."         print("NSWA: " .. text)         SendChatMessage(text, "PARTY")         already_warned = true     end end local function delayed_check()     C_Timer.After(1, function()         check_dungeon_difficulty()     end) end local function reset_state()     already_warned = false end local frame = CreateFrame("Frame") frame:RegisterEvent("GROUP_ROSTER_UPDATE") frame:RegisterEvent("PLAYER_ENTERING_WORLD") frame:SetScript("OnEvent", function(self, event)     if event == "GROUP_ROSTER_UPDATE" then         if not IsInGroup() then             reset_state()         else             delayed_check()         end     elseif event == "PLAYER_ENTERING_WORLD" then         delayed_check()     end end) diff --git a/nswa_gossip.lua b/nswa_gossip.lua new file mode 100644 index 0000000..42a6f31 --- /dev/null +++ b/nswa_gossip.lua @@ -0,0 +1,38 @@ local frame = CreateFrame("Frame") frame:RegisterEvent("GOSSIP_SHOW") -- Note: We need instanceIDs not mapIDs. Because an instance can have multiple mapIDs! --       And whatever those IDs below are, they're wrong ... -- Keeping it in case I have to change it back. -- local allowed_zones = { --     14032,  -- Algethar Academy --     15808,  -- Windrunner Spire --     15829,  -- Magisters Terrace --     16395,  -- Maisara Caverns --     16573,  -- Nexus Point Xenas --     4813,   -- Pit of Saron --     6988,   -- Skyreach --     8910,   -- The Seat of the Triumvirate -- } local function select_first_gossip_option(self, event)     if not IsInInstance() then return end     local options = C_GossipInfo.GetOptions()     if options and #options == 1 then         C_Timer.After(0.1, function()             if not IsShiftKeyDown() then                 C_GossipInfo.SelectOption(options[1].gossipOptionID)             end         end)         print("NSWA - Info: Chosen the first gossip option.")     end end frame:SetScript("OnEvent", select_first_gossip_option) diff --git a/nswa_group_is_full.lua b/nswa_group_is_full.lua new file mode 100644 index 0000000..e713512 --- /dev/null +++ b/nswa_group_is_full.lua @@ -0,0 +1,35 @@ local was_full = false local last_play = 0 local sound_fp = "Interface\\AddOns\\nswa\\assets\\okletsgo.ogg" local frame = CreateFrame("Frame") frame:RegisterEvent("GROUP_ROSTER_UPDATE") frame:RegisterEvent("PLAYER_ENTERING_WORLD") local function play_sound()     local now = GetTime()     if now - last_play > 3 then         PlaySoundFile(sound_fp, "Master")         last_play = now     end end frame:SetScript("OnEvent", function()     if IsInGroup() and not IsInRaid() then         local num = GetNumGroupMembers()         local is_full = (num == 5)         if is_full and not was_full then             print("Full House!")             play_sound()         end         was_full = is_full     else         was_full = false     end end) diff --git a/nswa_hello.lua b/nswa_hello.lua new file mode 100644 index 0000000..a12678e --- /dev/null +++ b/nswa_hello.lua @@ -0,0 +1,53 @@ local DELAY_MIN = 2 local DELAY_MAX = 5 local messages = {     "hi",     "o/",     "o7",     "yo",     "hello", } local is_in_group = false local has_greeted = false local frame = CreateFrame("Frame") frame:RegisterEvent("GROUP_ROSTER_UPDATE") local function send_message(msg)     if IsInGroup(LE_PARTY_CATEGORY_INSTANCE) then         SendChatMessage(msg, "INSTANCE_CHAT")     elseif IsInGroup() then         SendChatMessage(msg, "PARTY")     end end local function greet_group()     local delay = math.random(DELAY_MIN, DELAY_MAX)     C_Timer.After(delay, function()         local msg = messages[ math.random(#messages) ]         send_message(msg)     end) end frame:SetScript("OnEvent", function()     if IsInGroup() and not IsInRaid() then         if not is_in_group then             if not has_greeted then                 greet_group()                 has_greeted = true             end             is_in_group = true         end     else         is_in_group = false         has_greeted = false     end end) diff --git a/nswa_hide_cringe.lua b/nswa_hide_cringe.lua new file mode 100644 index 0000000..19967b5 --- /dev/null +++ b/nswa_hide_cringe.lua @@ -0,0 +1,68 @@ local N = nswa function N.init_hide_cringe()     -- Error/event frames     EventToastManagerFrame:SetAlpha(0)     UIErrorsFrame:SetAlpha(0)     RaidBossEmoteFrame:SetAlpha(0)     -- Loot window after boss     GroupLootHistoryFrame:UnregisterAllEvents()     GroupLootHistoryFrame:Hide()     GroupLootHistoryFrame:SetScript("OnShow", GroupLootHistoryFrame.Hide)     -- Yap frame     hooksecurefunc(TalkingHeadFrame, "PlayCurrent", function(self)         self:Hide()     end)     -- Tooltips     N.hide_tooltips()     -- Spell overlays     -- Blizzard does a great job displaying overlays that are basically constant active.     local HIDDEN = {         [1270990] = true,  -- Brewmaster talent: harmonic wave, or whatever     }     local original_ShowOverlay = SpellActivationOverlayFrame.ShowOverlay     SpellActivationOverlayFrame.ShowOverlay = function(self, spellID, ...)         if HIDDEN[spellID] then             return         end         return original_ShowOverlay(self, spellID, ...)     end     print("Cringe Removed!") end function N.hc_toggle_error()     if UIErrorsFrame:GetAlpha() == 0 then         print("Showing Error Frame")         UIErrorsFrame:SetAlpha(100)     else         print("Hiding Error Frame")         UIErrorsFrame:SetAlpha(0)     end end function N.hide_tooltips()     local tooltips = {         GameTooltip,         ItemRefTooltip,         ShoppingTooltip1,         ShoppingTooltip2,     }     for _, tip in ipairs(tooltips) do         tip:HookScript("OnShow", function(self)             if InCombatLockdown() then                 self:Hide()             end         end)     end end diff --git a/nswa_load.lua b/nswa_load.lua new file mode 100644 index 0000000..ecd7f42 --- /dev/null +++ b/nswa_load.lua @@ -0,0 +1,25 @@ nswa = nswa or {} local N = nswa local frame = CreateFrame("Frame") frame:RegisterEvent("ADDON_LOADED") frame:SetScript("OnEvent", function(self, event, arg1)     if event == "ADDON_LOADED" and arg1 == "nswa" then         nswa_db_global = nswa_db_global or {}         nswa_db = nswa_db or {}         nswa_db.activity_state = nswa_db.activity_state or {}         local activity_state = nswa_db.activity_state         activity_state.pos   = activity_state.pos or {}         print("NSWA - Debug: Hii!")         N.main()     end end) --DevTools_Dump(thing) diff --git a/nswa_main.lua b/nswa_main.lua new file mode 100644 index 0000000..a5a9996 --- /dev/null +++ b/nswa_main.lua @@ -0,0 +1,30 @@ local N = nswa SLASH_NSWA1 = "/nswa" SLASH_NSWA2 = "/wa" local function argparse(args)     if args == "" then         print("Beep bop")     elseif args == "move" then         N.move_activity_label()     elseif args == "err" then         N.hc_toggle_error()     elseif args == "d" then         N.destroy_activity_frame()     elseif args == "zone" then         local current_zone = C_Map.GetBestMapForUnit("player")         print("Current Zone: " .. current_zone)     end end function N.main()     N.init_portals()     N.init_activity_frame()     N.init_hide_cringe()     SlashCmdList["NSWA"] = argparse end diff --git a/nswa_portals.lua b/nswa_portals.lua new file mode 100644 index 0000000..f261b63 --- /dev/null +++ b/nswa_portals.lua @@ -0,0 +1,131 @@ local N = nswa local season_portals = {     { id = 393273,  label = "AA",   map_id = 2526 }, -- Algeth'ar Academy     { id = 1254400, label = "WS",   map_id = 2805 }, -- Windrunner Spire     { id = 1254572, label = "MT",   map_id = 2811 }, -- Magisters' Terrace     { id = 1254563, label = "NPX",  map_id = 2915 }, -- Nexus-Point Xenas     { id = 1254559, label = "MC",   map_id = 2874 }, -- Maisara Caverns     { id = 159898,  label = "SKYR", map_id = 1209 }, -- Skyreach     { id = 1254555, label = "POS",  map_id = 658  }, -- Pit of Saron     { id = 1254551, label = "SOT",  map_id = 1753 }, -- Seat of Triumvirate } -- mapID from GetActivityInfoTable -- ------------------------------- -- Triumvirat       1753 -- Xenas            2915 -- Mördergasse      2813 -- Windspire        2805 -- Blendendes Tal   2859 -- Nalorakks Bau    2825 -- Leerennarbe      2923 -- Magister         2811 -- Kavernen         2874 -- Skyreach         1209 -- Saron            658 -- Akademie         2526 local buttons = {} local index = 0 local size = 36 local spacing = 4 local current_id = nil local function button_create(spell_id, label, parent_frame)     local button = CreateFrame("Button", "Nswa_portals_button"..index, parent_frame, "SecureActionButtonTemplate")     button:SetSize(size, size)     if index == 0 then         button:SetPoint("BOTTOMLEFT", parent_frame, "BOTTOMLEFT", 30, -80)     else         button:SetPoint("LEFT", buttons[index-1], "RIGHT", spacing, 0)     end     button:RegisterForClicks("AnyUp", "AnyDown")     button:SetAttribute("type", "spell")     button:SetAttribute("spell", spell_id)     local icon = button:CreateTexture(nil, "BACKGROUND")     icon:SetAllPoints()     icon:SetTexture(C_Spell.GetSpellTexture(spell_id))     local text = button:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")     text:SetPoint("TOP", button, "BOTTOM", 0, -2)     text:SetText(label)     button.icon = icon     button.label = text     buttons[index] = button end local function buttons_clear()     for _, button in ipairs(buttons) do         if button and button:IsShown() then             button:Hide()             button:SetParent(nil)         end     end     buttons = {}     index = 0 end local function button_glow(id, index)     if not current_id then return end     if not id then         local _, id, _, _, name = GetLFGProposal()         print("INFO: Maybe unknown id: " .. id .. ", name: " .. name)         return     end     if id == current_id then         local button = buttons[index]         ActionButtonSpellAlertManager:ShowAlert(button)     end end local function buttons_build(parent_frame)     index = 0  -- maybe redundant     for _, data in ipairs(season_portals) do         local spell_id = data.id         local label = data.label         if IsSpellKnown(spell_id) then             button_create(spell_id, label, parent_frame)             button_glow(data.map_id, index)             index = index + 1         end     end end local function wait_for_pve_frame()     local frame = CreateFrame("Frame")     frame:RegisterEvent("LFG_LOCK_INFO_RECEIVED")     frame:SetScript("OnEvent", function(self, event, ...)         if event == "LFG_LOCK_INFO_RECEIVED" then             frame:UnregisterEvent("LFG_LOCK_INFO_RECEIVED")             PVEFrame:HookScript("OnShow", function()                 buttons_clear()                 buttons_build(PVEFrame)             end)         end     end) end function N.portals_info_create(map_id)     current_id = map_id end function N.portals_info_destroy()     current_id = nil end function N.init_portals()     wait_for_pve_frame() end diff --git a/nswa_repair.lua b/nswa_repair.lua new file mode 100644 index 0000000..f9ea522 --- /dev/null +++ b/nswa_repair.lua @@ -0,0 +1,12 @@ local frame = CreateFrame("Frame") frame:RegisterEvent("MERCHANT_SHOW") frame:SetScript("OnEvent", function()     if CanMerchantRepair() then         print("Repaired items!")         RepairAllItems()     end end) diff --git a/nswa_which_dungeon.lua b/nswa_which_dungeon.lua new file mode 100644 index 0000000..58eead9 --- /dev/null +++ b/nswa_which_dungeon.lua @@ -0,0 +1,255 @@ -- Code for reference --  - When in a LFG group, get the ID first via  `_, id = C_LFGList.GetActiveEntryInfo()` --  - Use the same id in  `info = C_LFGList.GetActivityInfoTable(id)` --  - Then you have things like `info.name, info.mapID` local N = nswa -- TODO: Instead of having multiple frames, just put it in one or two frames and filter by event local frame = CreateFrame("Frame", "FrameActivity", UIParent) local frame_player = CreateFrame("Frame") local frame_lfg = CreateFrame("Frame") local frame_group = CreateFrame("Frame") local text = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") local was_in_group = false local activity_label_is_moving = true local is_shown = false local text_label = "init" local INSTACE_TYPES_TO_CHECK = {     party = true,     raid = true,     pvp = true,     arena = true,     scenario = true, } -- -- Local functions -- local function move_enable()     text:SetText("MOVING")     frame:Show()     frame:EnableMouse(true)     frame:SetMovable(true) end local function move_disable()     if not is_shown then         frame:Hide()     end     text:SetText(text_label)     frame:EnableMouse(false)     frame:SetMovable(false) end function N.move_activity_label()     if activity_label_is_moving then         move_enable()     else         move_disable()     end     activity_label_is_moving = not activity_label_is_moving end local function activity_get_try()     local active_entry_info = C_LFGList.GetActiveEntryInfo()     if active_entry_info == nil then         return nil     end     local _, first_id = next(active_entry_info.activity_ids)     if first_id == nil then         return nil     end     local activity_info = C_LFGList.GetActivityInfoTable(first_id)     if activity_info == nil then         return nil     end     return activity_info.fullName, activity_info.mapID end local function activity_get()     local delay = 1     local max_tries = 3     local tries = 0     local function try_once()         tries = tries + 1         local name, map_id = activity_get_try()         if name ~= nil and map_id ~= nil then             return name, map_id         end         if tries < max_tries then             C_Timer.After(delay, try_once)         else             return "error", ""         end     end     return try_once() end local function activity_text_update(message)     if text == nil then         print("activity_text_update: nil")         return     end     text_label = message     is_shown = true     text:SetText(message)     frame:Show() end local function activity_text_destroy()     if text == nil then         print("activity_text_destroy: nil")         return     end     text_label = ""     is_shown = false     text:SetText("")     frame:Hide() end local function frames_unregister()     frame_lfg:UnregisterEvent("LFG_LIST_APPLICATION_STATUS_UPDATED") end local function lfg_info_get(callback)     frame_lfg:RegisterEvent("LFG_LIST_APPLICATION_STATUS_UPDATED")     frame_lfg:SetScript("OnEvent", function(self, event, search_result_id)         local search_result_data = C_LFGList.GetSearchResultInfo(search_result_id)         local activity_ids = search_result_data.activityIDs         if not activity_ids then return end         local activity_id = activity_ids[1]         assert(activity_id, "activity_id")         local activity_info  = C_LFGList.GetActivityInfoTable(activity_id)         local ai = activity_info         assert(ai, "activity_info")         callback(ai.fullName, ai.mapID)         frames_unregister()     end) end local function dispatch_lfg_info(activity_name, map_id)     activity_text_update(activity_name)     N.portals_info_create(map_id) end -- -- Public functions -- function N.destroy_activity_frame()     activity_text_destroy()     N.portals_info_destroy()     frames_unregister() end function N.init_activity_frame()     local DB = nswa_db     --     -- Init hook for activity info     --     LFGListInviteDialog:HookScript("OnShow", function(self)         lfg_info_get(function(activity_name, map_id)             dispatch_lfg_info(activity_name, map_id)         end)     end)     LFGListInviteDialog.DeclineButton:HookScript("OnClick", function(self)         N.destroy_activity_frame()     end)     --     -- Events     --     frame_player:RegisterEvent("PLAYER_ENTERING_WORLD")     frame_group:RegisterEvent("GROUP_ROSTER_UPDATE");     frame_player:SetScript("OnEvent", function(self, event, ...)         C_Timer.After(1, function()             local in_instance, instance_type = IsInInstance()             if in_instance and INSTACE_TYPES_TO_CHECK[instance_type] then                 N.destroy_activity_frame()             end         end)     end)     frame_group:SetScript("OnEvent", function(self, event, ...)         C_Timer.After(0.5, function()             if not IsInGroup() and not IsInRaid() then                 N.destroy_activity_frame()             end         end)     end)     --     -- Save / Restore position of info text     --     frame:SetSize(300, 50)     frame:RegisterForDrag("LeftButton")     frame:SetClampedToScreen(true)     is_shown = false     frame:Hide()     if DB.activity_state.pos.point then         frame:SetPoint(             DB.activity_state.pos.point,             UIParent,             DB.activity_state.pos.relPoint,             DB.activity_state.pos.x,             DB.activity_state.pos.y         )     else         frame:SetPoint("CENTER")     end     text:SetFont("Fonts\\FRIZQT__.TTF", 24)     text:SetTextColor(0, 1, 0)     text:SetPoint("CENTER")     text:SetText(text_label)     frame:SetScript("OnDragStop", function(self)         self:StopMovingOrSizing()         local point, _, relPoint, x, y = self:GetPoint()         DB.activity_state.pos.point = point         DB.activity_state.pos.relPoint = relPoint         DB.activity_state.pos.x = x         DB.activity_state.pos.y = y         -- DevTools_Dump(DB.activity_state)     end)     frame:SetScript("OnDragStart", function(self)         self:StartMoving()     end)     frame:SetScript("OnEvent", frame.OnEvent) end