├── img ├── config.tga ├── target-left.tga └── target-right.tga ├── screenshots ├── config.jpg ├── infight.jpg └── raidtargets.jpg ├── ShaguScan.toc ├── global.lua ├── LICENSE ├── core.lua ├── README.md ├── utils.lua ├── filter.lua ├── ui.lua └── settings.lua /img/config.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shagu/ShaguScan/HEAD/img/config.tga -------------------------------------------------------------------------------- /img/target-left.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shagu/ShaguScan/HEAD/img/target-left.tga -------------------------------------------------------------------------------- /img/target-right.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shagu/ShaguScan/HEAD/img/target-right.tga -------------------------------------------------------------------------------- /screenshots/config.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shagu/ShaguScan/HEAD/screenshots/config.jpg -------------------------------------------------------------------------------- /screenshots/infight.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shagu/ShaguScan/HEAD/screenshots/infight.jpg -------------------------------------------------------------------------------- /screenshots/raidtargets.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shagu/ShaguScan/HEAD/screenshots/raidtargets.jpg -------------------------------------------------------------------------------- /ShaguScan.toc: -------------------------------------------------------------------------------- 1 | ## Interface: 11200 2 | ## Title: |cffffcc00Shagu|cffffffffScan 3 | ## Author: Shagu 4 | ## Notes: Uses SuperWoW GUIDs to show nearby units 5 | ## Version: 1.0 6 | ## SavedVariablesPerCharacter: ShaguScan_db 7 | 8 | global.lua 9 | utils.lua 10 | settings.lua 11 | core.lua 12 | filter.lua 13 | ui.lua 14 | -------------------------------------------------------------------------------- /global.lua: -------------------------------------------------------------------------------- 1 | ShaguScan = {} 2 | ShaguScan_db = { 3 | config = { 4 | ["Infight NPCs"] = { 5 | filter = "npc,infight", 6 | scale = 1, anchor = "CENTER", x = -240, y = 120, width = 100, height = 14, spacing = 4, maxrow = 20 7 | }, 8 | 9 | ["Raid Targets"] = { 10 | filter = "icon,alive", 11 | scale = 1, anchor = "CENTER", x = 240, y = 120, width = 100, height = 14, spacing = 4, maxrow = 20 12 | } 13 | } 14 | } 15 | 16 | if not GetPlayerBuffID or not CombatLogAdd or not SpellInfo then 17 | local notify = CreateFrame("Frame", nil, UIParent) 18 | notify:SetScript("OnUpdate", function() 19 | DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00Shagu|cffffffffScan:|cffffaaaa Couldn't detect SuperWoW.") 20 | this:Hide() 21 | end) 22 | 23 | ShaguScan.disabled = true 24 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 Eric Mauser (Shagu) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /core.lua: -------------------------------------------------------------------------------- 1 | if ShaguScan.disabled then return end 2 | 3 | local core = CreateFrame("Frame", nil, WorldFrame) 4 | 5 | core.guids = {} 6 | 7 | core.add = function(unit) 8 | local _, guid = UnitExists(unit) 9 | 10 | if guid then 11 | core.guids[guid] = GetTime() 12 | end 13 | end 14 | 15 | -- unitstr 16 | core:RegisterEvent("UPDATE_MOUSEOVER_UNIT") 17 | core:RegisterEvent("PLAYER_TARGET_CHANGED") 18 | core:RegisterEvent("PLAYER_ENTERING_WORLD") 19 | 20 | -- arg1 21 | core:RegisterEvent("UNIT_COMBAT") 22 | core:RegisterEvent("UNIT_HAPPINESS") 23 | core:RegisterEvent("UNIT_MODEL_CHANGED") 24 | core:RegisterEvent("UNIT_PORTRAIT_UPDATE") 25 | core:RegisterEvent("UNIT_FACTION") 26 | core:RegisterEvent("UNIT_FLAGS") 27 | core:RegisterEvent("UNIT_AURA") 28 | core:RegisterEvent("UNIT_HEALTH") 29 | core:RegisterEvent("UNIT_CASTEVENT") 30 | 31 | core:SetScript("OnEvent", function() 32 | if event == "UPDATE_MOUSEOVER_UNIT" then 33 | this.add("mouseover") 34 | elseif event == "PLAYER_ENTERING_WORLD" then 35 | this.add("player") 36 | elseif event == "PLAYER_TARGET_CHANGED" then 37 | this.add("target") 38 | else 39 | this.add(arg1) 40 | end 41 | end) 42 | 43 | ShaguScan.core = core 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShaguScan 2 | 3 | 4 | 5 | An addon that scans for nearby units and filters them by custom attributes. 6 | It's made for World of Warcraft: Vanilla (1.12.1) and is only tested on [Turtle WoW](https://turtle-wow.org/). 7 | 8 | The addon can be used to see all marked raid targets, detect rare mobs, find nearby players that decided to do pvp, and much more! 9 | 10 | > [!IMPORTANT] 11 | > 12 | > **This addon requires you to have [SuperWoW](https://github.com/balakethelock/SuperWoW) installed.** 13 | > 14 | > It won't work without it. Really. 15 | 16 | ## Installation (Vanilla, 1.12) 17 | 1. Download **[Latest Version](https://github.com/shagu/ShaguScan/archive/master.zip)** 18 | 2. Unpack the Zip file 19 | 3. Rename the folder "ShaguScan-master" to "ShaguScan" 20 | 4. Copy "ShaguScan" into Wow-Directory\Interface\AddOns 21 | 5. Restart Wow 22 | 23 | # Usage 24 | 25 | Multiple windows can be created that each show health bars of nearby units based on custom filters. A new window with the title "Alliance PvP" can be created by typing `/scan Alliance PvP`. A configuration will appear, in which the created window can be customized. You can chose as many filters as you wish (comma separated). 26 | 27 | In case `/scan` is already blocked by another addon, you can also use `/sscan` or `/shaguscan`. 28 | 29 | As a filter you could for example choose: `player,pvp,alliance,alive` to only show players with pvp enabled on the alliance side that are alive. 30 | 31 | You can build the lists as you want them, there are now limits as long as the filter for it exists. 32 | 33 | ![config](./screenshots/config.jpg) 34 | 35 | # Filters 36 | 37 | 38 | 39 | - **player**: all player characters 40 | - **npc**: all non-player characters 41 | - **infight**: only units that are in combat 42 | - **dead**: only dead units 43 | - **alive**: only alive units 44 | - **horde**: only horde units 45 | - **alliance**: only alliance units 46 | - **hardcore**: only hardcore enabled players 47 | - **pve**: only pve-flagged units 48 | - **pvp**: only pvp-flagged units 49 | - **icon**: only units with an assigned raid icon 50 | - **normal**: only units of type "normal" (no elite, rare, etc.) 51 | - **elite**: only units of type "elite" or "rareelite" 52 | - **rare**: only units of type "rare" or "rareelite" 53 | - **rareelite**: only units of type "rareelite" 54 | - **worldboss**: only units of type "worldboss" 55 | - **hostile**: only hostile units 56 | - **neutral**: only neutral units 57 | - **friendly**: only friendly units 58 | - **attack**: only units that can be attacked 59 | - **noattack**: only units that can't be attacked 60 | - **pet**: only units that are pet or totems 61 | - **nopet**: only units that aren't pets or totems 62 | - **human**: only human players 63 | - **orc**: only orc players 64 | - **dwarf**: only dwarf players 65 | - **nightelf**: only night elf players 66 | - **undead**: only scourge players 67 | - **tauren**: only tauren players 68 | - **gnome**: only gnome players 69 | - **troll**: only troll players 70 | - **goblin**: only goblin players 71 | - **highelf**: only high elf players 72 | - **warlock**: only warlock players 73 | - **warrior**: only warrior players 74 | - **hunter**: only hunter players 75 | - **mage**: only mage players 76 | - **priest**: only priest players 77 | - **druid**: only druid players 78 | - **paladin**: only paladin players 79 | - **shaman**: only shaman players 80 | - **rogue**: only rogue players 81 | - **aggro**: units that target you 82 | - **noaggro**: units that don't target you 83 | - **pfquest**: units that have a pfquest tooltip 84 | - **range**: units that are within max [interaction distance](https://wowwiki-archive.fandom.com/wiki/API_CheckInteractDistance) (28y) 85 | - **level:NUMBER**: units that are level NUMBER 86 | - **minlevel:NUMBER**: units that are at least level NUMBER 87 | - **maxlevel:NUMBER**: units that are at most level NUMBER 88 | - **name:STRING**: units that have STRING in their name 89 | 90 | New and custom filters are easy to implement, if you wish to create your own, please have a look at: [filter.lua](./filter.lua). 91 | -------------------------------------------------------------------------------- /utils.lua: -------------------------------------------------------------------------------- 1 | if ShaguScan.disabled then return end 2 | 3 | local utils = {} 4 | 5 | utils.strsplit = function(delimiter, subject) 6 | if not subject then return nil end 7 | local delimiter, fields = delimiter or ":", {} 8 | local pattern = string.format("([^%s]+)", delimiter) 9 | string.gsub(subject, pattern, function(c) fields[table.getn(fields)+1] = c end) 10 | return unpack(fields) 11 | end 12 | 13 | utils.round = function(input, places) 14 | if not places then places = 0 end 15 | if type(input) == "number" and type(places) == "number" then 16 | local pow = 1 17 | for i = 1, places do pow = pow * 10 end 18 | return floor(input * pow + 0.5) / pow 19 | end 20 | end 21 | 22 | utils.IsValidAnchor = function(anchor) 23 | if anchor == "TOP" then return true end 24 | if anchor == "TOPLEFT" then return true end 25 | if anchor == "TOPRIGHT" then return true end 26 | if anchor == "CENTER" then return true end 27 | if anchor == "LEFT" then return true end 28 | if anchor == "RIGHT" then return true end 29 | if anchor == "BOTTOM" then return true end 30 | if anchor == "BOTTOMLEFT" then return true end 31 | if anchor == "BOTTOMRIGHT" then return true end 32 | return false 33 | end 34 | 35 | utils.GetBestAnchor = function(self) 36 | local scale = self:GetScale() 37 | local x, y = self:GetCenter() 38 | local a = GetScreenWidth() / scale / 3 39 | local b = GetScreenWidth() / scale / 3 * 2 40 | local c = GetScreenHeight() / scale / 3 * 2 41 | local d = GetScreenHeight() / scale / 3 42 | if not x or not y then return end 43 | 44 | if x < a and y > c then 45 | return "TOPLEFT" 46 | elseif x > a and x < b and y > c then 47 | return "TOP" 48 | elseif x > b and y > c then 49 | return "TOPRIGHT" 50 | elseif x < a and y > d and y < c then 51 | return "LEFT" 52 | elseif x > a and x < b and y > d and y < c then 53 | return "CENTER" 54 | elseif x > b and y > d and y < c then 55 | return "RIGHT" 56 | elseif x < a and y < d then 57 | return "BOTTOMLEFT" 58 | elseif x > a and x < b and y < d then 59 | return "BOTTOM" 60 | elseif x > b and y < d then 61 | return "BOTTOMRIGHT" 62 | end 63 | end 64 | 65 | utils.ConvertFrameAnchor = function(self, anchor) 66 | local scale, x, y, _ = self:GetScale(), nil, nil, nil 67 | 68 | if anchor == "CENTER" then 69 | x, y = self:GetCenter() 70 | x, y = x - GetScreenWidth()/2/scale, y - GetScreenHeight()/2/scale 71 | elseif anchor == "TOPLEFT" then 72 | x, y = self:GetLeft(), self:GetTop() - GetScreenHeight()/scale 73 | elseif anchor == "TOP" then 74 | x, _ = self:GetCenter() 75 | x, y = x - GetScreenWidth()/2/scale, self:GetTop() - GetScreenHeight()/scale 76 | elseif anchor == "TOPRIGHT" then 77 | x, y = self:GetRight() - GetScreenWidth()/scale, self:GetTop() - GetScreenHeight()/scale 78 | elseif anchor == "RIGHT" then 79 | _, y = self:GetCenter() 80 | x, y = self:GetRight() - GetScreenWidth()/scale, y - GetScreenHeight()/2/scale 81 | elseif anchor == "BOTTOMRIGHT" then 82 | x, y = self:GetRight() - GetScreenWidth()/scale, self:GetBottom() 83 | elseif anchor == "BOTTOM" then 84 | x, _ = self:GetCenter() 85 | x, y = x - GetScreenWidth()/2/scale, self:GetBottom() 86 | elseif anchor == "BOTTOMLEFT" then 87 | x, y = self:GetLeft(), self:GetBottom() 88 | elseif anchor == "LEFT" then 89 | _, y = self:GetCenter() 90 | x, y = self:GetLeft(), y - GetScreenHeight()/2/scale 91 | end 92 | 93 | return anchor, utils.round(x, 2), utils.round(y, 2) 94 | end 95 | 96 | local _r, _g, _b, _a 97 | utils.rgbhex = function(r, g, b, a) 98 | if type(r) == "table" then 99 | if r.r then 100 | _r, _g, _b, _a = r.r, r.g, r.b, (r.a or 1) 101 | elseif table.getn(r) >= 3 then 102 | _r, _g, _b, _a = r[1], r[2], r[3], (r[4] or 1) 103 | end 104 | elseif tonumber(r) then 105 | _r, _g, _b, _a = r, g, b, (a or 1) 106 | end 107 | 108 | if _r and _g and _b and _a then 109 | -- limit values to 0-1 110 | _r = _r + 0 > 1 and 1 or _r + 0 111 | _g = _g + 0 > 1 and 1 or _g + 0 112 | _b = _b + 0 > 1 and 1 or _b + 0 113 | _a = _a + 0 > 1 and 1 or _a + 0 114 | return string.format("|c%02x%02x%02x%02x", _a*255, _r*255, _g*255, _b*255) 115 | end 116 | 117 | return "" 118 | end 119 | 120 | utils.GetReactionColor = function(unitstr) 121 | local color = UnitReactionColor[UnitReaction(unitstr, "player")] 122 | local r, g, b = .8, .8, .8 123 | 124 | if color then 125 | r, g, b = color.r, color.g, color.b 126 | end 127 | 128 | return utils.rgbhex(r,g,b), r, g, b 129 | end 130 | 131 | utils.GetUnitColor = function(unitstr) 132 | local r, g, b = .8, .8, .8 133 | 134 | if UnitIsPlayer(unitstr) then 135 | local _, class = UnitClass(unitstr) 136 | 137 | if RAID_CLASS_COLORS[class] then 138 | r, g, b = RAID_CLASS_COLORS[class].r, RAID_CLASS_COLORS[class].g, RAID_CLASS_COLORS[class].b 139 | end 140 | else 141 | return utils.GetReactionColor(unitstr) 142 | end 143 | 144 | return utils.rgbhex(r,g,b), r, g, b 145 | end 146 | 147 | utils.GetLevelColor = function(unitstr) 148 | local color = GetDifficultyColor(UnitLevel(unitstr)) 149 | local r, g, b = .8, .8, .8 150 | 151 | if color then 152 | r, g, b = color.r, color.g, color.b 153 | end 154 | 155 | return utils.rgbhex(r,g,b), r, g, b 156 | end 157 | 158 | utils.GetLevelString = function(unitstr) 159 | local level = UnitLevel(unitstr) 160 | if level == -1 then level = "??" end 161 | 162 | local elite = UnitClassification(unitstr) 163 | if elite == "worldboss" then 164 | level = level .. "B" 165 | elseif elite == "rareelite" then 166 | level = level .. "R+" 167 | elseif elite == "elite" then 168 | level = level .. "+" 169 | elseif elite == "rare" then 170 | level = level .. "R" 171 | end 172 | 173 | return level 174 | end 175 | 176 | ShaguScan.utils = utils 177 | -------------------------------------------------------------------------------- /filter.lua: -------------------------------------------------------------------------------- 1 | if ShaguScan.disabled then return end 2 | 3 | local filter = { } 4 | 5 | filter.player = function(unit) 6 | return UnitIsPlayer(unit) and true or false 7 | end 8 | 9 | filter.npc = function(unit) 10 | return not UnitIsPlayer(unit) and true or false 11 | end 12 | 13 | filter.infight = function(unit) 14 | return UnitAffectingCombat(unit) and true or false 15 | end 16 | 17 | filter.dead = function(unit) 18 | return UnitIsDead(unit) and true or false 19 | end 20 | 21 | filter.alive = function(unit) 22 | return not UnitIsDead(unit) and true or false 23 | end 24 | 25 | filter.horde = function(unit) 26 | return UnitFactionGroup(unit) == "Horde" and true or false 27 | end 28 | 29 | filter.alliance = function(unit) 30 | return UnitFactionGroup(unit) == "Alliance" and true or false 31 | end 32 | 33 | filter.hardcore = function(unit) 34 | return string.find((UnitPVPName(unit) or ""), "Still Alive") and true or false 35 | end 36 | 37 | filter.pve = function(unit) 38 | return not UnitIsPVP(unit) and true or false 39 | end 40 | 41 | filter.pvp = function(unit) 42 | return UnitIsPVP(unit) and true or false 43 | end 44 | 45 | filter.icon = function(unit) 46 | return GetRaidTargetIndex(unit) and true or false 47 | end 48 | 49 | filter.normal = function(unit) 50 | local elite = UnitClassification(unit) 51 | return elite == "normal" and true or false 52 | end 53 | 54 | filter.elite = function(unit) 55 | local elite = UnitClassification(unit) 56 | return (elite == "elite" or elite == "rareelite") and true or false 57 | end 58 | 59 | filter.rare = function(unit) 60 | local elite = UnitClassification(unit) 61 | return (elite == "rare" or elite == "rareelite") and true or false 62 | end 63 | 64 | filter.rareelite = function(unit) 65 | local elite = UnitClassification(unit) 66 | return elite == "rareelite" and true or false 67 | end 68 | 69 | filter.worldboss = function(unit) 70 | local elite = UnitClassification(unit) 71 | return elite == "worldboss" and true or false 72 | end 73 | 74 | filter.hostile = function(unit) 75 | return UnitIsEnemy("player", unit) and true or false 76 | end 77 | 78 | filter.neutral = function(unit) 79 | return not UnitIsEnemy("player", unit) and not UnitIsFriend("player", unit) and true or false 80 | end 81 | 82 | filter.friendly = function(unit) 83 | return UnitIsFriend("player", unit) and true or false 84 | end 85 | 86 | filter.attack = function(unit) 87 | return UnitCanAttack("player", unit) and true or false 88 | end 89 | 90 | filter.noattack = function(unit) 91 | return not UnitCanAttack("player", unit) and true or false 92 | end 93 | 94 | filter.pet = function(unit) 95 | local player = UnitIsPlayer(unit) and true or false 96 | local controlled = UnitPlayerControlled(unit) and true or false 97 | local pet = not player and controlled and true or false 98 | return pet and true or false 99 | end 100 | 101 | filter.nopet = function(unit) 102 | local player = UnitIsPlayer(unit) and true or false 103 | local controlled = UnitPlayerControlled(unit) and true or false 104 | local pet = not player and controlled and true or false 105 | return not pet and true or false 106 | end 107 | 108 | filter.human = function(unit) 109 | local _, race = UnitRace(unit) 110 | return race == "Human" and true or false 111 | end 112 | 113 | filter.orc = function(unit) 114 | local _, race = UnitRace(unit) 115 | return race == "Orc" and true or false 116 | end 117 | 118 | filter.dwarf = function(unit) 119 | local _, race = UnitRace(unit) 120 | return race == "Dwarf" and true or false 121 | end 122 | 123 | filter.nightelf = function(unit) 124 | local _, race = UnitRace(unit) 125 | return race == "NightElf" and true or false 126 | end 127 | 128 | filter.undead = function(unit) 129 | local _, race = UnitRace(unit) 130 | return race == "Scourge" and true or false 131 | end 132 | 133 | filter.tauren = function(unit) 134 | local _, race = UnitRace(unit) 135 | return race == "Tauren" and true or false 136 | end 137 | 138 | filter.gnome = function(unit) 139 | local _, race = UnitRace(unit) 140 | return race == "Gnome" and true or false 141 | end 142 | 143 | filter.troll = function(unit) 144 | local _, race = UnitRace(unit) 145 | return race == "Troll" and true or false 146 | end 147 | 148 | filter.goblin = function(unit) 149 | local _, race = UnitRace(unit) 150 | return race == "Goblin" and true or false 151 | end 152 | 153 | filter.highelf = function(unit) 154 | local _, race = UnitRace(unit) 155 | return race == "BloodElf" and true or false 156 | end 157 | 158 | filter.warlock = function(unit) 159 | local _, class = UnitClass(unit) 160 | local player = UnitIsPlayer(unit) 161 | 162 | return player and class == "WARLOCK" and true or false 163 | end 164 | 165 | filter.warrior = function(unit) 166 | local _, class = UnitClass(unit) 167 | local player = UnitIsPlayer(unit) 168 | 169 | return player and class == "WARRIOR" and true or false 170 | end 171 | 172 | filter.hunter = function(unit) 173 | local _, class = UnitClass(unit) 174 | local player = UnitIsPlayer(unit) 175 | 176 | return player and class == "HUNTER" and true or false 177 | end 178 | 179 | filter.mage = function(unit) 180 | local _, class = UnitClass(unit) 181 | local player = UnitIsPlayer(unit) 182 | 183 | return player and class == "MAGE" and true or false 184 | end 185 | 186 | filter.priest = function(unit) 187 | local _, class = UnitClass(unit) 188 | local player = UnitIsPlayer(unit) 189 | 190 | return player and class == "PRIEST" and true or false 191 | end 192 | 193 | filter.druid = function(unit) 194 | local _, class = UnitClass(unit) 195 | local player = UnitIsPlayer(unit) 196 | 197 | return player and class == "DRUID" and true or false 198 | end 199 | 200 | filter.paladin = function(unit) 201 | local _, class = UnitClass(unit) 202 | local player = UnitIsPlayer(unit) 203 | 204 | return player and class == "PALADIN" and true or false 205 | end 206 | 207 | filter.shaman = function(unit) 208 | local _, class = UnitClass(unit) 209 | local player = UnitIsPlayer(unit) 210 | 211 | return player and class == "SHAMAN" and true or false 212 | end 213 | 214 | filter.rogue = function(unit) 215 | local _, class = UnitClass(unit) 216 | local player = UnitIsPlayer(unit) 217 | 218 | return player and class == "ROGUE" and true or false 219 | end 220 | 221 | filter.aggro = function(unit) 222 | return UnitExists(unit .. "target") and UnitIsUnit(unit .. "target", "player") and true or false 223 | end 224 | 225 | filter.noaggro = function(unit) 226 | return not UnitExists(unit .. "target") or not UnitIsUnit(unit .. "target", "player") and true or false 227 | end 228 | 229 | filter.pfquest = function(unit) 230 | return pfQuest and pfMap and UnitName(unit) and pfMap.tooltips[UnitName(unit)] and true or false 231 | end 232 | 233 | filter.range = function(unit) 234 | return CheckInteractDistance(unit, 4) and true or false 235 | end 236 | 237 | local level = nil 238 | filter.level = function(unit, args) 239 | level = tonumber(args) 240 | return level and UnitLevel(unit) == level and true or false 241 | end 242 | 243 | local level = nil 244 | filter.minlevel = function(unit, args) 245 | level = tonumber(args) 246 | return level and UnitLevel(unit) >= level and true or false 247 | end 248 | 249 | local level = nil 250 | filter.maxlevel = function(unit, args) 251 | level = tonumber(args) 252 | return level and UnitLevel(unit) <= level and true or false 253 | end 254 | 255 | filter.name = function(unit, name) 256 | name = strlower(name or "") 257 | unit = strlower(UnitName(unit) or "") 258 | return string.find(unit, name) and true or false 259 | end 260 | 261 | ShaguScan.filter = filter 262 | -------------------------------------------------------------------------------- /ui.lua: -------------------------------------------------------------------------------- 1 | if ShaguScan.disabled then return end 2 | 3 | local utils = ShaguScan.utils 4 | local filter = ShaguScan.filter 5 | local settings = ShaguScan.settings 6 | 7 | local ui = CreateFrame("Frame", nil, UIParent) 8 | 9 | ui.border = { 10 | edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", 11 | tile = true, tileSize = 16, edgeSize = 8, 12 | insets = { left = 2, right = 2, top = 2, bottom = 2 } 13 | } 14 | 15 | ui.background = { 16 | bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", 17 | tile = true, tileSize = 16, edgeSize = 8, 18 | insets = { left = 0, right = 0, top = 0, bottom = 0 } 19 | } 20 | 21 | ui.frames = {} 22 | 23 | ui.CreateRoot = function(parent, caption) 24 | local frame = CreateFrame("Frame", "ShaguScan"..caption, parent) 25 | frame.id = caption 26 | 27 | frame:EnableMouse(true) 28 | frame:RegisterForDrag("LeftButton") 29 | frame:SetMovable(true) 30 | 31 | frame:SetScript("OnDragStart", function() 32 | this.lock = true 33 | this:StartMoving() 34 | end) 35 | 36 | frame:SetScript("OnDragStop", function() 37 | -- load current window config 38 | local config = ShaguScan_db.config[this.id] 39 | 40 | -- convert to best anchor depending on position 41 | local new_anchor = utils.GetBestAnchor(this) 42 | local anchor, x, y = utils.ConvertFrameAnchor(this, new_anchor) 43 | this:ClearAllPoints() 44 | this:SetPoint(anchor, UIParent, anchor, x, y) 45 | 46 | -- save new position 47 | local anchor, _, _, x, y = this:GetPoint() 48 | config.anchor, config.x, config.y = anchor, x, y 49 | 50 | -- stop drag 51 | this:StopMovingOrSizing() 52 | this.lock = false 53 | end) 54 | 55 | -- assign/initialize elements 56 | frame.CreateBar = ui.CreateBar 57 | frame.frames = {} 58 | 59 | -- create title text 60 | frame.caption = frame:CreateFontString(nil, "HIGH", "GameFontWhite") 61 | frame.caption:SetFont(STANDARD_TEXT_FONT, 9, "THINOUTLINE") 62 | frame.caption:SetPoint("TOPLEFT", frame, "TOPLEFT", 10, -2) 63 | frame.caption:SetTextColor(1, 1, 1, 1) 64 | frame.caption:SetText(caption) 65 | 66 | -- create option button 67 | frame.settings = CreateFrame("Button", nil, frame) 68 | frame.settings:SetPoint("RIGHT", frame.caption, "LEFT", -2, 0) 69 | frame.settings:SetWidth(8) 70 | frame.settings:SetHeight(8) 71 | 72 | frame.settings:SetScript("OnEnter", function() 73 | frame.settings.tex:SetAlpha(1) 74 | end) 75 | 76 | frame.settings:SetScript("OnLeave", function() 77 | frame.settings.tex:SetAlpha(.5) 78 | end) 79 | 80 | frame.settings.tex = frame.settings:CreateTexture(nil, 'OVERLAY') 81 | frame.settings.tex:SetTexture("Interface\\AddOns\\ShaguScan\\img\\config") 82 | frame.settings.tex:SetAllPoints() 83 | frame.settings.tex:SetAlpha(.5) 84 | 85 | frame.settings:SetScript("OnClick", function() 86 | settings.OpenConfig(this:GetParent().id) 87 | end) 88 | 89 | return frame 90 | end 91 | 92 | ui.BarEnter = function() 93 | this.border:SetBackdropBorderColor(1, 1, 1, 1) 94 | this.hover = true 95 | 96 | GameTooltip_SetDefaultAnchor(GameTooltip, this) 97 | GameTooltip:SetUnit(this.guid) 98 | GameTooltip:Show() 99 | end 100 | 101 | ui.BarLeave = function() 102 | this.hover = false 103 | GameTooltip:Hide() 104 | end 105 | 106 | ui.BarUpdate = function() 107 | -- animate combat text 108 | CombatFeedback_OnUpdate(arg1) 109 | 110 | -- update statusbar values 111 | this.bar:SetMinMaxValues(0, UnitHealthMax(this.guid)) 112 | this.bar:SetValue(UnitHealth(this.guid)) 113 | 114 | -- update health bar color 115 | local hex, r, g, b, a = utils.GetUnitColor(this.guid) 116 | this.bar:SetStatusBarColor(r, g, b, a) 117 | 118 | -- update caption text 119 | local level = utils.GetLevelString(this.guid) 120 | local level_color = utils.GetLevelColor(this.guid) 121 | local name = UnitName(this.guid) 122 | this.text:SetText(level_color..level.."|r "..name) 123 | 124 | -- update health bar border 125 | if this.hover then 126 | this.border:SetBackdropBorderColor(1, 1, 1, 1) 127 | elseif UnitAffectingCombat(this.guid) then 128 | this.border:SetBackdropBorderColor(.8, .2, .2, 1) 129 | else 130 | this.border:SetBackdropBorderColor(.2, .2, .2, 1) 131 | end 132 | 133 | -- show raid icon if existing 134 | if GetRaidTargetIndex(this.guid) then 135 | SetRaidTargetIconTexture(this.icon, GetRaidTargetIndex(this.guid)) 136 | this.icon:Show() 137 | else 138 | this.icon:Hide() 139 | end 140 | 141 | -- update target indicator 142 | if UnitIsUnit("target", this.guid) then 143 | this.target_left:Show() 144 | this.target_right:Show() 145 | else 146 | this.target_left:Hide() 147 | this.target_right:Hide() 148 | end 149 | end 150 | 151 | ui.BarClick = function() 152 | TargetUnit(this.guid) 153 | end 154 | 155 | ui.BarEvent = function() 156 | if arg1 ~= this.guid then return end 157 | CombatFeedback_OnCombatEvent(arg2, arg3, arg4, arg5) 158 | end 159 | 160 | ui.CreateBar = function(parent, guid) 161 | local frame = CreateFrame("Button", nil, parent) 162 | frame.guid = guid 163 | 164 | -- assign required events and scripts 165 | frame:RegisterEvent("UNIT_COMBAT") 166 | frame:SetScript("OnEvent", ui.BarEvent) 167 | frame:SetScript("OnClick", ui.BarClick) 168 | frame:SetScript("OnEnter", ui.BarEnter) 169 | frame:SetScript("OnLeave", ui.BarLeave) 170 | frame:SetScript("OnUpdate", ui.BarUpdate) 171 | 172 | -- create health bar 173 | local bar = CreateFrame("StatusBar", nil, frame) 174 | bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar") 175 | bar:SetStatusBarColor(1, .8, .2, 1) 176 | bar:SetMinMaxValues(0, 100) 177 | bar:SetValue(20) 178 | bar:SetAllPoints() 179 | frame.bar = bar 180 | 181 | -- create caption text 182 | local text = frame.bar:CreateFontString(nil, "HIGH", "GameFontWhite") 183 | text:SetPoint("TOPLEFT", bar, "TOPLEFT", 2, -2) 184 | text:SetPoint("BOTTOMRIGHT", bar, "BOTTOMRIGHT", -2, 2) 185 | text:SetFont(STANDARD_TEXT_FONT, 9, "THINOUTLINE") 186 | text:SetJustifyH("LEFT") 187 | frame.text = text 188 | 189 | -- create combat feedback text 190 | local feedback = bar:CreateFontString(guid.."feedback"..GetTime(), "OVERLAY", "NumberFontNormalHuge") 191 | feedback:SetAlpha(.8) 192 | feedback:SetFont(DAMAGE_TEXT_FONT, 12, "OUTLINE") 193 | feedback:SetParent(bar) 194 | feedback:ClearAllPoints() 195 | feedback:SetPoint("CENTER", bar, "CENTER", 0, 0) 196 | 197 | frame.feedbackFontHeight = 14 198 | frame.feedbackStartTime = GetTime() 199 | frame.feedbackText = feedback 200 | 201 | -- create raid icon textures 202 | local icon = bar:CreateTexture(nil, "OVERLAY") 203 | icon:SetWidth(16) 204 | icon:SetHeight(16) 205 | icon:SetPoint("RIGHT", frame, "RIGHT", -2, 0) 206 | icon:SetTexture("Interface\\TargetingFrame\\UI-RaidTargetingIcons") 207 | icon:Hide() 208 | frame.icon = icon 209 | 210 | -- create target indicator 211 | local target_left = bar:CreateTexture(nil, "OVERLAY") 212 | target_left:SetWidth(8) 213 | target_left:SetHeight(8) 214 | target_left:SetPoint("LEFT", frame, "LEFT", -4, 0) 215 | target_left:SetTexture("Interface\\AddOns\\ShaguScan\\img\\target-left") 216 | target_left:Hide() 217 | frame.target_left = target_left 218 | 219 | local target_right = bar:CreateTexture(nil, "OVERLAY") 220 | target_right:SetWidth(8) 221 | target_right:SetHeight(8) 222 | target_right:SetPoint("RIGHT", frame, "RIGHT", 4, 0) 223 | target_right:SetTexture("Interface\\AddOns\\ShaguScan\\img\\target-right") 224 | target_right:Hide() 225 | frame.target_right = target_right 226 | 227 | -- create frame backdrops 228 | if pfUI and pfUI.uf then 229 | pfUI.api.CreateBackdrop(frame) 230 | frame.border = frame.backdrop 231 | else 232 | frame:SetBackdrop(ui.background) 233 | frame:SetBackdropColor(0, 0, 0, 1) 234 | 235 | local border = CreateFrame("Frame", nil, frame.bar) 236 | border:SetBackdrop(ui.border) 237 | border:SetBackdropColor(.2, .2, .2, 1) 238 | border:SetPoint("TOPLEFT", frame.bar, "TOPLEFT", -2,2) 239 | border:SetPoint("BOTTOMRIGHT", frame.bar, "BOTTOMRIGHT", 2,-2) 240 | frame.border = border 241 | end 242 | 243 | return frame 244 | end 245 | 246 | ui:SetAllPoints() 247 | ui:SetScript("OnUpdate", function() 248 | if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .5 end 249 | 250 | -- remove old leftover frames 251 | for caption, root in pairs(ui.frames) do 252 | if not ShaguScan_db.config[caption] then 253 | ui.frames[caption]:Hide() 254 | ui.frames[caption] = nil 255 | end 256 | end 257 | 258 | -- create ui frames based on config values 259 | for caption, config in pairs(ShaguScan_db.config) do 260 | -- create root frame if not existing 261 | ui.frames[caption] = ui.frames[caption] or ui:CreateRoot(caption) 262 | local root = ui.frames[caption] 263 | 264 | -- skip if locked (due to moving) 265 | if root.lock then return end 266 | 267 | -- update position based on config 268 | if not root.pos or root.pos ~= config.anchor..config.x..config.y..config.scale then 269 | root.pos = config.anchor..config.x..config.y..config.scale 270 | root:ClearAllPoints() 271 | root:SetPoint(config.anchor, config.x, config.y) 272 | root:SetScale(config.scale) 273 | end 274 | 275 | -- update filter if required 276 | if not root.filter_conf or root.filter_conf ~= config.filter then 277 | root.filter = {} 278 | 279 | -- prepare all filter texts 280 | local filter_texts = { utils.strsplit(',', config.filter) } 281 | for id, filter_text in pairs(filter_texts) do 282 | local name, args = utils.strsplit(':', filter_text) 283 | root.filter[name] = args or true 284 | end 285 | 286 | -- mark current state of data 287 | root.filter_conf = config.filter 288 | end 289 | 290 | -- run through all guids and fill with bars 291 | local title_size = 12 + config.spacing 292 | local width, height = config.width, config.height + title_size 293 | local x, y, count = 0, 0, 0 294 | for guid, time in pairs(ShaguScan.core.guids) do 295 | -- apply filters 296 | local visible = true 297 | for name, args in pairs(root.filter) do 298 | if filter[name] then 299 | visible = visible and filter[name](guid, args) 300 | end 301 | end 302 | 303 | -- display element if filters allow it 304 | if UnitExists(guid) and visible then 305 | count = count + 1 306 | 307 | if count > config.maxrow then 308 | count, x = 1, x + config.width + config.spacing 309 | width = math.max(x + config.width, width) 310 | end 311 | 312 | y = (count-1) * (config.height + config.spacing) + title_size 313 | height = math.max(y + config.height + config.spacing, height) 314 | 315 | root.frames[guid] = root.frames[guid] or root:CreateBar(guid) 316 | 317 | -- update position if required 318 | if not root.frames[guid].pos or root.frames[guid].pos ~= x..-y then 319 | root.frames[guid]:ClearAllPoints() 320 | root.frames[guid]:SetPoint("TOPLEFT", root, "TOPLEFT", x, -y) 321 | root.frames[guid].pos = x..-y 322 | end 323 | 324 | -- update sizes if required 325 | if not root.frames[guid].sizes or root.frames[guid].sizes ~= config.width..config.height then 326 | root.frames[guid]:SetWidth(config.width) 327 | root.frames[guid]:SetHeight(config.height) 328 | root.frames[guid].sizes = config.width..config.height 329 | end 330 | 331 | root.frames[guid]:Show() 332 | elseif root.frames[guid] then 333 | root.frames[guid]:Hide() 334 | root.frames[guid] = nil 335 | end 336 | end 337 | 338 | -- update window size 339 | root:SetWidth(width) 340 | root:SetHeight(height) 341 | end 342 | end) 343 | 344 | ShaguScan.ui = ui 345 | -------------------------------------------------------------------------------- /settings.lua: -------------------------------------------------------------------------------- 1 | if ShaguScan.disabled then return end 2 | 3 | local utils = ShaguScan.utils 4 | 5 | local settings = {} 6 | 7 | SLASH_SHAGUSCAN1, SLASH_SHAGUSCAN2, SLASH_SHAGUSCAN3 = "/scan", "/sscan", "/shaguscan" 8 | 9 | SlashCmdList["SHAGUSCAN"] = function(input) 10 | local caption = input and input ~= '' and input or "Scanner" 11 | settings.OpenConfig(caption) 12 | end 13 | 14 | settings.backdrop = { 15 | edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", 16 | bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", 17 | tile = true, tileSize = 16, edgeSize = 12, 18 | insets = { left = 2, right = 2, top = 2, bottom = 2 } 19 | } 20 | 21 | settings.textborder = { 22 | edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", 23 | bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", 24 | tile = true, tileSize = 16, edgeSize = 8, 25 | insets = { left = 2, right = 2, top = 2, bottom = 2 } 26 | } 27 | 28 | settings.CreateLabel = function(parent, text) 29 | local label = parent:CreateFontString(nil, 'HIGH', 'GameFontWhite') 30 | label:SetFont(STANDARD_TEXT_FONT, 9) 31 | label:SetText(text) 32 | label:SetHeight(18) 33 | return label 34 | end 35 | 36 | settings.CreateTextBox = function(parent, text) 37 | local textbox = CreateFrame("EditBox", nil, parent) 38 | textbox.ShowTooltip = settings.ShowTooltip 39 | 40 | textbox:SetTextColor(1,.8,.2,1) 41 | textbox:SetJustifyH("RIGHT") 42 | textbox:SetTextInsets(5,5,5,5) 43 | textbox:SetBackdrop(settings.textborder) 44 | textbox:SetBackdropColor(.1,.1,.1,1) 45 | textbox:SetBackdropBorderColor(.2,.2,.2,1) 46 | 47 | textbox:SetHeight(18) 48 | 49 | textbox:SetFontObject(GameFontNormal) 50 | textbox:SetFont(STANDARD_TEXT_FONT, 9) 51 | textbox:SetAutoFocus(false) 52 | textbox:SetText((text or "")) 53 | 54 | textbox:SetScript("OnEscapePressed", function(self) 55 | this:ClearFocus() 56 | end) 57 | 58 | return textbox 59 | end 60 | 61 | settings.ShowTooltip = function(parent, strings) 62 | GameTooltip:SetOwner(parent, "ANCHOR_RIGHT") 63 | for id, entry in pairs(strings) do 64 | if type(entry) == "table" then 65 | GameTooltip:AddDoubleLine(entry[1], entry[2]) 66 | else 67 | GameTooltip:AddLine(entry) 68 | end 69 | end 70 | GameTooltip:Show() 71 | end 72 | 73 | settings.OpenConfig = function(caption) 74 | -- Toggle Existing Dialog 75 | local existing = getglobal("ShaguScanConfigDialog"..caption) 76 | if existing then 77 | if existing:IsShown() then existing:Hide() else existing:Show() end 78 | return 79 | end 80 | 81 | -- Create defconfig if new config 82 | if not ShaguScan_db.config[caption] then 83 | ShaguScan_db.config[caption] = { 84 | filter = "npc,infight,alive", 85 | scale = 1, anchor = "CENTER", x = 0, y = 0, width = 75, height = 12, spacing = 4, maxrow = 20 86 | } 87 | end 88 | 89 | -- Main Dialog 90 | local dialog = CreateFrame("Frame", "ShaguScanConfigDialog"..caption, UIParent) 91 | table.insert(UISpecialFrames, "ShaguScanConfigDialog"..caption) 92 | 93 | -- Save Shortcuts 94 | local config = ShaguScan_db.config[caption] 95 | local caption = caption 96 | 97 | dialog:SetFrameStrata("DIALOG") 98 | dialog:SetPoint("CENTER", 0, 0) 99 | dialog:SetWidth(264) 100 | dialog:SetHeight(264) 101 | 102 | dialog:EnableMouse(true) 103 | dialog:RegisterForDrag("LeftButton") 104 | dialog:SetMovable(true) 105 | dialog:SetScript("OnDragStart", function() this:StartMoving() end) 106 | dialog:SetScript("OnDragStop", function() this:StopMovingOrSizing() end) 107 | 108 | dialog:SetBackdrop(settings.backdrop) 109 | dialog:SetBackdropColor(.2, .2, .2, 1) 110 | dialog:SetBackdropBorderColor(.2, .2, .2, 1) 111 | 112 | -- Assign functions to dialog 113 | dialog.CreateTextBox = settings.CreateTextBox 114 | dialog.CreateLabel = settings.CreateLabel 115 | 116 | -- Save & Reload 117 | dialog.save = CreateFrame("Button", nil, dialog, "GameMenuButtonTemplate") 118 | dialog.save:SetWidth(96) 119 | dialog.save:SetHeight(18) 120 | dialog.save:SetFont(STANDARD_TEXT_FONT, 10) 121 | dialog.save:SetPoint("BOTTOMRIGHT", dialog, "BOTTOMRIGHT", -8, 8) 122 | dialog.save:SetText("Save") 123 | dialog.save:SetScript("OnClick", function() 124 | local new_caption = dialog.caption:GetText() 125 | 126 | local filter = dialog.filter:GetText() 127 | local width = dialog.width:GetText() 128 | local height = dialog.height:GetText() 129 | local spacing = dialog.spacing:GetText() 130 | local maxrow = dialog.maxrow:GetText() 131 | local anchor = dialog.anchor:GetText() 132 | local scale = dialog.scale:GetText() 133 | local x = dialog.x:GetText() 134 | local y = dialog.y:GetText() 135 | 136 | -- build new config 137 | local new_config = { 138 | filter = filter, 139 | width = tonumber(width) or config.width, 140 | height = tonumber(height) or config.height, 141 | spacing = tonumber(spacing) or config.spacing, 142 | maxrow = tonumber(maxrow) or config.maxrow, 143 | anchor = utils.IsValidAnchor(anchor) and anchor or config.anchor, 144 | scale = tonumber(scale) or config.scale, 145 | x = tonumber(x) or config.x, 146 | y = tonumber(y) or config.y, 147 | } 148 | 149 | ShaguScan_db.config[caption] = nil 150 | ShaguScan_db.config[new_caption] = new_config 151 | this:GetParent():Hide() 152 | end) 153 | 154 | -- Delete 155 | dialog.delete = CreateFrame("Button", nil, dialog, "GameMenuButtonTemplate") 156 | dialog.delete:SetWidth(96) 157 | dialog.delete:SetHeight(18) 158 | dialog.delete:SetFont(STANDARD_TEXT_FONT, 10) 159 | dialog.delete:SetPoint("BOTTOMLEFT", dialog, "BOTTOMLEFT", 8, 8) 160 | dialog.delete:SetText("Delete") 161 | dialog.delete:SetScript("OnClick", function() 162 | ShaguScan_db.config[caption] = nil 163 | this:GetParent():Hide() 164 | end) 165 | 166 | dialog.close = CreateFrame("Button", nil, dialog, "UIPanelCloseButton") 167 | dialog.close:SetWidth(20) 168 | dialog.close:SetHeight(20) 169 | dialog.close:SetPoint("TOPRIGHT", dialog, "TOPRIGHT", 0, 0) 170 | dialog.close:SetScript("OnClick", function() 171 | this:GetParent():Hide() 172 | end) 173 | 174 | -- Caption (Title) 175 | dialog.caption = dialog:CreateTextBox(caption) 176 | dialog.caption:SetPoint("TOPLEFT", dialog, "TOPLEFT", 8, -18) 177 | dialog.caption:SetPoint("TOPRIGHT", dialog, "TOPRIGHT", -8, -18) 178 | dialog.caption:SetFont(STANDARD_TEXT_FONT, 10) 179 | dialog.caption:SetJustifyH("CENTER") 180 | dialog.caption:SetHeight(20) 181 | 182 | -- Backdrop 183 | local backdrop = CreateFrame("Frame", nil, dialog) 184 | backdrop:SetBackdrop(settings.backdrop) 185 | backdrop:SetBackdropBorderColor(.2,.2,.2,1) 186 | backdrop:SetBackdropColor(.2,.2,.2,1) 187 | 188 | backdrop:SetPoint("TOPLEFT", dialog, "TOPLEFT", 8, -40) 189 | backdrop:SetPoint("BOTTOMRIGHT", dialog, "BOTTOMRIGHT", -8, 28) 190 | 191 | backdrop.CreateTextBox = settings.CreateTextBox 192 | backdrop.CreateLabel = settings.CreateLabel 193 | 194 | backdrop.pos = 8 195 | 196 | -- Filter 197 | local caption = backdrop:CreateLabel("Filter:") 198 | caption:SetPoint("TOPLEFT", backdrop, 10, -backdrop.pos) 199 | 200 | dialog.filter = backdrop:CreateTextBox(config.filter) 201 | dialog.filter:SetPoint("TOPLEFT", backdrop, "TOPLEFT", 60, -backdrop.pos) 202 | dialog.filter:SetPoint("TOPRIGHT", backdrop, "TOPRIGHT", -8, -backdrop.pos) 203 | dialog.filter:SetScript("OnEnter", function() 204 | dialog.filter:ShowTooltip({ 205 | "Unit Filters", 206 | "|cffaaaaaaA comma separated list of filters.", 207 | " ", 208 | { "|cffffffffplayer", "Player Characters" }, 209 | { "|cffffffffnpc", "NPC Units" }, 210 | { "|cffffffffinfight", "Infight Units" }, 211 | { "|cffffffffdead", "Dead Units" }, 212 | { "|cffffffffalive", "Living Units" }, 213 | { "|cffffffffhorde", "Horde Units" }, 214 | { "|cffffffffalliance", "Alliance Units" }, 215 | { "|cffffffffhardcore", "Hardcore Players" }, 216 | { "|cffffffffpve", "PvE Units" }, 217 | { "|cffffffffpvp", "PvP Enabled Units" }, 218 | { "|cfffffffficon", "Units With Raid Icons" }, 219 | " ", 220 | "|cffffffffA complete list of filters can be found in the README." 221 | }) 222 | end) 223 | 224 | dialog.filter:SetScript("OnLeave", function() 225 | GameTooltip:Hide() 226 | end) 227 | 228 | backdrop.pos = backdrop.pos + 18 229 | 230 | -- Spacer 231 | backdrop.pos = backdrop.pos + 9 232 | 233 | -- Width 234 | local caption = backdrop:CreateLabel("Width:") 235 | caption:SetPoint("TOPLEFT", backdrop, 10, -backdrop.pos) 236 | 237 | dialog.width = backdrop:CreateTextBox(config.width) 238 | dialog.width:SetPoint("TOPLEFT", backdrop, "TOPLEFT", 60, -backdrop.pos) 239 | dialog.width:SetPoint("TOPRIGHT", backdrop, "TOPRIGHT", -8, -backdrop.pos) 240 | dialog.width:SetScript("OnEnter", function() 241 | dialog.width:ShowTooltip({ 242 | "Health Bar Width", 243 | "|cffaaaaaaAn Integer Value in Pixels" 244 | }) 245 | end) 246 | 247 | dialog.width:SetScript("OnLeave", function() 248 | GameTooltip:Hide() 249 | end) 250 | backdrop.pos = backdrop.pos + 18 251 | 252 | -- Height 253 | local caption = backdrop:CreateLabel("Height:") 254 | caption:SetPoint("TOPLEFT", backdrop, 10, -backdrop.pos) 255 | 256 | dialog.height = backdrop:CreateTextBox(config.height) 257 | dialog.height:SetPoint("TOPLEFT", backdrop, "TOPLEFT", 60, -backdrop.pos) 258 | dialog.height:SetPoint("TOPRIGHT", backdrop, "TOPRIGHT", -8, -backdrop.pos) 259 | dialog.height:SetScript("OnEnter", function() 260 | dialog.height:ShowTooltip({ 261 | "Health Bar Height", 262 | "|cffaaaaaaAn Integer Value in Pixels" 263 | }) 264 | end) 265 | 266 | dialog.height:SetScript("OnLeave", function() 267 | GameTooltip:Hide() 268 | end) 269 | 270 | backdrop.pos = backdrop.pos + 18 271 | 272 | -- Spacing 273 | local caption = backdrop:CreateLabel("Spacing:") 274 | caption:SetPoint("TOPLEFT", backdrop, 10, -backdrop.pos) 275 | 276 | dialog.spacing = backdrop:CreateTextBox(config.spacing) 277 | dialog.spacing:SetPoint("TOPLEFT", backdrop, "TOPLEFT", 60, -backdrop.pos) 278 | dialog.spacing:SetPoint("TOPRIGHT", backdrop, "TOPRIGHT", -8, -backdrop.pos) 279 | dialog.spacing:SetScript("OnEnter", function() 280 | dialog.spacing:ShowTooltip({ 281 | "Spacing Between Health Bars", 282 | "|cffaaaaaaAn Integer Value in Pixels" 283 | }) 284 | end) 285 | 286 | dialog.spacing:SetScript("OnLeave", function() 287 | GameTooltip:Hide() 288 | end) 289 | 290 | backdrop.pos = backdrop.pos + 18 291 | 292 | -- Max per Row 293 | local caption = backdrop:CreateLabel("Max-Row:") 294 | caption:SetPoint("TOPLEFT", backdrop, 10, -backdrop.pos) 295 | 296 | dialog.maxrow = backdrop:CreateTextBox(config.maxrow) 297 | dialog.maxrow:SetPoint("TOPLEFT", backdrop, "TOPLEFT", 60, -backdrop.pos) 298 | dialog.maxrow:SetPoint("TOPRIGHT", backdrop, "TOPRIGHT", -8, -backdrop.pos) 299 | dialog.maxrow:SetScript("OnEnter", function() 300 | dialog.maxrow:ShowTooltip({ 301 | "Maximum Entries Per Column", 302 | "|cffaaaaaaA new column will be created once exceeded" 303 | }) 304 | end) 305 | 306 | dialog.maxrow:SetScript("OnLeave", function() 307 | GameTooltip:Hide() 308 | end) 309 | 310 | backdrop.pos = backdrop.pos + 18 311 | 312 | -- Spacer 313 | backdrop.pos = backdrop.pos + 9 314 | 315 | -- Anchor 316 | local caption = backdrop:CreateLabel("Anchor:") 317 | caption:SetPoint("TOPLEFT", backdrop, 10, -backdrop.pos) 318 | 319 | dialog.anchor = backdrop:CreateTextBox(config.anchor) 320 | dialog.anchor:SetPoint("TOPLEFT", backdrop, "TOPLEFT", 60, -backdrop.pos) 321 | dialog.anchor:SetPoint("TOPRIGHT", backdrop, "TOPRIGHT", -8, -backdrop.pos) 322 | dialog.anchor:SetScript("OnEnter", function() 323 | dialog.anchor:ShowTooltip({ 324 | "Window Anchor", 325 | "|cffaaaaaaThe Anchor From Where Positions Are Calculated.", 326 | " ", 327 | {"TOP", "TOPLEFT"}, 328 | {"TOPRIGHT", "CENTER"}, 329 | {"LEFT", "RIGHT"}, 330 | {"BOTTOM", "BOTTOMLEFT"}, 331 | {"BOTTOMRIGHT", ""} 332 | }) 333 | end) 334 | 335 | dialog.anchor:SetScript("OnLeave", function() 336 | GameTooltip:Hide() 337 | end) 338 | 339 | backdrop.pos = backdrop.pos + 18 340 | 341 | -- Scale 342 | local caption = backdrop:CreateLabel("Scale:") 343 | caption:SetPoint("TOPLEFT", backdrop, 10, -backdrop.pos) 344 | 345 | dialog.scale = backdrop:CreateTextBox(utils.round(config.scale, 2)) 346 | dialog.scale:SetPoint("TOPLEFT", backdrop, "TOPLEFT", 60, -backdrop.pos) 347 | dialog.scale:SetPoint("TOPRIGHT", backdrop, "TOPRIGHT", -8, -backdrop.pos) 348 | dialog.scale:SetScript("OnEnter", function() 349 | dialog.scale:ShowTooltip({ 350 | "Window Scale", 351 | "|cffaaaaaaA floating point number, 1 equals 100%" 352 | }) 353 | end) 354 | 355 | dialog.scale:SetScript("OnLeave", function() 356 | GameTooltip:Hide() 357 | end) 358 | 359 | backdrop.pos = backdrop.pos + 18 360 | 361 | -- Position-X 362 | local caption = backdrop:CreateLabel("X-Position:") 363 | caption:SetPoint("TOPLEFT", backdrop, 10, -backdrop.pos) 364 | 365 | dialog.x = backdrop:CreateTextBox(utils.round(config.x, 2)) 366 | dialog.x:SetPoint("TOPLEFT", backdrop, "TOPLEFT", 60, -backdrop.pos) 367 | dialog.x:SetPoint("TOPRIGHT", backdrop, "TOPRIGHT", -8, -backdrop.pos) 368 | dialog.x:SetScript("OnEnter", function() 369 | dialog.x:ShowTooltip({ 370 | "X-Position of Window", 371 | "|cffaaaaaaA Number in Pixels" 372 | }) 373 | end) 374 | 375 | dialog.x:SetScript("OnLeave", function() 376 | GameTooltip:Hide() 377 | end) 378 | 379 | backdrop.pos = backdrop.pos + 18 380 | 381 | -- Position-Y 382 | local caption = backdrop:CreateLabel("Y-Position:") 383 | caption:SetPoint("TOPLEFT", backdrop, 10, -backdrop.pos) 384 | 385 | dialog.y = backdrop:CreateTextBox(utils.round(config.y, 2)) 386 | dialog.y:SetPoint("TOPLEFT", backdrop, "TOPLEFT", 60, -backdrop.pos) 387 | dialog.y:SetPoint("TOPRIGHT", backdrop, "TOPRIGHT", -8, -backdrop.pos) 388 | dialog.y:SetScript("OnEnter", function() 389 | dialog.y:ShowTooltip({ 390 | "Y-Position of Window", 391 | "|cffaaaaaaA Number in Pixels" 392 | }) 393 | end) 394 | 395 | dialog.y:SetScript("OnLeave", function() 396 | GameTooltip:Hide() 397 | end) 398 | backdrop.pos = backdrop.pos + 18 399 | end 400 | 401 | ShaguScan.settings = settings 402 | --------------------------------------------------------------------------------