[vJASS] [System] Physical Damage Detection

Level 7
Joined
Nov 15, 2009
Messages
225
After taking minutes of time for reading all the posts here.. I am completly lost for words.

In my entire warcraft time (and this is a very long time) I haven't seen such a negative thread about something good..

Anyone has an own and somehow unique style to create something.
Why do people try to destroy other folks style just because it does not fit perfectly?
I enjoy doing anything on my own, I've never used systems.
Why is this so hard to realize that there are still people who like to do it on their own way?


And a word to all those who think there is just this one perfect ressource:
What if I say people might not understand it?
What if I say people might want to modify it slightly, but can't because of the 'bad' knowledge..

Really, there is no bad thing about several ressources. You can even declare a ressource to a highly recommented one, if needed.

But in my case, if I want a ressource and I have to choose from several WORKING ressources I would take that one I am understanding.

Sorry for this long post. But I needed to post this..

+rep for the system
+rep for cokes nice comment

Don't stop working on this ressource, even if it's not approved.
It's nice and you shouldn't care about those who are pissed off because they get competition.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
handles are cleared in bunches and there is never single handle clean, if you print highest handle Id in the map while clearing leaks you will notice it will go up for around 1000 and then drop

Ok, this explains a lot. So, what would be the best way to check for leaks in general? Just wait for hours and see if the mean handle count doesn't increase?

Don't stop working on this ressource, even if it's not approved.

Thanks, I also think that several resources are nothing bad especially when they have different advantages. For me for example, the flickering healthbars of Advanced DamageEvent are not acceptable, even if it is more efficient than my system.

I won't stop working on the system, I will provide a GUI version soon.

lfh, yes that will fix the leaks, but you should really use a static group and a longer reset period than 5 seconds.

Yes, the 5 seconds were just for testing reasons. But I have some further questions:

1.) Do you mean something like this:
JASS:
static group g = bj_mapInitialPlayableArea
? What advantage does this have compared to a "normal" group? And where would I have to put this?

2.) Is there a way to call a function that is declared in a scope from a library? Something like this:

JASS:
scope b
    function test takes nothing returns nothing
        call BJDebugMsg("test")
    endfunction
endscope
JASS:
library a
    call b_test() // This doesn't work
endlibrary

Greetings,
lfh
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
you can do test.execute() which will put it into trigger and execute the trigger

Hm yes, but basically I want to pass the function as a code argument. Just like this:

JASS:
scope b
    function test takes nothing returns nothing
        call BJDebugMsg("test")
    endfunction
endscope

JASS:
library a
    call TriggerAddAction(myTrigger, function b_test )
endlibrary

Can this be done?
 
Ok, this explains a lot. So, what would be the best way to check for leaks in general? Just wait for hours and see if the mean handle count doesn't increase?

The best way is to know exactly what variable types leak. The type that leaks is called an agent and is what all non-pseudo handles extend from.

I searched for an explanation for you and this was the first thing I found - http://www.wc3c.net/showpost.php?p=1092474&postcount=6

Basically most things that can be created will also leak if not destroyed.

1.) Do you mean something like this:
JASS:
static group g = bj_mapInitialPlayableArea
? What advantage does this have compared to a "normal" group? And where would I have to put this?

Your example won't work because bj_mapInitialPlayableArea is a rect, and static is a keyword that can only exist inside structs. I only said static because of the definition.

In reality, you would want any global variable.

There's an example of doing this here.

The advantage is that you'll never have to create, destroy, or null the group after initialization.

2.) Is there a way to call a function that is declared in a scope from a library? Something like this:

JASS:
scope b
    function test takes nothing returns nothing
        call BJDebugMsg("test")
    endfunction
endscope
JASS:
library a
    call b_test() // This doesn't work
endlibrary

No, because JASS is a language for which no module can be referenced until after it exists. EG. This would never work:

JASS:
function a takes nothing returns nothing
    call b()
endfunction

function b takes nothing returns nothing
endfuntion

The same is true with scopes and libraries - these constructs only exist in vjass, but in reality are pre-processed down to simple jass functions (in a "smart" order).

Scopes can always call libraries because libraries are written to the map script "first".

Thus, if you want a library to be able to call an outside member, you must put that member in its own library and make *your* library requires it.

I hope that makes sense. For more info, take a look at the vjass documentation here.

By the way, try the [icode=jass][/icode] block which creates a single line of jass.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
I searched for an explanation for you and this was the first thing I found - http://www.wc3c.net/showpost.php?p=1092474&postcount=6

Your example won't work because bj_mapInitialPlayableArea is a rect, and static is a keyword that can only exist inside structs. I only said static because of the definition.

There's an example of doing this here.

I hope that makes sense. For more info, take a look at the vjass documentation here.

Thank you very much for this detailed explanations, this really helped me a lot. Especially the last link is very usefull, I didn't know that documentation.

Greetings,
lfh
 
Level 17
Joined
Jul 17, 2011
Messages
1,863
please make it like this: only have 1 function in your library called GetDamageType which takes unit source, unit target, damage amount and returns an integer
if it returns 1 , damage is physical if it returns 2 ,damage is magical most people who will use this already have some kind of damage engine in their maps so they dont need another damage engine or anything similar this will also simplify the system and allow noobs to use it too
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
please make it like this: only have 1 function in your library called GetDamageType which takes unit source, unit target, damage amount and returns an integer
if it returns 1 , damage is physical if it returns 2 ,damage is magical most people who will use this already have some kind of damage engine in their maps so they dont need another damage engine or anything similar

If you allready have a DDS you can either use this or this, depending on which DDS you use - So I really don't understand your request.

Its just not that simple that you can make a general DTDS which is independent of the DDS you want to use.

this will also simplify the system and allow noobs to use it too

The system is allready extremly easy to use and to implement, you hardly can make it more simple to use.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
UPDATE Version 0.9.9.1:
- Implemented damage modifiers that allow to block, reduce, increase or invert damage
- Fixed bugs with attacks that share the same instance
- Fixed rounding errors with very high spell damages
- Removed all memory leaks
- Fixed a bug with locust swarm ability
- Added additional support for customized damage tables with spell damage above 100%
- Updated the documentation and added a new testmap

To-Do:
- Allocate attacks from damage event
 
Review

It's not documented what values DamageType can be, so while what the variable refers to may be known, how to use it is not known.

SetEventDamage is completely pointless as the amount variable is already there

Prioritized damage is not supported (see PriorityEvent)

Settings are mixed with code. They should be located squarely in the documentation.

Many setting values are strange and... yea..

This practice is frowned upon nowadays
private constant boolean USE_SPELL_RESISTANCE_AUTO_DETECT = false

Put it in a separate lib and check for the lib.

This is not necessary
private constant integer SPELL_DAMAGE_REDUCTION_ITEM = 'brac'

These aren't necessary, it should always be automatic.
JASS:
        private constant real ETHEREAL_DAMAGE_FACTOR = 1.66
        private constant real BRACERS_SPELL_DAMAGE_REDUCTION = 0.33

Move to Trigger Refresh. It does much smarter trigger refreshing.
private constant real TRIGGER_CLEANUP_PERIOD = 60.0

This is definitely frowned upon
JASS:
        unit source
        unit target
        real amount
        integer DamageType
        real DamageEventTrigger

Those variable names can easily collide with variables from other systems (very vague, like amount). DamageEventTrigger is not documented. The variables should be readonly, unless you want users to be able to set source/target and so on, which is strange to say the least.

This function isn't documented
public function DealFixDamage takes unit source, unit target, real pureAmount returns nothing

This function isn't documented
public function GetEventDamageType takes real pureAmount returns integer

This is a very poor way of doing events
JASS:
    public function AddDamageHandler takes code func returns nothing
        local trigger t = CreateTrigger()

        call TriggerRegisterVariableEvent(t, "DamageEventTrigger", EQUAL, 1.0)
        call TriggerAddAction(t, func)

        set t = null
    endfunction

Just use the Event or PriorityEvent library and have people register to those. Don't create 1 trigger for each function and then do a variable event for each trigger... that is madness.

public function SetEventDamage takes real modifiedAmount returns nothing
The amount variable should be used for this. It is very confusing to have both. Furthermore, from what I see here, setting the amount variable would break everything.

Not documented
public function AllocateNextAttack takes nothing returns nothing

Poor initialization
private function Init takes nothing returns nothing

Always assume Vex order of initialization. The onInit method in a module will run before anything else, followed by onInit for structs, followed by initializers for libraries, followed by initializers for scopes.

Your initializer
initializer Init

Is practically the last thing that runs in the map. Do you really want users that have structs and are using this system to run before the system? : p

There is also no module support, making stuff take a little bit longer to do in this than in other DDS's


Documentation: Poor
API: Poor
Encapsulation: Poor
Design: Poor
Trigger Refreshing: Poor
Settings: Poor
Installation: Excellent+
Ease of Learning: Decent (due to undocumented features and some confusion)
Ease of Use: Decent (due to no module support)
Feature Support: Decent+ (supports most common DDS features)
User Code Readability: Decent
Extensibility: Poor

Poor: 0
Poor+: 1
Decent: 2
Decent+: 3
Excellent: 4
Excellent+: 5

Score: 14/60
 
Prioritized damage is not supported (see PriorityEvent)

Completely unnecessary.

This practice is frowned upon nowadays
private constant boolean USE_SPELL_RESISTANCE_AUTO_DETECT = false

No it's not.

Put it in a separate lib and check for the lib.

Don't do that.

This is not necessary
private constant integer SPELL_DAMAGE_REDUCTION_ITEM = 'brac'

It improves readability.

These aren't necessary, it should always be automatic.
JASS:
        private constant real ETHEREAL_DAMAGE_FACTOR = 1.66
        private constant real BRACERS_SPELL_DAMAGE_REDUCTION = 0.33
That's not true. http://www.hiveworkshop.com/forums/2296369-post46.html

Move to Trigger Refresh. It does much smarter trigger refreshing.
private constant real TRIGGER_CLEANUP_PERIOD = 60.0

Unnecessary.
 
If you want to deal a fixed amount of damage, do a fixed variable and an amount that will make the next attack, regardless of the amount of damage inflicted, deal the amount of damage stored in the amount variable. It's much simpler -.-.

And how does this
private constant integer SPELL_DAMAGE_REDUCTION_ITEM = 'brac'

Improve readability when you shouldn't be checking for a specific wc3 item anyways? It should all be auto. There is a specific way to do auto spell resistance checking that doesn't have a HUGE impact on performance ;o.

The incorrect way: call a spell resistance function to get the damage factor and so on, then apply the correct damage (fires the event 3 times, 1 for neg, 1 for calculate, 1 for apply actual damage as negative damage event can't kill unit correctly (bounty and etc gets lost)).

The correct way: apply some constant amount of damage to the target if the damage is negative. Pass the negative damage to the newly generated event. Calculate the factor in the new event and then change the damage to the correct damage that was carried over from old event. Disable firing of user events in the old event. This fires event 2 times, and only if the damage was negative.

private constant real TRIGGER_CLEANUP_PERIOD = 60.0

A trigger cleanup period isn't as good as the trigger refresh macro : \.

Prioritized damage is not supported (see PriorityEvent)

Make it optional like I did in my DDS Damage Event plugin... wow -.-

Not supporting it outright is lame. My stuff was almost deprecated and etc when someone came out with prioritized damage and I didn't have support for it. Quit being stubborn. If one lib supports it and one doesn't, which lib do you think the user is ganna choose?
 
The ability makes all spells deal negative damage. No damage can deal negative damage but spells. This means that if the damage is negative, the damage came from a spell.

The damage can only come from code if some sort of flag is set from the code. If the code sets that flag and deals damage, the damage must have come from code.

If the damage was not negative and the flag from code was not set, the damage must have come from some sort of physical attack.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Hi again,

first of all thanks @Nestharus and Cokemonkey11 for the review and suggestions. I take them serious and will overthink some things. At the moment I am rewriting the whole system and I will release a new version soon (maybe with a leading 1).

Now, according to some of the discussion points:

This practice is frowned upon nowadays
private constant boolean USE_SPELL_RESISTANCE_AUTO_DETECT = false

No it's not.

So, what now? :D

Put it in a separate lib and check for the lib.
These aren't necessary, it should always be automatic.

It is automatic, but not for items that have the spell damage reduction ability. Such an item would overwrite the damage type detector and therefore break the system, thats why you have to check for such items anyway.

As those items are unstackable, thats not a big deal, but think I will also make an additional library for multiple, stacking damage items (so that the user just have to put all his spell damage reducing items into an array). Its the only way stacking spell resistance can be achieved anyway, so its no disadvantage of the system.

Move to Trigger Refresh. It does much smarter trigger refreshing.

I will add this optional to the system.

Just use the Event or PriorityEvent library and have people register to those. Don't create 1 trigger for each function and then do a variable event for each trigger... that is madness.

I don't understand this... I don't create one trigger for each function. Its just one single trigger for all damage handler functions in total. They get then all called by one single variable event.

Thats basically the way it is done in Bribes system, and as I want to convert this system to GUI as well, I think I will stick with this solution.

Prioritized damage is not supported (see PriorityEvent)

Completely unnecessary.

Hm, what does prioritized damage exactly do, could you briefly explain to me?

If you want to deal a fixed amount of damage, do a fixed variable and an amount that will make the next attack, regardless of the amount of damage inflicted, deal the amount of damage stored in the amount variable. It's much simpler

Customized damage tables.

Is practically the last thing that runs in the map. Do you really want users that have structs and are using this system to run before the system? : p

I didn't knew that... But what problems can occure from that? I mean, is this a problem that can break the system or what are the problems?

Documentation: Poor

Come on, this is at least a Poor+ ;D
 
Hm, what does prioritized damage exactly do, could you briefly explain to me?

It fires the damage events in a certain order.
If you register a damage event with a priority of 0x7FFFFFFF, the code you're registering will always run before any other code registered to the event.

This is cool because it allows damage modification systems to run before the damage events are passed on to spells and map-specific scripts like a Floating-text damage system :D
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
I don't understand this... I don't create one trigger for each function. Its just one single trigger for all damage handler functions in total. They get then all called by one single variable event.

Damn, right now I see that Nes were right with this.^^
I fixed this a few days ago, but didn't upload an updated version yet.

If you register a damage event with a priority of 0x7FFFFFFF, the code you're registering will always run before any other code registered to the event.

Ok, but when I want to do this I can also add the damage handler functions in the desired sequence to the system, right? So if I want that the modifier function runs before the damage display function, I just add it first.
 
If you want to deal a fixed amount of damage, do a fixed variable and an amount that will make the next attack, regardless of the amount of damage inflicted, deal the amount of damage stored in the amount variable. It's much simpler -.-.

What?

And how does this


Improve readability when you shouldn't be checking for a specific wc3 item anyways? It should all be auto. There is a specific way to do auto spell resistance checking that doesn't have a HUGE impact on performance ;o.

Because his system was designed to work with ladder maps and ladder constants without any unnecessary changes. Perhaps he should have made it optional, but certainly checking for 'Brac' is less readable than a constant with a useful description.

The incorrect way: call a spell resistance function to get the damage factor and so on, then apply the correct damage (fires the event 3 times, 1 for neg, 1 for calculate, 1 for apply actual damage as negative damage event can't kill unit correctly (bounty and etc gets lost)).

The correct way: apply some constant amount of damage to the target if the damage is negative. Pass the negative damage to the newly generated event. Calculate the factor in the new event and then change the damage to the correct damage that was carried over from old event. Disable firing of user events in the old event. This fires event 2 times, and only if the damage was negative.

Try it yourself. (Or just download my test map since I did everything already)


A trigger cleanup period isn't as good as the trigger refresh macro : \.

Show me. Seed n triggers with a Gaussian distribution of [1,BUCKET_SIZE] units and compare framerates for values of n when BUCKET_SIZE = [5,50].

Show precisely what values of BUCKET_SIZE and n you'll need so that the refresh macro guarantees better performance than periodic cleanup.


Make it optional like I did in my DDS Damage Event plugin... wow -.-

Not supporting it outright is lame. My stuff was almost deprecated and etc when someone came out with prioritized damage and I didn't have support for it. Quit being stubborn. If one lib supports it and one doesn't, which lib do you think the user is ganna choose?

It's only lame if the feature in question is useful. And I'm going to call you out on this one too - show me when and who threatened to deprecate your system due to lack of priority events? I'm guessing you're the only person who gives two shits about it.

I think the user is going to choose the one that's better documented, has code they can understand, and is trivial to install in their map.
 
Saying that 3 trigger evaluations isn't as fast as 2, lol...

Stop being so arrogant and absurd. My test showed that one function call (with zero triggers fired) was significantly slower than registering the entire logic tree of DamageType and StructuredDD.

If you don't believe me open the test map and disable DamageType completely.
 
You still do this...

call UnitDamageTarget(u,u,DUMMY_DAMAGE,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_UNIVERSAL,null)

This, even though you disable the boolean, still runs the triggers -.-. The triggers still get evaluated.

How many triggers get evaluated? The onDamage and the dependencies. The code may not get evaluated inside DamageType (the thing is still run mind u, just code inside is skipped from if statement), but everything else does and the triggers are still evaluated.

How many evaluations will this mean? Well.. in mine, it's 2. In yours, who knows. The normal should be 3, 1 for UnitDamageTarget, 1 for original event, and 1 for apply damage. I do the apply damage in #2 with scaling to avoid that second UnitDamageTarget call. I also translate the stuff from #1 to #2 so that people have access to the actual damage that should be applied =).

It just seems like something got lost in our conversation. I was discussing how many triggers end up getting evaluated from the use of UnitDamageTarget. You were discussing I don't know what ; p, but your function still evaluates those triggers as it still uses UnitDamageTarget =).

edit
as for dealing fixed damage

set var = amount of damage
set doFixed = true
call UnitDamageTarget(some random amount)

onDamage
if doFixed
set amount = var
set doFixed = false

done
 
You still do this...

call UnitDamageTarget(u,u,DUMMY_DAMAGE,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_UNIVERSAL,null)

This, even though you disable the boolean, still runs the triggers -.-. The triggers still get evaluated.

Okay, look, dude..: I'm sorry if the test map wasn't clear enough.

Is this any better?

RtCpXkJ.png


zkLPBcH.png
 
lemme compare with what I'm doing really fast

This seems strange. I have a feeling that it has to do with the abilities, but the trigger evals appear to be less : |.

edit
My results

regular: 39 fps (UnitDamageTarget, all enabled)
resistance: 52 fps (Resistanced, Type disabled)
resistance standard: 39 fps (Resistance, Type enabled)
 
Last edited:
Level 3
Joined
Nov 30, 2012
Messages
30
I just use a 1.01 reducer ability,so I just need to reduce the negative damage by set life in a 0s timer when the unit's life larger than "real damage".
It may not so accurate,but it is simple and works well in my map.
 
Dang you coke, you had the wrong ability set in the test map, so I got wrong benchmarks >.>. It's no wonder ur benchmarks were so damn high... I spent all day optimizing my lib wondering how the heck your lib got ~63-64 fps on a .0025 timer...

Nope, I just checked. The ability ID's are set correctly in my test map.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Ok guys,

lets calm down a bit ;)

According to my tests too, the complete auto detect requires more performance (which is quite obvious). Its not much performance, as it can only be seen in very unrealistic and extreme scenarios, but: As it is only neccessary with a customized damage table for spell resistances above 100%, which most likely never happens in any map, it makes sense to give the user the free choice.

But this is more a design philosophy, so there is no "wrong" or "right" - thats what different systems with different approaches are for. I like the solution with the constant boolean, its just so easy for users the change.


According to the priority thing: My system already has priorities. The damage handler functions get executed in the sequence they were added to the system, which is imho an even more intuitive and easier way than declaring an extra priority variable. But I should mention this in the documentation, so that it gets clear.

In the next version I will upload, there is a RemoveDamageHandler function with which you can even dynamically remove (and re-add) damage handlers, so priorities could even be changed during runtime if you would like to do that. But I don't think anyone will ever use this :D

I just use a 1.01 reducer ability,so I just need to reduce the negative damage by set life in a 0s timer when the unit's life larger than "real damage".
It may not so accurate,but it is simple and works well in my map.

I did this too in the very beginning while developing the first version of this system and I can definitly tell you its a very bad solution. A batrider with 600 damage will allready deal 1% = 6 damage more than normaly, making it effectivly 606 damage. If you have custom spells with more damage, the error will become bigger.

There is really no reason why you should do it this way, I really don't recommend this. Using a 1.01 reducer ability over a 2.00 reducer ability has no advantages, only disadvantages.


@ Nes: Here is a quick Bug Report on your latest system. Damage modification seems to be broken and healthbars still flicker.

I found a way to completly avoid health bar flickering for a huge range (btw the first DDS system that uses this technique AFAIK), check my system if you want to solve it.
 
Last edited:
According to the priority thing: My system already has priorities. The damage handler functions get executed in the sequence they were added to the system, which is imho an even more intuitive and easier way than declaring an extra priority variable. But I should mention this in the documentation, so that it gets clear.

I'm on your side - but only because I think there are very limited cases where priority triggers are actually necessary. However, it can be a pain to ensure a proper order of addHandler calls if for example three libraries all have initializer functions. My solution in DamageType is to warn users in the documentation that any other library that uses addHandler on initialization should requires DamageType - this ensures that handler[0] is DamageType.handler. (You can begin to see why priority handlers might be useful if a map needs more than one of those)

Edit: Can you explain what you've done differently to fix the flickering health bars? My system doesn't have this issue, but I'm just curious.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
I'm on your side - but only because I think there are very limited cases where priority triggers are actually necessary. However, it can be a pain to ensure a proper order of addHandler calls if for example three libraries all have initializer functions. My solution in DamageType is to warn users in the documentation that any other library that uses addHandler on initialization should requires DamageType - this ensures that handler[0] is DamageType.handler. (You can begin to see why priority handlers might be useful if a map needs more than one of those)

Aha, ok that makes sense. What happens if two events have the same priority?

Edit: Can you explain what you've done differently to fix the flickering health bars? My system doesn't have this issue, but I'm just curious.

Your system doesn't have this issue, but only because you don't use a rescue ability at all, which is also the reason why the damage blocking in the shield system is so bugged and doesn't work properly. ;)
To do correct damage modifying (including blocking) you have to do that.

The idea to avoid hp flickering is some extra logic which streches the healthbar of the target to the length before the modification took place. With this technique, only reduced damage amounts above 15000 can cause flickering at all, which is much better than the standard technique.

Still not absolutly perfect though, but much better. In 99% of all maps you won't have so huge damages at all, and if so they have to appear very fast and frequent to cause flickering -> effectively solving the problem for all practical applications.
 
Nope, I just checked. The ability ID's are set correctly in my test map.

Yea, it was cuz I cnp'd DamageType again since your library was issuing wrong values and the output was only showing 0/-1 : \. In the background calculation, your library was showing 2. The correct value was 1.5.

Now given this, your library will probably end up showing all incorrect values again as the ability was changed to the correct id ; P.

edit
As for priority... consider 100 random structs. Some are supposed to apply damage at certain points. Are you going to order all 100 manually or would you rather they order themselves?

Let's say a struct that gives an enhancement to all previous damage bonuses applied? You'd need damage bonuses to run, then that struct. This is hardly easier for the user...

edit
You also denied doing 1 trigger per function and said that you just had 1 trigger for all... what's this then?

JASS:
    public function AddDamageHandler takes code func returns nothing
        local trigger t = CreateTrigger() //... this looks like ur making a full blown trigger to me every time u add a function

        call TriggerRegisterVariableEvent(t, "DamageEventTrigger", EQUAL, 1.0)
        call TriggerAddAction(t, func)

        set t = null
    endfunction

edit
you don't set event response variables correctly either

JASS:
        set target = GetTriggerUnit() 
        set source = GetEventDamageSource()
        set pureAmount = localDamage
        set targetId = GetHandleId(target)

I could easily break your system by doing UnitDamageTarget inside of a damage event.
 
Last edited:
Level 14
Joined
Dec 12, 2012
Messages
1,007
edit
You also denied doing 1 trigger per function and said that you just had 1 trigger for all... what's this then?

Yes, I already made a comment on this:

Damn, right now I see that Nes were right with this.^^
I fixed this a few days ago, but didn't upload an updated version yet.

The new version will have this fixed.

I could easily break your system by doing UnitDamageTarget inside of a damage event.

Yes I know :p

UPDATE Version 0.9.9.1:
- Implemented damage modifiers that allow to block, reduce, increase or invert damage
- Fixed bugs with attacks that share the same instance
- Fixed rounding errors with very high spell damages
- Removed all memory leaks
- Fixed a bug with locust swarm ability
- Added additional support for customized damage tables with spell damage above 100%
- Updated the documentation and added a new testmap

To-Do:
- Allocate attacks from damage event

I'm basically done with the rework of the system, I only have to implement the AllocateAttack function (at the moment it doesn't work).
 
Your system doesn't have this issue, but only because you don't use a rescue ability at all, which is also the reason why the damage blocking in the shield system is so bugged and doesn't work properly. ;)

Actually that's not why. There's just a logic problem in my code and I haven't had time to fix it. Before I do that though I want to do some extensive testing to the "rescue" ability and decide what to do about it.

To do correct damage modifying (including blocking) you have to do that.

Only if you want to prevent deathblows.

The idea to avoid hp flickering is some extra logic which streches the healthbar of the target to the length before the modification took place. With this technique, only reduced damage amounts above 15000 can cause flickering at all, which is much better than the standard technique.

I don't see where in your code you've done such logic. (Do you have a line number?)

Yea, it was cuz I cnp'd DamageType again since your library was issuing wrong values and the output was only showing 0/-1 : \. In the background calculation, your library was showing 2. The correct value was 1.5.

Now given this, your library will probably end up showing all incorrect values again as the ability was changed to the correct id ; P.

Yep, we've already talked about this -

http://www.hiveworkshop.com/forums/2289028-post41.html

but that's completely irrelevant to the performance measurement.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Only if you want to prevent deathblows.

Well, thats right. Then the blocking depends on the units max life and not on the user settings... If you like it this way, Ok.

I don't see where in your code you've done such logic. (Do you have a line number?)

The idea is simple:

  • Save the original health of the unit
  • Add the rescue item to the unit
  • Now increase the increased health of the unit only by the incoming damage -> As the health bar has a range from 0 to ~2000000, you can't notice the difference between for example 1571000 health before and 1570000 after a 1000 damage event. You will have to use GetWidgetLife before and after adding the rescue ability, so one function call more. But with this, the problem can be easily fixed.

The important thing is that the proportion of the health bar is the same during the time window between the damage and the after damage event, then the life change doesn't get visible.

If you use the damage modifier to deal higher damages than 15000 via trigger, you can btw. completly avoid flickering hp bars, even for damage amounts beyond 100000.
 
Level 3
Joined
Nov 30, 2012
Messages
30
I prefer 1.01 ability is better.
Because no unit will reduce 99% magic damage in genera map,I can easily deal with the negative damage that may add unit's hp.

When the unit's hp>event damage+0.405,I can give the real damage and check if the unit dead after 0s.If it alive,I set the unit's hp;else I would do nothing.

When the unit's hp<=event damage+0.405,because the real damge would be far bigger than event damage,I just give the real damage and I'm sure it will die.

In 2.00 situation,when a unit's maxhp<=event damage+0.405 but maxhp>the real damage,we can do nothing to make sure it's life is correct after 0s timer.

Maybe it's my mistake because I have no time to read your code seriously.I will check your latest code when I'm free.

I suggest "UnitHasItemOfType" can be replaced by "GetUnitAbilityLevel" to check the 'brac' Item's ability(I forgot the ability id).
And register AnyUnitDamageEvent with adding the DETECTOR ability needs only One filter function.You can take a look at "library UnitIndexer".
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
In 2.00 situation,when a unit's maxhp<=event damage+0.405 but maxhp>the real damage,we can do nothing to make sure it's life is correct after 0s timer.
...
I suggest "UnitHasItemOfType" can be replaced by "GetUnitAbilityLevel" to check the 'brac' Item's ability(I forgot the ability id).

You definitly don't understand how the system really works. Thats a) not true and would b) screw up the system.

Maybe it's my mistake because I have no time to read your code seriously.I will check your latest code when I'm free.

That would be a good idea ;)
 
Level 3
Joined
Nov 30, 2012
Messages
30
Well,I tried your map.You use an ability that add unit's maxhp to prevent the change of negative damage.I don't think it a good choice.If you make an ability concerned with unit's maxhp,such us "damage the unit by 3% of it's maxhp per second".The extra 100000 maxhp may cause bugs.I don't think remove the ability in 0s can avoid all of these bugs
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Ability 'Aegr' doesn't work in your system.

Which is quite logical if you read the documentation close, right?

Remove the spell damage reduction ability from the spell damage reduction items you use (runed bracers).

But you are right, I should mention explicitly that this applies not only for runed bracers but also for elunes grace (which are basically all items that have a spell damage reduction ability).

It is btw trivial easy to fix this (as with runed bracers). Just set DataH1 to 0.0 and DataE1 to 1.0 and apply a damage modifier for spell damage if a unit has this item.

Elunes grace is btw a bit nasty anyway, because if you have it together with runed bracers in you map, only one of them will work ,depending which was added last to the unit.
And this is not because of this system but because spell reduction abilities can never stack.

So its best to trigger this anyway, because its the only possibility to achieve stacking spell resistance items.


You could even use elunes grace to detect piercing damage in exacly the same way as I did in this system for spell damage. Unfortunatly (-1)*(-1) = 1, so we can't use both
techniques together.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
UPDATE Version 1.0.0.0:
- Completly reworked the whole system
- Increased performance dramatically
- Added a GUI Version of the system
- Implemented a function to allocate damage events from already running damage events
- Allocating damage events is implemented recursive, so users can allocate as many events as they want
- Implemented a function to dynamically remove damage handlers from the system
- Implemented a function to get a units life always correct, even when damage modifiers are used
- Implemented a function to get a units max life always correct, even when damage modifiers are used
- Implemented a function to set a units life always correct, even when damage modifiers are used
- Changed the way events are triggered and fixed a bug with this
- Moved from trigger actions to trigger conditions
- Added optional support for the TriggerRefresh library by Nestharus
- Reworked the system API, users can now set the damage amount directly without a function call
- Added documentation for all user-relevant functions
- Narrowed the window for the rescue ability to get applied
- Fixed bugs with negative damage events that share the same instance
- Fixed a bug with mortal spell and physical damage that run in the same instance
- Fixed a bug that could occure with zero damage events
- Proved bug-freeness for parallel damage events by brute forcing through all possibilities
- Updated the documentation and added a new testmap


So, finally this is the version on which I can put a leading "1". I basically reworked the whole system making it now extremly efficient and able to compete with the best systems.

While working on this update I also experimented quite much with other advanced DDS available and I basically found bugs in all of them. Thats why I decided to write a script that automatically generates all testcases to test of the system will work in every situation. Therefore, the testcases were generated via brute force with the following order (allocated in the same event):

unmodified physical damage - unmodified physical damage - ...
unmodified physical damage - reduced physical damage - ...
unmodified physical damage - increased physical damage - ...
unmodified physical damage - nulled physical damage - ...
unmodified physical damage - unmodified spell damage - ...
unmodified physical damage - reduced spell damage - ...
unmodified physical damage - increased spell damage - ...
unmodified physical damage - nulled spell damage - ...
unmodified physical damage - unmodified code (physical) damage - ...
unmodified physical damage - reduced code (physical) damage - ...
unmodified physical damage - increased code (physical) damage - ...
unmodified physical damage - nulled code (physical) damage - ...
... and so on


The tree of damage combinations was evaluated till a depth of four, leading to 80000 different test cases in total, which the system passed all.

Of course there can never be a guarantee that there are no bugs, but especially the damage handling of modified damage works really well as this was the only DDS in which I couldn't find anymore bugs.

I hope you enjoy it!

Greetings,
lfh
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
You've found bugs with StructuredDD?

No, I refered only to DDS that allow damage modification. Your system works fine for detection, at least I couldn't find any bugs. Good job. :thumbs_up:

I also noticed that I deleted an important line in Version 1.0.0.0 for debugging reasons and forgot to put it back^^.

Therefore I made a mini update.

Version 1.0.0.1:
- Fixed a minor bug with allocated attacks
 
Last edited:
Top