• 🏆 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!

The problems with damage modification

Status
Not open for further replies.
Level 21
Joined
Mar 27, 2012
Messages
3,232
This post will be hard to understand for most people. I'm aware of it and can't change that.

I've so far mainly used lfh's damage detection. As such, I will use it as a reference. I believe I've found a way to fix a crucial issue with how it works.

The current design allows setting handlers to damage events in general, which always run after the fact. This is fine for GUI resources as an event, but breaks down in any attempts to make more complex systems.

To support recursion, it has a special function to be used inside events. There are some cases though where it can not be known whether a damage event is currently running. For instance, consider a custom buff system. Within that system some buff deals damage upon being removed. Among the ways to remove buffs are at least those two: A spell that clears buffs and a basic attack modifier. In this case it's impossible to know which function to use.

The solution is to not consider triggered damage after the fact. This brings up a problem - How to make sure that custom system variables are passed correctly to the handler? For instance, if I want to tag damage events with tags such as "DoT" and "AoE", I'd have to have a way to apply them on a specific damage event. If the damage simply waited until a current event stopped running, things would break once again.
The solution is to have the damage handler run instantly. However, this could break the previously running event. As such, the solution is impossible with the current design.

What to do though? Index the events and provide a function that allows creating and running a damage handler instantly, even if it can't be applied right away. This can potentially create a problem once again - Tags such as DoT and AoE have to be set every single time, even if they are not used. This necessitates having some more complex kind of handling. That is, damage events have to be extendable in the way that upon creating a new damage event, the data from its previous(expired) state is flushed. There's an inherent lack of knowledge from the system's perspective, because stuff like this has to be extendable.
This can be achieved in 2 ways - methods that can be registered or a hashtable. The upside of using methods is that it can potentially be very fast. The upside of a hashtable is that hashtable values can be flushed without any regard for how many there are or what they contain.
I consider the hashtable approach much more extendable, despite possibly being slower.

With a complex system like I'm proposing there come 3 possible use-cases that are hard to appease.
1. A vJASS poweruser who wants full control over how stuff works.
2. A person that understand very little beyond GUI and just wants some basic damage modification functionality.
3. A mix of the above, who wants the system to be melee-friendly, while still providing the customization of #1 as an optional feature.

I believe all those can be supported within the same system without excessive setup.

The architecture of the system would be as follows:
*All units are automatically set up to detect damage received, like in all DD systems. The damage events are indexed in a hashtable to provide support for multi-instancing.
*All damage is indexed in the same way, with non-triggered damage being passed to the damage handler when it appears.
*Triggered damage is called through a special function in order to separate it from any melee-friendly stuff. However the damage goes through the same handler, with merely a single flag set to determine that it's triggered.
*Damage event instances can be created, handled and applied at different times. The reason for this is to allow adding tags or other data to triggered damage events before the fact.
Non-triggered damage is handled in one piece.

Key differences to lfh's system(the one I've used the most):
*Damage events are indexed, granting support for multi-instancing that doesn't fully exist in his system.
*Dealing triggered damage can be optionally spread into multiple functions in order to attach data before the handler runs.
*All damage events can be handled instantly without breaking recursion. Damage can be applied at a later time if any problems are found with applying it instantly.
*The minimum amount of configuration remains the same, but with added optional functionality and fixing multi-instancing.
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
*Is there a vJASS version? GUIJass is inherently not extendable.
*"I did not implement custom script functions to get/set life/max life in your map. " - I have a problem with this. Stuff will break.
*You don't appear to index damage events. If so, you don't have a way to assign data onto specific damage events. As such, multi-instancing is not fully possible.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
1) GUIJASS can easily be brought into vJass territory. I wrote the code for GUI->vJass Unit Indexer in five minutes.

2) Custom get/set life functions didn't exist in DamageEngine 2 and no one complained even after 3 or 4 years. I think their significance is blown out of proportion. Anyway, could be done - but I haven't done it, yet.

The life set/get thing is a bit awkward to handle as you gotta ask yourself if the user wants the current life or the life it's going to have.

The max life check can ask if the ability level of Cheat Death is above 0, and subtract 500,000 if so.

Either way, I would include these in a vJass plugin.

3) You can attach data using DamageEventType, that's why I said it's doable. To expand on what I said in my first post, that variable can be set before or during damage modification as it is cached and its previous value is also cached (on top of that, recursion doesn't break that as both variables are recursion-proof). I did the same thing with the DamageEventExplodesUnit variable.
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
1) GUIJASS can easily be brought into vJass territory. I wrote the code for GUI->vJass Unit Indexer in five minutes.

2) Custom get/set life functions didn't exist in DamageEngine 2 and no one complained even after 3 or 4 years. I think their significance is blown out of proportion. Anyway, could be done - but I haven't done it, yet.

The life set/get thing is a bit awkward to handle as you gotta ask yourself if the user wants the current life or the life it's going to have.

The max life check can ask if the ability level of Cheat Death is above 0, and subtract 500,000 if so.

Either way, I would include these in a vJass plugin.

3) You can attach data using DamageEventType, that's why I said it's doable. To expand on what I said in my first post, that variable can be set before or during damage modification as it is cached and its previous value is also cached (on top of that, recursion doesn't break that as both variables are recursion-proof). I did the same thing with the DamageEventExplodesUnit variable.

1. Well, if you provide a vJASS version then this complaint of mine goes away.
2. I assume it wasn't really used much because it's a GUI system. GUI users tend to want the least power and the most simplicity. Not using a custom function makes sense from that perspective.
Which one to use is a non-problem, because with an appropriate function I could just get either. If I know the current one(before damage is applied) I can easily calculate the other as well. If I can't due to what I do in the damage handler, then neither could the system.
You don't need to calculate anything for such a function. Just remove the ability for a moment and the health becomes what it should be.
3. DamageEventType doesn't sound like it'd index damage events. Thus, it might only partially circumvent the problem and even that is up to debate.
The problems with recursion don't come from handling any system variables. They come from extending the system from outwards, which is what I do a lot. You can't cache variables that are not part of the system, so how does it solve recursion for anything beyond the core system?
DDS is also capable of this... : )

Can organize events however you want and can organize your data however you want.

This is all made possible by Trigger. Any DDS that runs on Trigger is capable of this really.

It's true that your system is probably the most powerful there is, but I've not found your systems in general to be very usable for me. Partly because they require more vJASS knowledge and partly because I don't use structs.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
My system does this... kind of.
There is one simple rule that is applied in my system: Make sure that you either remove the indexed damage manually or make sure that you run the ApplyDamage function.
(Same rule as ReleaseTimer() exists in TimerUtils.)
It wont break the system or the map but it makes it slower and eventually have no free indexes any more thus making it unable to create any new damages.

But next to that, it does everything that you want.

* It indexes the damage. (In arrays though and not hashtable.)
* It allows you to create a damage index (and set its variables) and do some other functions before applying the damage or even wait some time before applying.
* It allows you to choose a hit type when applying coded damage.
The hit type determines how the damage is applied.
Hit type "BasicAttack" is used for basic attacks, hit type "SpellDamage" is used for spells, you can create your own hit types as this is simply an integer variable.
When dealing coded damage, the standard hit type is spells but can be changed to whatever you want.
This allows making physical damage spells possible without problems.
* All units are included in the damage event.
* All damages run through the same function that applies the damage.
* Everything is 100% GUI friendly cause udg_ globals are used and (with the use of hooks) UnitDamageTarget(), UnitDamageTargetBJ(), UnitDamagePoint() and UnitDamagePointLoc() are all handled flawlessly to be handled by the system and ignore the damage by the original function.
It also instantly removes the max life bonus after the damage causing no need for custom functions to get the max life/current life.
* 0 Object Editor data required (using Object Merger).
* It has several events which all have their own uses to prevent certain things to happen when they shouldnt.

Problems I want to solve:
* I want to find all normal abilities that would not work with this system and try to make them work anyway.
For example, Runed Bracers Item doesnt work any more cause it overwrites the 2.00 spell damage reduction.
To solve this, I re-add the 2.00 spell damage reduction ability.
* I want to revert damage taken into damage sent.
I want the amount in the onDamage event be equal to whatever is placed in the object editor (before armor, armor type, spells, ets.)
This almost works with one exception, that Attack-Type on basic attacks is hard to load.
I can assume that all attacks are done using the Attack-Type of Attack#1, but War Club, Orbs, etc will break this method.
* I want to make users be able to turn certain features on and off by setting global variables or disabling triggers in combination with static ifs.
Not everyone wants the hooks, because they are only usefull when you use the natives or BJs, which are basically slower than using the custom way itself cause that would remove unnecessary work.
Or disable the damage amount reversion because it is a heavy operation and it only makes the applied damage more accurate because added damage is also reduced by armor. However I for example dont use standard armor/attacktype in my map so I dont need it.


You can see that these three things that I want to make are very hard to achieve... maybe so hard that they are impossible with the current version of WC3.
That said, it is not likely to be released soon.

I am sure DDS has almost every feature... except for GUI friendlyness and maybe another spot but it should work fine as well.
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
* It allows you to create a damage index (and set its variables) and do some other functions before applying the damage or even wait some time before applying.
* It also instantly removes the max life bonus after the damage causing no need for custom functions to get the max life/current life.

Problems I want to solve:
* I want to revert damage taken into damage sent.
I want the amount in the onDamage event be equal to whatever is placed in the object editor (before armor, armor type, spells, ets.)
This almost works with one exception, that Attack-Type on basic attacks is hard to load.
I can assume that all attacks are done using the Attack-Type of Attack#1, but War Club, Orbs, etc will break this method.

*The problem with using arrays is that there have to be extendable handlers... since it can be done, I keep it as an option, but hashtables are likely superior.
*How do you handle checking life during a damage event then?
*This is either very heavy or very information-intensive. You could provide a function that, given the armor value, reverts the change that this amount of armor would make. That way if someone would get the armor value in an easier way that detection, it would be fairly easy to make the system use it.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
What do you mean with extendable handlers?
Like making more variables?

Checking life during damage event?
There is no during, there is only before and after, you cannot place actions during the event but only when the onDamage and afterDamage events are fired in which case the damage has either been applied or not yet.

My generic version is extremely heavy and will not support 400+ units in combat on a map with other stuff in it.
However, for an RPG, AoS, Dungeon, Hero Arena, etc, etc, etc it is not a big problem though. (And when you remove the damage reversion then it does support a hell of a lot more.)

To make the system use armor, it requires some configuration like how much 1 armor does.
I like armor that increases 1% of effective health. (Meaning every point of armor increases effective health by 1%.) In WC3, this is 6% by default.
However the current way of how this is done is applying a dummy damage to the target and finding the factor between the damage dealt and health change and uses that factor to revert the damage.
So I need both damagetype and attacktype.
Damagetype is no problem, attacktype is for basic attacks.
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
Essentially when a damage event runs, it can gather some data from the game. Stuff like setting a specific damage event to be special in some way(extra lifesteal, critical strike, deals area damage, kills the user, etc) can not be handled by the system because the system can't know everything and neither should it. Thus, this data has to be held somewhere and recycled at the appropriate time. This is one of the reasons why it's good to index event instances.
The beauty of hashtables in this case is that clearing them is very loosely related to how much data there is and what the data is, while with arrays the user would have to first set everything up separately for each damage event and then clear everything after each event.

So you add the life modifier ability at a time where no damage events can run?

I'd still make the detection of original damage and method of doing so separate and optional.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
With hashtables the system has to create the new data per onDamage anyway.
"extra lifesteal, critical strike, deals area damage, kills the user, etc"
This all should be done by other triggers that run on the onDamage event.
Those triggers will individually check if user must be killed for example, and if so, then they kill him.

I add the life modifier ability when damage is detected after I dealt the triggered (modified) damage to that unit.
The life modifier ability is removed immediately after the unit has taken the original damage.

I know about making it optional :D
I for example turn it off because I trigger all damage, hell I dont even use UniyDamageTarget() any more.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Upon re-reading this after making my latest changes with DamageEngine, I can state with the confidence of heavy testing that it fixes these problems. It allows damage type to be set before or during damage processing, and has strong safeguards to prevent recursion issues. It has an AfterDamageEvent if you want to append damage and regular UnitDamageTarget (even UnitDamagePoint if you don't attach data) work fine if you want it to apply right away.

If there's something I'm missing, please let me know so I can see if it can be added to the system.
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
I forgot this thread for a while. Anyway, here goes...

With hashtables the system has to create the new data per onDamage anyway.
"extra lifesteal, critical strike, deals area damage, kills the user, etc"
This all should be done by other triggers that run on the onDamage event.
Those triggers will individually check if user must be killed for example, and if so, then they kill him.

I add the life modifier ability when damage is detected after I dealt the triggered (modified) damage to that unit.
The life modifier ability is removed immediately after the unit has taken the original damage.

I know about making it optional :D
I for example turn it off because I trigger all damage, hell I dont even use UniyDamageTarget() any more.
The benefit with hashtables is that I only have to set the values that I actually use, while with arrays every single thing has to be set to its default value upon the start or end of an event. This makes event cleanup and/or setup scale somewhat better when using hashtables.

Okay, if the ability is only there during the actual moment that damage is done, then it seems like it would work.

Upon re-reading this after making my latest changes with DamageEngine, I can state with the confidence of heavy testing that it fixes these problems. It allows damage type to be set before or during damage processing, and has strong safeguards to prevent recursion issues. It has an AfterDamageEvent if you want to append damage and regular UnitDamageTarget (even UnitDamagePoint if you don't attach data) work fine if you want it to apply right away.

If there's something I'm missing, please let me know so I can see if it can be added to the system.

*Do you index events? If so, do you use arrays or hashtables? If not, why?
*Do you mean the damage type that is used along with armor types or do you mean whether the damage is physical or magical? (Code was always a pseudotype and should be a flag, not an actual type)
*What kind of safeguards?
*Do you provide a way to handle the damage separately from applying it?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Here is a readable version of DamageEngine (expirimental API and functionality that GUI Damage Engine doesn't have).

http://www.hiveworkshop.com/forums/lab-715/vjass-alternative-damageengine-270064/

Event handling uses a combination of locals and arrays. Locals for small things like damage modification, arrays for preserving data in the event of recursion (as the arrays must be set to their previous stack during the after damage event which is in a separate function so locals don't work there).

With Damage Type I refer to a custom integer which can be set before or during the damage event.

Recursion is overcome due to me setting a boolean before firing the first user events and storing the previous variables for safekeeping until that recursive damage has run its event and its after damage event if applicable.

What do you mean by handling the damage separately from applying it? Like allowing a non-UnitDamageTarget/in-game attack to trigger it? I do have a way of letting the user skip the damage modification phase and therefore deal pure damage, if that's what you're looking for. I'll need you to clarify though.

Please check out my Damage Engine link that I posted, though. I am barely getting any feedback but I take all suggestions wholeheartedly.
 
Status
Not open for further replies.
Top