diff --git a/oUF/LICENSE b/oUF/LICENSE index baea223..476398b 100644 --- a/oUF/LICENSE +++ b/oUF/LICENSE @@ -1,7 +1,7 @@ -Copyright (c) 2006-2020 Trond A Ekseth <troeks@gmail.com> -Copyright (c) 2016-2020 Val Voronov <i.lightspark@gmail.com> -Copyright (c) 2016-2020 Adrian L Lange <contact@p3lim.net> -Copyright (c) 2016-2020 Rainrider <rainrider.wow@gmail.com> +Copyright (c) 2006-2022 Trond A Ekseth <troeks@gmail.com> +Copyright (c) 2016-2022 Val Voronov <i.lightspark@gmail.com> +Copyright (c) 2016-2022 Adrian L Lange <contact@p3lim.net> +Copyright (c) 2016-2022 Rainrider <rainrider.wow@gmail.com> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/oUF/blizzard.lua b/oUF/blizzard.lua index 48d1a2f..e90aae8 100644 --- a/oUF/blizzard.lua +++ b/oUF/blizzard.lua @@ -1,14 +1,14 @@ -local parent, ns = ... +local _, ns = ... local oUF = ns.oUF -- sourced from Blizzard_ArenaUI/Blizzard_ArenaUI.lua -local MAX_ARENA_ENEMIES = MAX_ARENA_ENEMIES or 5 +local MAX_ARENA_ENEMIES = _G.MAX_ARENA_ENEMIES or 5 -- sourced from FrameXML/TargetFrame.lua -local MAX_BOSS_FRAMES = MAX_BOSS_FRAMES or 5 +local MAX_BOSS_FRAMES = _G.MAX_BOSS_FRAMES or 5 -- sourced from FrameXML/PartyMemberFrame.lua -local MAX_PARTY_MEMBERS = MAX_PARTY_MEMBERS or 4 +local MAX_PARTY_MEMBERS = _G.MAX_PARTY_MEMBERS or 4 local hiddenParent = CreateFrame('Frame', nil, UIParent) hiddenParent:SetAllPoints() @@ -69,10 +69,10 @@ function oUF:DisableBlizzard(unit) -- For the damn vehicle support: PlayerFrame:RegisterEvent('PLAYER_ENTERING_WORLD') - PlayerFrame:RegisterEvent('UNIT_ENTERING_VEHICLE') - PlayerFrame:RegisterEvent('UNIT_ENTERED_VEHICLE') - PlayerFrame:RegisterEvent('UNIT_EXITING_VEHICLE') - PlayerFrame:RegisterEvent('UNIT_EXITED_VEHICLE') + --PlayerFrame:RegisterEvent('UNIT_ENTERING_VEHICLE') + --PlayerFrame:RegisterEvent('UNIT_ENTERED_VEHICLE') + --PlayerFrame:RegisterEvent('UNIT_EXITING_VEHICLE') + --PlayerFrame:RegisterEvent('UNIT_EXITED_VEHICLE') -- User placed frames don't animate PlayerFrame:SetUserPlaced(true) @@ -87,7 +87,7 @@ function oUF:DisableBlizzard(unit) handleFrame(TargetofFocusFrame) elseif(unit == 'targettarget') then handleFrame(TargetFrameToT) - elseif(unit:match('boss%d?$')) then +--[[elseif(unit:match('boss%d?$')) then local id = unit:match('boss(%d)') if(id) then handleFrame('Boss' .. id .. 'TargetFrame') @@ -95,7 +95,7 @@ function oUF:DisableBlizzard(unit) for i = 1, MAX_BOSS_FRAMES do handleFrame(string.format('Boss%dTargetFrame', i)) end - end + end]] elseif(unit:match('party%d?$')) then local id = unit:match('party(%d)') if(id) then @@ -116,7 +116,7 @@ function oUF:DisableBlizzard(unit) end -- Blizzard_ArenaUI should not be loaded - Arena_LoadUI = function() end + _G.Arena_LoadUI = function() end SetCVar('showArenaEnemyFrames', '0', 'SHOW_ARENA_ENEMY_FRAMES_TEXT') elseif(unit:match('nameplate%d+$')) then local frame = C_NamePlate.GetNamePlateForUnit(unit) @@ -129,4 +129,4 @@ function oUF:DisableBlizzard(unit) handleFrame(frame.UnitFrame, true) end end -end +end \ No newline at end of file diff --git a/oUF/colors.lua b/oUF/colors.lua index 771c958..9bfe2b9 100644 --- a/oUF/colors.lua +++ b/oUF/colors.lua @@ -38,7 +38,11 @@ local colors = { debuff = {}, reaction = {}, power = {}, - threat = {}, + happiness = { + [1] = {1, 0, 0}, -- need.... | unhappy + [2] = {1, 1, 0}, -- new..... | content + [3] = {0, 1, 0}, -- colors.. | happy + }, } -- We do this because people edit the vars directly, and changing the default @@ -117,16 +121,6 @@ colors.power[16] = colors.power.ARCANE_CHARGES colors.power[17] = colors.power.FURY colors.power[18] = colors.power.PAIN --- alternate power, sourced from FrameXML/CompactUnitFrame.lua -colors.power.ALTERNATE = {0.7, 0.7, 0.6} -colors.power[10] = colors.power.ALTERNATE - -for i = 0, 3 do - if GetThreatStatusColor then - colors.threat[i] = {GetThreatStatusColor(i)} - end -end - local function colorsAndPercent(a, b, ...) if(a <= 0 or b == 0) then return nil, ... @@ -271,4 +265,4 @@ oUF.colors = colors oUF.useHCYColorGradient = false frame_metatable.__index.colors = colors -frame_metatable.__index.ColorGradient = oUF.ColorGradient +frame_metatable.__index.ColorGradient = oUF.ColorGradient \ No newline at end of file diff --git a/oUF/combatevents.lua b/oUF/combatevents.lua new file mode 100644 index 0000000..d54ad3c --- /dev/null +++ b/oUF/combatevents.lua @@ -0,0 +1,116 @@ +local _, ns = ... +local oUF = ns.oUF +local Private = oUF.Private + +local argcheck = Private.argcheck +local frame_metatable = Private.frame_metatable + +local CombatLogGetCurrentEventInfo = _G.CombatLogGetCurrentEventInfo + +local event_metatable = { + __call = function(funcs, ...) + for self, func in next, funcs do + if (self:IsVisible()) then + func(self, ...) + end + end + end, +} + +local self_metatable = { + __call = function(funcs, self, ...) + for _, func in next, funcs do + func(self, ...) + end + end +} + +local listener = CreateFrame('Frame') +listener.activeEvents = 0 + +local function filter(_, event, ...) + if(listener[event]) then + listener[event](event, ...) + end +end + +listener:SetScript('OnEvent', function(self, event) + filter(CombatLogGetCurrentEventInfo()) +end) + +--[[ CombatEvents: frame:RegisterCombatEvent(event, handler) +Used to register a frame for a combat log event and add an event handler. + +* self - frame that will be registered for the given event +* event - name of the combat log event to register (string) +* handler - function which will be executed when the combat log event fires. Multiple handlers can be added for the + same frame and event (function) +--]] +function frame_metatable.__index:RegisterCombatEvent(event, handler) + argcheck(event, 2, 'string') + argcheck(handler, 3, 'function') + + if(not listener[event]) then + listener[event] = setmetatable({}, event_metatable) + listener.activeEvents = listener.activeEvents + 1 + end + + local current = listener[event][self] + + if(current) then + for _, func in next, current do + if(func == handler) then return end + end + + table.insert(current, handler) + else + -- even with a single handler we want to make sure the frame is visible + listener[event][self] = setmetatable({handler}, self_metatable) + end + + if(listener.activeEvents > 0) then + listener:RegisterEvent('COMBAT_LOG_EVENT_UNFILTERED') + end +end + +--[[ CombatEvents: frame:UnregisterCombatEvent(event, handler) +Used to remove a function from the event handler list for a combat log event. + +* self - the frame registered for the event +* event - name of the registered combat log event (string) +* handler - function to be removed from the list of event handlers +--]] +function frame_metatable.__index:UnregisterCombatEvent(event, handler) + argcheck(event, 2, 'string') + + if(not listener[event]) then return end + + local cleanUp = false + local current = listener[event][self] + if(current) then + for i, func in next, current do + if(func == handler) then + current[i] = nil + + break + end + end + + if(not next(current)) then + cleanUp = true + end + end + + if(cleanUp) then + listener[event][self] = nil + + if(not next(listener[event])) then + listener[event] = nil + listener.activeEvents = listener.activeEvents - 1 + + if(listener.activeEvents <= 0) then + listener:UnregisterEvent('COMBAT_LOG_EVENT_UNFILTERED') + end + end + end +end diff --git a/oUF/elements/additionalpower.lua b/oUF/elements/additionalpower.lua index bcd70fb..8fbaa52 100644 --- a/oUF/elements/additionalpower.lua +++ b/oUF/elements/additionalpower.lua @@ -1,8 +1,7 @@ --[[ # Element: Additional Power Bar -Handles the visibility and updating of a status bar that displays the player's additional power, such as Mana for -Balance druids. +Handles the visibility and updating of a status bar that displays the player's additional power, such as Mana for druids. ## Widget @@ -18,18 +17,18 @@ A default texture will be applied if the widget is a StatusBar and doesn't have ## Options -.frequentUpdates - Indicates whether to use UNIT_POWER_FREQUENT instead UNIT_POWER_UPDATE to update the bar (boolean) -.displayPairs - Use to override display pairs. (table) -.smoothGradient - 9 color values to be used with the .colorSmooth option (table) +.frequentUpdates - Indicates whether to use UNIT_POWER_FREQUENT instead UNIT_POWER_UPDATE to update the + bar (boolean) +.smoothGradient - 9 color values to be used with the .colorSmooth option (table) The following options are listed by priority. The first check that returns true decides the color of the bar. -.colorPower - Use `self.colors.power[token]` to color the bar based on the player's additional power type - (boolean) -.colorClass - Use `self.colors.class[class]` to color the bar based on unit class. `class` is defined by the - second return of [UnitClass](http://wowprogramming.com/docs/api/UnitClass.html) (boolean) -.colorSmooth - Use `self.colors.smooth` to color the bar with a smooth gradient based on the player's current - additional power percentage (boolean) +.colorPower - Use `self.colors.power[token]` to color the bar based on the player's additional power type + (boolean) +.colorClass - Use `self.colors.class[class]` to color the bar based on unit class. `class` is defined by the + second return of [UnitClass](http://wowprogramming.com/docs/api/UnitClass.html) (boolean) +.colorSmooth - Use `self.colors.smooth` to color the bar with a smooth gradient based on the player's current + additional power percentage (boolean) ## Sub-Widget Options @@ -54,25 +53,21 @@ The following options are listed by priority. The first check that returns true self.AdditionalPower = AdditionalPower --]] +if(select(2, UnitClass('player')) ~= 'DRUID') then return end + local _, ns = ... local oUF = ns.oUF -local _, playerClass = UnitClass('player') - --- sourced from FrameXML/AlternatePowerBar.lua -local ADDITIONAL_POWER_BAR_NAME = ADDITIONAL_POWER_BAR_NAME or 'MANA' -local ADDITIONAL_POWER_BAR_INDEX = ADDITIONAL_POWER_BAR_INDEX or 0 -local ALT_MANA_BAR_PAIR_DISPLAY_INFO = ALT_MANA_BAR_PAIR_DISPLAY_INFO - -local function UpdateColor(self, event, unit, powerType) - if(not (unit and UnitIsUnit(unit, 'player') and powerType == ADDITIONAL_POWER_BAR_NAME)) then return end +local function UpdateColor(self, event, unit, powertype) + if(not (unit and unit == 'player') and powertype == 'MANA') then return end local element = self.AdditionalPower local r, g, b, t if(element.colorPower) then - t = self.colors.power[ADDITIONAL_POWER_BAR_INDEX] - elseif(element.colorClass) then - t = self.colors.class[playerClass] + t = self.colors.power[0] + elseif(element.colorClass and UnitIsPlayer(unit)) then + local _, class = UnitClass(unit) + t = self.colors.class[class] elseif(element.colorSmooth) then r, g, b = self:ColorGradient(element.cur or 1, element.max or 1, unpack(element.smoothGradient or self.colors.smooth)) end @@ -91,23 +86,27 @@ local function UpdateColor(self, event, unit, powerType) end end - --[[ Callback: AdditionalPower:PostUpdateColor(r, g, b) - Called after the element color has been updated. - - * self - the AdditionalPower element - * r - the red component of the used color (number)[0-1] - * g - the green component of the used color (number)[0-1] - * b - the blue component of the used color (number)[0-1] - --]] if(element.PostUpdateColor) then - element:PostUpdateColor(r, g, b) + element:PostUpdateColor(unit, r, g, b) end end -local function Update(self, event, unit, powerType) - if(not (unit and UnitIsUnit(unit, 'player') and powerType == ADDITIONAL_POWER_BAR_NAME)) then return end - local element = self.AdditionalPower +local function ColorPath(self, ...) + --[[ Override: AdditionalPower.UpdateColor(self, event, unit, ...) + Used to completely override the internal function for updating the widgets' colors. + * self - the parent object + * event - the event triggering the update (string) + * unit - the unit accompanying the event (string) + * ... - the arguments accompanying the event + --]] + (self.AdditionalPower.UpdateColor or UpdateColor) (self, ...) +end + +local function Update(self, event, unit, powertype) + if(not (unit and unit == 'player') and powertype == 'MANA') then return end + + local element = self.AdditionalPower --[[ Callback: AdditionalPower:PreUpdate(unit) Called before the element has been updated. @@ -118,22 +117,25 @@ local function Update(self, event, unit, powerType) element:PreUpdate(unit) end - local cur, max = UnitPower('player', ADDITIONAL_POWER_BAR_INDEX), UnitPowerMax('player', ADDITIONAL_POWER_BAR_INDEX) + local cur = UnitPower('player', 0) + local max = UnitPowerMax('player', 0) + element:SetMinMaxValues(0, max) element:SetValue(cur) element.cur = cur element.max = max - --[[ Callback: AdditionalPower:PostUpdate(cur, max) + --[[ Callback: AdditionalPower:PostUpdate(unit, cur, max) Called after the element has been updated. * self - the AdditionalPower element + * unit - the unit for which the update has been triggered (string) * cur - the current value of the player's additional power (number) * max - the maximum value of the player's additional power (number) --]] if(element.PostUpdate) then - return element:PostUpdate(cur, max) + return element:PostUpdate(unit, cur, max) end end @@ -146,17 +148,9 @@ local function Path(self, ...) * unit - the unit accompanying the event (string) * ... - the arguments accompanying the event --]] - (self.AdditionalPower.Override or Update) (self, ...); + (self.AdditionalPower.Override or Update) (self, ...) - --[[ Override: AdditionalPower.UpdateColor(self, event, unit, ...) - Used to completely override the internal function for updating the widgets' colors. - - * self - the parent object - * event - the event triggering the update (string) - * unit - the unit accompanying the event (string) - * ... - the arguments accompanying the event - --]] - (self.AdditionalPower.UpdateColor or UpdateColor) (self, ...) + ColorPath(self, ...) end local function ElementEnable(self) @@ -172,58 +166,36 @@ local function ElementEnable(self) element:Show() - element.__isEnabled = true - Path(self, 'ElementEnable', 'player', ADDITIONAL_POWER_BAR_NAME) + Path(self, 'ElementEnable', 'player', 'MANA') end local function ElementDisable(self) local element = self.AdditionalPower + if(element.frequentUpdates) then + self:RegisterEvent('UNIT_POWER_FREQUENT', Path) + else + self:RegisterEvent('UNIT_POWER_UPDATE', Path) + end + self:UnregisterEvent('UNIT_MAXPOWER', Path) - self:UnregisterEvent('UNIT_POWER_FREQUENT', Path) - self:UnregisterEvent('UNIT_POWER_UPDATE', Path) - element:Hide() + self.AdditionalPower:Hide() - element.__isEnabled = false - Path(self, 'ElementDisable', 'player', ADDITIONAL_POWER_BAR_NAME) + Path(self, 'ElementDisable', 'player', 'MANA') end -local function Visibility(self, event, unit) - local element = self.AdditionalPower +local function Visibility(self) local shouldEnable - if(not UnitHasVehicleUI('player')) then - if(UnitPowerMax(unit, ADDITIONAL_POWER_BAR_INDEX) ~= 0) then - if(element.displayPairs[playerClass]) then - local powerType = UnitPowerType(unit) - shouldEnable = element.displayPairs[playerClass][powerType] - end - end + if((UnitPowerType('player') ~= 0) and (UnitPowerMax('player', 0) ~= 0)) then + shouldEnable = true end - local isEnabled = element.__isEnabled - - if(shouldEnable and not isEnabled) then + if(shouldEnable) then ElementEnable(self) - - --[[ Callback: AdditionalPower:PostVisibility(isVisible) - Called after the element's visibility has been changed. - - * self - the AdditionalPower element - * isVisible - the current visibility state of the element (boolean) - --]] - if(element.PostVisibility) then - element:PostVisibility(true) - end - elseif(not shouldEnable and (isEnabled or isEnabled == nil)) then + else ElementDisable(self) - - if(element.PostVisibility) then - element:PostVisibility(false) - end - elseif(shouldEnable and isEnabled) then - Path(self, event, unit, ADDITIONAL_POWER_BAR_NAME) end end @@ -235,47 +207,22 @@ local function VisibilityPath(self, ...) * event - the event triggering the update (string) * unit - the unit accompanying the event (string) --]] - (self.AdditionalPower.OverrideVisibility or Visibility) (self, ...) + return (self.AdditionalPower.OverrideVisibility or Visibility) (self, ...) end local function ForceUpdate(element) - VisibilityPath(element.__owner, 'ForceUpdate', element.__owner.unit) -end - ---[[ Power:SetFrequentUpdates(state, isForced) -Used to toggle frequent updates. - -* self - the Power element -* state - the desired state (boolean) -* isForced - forces the event update even if the state wasn't changed (boolean) ---]] -local function SetFrequentUpdates(element, state, isForced) - if(element.frequentUpdates ~= state or isForced) then - element.frequentUpdates = state - if(state) then - element.__owner:UnregisterEvent('UNIT_POWER_UPDATE', Path) - element.__owner:RegisterEvent('UNIT_POWER_FREQUENT', Path) - else - element.__owner:UnregisterEvent('UNIT_POWER_FREQUENT', Path) - element.__owner:RegisterEvent('UNIT_POWER_UPDATE', Path) - end - end + return VisibilityPath(element.__owner, 'ForceUpdate', element.__owner.unit) end local function Enable(self, unit) local element = self.AdditionalPower - if(element and UnitIsUnit(unit, 'player')) then + if(element and unit == 'player') then element.__owner = self element.ForceUpdate = ForceUpdate - element.SetFrequentUpdates = SetFrequentUpdates self:RegisterEvent('UNIT_DISPLAYPOWER', VisibilityPath) - if(not element.displayPairs) then - element.displayPairs = CopyTable(ALT_MANA_BAR_PAIR_DISPLAY_INFO) - end - - if(element:IsObjectType('StatusBar') and not (element:GetStatusBarTexture() or element:GetStatusBarAtlas())) then + if(element:IsObjectType('StatusBar') and not element:GetStatusBarTexture()) then element:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]]) end @@ -292,4 +239,4 @@ local function Disable(self) end end -oUF:AddElement('AdditionalPower', VisibilityPath, Enable, Disable) +oUF:AddElement('AdditionalPower', VisibilityPath, Enable, Disable) \ No newline at end of file diff --git a/oUF/elements/auras.lua b/oUF/elements/auras.lua index 3d2d52a..d1ee6a5 100644 --- a/oUF/elements/auras.lua +++ b/oUF/elements/auras.lua @@ -74,24 +74,17 @@ local VISIBLE = 1 local HIDDEN = 0 local function UpdateTooltip(self) - if(GameTooltip:IsForbidden()) then return end - GameTooltip:SetUnitAura(self:GetParent().__owner.unit, self:GetID(), self.filter) end local function onEnter(self) - if(GameTooltip:IsForbidden() or not self:IsVisible()) then return end + if(not self:IsVisible()) then return end - -- Avoid parenting GameTooltip to frames with anchoring restrictions, - -- otherwise it'll inherit said restrictions which will cause issues with - -- its further positioning, clamping, etc - GameTooltip:SetOwner(self, self:GetParent().__restricted and 'ANCHOR_CURSOR' or self:GetParent().tooltipAnchor) + GameTooltip:SetOwner(self, self:GetParent().tooltipAnchor) self:UpdateTooltip() end local function onLeave() - if(GameTooltip:IsForbidden()) then return end - GameTooltip:Hide() end @@ -151,9 +144,7 @@ local function customFilter(element, unit, button, name) end local function updateIcon(element, unit, index, offset, filter, isDebuff, visible) - local name, texture, count, debuffType, duration, expiration, caster, isStealable, - nameplateShowSelf, spellID, canApply, isBossDebuff, casterIsPlayer, nameplateShowAll, - timeMod, effect1, effect2, effect3 = UnitAura(unit, index, filter) + local name, texture, count, debuffType, duration, expiration, caster, isStealable, nameplateShowSelf, spellID, canApply, isBossDebuff, casterIsPlayer, nameplateShowAll, timeMod, effect1, effect2, effect3 = UnitAura(unit, index, filter) if(name) then local position = visible + offset + 1 @@ -492,13 +483,19 @@ local function Enable(self) local buffs = self.Buffs if(buffs) then buffs.__owner = self - -- check if there's any anchoring restrictions - buffs.__restricted = not pcall(self.GetCenter, self) buffs.ForceUpdate = ForceUpdate buffs.createdIcons = buffs.createdIcons or 0 buffs.anchoredIcons = 0 - buffs.tooltipAnchor = buffs.tooltipAnchor or 'ANCHOR_BOTTOMRIGHT' + + -- Avoid parenting GameTooltip to frames with anchoring restrictions, + -- otherwise it'll inherit said restrictions which will cause issues + -- with its further positioning, clamping, etc + if(not pcall(self.GetCenter, self)) then + buffs.tooltipAnchor = 'ANCHOR_CURSOR' + else + buffs.tooltipAnchor = buffs.tooltipAnchor or 'ANCHOR_BOTTOMRIGHT' + end buffs:Show() end @@ -506,13 +503,19 @@ local function Enable(self) local debuffs = self.Debuffs if(debuffs) then debuffs.__owner = self - -- check if there's any anchoring restrictions - debuffs.__restricted = not pcall(self.GetCenter, self) debuffs.ForceUpdate = ForceUpdate debuffs.createdIcons = debuffs.createdIcons or 0 debuffs.anchoredIcons = 0 - debuffs.tooltipAnchor = debuffs.tooltipAnchor or 'ANCHOR_BOTTOMRIGHT' + + -- Avoid parenting GameTooltip to frames with anchoring restrictions, + -- otherwise it'll inherit said restrictions which will cause issues + -- with its further positioning, clamping, etc + if(not pcall(self.GetCenter, self)) then + debuffs.tooltipAnchor = 'ANCHOR_CURSOR' + else + debuffs.tooltipAnchor = debuffs.tooltipAnchor or 'ANCHOR_BOTTOMRIGHT' + end debuffs:Show() end @@ -520,13 +523,19 @@ local function Enable(self) local auras = self.Auras if(auras) then auras.__owner = self - -- check if there's any anchoring restrictions - auras.__restricted = not pcall(self.GetCenter, self) auras.ForceUpdate = ForceUpdate auras.createdIcons = auras.createdIcons or 0 auras.anchoredIcons = 0 - auras.tooltipAnchor = auras.tooltipAnchor or 'ANCHOR_BOTTOMRIGHT' + + -- Avoid parenting GameTooltip to frames with anchoring restrictions, + -- otherwise it'll inherit said restrictions which will cause issues + -- with its further positioning, clamping, etc + if(not pcall(self.GetCenter, self)) then + auras.tooltipAnchor = 'ANCHOR_CURSOR' + else + auras.tooltipAnchor = auras.tooltipAnchor or 'ANCHOR_BOTTOMRIGHT' + end auras:Show() end @@ -545,4 +554,4 @@ local function Disable(self) end end -oUF:AddElement('Auras', Update, Enable, Disable) +oUF:AddElement('Auras', Update, Enable, Disable) \ No newline at end of file diff --git a/oUF/elements/castbar.lua b/oUF/elements/castbar.lua index f43797f..a4e8cb1 100644 --- a/oUF/elements/castbar.lua +++ b/oUF/elements/castbar.lua @@ -2,6 +2,7 @@ # Element: Castbar Handles the visibility and updating of spell castbars. +Based upon oUF_Castbar by starlon. ## Widget @@ -9,12 +10,11 @@ Castbar - A `StatusBar` to represent spell cast/channel progress. ## Sub-Widgets -.Icon - A `Texture` to represent spell icon. -.SafeZone - A `Texture` to represent latency. -.Shield - A `Texture` to represent if it's possible to interrupt or spell steal. -.Spark - A `Texture` to represent the castbar's edge. .Text - A `FontString` to represent spell name. +.Icon - A `Texture` to represent spell icon. .Time - A `FontString` to represent spell duration. +.Shield - A `Texture` to represent if it's possible to interrupt or spell steal. +.SafeZone - A `Texture` to represent latency. ## Notes @@ -22,17 +22,16 @@ A default texture will be applied to the StatusBar and Texture widgets if they d ## Options -.timeToHold - Indicates for how many seconds the castbar should be visible after a _FAILED or _INTERRUPTED - event. Defaults to 0 (number) -.hideTradeSkills - Makes the element ignore casts related to crafting professions (boolean) +.timeToHold - indicates for how many seconds the castbar should be visible after a _FAILED or _INTERRUPTED + event. Defaults to 0 (number) ## Attributes -.castID - A globally unique identifier of the currently cast spell (string?) -.casting - Indicates whether the current spell is an ordinary cast (boolean) -.channeling - Indicates whether the current spell is a channeled cast (boolean) -.notInterruptible - Indicates whether the current spell is interruptible (boolean) -.spellID - The spell identifier of the currently cast/channeled spell (number) +.castID - a globally unique identifier of the currently cast spell (string?) +.casting - indicates whether the current spell is an ordinary cast (boolean) +.channeling - indicates whether the current spell is a channeled cast (boolean) +.notInterruptible - indicates whether the current spell is interruptible (boolean) +.spellID - the spell identifier of the currently cast/channeled spell (number) ## Examples @@ -52,7 +51,6 @@ A default texture will be applied to the StatusBar and Texture widgets if they d local Spark = Castbar:CreateTexture(nil, 'OVERLAY') Spark:SetSize(20, 20) Spark:SetBlendMode('ADD') - Spark:SetPoint('CENTER', Castbar:GetStatusBarTexture(), 'RIGHT', 0, 0) -- Add a timer local Time = Castbar:CreateFontString(nil, 'OVERLAY', 'GameFontNormalSmall') @@ -85,291 +83,423 @@ A default texture will be applied to the StatusBar and Texture widgets if they d Castbar.SafeZone = SafeZone self.Castbar = Castbar --]] - local _, ns = ... local oUF = ns.oUF -local FALLBACK_ICON = 136243 -- Interface\ICONS\Trade_Engineering +local GetNetStats = GetNetStats +local GetTime = GetTime + +local function updateSafeZone(self) + local safeZone = self.SafeZone + local width = self:GetWidth() + local _, _, _, ms = GetNetStats() -local function resetAttributes(self) - self.castID = nil - self.casting = nil - self.channeling = nil - self.notInterruptible = nil - self.spellID = nil + local safeZoneRatio = (ms / 1e3) / self.max + if(safeZoneRatio > 1) then + safeZoneRatio = 1 + end + + safeZone:SetWidth(width * safeZoneRatio) end -local function CastStart(self, event, unit) - if(self.unit ~= unit) then return end +local function UNIT_SPELLCAST_START(self, event, unit) + if(self.unit ~= unit and self.realUnit ~= unit) then return end local element = self.Castbar - local name, _, texture, startTime, endTime, isTradeSkill, castID, notInterruptible, spellID = UnitCastingInfo(unit) - event = 'UNIT_SPELLCAST_START' - if(not name) then - name, _, texture, startTime, endTime, isTradeSkill, notInterruptible, spellID = UnitChannelInfo(unit) - event = 'UNIT_SPELLCAST_CHANNEL_START' - end + local name, text, texture, startTime, endTime, _, castID, notInterruptible, spellID = UnitCastingInfo(unit) - if(not name or (isTradeSkill and element.hideTradeSkills)) then - resetAttributes(element) - element:Hide() - - return + if(not name) then + return element:Hide() end - endTime = endTime / 1000 - startTime = startTime / 1000 + endTime = endTime / 1e3 + startTime = startTime / 1e3 + local max = endTime - startTime - element.max = endTime - startTime - element.startTime = startTime + element.castID = castID + element.duration = GetTime() - startTime + element.max = max element.delay = 0 - element.casting = event == 'UNIT_SPELLCAST_START' - element.channeling = event == 'UNIT_SPELLCAST_CHANNEL_START' + element.casting = true element.notInterruptible = notInterruptible element.holdTime = 0 - element.castID = castID element.spellID = spellID - if(element.casting) then - element.duration = GetTime() - startTime - else - element.duration = endTime - GetTime() - end - - element:SetMinMaxValues(0, element.max) - element:SetValue(element.duration) + element:SetMinMaxValues(0, max) + element:SetValue(0) - if(element.Icon) then element.Icon:SetTexture(texture or FALLBACK_ICON) end - if(element.Shield) then element.Shield:SetShown(notInterruptible) end - if(element.Spark) then element.Spark:Show() end - if(element.Text) then element.Text:SetText(name) end + if(element.Text) then element.Text:SetText(text) end + if(element.Icon) then element.Icon:SetTexture(texture) end if(element.Time) then element.Time:SetText() end - local safeZone = element.SafeZone - if(safeZone) then - local isHoriz = element:GetOrientation() == 'HORIZONTAL' + local shield = element.Shield + if(shield and notInterruptible) then + shield:Show() + elseif(shield) then + shield:Hide() + end - safeZone:ClearAllPoints() - safeZone:SetPoint(isHoriz and 'TOP' or 'LEFT') - safeZone:SetPoint(isHoriz and 'BOTTOM' or 'RIGHT') + local sf = element.SafeZone + if(sf) then + sf:ClearAllPoints() + sf:SetPoint(element:GetReverseFill() and 'LEFT' or 'RIGHT') + sf:SetPoint('TOP') + sf:SetPoint('BOTTOM') + updateSafeZone(element) + end - if(element.casting) then - safeZone:SetPoint(element:GetReverseFill() and (isHoriz and 'LEFT' or 'BOTTOM') or (isHoriz and 'RIGHT' or 'TOP')) - else - safeZone:SetPoint(element:GetReverseFill() and (isHoriz and 'RIGHT' or 'TOP') or (isHoriz and 'LEFT' or 'BOTTOM')) - end + --[[ Callback: Castbar:PostCastStart(unit, name) + Called after the element has been updated upon a spell cast start. - local ratio = (select(4, GetNetStats()) / 1000) / element.max - if(ratio > 1) then - ratio = 1 - elseif(ratio == 0) then - ratio = 0.001 - end + * self - the Castbar widget + * unit - unit for which the update has been triggered (string) + * name - name of the spell being cast (string) + --]] + if(element.PostCastStart) then + element:PostCastStart(unit, name) + end + element:Show() +end + +local function UNIT_SPELLCAST_FAILED(self, event, unit, castID) + if(self.unit ~= unit and self.realUnit ~= unit) then return end - safeZone[isHoriz and 'SetWidth' or 'SetHeight'](safeZone, element[isHoriz and 'GetWidth' or 'GetHeight'](element) * ratio) + local element = self.Castbar + if(element.castID ~= castID) then + return end - --[[ Callback: Castbar:PostCastStart(unit) - Called after the element has been updated upon a spell cast or channel start. + local text = element.Text + if(text) then + text:SetText(FAILED) + end + + element.casting = nil + element.notInterruptible = nil + element.holdTime = element.timeToHold or 0 + + --[[ Callback: Castbar:PostCastFailed(unit) + Called after the element has been updated upon a failed spell cast. * self - the Castbar widget - * unit - the unit for which the update has been triggered (string) + * unit - unit for which the update has been triggered (string) --]] - if(element.PostCastStart) then - element:PostCastStart(unit) + if(element.PostCastFailed) then + return element:PostCastFailed(unit) end - - element:Show() end -local function CastUpdate(self, event, unit, castID, spellID) - if(self.unit ~= unit) then return end +local function UNIT_SPELLCAST_INTERRUPTED(self, event, unit, castID) + if(self.unit ~= unit and self.realUnit ~= unit) then return end local element = self.Castbar - if(not element:IsShown() or element.castID ~= castID or element.spellID ~= spellID) then + if(element.castID ~= castID) then return end - local name, startTime, endTime, _ - if(event == 'UNIT_SPELLCAST_DELAYED') then - name, _, _, startTime, endTime = UnitCastingInfo(unit) - else - name, _, _, startTime, endTime = UnitChannelInfo(unit) + local text = element.Text + if(text) then + text:SetText(INTERRUPTED) end - if(not name) then return end + element.casting = nil + element.channeling = nil + element.holdTime = element.timeToHold or 0 - endTime = endTime / 1000 - startTime = startTime / 1000 + --[[ Callback: Castbar:PostCastInterrupted(unit) + Called after the element has been updated upon an interrupted spell cast. - local delta - if(element.casting) then - delta = startTime - element.startTime + * self - the Castbar widget + * unit - unit for which the update has been triggered (string) + --]] + if(element.PostCastInterrupted) then + return element:PostCastInterrupted(unit) + end +end - element.duration = GetTime() - startTime - else - delta = element.startTime - startTime +local function UNIT_SPELLCAST_NOT_INTERRUPTIBLE(self, event, unit) + if(self.unit ~= unit and self.realUnit ~= unit) then return end - element.duration = endTime - GetTime() + local element = self.Castbar + local shield = element.Shield + if(shield) then + shield:Show() end - if(delta < 0) then - delta = 0 + element.notInterruptible = true + + --[[ Callback: Castbar:PostCastNotInterruptible(unit) + Called after the element has been updated when a spell cast has become non-interruptible. + + * self - the Castbar widget + * unit - unit for which the update has been triggered (string) + --]] + if(element.PostCastNotInterruptible) then + return element:PostCastNotInterruptible(unit) end +end - element.max = endTime - startTime - element.startTime = startTime - element.delay = element.delay + delta +local function UNIT_SPELLCAST_DELAYED(self, event, unit) + if(self.unit ~= unit and self.realUnit ~= unit) then return end - element:SetMinMaxValues(0, element.max) - element:SetValue(element.duration) + local element = self.Castbar + local name, _, _, startTime = UnitCastingInfo(unit) + if(not startTime or not element:IsShown()) then return end + + local duration = GetTime() - (startTime / 1000) + if(duration < 0) then duration = 0 end + + element.delay = element.delay + element.duration - duration + element.duration = duration - --[[ Callback: Castbar:PostCastUpdate(unit) - Called after the element has been updated when a spell cast or channel has been updated. + element:SetValue(duration) + + --[[ Callback: Castbar:PostCastDelayed(unit, name) + Called after the element has been updated when a spell cast has been delayed. * self - the Castbar widget - * unit - the unit that the update has been triggered (string) + * unit - unit that the update has been triggered (string) + * name - name of the delayed spell (string) --]] - if(element.PostCastUpdate) then - return element:PostCastUpdate(unit) + if(element.PostCastDelayed) then + return element:PostCastDelayed(unit, name) end end -local function CastStop(self, event, unit, castID, spellID) - if(self.unit ~= unit) then return end +local function UNIT_SPELLCAST_STOP(self, event, unit, castID) + if(self.unit ~= unit and self.realUnit ~= unit) then return end local element = self.Castbar - if(not element:IsShown() or element.castID ~= castID or element.spellID ~= spellID) then + if(element.castID ~= castID) then return end - resetAttributes(element) + element.casting = nil + element.notInterruptible = nil - --[[ Callback: Castbar:PostCastStop(unit, spellID) - Called after the element has been updated when a spell cast or channel has stopped. + --[[ Callback: Castbar:PostCastStop(unit) + Called after the element has been updated when a spell cast has finished. - * self - the Castbar widget - * unit - the unit for which the update has been triggered (string) - * spellID - the ID of the spell (number) + * self - the Castbar widget + * unit - unit for which the update has been triggered (string) --]] if(element.PostCastStop) then - return element:PostCastStop(unit, spellID) + return element:PostCastStop(unit) end end -local function CastFail(self, event, unit, castID, spellID) - if(self.unit ~= unit) then return end +local function UNIT_SPELLCAST_CHANNEL_START(self, event, unit, _, spellID) + if(self.unit ~= unit and self.realUnit ~= unit) then return end local element = self.Castbar - if(not element:IsShown() or element.castID ~= castID or element.spellID ~= spellID) then + + local name, _, texture, startTime, endTime, _, notInterruptible, spellID = UnitChannelInfo(unit) + + if(not name) then return end - if(element.Text) then - element.Text:SetText(event == 'UNIT_SPELLCAST_FAILED' and FAILED or INTERRUPTED) - end + endTime = endTime / 1e3 + startTime = startTime / 1e3 + local max = (endTime - startTime) + local duration = endTime - GetTime() - if(element.Spark) then element.Spark:Hide() end + element.duration = duration + element.max = max + element.delay = 0 + element.channeling = true + element.notInterruptible = notInterruptible + element.holdTime = 0 + element.spellID = spellID - element.holdTime = element.timeToHold or 0 + -- We have to do this, as it's possible for spell casts to never have _STOP + -- executed or be fully completed by the OnUpdate handler before CHANNEL_START + -- is called. + element.casting = nil + element.castID = nil + + element:SetMinMaxValues(0, max) + element:SetValue(duration) + + if(element.Text) then element.Text:SetText(name) end + if(element.Icon) then element.Icon:SetTexture(texture) end + if(element.Time) then element.Time:SetText() end - resetAttributes(element) - element:SetValue(element.max) + local shield = element.Shield + if(shield and notInterruptible) then + shield:Show() + elseif(shield) then + shield:Hide() + end + + local sf = element.SafeZone + if(sf) then + sf:ClearAllPoints() + sf:SetPoint(element:GetReverseFill() and 'RIGHT' or 'LEFT') + sf:SetPoint('TOP') + sf:SetPoint('BOTTOM') + updateSafeZone(element) + end - --[[ Callback: Castbar:PostCastFail(unit, spellID) - Called after the element has been updated upon a failed or interrupted spell cast. + --[[ Callback: Castbar:PostChannelStart(unit, name) + Called after the element has been updated upon a spell channel start. - * self - the Castbar widget - * unit - the unit for which the update has been triggered (string) - * spellID - the ID of the spell (number) + * self - the Castbar widget + * unit - unit for which the update has been triggered (string) + * name - name of the channeled spell (string) --]] - if(element.PostCastFail) then - return element:PostCastFail(unit, spellID) + if(element.PostChannelStart) then + element:PostChannelStart(unit, name) end + element:Show() end -local function CastInterruptible(self, event, unit) - if(self.unit ~= unit) then return end +local function UNIT_SPELLCAST_CHANNEL_UPDATE(self, event, unit) + if(self.unit ~= unit and self.realUnit ~= unit) then return end local element = self.Castbar - if(not element:IsShown()) then return end + local name, _, _, startTime, endTime = UnitChannelInfo(unit) + if(not name or not element:IsShown()) then + return + end - element.notInterruptible = event == 'UNIT_SPELLCAST_NOT_INTERRUPTIBLE' + local duration = (endTime / 1000) - GetTime() - if(element.Shield) then element.Shield:SetShown(element.notInterruptible) end + element.delay = element.delay + element.duration - duration + element.duration = duration + element.max = (endTime - startTime) / 1000 - --[[ Callback: Castbar:PostCastInterruptible(unit) - Called after the element has been updated when a spell cast has become interruptible or uninterruptible. + element:SetMinMaxValues(0, element.max) + element:SetValue(duration) + + --[[ Callback: Castbar:PostChannelUpdate(unit, name) + Called after the element has been updated after a channeled spell has been delayed or interrupted. * self - the Castbar widget - * unit - the unit for which the update has been triggered (string) + * unit - unit for which the update has been triggered (string) + * name - name of the channeled spell (string) --]] - if(element.PostCastInterruptible) then - return element:PostCastInterruptible(unit) + if(element.PostChannelUpdate) then + return element:PostChannelUpdate(unit, name) + end +end + +local function UNIT_SPELLCAST_CHANNEL_STOP(self, event, unit) + if(self.unit ~= unit and self.realUnit ~= unit) then return end + + local element = self.Castbar + if(element:IsShown()) then + element.channeling = nil + element.notInterruptible = nil + + --[[ Callback: Castbar:PostChannelStop(unit) + Called after the element has been updated after a channeled spell has been completed. + + * self - the Castbar widget + * unit - unit for which the update has been triggered (string) + --]] + if(element.PostChannelStop) then + return element:PostChannelStop(unit) + end end end local function onUpdate(self, elapsed) - if(self.casting or self.channeling) then - local isCasting = self.casting - if(isCasting) then - self.duration = self.duration + elapsed - if(self.duration >= self.max) then - local spellID = self.spellID - - resetAttributes(self) - self:Hide() - - if(self.PostCastStop) then - self:PostCastStop(self.__owner.unit, spellID) - end + if(self.casting) then + local duration = self.duration + elapsed + if(duration >= self.max) then + self.casting = nil + self:Hide() + + if(self.PostCastStop) then self:PostCastStop(self.__owner.unit) end + return + end - return + if(self.Time) then + if(self.delay ~= 0) then + if(self.CustomDelayText) then + self:CustomDelayText(duration) + else + self.Time:SetFormattedText('%.1f|cffff0000-%.1f|r', duration, self.delay) + end + else + if(self.CustomTimeText) then + self:CustomTimeText(duration) + else + self.Time:SetFormattedText('%.1f', duration) + end end - else - self.duration = self.duration - elapsed - if(self.duration <= 0) then - local spellID = self.spellID + end - resetAttributes(self) - self:Hide() + self.duration = duration + self:SetValue(duration) - if(self.PostCastStop) then - self:PostCastStop(self.__owner.unit, spellID) - end + if(self.Spark) then + local horiz = self.horizontal + local size = self[horiz and 'GetWidth' or 'GetHeight'](self) - return + local offset = (duration / self.max) * size + if(self:GetReverseFill()) then + offset = size - offset end + + self.Spark:SetPoint('CENTER', self, horiz and 'LEFT' or 'BOTTOM', horiz and offset or 0, horiz and 0 or offset) + end + elseif(self.channeling) then + local duration = self.duration - elapsed + + if(duration <= 0) then + self.channeling = nil + self:Hide() + + if(self.PostChannelStop) then self:PostChannelStop(self.__owner.unit) end + return end if(self.Time) then if(self.delay ~= 0) then if(self.CustomDelayText) then - self:CustomDelayText(self.duration) + self:CustomDelayText(duration) else - self.Time:SetFormattedText('%.1f|cffff0000%s%.2f|r', self.duration, isCasting and '+' or '-', self.delay) + self.Time:SetFormattedText('%.1f|cffff0000-%.1f|r', duration, self.delay) end else if(self.CustomTimeText) then - self:CustomTimeText(self.duration) + self:CustomTimeText(duration) else - self.Time:SetFormattedText('%.1f', self.duration) + self.Time:SetFormattedText('%.1f', duration) end end end - self:SetValue(self.duration) + self.duration = duration + self:SetValue(duration) + if(self.Spark) then + local horiz = self.horizontal + local size = self[horiz and 'GetWidth' or 'GetHeight'](self) + + local offset = (duration / self.max) * size + if(self:GetReverseFill()) then + offset = size - offset + end + + self.Spark:SetPoint('CENTER', self, horiz and 'LEFT' or 'BOTTOM', horiz and offset or 0, horiz and 0 or offset) + end elseif(self.holdTime > 0) then self.holdTime = self.holdTime - elapsed else - resetAttributes(self) + self.casting = nil + self.castID = nil + self.channeling = nil + self:Hide() end end -local function Update(...) - CastStart(...) +local function Update(self, ...) + UNIT_SPELLCAST_START(self, ...) + return UNIT_SPELLCAST_CHANNEL_START(self, ...) end local function ForceUpdate(element) @@ -378,31 +508,33 @@ end local function Enable(self, unit) local element = self.Castbar - if(element and unit and not unit:match('%wtarget$')) then + if(element) then element.__owner = self element.ForceUpdate = ForceUpdate - self:RegisterEvent('UNIT_SPELLCAST_START', CastStart) - self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_START', CastStart) - self:RegisterEvent('UNIT_SPELLCAST_STOP', CastStop) - self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_STOP', CastStop) - self:RegisterEvent('UNIT_SPELLCAST_DELAYED', CastUpdate) - self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_UPDATE', CastUpdate) - self:RegisterEvent('UNIT_SPELLCAST_FAILED', CastFail) - self:RegisterEvent('UNIT_SPELLCAST_INTERRUPTED', CastFail) - - if oUF.isRetail then - self:RegisterEvent('UNIT_SPELLCAST_INTERRUPTIBLE', CastInterruptible) - self:RegisterEvent('UNIT_SPELLCAST_NOT_INTERRUPTIBLE', CastInterruptible) + if(not (unit and unit:match'%wtarget$')) then + self:RegisterEvent('UNIT_SPELLCAST_START', UNIT_SPELLCAST_START) + self:RegisterEvent('UNIT_SPELLCAST_FAILED', UNIT_SPELLCAST_FAILED) + self:RegisterEvent('UNIT_SPELLCAST_STOP', UNIT_SPELLCAST_STOP) + self:RegisterEvent('UNIT_SPELLCAST_INTERRUPTED', UNIT_SPELLCAST_INTERRUPTED) + self:RegisterEvent('UNIT_SPELLCAST_DELAYED', UNIT_SPELLCAST_DELAYED) + self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_START', UNIT_SPELLCAST_CHANNEL_START) + self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_UPDATE', UNIT_SPELLCAST_CHANNEL_UPDATE) + self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_STOP', UNIT_SPELLCAST_CHANNEL_STOP) end + element.horizontal = element:GetOrientation() == 'HORIZONTAL' element.holdTime = 0 - element:SetScript('OnUpdate', element.OnUpdate or onUpdate) - if(self.unit == 'player' and not (self.hasChildren or self.isChild or self.isNamePlate)) then - CastingBarFrame_SetUnit(CastingBarFrame, nil) - CastingBarFrame_SetUnit(PetCastingBarFrame, nil) + if(self.unit == 'player') then + CastingBarFrame:UnregisterAllEvents() + CastingBarFrame.Show = CastingBarFrame.Hide + CastingBarFrame:Hide() + + PetCastingBarFrame:UnregisterAllEvents() + PetCastingBarFrame.Show = PetCastingBarFrame.Hide + PetCastingBarFrame:Hide() end if(element:IsObjectType('StatusBar') and not element:GetStatusBarTexture()) then @@ -424,8 +556,6 @@ local function Enable(self, unit) safeZone:SetColorTexture(1, 0, 0) end - element:Hide() - return true end end @@ -435,27 +565,17 @@ local function Disable(self) if(element) then element:Hide() - self:UnregisterEvent('UNIT_SPELLCAST_START', CastStart) - self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_START', CastStart) - self:UnregisterEvent('UNIT_SPELLCAST_DELAYED', CastUpdate) - self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_UPDATE', CastUpdate) - self:UnregisterEvent('UNIT_SPELLCAST_STOP', CastStop) - self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_STOP', CastStop) - self:UnregisterEvent('UNIT_SPELLCAST_FAILED', CastFail) - self:UnregisterEvent('UNIT_SPELLCAST_INTERRUPTED', CastFail) - - if oUF.isRetail then - self:UnregisterEvent('UNIT_SPELLCAST_INTERRUPTIBLE', CastInterruptible) - self:UnregisterEvent('UNIT_SPELLCAST_NOT_INTERRUPTIBLE', CastInterruptible) - end + self:UnregisterEvent('UNIT_SPELLCAST_START', UNIT_SPELLCAST_START) + self:UnregisterEvent('UNIT_SPELLCAST_FAILED', UNIT_SPELLCAST_FAILED) + self:UnregisterEvent('UNIT_SPELLCAST_STOP', UNIT_SPELLCAST_STOP) + self:UnregisterEvent('UNIT_SPELLCAST_INTERRUPTED', UNIT_SPELLCAST_INTERRUPTED) + self:UnregisterEvent('UNIT_SPELLCAST_DELAYED', UNIT_SPELLCAST_DELAYED) + self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_START', UNIT_SPELLCAST_CHANNEL_START) + self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_UPDATE', UNIT_SPELLCAST_CHANNEL_UPDATE) + self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_STOP', UNIT_SPELLCAST_CHANNEL_STOP) element:SetScript('OnUpdate', nil) - - if(self.unit == 'player' and not (self.hasChildren or self.isChild or self.isNamePlate)) then - CastingBarFrame_OnLoad(CastingBarFrame, 'player', true, false) - PetCastingBarFrame_OnLoad(PetCastingBarFrame) - end end end -oUF:AddElement('Castbar', Update, Enable, Disable) +oUF:AddElement('Castbar', Update, Enable, Disable) \ No newline at end of file diff --git a/oUF/elements/classpower.lua b/oUF/elements/classpower.lua index 445b37b..c2cc7aa 100644 --- a/oUF/elements/classpower.lua +++ b/oUF/elements/classpower.lua @@ -50,21 +50,13 @@ local oUF = ns.oUF local _, PlayerClass = UnitClass('player') -- sourced from FrameXML/Constants.lua -local SPEC_MAGE_ARCANE = SPEC_MAGE_ARCANE or 1 -local SPEC_MONK_WINDWALKER = SPEC_MONK_WINDWALKER or 3 -local SPEC_PALADIN_RETRIBUTION = SPEC_PALADIN_RETRIBUTION or 3 -local SPEC_WARLOCK_DESTRUCTION = SPEC_WARLOCK_DESTRUCTION or 3 local SPELL_POWER_ENERGY = Enum.PowerType.Energy or 3 local SPELL_POWER_COMBO_POINTS = Enum.PowerType.ComboPoints or 4 -local SPELL_POWER_SOUL_SHARDS = Enum.PowerType.SoulShards or 7 -local SPELL_POWER_HOLY_POWER = Enum.PowerType.HolyPower or 9 -local SPELL_POWER_CHI = Enum.PowerType.Chi or 12 -local SPELL_POWER_ARCANE_CHARGES = Enum.PowerType.ArcaneCharges or 16 -- Holds the class specific stuff. local ClassPowerID, ClassPowerType local ClassPowerEnable, ClassPowerDisable -local RequireSpec, RequirePower, RequireSpell +local RequirePower, RequireSpell local function UpdateColor(element, powerType) local color = element.__owner.colors.power[powerType] @@ -79,23 +71,16 @@ local function UpdateColor(element, powerType) bg:SetVertexColor(r * mu, g * mu, b * mu) end end - - --[[ Callback: ClassPower:PostUpdateColor(r, g, b) - Called after the element color has been updated. - - * self - the ClassPower element - * r - the red component of the used color (number)[0-1] - * g - the green component of the used color (number)[0-1] - * b - the blue component of the used color (number)[0-1] - --]] - if(element.PostUpdateColor) then - element:PostUpdateColor(r, g, b) - end end local function Update(self, event, unit, powerType) - if(not (unit and (UnitIsUnit(unit, 'player') and (not powerType or powerType == ClassPowerType) - or unit == 'vehicle' and powerType == 'COMBO_POINTS'))) then + if event == "PLAYER_TARGET_CHANGED" then + unit, powerType = "player", "COMBO_POINTS" + elseif powerType == "ENERGY" then + powerType = "COMBO_POINTS" -- sometimes powerType return ENERGY for the first combo point + end + + if (not (unit and (UnitIsUnit(unit, 'player') and powerType == ClassPowerType))) then return end @@ -110,30 +95,17 @@ local function Update(self, event, unit, powerType) element:PreUpdate() end - local cur, max, mod, oldMax, chargedIndex + local cur, max, mod, oldMax if(event ~= 'ClassPowerDisable') then - local powerID = unit == 'vehicle' and SPELL_POWER_COMBO_POINTS or ClassPowerID - cur = UnitPower(unit, powerID, true) + local powerID = ClassPowerID + --cur = UnitPower(unit, powerID, true) + cur = GetComboPoints(unit, "target") -- has to use GetComboPoints in classic max = UnitPowerMax(unit, powerID) mod = UnitPowerDisplayMod(powerID) -- mod should never be 0, but according to Blizz code it can actually happen cur = mod == 0 and 0 or cur / mod - -- BUG: Destruction is supposed to show partial soulshards, but Affliction and Demonology should only show full ones - if(ClassPowerType == 'SOUL_SHARDS' and GetSpecialization() ~= SPEC_WARLOCK_DESTRUCTION) then - cur = cur - cur % 1 - end - - if(PlayerClass == 'ROGUE') then - local chargedPoints = GetUnitChargedPowerPoints(unit) - -- according to Blizzard there will only be one - chargedIndex = chargedPoints and chargedPoints[1] - - -- UNIT_POWER_POINT_CHARGE doesn't provide a power type - powerType = powerType or ClassPowerType - end - local numActive = cur + 0.9 for i = 1, max do if(i > numActive) then @@ -165,10 +137,9 @@ local function Update(self, event, unit, powerType) * max - the maximum amount of power (number) * hasMaxChanged - indicates whether the maximum amount has changed since the last update (boolean) * powerType - the active power type (string) - * chargedIndex - the index of the currently charged power point (number?) --]] if(element.PostUpdate) then - return element:PostUpdate(cur, max, oldMax ~= max, powerType, chargedIndex) + return element:PostUpdate(cur, max, oldMax ~= max, powerType) end end @@ -188,26 +159,21 @@ local function Visibility(self, event, unit) local element = self.ClassPower local shouldEnable - if(UnitHasVehicleUI('player')) then - shouldEnable = PlayerVehicleHasComboPoints() - unit = 'vehicle' - elseif(ClassPowerID) then - if(not RequireSpec or RequireSpec == GetSpecialization()) then - -- use 'player' instead of unit because 'SPELLS_CHANGED' is a unitless event - if(not RequirePower or RequirePower == UnitPowerType('player')) then - if(not RequireSpell or IsPlayerSpell(RequireSpell)) then - self:UnregisterEvent('SPELLS_CHANGED', Visibility) - shouldEnable = true - unit = 'player' - else - self:RegisterEvent('SPELLS_CHANGED', Visibility, true) - end + if(ClassPowerID) then + -- use 'player' instead of unit because 'SPELLS_CHANGED' is a unitless event + if(not RequirePower or RequirePower == UnitPowerType('player')) then + if(not RequireSpell or IsPlayerSpell(RequireSpell)) then + self:UnregisterEvent('SPELLS_CHANGED', Visibility) + shouldEnable = true + unit = 'player' + else + self:RegisterEvent('SPELLS_CHANGED', Visibility, true) end end end - local isEnabled = element.__isEnabled - local powerType = unit == 'vehicle' and 'COMBO_POINTS' or ClassPowerType + local isEnabled = element.isEnabled + local powerType = ClassPowerType if(shouldEnable) then --[[ Override: ClassPower:UpdateColor(powerType) @@ -221,22 +187,8 @@ local function Visibility(self, event, unit) if(shouldEnable and not isEnabled) then ClassPowerEnable(self) - - --[[ Callback: ClassPower:PostVisibility(isVisible) - Called after the element's visibility has been changed. - - * self - the ClassPower element - * isVisible - the current visibility state of the element (boolean) - --]] - if(element.PostVisibility) then - element:PostVisibility(true) - end elseif(not shouldEnable and (isEnabled or isEnabled == nil)) then ClassPowerDisable(self) - - if(element.PostVisibility) then - element:PostVisibility(false) - end elseif(shouldEnable and isEnabled) then Path(self, event, unit, powerType) end @@ -260,57 +212,36 @@ end do function ClassPowerEnable(self) self:RegisterEvent('UNIT_POWER_FREQUENT', Path) + self:RegisterEvent('PLAYER_TARGET_CHANGED', Path, true) self:RegisterEvent('UNIT_MAXPOWER', Path) - if(PlayerClass == 'ROGUE') then - self:RegisterEvent('UNIT_POWER_POINT_CHARGE', Path) - end - - self.ClassPower.__isEnabled = true + self.ClassPower.isEnabled = true - if(UnitHasVehicleUI('player')) then - Path(self, 'ClassPowerEnable', 'vehicle', 'COMBO_POINTS') - else - Path(self, 'ClassPowerEnable', 'player', ClassPowerType) - end + Path(self, 'ClassPowerEnable', 'player', ClassPowerType) end function ClassPowerDisable(self) self:UnregisterEvent('UNIT_POWER_FREQUENT', Path) + self:UnregisterEvent('PLAYER_TARGET_CHANGED', Path) self:UnregisterEvent('UNIT_MAXPOWER', Path) - self:UnregisterEvent('UNIT_POWER_POINT_CHARGE', Path) local element = self.ClassPower for i = 1, #element do element[i]:Hide() end - element.__isEnabled = false + self.ClassPower.isEnabled = false Path(self, 'ClassPowerDisable', 'player', ClassPowerType) end - if(PlayerClass == 'MONK') then - ClassPowerID = SPELL_POWER_CHI - ClassPowerType = 'CHI' - RequireSpec = SPEC_MONK_WINDWALKER - elseif(PlayerClass == 'PALADIN') then - ClassPowerID = SPELL_POWER_HOLY_POWER - ClassPowerType = 'HOLY_POWER' - elseif(PlayerClass == 'WARLOCK') then - ClassPowerID = SPELL_POWER_SOUL_SHARDS - ClassPowerType = 'SOUL_SHARDS' - elseif(PlayerClass == 'ROGUE' or PlayerClass == 'DRUID') then + if(PlayerClass == 'ROGUE' or PlayerClass == 'DRUID') then ClassPowerID = SPELL_POWER_COMBO_POINTS ClassPowerType = 'COMBO_POINTS' if(PlayerClass == 'DRUID') then RequirePower = SPELL_POWER_ENERGY - RequireSpell = 5221 -- Shred + RequireSpell = 768 -- Cat Form end - elseif(PlayerClass == 'MAGE') then - ClassPowerID = SPELL_POWER_ARCANE_CHARGES - ClassPowerType = 'ARCANE_CHARGES' - RequireSpec = SPEC_MAGE_ARCANE end end @@ -321,10 +252,6 @@ local function Enable(self, unit) element.__max = #element element.ForceUpdate = ForceUpdate - if(RequireSpec or RequireSpell) then - self:RegisterEvent('PLAYER_TALENT_UPDATE', VisibilityPath, true) - end - if(RequirePower) then self:RegisterEvent('UNIT_DISPLAYPOWER', VisibilityPath) end @@ -335,7 +262,7 @@ local function Enable(self, unit) for i = 1, #element do local bar = element[i] if(bar:IsObjectType('StatusBar')) then - if(not (bar:GetStatusBarTexture() or bar:GetStatusBarAtlas())) then + if(not bar:GetStatusBarTexture()) then bar:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]]) end @@ -351,10 +278,9 @@ local function Disable(self) if(self.ClassPower) then ClassPowerDisable(self) - self:UnregisterEvent('PLAYER_TALENT_UPDATE', VisibilityPath) self:UnregisterEvent('UNIT_DISPLAYPOWER', VisibilityPath) self:UnregisterEvent('SPELLS_CHANGED', Visibility) end end -oUF:AddElement('ClassPower', VisibilityPath, Enable, Disable) +oUF:AddElement('ClassPower', VisibilityPath, Enable, Disable) \ No newline at end of file diff --git a/oUF/elements/healprediction.lua b/oUF/elements/healprediction.lua new file mode 100644 index 0000000..1d85f26 --- /dev/null +++ b/oUF/elements/healprediction.lua @@ -0,0 +1,140 @@ +local _, ns = ... +local oUF = ns.oUF + +local myGUID = UnitGUID('player') +local HealComm = LibStub("LibHealComm-4.0") + +local function UpdateFillBar(previousTexture, bar, amount, ratio) + if amount <= 0 then + bar:Hide() + return previousTexture + end + + bar:SetPoint("TOPLEFT", previousTexture, "TOPRIGHT", 0, 0) + bar:SetPoint("BOTTOMLEFT", previousTexture, "BOTTOMRIGHT", 0, 0) + bar:SetWidth(amount * ratio) + bar:Show() + return bar +end + +local function Update(self, event, unit) + if(self.unit ~= unit) then return end + + local hp = self.HealPredictionAndAbsorb + if(hp.PreUpdate) then hp:PreUpdate(unit) end + + local guid = UnitGUID(unit) + + local myIncomingHeal = UnitGetIncomingHeals(unit, 'player') or 0 + local allIncomingHeal = UnitGetIncomingHeals(unit) or 0 + local allHot = HealComm:GetHealAmount(guid, hp.healType) or 0 + local myHot = (HealComm:GetHealAmount(guid, hp.healType, nil, myGUID) or 0) * (HealComm:GetHealModifier(myGUID) or 1) + local health, maxHealth = UnitHealth(unit), UnitHealthMax(unit) + local ratio = self.Health:GetWidth() / maxHealth + + allIncomingHeal = allIncomingHeal + allHot + myIncomingHeal = myIncomingHeal + myHot + + if(health + allIncomingHeal > maxHealth * hp.maxOverflow) then + allIncomingHeal = maxHealth * hp.maxOverflow - health + end + + if(allIncomingHeal < myIncomingHeal) then + myIncomingHeal = allIncomingHeal + allIncomingHeal = 0 + else + allIncomingHeal = allIncomingHeal - myIncomingHeal + end + + if UnitIsDeadOrGhost(unit) then + myIncomingHeal, allIncomingHeal = 0, 0 + end + + local previousTexture = self.Health:GetStatusBarTexture() + previousTexture = UpdateFillBar(previousTexture, hp.myBar, myIncomingHeal, ratio) + previousTexture = UpdateFillBar(previousTexture, hp.otherBar, allIncomingHeal, ratio) + + if(hp.PostUpdate) then + return hp:PostUpdate(unit) + end +end + +local function Path(self, ...) + return (self.HealPredictionAndAbsorb.Override or Update) (self, ...) +end + +local ForceUpdate = function(element) + return Path(element.__owner, 'ForceUpdate', element.__owner.unit) +end + +local function Enable(self) + local hp = self.HealPredictionAndAbsorb + if(hp) then + hp.__owner = self + hp.ForceUpdate = ForceUpdate + hp.healType = hp.healType or HealComm.OVERTIME_AND_BOMB_HEALS + + self:RegisterEvent('UNIT_MAXHEALTH', Path) + self:RegisterEvent('UNIT_HEALTH_FREQUENT', Path) + self:RegisterEvent('UNIT_HEAL_PREDICTION', Path) + + local function HealCommUpdate(...) + if self.HealPredictionAndAbsorb and self:IsVisible() then + for i = 1, select('#', ...) do + if self.unit and UnitGUID(self.unit) == select(i, ...) then + Path(self, nil, self.unit) + end + end + end + end + + local function HealComm_Heal_Update(event, casterGUID, spellID, healType, _, ...) + HealCommUpdate(...) + end + + local function HealComm_Modified(event, guid) + HealCommUpdate(guid) + end + + HealComm.RegisterCallback(hp, 'HealComm_HealStarted', HealComm_Heal_Update) + HealComm.RegisterCallback(hp, 'HealComm_HealUpdated', HealComm_Heal_Update) + HealComm.RegisterCallback(hp, 'HealComm_HealDelayed', HealComm_Heal_Update) + HealComm.RegisterCallback(hp, 'HealComm_HealStopped', HealComm_Heal_Update) + HealComm.RegisterCallback(hp, 'HealComm_ModifierChanged', HealComm_Modified) + HealComm.RegisterCallback(hp, 'HealComm_GUIDDisappeared', HealComm_Modified) + + if(not hp.maxOverflow) then + hp.maxOverflow = 1.05 + end + + if(hp.myBar and hp.myBar:IsObjectType'Texture' and not hp.myBar:GetTexture()) then + hp.myBar:SetTexture([[Interface\TargetingFrame\UI-StatusBar]]) + end + if(hp.otherBar and hp.otherBar:IsObjectType'Texture' and not hp.otherBar:GetTexture()) then + hp.otherBar:SetTexture([[Interface\TargetingFrame\UI-StatusBar]]) + end + + return true + end +end + +local function Disable(self) + local hp = self.HealPredictionAndAbsorb + if(hp) then + hp.myBar:Hide() + hp.otherBar:Hide() + + HealComm.UnregisterCallback(hp, 'HealComm_HealStarted') + HealComm.UnregisterCallback(hp, 'HealComm_HealUpdated') + HealComm.UnregisterCallback(hp, 'HealComm_HealDelayed') + HealComm.UnregisterCallback(hp, 'HealComm_HealStopped') + HealComm.UnregisterCallback(hp, 'HealComm_ModifierChanged') + HealComm.UnregisterCallback(hp, 'HealComm_GUIDDisappeared') + + self:UnregisterEvent('UNIT_MAXHEALTH', Path) + self:UnregisterEvent('UNIT_HEALTH_FREQUENT', Path) + self:UnregisterEvent('UNIT_HEAL_PREDICTION', Path) + end +end + +oUF:AddElement('HealPredictionAndAbsorb', Path, Enable, Disable) \ No newline at end of file diff --git a/oUF/elements/health.lua b/oUF/elements/health.lua index 9822178..8ef1500 100644 --- a/oUF/elements/health.lua +++ b/oUF/elements/health.lua @@ -23,10 +23,8 @@ A default texture will be applied if the widget is a StatusBar and doesn't have The following options are listed by priority. The first check that returns true decides the color of the bar. -.colorDisconnected - Use `self.colors.disconnected` to color the bar if the unit is offline (boolean) .colorTapping - Use `self.colors.tapping` to color the bar if the unit isn't tapped by the player (boolean) -.colorThreat - Use `self.colors.threat[threat]` to color the bar based on the unit's threat status. `threat` is - defined by the first return of [UnitThreatSituation](https://wow.gamepedia.com/API_UnitThreatSituation) (boolean) +.colorDisconnected - Use `self.colors.disconnected` to color the bar if the unit is offline (boolean) .colorClass - Use `self.colors.class[class]` to color the bar based on unit class. `class` is defined by the second return of [UnitClass](http://wowprogramming.com/docs/api/UnitClass.html) (boolean) .colorClassNPC - Use `self.colors.class[class]` to color the bar if the unit is a NPC (boolean) @@ -47,33 +45,37 @@ The following options are listed by priority. The first check that returns true .multiplier - Used to tint the background based on the main widgets R, G and B values. Defaults to 1 (number)[0-1] +## Attributes + +.disconnected - Indicates whether the unit is disconnected (boolean) + ## Examples - -- Position and size - local Health = CreateFrame('StatusBar', nil, self) - Health:SetHeight(20) - Health:SetPoint('TOP') - Health:SetPoint('LEFT') - Health:SetPoint('RIGHT') - - -- Add a background - local Background = Health:CreateTexture(nil, 'BACKGROUND') - Background:SetAllPoints(Health) - Background:SetTexture(1, 1, 1, .5) - - -- Options - Health.colorTapping = true - Health.colorDisconnected = true - Health.colorClass = true - Health.colorReaction = true - Health.colorHealth = true - - -- Make the background darker. - Background.multiplier = .5 - - -- Register it with oUF - Health.bg = Background - self.Health = Health + -- Position and size + local Health = CreateFrame('StatusBar', nil, self) + Health:SetHeight(20) + Health:SetPoint('TOP') + Health:SetPoint('LEFT') + Health:SetPoint('RIGHT') + + -- Add a background + local Background = Health:CreateTexture(nil, 'BACKGROUND') + Background:SetAllPoints(Health) + Background:SetTexture(1, 1, 1, .5) + + -- Options + Health.colorTapping = true + Health.colorDisconnected = true + Health.colorClass = true + Health.colorReaction = true + Health.colorHealth = true + + -- Make the background darker. + Background.multiplier = .5 + + -- Register it with oUF + Health.bg = Background + self.Health = Health --]] local _, ns = ... @@ -87,17 +89,15 @@ local function UpdateColor(self, event, unit) local element = self.Health local r, g, b, t - if(element.colorDisconnected and not UnitIsConnected(unit)) then + if(element.colorDisconnected and element.disconnected) then t = self.colors.disconnected elseif(element.colorTapping and not UnitPlayerControlled(unit) and UnitIsTapDenied(unit)) then t = self.colors.tapped - elseif(element.colorHappiness and UnitIsUnit(unit, "pet") and GetPetHappiness and GetPetHappiness()) then + elseif(element.colorHappiness and unit == "pet" and GetPetHappiness()) then t = self.colors.happiness[GetPetHappiness()] - elseif(element.colorThreat and not UnitPlayerControlled(unit) and UnitThreatSituation('player', unit)) then - t = self.colors.threat[UnitThreatSituation('player', unit)] - elseif(element.colorClass and UnitIsPlayer(unit)) - or (element.colorClassNPC and not UnitIsPlayer(unit)) - or (element.colorClassPet and UnitPlayerControlled(unit) and not UnitIsPlayer(unit)) then + elseif(element.colorClass and UnitIsPlayer(unit)) or + (element.colorClassNPC and not UnitIsPlayer(unit)) or + (element.colorClassPet and UnitPlayerControlled(unit) and not UnitIsPlayer(unit)) then local _, class = UnitClass(unit) t = self.colors.class[class] elseif(element.colorSelection and unitSelectionType(unit, element.considerSelectionInCombatHostile)) then @@ -124,15 +124,6 @@ local function UpdateColor(self, event, unit) end end - --[[ Callback: Health:PostUpdateColor(unit, r, g, b) - Called after the element color has been updated. - - * self - the Health element - * unit - the unit for which the update has been triggered (string) - * r - the red component of the used color (number)[0-1] - * g - the green component of the used color (number)[0-1] - * b - the blue component of the used color (number)[0-1] - --]] if(element.PostUpdateColor) then element:PostUpdateColor(unit, r, g, b) end @@ -164,27 +155,18 @@ local function Update(self, event, unit) end local cur, max = UnitHealth(unit), UnitHealthMax(unit) - - if element.smoothing then - element:SetMinMaxSmoothedValue(0, max) + local disconnected = not UnitIsConnected(unit) + element:SetMinMaxValues(0, max) - if(UnitIsConnected(unit)) then - element:SetSmoothedValue(cur) - else - element:SetSmoothedValue(max) - end + if(disconnected) then + element:SetValue(max) else - element:SetMinMaxValues(0, max) - - if(UnitIsConnected(unit)) then - element:SetValue(cur) - else - element:SetValue(max) - end + element:SetValue(cur) end element.cur = cur element.max = max + element.disconnected = disconnected --[[ Callback: Health:PostUpdate(unit, cur, max) Called after the element has been updated. @@ -199,7 +181,7 @@ local function Update(self, event, unit) end end -local function Path(self, ...) +local function Path(self, event, ...) --[[ Override: Health.Override(self, event, unit) Used to completely override the internal update function. @@ -207,83 +189,45 @@ local function Path(self, ...) * event - the event triggering the update (string) * unit - the unit accompanying the event (string) --]] - (self.Health.Override or Update) (self, ...); + (self.Health.Override or Update) (self, event, ...); - ColorPath(self, ...) + ColorPath(self, event, ...) end local function ForceUpdate(element) Path(element.__owner, 'ForceUpdate', element.__owner.unit) end ---[[ Health:SetColorDisconnected(state, isForced) -Used to toggle coloring if the unit is offline. - -* self - the Health element -* state - the desired state (boolean) -* isForced - forces the event update even if the state wasn't changed (boolean) ---]] -local function SetColorDisconnected(element, state, isForced) - if(element.colorDisconnected ~= state or isForced) then - element.colorDisconnected = state - if(state) then - element.__owner:RegisterEvent('UNIT_CONNECTION', ColorPath) - else - element.__owner:UnregisterEvent('UNIT_CONNECTION', ColorPath) - end - end -end - ---[[ Health:SetColorSelection(state, isForced) -Used to toggle coloring by the unit's selection. +local onUpdateElapsed, onUpdateWait = 0, 0.25 +local function onUpdateHealth(self, elapsed) + if onUpdateElapsed > onUpdateWait then + Path(self.__owner, 'OnUpdate', self.__owner.unit) -* self - the Health element -* state - the desired state (boolean) -* isForced - forces the event update even if the state wasn't changed (boolean) ---]] -local function SetColorSelection(element, state, isForced) - if(element.colorSelection ~= state or isForced) then - element.colorSelection = state - if(state) then - element.__owner:RegisterEvent('UNIT_FLAGS', ColorPath) - else - element.__owner:UnregisterEvent('UNIT_FLAGS', ColorPath) - end + onUpdateElapsed = 0 + else + onUpdateElapsed = onUpdateElapsed + elapsed end end ---[[ Health:SetColorTapping(state, isForced) -Used to toggle coloring if the unit isn't tapped by the player. - -* self - the Health element -* state - the desired state (boolean) -* isForced - forces the event update even if the state wasn't changed (boolean) ---]] -local function SetColorTapping(element, state, isForced) - if(element.colorTapping ~= state or isForced) then - element.colorTapping = state - if(state) then - element.__owner:RegisterEvent('UNIT_FACTION', ColorPath) - else - element.__owner:UnregisterEvent('UNIT_FACTION', ColorPath) - end - end +local function SetHealthUpdateSpeed(self, state) + if state < .1 then state = .1 end + onUpdateWait = state end ---[[ Health:SetColorThreat(state, isForced) -Used to toggle coloring by the unit's threat status. +local function SetHealthUpdateMethod(self, state, force) + if self.effectiveHealth ~= state or force then + self.effectiveHealth = state -* self - the Health element -* state - the desired state (boolean) -* isForced - forces the event update even if the state wasn't changed (boolean) ---]] -local function SetColorThreat(element, state, isForced) - if(element.colorThreat ~= state or isForced) then - element.colorThreat = state - if(state) then - element.__owner:RegisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath) + if state then + self.Health:SetScript('OnUpdate', onUpdateHealth) + self:UnregisterEvent('UNIT_HEALTH_FREQUENT', Path) + self:UnregisterEvent('UNIT_HEALTH', Path) + self:UnregisterEvent('UNIT_MAXHEALTH', Path) else - element.__owner:UnregisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath) + self.Health:SetScript('OnUpdate', nil) + self:RegisterEvent('UNIT_HEALTH', Path) -- Needed for Pet Battles + self:RegisterEvent('UNIT_HEALTH_FREQUENT', Path) + self:RegisterEvent('UNIT_MAXHEALTH', Path) end end end @@ -293,41 +237,23 @@ local function Enable(self, unit) if(element) then element.__owner = self element.ForceUpdate = ForceUpdate - element.SetColorDisconnected = SetColorDisconnected - element.SetColorSelection = SetColorSelection - element.SetColorTapping = SetColorTapping - element.SetColorThreat = SetColorThreat + + self.SetHealthUpdateSpeed = SetHealthUpdateSpeed + self.SetHealthUpdateMethod = SetHealthUpdateMethod + SetHealthUpdateMethod(self, self.effectiveHealth, true) if(element.colorDisconnected) then self:RegisterEvent('UNIT_CONNECTION', ColorPath) - end - - if(element.colorSelection) then - self:RegisterEvent('UNIT_FLAGS', ColorPath) + self:RegisterEvent('PARTY_MEMBER_ENABLE', ColorPath) + self:RegisterEvent('PARTY_MEMBER_DISABLE', ColorPath) end if(element.colorTapping) then self:RegisterEvent('UNIT_FACTION', ColorPath) end + self:RegisterEvent('UNIT_HAPPINESS', ColorPath) - if(element.colorThreat) then - self:RegisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath) - end - - if(element.smoothing) then - element.SetSmoothedValue = SmoothStatusBarMixin.SetSmoothedValue - element.SetMinMaxSmoothedValue = SmoothStatusBarMixin.SetMinMaxSmoothedValue - end - - if oUF.Retail then - self:RegisterEvent('UNIT_HEALTH', Path) - else - self:RegisterEvent('UNIT_HEALTH_FREQUENT', Path) - end - - self:RegisterEvent('UNIT_MAXHEALTH', Path) - - if(element:IsObjectType('StatusBar') and not (element:GetStatusBarTexture() or element:GetStatusBarAtlas())) then + if(element:IsObjectType('StatusBar') and not element:GetStatusBarTexture()) then element:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]]) end @@ -342,13 +268,17 @@ local function Disable(self) if(element) then element:Hide() + element:SetScript('OnUpdate', nil) + self:UnregisterEvent('UNIT_HEALTH_FREQUENT', Path) self:UnregisterEvent('UNIT_HEALTH', Path) self:UnregisterEvent('UNIT_MAXHEALTH', Path) + self:UnregisterEvent('UNIT_CONNECTION', ColorPath) self:UnregisterEvent('UNIT_FACTION', ColorPath) - self:UnregisterEvent('UNIT_FLAGS', ColorPath) - self:UnregisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath) + self:UnregisterEvent('PARTY_MEMBER_ENABLE', ColorPath) + self:UnregisterEvent('PARTY_MEMBER_DISABLE', ColorPath) + self:UnregisterEvent('UNIT_HAPPINESS', ColorPath) end end -oUF:AddElement('Health', Path, Enable, Disable) +oUF:AddElement('Health', Path, Enable, Disable) \ No newline at end of file diff --git a/oUF/elements/phaseindicator.lua b/oUF/elements/phaseindicator.lua index 4242a7f..24ac51d 100644 --- a/oUF/elements/phaseindicator.lua +++ b/oUF/elements/phaseindicator.lua @@ -7,27 +7,16 @@ Toggles the visibility of an indicator based on the unit's phasing relative to t PhaseIndicator - Any UI widget. -## Sub-Widgets - -Icon - A `Texture` to represent the phased status. - ## Notes A default texture will be applied if the widget is a Texture and doesn't have a texture or a color set. -OnEnter and OnLeave script handlers will be set to display a Tooltip if the widget is mouse enabled and does not have -OnEnter and/or OnLeave handlers. ## Examples -- Position and size - local PhaseIndicator = CreateFrame('Frame', nil, self) + local PhaseIndicator = self:CreateTexture(nil, 'OVERLAY') PhaseIndicator:SetSize(16, 16) PhaseIndicator:SetPoint('TOPLEFT', self) - PhaseIndicator:EnableMouse(true) - - local Icon = PhaseIndicator:CreateTexture(nil, 'OVERLAY') - Icon:SetAllPoints() - PhaseIndicator.Icon = Icon -- Register it with oUF self.PhaseIndicator = PhaseIndicator @@ -36,32 +25,6 @@ OnEnter and/or OnLeave handlers. local _, ns = ... local oUF = ns.oUF ---[[ Override: PhaseIndicator:UpdateTooltip() -Used to populate the tooltip when the widget is hovered. - -* self - the PhaseIndicator widget ---]] -local function UpdateTooltip(element) - local text = PartyUtil.GetPhasedReasonString(element.reason, element.__owner.unit) - if(text) then - GameTooltip:SetText(text, nil, nil, nil, nil, true) - GameTooltip:Show() - end -end - -local function onEnter(element) - if(not element:IsVisible()) then return end - - if(element.reason) then - GameTooltip:SetOwner(element, 'ANCHOR_BOTTOMRIGHT') - element:UpdateTooltip() - end -end - -local function onLeave() - GameTooltip:Hide() -end - local function Update(self, event, unit) if(self.unit ~= unit) then return end @@ -76,26 +39,21 @@ local function Update(self, event, unit) element:PreUpdate() end - -- BUG: UnitPhaseReason returns wrong data for friendly NPCs in phased scenarios like WM or Chromie Time - -- https://github.com/Stanzilla/WoWUIBugs/issues/49 - local phaseReason = UnitIsPlayer(unit) and UnitIsConnected(unit) and UnitPhaseReason(unit) or nil - if(phaseReason) then + local isInSamePhase = UnitInPhase(unit) + if(not isInSamePhase and UnitIsPlayer(unit) and UnitIsConnected(unit)) then element:Show() else element:Hide() end - element.reason = phaseReason - - --[[ Callback: PhaseIndicator:PostUpdate(isInSamePhase, phaseReason) + --[[ Callback: PhaseIndicator:PostUpdate(isInSamePhase) Called after the element has been updated. * self - the PhaseIndicator element * isInSamePhase - indicates whether the unit is in the same phase as the player (boolean) - * phaseReason - the reason why the unit is in a different phase (number?) --]] if(element.PostUpdate) then - return element:PostUpdate(not phaseReason, phaseReason) + return element:PostUpdate(isInSamePhase) end end @@ -122,21 +80,8 @@ local function Enable(self) self:RegisterEvent('UNIT_PHASE', Path) - local icon = (element.Icon or element) - if(icon:IsObjectType('Texture') and not icon:GetTexture()) then - icon:SetTexture([[Interface\TargetingFrame\UI-PhasingIcon]]) - end - - if(element.IsMouseEnabled and element:IsMouseEnabled()) then - if(not element:GetScript('OnEnter')) then - element:SetScript('OnEnter', onEnter) - end - - if(not element:GetScript('OnLeave')) then - element:SetScript('OnLeave', onLeave) - end - - element.UpdateTooltip = element.UpdateTooltip or UpdateTooltip + if(element:IsObjectType('Texture') and not element:GetTexture()) then + element:SetTexture([[Interface\TargetingFrame\UI-PhasingIcon]]) end return true @@ -152,4 +97,4 @@ local function Disable(self) end end -oUF:AddElement('PhaseIndicator', Path, Enable, Disable) +oUF:AddElement('PhaseIndicator', Path, Enable, Disable) \ No newline at end of file diff --git a/oUF/elements/power.lua b/oUF/elements/power.lua index 8767086..873618b 100644 --- a/oUF/elements/power.lua +++ b/oUF/elements/power.lua @@ -108,9 +108,9 @@ type and zero for the minimum value. --]] local function GetDisplayPower(element) local unit = element.__owner.unit - local barInfo = GetUnitPowerBarInfo(unit) - if(barInfo and barInfo.showOnRaid and (UnitInParty(unit) or UnitInRaid(unit))) then - return ALTERNATE_POWER_INDEX, barInfo.minPower + local _, min, _, _, _, _, showOnRaid = UnitAlternatePowerInfo(unit) + if(showOnRaid) then + return ALTERNATE_POWER_INDEX, min end end @@ -121,10 +121,13 @@ local function UpdateColor(self, event, unit) local pType, pToken, altR, altG, altB = UnitPowerType(unit) local r, g, b, t + local happiness = GetPetHappiness() if(element.colorDisconnected and not UnitIsConnected(unit)) then t = self.colors.disconnected elseif(element.colorTapping and not UnitPlayerControlled(unit) and UnitIsTapDenied(unit)) then t = self.colors.tapped + elseif(element.colorHappiness and unit == "pet" and happiness) then + t = self.colors.happiness[happiness] elseif(element.colorThreat and not UnitPlayerControlled(unit) and UnitThreatSituation('player', unit)) then t = self.colors.threat[UnitThreatSituation('player', unit)] elseif(element.colorPower) then @@ -219,23 +222,12 @@ local function Update(self, event, unit) end local cur, max = UnitPower(unit, displayType), UnitPowerMax(unit, displayType) - - if element.smoothing then - element:SetMinMaxSmoothedValue(min or 0, max) + element:SetMinMaxValues(min or 0, max) - if(UnitIsConnected(unit)) then - element:SetSmoothedValue(cur) - else - element:SetSmoothedValue(max) - end + if(UnitIsConnected(unit)) then + element:SetValue(cur) else - element:SetMinMaxValues(min or 0, max) - - if(UnitIsConnected(unit)) then - element:SetValue(cur) - else - element:SetValue(max) - end + element:SetValue(max) end element.cur = cur @@ -399,16 +391,12 @@ local function Enable(self) else self:RegisterEvent('UNIT_POWER_UPDATE', Path) end - - if(element.smoothing) then - element.SetSmoothedValue = SmoothStatusBarMixin.SetSmoothedValue - element.SetMinMaxSmoothedValue = SmoothStatusBarMixin.SetMinMaxSmoothedValue - end self:RegisterEvent('UNIT_DISPLAYPOWER', Path) self:RegisterEvent('UNIT_MAXPOWER', Path) self:RegisterEvent('UNIT_POWER_BAR_HIDE', Path) self:RegisterEvent('UNIT_POWER_BAR_SHOW', Path) + self:RegisterEvent('UNIT_HAPPINESS', Path) if(element:IsObjectType('StatusBar') and not (element:GetStatusBarTexture() or element:GetStatusBarAtlas())) then element:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]]) @@ -435,6 +423,7 @@ local function Disable(self) self:UnregisterEvent('UNIT_POWER_BAR_SHOW', Path) self:UnregisterEvent('UNIT_POWER_FREQUENT', Path) self:UnregisterEvent('UNIT_POWER_UPDATE', Path) + self:UnregisterEvent('UNIT_HAPPINESS', Path) self:UnregisterEvent('UNIT_CONNECTION', ColorPath) self:UnregisterEvent('UNIT_FACTION', ColorPath) self:UnregisterEvent('UNIT_FLAGS', ColorPath) @@ -442,4 +431,4 @@ local function Disable(self) end end -oUF:AddElement('Power', Path, Enable, Disable) +oUF:AddElement('Power', Path, Enable, Disable) \ No newline at end of file diff --git a/oUF/elements/raidroleindicator.lua b/oUF/elements/raidroleindicator.lua index 6bdb92c..ed63d82 100644 --- a/oUF/elements/raidroleindicator.lua +++ b/oUF/elements/raidroleindicator.lua @@ -42,10 +42,7 @@ local function Update(self, event) end local role, isShown - --[[ classic disabled - if(UnitInRaid(unit) and not UnitHasVehicleUI(unit)) then - ]] - if UnitInRaid(unit) then + if(UnitInRaid(unit)) then if(GetPartyAssignment('MAINTANK', unit)) then isShown = true element:SetTexture(MAINTANK_ICON) diff --git a/oUF/elements/range.lua b/oUF/elements/range.lua index f68e920..071e32d 100644 --- a/oUF/elements/range.lua +++ b/oUF/elements/range.lua @@ -33,15 +33,6 @@ local OnRangeFrame local UnitInRange, UnitIsConnected = UnitInRange, UnitIsConnected -local RangeSpellID = { - ["PALADIN"] = 19750, - ["SHAMAN"] = 25357, - ["DRUID"] = 774, - ["PRIEST"] = 2050, - ["WARLOCK"] = 5697, - ["MAGE"] = 475, -} - local function Update(self, event) local element = self.Range local unit = self.unit @@ -58,29 +49,7 @@ local function Update(self, event) local inRange, checkedRange local connected = UnitIsConnected(unit) if(connected) then - if oUF.Retail then - inRange, checkedRange = UnitInRange(unit) - else - local Spell = RangeSpellID[select(2, UnitClass("player"))] - local IsFriend = UnitIsFriend(unit, "player") - - if IsFriend and Spell and IsSpellKnown(Spell) then - local name, rank, icon, castTime, minRange, maxRange, spellId = GetSpellInfo(Spell) - local IsSpellInRangeFromPlayer = IsSpellInRange(name, unit) - - if IsSpellInRangeFromPlayer == 1 then - inRange = true - checkedRange = true - else - inRange = false - checkedRange = true - end - else - inRange = CheckInteractDistance(unit, 4) - checkedRange = true - end - end - + inRange, checkedRange = UnitInRange(unit) if(checkedRange and not inRange) then self:SetAlpha(element.outsideAlpha) else diff --git a/oUF/elements/readycheckindicator.lua b/oUF/elements/readycheckindicator.lua index 60eac4c..1906301 100644 --- a/oUF/elements/readycheckindicator.lua +++ b/oUF/elements/readycheckindicator.lua @@ -122,7 +122,7 @@ end local function Enable(self, unit) local element = self.ReadyCheckIndicator - unit = unit and unit:match('(%a+)%d*$') + unit = unit and unit:match('(%a+)%d*') if(element and (unit == 'party' or unit == 'raid')) then element.__owner = self element.ForceUpdate = ForceUpdate diff --git a/oUF/elements/tags.lua b/oUF/elements/tags.lua index ae10d79..1d51f06 100644 --- a/oUF/elements/tags.lua +++ b/oUF/elements/tags.lua @@ -12,25 +12,15 @@ A FontString to hold a tag string. Unlike other elements, this widget must not h ## Notes A `Tag` is a Lua string consisting of a function name surrounded by square brackets. The tag will be replaced by the -output of the function and displayed as text on the font string widget with that the tag has been registered. +output of the function and displayed as text on the font string widget with that the tag has been registered. Literals +can be pre- or appended by separating them with a `>` before or `<` after the function name. The literals will be only +displayed when the function returns a non-nil value. I.e. `"[perhp<%]"` will display the current health as a percentage +of the maximum health followed by the % sign. -A `Tag String` is a Lua string consisting of one or multiple tags with optional literals and parameters around them. -Each tag will be updated individually and the output will follow the tags order. Literals will be displayed in the -output string regardless of whether the surrounding tag functions return a value. I.e. `"[curhp]/[maxhp]"` will resolve -to something like `2453/5000`. - -There's also an optional prefix and suffix that are separated from the tag name by `$>` and `<$` respectively, -for example, `"[==$>name<$==]"` will resolve to `==Thrall==`, and `"[perhp<$%]"` will resole to `100%`, however, said -affixes will only be added if the tag function returns a non-empty string, if it returns `nil` or `""` affixes will be -omitted. - -Additionally, it's possible to pass optional arguments to a tag function to alter its behaviour. Optional arguments are -defined via `()` at the end of a tag and separated by commas (`,`). For example, `"[name(a,r,g,s)]"`, in this case 4 -additional arguments, `"a"`, `"r"`, `"g"`, and `"s"` will be passed to the name tag function, what to do with them, -however, is up to a developer to decide. - -The full tag syntax looks like this: `"[prefix$>tag<$suffix(a,r,g,s)]"`. The order of optional elements is important, -while they can be independently omitted, they can't be reordered. +A `Tag String` is a Lua string consisting of one or multiple tags with optional literals between them. Each tag will be +updated individually and the output will follow the tags order. Literals will be displayed in the output string +regardless of whether the surrounding tag functions return a value. I.e. `"[curhp]/[maxhp]"` will resolve to something +like `2453/5000`. A `Tag Function` is used to replace a single tag in a tag string by its output. A tag function receives only two arguments - the unit and the realUnit of the unit frame used to register the tag (see Options for further details). The @@ -47,9 +37,9 @@ in the `oUF.Tags.SharedEvents` table as follows: `oUF.Tags.SharedEvents.EVENT_NA .overrideUnit - if specified on the font string widget, the frame's realUnit will be passed as the second argument to every tag function whose name is contained in the relevant tag string. Otherwise the second argument is always nil (boolean) -.frequentUpdates - defines how often the corresponding tag function(s) should be called. This will override the events - for the tag(s), if any. If the value is a number, it is taken as a time interval in seconds. If the - value is a boolean, the time interval is set to 0.5 seconds (number or boolean) +.frequentUpdates - defines how often the correspondig tag function(s) should be called. This will override the events for + the tag(s), if any. If the value is a number, it is taken as a time interval in seconds. If the value + is a boolean, the time interval is set to 0.5 seconds (number or boolean) ## Attributes @@ -57,8 +47,6 @@ in the `oUF.Tags.SharedEvents` table as follows: `oUF.Tags.SharedEvents.EVENT_NA ## Examples -### Example 1 - -- define the tag function oUF.Tags.Methods['mylayout:threatname'] = function(unit, realUnit) local color = _TAGS['threatcolor'](unit) @@ -75,39 +63,15 @@ in the `oUF.Tags.SharedEvents` table as follows: `oUF.Tags.SharedEvents.EVENT_NA -- register the tag on the text widget with oUF self:Tag(info, '[mylayout:threatname]') - -### Example 2 - - -- define the tag function that accepts optional arguments - oUF.Tags.Methods['mylayout:name'] = function(unit, realUnit, ...) - local name = _TAGS['name'](unit, realUnit) - local length = tonumber(...) - if(length) then - return name:sub(1, length) -- please note, this code doesn't support UTF-8 chars - else - return name - end - end - - -- add the events - oUF.Tags.Events['mylayout:name'] = 'UNIT_NAME_UPDATE' - - -- create the text widget - local info = self.Health:CreateFontString(nil, 'OVERLAY', 'GameFontNormal') - info:SetPoint('LEFT') - - -- register the tag on the text widget with oUF - self:Tag(info, '[mylayout:name(5)]') -- the output will be shortened to 5 characters - -- self:Tag(info, '[mylayout:name]') -- alternative, the output won't be adjusted - -- self:Tag(info, '[mylayout:name(10)]') -- alternative, the output will be shortened to 10 characters --]] local _, ns = ... local oUF = ns.oUF local Private = oUF.Private -local xpcall = Private.xpcall +local nierror = Private.nierror local unitExists = Private.unitExists +local xpcall = Private.xpcall local _PATTERN = '%[..-%]+' @@ -146,17 +110,6 @@ local tagStrings = { end end]], - ['arenaspec'] = [[function(u) - local id = u:match('arena(%d)$') - if(id) then - local specID = GetArenaOpponentSpec(tonumber(id)) - if(specID and specID > 0) then - local _, specName = GetSpecializationInfoByID(specID) - return specName - end - end - end]], - ['chi'] = [[function() if(GetSpecialization() == SPEC_MONK_WINDWALKER) then local num = UnitPower('player', Enum.PowerType.Chi) @@ -258,9 +211,6 @@ local tagStrings = { ['level'] = [[function(u) local l = UnitLevel(u) - if(UnitIsWildBattlePet and UnitIsWildBattlePet(u) or UnitIsWildBattlePet and UnitIsBattlePetCompanion(u)) then - l = UnitBattlePetLevel(u) - end if(l > 0) then return l @@ -334,7 +284,7 @@ local tagStrings = { return Hex(altR, altG, altB) end else - return Hex(_COLORS.power[pType] or _COLORS.power.MANA) + return Hex(_COLORS.power[pType]) end end @@ -536,7 +486,6 @@ _ENV._VARS = vars local tagEvents = { ['affix'] = 'UNIT_CLASSIFICATION_CHANGED', ['arcanecharges'] = 'UNIT_POWER_UPDATE PLAYER_TALENT_UPDATE', - ['arenaspec'] = 'ARENA_PREP_OPPONENT_SPECIALIZATIONS', ['chi'] = 'UNIT_POWER_UPDATE PLAYER_TALENT_UPDATE', ['classification'] = 'UNIT_CLASSIFICATION_CHANGED', ['cpoints'] = 'UNIT_POWER_FREQUENT PLAYER_TARGET_CHANGED', @@ -571,17 +520,13 @@ local tagEvents = { ['smartlevel'] = 'UNIT_LEVEL PLAYER_LEVEL_UP UNIT_CLASSIFICATION_CHANGED', ['soulshards'] = 'UNIT_POWER_UPDATE', ['status'] = 'UNIT_HEALTH PLAYER_UPDATE_RESTING UNIT_CONNECTION', - ['threat'] = 'UNIT_THREAT_SITUATION_UPDATE', - ['threatcolor'] = 'UNIT_THREAT_SITUATION_UPDATE', } local unitlessEvents = { - ARENA_PREP_OPPONENT_SPECIALIZATIONS = true, GROUP_ROSTER_UPDATE = true, NEUTRAL_FACTION_SELECT_RESULT = true, PARTY_LEADER_CHANGED = true, PLAYER_LEVEL_UP = true, - PLAYER_TALENT_UPDATE = true, PLAYER_TARGET_CHANGED = true, PLAYER_UPDATE_RESTING = true, RUNE_POWER_UPDATE = true, @@ -644,10 +589,10 @@ local tagPool = {} local funcPool = {} local tmp = {} -local function getBracketData(tag) - -- full tag syntax: '[prefix$>tag-name<$suffix(a,r,g,s)]' - local suffixEnd = (tag:match('()%(') or -1) - 1 +-- full tag syntax: '[prefix$>tag-name<$suffix(a,r,g,s)]' +-- for a small test case see https://github.com/oUF-wow/oUF/pull/602 +local function getBracketData(tag) local prefixEnd, prefixOffset = tag:match('()$>'), 1 if(not prefixEnd) then prefixEnd = 1 @@ -656,6 +601,7 @@ local function getBracketData(tag) prefixOffset = 3 end + local suffixEnd = (tag:match('()%(', prefixOffset + 1) or -1) - 1 local suffixStart, suffixOffset = tag:match('<$()', prefixEnd), 1 if(not suffixStart) then suffixStart = suffixEnd + 1 @@ -663,7 +609,11 @@ local function getBracketData(tag) suffixOffset = 3 end - return tag:sub(prefixEnd + prefixOffset, suffixStart - suffixOffset), prefixEnd, suffixStart, suffixEnd, tag:match('%((.-)%)') + return tag:sub(prefixEnd + prefixOffset, suffixStart - suffixOffset), + prefixEnd, + suffixStart, + suffixEnd, + tag:match('%((.-)%)', suffixOffset + 1) end local function getTagFunc(tagstr) @@ -671,6 +621,12 @@ local function getTagFunc(tagstr) if(not func) then local format, numTags = tagstr:gsub('%%', '%%%%'):gsub(_PATTERN, '%%s') local args = {} + local idx = 1 + + local format_ = {} + for i = 1, numTags do + format_[i] = '%s' + end for bracket in tagstr:gmatch(_PATTERN) do local tagFunc = funcPool[bracket] or tags[bracket:sub(2, -2)] @@ -678,14 +634,14 @@ local function getTagFunc(tagstr) local tagName, prefixEnd, suffixStart, suffixEnd, customArgs = getBracketData(bracket) local tag = tags[tagName] if(tag) then - if(prefixEnd ~= 1 and suffixStart - suffixEnd ~= 1) then - local prefix = bracket:sub(2, prefixEnd) - local suffix = bracket:sub(suffixStart, suffixEnd) + if(prefixEnd ~= 1 or suffixStart - suffixEnd ~= 1) then + local prefix = prefixEnd ~= 1 and bracket:sub(2, prefixEnd) or '' + local suffix = suffixStart - suffixEnd ~= 1 and bracket:sub(suffixStart, suffixEnd) or '' tagFunc = function(unit, realUnit) local str if(customArgs) then - str = tag(unit, realUnit, strsplit(',', customArgs)) + str = tag(unit, realUnit, string.split(',', customArgs)) else str = tag(unit, realUnit) end @@ -694,41 +650,11 @@ local function getTagFunc(tagstr) return prefix .. str .. suffix end end - elseif(prefixEnd ~= 1) then - local prefix = bracket:sub(2, prefixEnd) - - tagFunc = function(unit, realUnit) - local str - if(customArgs) then - str = tag(unit, realUnit, strsplit(',', customArgs)) - else - str = tag(unit, realUnit) - end - - if(str and str ~= '') then - return prefix .. str - end - end - elseif(suffixStart - suffixEnd ~= 1) then - local suffix = bracket:sub(suffixStart, suffixEnd) - - tagFunc = function(unit, realUnit) - local str - if(customArgs) then - str = tag(unit, realUnit, strsplit(',', customArgs)) - else - str = tag(unit, realUnit) - end - - if(str and str ~= '') then - return str .. suffix - end - end else tagFunc = function(unit, realUnit) local str if(customArgs) then - str = tag(unit, realUnit, strsplit(',', customArgs)) + str = tag(unit, realUnit, string.split(',', customArgs)) else str = tag(unit, realUnit) end @@ -745,8 +671,16 @@ local function getTagFunc(tagstr) if(tagFunc) then table.insert(args, tagFunc) + + idx = idx + 1 else - return error(string.format('Attempted to use invalid tag %s.', bracket), 3) + nierror(string.format('Attempted to use invalid tag %s.', bracket)) + + format_[idx] = bracket + format = format:format(unpack(format_, 1, numTags)) + format_[idx] = '%s' + + numTags = numTags - 1 end end @@ -960,4 +894,4 @@ oUF.Tags = { oUF:RegisterMetaFunction('Tag', Tag) oUF:RegisterMetaFunction('Untag', Untag) -oUF:RegisterMetaFunction('UpdateTags', Update) +oUF:RegisterMetaFunction('UpdateTags', Update) \ No newline at end of file diff --git a/oUF/elements/threatindicator.lua b/oUF/elements/threatindicator.lua index 75d7331..6eb6fd3 100644 --- a/oUF/elements/threatindicator.lua +++ b/oUF/elements/threatindicator.lua @@ -34,6 +34,20 @@ local Private = oUF.Private local unitExists = Private.unitExists +if not GetThreatStatusColor then + function GetThreatStatusColor(status) + if status == 3 then + return 1, 0, 0 + elseif status == 2 then + return 1, .6, 0 + elseif status == 1 then + return 1, 1, .47 + else + return .69, .69, .69 + end + end +end + local function Update(self, event, unit) if(unit ~= self.unit) then return end @@ -61,7 +75,7 @@ local function Update(self, event, unit) local r, g, b if(status and status > 0) then - r, g, b = unpack(self.colors.threat[status]) + r, g, b = GetThreatStatusColor(status) if(element.SetVertexColor) then element:SetVertexColor(r, g, b) diff --git a/oUF/events.lua b/oUF/events.lua index 9ebdf17..98db72a 100644 --- a/oUF/events.lua +++ b/oUF/events.lua @@ -4,7 +4,6 @@ local Private = oUF.Private local argcheck = Private.argcheck local error = Private.error -local validateEvent = Private.validateEvent local validateUnit = Private.validateUnit local frame_metatable = Private.frame_metatable @@ -17,12 +16,6 @@ local isEventRegistered = frame_metatable.__index.IsEventRegistered -- to update unit frames correctly, some events need to be registered for -- a specific combination of primary and secondary units local secondaryUnits = { - UNIT_ENTERED_VEHICLE = { - pet = 'player', - }, - UNIT_EXITED_VEHICLE = { - pet = 'player', - }, UNIT_PET = { pet = 'player', }, @@ -105,8 +98,8 @@ function frame_metatable.__index:RegisterEvent(event, func, unitless) argcheck(func, 3, 'function') local curev = self[event] + local kind = type(curev) if(curev) then - local kind = type(curev) if(kind == 'function' and curev ~= func) then self[event] = setmetatable({curev, func}, event_metatable) elseif(kind == 'table') then @@ -120,12 +113,11 @@ function frame_metatable.__index:RegisterEvent(event, func, unitless) if(unitless or self.__eventless) then -- re-register the event in case we have mixed registration registerEvent(self, event) - if(self.unitEvents) then self.unitEvents[event] = nil end end - elseif(validateEvent(event)) then + else self[event] = func if(not self:GetScript('OnEvent')) then @@ -137,7 +129,6 @@ function frame_metatable.__index:RegisterEvent(event, func, unitless) else self.unitEvents = self.unitEvents or {} self.unitEvents[event] = true - -- UpdateUnits will take care of unit event registration for header -- units in case we don't have a valid unit yet local unit1, unit2 = self.unit @@ -187,4 +178,4 @@ function frame_metatable.__index:UnregisterEvent(event, func) unregisterEvent(self, event) end -end +end \ No newline at end of file diff --git a/oUF/factory.lua b/oUF/factory.lua index 59b73e9..f862eba 100644 --- a/oUF/factory.lua +++ b/oUF/factory.lua @@ -4,68 +4,45 @@ local Private = oUF.Private local argcheck = Private.argcheck -local queue = {} -local factory = CreateFrame('Frame') -factory:SetScript('OnEvent', function(self, event, ...) +local _QUEUE = {} +local _FACTORY = CreateFrame'Frame' +_FACTORY:SetScript('OnEvent', function(self, event, ...) return self[event](self, event, ...) end) -factory:RegisterEvent('PLAYER_LOGIN') -factory.active = true +_FACTORY:RegisterEvent'PLAYER_LOGIN' +_FACTORY.active = true -function factory:PLAYER_LOGIN() +function _FACTORY:PLAYER_LOGIN() if(not self.active) then return end - for _, func in next, queue do + for _, func in next, _QUEUE do func(oUF) end -- Avoid creating dupes. - wipe(queue) + wipe(_QUEUE) end ---[[ Factory: oUF:Factory(func) -Used to call a function directly if the current character is logged in and the factory is active. Else the function is -queued up to be executed at a later time (upon PLAYER_LOGIN by default). - -* self - the global oUF object -* func - function to be executed or delayed (function) ---]] function oUF:Factory(func) argcheck(func, 2, 'function') -- Call the function directly if we're active and logged in. - if(IsLoggedIn() and factory.active) then + if(IsLoggedIn() and _FACTORY.active) then return func(self) else - table.insert(queue, func) + table.insert(_QUEUE, func) end end ---[[ Factory: oUF:EnableFactory() -Used to enable the factory. - -* self - the global oUF object ---]] function oUF:EnableFactory() - factory.active = true + _FACTORY.active = true end ---[[ Factory: oUF:DisableFactory() -Used to disable the factory. - -* self - the global oUF object ---]] function oUF:DisableFactory() - factory.active = nil + _FACTORY.active = nil end ---[[ Factory: oUF:RunFactoryQueue() -Used to try to execute queued up functions. The current player must be logged in and the factory must be active for -this to succeed. - -* self - the global oUF object ---]] function oUF:RunFactoryQueue() - factory:PLAYER_LOGIN() -end + _FACTORY:PLAYER_LOGIN() +end \ No newline at end of file diff --git a/oUF/finalize.lua b/oUF/finalize.lua index 9355d19..dec0bc1 100644 --- a/oUF/finalize.lua +++ b/oUF/finalize.lua @@ -1,4 +1,4 @@ local parent, ns = ... -- It's named Private for a reason! -ns.oUF.Private = nil +ns.oUF.Private = nil \ No newline at end of file diff --git a/oUF/init.lua b/oUF/init.lua index 2e2672f..dabbd5d 100644 --- a/oUF/init.lua +++ b/oUF/init.lua @@ -1,8 +1,3 @@ local parent, ns = ... ns.oUF = {} -ns.oUF.Private = {} - -ns.oUF.isRetail = WOW_PROJECT_ID == WOW_PROJECT_MAINLINE -ns.oUF.isClassic = WOW_PROJECT_ID == WOW_PROJECT_CLASSIC -ns.oUF.isTBC = WOW_PROJECT_ID == WOW_PROJECT_BURNING_CRUSADE_CLASSIC -ns.oUF.isWotLK = false +ns.oUF.Private = {} \ No newline at end of file diff --git a/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.lua b/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.lua index 21bf729..2a74cc1 100644 --- a/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.lua +++ b/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.lua @@ -723,25 +723,35 @@ end --[[ What the different callbacks do: + AuraHandler: Specific aura tracking needed for this class, who has Beacon up on them and such + ResetChargeData: Due to spell "queuing" you can't always rely on aura data for buffs that last one or two casts, for example Divine Favor (+100% crit, one spell) if you cast Holy Light and queue Flash of Light the library would still see they have Divine Favor and give them crits on both spells. The reset means that the flag that indicates they have the aura can be killed and if they interrupt the cast then it will call this and let you reset the flags. + What happens in terms of what the client thinks and what actually is, is something like this: + UNIT_SPELLCAST_START, Holy Light -> Divine Favor up UNIT_SPELLCAST_SUCCEEDED, Holy Light -> Divine Favor up (But it was really used) UNIT_SPELLCAST_START, Flash of Light -> Divine Favor up (It's not actually up but auras didn't update) UNIT_AURA -> Divine Favor up (Split second where it still thinks it's up) UNIT_AURA -> Divine Favor faded (Client catches up and realizes it's down) + CalculateHealing: Calculates the healing value, does all the formula calculations talent modifiers and such + CalculateHotHealing: Used specifically for calculating the heals of hots + GetHealTargets: Who the heal is going to hit, used for setting extra targets for Beacon of Light + Paladin heal or Prayer of Healing. The returns should either be: + "compressedGUID1,compressedGUID2,compressedGUID3,compressedGUID4", healthAmount Or if you need to set specific healing values for one GUID it should be "compressedGUID1,healthAmount1,compressedGUID2,healAmount2,compressedGUID3,healAmount3", -1 + The latter is for cases like Glyph of Healing Wave where you need a heal for 1,000 on A and a heal for 200 on the player for B without sending 2 events. The -1 tells the library to look in the GUId list for the heal amounts + **NOTE** Any GUID returned from GetHealTargets must be compressed through a call to compressGUID[guid] ]] @@ -757,7 +767,7 @@ local function getBaseHealAmount(spellData, spellName, spellID, spellRank) if type(average) == "number" then return average end - local requiresLevel = spellData.levels[spellRank] + local requiresLevel = spellData.levels[spellRank] or spellData.levels[1] -- needs review return average[min(playerLevel - requiresLevel + 1, #average)] end @@ -1133,8 +1143,7 @@ if( playerClass == "PRIEST" ) then hotData[Renew] = {coeff = 1, interval = 3, ticks = 5, levels = {8, 14, 20, 26, 32, 38, 44, 50, 56, 60, 65, 70}, averages = { 45, 100, 175, 245, 315, 400, 510, 650, 810, 970, 1010, 1110 }} hotData[GreaterHealHot] = hotData[Renew] - - if Renewal then + if isTBC then -- prevent error on Classic Era realms hotData[Renewal] = {coeff = 0, interval = 3, ticks = 3, levels = {70}, averages = {150}} end @@ -2178,7 +2187,7 @@ function HealComm:COMBAT_LOG_EVENT_UNFILTERED(...) updateRecord(pending, destGUID, amount, stack, endTime, ticksLeft) - if( pending.isMultiTarget ) then + if( pending.isMultiTarget ) and sourceGUID then bucketHeals[sourceGUID] = bucketHeals[sourceGUID] or {} bucketHeals[sourceGUID][spellID] = bucketHeals[sourceGUID][spellID] or {} diff --git a/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.xml b/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.xml index 730fcb3..3f70d20 100644 --- a/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.xml +++ b/oUF/libs/LibHealComm-4.0/LibHealComm-4.0.xml @@ -1,5 +1,4 @@ -<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ -..\FrameXML\UI.xsd"> +<Ui xmlns="http://www.blizzard.com/wow/ui/"> <Script file="ChatThrottleLib.lua"/> <Script file="LibHealComm-4.0.lua"/> </Ui> \ No newline at end of file diff --git a/oUF/oUF.toc b/oUF/oUF.toc index a9336dd..822f8b7 100644 --- a/oUF/oUF.toc +++ b/oUF/oUF.toc @@ -19,6 +19,7 @@ init.lua private.lua ouf.lua events.lua +combatevents.lua factory.lua blizzard.lua units.lua diff --git a/oUF/oUF.xml b/oUF/oUF.xml index 5e8faf2..043ccc6 100644 --- a/oUF/oUF.xml +++ b/oUF/oUF.xml @@ -3,84 +3,39 @@ <Script file="private.lua"/> <Script file="ouf.lua"/> <Script file="events.lua"/> + <Script file="combatevents.lua"/> <Script file="factory.lua"/> <Script file="blizzard.lua"/> <Script file="units.lua"/> <Script file="colors.lua"/> + <Script file='Plugins\RaidDebuffs.lua' /> + <Script file="elements\additionalpower.lua"/> - <Script file="elements\alternativepower.lua"/> <Script file="elements\assistantindicator.lua"/> <Script file="elements\auras.lua"/> <Script file="elements\castbar.lua"/> <Script file="elements\classpower.lua"/> <Script file="elements\combatindicator.lua"/> - <Script file="elements\grouproleindicator.lua"/> + <Script file="elements\healprediction.lua"/> <Script file="elements\health.lua"/> - <Script file="elements\healthprediction.lua"/> <Script file="elements\leaderindicator.lua"/> + <Script file="elements\masterlooterindicator.lua"/> <Script file="elements\phaseindicator.lua"/> <Script file="elements\portrait.lua"/> <Script file="elements\power.lua"/> - <Script file="elements\powerprediction.lua"/> - <Script file="elements\pvpclassificationindicator.lua"/> - <Script file="elements\pvpindicator.lua"/> - <Script file="elements\questindicator.lua"/> <Script file="elements\raidroleindicator.lua"/> <Script file="elements\raidtargetindicator.lua"/> <Script file="elements\range.lua"/> <Script file="elements\readycheckindicator.lua"/> <Script file="elements\restingindicator.lua"/> <Script file="elements\resurrectindicator.lua"/> - <Script file="elements\runes.lua"/> - <Script file="elements\stagger.lua"/> - <Script file="elements\summonindicator.lua"/> <Script file="elements\tags.lua"/> <Script file="elements\threatindicator.lua"/> - <Script file="elements\totems.lua"/> - - <Script file="finalize.lua"/> - - <!-- - Sub-object as a child of the parent unit frame: - <Button name="oUF_HeaderTargetTemplate" inherits="SecureUnitButtonTemplate" virtual="true"> - <Frames> - <Button name="$parentTarget" inherits="SecureUnitButtonTemplate"> - <Attributes> - <Attribute name="unitsuffix" type="string" value="target"/> - <Attribute name="useparent-unit" type="boolean" value="true"/> - </Attributes> - </Button> - </Frames> - </Button> - Separate unit template example: - <Button name="oUF_HeaderSeparateSubOjectsTemplate" inherits="SecureUnitButtonTemplate" virtual="true"> - <Attributes> - <Attribute name="oUF-onlyProcessChildren" type="boolean" value="true"/> - </Attributes> + <Script file='Plugins\FloatingCombatFeedback.lua' /> + <Script file='Plugins\Swing.lua' /> + <Script file='Plugins\EnergyManaRegen.lua' /> - <Frames> - <Button name="$parentUnit" inherits="SecureUnitButtonTemplate"> - <Attributes> - <Attribute name="useparent-unit" type="boolean" value="true"/> - </Attributes> - </Button> - - <Button name="$parentPet" inherits="SecureUnitButtonTemplate"> - <Attributes> - <Attribute name="unitsuffix" type="string" value="pet"/> - <Attribute name="useparent-unit" type="boolean" value="true"/> - </Attributes> - </Button> - - <Button name="$parentTarget" inherits="SecureUnitButtonTemplate"> - <Attributes> - <Attribute name="unitsuffix" type="string" value="target"/> - <Attribute name="useparent-unit" type="boolean" value="true"/> - </Attributes> - </Button> - </Frames> - </Button> - --> -</Ui> + <Script file="finalize.lua"/> +</Ui> \ No newline at end of file diff --git a/oUF/ouf.lua b/oUF/ouf.lua index 91c5e55..9342a92 100644 --- a/oUF/ouf.lua +++ b/oUF/ouf.lua @@ -24,10 +24,6 @@ PetBattleFrameHider:SetAllPoints() PetBattleFrameHider:SetFrameStrata('LOW') RegisterStateDriver(PetBattleFrameHider, 'visibility', '[petbattle] hide; show') -oUF.Retail = WOW_PROJECT_ID == WOW_PROJECT_MAINLINE -oUF.BCC = WOW_PROJECT_ID == WOW_PROJECT_BURNING_CRUSADE_CLASSIC -oUF.Classic = WOW_PROJECT_ID == WOW_PROJECT_CLASSIC - -- updating of "invalid" units. local function enableTargetUpdate(object) object.onUpdateFrequency = object.onUpdateFrequency or .5 @@ -58,10 +54,6 @@ local function updateActiveUnit(self, event, unit) realUnit = 'target' end - if(modUnit == 'pet' and realUnit ~= 'pet') then - modUnit = 'vehicle' - end - if(not unitExists(modUnit)) then return end -- Change the active unit and run a full update. @@ -136,12 +128,10 @@ for k, v in next, { if(not enabled) then return end local update = elements[name].update - if(update) then - for k, func in next, self.__elements do - if(func == update) then - table.remove(self.__elements, k) - break - end + for k, func in next, self.__elements do + if(func == update) then + table.remove(self.__elements, k) + break end end @@ -287,9 +277,6 @@ local function initObject(unit, style, styleFunc, header, ...) end if(not (suffix == 'target' or objectUnit and objectUnit:match('target'))) then - object:RegisterEvent('UNIT_ENTERED_VEHICLE', updateActiveUnit) - object:RegisterEvent('UNIT_EXITED_VEHICLE', updateActiveUnit) - -- We don't need to register UNIT_PET for the player unit. We register it -- mainly because UNIT_EXITED_VEHICLE and UNIT_ENTERED_VEHICLE doesn't always -- have pet information when they fire for party and raid units. @@ -303,11 +290,6 @@ local function initObject(unit, style, styleFunc, header, ...) object:SetAttribute('*type1', 'target') object:SetAttribute('*type2', 'togglemenu') - -- No need to enable this for *target frames. - if(not (unit:match('target') or suffix == 'target')) then - object:SetAttribute('toggleForVehicle', true) - end - -- Other boss and target units are handled by :HandleUnit(). if(suffix == 'target') then enableTargetUpdate(object) @@ -646,7 +628,7 @@ do local name = overrideName or generateName(nil, ...) local header = CreateFrame('Frame', name, PetBattleFrameHider, template) - header:SetAttribute('template', 'SecureUnitButtonTemplate, SecureHandlerStateTemplate, SecureHandlerEnterLeaveTemplate') + header:SetAttribute('template', 'SecureUnitButtonTemplate, SecureHandlerStateTemplate, SecureHandlerEnterLeaveTemplate, SecureHandlerShowHideTemplate, SecureHandlerMouseUpDownTemplate') for i = 1, select('#', ...), 2 do local att, val = select(i, ...) if(not att) then break end @@ -662,35 +644,6 @@ do -- We set it here so layouts can't directly override it. header:SetAttribute('initialConfigFunction', initialConfigFunction) - header:SetAttribute('_initialAttributeNames', '_onenter,_onleave,refreshUnitChange,_onstate-vehicleui') - header:SetAttribute('_initialAttribute-_onenter', [[ - local snippet = self:GetAttribute('clickcast_onenter') - if(snippet) then - self:Run(snippet) - end - ]]) - header:SetAttribute('_initialAttribute-_onleave', [[ - local snippet = self:GetAttribute('clickcast_onleave') - if(snippet) then - self:Run(snippet) - end - ]]) - header:SetAttribute('_initialAttribute-refreshUnitChange', [[ - local unit = self:GetAttribute('unit') - if(unit) then - RegisterStateDriver(self, 'vehicleui', '[@' .. unit .. ',unithasvehicleui]vehicle; novehicle') - else - UnregisterStateDriver(self, 'vehicleui') - end - ]]) - header:SetAttribute('_initialAttribute-_onstate-vehicleui', [[ - local unit = self:GetAttribute('unit') - if(newstate == 'vehicle' and unit and UnitPlayerOrPetInRaid(unit) and not UnitTargetsVehicleInRaidUI(unit)) then - self:SetAttribute('toggleForVehicle', false) - else - self:SetAttribute('toggleForVehicle', true) - end - ]]) header:SetAttribute('oUF-headerType', isPetHeader and 'pet' or 'group') if(Clique) then @@ -729,7 +682,7 @@ oUF implements some of its own attributes. These can be supplied by the layout, * oUF-enableArenaPrep - can be used to toggle arena prep support. Defaults to true (boolean) --]] -function oUF:Spawn(unit, overrideName) +function oUF:Spawn(unit, overrideName, noHandle) argcheck(unit, 2, 'string') if(not style) then return error('Unable to create frame. No styles have been registered.') end @@ -739,7 +692,7 @@ function oUF:Spawn(unit, overrideName) local object = CreateFrame('Button', name, PetBattleFrameHider, 'SecureUnitButtonTemplate') Private.UpdateUnits(object, unit) - self:DisableBlizzard(unit) + if not noHandle then self:DisableBlizzard(unit) end walkObject(object, unit) object:SetAttribute('unit', unit) @@ -813,8 +766,6 @@ function oUF:SpawnNamePlates(namePrefix, nameplateCallback, nameplateCVars) elseif(event == 'NAME_PLATE_UNIT_ADDED' and unit) then local nameplate = C_NamePlate.GetNamePlateForUnit(unit) if(not nameplate) then return end - - local widgets = UnitWidgetSet and UnitWidgetSet(unit) if(not nameplate.unitFrame) then nameplate.style = style @@ -822,10 +773,6 @@ function oUF:SpawnNamePlates(namePrefix, nameplateCallback, nameplateCVars) nameplate.unitFrame = CreateFrame('Button', prefix..nameplate:GetName(), nameplate) nameplate.unitFrame:EnableMouse(false) nameplate.unitFrame.isNamePlate = true - - if nameplate.UnitFrame.WidgetContainer then - nameplate.UnitFrame.WidgetContainer:SetParent(WorldFrame) - end Private.UpdateUnits(nameplate.unitFrame, unit) @@ -833,12 +780,6 @@ function oUF:SpawnNamePlates(namePrefix, nameplateCallback, nameplateCVars) else Private.UpdateUnits(nameplate.unitFrame, unit) end - - if widgets then - nameplate.unitFrame:SetAlpha(0) - else - nameplate.unitFrame:SetAlpha(1) - end nameplate.unitFrame:SetAttribute('unit', unit) @@ -867,15 +808,15 @@ Used to register an element with oUF. * self - the global oUF object * name - unique name of the element (string) -* update - used to update the element (function) -* enable - used to enable the element for a given unit frame and unit (function) -* disable - used to disable the element for a given unit frame (function) +* update - used to update the element (function?) +* enable - used to enable the element for a given unit frame and unit (function?) +* disable - used to disable the element for a given unit frame (function?) --]] function oUF:AddElement(name, update, enable, disable) argcheck(name, 2, 'string') argcheck(update, 3, 'function', 'nil') - argcheck(enable, 4, 'function') - argcheck(disable, 5, 'function') + argcheck(enable, 4, 'function', 'nil') + argcheck(disable, 5, 'function', 'nil') if(elements[name]) then return error('Element [%s] is already registered.', name) end elements[name] = { diff --git a/oUF/private.lua b/oUF/private.lua index b18a0e8..9d31559 100644 --- a/oUF/private.lua +++ b/oUF/private.lua @@ -21,6 +21,10 @@ function Private.error(...) Private.print('|cffff0000Error:|r ' .. string.format(...)) end +function Private.nierror(...) + return geterrorhandler()(...) +end + function Private.unitExists(unit) return unit and (UnitExists(unit) or ShowBossFrameWhenUninteractable(unit)) end @@ -62,19 +66,15 @@ function Private.unitSelectionType(unit, considerHostile) end end -local function errorHandler(...) - return geterrorhandler()(...) -end - function Private.xpcall(func, ...) - return xpcall(func, errorHandler, ...) + return xpcall(func, Private.nierror, ...) end function Private.validateEvent(event) - local isOK = xpcall(validator.RegisterEvent, errorHandler, validator, event) + local isOK = xpcall(validator.RegisterEvent, Private.nierror, validator, event) if(isOK) then validator:UnregisterEvent(event) end return isOK -end +end \ No newline at end of file diff --git a/oUF/units.lua b/oUF/units.lua index ebcf9d0..517090f 100644 --- a/oUF/units.lua +++ b/oUF/units.lua @@ -4,168 +4,6 @@ local Private = oUF.Private local enableTargetUpdate = Private.enableTargetUpdate -local function updateArenaPreparationElements(self, event, elementName, specID) - if not oUF.Retail then - return - end - - local element = self[elementName] - if(element and self:IsElementEnabled(elementName)) then - if(element.OverrideArenaPreparation) then - --[[ Override: Health.OverrideArenaPreparation(self, event, specID) - Used to completely override the internal update function for arena preparation. - - * self - the parent object - * event - the event triggering the update (string) - * specID - the specialization ID for the opponent (number) - --]] - --[[ Override: Power.OverrideArenaPreparation(self, event, specID) - Used to completely override the internal update function for arena preparation. - - * self - the parent object - * event - the event triggering the update (string) - * specID - the specialization ID for the opponent (number) - --]] - element.OverrideArenaPreparation(self, event, specID) - return - end - - element:SetMinMaxValues(0, 1) - element:SetValue(1) - if(element.UpdateColorArenaPreparation) then - --[[ Override: Health:UpdateColor(specID) - Used to completely override the internal function for updating the widget's colors - during arena preparation. - - * self - the Health element - * specID - the specialization ID for the opponent (number) - --]] - --[[ Override: Power:UpdateColor(specID) - Used to completely override the internal function for updating the widget's colors - during arena preparation. - - * self - the Power element - * specID - the specialization ID for the opponent (number) - --]] - element:UpdateColorArenaPreparation(specID) - else - -- this section just replicates the color options available to the Health and Power elements - local r, g, b, t, _ - -- if(element.colorPower and elementName == 'Power') then - -- FIXME: no idea if we can get power type here without the unit - if(element.colorClass) then - local _, _, _, _, _, class = GetSpecializationInfoByID(specID) - t = self.colors.class[class] - elseif(element.colorReaction) then - t = self.colors.reaction[2] - elseif(element.colorSmooth) then - _, _, _, _, _, _, r, g, b = unpack(element.smoothGradient or self.colors.smooth) - elseif(element.colorHealth and elementName == 'Health') then - t = self.colors.health - end - - if(t) then - r, g, b = t[1], t[2], t[3] - end - - if(r or g or b) then - element:SetStatusBarColor(r, g, b) - - local bg = element.bg - if(bg) then - local mu = bg.multiplier or 1 - bg:SetVertexColor(r * mu, g * mu, b * mu) - end - end - end - - if(element.PostUpdateArenaPreparation) then - --[[ Callback: Health:PostUpdateArenaPreparation(event, specID) - Called after the element has been updated during arena preparation. - - * self - the Health element - * event - the event triggering the update (string) - * specID - the specialization ID for the opponent (number) - --]] - --[[ Callback: Power:PostUpdateArenaPreparation(event, specID) - Called after the element has been updated during arena preparation. - - * self - the Power element - * event - the event triggering the update (string) - * specID - the specialization ID for the opponent (number) - --]] - element:PostUpdateArenaPreparation(event, specID) - end - end -end - -local function updateArenaPreparation(self, event) - if(not self:GetAttribute('oUF-enableArenaPrep')) then - return - end - - if(event == 'ARENA_OPPONENT_UPDATE' and not self:IsEnabled()) then - self:Enable() - self:UpdateAllElements('ArenaPreparation') - self:UnregisterEvent(event, updateArenaPreparation) - - -- show elements that don't handle their own visibility - if(self:IsElementEnabled('Auras')) then - if(self.Auras) then self.Auras:Show() end - if(self.Buffs) then self.Buffs:Show() end - if(self.Debuffs) then self.Debuffs:Show() end - end - - if(self.Portrait and self:IsElementEnabled('Portrait')) then - self.Portrait:Show() - end - elseif(event == 'PLAYER_ENTERING_WORLD' and not UnitExists(self.unit)) then - -- semi-recursive call for when the player zones into an arena - updateArenaPreparation(self, 'ARENA_PREP_OPPONENT_SPECIALIZATIONS') - elseif(event == 'ARENA_PREP_OPPONENT_SPECIALIZATIONS') then - if(self.PreUpdate) then - self:PreUpdate(event) - end - - local id = tonumber(self.id) - if(not self:IsEnabled() and GetNumArenaOpponentSpecs() < id) then - -- hide the object if the opponent leaves - self:Hide() - end - - local specID = GetArenaOpponentSpec(id) - if(specID) then - if(self:IsEnabled()) then - -- disable the unit watch so we can forcefully show the object ourselves - self:Disable() - self:RegisterEvent('ARENA_OPPONENT_UPDATE', updateArenaPreparation) - end - - -- update Health and Power (if available) with "fake" data - updateArenaPreparationElements(self, event, 'Health', specID) - updateArenaPreparationElements(self, event, 'Power', specID) - - -- hide all other (relevant) elements (they have no effect during arena prep) - if(self.Auras) then self.Auras:Hide() end - if(self.Buffs) then self.Buffs:Hide() end - if(self.Debuffs) then self.Debuffs:Hide() end - if(self.Castbar) then self.Castbar:Hide() end - if(self.CombatIndicator) then self.CombatIndicator:Hide() end - if(self.GroupRoleIndicator) then self.GroupRoleIndicator:Hide() end - if(self.Portrait) then self.Portrait:Hide() end - if(self.PvPIndicator) then self.PvPIndicator:Hide() end - if(self.RaidTargetIndicator) then self.RaidTargetIndicator:Hide() end - - self:Show() - self:UpdateTags() - end - - if(self.PostUpdate) then - self:PostUpdate(event) - end - end -end - -- Handles unit specific actions. function oUF:HandleUnit(object, unit) local unit = object.unit or unit @@ -175,19 +13,9 @@ function oUF:HandleUnit(object, unit) object:RegisterEvent('UPDATE_MOUSEOVER_UNIT', object.UpdateAllElements, true) elseif(unit == 'focus') then object:RegisterEvent('PLAYER_FOCUS_CHANGED', object.UpdateAllElements, true) - elseif(unit:match('boss%d?$')) then - object:RegisterEvent('INSTANCE_ENCOUNTER_ENGAGE_UNIT', object.UpdateAllElements, true) - object:RegisterEvent('UNIT_TARGETABLE_CHANGED', object.UpdateAllElements) elseif(unit:match('arena%d?$')) then object:RegisterEvent('ARENA_OPPONENT_UPDATE', object.UpdateAllElements, true) - - if oUF.Retail then - object:RegisterEvent('ARENA_PREP_OPPONENT_SPECIALIZATIONS', updateArenaPreparation, true) - object:SetAttribute('oUF-enableArenaPrep', true) - -- the event handler only fires for visible frames, so we have to hook it for arena prep - object:HookScript('OnEvent', updateArenaPreparation) - end - elseif(unit:match('%w+target')) then + elseif(unit and unit:match('%w+target')) then enableTargetUpdate(object) end -end +end \ No newline at end of file diff --git a/oUF_Simple/modules/oUF_EnergyManaRegen.lua b/oUF_Simple/modules/oUF_EnergyManaRegen.lua index bc7d60f..cf43d06 100644 --- a/oUF_Simple/modules/oUF_EnergyManaRegen.lua +++ b/oUF_Simple/modules/oUF_EnergyManaRegen.lua @@ -1,125 +1,117 @@ -local A, L = ... -local oUF = L.oUF or oUF -if not oUF then return end - -local _G = _G -local GetTime = GetTime -local UnitPower = UnitPower -local UnitClass = UnitClass -local tonumber = tonumber -local UnitPowerType = UnitPowerType -local UnitPowerMax = UnitPowerMax -local GetSpellPowerCost = GetSpellPowerCost - +local _, ns = ... +local oUF = ns.oUF or oUF local LastTickTime = GetTime() -local TickDelay = 2.025 -- Average tick time is slightly over 2 seconds +local TickValue = 2 local CurrentValue = UnitPower('player') local LastValue = CurrentValue -local myClass = select(2, UnitClass('player')) +local allowPowerEvent = true +local myClass = select(2, UnitClass("player")) local Mp5Delay = 5 -local Mp5DelayWillEnd = nil local Mp5IgnoredSpells = { - [18182] = true, -- Improved Life Tap 1 - [18183] = true, -- Improved Life Tap 2 - [1454] = true, -- Life Tap 1 - [1455] = true, -- Life Tap 2 - [1456] = true, -- Life Tap 3 - [11687] = true, -- Life Tap 4 - [11688] = true, -- Life Tap 5 - [11689] = true, -- Life Tap 6 + [11689] = true, -- life tap 6 + [11688] = true, -- life tap 5 + [11687] = true, -- life tap 4 + [1456] = true, -- life tap 3 + [1455] = true, -- life tap 2 + [1454] = true, -- life tap 1 + [18182] = true, -- improved life tap 1 + [18183] = true, -- improved life tap 2 +} +local rangeWeaponSpells = { + [75] = true, -- auto shot + [5019] = true, -- shoot } - --- Sets tick time to the last possible time based on the last tick -local UpdateTickTime = function(now) - LastTickTime = now - ((now - LastTickTime) % TickDelay) -end local Update = function(self, elapsed) local element = self.EnergyManaRegen + element.sinceLastUpdate = (element.sinceLastUpdate or 0) + (tonumber(elapsed) or 0) if element.sinceLastUpdate > 0.01 then - local powerType = UnitPowerType('player') + local powerType = UnitPowerType("player") + if powerType ~= Enum.PowerType.Energy and powerType ~= Enum.PowerType.Mana then element.Spark:Hide() return end CurrentValue = UnitPower('player', powerType) - local MaxPower = UnitPowerMax('player', powerType) - local Now = GetTime() - - if powerType == Enum.PowerType.Mana then - if CurrentValue >= MaxPower then - element:SetValue(0) - element.Spark:Hide() - return - end - -- Sync last tick time after 5 seconds are over - if Mp5DelayWillEnd and Mp5DelayWillEnd < Now then - Mp5DelayWillEnd = nil - UpdateTickTime(Now) - end - elseif powerType == Enum.PowerType.Energy then - -- If energy is not full we just wait for the next tick - if Now >= LastTickTime + TickDelay and CurrentValue >= MaxPower then - UpdateTickTime(Now) - end + if powerType == Enum.PowerType.Mana and (not CurrentValue or CurrentValue >= UnitPowerMax('player', Enum.PowerType.Mana)) then + element:SetValue(0) + element.Spark:Hide() + return end - if Mp5DelayWillEnd and powerType == Enum.PowerType.Mana then - -- Show 5 second indicator - element.Spark:Show() - element:SetMinMaxValues(0, Mp5Delay) - element.Spark:SetVertexColor(1, 1, 0, 1) - element:SetValue(Mp5DelayWillEnd - Now) - else - -- Show tick indicator - element.Spark:Show() - element:SetMinMaxValues(0, TickDelay) - element.Spark:SetVertexColor(1, 1, 1, 1) - element:SetValue(Now - LastTickTime) - end + local Now = GetTime() or 0 + if not (Now == nil) then + local Timer = Now - LastTickTime - element.sinceLastUpdate = 0 - end -end + if (CurrentValue > LastValue) or powerType == Enum.PowerType.Energy and (Now >= LastTickTime + 2) then + LastTickTime = Now + end -local OnUnitPowerUpdate = function() - local powerType = UnitPowerType('player') - if powerType ~= Enum.PowerType.Mana and powerType ~= Enum.PowerType.Energy then - return - end + if Timer > 0 then + element.Spark:Show() + element:SetMinMaxValues(0, 2) + element.Spark:SetVertexColor(1, 1, 1, 1) + element:SetValue(Timer) + allowPowerEvent = true + + LastValue = CurrentValue + elseif Timer < 0 then + -- if negative, it's mp5delay + element.Spark:Show() + element:SetMinMaxValues(0, Mp5Delay) + element.Spark:SetVertexColor(1, 1, 0, 1) + + element:SetValue(math.abs(Timer)) + end - -- We also register ticks from mp5 gear within the 5-second-rule to get a more accurate sync later. - -- Unfortunately this registers a tick when a mana pot or life tab is used. - local CurrentValue = UnitPower('player', powerType) - if CurrentValue > LastValue then - LastTickTime = GetTime() + element.sinceLastUpdate = 0 + end end - LastValue = CurrentValue end -local OnUnitSpellcastSucceeded = function(_, _, _, _, spellID) - local powerType = UnitPowerType('player') +local EventHandler = function(self, event, _, _, spellID) + local powerType = UnitPowerType("player") + if powerType ~= Enum.PowerType.Mana then return end - local spellCost = false - local costTable = GetSpellPowerCost(spellID) - for _, costInfo in next, costTable do - if costInfo.cost then - spellCost = true + if event == 'UNIT_POWER_UPDATE' and allowPowerEvent then + local Time = GetTime() + + TickValue = Time - LastTickTime + + if TickValue > 5 then + if powerType == Enum.PowerType.Mana and InCombatLockdown() then + TickValue = 5 + else + TickValue = 2 + end end - end - if not spellCost or Mp5IgnoredSpells[spellID] then - return + LastTickTime = Time end - Mp5DelayWillEnd = GetTime() + 5 + if event == 'UNIT_SPELLCAST_SUCCEEDED' and not rangeWeaponSpells[spellID] then + local spellCost = false + local costTable = GetSpellPowerCost(spellID) + for _, costInfo in next, costTable do + if costInfo.cost then + spellCost = true + end + end + + if (CurrentValue < LastValue) and (not spellCost or Mp5IgnoredSpells[spellID]) then + return + end + + LastTickTime = GetTime() + 5 + allowPowerEvent = false + end end local Path = function(self, ...) @@ -130,7 +122,7 @@ local Enable = function(self, unit) local element = self.EnergyManaRegen local Power = self.Power - if (unit == 'player') and element and Power and myClass ~= 'WARRIOR' then + if (unit == "player") and element and Power and myClass ~= 'WARRIOR' then element.__owner = self if(element:IsObjectType('StatusBar')) then @@ -142,13 +134,15 @@ local Enable = function(self, unit) local spark = element.Spark if(spark and spark:IsObjectType('Texture')) then spark:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]]) - spark:SetSize(5, 5) spark:SetBlendMode('ADD') - spark:SetPoint('CENTER', element:GetStatusBarTexture(), 'RIGHT') + spark:SetPoint("TOPLEFT", element:GetStatusBarTexture(), "TOPRIGHT", -3, 3) + spark:SetPoint("BOTTOMRIGHT", element:GetStatusBarTexture(), "BOTTOMRIGHT", 3, -3) end - self:RegisterEvent('UNIT_SPELLCAST_SUCCEEDED', OnUnitSpellcastSucceeded) - self:RegisterEvent('UNIT_POWER_UPDATE', OnUnitPowerUpdate) + self:RegisterEvent("PLAYER_REGEN_ENABLED", EventHandler, true) + self:RegisterEvent("PLAYER_REGEN_DISABLED", EventHandler, true) + self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED", EventHandler) + self:RegisterEvent("UNIT_POWER_UPDATE", EventHandler) element:SetScript('OnUpdate', function(_, elapsed) Path(self, elapsed) end) @@ -161,14 +155,16 @@ local Disable = function(self) local Power = self.Power if (Power) and (element) then - self:UnregisterEvent('UNIT_SPELLCAST_SUCCEEDED', OnUnitSpellcastSucceeded) - self:UnregisterEvent('UNIT_POWER_UPDATE', OnUnitPowerUpdate) + self:UnregisterEvent("PLAYER_REGEN_ENABLED", EventHandler, true) + self:UnregisterEvent("PLAYER_REGEN_DISABLED", EventHandler, true) + self:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED", EventHandler) + self:UnregisterEvent("UNIT_POWER_UPDATE", EventHandler) element.Spark:Hide() - element:SetScript('OnUpdate', nil) + element:SetScript("OnUpdate", nil) return false end end -oUF:AddElement('EnergyManaRegen', Path, Enable, Disable) \ No newline at end of file +oUF:AddElement("EnergyManaRegen", Path, Enable, Disable) \ No newline at end of file diff --git a/oUF_Simple/modules/oUF_Swing.lua b/oUF_Simple/modules/oUF_Swing.lua index 848e088..1c38c9f 100644 --- a/oUF_Simple/modules/oUF_Swing.lua +++ b/oUF_Simple/modules/oUF_Swing.lua @@ -1,10 +1,9 @@ ------------------------- +------------------------- -- oUF_Swing, by p3lim -- NDui MOD ------------------------- -local A, L = ... -local oUF = L.oUF or oUF -if not oUF then return end +local _, ns = ... +local oUF = ns.oUF or oUF local select = select local GetTime = GetTime @@ -22,16 +21,6 @@ local playerGUID = UnitGUID("player") local AUTO_CAST_TIME = .65 local delayTime = 0 -local function Round(number, idp) - idp = idp or 0 - local mult = 10 ^ idp - return floor(number * mult + .5) / mult -end - -local function RoundPercent(percent) - return Round(percent, 2) -end - local function SwingStopped(element) local bar = element.__owner local swing = bar.Twohand @@ -52,7 +41,7 @@ end local function UpdateBarValue(self, value) self:SetValue(value) - if self.Text then + if self.Text and self.Text:IsShown() then if self.__owner.OverrideText then self.__owner.OverrideText(self, value) else @@ -108,8 +97,7 @@ do end end - local spell = UnitCastingInfo("player") - if slam == spell then + if UnitCastingInfo("player") == slam then -- slamelapsed: time to add for one slam slamelapsed = slamelapsed + elapsed -- slamtime: needed for meleeing hack (see some lines above) @@ -212,14 +200,14 @@ local function MeleeChange(self, _, unit) else if ohspeed then if swingMH.speed and swingMH.speed ~= mhspeed then - local percentage = RoundPercent(((swingMH.max or 10) - now) / swingMH.speed) + local percentage = ((swingMH.max or 10) - now) / (swingMH.speed) swingMH.min = now - mhspeed * (1 - percentage) swingMH.max = now + mhspeed * percentage UpdateBarMinMaxValues(swingMH) swingMH.speed = mhspeed end if swingOH.speed and swingOH.speed ~= ohspeed then - local percentage = RoundPercent(((swingOH.max or 10)- now) / swingOH.speed) + local percentage = ((swingOH.max or 10)- now) / (swingOH.speed) swingOH.min = now - ohspeed * (1 - percentage) swingOH.max = now + ohspeed * percentage UpdateBarMinMaxValues(swingOH) @@ -227,7 +215,7 @@ local function MeleeChange(self, _, unit) end else if swing.max and swing.speed ~= mhspeed then - local percentage = RoundPercent((swing.max - now) / swing.speed) + local percentage = (swing.max - now) / (swing.speed) swing.min = now - mhspeed * (1 - percentage) swing.max = now + mhspeed * percentage UpdateBarMinMaxValues(swing) @@ -257,7 +245,7 @@ local function RangedChange(self, _, unit) swing:SetScript("OnUpdate", OnDurationUpdate) else if swing.speed ~= speed then - local percentage = RoundPercent((swing.max - now) / swing.speed) + local percentage = (swing.max - now) / (swing.speed) swing.min = now - speed * (1 - percentage) swing.max = now + speed * percentage swing.speed = speed @@ -347,6 +335,14 @@ local function Melee(self, event, _, sourceGUID) lasthit = now end +local function GetHasteMult(speed, now, percentage) + if percentage == 1 then + return 0 + else + return (speed - now) * percentage / (1 - percentage) + end +end + local function ParryHaste(self, ...) local destGUID, _, _, _, missType = select(7, ...) @@ -364,39 +360,39 @@ local function ParryHaste(self, ...) -- needed calculations, so the timer doesnt jump on parryhaste if dualwield then - local percentage = RoundPercent((swingMH.max - now) / swingMH.speed) + local percentage = (swingMH.max - now) / swingMH.speed if percentage > .6 then swingMH.max = now + swingMH.speed * .6 - swingMH.min = now - (swingMH.max - now) * percentage / (1 - percentage) + swingMH.min = now - GetHasteMult(swingMH.max, now, percentage) UpdateBarMinMaxValues(swingMH) elseif percentage > .2 then swingMH.max = now + swingMH.speed * .2 - swingMH.min = now - (swingMH.max - now) * percentage / (1 - percentage) + swingMH.min = now - GetHasteMult(swingMH.max, now, percentage) UpdateBarMinMaxValues(swingMH) end - percentage = RoundPercent((swingOH.max - now) / swingOH.speed) + percentage = (swingOH.max - now) / swingOH.speed if percentage > .6 then swingOH.max = now + swingOH.speed * .6 - swingOH.min = now - (swingOH.max - now) * percentage / (1 - percentage) + swingOH.min = now - GetHasteMult(swingOH.max, now, percentage) UpdateBarMinMaxValues(swingOH) elseif percentage > .2 then swingOH.max = now + swingOH.speed * .2 - swingOH.min = now - (swingOH.max - now) * percentage / (1 - percentage) + swingOH.min = now - GetHasteMult(swingOH.max, now, percentage) UpdateBarMinMaxValues(swingOH) end else - local percentage = RoundPercent((swing.max - now) / swing.speed) + local percentage = (swing.max - now) / swing.speed if percentage > .6 then swing.max = now + swing.speed * .6 - swing.min = now - (swing.max - now) * percentage / (1 - percentage) + swing.min = now - GetHasteMult(swing.max, now, percentage) UpdateBarMinMaxValues(swing) elseif percentage > .2 then swing.max = now + swing.speed * .2 - swing.min = now - (swing.max - now) * percentage / (1 - percentage) + swing.min = now - GetHasteMult(swing.max, now, percentage) UpdateBarMinMaxValues(swing) end end