oUF

99.9k Downloads

strange implementations in health prediction

cjhwang0222 opened this issue ยท 16 comments

commented

local hasOverHealAbsorb = false
if(health < healAbsorb) then
hasOverHealAbsorb = true
healAbsorb = health
end

Replacing the value of healAbsorb with that of Health at the line would bias the calculations after the line. healAbsorb Bar's max value should not be over health bar's length but shouldn't it be done later?

commented

Flying back home later today, will take at look later.

commented

https://github.com/tomrus88/BlizzardInterfaceCode/blob/37e971677afa43e2d2a6cd39dff2e34aeaedf180/Interface/FrameXML/UnitFrame.lua#L273-L279 reference to stock ui code

The healAbsorb is placed above the the health bar because healing someone with a healabsorb would not change their actual health value but deplete the healabsorb first. This is the default ui behavior and it seems logical to me.

Yet, I'm a bit out of context right now, so feel free to object.

commented

first of all, I'm not English speaker, so please consider it :)

Let's say an example, a unit whose current health is 1000 and got a debuff of heal absorb 2000, and there is incoming heal as 2400 which can erase the debuff and would give the unit some health.

so the math should be like this; the incoming heal would covers the heal absorb(2400 - 2000) and the remained 400 incoming heal should be shown. BUT, the code overrides the heal absorb (2000 -> 1000) before considering incoming heal, finally, the incoming heal (2400 - 1000 = 1400) would be shown, which isn't right. After that, if the remained incoming heal exceed the max health then it should be capped based no the max overflow.

another example
If the unit got 400 incoming heal, then the expected heal absorb should be 1600. In this point, as you mentioned the heal absorb should not be over health bar, it should be overrided as 1000, the unit's current health.
BUT, the code overrides the heal absorb FIRST, the incoming heal covers 400 of 1000 heal absorb so the 600 heal absorb would be shown.

Am I right?

p.s. Happy New Year!

commented

If you want to test your assumptions, run the following code on https://www.lua.org/cgi-bin/demo and adjust the values accordingly.
This is basically the body of the update function of the healthprediction element. overflow is a substitution for element.maxOverflow

local overflow = 1
local myIncomingHeal = 2400
local allIncomingHeal = 2400
local absorb = 0
local healAbsorb = 2000
local health, maxHealth = 1000, 5000

local hasOverHealAbsorb = false
if(health < healAbsorb) then
	hasOverHealAbsorb = true
	healAbsorb = health
end

if(health - healAbsorb + allIncomingHeal > maxHealth * overflow) then
	allIncomingHeal = maxHealth * overflow - health + healAbsorb
end

local otherIncomingHeal = 0
if(allIncomingHeal < myIncomingHeal) then
	myIncomingHeal = allIncomingHeal
else
	otherIncomingHeal = allIncomingHeal - myIncomingHeal
end

local hasOverAbsorb = false
if(health - healAbsorb + allIncomingHeal + absorb >= maxHealth or health + absorb >= maxHealth) then
	if(absorb > 0) then
		hasOverAbsorb = true
	end

	if(allIncomingHeal > healAbsorb) then
		absorb = math.max(0, maxHealth - (health - healAbsorb + allIncomingHeal))
	else
		absorb = math.max(0, maxHealth - health)
	end
end

if(healAbsorb > allIncomingHeal) then
	healAbsorb = healAbsorb - allIncomingHeal
else
	healAbsorb = 0
end

print("mine:", myIncomingHeal)
print("others:", otherIncomingHeal)
print("absorb:", absorb)
print("healAbsorb:", healAbsorb)
print("overAbsorb:", hasOverAbsorb)
print("overHealAbsorb:", hasOverHealAbsorb)

So, yes it seems the returned values are not the expected ones. I'll evaluate this against Blizzard's implementation.

Points of interest here:

commented

There must be some sort of dummy vars for graphics and ones for actual math.
Ah. and thank you for letting me know such a site to test some Lua code

commented

Use the version I posted above. I have stripped the part where the bar values are set and substituted with the prints.

commented

I've tried my own version(?).


local overflow = 1
local allIncomingHeal = 2400
local absorb = 0
local healAbsorb = 2000
local health, maxHealth = 1000, 5000

-- for math
if (healAbsorb > allIncomingHeal) then
	healAbsorb = healAbsorb - allIncomingHeal
	allIncomingHeal = 0
else
	allIncomingHeal = allIncomingHeal - healAbsorb
	healAbsorb = 0
end

-- for graphic
local hasOverAbsorb = false

local remaining_heal = math.max(allIncomingHeal - healAbsorb, 0)

if(health + remaining_heal + absorb >= maxHealth * overflow) then
	hasOverAbsorb = true
	if(health + remaining_heal > maxHealth * overflow) then
		absorb = 0
	else
		absorb = math.min(maxHealth * overflow - (health + remaining_heal), absorb)
	end
end

local hasOverHealAbsorb = false
if(health < healAbsorb) then
	hasOverHealAbsorb = true
	healAbsorb = health
end

print("allHeal:", allIncomingHeal)
print("overAbsorb:", hasOverAbsorb)
print("absorb:", absorb)
print("overHealAbsorb:", hasOverHealAbsorb)
print("healAbsorb:", healAbsorb)


I don't know the priority of heal consumption (mine first? other's first?) so I gave it up to split them. except that, I think this one works as theory.

commented

Do you know where/how I can get a heal absorb debuff? I just want to verify the UnitGetIncomingHeals returns.

commented
commented

Full(?) list of heal absorb spells. Harmful Strike is used by some mobs in Tanaan Jungle. UnitGetIncomingHeals returns raw (and sometimes incorrect) values, so it does not account for heal absorbs.

@p3lim
Why is this needed? The healthprediction element should handle those.

@selfaddicted
My current quick and dirty solution would be something like that:

local overflow = 1
local myIncomingHeal = 2400
local allIncomingHeal = 2400
local absorb = 3600
local healAbsorb = 2000
local health, maxHealth = 1000, 5000
local hasOverAbsorb = false
local hasOverHealAbsorb = false

local otherIncomingHeal = 0
if(allIncomingHeal < myIncomingHeal) then
	myIncomingHeal = allIncomingHeal
else
	otherIncomingHeal = allIncomingHeal - myIncomingHeal
end

if(healAbsorb >= allIncomingHeal) then
	healAbsorb = healAbsorb - allIncomingHeal
	allIncomingHeal = 0
	myIncomingHeal = 0
	otherIncomingHeal = 0
else
	local reduced = allIncomingHeal - healAbsorb
	local ratio = reduced / allIncomingHeal
	myIncomingHeal = myIncomingHeal * ratio
	otherIncomingHeal = otherIncomingHeal * ratio
	allIncomingHeal = reduced
	healAbsorb = 0
end

if(health - healAbsorb + allIncomingHeal > maxHealth * overflow) then
	allIncomingHeal = maxHealth * overflow - health + healAbsorb
end

if(health - healAbsorb + allIncomingHeal + absorb >= maxHealth or health + absorb >= maxHealth) then
	if(absorb > 0) then
		hasOverAbsorb = true
	end

	if(allIncomingHeal > healAbsorb) then
		absorb = math.max(0, maxHealth - (health - healAbsorb + allIncomingHeal))
	else
		absorb = math.max(0, maxHealth - health)
	end
end

if(health < healAbsorb) then
	hasOverHealAbsorb = true
	healAbsorb = health
end

print("mine:", myIncomingHeal)
print("others:", otherIncomingHeal)
print("all:", allIncomingHeal)
print("absorb:", absorb)
print("healAbsorb:", healAbsorb)
print("overAbsorb:", hasOverAbsorb)
print("overHealAbsorb:", hasOverHealAbsorb)

If I get the idea right, we should have either healAbsorb or allIncomingHeal, whichever is bigger will negate the other. This distributes the healing reduced by heal absorbs between the player's and others' heals. It could be cleaned up a bit maybe, will test a bit more first.

commented

Ah, Blizzard displays the incoming heals on top of the healAbsorb texture. It makes sense because this way it will show how much of the heal absorb will be "eaten" by the heal. The example in oUF's healthprediction element suggests to anchor the element.myBar to the texture of the health bar instead of the texture of the healAbsorbBar. The question is now is whether to fix the anchoring suggestion (the amount of incoming heals will need to be adjusted by the amount by which healAbsorb is reduced to accommodate the current health) and inform layout authors to adjust, or apply something like above.

Either way I take responsibility for it, you can beat me now :(

commented

I pushed a branch in which I mimic the default UI exactly and fix the edge cases where an overhealabsorb is shown (healAbsorb = health). See master...patch-healthprediction for details. What layouts would need to change is the anchoring of the self.HealthPrediction.myBar. Technically it will not break existing layouts (if they don't adapt, they won't get Lua errors) and, because the element returns wrong values currently, it won't hurt to merge it either way.

You can use this for testing out-of-game:

local maxOverflow = 1
local myIncomingHeal = 2400
local allIncomingHeal = 2400
local absorb = 0
local healAbsorb = 2000
local health, maxHealth = 1000, 5000

local hasOverHealAbsorb = false
if(health < healAbsorb) then
	hasOverHealAbsorb = true
	local reduction = healAbsorb - health
	if(allIncomingHeal > reduction) then
		myIncomingHeal = myIncomingHeal * (allIncomingHeal - reduction) / allIncomingHeal
		allIncomingHeal = allIncomingHeal - reduction
	else
		allIncomingHeal = 0
		myIncomingHeal = 0
	end
	healAbsorb = health
end

if(health - healAbsorb + allIncomingHeal > maxHealth * maxOverflow) then
	allIncomingHeal = maxHealth * maxOverflow - health + healAbsorb
end

local otherIncomingHeal = 0
if(allIncomingHeal < myIncomingHeal) then
	myIncomingHeal = allIncomingHeal
else
	otherIncomingHeal = allIncomingHeal - myIncomingHeal
end

local hasOverAbsorb = false
if(health - healAbsorb + allIncomingHeal + absorb >= maxHealth or health + absorb >= maxHealth) then
	if(absorb > 0) then
		hasOverAbsorb = true
	end

	if(allIncomingHeal > healAbsorb) then
		absorb = math.max(0, maxHealth - (health - healAbsorb + allIncomingHeal))
	else
		absorb = math.max(0, maxHealth - health)
	end
end

print('mine:', myIncomingHeal)
print('others:', otherIncomingHeal)
print('all:', allIncomingHeal)
print('absorb:', absorb)
print('healAbsorb:', healAbsorb)
print('overAbsorb:', hasOverAbsorb)
print('overHealAbsorb:', hasOverHealAbsorb)

@ls- will take a look at it tomorrow, but feel free to give it a go.

commented

After a lengthy discussion with @ls- we figured the above solution won't work without dynamic re-anchoring of the absorb bar (depending on whether incoming heals exceed the healAbsorb or not). Since we want to stay away from dynamic anchoring (layouts will have to use PostUpdate or we'll have to account for possible statusbar orientations and stuff in update), we'll stick to a solution similar to the one in #409 (comment) (Val will punch it pretty first ๐Ÿ˜„ ๐Ÿ”ซ)

commented

Wuuuut? @Rainrider, you want to stay away from dynamic reanchoring, I'm pro-dynamic-anchoring :D

Thing is, current implementation is almost correct, inc heal values need slight adjustment, but other than that it's okay. Thing is, we try to keep things as Blizzlike as possible. So you can attach 3 out of 4 bars permanently to each others' bar textures (statusbar:GetStatusBarTexture()), health > reversed heal absorb > my inc heals > others' inc heals, but damage absorb bar has to be anchored dynamically to the rightmost bar.

For instance, if inc heals overheal heal absorb, absorb bar should be attached to others' inc heals bar, but if heal absorb is still present, damage absorb bar should be attached to our health bar.

That's how Blizz UI works.

You see everything at once.

However, there's another non-Blizzlike solution that recalculates all values, primarily inc heals and heal absorbs which was discussed in this thread earlier, kinda. This way people will be able to attach reversed heal absorb bar to the health bar, and other 3 bars can be appended to each other w/o any need to reattach them later.

commented

I have a working non-Blizzlike solution, @Rainrider already tested it, but I want to adjust maths for our Blizzlike approach and update docs accordingly by adding PostUpdate func example.

That's what I'll do later when I get back home from work.

commented

Done. See #415 for more info.