vrtc / chorus (public) (License: CC0) (since 2023-08-12) (hash sha1)
World of Warcraft add-on stub. The overall goal is to create a specialized raid frame.
List of commits:
Subject Hash Author Date (UTC)
feat!: Add power bar 654fe43cb55a0e440bdead43d86e3d49bf331c3a Vladyslav Bondarenko 2023-08-15 17:43:59
feat!: Add health bar 16bcad2d2169a7caa2a0f17513202673d178fa98 Vladyslav Bondarenko 2023-08-14 21:59:43
feat!: Add target aura tracker 96105088fc793845cde21639e0221b82de897262 Vladyslav Bondarenko 2023-08-12 21:49:26
Commit 654fe43cb55a0e440bdead43d86e3d49bf331c3a - feat!: Add power bar
Author: Vladyslav Bondarenko
Author date (UTC): 2023-08-15 17:43
Committer name: Vladyslav Bondarenko
Committer date (UTC): 2023-08-15 17:43
Parent(s): 16bcad2d2169a7caa2a0f17513202673d178fa98
Signer:
Signing key: EFF9624877D25D02
Signing status: E
Tree: 51777f1ccdb6251cc6be755ba9ff78fa08d69740
File Lines added Lines deleted
chorus.toc 2 0
etc/luacheckrc.lua 5 0
src/ChorusHealthFrameTemplate.xml 9 0
src/ChorusPowerFrameTemplate.xml 9 0
src/ChorusProgressFrameTemplate.lua 202 29
src/ChorusProgressFrameTemplate.xml 2 2
src/ChorusTestFrame.xml 23 4
File chorus.toc changed (mode: 100644) (index a2b902b..1096ef5)
... ... src\Chorus.xml
6 6 src\ChorusAuraButtonTemplate.xml src\ChorusAuraButtonTemplate.xml
7 7 src\ChorusAuraFrameTemplate.xml src\ChorusAuraFrameTemplate.xml
8 8 src\ChorusProgressFrameTemplate.xml src\ChorusProgressFrameTemplate.xml
9 src\ChorusHealthFrameTemplate.xml
10 src\ChorusPowerFrameTemplate.xml
9 11 src\ChorusTestFrame.xml src\ChorusTestFrame.xml
File etc/luacheckrc.lua changed (mode: 100644) (index d1de12f..46736be)
... ... stds.wow = {
7 7 'GetSpellInfo', 'GetSpellInfo',
8 8 'GetSpellTexture', 'GetSpellTexture',
9 9 'GetTime', 'GetTime',
10 'PowerBarColor',
10 11 'RAID_CLASS_COLORS', 'RAID_CLASS_COLORS',
11 12 'UIParent', 'UIParent',
12 13 'UnitAura', 'UnitAura',
14 'UnitClass',
13 15 'UnitExists', 'UnitExists',
14 16 'UnitGUID', 'UnitGUID',
15 17 'UnitHealth', 'UnitHealth',
16 18 'UnitHealthMax', 'UnitHealthMax',
17 19 'UnitIsCorpse', 'UnitIsCorpse',
18 20 'UnitIsDead', 'UnitIsDead',
21 'UnitIsEnemy',
22 'UnitIsFriend',
19 23 'UnitIsGhost', 'UnitIsGhost',
20 24 'UnitPower', 'UnitPower',
21 25 'UnitPowerMax', 'UnitPowerMax',
26 'UnitPowerType',
22 27 'date', 'date',
23 28 'strtrim', 'strtrim',
24 29 'tContains', 'tContains',
File src/ChorusHealthFrameTemplate.xml added (mode: 100644) (index 0000000..b823080)
1 <?xml version="1.0" encoding="UTF-8"?>
2 <Ui xmlns="http://www.blizzard.com/wow/ui/">
3 <Script file="ChorusProgressFrameTemplate.lua"/>
4 <Frame name="ChorusHealthFrameTemplate" inherits="ChorusProgressFrameTemplate" virtual="true">
5 <Scripts>
6 <OnLoad>Chorus.healthFrameMain(self);</OnLoad>
7 </Scripts>
8 </Frame>
9 </Ui>
File src/ChorusPowerFrameTemplate.xml added (mode: 100644) (index 0000000..e101f80)
1 <?xml version="1.0" encoding="UTF-8"?>
2 <Ui xmlns="http://www.blizzard.com/wow/ui/">
3 <Script file="ChorusProgressFrameTemplate.lua"/>
4 <Frame name="ChorusPowerFrameTemplate" inherits="ChorusProgressFrameTemplate" virtual="true">
5 <Scripts>
6 <OnLoad>Chorus.powerFrameMain(self);</OnLoad>
7 </Scripts>
8 </Frame>
9 </Ui>
File src/ChorusProgressFrameTemplate.lua changed (mode: 100644) (index e6744fb..d72f7d8)
1 1 local GetTime = GetTime local GetTime = GetTime
2 local PowerBarColor = PowerBarColor
3 local RAID_CLASS_COLORS = RAID_CLASS_COLORS
2 4 local UnitExists = UnitExists local UnitExists = UnitExists
3 5 local UnitHealth = UnitHealth local UnitHealth = UnitHealth
4 6 local UnitHealthMax = UnitHealthMax local UnitHealthMax = UnitHealthMax
5 7 local UnitIsCorpse = UnitIsCorpse local UnitIsCorpse = UnitIsCorpse
6 8 local UnitIsDead = UnitIsDead local UnitIsDead = UnitIsDead
7 9 local UnitIsGhost = UnitIsGhost local UnitIsGhost = UnitIsGhost
8 local UnitPower = UnitHealth
9 local UnitPowerMax = UnitHealthMax
10 local UnitPower = UnitPower
11 local UnitPowerMax = UnitPowerMax
12 local UnitPowerType = UnitPowerType
10 13
11 14 local Chorus = Chorus local Chorus = Chorus
12 15
 
... ... local function validateProgressFrame(self)
26 29 assert(ratio <= 1.0) assert(ratio <= 1.0)
27 30
28 31 local strategy = self.strategy local strategy = self.strategy
29 assert(strategy ~= nil)
30 assert('string' == type(strategy))
31 assert('UNIT_HEALTH' == strategy or 'UNIT_POWER' == strategy)
32 if 'UNIT_HEALTH' == strategy or 'UNIT_POWER' == strategy then
32 if strategy then
33 assert('string' == type(strategy))
34 strategy = strtrim(strategy)
35 assert(string.len(strategy) >= 1)
36 assert(string.len(strategy) <= 8192)
37
33 38 local unitDesignation = self.unit local unitDesignation = self.unit
34 39 assert('string' == type(unitDesignation)) assert('string' == type(unitDesignation))
35 40 unitDesignation = string.lower(strtrim(unitDesignation)) unitDesignation = string.lower(strtrim(unitDesignation))
 
... ... local function applyOverlay(self, a, b)
113 118
114 119 local ratio = getRatio(a, b) local ratio = getRatio(a, b)
115 120
116 local t
121 local t = ''
117 122 if a < 1000 then if a < 1000 then
118 t = string.format('%d\n%.0f%%', a, ratio * 100)
123 t = t .. string.format('%d', a)
119 124 elseif a < 1000000 then elseif a < 1000000 then
120 t = string.format('%.2f K\n%.0f%%', a / 1000, ratio * 100)
125 t = t .. string.format('%.2f K', a / 1000)
126 else
127 t = t .. string.format('%.2f M', a / 1000000)
128 end
129
130 if 23 <= self:GetHeight() then
131 t = t .. '\n'
121 132 else else
122 t = string.format('%.2f M\n%.0f%%', a / 1000000, ratio * 100)
133 t = t .. ' '
123 134 end end
124 135
136 t = t .. string.format('%.0f%%', ratio * 100)
137
125 138 local label = self.label or _G[self:GetName() .. 'Text'] local label = self.label or _G[self:GetName() .. 'Text']
126 139 assert(label ~= nil) assert(label ~= nil)
127 140 label:SetText(t) label:SetText(t)
 
... ... local function applyOverlayHealth(self, unitDesignation)
164 177 end end
165 178 end end
166 179
167 local function applyRatioPower(self, unitDesignation)
180 local function applyRatioPower(self, unitDesignation, powerTypeEnum)
168 181 assert(self ~= nil) assert(self ~= nil)
169
170 182 assert(unitDesignation ~= nil) assert(unitDesignation ~= nil)
183 assert(powerTypeEnum ~= nil)
171 184
172 local a = UnitPower(unitDesignation) or 0
173 local b = UnitPowerMax(unitDesignation) or 1
185 local a = UnitPower(unitDesignation, powerTypeEnum) or 0
186 local b = UnitPowerMax(unitDesignation, powerTypeEnum) or 1
174 187 applyRatio(self, a, b) applyRatio(self, a, b)
175 188 end end
176 189
177 local function applyOverlayPower(self, unitDesignation)
190 local function applyOverlayPower(self, unitDesignation, powerTypeEnum)
178 191 assert(self ~= nil) assert(self ~= nil)
179
180 192 assert(unitDesignation ~= nil) assert(unitDesignation ~= nil)
193 assert(powerTypeEnum ~= nil)
181 194
182 local a = UnitPower(unitDesignation) or 0
183 local b = UnitPowerMax(unitDesignation) or 1
195 local a = UnitPower(unitDesignation, powerTypeEnum) or 0
196 local b = UnitPowerMax(unitDesignation, powerTypeEnum) or 1
184 197 applyOverlay(self, a, b) applyOverlay(self, a, b)
185 198 end end
186 199
187 local function progressFrameEventProcessor(self)
200 local function applyHealthFrameColorUnitClass(self, unitDesignation)
201 assert(self ~= nil)
202 assert(unitDesignation ~= nil)
203
204 local _, classDesignation = UnitClass(unitDesignation)
205 if not classDesignation then
206 return
207 end
208
209 local map = RAID_CLASS_COLORS
210 assert(map ~= nil)
211 assert('table' == type(map))
212 local colorTuple = map[classDesignation]
213
214 local r = 1
215 local g = 1
216 local b = 1
217 if colorTuple then
218 assert('table' == type(colorTuple))
219 r = math.min(math.max(0, math.abs(colorTuple.r)), 1)
220 g = math.min(math.max(0, math.abs(colorTuple.g)), 1)
221 b = math.min(math.max(0, math.abs(colorTuple.b)), 1)
222 end
223
224 local artwork = self.artwork
225 assert(artwork ~= nil)
226
227 artwork:SetTexture(r, g, b)
228 end
229
230 local function applyHealthFrameColorUnitIsFriend(self, unitDesignation)
231 assert(self ~= nil)
232 assert(unitDesignation ~= nil)
233
234 local r = 255 / 255
235 local g = 215 / 255
236 local b = 0 / 255
237
238 if UnitIsFriend('player', unitDesignation) then
239 r = 124 / 255
240 g = 252 / 255
241 b = 0 / 255
242 elseif UnitIsEnemy('player', unitDesignation) then
243 r = 255 / 255
244 g = 69 / 255
245 b = 0 / 255
246 end
247
248 local artwork = self.artwork
249 assert(artwork ~= nil)
250
251 artwork:SetTexture(r, g, b)
252 end
253
254 local function healthFrameEventProcessor(self)
255 validateProgressFrame(self)
256
257 local p = self:GetParent()
258
259 local unitDesignation = self.unit
260 if not unitDesignation and p then
261 unitDesignation = unitDesignation or p.unit
262 end
263
264 assert(unitDesignation ~= nil)
265 assert('string' == type(unitDesignation))
266 unitDesignation = string.lower(strtrim(unitDesignation))
267 assert(string.len(unitDesignation) >= 1)
268 assert(string.len(unitDesignation) <= 256)
269
270 if UnitExists(unitDesignation) then
271 self:Show()
272 else
273 self:Hide()
274 return
275 end
276
277 applyRatioHealth(self, unitDesignation)
278 applyOverlayHealth(self, unitDesignation)
279
280 local strategy = self.strategy
281 assert(strategy ~= nil)
282 assert('string' == type(strategy))
283 strategy = strtrim(strategy)
284 assert(string.len(strategy) >= 1)
285 assert(string.len(strategy) <= 256)
286
287 assert(strategy == 'UnitClass' or strategy == 'UnitIsFriend')
288
289 if 'UnitClass' == strategy then
290 applyHealthFrameColorUnitClass(self, unitDesignation)
291 elseif 'UnitIsFriend' == strategy then
292 applyHealthFrameColorUnitIsFriend(self, unitDesignation)
293 else
294 error('invalid enum: strategy must be either UnitClass or UnitIsFriend')
295 end
296 end
297
298 local function applyPowerFrameColor(self, powerTypeEnum)
299 assert(self ~= nil)
300
301 local r = 1
302 local g = 0
303 local b = 1
304 if powerTypeEnum then
305 --[[ See FrameXML/UnitFrame.lua:PowerBarColor ]]--
306 local map = PowerBarColor
307 assert(map ~= nil)
308 assert('table' == type(map))
309
310 local colorTuple = map[powerTypeEnum]
311 if colorTuple then
312 assert('table' == type(colorTuple))
313 r = math.min(math.max(0, math.abs(colorTuple.r)), 1)
314 g = math.min(math.max(0, math.abs(colorTuple.g)), 1)
315 b = math.min(math.max(0, math.abs(colorTuple.b)), 1)
316 end
317 end
318
319 local artwork = self.artwork
320 assert(artwork ~= nil)
321 artwork:SetTexture(r, g, b)
322 end
323
324 local function powerFrameEventProcessor(self)
188 325 validateProgressFrame(self) validateProgressFrame(self)
189 326
190 327 local p = self:GetParent() local p = self:GetParent()
 
... ... local function progressFrameEventProcessor(self)
207 344 return return
208 345 end end
209 346
210 --[[ TODO Add alternative flavours of progress bar for mana and other resources. ]]--
211 --[[ TODO Add different coloring strategies for progress bars. ]]--
347 local powerTypeEnum
212 348 local strategy = self.strategy local strategy = self.strategy
213 if 'UNIT_HEALTH' == strategy then
214 applyRatioHealth(self, unitDesignation)
215 applyOverlayHealth(self, unitDesignation)
216 elseif 'UNIT_POWER' == strategy then
217 applyRatioPower(self, unitDesignation)
218 applyOverlayPower(self, unitDesignation)
349 assert(strategy ~= nil)
350 assert('string' == type(strategy))
351 strategy = strtrim(strategy)
352 assert(string.len(strategy) >= 1)
353 assert(string.len(strategy) <= 256)
354 if 'UnitPowerType' == strategy then
355 local enum, category = UnitPowerType(unitDesignation)
356 if category then
357 powerTypeEnum = enum
358 else
359 self:Hide()
360 return
361 end
362 else
363 --[[ See FrameXML/Constants.lua:SPELL_POWER_MANA ]]--
364 powerTypeEnum = _G[strategy]
365 end
366 assert(powerTypeEnum ~= nil)
367 assert('number' == type(powerTypeEnum))
368 assert(powerTypeEnum >= 0)
369 powerTypeEnum = math.min(math.max(0, math.floor(math.abs(powerTypeEnum))), 8192)
370
371 if UnitPowerMax(unitDesignation, powerTypeEnum) > 0 then
372 self:Show()
373 applyRatioPower(self, unitDesignation, powerTypeEnum)
374 applyOverlayPower(self, unitDesignation, powerTypeEnum)
375 applyPowerFrameColor(self, powerTypeEnum)
376 else
377 self:Hide()
219 378 end end
220 379 end end
221 380
381 --[[ TODO Maybe make progress frame completely generic, by removing the unit field. ]]--
222 382 function Chorus.progressFrameMain(self) function Chorus.progressFrameMain(self)
223 383 assert(self ~= nil) assert(self ~= nil)
224 384
 
... ... function Chorus.progressFrameMain(self)
239 399
240 400 self.ratio = 1 self.ratio = 1
241 401
242 self:SetScript('OnEvent', progressFrameEventProcessor)
243 402 self:SetScript('OnUpdate', progressFrameUpdateProcessor) self:SetScript('OnUpdate', progressFrameUpdateProcessor)
244 403
245 404 self:RegisterEvent('PARTY_CONVERTED_TO_RAID') self:RegisterEvent('PARTY_CONVERTED_TO_RAID')
 
... ... function Chorus.progressFrameMain(self)
251 410 self:RegisterEvent('PLAYER_LOGIN') self:RegisterEvent('PLAYER_LOGIN')
252 411 self:RegisterEvent('PLAYER_TARGET_CHANGED') self:RegisterEvent('PLAYER_TARGET_CHANGED')
253 412 self:RegisterEvent('RAID_ROSTER_UPDATE') self:RegisterEvent('RAID_ROSTER_UPDATE')
254 self:RegisterEvent('UNIT_ENERGY')
413 end
414
415 function Chorus.healthFrameMain(self, ...)
416 Chorus.progressFrameMain(self, ...)
417 self.strategy = 'UnitIsFriend'
418 self:SetScript('OnEvent', healthFrameEventProcessor)
419
255 420 self:RegisterEvent('UNIT_HEALTH') self:RegisterEvent('UNIT_HEALTH')
421 end
422
423 function Chorus.powerFrameMain(self, ...)
424 Chorus.progressFrameMain(self, ...)
425 self.strategy = 'UnitPowerType'
426 self:SetScript('OnEvent', powerFrameEventProcessor)
427
428 self:RegisterEvent('UNIT_ENERGY')
256 429 self:RegisterEvent('UNIT_MANA') self:RegisterEvent('UNIT_MANA')
257 430 self:RegisterEvent('UNIT_MAXPOWER') self:RegisterEvent('UNIT_MAXPOWER')
258 431 self:RegisterEvent('UNIT_POWER') self:RegisterEvent('UNIT_POWER')
File src/ChorusProgressFrameTemplate.xml changed (mode: 100644) (index 37d788f..026906c)
8 8 <Layers> <Layers>
9 9 <Layer level="ARTWORK"> <Layer level="ARTWORK">
10 10 <Texture name="$parentArtwork" nonBlocking="true" setAllPoints="true"> <Texture name="$parentArtwork" nonBlocking="true" setAllPoints="true">
11 <Color r="0.13" g="0.55" b="0.13" a="0.20"/>
11 <Color r="1" g="0" b="1" a="1"/>
12 12 <Anchors> <Anchors>
13 13 <Anchor point="BOTTOMLEFT"> <Anchor point="BOTTOMLEFT">
14 14 <Offset> <Offset>
 
25 25 </Layer> </Layer>
26 26 <Layer level="BACKGROUND"> <Layer level="BACKGROUND">
27 27 <Texture name="$parentBackground" nonBlocking="true" setAllPoints="true"> <Texture name="$parentBackground" nonBlocking="true" setAllPoints="true">
28 <Color r="0" g="0" b="0" a="0.9"/>
28 <Color r="0" g="0" b="0" a="1"/>
29 29 </Texture> </Texture>
30 30 </Layer> </Layer>
31 31 <Layer level="OVERLAY"> <Layer level="OVERLAY">
File src/ChorusTestFrame.xml changed (mode: 100644) (index 627f266..53a2369)
2 2 <Ui xmlns="http://www.blizzard.com/wow/ui/"> <Ui xmlns="http://www.blizzard.com/wow/ui/">
3 3 <Frame name="ChorusTestFrame"> <Frame name="ChorusTestFrame">
4 4 <Size> <Size>
5 <AbsDimension x="216" y="108"/>
5 <AbsDimension x="216" y="144"/>
6 6 </Size> </Size>
7 7 <Anchors> <Anchors>
8 <Anchor point="TOPLEFT">
9 <Offset>
10 <AbsDimension x="512" y="0"/>
11 </Offset>
12 </Anchor>
8 13 <Anchor point="CENTER"> <Anchor point="CENTER">
9 14 <Offset> <Offset>
10 <AbsDimension x="0" y="-144"/>
15 <AbsDimension x="0" y="0"/>
11 16 </Offset> </Offset>
12 17 </Anchor> </Anchor>
13 18 </Anchors> </Anchors>
14 19 <Frames> <Frames>
15 <Frame name="ChorusTestTargetHealthFrame" inherits="ChorusProgressFrameTemplate">
20 <Frame name="ChorusTestTargetHealthFrame" inherits="ChorusHealthFrameTemplate">
21 <Anchors>
22 <Anchor point="BOTTOMLEFT">
23 <Offset>
24 <AbsDimension x="0" y="86"/>
25 </Offset>
26 </Anchor>
27 </Anchors>
28 </Frame>
29 <Frame name="ChorusTestTargetPowerFrame" inherits="ChorusPowerFrameTemplate">
30 <Size>
31 <AbsDimension x="144" y="12"/>
32 </Size>
16 33 <Anchors> <Anchors>
17 34 <Anchor point="BOTTOMLEFT"> <Anchor point="BOTTOMLEFT">
18 35 <Offset> <Offset>
 
46 63 ChorusTestTargetBuffFrame.unit = 'target'; ChorusTestTargetBuffFrame.unit = 'target';
47 64 ChorusTestTargetDebuffFrame.filter = 'HARMFUL'; ChorusTestTargetDebuffFrame.filter = 'HARMFUL';
48 65 ChorusTestTargetDebuffFrame.unit = 'target'; ChorusTestTargetDebuffFrame.unit = 'target';
49 ChorusTestTargetHealthFrame.strategy = 'UNIT_HEALTH';
66 ChorusTestTargetHealthFrame.strategy = 'UnitIsFriend';
50 67 ChorusTestTargetHealthFrame.unit = 'target'; ChorusTestTargetHealthFrame.unit = 'target';
68 ChorusTestTargetPowerFrame.unit = 'target';
69 ChorusTestTargetPowerFrame.strategy = 'UnitPowerType';
51 70 </OnLoad> </OnLoad>
52 71 </Scripts> </Scripts>
53 72 </Frame> </Frame>
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/vrtc/chorus

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/vrtc/chorus

Clone this repository using git:
git clone git://git.rocketgit.com/user/vrtc/chorus

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main