From 919534f3d8920d9be58bb5a5e78ad2e2a1f9974a Mon Sep 17 00:00:00 2001 From: shaga <14+shaga@noreply.octowow.st> Date: Tue, 27 Oct 2026 00:08:05 +0000 Subject: [PATCH] Add an options window with an on/off switch /unitscan now opens an options window (no minimap icon) with a button that turns scanning on or off and checkboxes for the alert sound, the screen flash and the built-in zone list. Your own targets can be added or removed from the window. Settings are saved per character in unitscan_settings. /unitscan on, /unitscan off and /unitscan list do the same from chat; /unitscan still adds or removes a target. --- README.md | 14 +- unitscan-turtle-hc.toc | 5 +- unitscan.lua | 301 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 294 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 588f352..c907c94 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,12 @@ ## Description This fork extends [unitscan-turtle](https://github.com/GryllsAddons/unitscan-turtle) by provinding a prepopulated list of dangerous elite mobs, rares, and other units. This version is especially useful if you're playing Turtle WoW's hardcore mode. -## Download -If using the launcher use this url: https://github.com/RetroCro/unitscan-turtle-hc.git +This fork adds an options window, opened with /unitscan, with an on/off switch for scanning and switches for the alert sound, the screen flash and the built-in zone list. Original addon: https://github.com/RetroCro/unitscan-turtle-hc -Download: [https://github.com/RetroCro/unitscan-turtle-hc/archive/refs/heads/master.zip](https://github.com/RetroCro/unitscan-turtle-hc/archive/refs/heads/master.zip) +## Download +If using the launcher use this url: https://git.octowow.st/git/shaga/unitscan-turtle-hc.git + +Download: [https://git.octowow.st/git/shaga/unitscan-turtle-hc/archive/master.zip](https://git.octowow.st/git/shaga/unitscan-turtle-hc/archive/master.zip) Copy folder to your Turtle WoW Addons folder ex: twmoa_1171\Interface\AddOns\unitscan-turtle-hc @@ -64,10 +66,14 @@ The list of units can be found in [zone targets.lua](https://raw.githubuserconte **You can move the unitscan frame by holding CTRL+SHIFT+LEFT CLICK dragging.** -**/unitscan** *lists the active scan targets* +**/unitscan** *opens the options window* +**/unitscan on**, **/unitscan off** *turns scanning on or off* +**/unitscan list** *lists the active scan targets in chat* **/unitscan name** *adds/removes **name** to/from the active scan targets* **/unitscantarget** *targets the most recently found target* +The options window has the on/off switch, switches for the alert sound, the screen flash and the built-in zone list, and a box to add or remove your own targets. The options are saved per character. + You can add custom targets to find players or targets not included in the zone targets list by using the command /unitscan *name*. Please Note: Targets added using /unitscan *name* will be removed from active scan targets after they are found. For a permanenet solution, edit [zone targets.lua](https://raw.githubusercontent.com/RetroCro/unitscan-turtle-hc/refs/heads/master/zonetargets.lua) and add a line entry for that unit. diff --git a/unitscan-turtle-hc.toc b/unitscan-turtle-hc.toc index 17760db..4a87eed 100644 --- a/unitscan-turtle-hc.toc +++ b/unitscan-turtle-hc.toc @@ -1,8 +1,9 @@ ## Interface: 11200 ## Author: RetroCro ## Title: unitscan|cff00ff98Turtle-HC -## Version: 1.0.5 -## Notes: Unit scanner with flashy alert +## Version: 1.1.0 +## Notes: Unit scanner with flashy alert. /unitscan opens the options. ## SavedVariables: unitscan_targets +## SavedVariablesPerCharacter: unitscan_settings unitscan.lua zonetargets.lua diff --git a/unitscan.lua b/unitscan.lua index 707f6ef..91097cf 100644 --- a/unitscan.lua +++ b/unitscan.lua @@ -2,8 +2,12 @@ local unitscan = CreateFrame'Frame' unitscan:SetScript('OnUpdate', function() unitscan.UPDATE() end) unitscan:SetScript('OnEvent', function() if event == "VARIABLES_LOADED" then + unitscan.load_settings() unitscan.LOAD() unitscan.scan = true + if not unitscan_settings.enabled then + unitscan.print('scanning is off, type /unitscan to turn it on') + end elseif event == "PLAYER_REGEN_DISABLED" then -- in combat -- disable scanning @@ -29,6 +33,7 @@ unitscan:SetScript('OnEvent', function() else unitscan.load_zonetargets() end + unitscan.config_refresh() end) unitscan:RegisterEvent'VARIABLES_LOADED' @@ -47,6 +52,24 @@ local CHECK_INTERVAL = 1 unitscan_targets = {} +local DEFAULTS = { + enabled = true, -- scanning on/off + sound = true, -- alert sound + flash = true, -- red screen flash + zonetargets = true, -- built-in list from zonetargets.lua +} + +function unitscan.load_settings() + if type(unitscan_settings) ~= 'table' then + unitscan_settings = {} + end + for key, value in pairs(DEFAULTS) do + if unitscan_settings[key] == nil then + unitscan_settings[key] = value + end + end +end + do local last_played @@ -62,6 +85,17 @@ do end end +function unitscan.alert() + if unitscan_settings.sound then + unitscan.play_sound() + end + if unitscan_settings.flash then + unitscan.flash.animation:Play() + end + unitscan.button:set_target() + unitscan.config_refresh() +end + function unitscan.load_zonetargets() unitscan_zone_targets() end @@ -92,20 +126,18 @@ do if name == unitscan.target(name) then unitscan.foundTarget = name unitscan.toggle_target(name) - unitscan.play_sound() - unitscan.flash.animation:Play() - unitscan.button:set_target() + unitscan.alert() end unitscan.restoreTarget() end + if not unitscan_settings.zonetargets then return end + for name, _ in unitscan_zonetargets do if strupper(name) == unitscan.target(name) then - unitscan.foundTarget = name + unitscan.foundTarget = name unitscan.toggle_zonetarget(name) - unitscan.play_sound() - unitscan.flash.animation:Play() - unitscan.button:set_target() + unitscan.alert() end unitscan.restoreTarget() end @@ -174,8 +206,13 @@ function unitscan.LOAD() end self:Show() end + function flash.animation:Stop() + self.t0 = nil + self:Hide() + flash:SetAlpha(0) + end end - + local button = CreateFrame("Button", "unitscan_button", UIParent) button:Hide() unitscan.button = button @@ -376,7 +413,8 @@ end do unitscan.last_check = GetTime() function unitscan.UPDATE() - if unitscan.scan then + -- unitscan.button exists once LOAD has run, after the settings are loaded + if unitscan.scan and unitscan.button and unitscan_settings.enabled then if GetTime() - unitscan.last_check >= CHECK_INTERVAL then unitscan.last_check = GetTime() if (unitscan.reloadtimer and (unitscan.last_check >= unitscan.reloadtimer)) then @@ -433,23 +471,246 @@ function unitscan.toggle_zonetarget(name) unitscan.reloadtimer = GetTime() + 90 end end - + +function unitscan.print_targets() + unitscan.print("Added targets:") + for _, key in ipairs(unitscan.sorted_targets()) do + unitscan.print(key) + end + -- zone targets + unitscan.print("Zone targets:") + for _, key in ipairs(unitscan.sorted_zonetargets()) do + unitscan.print(key) + end +end + +function unitscan.set_enabled(enabled) + unitscan_settings.enabled = enabled + if enabled then + unitscan.print('scanning on') + else + -- dismiss an alert that is still showing + unitscan.button:Hide() + unitscan.flash.animation:Stop() + unitscan.print('scanning off') + end + unitscan.config_refresh() +end + +function unitscan.count_zonetargets() + local count = 0 + for _ in pairs(unitscan_zonetargets or {}) do + count = count + 1 + end + return count +end + +function unitscan.config_create() + local frame = CreateFrame('Frame', 'unitscan_config', UIParent) + unitscan.config = frame + frame:Hide() + frame:SetWidth(320) + frame:SetHeight(328) + frame:SetPoint('CENTER', UIParent, 0, 80) + frame:SetFrameStrata'DIALOG' + frame:SetMovable(true) + frame:SetClampedToScreen(true) + frame:EnableMouse(true) + frame:RegisterForDrag'LeftButton' + frame:SetScript('OnDragStart', function() this:StartMoving() end) + frame:SetScript('OnDragStop', function() this:StopMovingOrSizing() end) + frame:SetScript('OnShow', function() unitscan.config_refresh() end) + frame:SetBackdrop{ + bgFile = [[Interface\DialogFrame\UI-DialogBox-Background]], + edgeFile = [[Interface\DialogFrame\UI-DialogBox-Border]], + tile = true, + tileSize = 32, + edgeSize = 32, + insets = {left = 11, right = 12, top = 12, bottom = 11}, + } + -- close with Escape + tinsert(UISpecialFrames, 'unitscan_config') + + do + local header = frame:CreateTexture(nil, 'ARTWORK') + header:SetTexture[[Interface\DialogFrame\UI-DialogBox-Header]] + header:SetWidth(256) + header:SetHeight(64) + header:SetPoint('TOP', 0, 12) + + local title = frame:CreateFontString(nil, 'OVERLAY', 'GameFontNormal') + title:SetPoint('TOP', header, 0, -14) + title:SetText'unitscan' + + local close = CreateFrame('Button', nil, frame, 'UIPanelCloseButton') + close:SetPoint('TOPRIGHT', -6, -6) + end + + do + local status = frame:CreateFontString(nil, 'OVERLAY', 'GameFontHighlight') + frame.status = status + status:SetPoint('TOP', 0, -32) + + local toggle = CreateFrame('Button', 'unitscan_config_toggle', frame, 'UIPanelButtonTemplate') + frame.toggle = toggle + toggle:SetWidth(160) + toggle:SetHeight(24) + toggle:SetPoint('TOP', 0, -52) + toggle:SetScript('OnClick', function() + unitscan.set_enabled(not unitscan_settings.enabled) + end) + end + + do + local function checkbox(key, label, y) + local check = CreateFrame('CheckButton', 'unitscan_config_' .. key, frame, 'UICheckButtonTemplate') + frame[key] = check + check:SetWidth(26) + check:SetHeight(26) + check:SetPoint('TOPLEFT', 20, y) + getglobal(check:GetName() .. 'Text'):SetText(label) + check:SetScript('OnClick', function() + if this:GetChecked() then + PlaySound'igMainMenuOptionCheckBoxOn' + else + PlaySound'igMainMenuOptionCheckBoxOff' + end + unitscan_settings[key] = this:GetChecked() and true or false + unitscan.config_refresh() + end) + end + checkbox('sound', 'Play the alert sound', -86) + checkbox('flash', 'Flash the screen', -112) + checkbox('zonetargets', 'Scan for the zone list (rares and elites)', -138) + + local zone = frame:CreateFontString(nil, 'OVERLAY', 'GameFontHighlightSmall') + frame.zone = zone + zone:SetPoint('TOPLEFT', 48, -168) + zone:SetWidth(250) + zone:SetJustifyH'LEFT' + end + + do + local label = frame:CreateFontString(nil, 'OVERLAY', 'GameFontNormal') + label:SetPoint('TOPLEFT', 22, -194) + label:SetText'Your targets' + + local list = CreateFrame('Button', 'unitscan_config_list', frame, 'UIPanelButtonTemplate') + list:SetWidth(90) + list:SetHeight(20) + list:SetPoint('TOPRIGHT', -22, -190) + list:SetText'List in chat' + list:SetScript('OnClick', function() unitscan.print_targets() end) + + local input = CreateFrame('EditBox', 'unitscan_config_input', frame, 'InputBoxTemplate') + frame.input = input + input:SetWidth(170) + input:SetHeight(20) + input:SetPoint('TOPLEFT', 30, -216) + input:SetAutoFocus(false) + input:SetMaxLetters(64) + input:SetScript('OnEnterPressed', function() unitscan.config_add() end) + + local add = CreateFrame('Button', 'unitscan_config_add', frame, 'UIPanelButtonTemplate') + add:SetWidth(96) + add:SetHeight(22) + add:SetPoint('LEFT', input, 'RIGHT', 6, 0) + add:SetText'Add / Remove' + add:SetScript('OnClick', function() unitscan.config_add() end) + + local targets = frame:CreateFontString(nil, 'OVERLAY', 'GameFontHighlightSmall') + frame.targets = targets + targets:SetPoint('TOPLEFT', 22, -244) + targets:SetWidth(276) + targets:SetHeight(36) + targets:SetJustifyH'LEFT' + targets:SetJustifyV'TOP' + end + + do + local note = frame:CreateFontString(nil, 'OVERLAY', 'GameFontDisableSmall') + note:SetPoint('BOTTOM', 0, 32) + note:SetText'A target you add is removed once it is found.' + + local help = frame:CreateFontString(nil, 'OVERLAY', 'GameFontDisableSmall') + help:SetPoint('BOTTOM', 0, 18) + help:SetText'Drag to move. /unitscan on, off, list, ' + end +end + +function unitscan.config_toggle() + if not unitscan.config then + unitscan.config_create() + end + if unitscan.config:IsShown() then + unitscan.config:Hide() + else + unitscan.config:Show() + end +end + +function unitscan.config_add() + local input = unitscan.config.input + local _, _, name = strfind(input:GetText(), '^%s*(.-)%s*$') + if name ~= '' then + unitscan.toggle_target(name) + end + input:SetText'' + input:ClearFocus() + unitscan.config_refresh() +end + +function unitscan.config_refresh() + local frame = unitscan.config + if not (frame and frame:IsShown()) then return end + + if not unitscan_settings.enabled then + frame.status:SetText'Scanning: |cffff2020off|r' + frame.toggle:SetText'Turn scanning on' + else + if unitscan.scan then + frame.status:SetText'Scanning: |cff20ff20on|r' + else + frame.status:SetText'Scanning: |cffffd100paused in combat|r' + end + frame.toggle:SetText'Turn scanning off' + end + + frame.sound:SetChecked(unitscan_settings.sound and true or nil) + frame.flash:SetChecked(unitscan_settings.flash and true or nil) + frame.zonetargets:SetChecked(unitscan_settings.zonetargets and true or nil) + + frame.zone:SetText(GetZoneText() .. ': ' .. unitscan.count_zonetargets() .. ' on the zone list') + if unitscan_settings.zonetargets then + frame.zone:SetTextColor(1, 1, 1) + else + frame.zone:SetTextColor(.5, .5, .5) + end + + local targets = unitscan.sorted_targets() + if targets[1] then + frame.targets:SetText(table.concat(targets, ', ')) + else + frame.targets:SetText'|cff808080none|r' + end +end + SLASH_UNITSCAN1 = '/unitscan' function SlashCmdList.UNITSCAN(parameter) local _, _, name = strfind(parameter, '^%s*(.-)%s*$') - + local command = strlower(name) + if name == '' then - unitscan.print("Added targets:") - for _, key in ipairs(unitscan.sorted_targets()) do - unitscan.print(key) - end - -- zone targets - unitscan.print("Zone targets:") - for _, key in ipairs(unitscan.sorted_zonetargets()) do - unitscan.print(key) - end + unitscan.config_toggle() + elseif command == 'on' then + unitscan.set_enabled(true) + elseif command == 'off' then + unitscan.set_enabled(false) + elseif command == 'list' then + unitscan.print_targets() else unitscan.toggle_target(name) + unitscan.config_refresh() end end