Basically, using a unit's life to detect if it's dead is a bad idea because a dead unit's life can still be modified when it is dead.
The best way to detect dead units is to use this library (requires vJASS):
(Special thanks to azlier for showing me this)
library dead
//returns true if the unit is alive. requires JassHelper // to declare the AI native "UnitAlive" as a JASS native, so that // we can use it in the trigger editor. If you trigger in vJASS, // you should DEFINITELY use this since it is the only way to be // sure that a unit is alive. native UnitAlive takesunit id returnsboolean
//returns true if the unit is dead. inline friendly. function UnitDead takesunit u returnsboolean returnnot UnitAlive(u) endfunction
//returns true if the unit exists in the game. inline friendly. function DoesUnitExist takesunit u returnsboolean return GetUnitTypeId(u) != 0 endfunction
endlibrary
I also included the DoesUnitExist function, since that seems to be a useful and popular check that not many people know about.
Plain JASS alternative (put this in your trigger editor map header):
//returns true if the unit exists in the game function DoesUnitExist takesunit u returnsboolean return GetUnitTypeId(u) != 0 endfunction
//returns true if the unit is dead (or does not exist) function IsUnitDead takesunit u returnsboolean return IsUnitType(u, UNIT_TYPE_DEAD) or not DoesUnitExist(u) endfunction
Old Explanation (no longer applies)
Summary
How do you detect if a unit is dead? Check if the unit’s life is less than .405, right? Wrong. There is only one way to tell if a unit is actually dead (or not alive):
function IsUnitDead takesunit u returnsboolean return IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0 endfunction
Stuff like GetUnitState(yourUnit, UNIT_STATE_LIFE) < 0.405 or GetWidgetLife(yourUnit) < .405 can and will cause bugs.
Why?
When a live unit's health goes anywhere below 0.405, it becomes dead. Its health is set to 0.000 and its unittype becomes UNIT_TYPE_DEAD. Once the unit is dead, its life can still be changed by triggers. This serves no practical purpose that I know of, and it does not revive the unit if its life is set above 0.405. However, this can result in bugs if you rely on using the unit's health to tell if it is dead or not. For example, if you forget to have a triggered AOE heal spell filter out dead units, it can heal a bunch of corpses. This would result in all of your other triggered spells thinking that the corpses are "alive." Then imagine the corpses being accidentally knocked back, frozen, meat-hooked, etc. Regardless of whether or not one spell remembers to check for dead units, it should not screw up all of your other spells.
So, the obvious solution is to use the unit's unittype. This unittype is updated automatically whenever the unit is killed, exploded, revived, etc. Unfortunately, IsUnitType returns false when the unit does not exist, meaning we could think a non-existent unit is alive. To make my function work for removed/null units, I also check if the unit's type id is 0. Now units that are null or no longer exist are considered "dead." No matter what mistakes you make, this function will always tell you whether a unit is dead or not.
Test Map
Some of you may want to verify the above information. For that reason, I have provided the map that I used to test things. In it, you will find a peasant whose basic information is displayed. You can damage, heal, and revive the peasant to see how the information changes. To damage the peasant, type "-damage x" where x is a real number. To heal the peasant, type "-heal x" where x is a real number. I disabled the peasant's health regeneration for testing purposes. To revive the peasant, use the paladin's skill. A zeppelin and necromancer are provided for further testing.
Credits, misc.
Please do NOT give me credits for this function. I do not know who first used unittype to detect dead units. I first saw it somewhere on wc3c and am only posting it here for the benefit of the community.
I did, however, do all the research on this topic and create the String library used in the test map.
Despite the small size of this function, I found it important enough to be in its own thread rather than in Small Code Snippets. After all, nearly every spell and map needs to detect dead units.
Last edited by maskedpoptart; 01-20-2010 at 12:46 AM.
No offense, but I do not understand you people. Why use a function that is guaranteed to screw up some of the time? Who cares if it is insignificantly faster... the function I have posted does EXACTLY what yours is meant to do, no strings attached. Yours has to come with a disclaimer: "Oh and be careful never to modify the life of a dead unit or this function will cause all of your spells to screw up".
My function is modular programming and is user friendly. No matter what experience level you are, you can count on it to do what IsUnitDeadBJ should have done in the first place.
People usually make some custom HealUnit function, which checks healed units health before changing it, so I dont really see why GetWidgetLife as a death check is so bad.
o_O
My personal favorite is:
Jass:
IsUnitInGroup(unit, DeadGuys )
Its just perfect. ( Part of unit recycling system, which naturally handles all death thingies nice and smooth )
Okay. I would prefer having the function as just
returnIsUnitType(u,UNIT_TYPE_DEAD)
but this would not return the correct value for null units or units that no longer exist. For example, IsUnitType(null, UNIT_TYPE_DEAD) would return false, making you believe a non-existent unit is actually alive.
So if the unit does not exist, we need to return true. Otherwise, check if the unittype is UNIT_TYPE_DEAD. I have only found one way to check if a unit does not exist, and that is if
GetUnitTypeId(u)==0
. Then our function comes out to
returnGetUnitTypeId(u)==0orIsUnitType(u,UNIT_TYPE_DEAD)
I am 99% sure this function will always work. If you find any cases where it does not work, please let me know. Otherwise, I believe there is no point in using GetWidgetLife or GetUnitState to check for dead units.
If any of you have a big enough ego to think your map will always work when you want it to, use GetWidgetLife. If you are more obsessed with speed than functionality, use GetWidgetLife. Otherwise, use my function and rest assured that when you think a unit is dead, IT IS FUCKING DEAD
…or has been removed.
If you only need to check for dead units in filters, you can use
IsUnitType(yourUnit,UNIT_TYPE_DEAD)
in place of
GetWidgetLife(yourUnit)<.405
. Otherwise, call this function (
IsUnitDead(yourUnit)
). It is shorter to type, not noticeably slower, and guaranteed to work for every circumstance.
Okay I'm done ranting. Sorry for the bit of swearing, kiddies.
I would suggest putting the IsUnitType() function as the first operand. It seems to me that checking a unit that doesn't exist anymore is the exception rather than the rule.
I would suggest putting the IsUnitType() function as the first operand. It seems to me that checking a unit that doesn't exist anymore is the exception rather than the rule.
You are absolutely right. It is changed now. Don't know why I had it like that...
Meh, I would never purposefully modify the life of a dead unit. I meant in case I somehow accidentally did so, my function would still work.
I know how to not be stupid and how to code well, but I recognize that I am (like you) only human, and prone to make mistakes. When these mistakes inevitably occur, I won't have to worry about this function making matters worse.
Anyway, thank you for recognizing the usefulness of this function.