WeakAuras

Time To Die

  • 15 posts
    #1 Sep 15, 2011 at 07:24 UTC - 0 likes

    Ive been wracking my brain all night trying to figure this out, theres a function in a few addons (namely MageManaBar and TimeToDie) that both do around the same calculations for giving an estimate for when a mob will die , i tried hooking it to Unit_Health to no avail (still new to lua) i even tried to essentially copy mage mana bars functionality just to make combine as much as i can into Weak Auras.

    My ultimate goal in all of this is to have an animation go off when time to oom is >= time to mob death to signify a final burn for arcane mages. But one step at a time for this, just attempting a simple text output of the time to die at the moment. Here is as far as i got, the problem was it was occasioanlly putting in the time but updating so often it defaulted to NIL and i couldn't reliably read it. i did try a few other things but lost the code :(. this was all in a custom text (%c)

    function()
    	local unit = Unitname("target")
    	local health = UnitHealth("target")
    	local timetodie = 0
    	
    	if oldhealth ~= health then
    		oldhealth = health
    		
    		local text
    		
    		if health == UnitHealthMax("target") then 
    			health0, time0, mhealth, mtime = nil
    			text = "INF"
    			return text
    		end
    		
    		local time = GetTime()
    		
    		if not health0 then
    			health0, time0 = health, time
    			mhealth, mtime = health, time
    			return
    		end
    		
    		mhealth = (mhealth + health) * .5
    		mtime = (mtime + time) * .5
    		
    		if mhealth >= health0 then
    			health0, time0, mhealth, mtime = nil
    			text= "healing"
    		else
    			timetodie = health * (time0 - mtime) / (mhealth - health0)
    			if  (timetodie <= 60) then
    				text = format("%0.2d",timetodie)
    			elseif (timetodie>60) then
    				text = format("%d:%0.2d",timetodie / 60, timetodie % 60)
    			end
    		end
    		
    		return text
    	elseif unit == nil then
    		return "nil"
    	end
    end
    

    Any help would be much appreciated, just found this addon this week and it has already gone beyond what i thought one addon could add!

    Last edited Sep 15, 2011 by Hx2600
    #2 Sep 15, 2011 at 19:25 UTC - 0 likes

    I think ive figured out how to make it work with a trigger for status Health on target with a modified lua code from up top as the %c .. could use a little cleanup on the math as its rounding up, and smoothing out the estimated time but for its a good first draft. I think the biggest issue i might have is finding a way for an animation to trigger when one function is > than the other..

    TTD Text (text is %c, this is whats in the editor) as it is now:

    function()
        local health = UnitHealth("target")
        local time = GetTime()
        local text
        local oldhealth
        if oldhealth ~= health then
            oldhealth = health
            if health == UnitHealthMax("target") then -- THiS BLOCK IS GOOD
                health0, time0, mhealth, mtime = nil
                text = " - - "
                return text
            end
            
            if not health0 then -- GOOD
                health0, time0 = health, time
                mhealth, mtime = health, time
                return
            end
            
            mhealth = (mhealth + health) * .5
            mtime = (mtime + time) * .5
            
            if mhealth >= health0 then
                text = " - - "
                health0, time0, mhealth, mtime =nil
            else
                time = health * (time0 - mtime) / (mhealth - health0)
                if (time <= 60) then
                    text = format("%0.2d",time)           
                elseif (time > 60) then
                    text = format("%d:%0.2d",time / 60, time % 60)
                end
                return text
            end
        end
    end
    

    in case anyone wants to fiddle with it:

    dieFhaGivLUeiLrPk1PuvSkLQ4viuzwkvUfs1UuvLFHqvdtvCmqyzQs8mOOPHOUgsP2guPVbsgNQKohuH1bveZdHc3dkTpLIyHif6HqfvtuPQSrKI(OQQYivQQojurzLaEjcfntek1nvvv1ovks)uPWtrYubrxvPkTxL8xKcgSCyiwmcL8yeYKrWLvzZiYNHcJgvNgOzdQBdPDJs)gfgoivlhQ65QQmDfxhfTDLI67QQQmELsNhPK5dvK2p9cIfKlk0ffHffHfKlkcG)GomcTG82olkOr0IIiM)ggOqEBNffjMSenGmyF)raZmdMbriWg)qGhUH12ll4S)dbE4V)oi4X4MVFeWmZGzqecSnGONH1(3nqcIEV)iGzMbZGieyBUTJaMzgmdIqGngWe4hc8WfWmZajzmGjWpe4HBItXA8dbE42WpwbmZmZmJbmb(HapCdRXpe4HlGzMzMzGKm(HapCdlwBVSGZ(pe4H))iBF)DqWJXnF)yd)yn60ni3piLrJe7nW5etdNrz)BJn2NaMzMzMzMz8dbE4p7Sbe9E2zO8dbE47m0be9mSgliSaMzMzMzMzZTDmS2xJUr3(kGzMzMzMzg(ByIN1MB7iGzMzMzhlbbmZmZmtaZmZmZajzSmgJFiWd)Xg(XA0PB)BJn2NaMzMzMzMz8dbE4p7Sbe9EmSg)qGh(oBarpbmZmZmZmZq5hc8W3zOdi6zyn(Hap8D2aIEcyMzMzMzMH)gM4zfWmZmZSJLGaMzMzMjGzMzMzO8dbE4gw7nk)qGhUrCg)qGh(hBp2F0waZmZmZqhq0ZWAVrhq0ZioBarVp2ES)OTaMzMzMjGzMzMzGKmu(HapCBtWA8dbE4p2WpwbmZmZmZmZMB7yyTVgDJU9vaZmZmZmZm(Hap8ND2aIEp7mu(Hap8Dg6aIEgwwqybmZmZm7G)DcyMzMzMzMnGONH14hc8WT9yVhq07XOBOdi69XiE7nk)qGhUr34hc8WF(iGzMzMzMzgij79aIEgXaRH7ZhB4hRaMzMzMzMzMz2CBhdRrIbEuK59xO983le(UBarVpMzMzMzMjGzMzMzMz2b)7ajzVhq0Z2ed3Np2WpwbmZmZmZmZmZS52ogwJed8OiZ7VqJaobAp)9cHV7gq0ZiEd3ND2aIEg0mCF(iGzMzMzMz2XsqaZmZmZmZm83WepRn32raZmZmZowccyMzhlbbowcciGaciGaciGaciGalkMSdEqmW4WVGCrXKfCwudcEmUznlkIy(ByGc5TD2l0jGm3I628XolQbpigyC4xqUOgAb9Br9BqgM)wumzbNf1GGhJBwum)D0atwWzrJlkM)oAa6hEIo2HFiWdFrSwumzVnFSZIcH5WynlQ)dKWWx0ekYq9cUKF5fYqbbzOEEfZfj6KFDrTDdsK(DZIM0PneycfzmFftiG4btiG6vYls0jtErH)WaKblK0c63IAUTZIIFGyWNfnXfhy(6R4(afoGGmeKH6bIfj6KXXIcKWIcsq0Zgg2(aVffT2GePF3SOjDYq9Gju4IluKXbMpyIJNxwKOtM2Rzr9BrbXFK)dZf1MxBkeKFEwZAb
    
    Last edited Sep 15, 2011 by Hx2600
    #3 Sep 22, 2011 at 18:00 UTC - 0 likes

    So after some debugging, here is some code:

    Time To OOM:

    -- Custom text function
    function()
        -- Setup trigger (once)
        if not WA_TTOOM_Windows then
            -- User variables
            WA_TTOOM_Windows = 8             -- Max number of samples
            WA_TTOOM_Steps = 1.5             -- Time between samples
            
            -- Code variables
            WA_TTOOM_MaxValue = 0            -- Remember max HP for relative shift
            WA_TTOOM_Last = GetTime()        -- Remember last update
            WA_TTOOM_Start = nil             -- First data collection time for relative shift
            WA_TTOOM_Index = 0               -- Current ring buffer index
            WA_TTOOM_Times = {}              -- Ring buffer data - data_x
            WA_TTOOM_Values = {}             -- Ring buffer data - data_y
            WA_TTOOM_Samples = 0             -- Number of collected (active) samples
            WA_TTOOM_Estimate = nil          -- Estimated end time (not relative)
        end
        
        -- Query current time (throttle updating over time)
        local now = GetTime()
        if now - WA_TTOOM_Last >= WA_TTOOM_Steps then
            -- Current data
            local data = UnitPower("player")
            
            -- Reset data?
            if data == UnitPowerMax("player") then
                WA_TTOOM_Start = nil
                WA_TTOOM_Estimate = nil
            end
    
    		-- No start time?
    		if not WA_TTOOM_Start then
    			WA_TTOOM_Start = now
    			WA_TTOOM_Index = 0
    			WA_TTOOM_Samples = 0
    			WA_TTOOM_MaxValue = UnitPowerMax("player") / 2
    		end
    		
    		-- Remember current time
    		WA_TTOOM_Last = now
    		
    		-- Save new data (Use relative values to prevent "overflow")
    		WA_TTOOM_Values[WA_TTOOM_Index] = data - WA_TTOOM_MaxValue
    		WA_TTOOM_Times[WA_TTOOM_Index] = now - WA_TTOOM_Start
    		
    		-- Next index
    		WA_TTOOM_Index = WA_TTOOM_Index + 1
    		
    		-- Update number of active samples
    		if WA_TTOOM_Index > WA_TTOOM_Samples then
    			WA_TTOOM_Samples = WA_TTOOM_Index
    		end
    
    		-- Using table as ring buffer        
    		if WA_TTOOM_Index >= WA_TTOOM_Windows then
    			WA_TTOOM_Index = 0
    		end
    
    		-- Min number of samples
    		if WA_TTOOM_Samples >= 2 then
    			-- Estimation variables
    			local SS_xy, SS_xx, x_M, y_M = 0, 0, 0, 0
    			
    			-- Calc pre-solution values
    			for index = 0, WA_TTOOM_Samples-1 do
    				-- Calc mean value 
    				x_M = x_M + WA_TTOOM_Times[index] / WA_TTOOM_Samples
    				y_M = y_M + WA_TTOOM_Values[index] / WA_TTOOM_Samples
    				
    				-- Calc sum of squares
    				SS_xx = SS_xx + WA_TTOOM_Times[index] * WA_TTOOM_Times[index]
    				SS_xy = SS_xy + WA_TTOOM_Times[index] * WA_TTOOM_Values[index]
    			end
    			
    			-- Few last additions to mean value / sum of squares
    			SS_xx = SS_xx - WA_TTOOM_Samples * x_M * x_M
    			SS_xy = SS_xy - WA_TTOOM_Samples * x_M * y_M
    			
    			-- Results
    			local a_0, a_1, x = 0, 0, 0
    			
    			-- Calc a_0, a_1 of linear interpolation (data_y = a_1 * data_x + a_0)
    			a_1 = SS_xy / SS_xx
    			a_0 = y_M - a_1 * x_M
    			
    			-- Find zero-point (Switch back to absolute values)
    			a_0 = a_0 + WA_TTOOM_MaxValue
    			x = - (a_0 / a_1)
    			
    			-- Valid/Usable solution
    			if a_1 and a_1 < 1 and a_0 and a_0 > 0 and x and x > 0 then
    				WA_TTOOM_Estimate = x + WA_TTOOM_Start
    				-- Fallback 
    			else
    				WA_TTOOM_Estimate = nil
    			end
    				
    		-- Not enough data
    		else
    			WA_TTOOM_Estimate = nil
    		end
        end
        
        -- No/False information
        if not WA_TTOOM_Estimate then
            return "<No Information>"
            
    	-- Already over
    	elseif now > WA_TTOOM_Estimate then 
    		return "<Estimated to be OOM>"
    		
    	-- Seconds only
        elseif WA_TTOOM_Estimate - now <= 60 then
            return string.format("%0.2d", WA_TTOOM_Estimate - now)
        
    	-- More than seconds
        else
            return string.format("%d:%0.2d", (WA_TTOOM_Estimate - now) / 60, (WA_TTOOM_Estimate - now) % 60)
        end
    end
    

    Time To Die:

    -- Custom text function
    function()
        -- Setup trigger (once)
        if not WA_TTD_Windows then
            -- User variables
            WA_TTD_Windows = 8             -- Max number of samples
            WA_TTD_Steps = 1.5             -- Time between samples
            
            -- Code variables
            WA_TTD_GUID = nil              -- Remember GUID of mob you are tracking
            WA_TTD_MaxValue = 0            -- Remember max HP for relative shift
            WA_TTD_Last = GetTime()        -- Remember last update
            WA_TTD_Start = nil             -- First data collection time for relative shift
            WA_TTD_Index = 0               -- Current ring buffer index
            WA_TTD_Times = {}              -- Ring buffer data - data_x
            WA_TTD_Values = {}             -- Ring buffer data - data_y
            WA_TTD_Samples = 0             -- Number of collected (active) samples
            WA_TTD_Estimate = nil          -- Estimated end time (not relative)
        end
        
        -- Query current time (throttle updating over time)
        local now = GetTime()
        if now - WA_TTD_Last >= WA_TTD_Steps then
            -- Current data
            local data = UnitHealth("target")
            
            -- Reset data?
            if data == UnitHealthMax("target") or not WA_TTD_GUID or WA_TTD_GUID ~= UnitGUID("target") then
                WA_TTD_GUID = nil
                WA_TTD_Start = nil
                WA_TTD_Estimate = nil
            end
    
    		-- No start time?
    		if not WA_TTD_Start or not WA_TTD_GUID then
    			WA_TTD_Start = now
    			WA_TTD_Index = 0
    			WA_TTD_Samples = 0
    			WA_TTD_MaxValue = UnitHealthMax("target") / 2
    			WA_TTD_GUID = UnitGUID("target")
    		end
    		
    		-- Remember current time
    		WA_TTD_Last = now
    		
    		-- Save new data (Use relative values to prevent "overflow")
    		WA_TTD_Values[WA_TTD_Index] = data - WA_TTD_MaxValue
    		WA_TTD_Times[WA_TTD_Index] = now - WA_TTD_Start
    		
    		-- Next index
    		WA_TTD_Index = WA_TTD_Index + 1
    		
    		-- Update number of active samples
    		if WA_TTD_Index > WA_TTD_Samples then
    			WA_TTD_Samples = WA_TTD_Index
    		end
    
    		-- Using table as ring buffer        
    		if WA_TTD_Index >= WA_TTD_Windows then
    			WA_TTD_Index = 0
    		end
    
    		-- Min number of samples
    		if WA_TTD_Samples >= 2 then
    			-- Estimation variables
    			local SS_xy, SS_xx, x_M, y_M = 0, 0, 0, 0
    			
    			-- Calc pre-solution values
    			for index = 0, WA_TTD_Samples-1 do
    				-- Calc mean value 
    				x_M = x_M + WA_TTD_Times[index] / WA_TTD_Samples
    				y_M = y_M + WA_TTD_Values[index] / WA_TTD_Samples
    				
    				-- Calc sum of squares
    				SS_xx = SS_xx + WA_TTD_Times[index] * WA_TTD_Times[index]
    				SS_xy = SS_xy + WA_TTD_Times[index] * WA_TTD_Values[index]
    			end
    			
    			-- Few last additions to mean value / sum of squares
    			SS_xx = SS_xx - WA_TTD_Samples * x_M * x_M
    			SS_xy = SS_xy - WA_TTD_Samples * x_M * y_M
    			
    			-- Results
    			local a_0, a_1, x = 0, 0, 0
    			
    			-- Calc a_0, a_1 of linear interpolation (data_y = a_1 * data_x + a_0)
    			a_1 = SS_xy / SS_xx
    			a_0 = y_M - a_1 * x_M
    			
    			-- Find zero-point (Switch back to absolute values)
    			a_0 = a_0 + WA_TTD_MaxValue
    			x = - (a_0 / a_1)
    			
    			-- Valid/Usable solution
    			if a_1 and a_1 < 1 and a_0 and a_0 > 0 and x and x > 0 then
    				WA_TTD_Estimate = x + WA_TTD_Start
    				-- Fallback 
    			else
    				WA_TTD_Estimate = nil
    			end
    				
    		-- Not enough data
    		else
    			WA_TTD_Estimate = nil
    		end
        end
        
        -- No/False information
        if not WA_TTD_Estimate then
            return "<No Information>"
            
    	-- Already over
    	elseif now > WA_TTD_Estimate then 
    		return "<Estimated to be dead>"
    		
    	-- Seconds only
        elseif WA_TTD_Estimate - now <= 60 then
            return string.format("%0.2d", WA_TTD_Estimate - now)
        
    	-- More than seconds
        else
            return string.format("%d:%0.2d", (WA_TTD_Estimate - now) / 60, (WA_TTD_Estimate - now) % 60)
        end
    end
    

    Both codes are are to be used as custom texts and contain two settings:

    • WA_TTD/TTOOM_Windows - Which is the maximum number of samples collected to estimate the time of death/oom. Higher numbers mean more accuracy (as more data is collected) but also mean slower processing time. I'd recommend values between 5-40 samples. (Higher number=More accuracy )
    • WA_TTD/TTOOM_Steps - The minimum delay between data collection. This can be used to "stretch" (higher numbers) the time the samples cover, but reduce short time accuracy as data is collected less often. Recommend values between 0.5-5 seconds. (Lower values=More accuracy)
    Last edited Nov 07, 2011 by CommanderSirow
    #4 Oct 31, 2011 at 13:19 UTC - 0 likes

    There were a couple extra ")" in there...

    the line that is: return "<Estimated to be OOM>") and return "<Estimated to be dead>")

    just remove the ) off each one..

    #5 Nov 06, 2011 at 18:48 UTC - 0 likes

    @CommanderSirow: Go

    I really like the look of this but Im struggling to find how to set it up. I tried to look at custom text but theres more boxes/choices and Im not sure which I should be selecting. Can you please tell me which options?

    #6 Nov 07, 2011 at 10:17 UTC - 0 likes

    Enter %c into the text-box, than a custom code box should appear. Copy/Paste code into it (Maybe change the 2 variables after "- - User variables" in the code), but that's really all that's to it.

    Oh and ofc you should add a trigger (and maybe a load-conditions) to the display, eg. use a condition trigger, not mounted, something that activates the display whenever needed.

    Last edited Nov 07, 2011 by CommanderSirow
    #7 Nov 08, 2011 at 21:54 UTC - 0 likes

    @CommanderSirow: Go

    Thanks but Im still having trouble with this. I have created 2 Text Auras, one called Time to Die, and another called Time to OOM. I put %c in the display text box. And then pasted the relevant code into the Custom function box, leaving Update custom text on Every Frame selected. I set load in combat. I have not set triggers because I simply dont know which ones to select. I couldnt see anything I thought was related to it. I know its annoying to get newbie questions like this but would be grateful for some final pointers.

    edit, I changed Time to Die trigger to health, target. and that gets the number but. Is there a way to get it converted into minutes/sounds? a number reading 500+ isnt really that useful unless you are good at maths on the fly. It also doesnt match my Time to Die addon text, not sure which is better, but both use huge numbers of seconds rather than minutes/seconds. Have done a similar thing for mana, using Power -> Type mana

    both texts say No information a lot during combat

    aanndddd the OP wanted to make auras that played off each other i.e. popped up if Mana would run out before boss death. Is this supposed to do that, if so, how? I havent find a way to link to auras together yet like Powauras does

    Last edited Nov 08, 2011 by knowntobe
    #8 Nov 09, 2011 at 10:46 UTC - 0 likes
    Quote:

    Is there a way to get it converted into minutes/sounds? a number reading 500+ isnt really that useful unless you are good at maths on the fly.

    Totally understandable! :)

    Replace:

        -- Seconds only
        elseif WA_TTD_Estimate - now <= 60 then
            return string.format("%0.2d", WA_TTD_Estimate - now)
        
    	-- More than seconds
        else
            return string.format("%d:%0.2d", (WA_TTD_Estimate - now) / 60, (WA_TTD_Estimate - now) % 60)
        end
    

    with:

        -- Convert time to readable format
        else
            local left    = WA_TTD_Estimate - now;
    
            local seconds = left >= 0        and math.floor((left % 60)    / 1   ) or 0;
            local minutes = left >= 60       and math.floor((left % 3600)  / 60  ) or 0;
            local hours   = left >= 3600     and math.floor((left % 86400) / 3600) or 0;
            local days    = left >= 86400    and math.floor((left % 31536000) / 86400) or 0;
            local years   = left >= 31536000 and math.floor( left / 31536000) or 0;
            
            if years > 0 then
                return string.format("%d [Y] %d [D] %d:%d:%d [H]", years, days, hours, minutes, seconds);
            elseif days > 0 then
                return string.format("%d [D] %d:%d:%d [H]", days, hours, minutes, seconds);
            elseif hours > 0 then
                return string.format("%d:%d:%d [H]", hours, minutes, seconds);
            elseif minutes > 0 then
                return string.format("%d:%d [M]", minutes, seconds);
            else
                return string.format("%d [S]", seconds);
            end
        end
    
    Quote:

    It also doesnt match my Time to Die addon text, not sure which is better, but both use huge numbers of seconds rather than minutes/seconds. Have done a similar thing for mana, using Power -> Type mana

    TTD and TTOOM basically use the same base code, that is, they

    • Collect a predefined number of samples (Current time and current health/mana pair) [*1]
    • Use this data to LINEARLY (by linear least squares) approximate the health/mana-over-time curve [*2]
    • Find the (first) zero-point of the approximated curve, which should mark the point to OOM/Death [*3]
    • Translate this zero-point back to a useable time format

    [*1] Most (simple coded) TTOOM/TTD addons just use two values here, one "initial" health/mana value and one that updates over time. The disadvantage is, results can get quite inaccurate the longer the fight takes, this is mainly duo to [*2]. The most robust solution would be to collect an endless amount of samples and weight (give them importance) according of the time since their recording. For simplicity and memory usage, the above code uses a finite number of samples and does not use weights (this is a bit countered by "old" samples being thrown away).

    [*2] The easiest assumption basically every TTD/TTOOM addon makes (my code included) about the health/mana-over-time graph, is that it is linear (in time), eg. that the amount of mana you burst out is constant. This should be true in the long run but is violated at times for example:

    • A mage is using evocation: Mana goes up instead of down, linearity is violated
    • Raid is using heroism: Boss health drops stronger over time, linearity is violated

    Whenever this assumptions is violated, all TTD/TTOOM addons will have wrong/inaccurate values. The problem with the simple ones, is that they will be off-value/wrong the whole time (although the error might be small) while using weighted linear least squares will calculate the "best" (in a certain sense) solution given the data and assumptions (linearity) about the problem.

    [*3] Again, this assumptions (zero-point == TTD/TTOOM) is only true, as long as the linearity assumption isn't violated and (, for my code code) there are enough samples to represent the curve correctly.

    Empirically I only tested the OOM code, which was pretty accurate when bursting out mana and also with little pauses in between (after the data-approximation had recovered from the non-linearity), this SHOULD also hold true for the health code. ;)

    Quote:

    both texts say No information a lot during combat

    Could you elaborate this? Does this happen at the start of combat or even some time during combat? There will generally be a data wipe from the TTD-code when you change targets to prevent transferring data from one target to another. (There is no multi-target support to prevent code-complexity explosion)

    NOTE: I could prevent data from wiping, when changing target to a friendly unit, but adding (PROPER) multi-target support would be really a pita (pain in the ass :P).

    Quote:

    aanndddd the OP wanted to make auras that played off each other i.e. popped up if Mana would run out before boss death. Is this supposed to do that, if so, how? I havent find a way to link to auras together yet like Powauras does

    Simplest solution, assuming both TTD and TTOOM displays are running (you can make them invisible, but they have to be active):

    • Type: Custom, Status (Every frame)
    • Custom show code:
    function()
        if WA_TTD_Estimate and WA_TTOOM_Estimate and WA_TTDOOM_Estimate < WA_TTD_Estimate then
            return true;
        else
            return false;
        end
    end
    
    • Custom hide code:
    function()
        if not WA_TTD_Estimate or not WA_TTOOM_Estimate or WA_TTDOOM_Estimate >= WA_TTD_Estimate then
            return true;
        else
            return false;
        end
    end
    
    Last edited Nov 11, 2011 by CommanderSirow
    #9 Nov 09, 2011 at 11:51 UTC - 0 likes

    If one wants to have a unified trigger:

    • Type: Custom, Status (Every frame)
    • Custom show code:
    -- Custom text function
    function()
        -- Setup trigger (once)
        if not WA_TTMAIN_Windows then
            -- User variables
            WA_TTMAIN_Windows = 50          -- Max number of samples
            WA_TTMAIN_Steps = 1.0          -- Time between samples
            
            -- Main-Code variables
            WA_TTMAIN_Last = 0             -- Remember last update
            
            -- TTOOM variables
            WA_TTOOM_Samples = 0           -- Number of collected (active) samples
            WA_TTOOM_Index = 0             -- Current ring buffer index
            WA_TTOOM_Start = nil           -- First data collection time for relative shift
            WA_TTOOM_MaxValue = 0          -- Remember max HP for relative shift
            WA_TTOOM_Times = {}            -- Ring buffer data - data_x
            WA_TTOOM_Values = {}           -- Ring buffer data - data_y
            WA_TTOOM_Estimate = nil        -- Estimated end time (not relative)
            
            -- TTD variables
            WA_TTD_Samples = 0             -- Number of collected (active) samples
            WA_TTD_Index = 0               -- Current ring buffer index
            WA_TTD_Start = nil             -- First data collection time for relative shift
            WA_TTD_MaxValue = 0            -- Remember max HP for relative shift
            WA_TTD_Times = {}              -- Ring buffer data - data_x
            WA_TTD_Values = {}             -- Ring buffer data - data_y
            WA_TTD_Estimate = nil          -- Estimated end time (not relative)
            WA_TTD_GUID = nil              -- Remember GUID of mob you are tracking
            
            -- Common functions
            WA_TTMAIN_NextIndex = function(index, samples)
                -- Next index
                local newIndex   = index + 1
                local newSamples = samples
                
                -- Update number of active samples
                if newIndex > samples then
                    newSamples = newIndex
                end
    
                -- Using table as ring buffer        
                if newIndex >= WA_TTMAIN_Windows then
                    newIndex = 0
                end
                
                -- Return data
                return newIndex, newSamples
            end
            WA_TTMAIN_CalcEstimate = function(times, values, samples, maxValue, startTime)
                -- Min number of samples
                if samples >= 2 then
                    -- Estimation variables
                    local SS_xy, SS_xx, x_M, y_M = 0, 0, 0, 0
                    
                    -- Calc pre-solution values
                    for index = 0, samples-1 do
                        -- Calc mean value 
                        x_M = x_M + times[index]  / samples
                        y_M = y_M + values[index] / samples
                        
                        -- Calc sum of squares
                        SS_xx = SS_xx + times[index] * times[index]
                        SS_xy = SS_xy + times[index] * values[index]
                    end
                    
                    -- Few last additions to mean value / sum of squares
                    SS_xx = SS_xx - samples * x_M * x_M
                    SS_xy = SS_xy - samples * x_M * y_M
                    
                    -- Results
                    local a_0, a_1, x = 0, 0, 0
                    
                    -- Calc a_0, a_1 of linear interpolation (data_y = a_1 * data_x + a_0)
                    a_1 = SS_xy / SS_xx
                    a_0 = y_M - a_1 * x_M
                    
                    -- Find zero-point (Switch back to absolute values)
                    a_0 = a_0 + maxValue
                    x = - (a_0 / a_1)
                    
                    -- Valid/Usable solution
                    if a_1 and a_1 < 1 and a_0 and a_0 > 0 and x and x > 0 then
                        return x + startTime
                        -- Fallback 
                    else
                        return -1
                    end
                        
                -- Not enough data
                else
                    return nil
                end        
            end
            WA_TTMAIN_GetString = function(estimate)
                -- No estimate available
                if not estimate then
                    return "<No Estimate>"
                    
                -- Convert time to readable format
                elseif estimate < 0 then
                    return "<Not decreasing>"
                else
                    -- Time left
                    local left    = estimate - GetTime();
    
                    -- Decode to format
                    local seconds = left >= 0        and math.floor((left % 60)    / 1   ) or 0;
                    local minutes = left >= 60       and math.floor((left % 3600)  / 60  ) or 0;
                    local hours   = left >= 3600     and math.floor((left % 86400) / 3600) or 0;
                    local days    = left >= 86400    and math.floor((left % 31536000) / 86400) or 0;
                    local years   = left >= 31536000 and math.floor( left / 31536000) or 0;
                    
                    -- Format time
                    if     years   > 0 then
                        return string.format("%d [Y] %d [D] %d:%d:%d [H]", years, days, hours, minutes, seconds);
                    elseif days    > 0 then
                        return string.format("%d [D] %d:%d:%d [H]", days, hours, minutes, seconds);
                    elseif hours   > 0 then
                        return string.format("%d:%d:%d [H]", hours, minutes, seconds);
                    elseif minutes > 0 then
                        return string.format("%d:%d [M]", minutes, seconds);
                    else
                        return string.format("%d [S]", seconds);
                    end
                end
            end
        end
        
        -- Query current time (throttle updating over time)
        local now = GetTime()
        if now - WA_TTMAIN_Last >= WA_TTMAIN_Steps then
            -- Remember current time
            WA_TTMAIN_Last = now
    
            -- Current data
            local dataTTOOM = UnitPower("player", SPELL_POWER_MANA)
            local dataTTD   = UnitHealth("target")
            
            -- Reset data?
            if dataTTOOM == UnitPowerMax("player", SPELL_POWER_MANA) then
                WA_TTOOM_Start    = nil
                WA_TTOOM_Estimate = nil
            end
            -- Reset data?
            if dataTTD == UnitHealthMax("target") or not WA_TTD_GUID or WA_TTD_GUID ~= UnitGUID("target") then
                WA_TTD_GUID       = nil
                WA_TTD_Start      = nil
                WA_TTD_Estimate   = nil
            end
    
            -- No start time?
            if not WA_TTOOM_Start then
                WA_TTOOM_Start    = now
                WA_TTOOM_Index    = 0
                WA_TTOOM_Samples  = 0
                WA_TTOOM_MaxValue = UnitPowerMax("player", SPELL_POWER_MANA) / 2
            end
            -- No start time?
            if not WA_TTD_Start or not WA_TTD_GUID then
                WA_TTD_Start      = now
                WA_TTD_Index      = 0
                WA_TTD_Samples    = 0
                WA_TTD_MaxValue   = UnitHealthMax("target") / 2
                WA_TTD_GUID       = UnitGUID("target")
            end
            
            -- Save new data (Use relative values to prevent "overflow")
            WA_TTOOM_Values[WA_TTOOM_Index] = dataTTOOM - WA_TTOOM_MaxValue
            WA_TTOOM_Times[WA_TTOOM_Index]  = now       - WA_TTOOM_Start
            -- Save new data (Use relative values to prevent "overflow")
            WA_TTD_Values[WA_TTD_Index]     = dataTTD   - WA_TTD_MaxValue
            WA_TTD_Times[WA_TTD_Index]      = now       - WA_TTD_Start
            
            -- Next index
            WA_TTOOM_Index, WA_TTOOM_Samples = WA_TTMAIN_NextIndex(WA_TTOOM_Index, WA_TTOOM_Samples)
            WA_TTD_Index,   WA_TTD_Samples   = WA_TTMAIN_NextIndex(WA_TTD_Index,   WA_TTD_Samples)
    
            -- Calc estimate
            WA_TTOOM_Estimate = WA_TTMAIN_CalcEstimate(WA_TTOOM_Times, WA_TTOOM_Values, WA_TTOOM_Samples, WA_TTOOM_MaxValue, WA_TTOOM_Start)
            WA_TTD_Estimate   = WA_TTMAIN_CalcEstimate(WA_TTD_Times,   WA_TTD_Values,   WA_TTD_Samples,   WA_TTD_MaxValue,   WA_TTD_Start)
        end
        
        -- Always show
        -- return true;
    
        -- OOM before kill
        if WA_TTOOM_Estimate and WA_TTD_Estimate and (WA_TTD_Estimate > WA_TTOOM_Estimate or WA_TTD_Estimate < 0) then
            return true
        else
            return false
        end
    end
    
    • Custom hide code:
    function()
        -- Never hide
        -- return false
    
        -- If if no data or kill before oom
        if not (WA_TTOOM_Estimate and WA_TTD_Estimate and (WA_TTD_Estimate > WA_TTOOM_Estimate or WA_TTD_Estimate < 0)) then
            return true;
        else
            return false;
        end
    end
    
    • (Optional) Custom Text code:
    function()
        if WA_TTMAIN_GetString then 
            -- Display both as aboslute values
            --return string.format("OOM: %s | Death: %s", WA_TTMAIN_GetString(WA_TTOOM_Estimate), WA_TTMAIN_GetString(WA_TTD_Estimate));
    
            -- Display TTD relative to TTOM
            return string.format("OOM: %s (%s)", WA_TTMAIN_GetString(WA_TTOOM_Estimate), WA_TTMAIN_GetString(WA_TTD_Estimate and WA_TTOOM_Estimate and (WA_TTD_Estimate - WA_TTOOM_Estimate)));
        end
    end
    

    (Negative relative death time means not oom before death, positive time means oom before death)

    Last edited Nov 13, 2011 by CommanderSirow
    #10 Nov 10, 2011 at 10:33 UTC - 0 likes

    @CommanderSirow: Go

    Great couple of posts! I will be trying this out tonight With regards to the No Information message, this was during a 5 minute DPS test on a single Heroic training dummy. It happens fairly often, normally in the middle of a cast. I will try to narrow it down a bit more when I test the above code.

    #11 Nov 11, 2011 at 08:47 UTC - 0 likes

    @CommanderSirow: Go I tried this but got the following error

    42x <string>:"return -- Custom text function...":130: bad argument #2 to 'format' (number expected, got no value)
    <string>:"return -- Custom text function...":130: in function <[string "return -- Custom text function..."]:2>
    WeakAuras-1.4.4b\RegionTypes\text.lua:89: in function `UpdateCustomText'
    WeakAuras-1.4.4b\WeakAuras.lua:4785: in function <Interface\AddOns\WeakAuras\WeakAuras.lua:4781>
    nil
    Locals:
    now = 58668.156
    left = 25124.244013111
    seconds = 44
    minutes = 58
    hours = 6
    days = 0
    years = 0
    

    as for the rest, its making feel rather stupid, because I just dont know where Im supposed to put any of it. Remember Im a newbie to Weak Auras, and Im struggling to find where any of this actually goes.

    Last edited Nov 11, 2011 by knowntobe
    #12 Nov 11, 2011 at 09:30 UTC - 0 likes
    Quote:

    I tried this but got the following error

    Whoops, the missing argument for string.format where fixed. :D (Hope I did not add any more error :>). Updated the above code(s). ;)

    Quote:

    as for the rest, its making feel rather stupid, because I just dont know where Im supposed to put any of it. Remember Im a newbie to Weak Auras, and Im struggling to find where any of this actually goes.

    I guess you mainly like to know, where you should add the (Optional) Custom Text code and the Custom hide code ?

    To get the dialog box for custom hide, in the trigger tab section, switch "Hide" from Timed to custom.

    To get the custom text code dialog box, enter "%c" ([C]ustom code) into the text box (on the display tab). ;)

    PS: (Important) One more thing, after you update the code enter

    /script WA_TTMAIN_Windows=nil
    

    into chat! (In order to reset internal code functions)

    Last edited Nov 11, 2011 by CommanderSirow
    #13 Nov 11, 2011 at 09:43 UTC - 0 likes
    Quote from knowntobe: Go

    With regards to the No Information message, this was during a 5 minute DPS test on a single Heroic training dummy. It happens fairly often, normally in the middle of a cast. I will try to narrow it down a bit more when I test the above code.

    Sorry to ask this again, (regarding the old setup with 2 triggers)(wth the new 1-display setup this is a bit more difficult to debug :P) does this happen with the TTOOM or the TTD display?

    Last edited Nov 11, 2011 by CommanderSirow
    #14 Nov 13, 2011 at 18:22 UTC - 0 likes

    @CommanderSirow: Go Im going to give up with this because I cant follow your steps as I dont have what you say I should have. If I try to muddle through it, it breaks. You either have a special version I dont (mines latest from curse) or are assuming I will "get" the stuff you didnt mention. Rather than keep on sounding like a moron, its best for both if us to just admit that all aparts of this addon arent for everyone.
    Overall it is more complicated and less "obvious" than powa auras i.e having to make 2 auras that powa auras can do with 1 is a little "annoying" due to extra time needed. And obviously this side of things (Custom code) is hard work also.
    I do like Weak auras for the stuff powa auras doesnt have, and will keep using it, but will give up on custom code.
    Will delete the time to oom, and Time to Die, and just use other addons for that.

    With regards to the no information, theres no set pattern to it. It can say "no information" in the middle of a cast, or when Im running a few yards, or in between casts. Its not constant and I cant reproduce it in any set way. I just cast spells.
    Happens with both TTD and TTOOM

    #15 Nov 13, 2011 at 22:55 UTC - 0 likes

    I updated the above code to eliminate all syntax error. It should work proper now.

    As for the 2-display stuff. I was only suggesting, if you were interested in debugging (any remaining "no info") issue, it would be easier for me to do this with using the 2 display version and than expand the fix onto the 1-display version (its really the same code as the one display code, but separated for target health and player mana), that was all. ;)

    Quote:

    With regards to the no information, theres no set pattern to it. It can say "no information" in the middle of a cast, or when Im running a few yards, or in between casts. Its not constant and I cant reproduce it in any set way. I just cast spells. Happens with both TTD and TTOOM

    Ah ok, what happens is (at least for mana calculation) is probably, that you gained more mana than you are loosing (at least in regards to to the "sample-window), thus it displays "no info", as you will/would not go oom if you continued this way.

    I updated the above code to increase the sample amount, less likely to have short term movement impact oom/death time, and it will display "Value increasing instead of no-info if gaining more mana/hp than loosing.

    PS: Sad to hear if you really plan to abandon WeakAuras due to this *sadface*

    WA really was designed to be both easy to use, but also flexible while still maintaining computational efficiency (the last part isn't completely true for PowerAuras in some parts, at least the last time I looked through the code). The flexibility part can sometimes lead to some/great complexity, as one can utilize the full power of lua + wow-api.

    This code will probably though never make its way into WeakAuras as a predefined trigger because of its OnUpdate nature. Basically it means, the trigger needs to be evaluated at every frame (eg. 60 times per second for an average computer), which is something that is tried to be avoided in WA (for performance/efficiency reasons)

    The best/most user friendly solution (while still being somewhat conform to WA design philosophies) is to include a list of cool custom triggers (Like this one and others) in the WeakAurasTutorials addon as some predefined examples.

    Pro: Don't drop design philosophies, Make some of the more advanced and cpu-intensive triggers available to non-lua coder users.

    Con: Not directly obvious for new users to explicitly find them (There should definitely a note on the main-page to redirect new users)

    Last edited Nov 13, 2011 by CommanderSirow
  • 15 posts

You must login to post a comment. Don't have an account? Register to get one!