├── 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 | 
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 |
--------------------------------------------------------------------------------