• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Solved] Why does this work?

Status
Not open for further replies.
Level 37
Joined
Aug 14, 2006
Messages
7,601
Hey,

  • BERSERK DAMAGE
    • Events
      • Unit - DRAGON 2 0027 <gen> Takes damage
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • ((Life of (Triggering unit)) - (Damage taken)) Less than 0.41
        • Then - Actions
          • Unit - Set life of (Triggering unit) to ((Damage taken) + 0.41)
        • Else - Actions
Just wanted an explanation to this mystery. Why doesn't this unit die?
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
maybe it has something to do with the fact that this runs slightly earlier then the damage is done(damage is done when all Takes damage triggers are ran) but the damage dealt is already calculated, so you heal the unit and it survives with 1 hp

so the unit is healed a little bit before it takes the damage
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,199
The WarCraft III engine apparently runs the JASS damage handler before actually dealing the damage. This makes preventing death from any reasonable damage source very easy but makes blocking damage when at maximum health very difficult. This is why so many damage block abilities have a bug where they will lose some health when at maximum HP.
 
The WarCraft III engine apparently runs the JASS damage handler before actually dealing the damage. This makes preventing death from any reasonable damage source very easy but makes blocking damage when at maximum health very difficult. This is why so many damage block abilities have a bug where they will lose some health when at maximum HP.

I wouldn't call that very difficult as you can just refill the lost health after a 0.01s timer.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,199
I wouldn't call that very difficult as you can just refill the lost health after a 0.01s timer.
Incorrect, you use a 0 expiration timer and even that is too slow. It is possible for many damage instances to be done to the unit in the time it takes the timer to fire and each has to be accounted for to prevent the final health being computed incorrectly. Especially if you are using a shield ability with a fixed quantity or absorption percentage less than 100% as you want to correctly credit any killing units that do reduce health below 0.405, preferably without generating another damage instance (so the killing blow has to be handled differently from non-killing blows).

Only if the damage is less than the units max hp...
Yes but I think each damage instance is atomic so you only need to run handling code if the unit receives damage greater than the maximum health at a single instance (which no well made map should ever do, but in any case you can just pump unit health to the millions with a health increasing ability). The problem complexity still remains with the 0 timeout timer used to correct health to appropriate levels as that needs to handle many damage instances correctly.

but that will as result make the health bar flicker and sometimes you could spot the 500k addition under units portrait, which is not best but its as close as it can get
Incorrect as all this happens sub frame so the health number cannot flicker. The health bar does flicker because damage instances instruct a bar reduction on the next frame which does not reflect the unit's actual health. The flicker is due to the bar graphics responding to the damage instance and not the delay in handling the damage instance by the 0 timeout timer. I have made one of these systems before and can report that the bar does flicker but the health numbers below the unit portrait remain accurate at all times.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Yes but I think each damage instance is atomic

No, damage events can run a coupled instance and thats exactly where most systems fail. The bugs in Bribes DDS for example are cause by exactly this problem.

Incorrect as all this happens sub frame so the health number cannot flicker.

I tested the following systems that allow damage modification while developing my system:


All of these, including my system, can have flickering health values on damage blocking. It happens very rare and usually only under extreme conditions, but it happens.

This has nothing to do with the graphic responding to slow, but just because a 0-timer runs asynchronous. Health bar flickering can be avoided by the way easily.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,199
No, damage events can run a coupled instance and thats exactly where most systems fail. The bugs in Bribes DDS for example are cause by exactly this problem.
So you mean a few damage events can run before damage is dealt to the unit? It seems a bit illogical for that to happen but maybe is a sub-frame optimization preventing the game from booting the JASS interpreter multiple times.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
So you mean a few damage events can run before damage is dealt to the unit?

No, I didn't meant that, but this can happen anyways. As a good DDS should allow you to allocate damage from an allready running damage event (e.g. for reflecting purposes), you can never be sure any damage event runs atomic.

Every damage event can potentially get interupted at any time and a good system should be able to deal with this.

I think he refers to the 0.00 timer, but I'm not sure. Most systems do not check for stacked damage during that timer.

Yes, I refered to the 0-second timer. There are basically three different cases and each has to be considered appropriately. In the enclosed diagram I listed these three cases:

damagecaseskopie.png


Greetings,
lfh
 

Attachments

  • damage_cases.png
    damage_cases.png
    21.7 KB · Views: 70

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,199
Every damage event can potentially get interupted at any time and a good system should be able to deal with this.
You should never deal damage to the same unit in response to it taking damage as that opens infinite looping problems. In any case the behaviour of that damage would need to be investigated, as it could happen that by the time the interpreter gets back to executing the initial damage event, the resulting damage and event are already processed and dealt to the unit.

What we know is that the game engine signals the damage event before it carries out the damage to a unit. What needs investigating is if damage can get queued, where multiple damage events fire before multiple damages are deducted. Knowing this behaviour is important as it could potentially allow for some optimizations.

A timer with a timeout of 0 still has a timeout. The script will be scheduled when the next timer check is run. One can safely assume that the game engine does not interrupt running tasks (such as computing movement or damage) to check for timer timeouts as this could cause strange behaviour and bugs. The reason such a timeout is used is because some delay is needed to allow the game engine to deal the damage to the unit and any other JASS method still runs atomically with the damage event response (before the damage is dealt to the unit).

The main complexity comes from running a good shield system with the damage events. If the shield fails (runs out of absorption) and the unit has low health you may want to let the blow kill the unit for accurate credits. If you block all the damage then you have given the shield more absorption than it should have had (bug). Throw in shields with fractional blocking and it is possible that a unit will run out of life before absorption in any case and so need to credit the correct killer. Throw in multiple shields at the same time and you will need to run an appropriate stacking behaviour, removing from each shield in a certain way.

Blocking all damage from is easy. Blocking damage for a damage absorbing shield is not.
 
Status
Not open for further replies.
Top