HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Delayed Damage - Avoiding Recursion

08-20-2008, 07:41 PM#1
Sophismata
Okay, been giving this a shot now, and while I think I have a handle on the situation, I wanted to see if anyone had some advice or a new perspective on how to delay the application of damage.


What I am attempting to do:

1. Unit X takes damage.
2. Unit X is healed for the amount of damage taken, the damage is saved.
3. 6 seconds later, the damage is dealt to Unit X, using as many of the orginal characteristics as possible.


The primary problem I'm having is with damage recursion, the secondary problem I'm having is with preventing the first instance of damage.


Update: I've just had it pointed out to me (thankyou Dusk!) that you can disable the trigger before the delayed damage is dealt, then re-enable it after. D'oh! So, my question now becomes: is there a possibility of the trigger missing a damage event in the brief time it is disabled?

Quote:
So far, these are the methods I've come up with to avoid damage recursion:

1. Save the damage and source unit to the cache as a unique string (this is right before the damage is dealt for the second time). When the damage event gets called again, have it reference the cache using the source unit and damage amount, to see if that damage has already been delayed.

2. Store the damage as a custom unit value to the source unit - similar to above, this avoids the cache call, but uses up Unit User Data.

3. Index all units on the map, flag the source unit just before the delayed damage is dealt, and check the flag on the event to prevent recursion (essentially the same as the last step).

4. Use a unique damage type for the delayed damage - have this damage type never be delayed (much simpler, but doesn't allow for custom damage types to this unit).


Presently I am using Unit User Data to handle the issue, and this has raised two questions. Firstly, is there a better method that I'm just not seeing? Secondly, is it possible for the unit to deal damage in between the time it is flagged and the time the event triggers?I don't want to bombard people with JASS, this is more of a brainstorm than me trying to get the actual code, but as a means of explanation...

(Uses Dusk's Intuitive Damage System, to explain the UnitDamageTargetEx and RegisterDamageEvent functions)

Expand JASS:

I am concerned that a unit's damage will be able to slip through the delay between the time

I just realised my concern there is utterly irrelevant. If a unit deals the same damage, and thus falsely unflags itself before the delayed damage is dealt, the delayed damage will merely recur, and it is the exact same damage, so there's no actual problem. Silly me.

Anyway, I'm not quite certain how WC3 will handle this event - is it actually possible to have a new trigger execute while an existing trigger is running? I do know that War3 will never handle events simultaneously (at least as far as damage is concerned) - I'm just not sure how this applies to threads.


Second problem - actions taken as a damage response occur before the damage is dealt. Is there a way to deal the damage and then apply actions without using a 0.01 timer? At the moment, I can notice the rapid health changes when the unit takes damage and has to be post-healed.


As a final note, am I correct in assuming it is impossible to acquire the details of Weapon Type and/or Attack Type from the damage event?


Anyway, this was a bit long-winded, but thankyou for any help. I hope I've been clear :).
08-20-2008, 07:55 PM#2
Themerion
Quote:
I've just had it pointed out to me (thankyou Dusk!) that you can disable the trigger before the delayed damage is dealt, then re-enable it after.
Yes, that works. Nothing will trigger in between.

In order to prevent the initial damage... Use timers instead of TriggerSleepAction if you want it to be less noticeable.
Collapse JASS:
function OnTakesDamage takes unit target, real takenDamage returns nothing
    call UnitAddAbility(target,ITEM_ABILITY_HIT_POINT_BONUS)
  // TSA is okay for 0.
    call TriggerSleepAction(0)
    call UnitRemoveAbility(target,ITEM_ABILITY_HIT_POINT_BONUS)
    call SetUnitState(target,UNIT_STATE_LIFE,takenDamage + GetUnitState(target,UNIT_STATE_LIFE))
endfunction
08-20-2008, 08:01 PM#3
Rising_Dusk
Quote:
Originally Posted by Sophismata
So, my question now becomes: is there a possibility of the trigger missing a damage event in the brief time it is disabled?
No, it is not possible, jass is instantaneous and like Themerion said, there is no multithreading.
Quote:
Originally Posted by Sophismata
the secondary problem I'm having is with preventing the first instance of damage.
Damage prevention is an interesting issue because there are 2 cases to consider.
  • Damage Dealt + Current Life > Max Life
  • Damage Dealt + Current Life <= Max Life
The second is easy, just heal the unit (damage dealt) life in the EVENT_UNIT_DAMAGED event callback and you're done. The first is trickier and requires you increase the unit's max life for a 0.0s instant so it can survive the damage, then in the callback of that 0.0s timer reset the unit's stats to the correct amounts. The flickering of unit's life bars can be adjusted for by doing some solid calculations to keep % life the same before and after the unit take's damage, but you can't really do it any other way. (Damage always occurs the instant after the damage event runs)
Quote:
Originally Posted by Sophismata
As a final note, am I correct in assuming it is impossible to acquire the details of Weapon Type and/or Attack Type from the damage event?
You are correct in your assumption.
08-20-2008, 08:17 PM#4
Anitarf
Threads can get interrupted in execution by other threads even when they don't wait - it happens precisely in situations when a function call in one thread causes an event of a trigger to run, for example creating units and unit-enters-region events as well as damage actions and unit-takes-damage events.

You're hitting a lot of very complicated problems with this spell, and it'll be difficult for me to adress them all in a single post. It would be smart to leave damage handling to systems; multiple spells trying to do damage detection and prevention each with it's own code is bound to bug sometime.

For starters, there's Vexorian's xedamage that lets you tag triggered damage you deal, this is a good way to avoid recursion since when damage is dealt you can check if it was triggered damage and what it's tag is, that way you can prevent the delayed damage from triggering the delay effect again.

Then there's my ADamage system that handles damage detection and prevention (not approved yet so I might have to change things that will break backwards compatibility; I mention it because no other damage detection systems in the database cover damage prevention), I don't consider it sane to try to do damage prevention without a system like that, due to all the nuances involved; just think of the trouble you can get in if you try to deal damage in response to damage being dealt.

You're right that you can't detect attack and damage types of damage being dealt ingame, but the good news is that when you detect damage being dealt it is already reduced by armour type and amount, so when you deal that same damage with a delayed effect you shouldn't deal it with it's original attack&damage type, but a type that doesn't get reduced by armour at all.