Wc3C.net

Wc3C.net (http://www.wc3c.net/forums.php)
-   Scripts (http://www.wc3c.net/forumdisplay.php?f=737)
-   -   DamageEvent & DamageModifiers (http://www.wc3c.net/showthread.php?t=108009)

Anitarf 10-21-2009 02:54 PM

DamageEvent & DamageModifiers
 
This is a compilation of libraries intended to replace the damage detection and prevention engine ADamage. Originally, this was intended to be merely the next version of ADamage, but some conceptual changes such as moving from damage prevention to more general damage modification made it difficult to maintain backwards compatibility, so I decided to make it a new resource instead which could provide a more general, robust and modular damage detection and modification framework than ADamage. Also, I know some people will like that fact that I gave it a new name.

The resource is split into two libraries:
  • DamageEvent, a damage detection engine.

    If you are already using another damage detection system in your map, DamageEvent will interface with it instead of creating its own damage detection triggers to avoid wasting performance. Currently supported damage detection systems are IDDS and LLDD.
    Expand DamageEvent:

  • DamageModifiers, an optional addon that allows you to modify damage.

    Two versions of DamageModifiers are provided, one that uses Table and one that uses AutoIndex.
    DamageModifiers can use either xepreload or AbilityPreload to preload the ability it uses.
    Expand DamageModifiers (Table version):
    Expand DamageModifiers (AutoIndex version):

Tot 10-21-2009 03:05 PM

This is a really cool system, but a bit complecated!!

can you please include a demo-map?

would be nice

nichts 10-21-2009 04:04 PM

now that there are static ifs and optional library requirements:
why don't you merge the Table and the AutoIndex version into one library - that would make later switching much easier (and they don't seem to be that different from each other anyway)

Anitarf 10-21-2009 05:01 PM

Well, optional requirements are just that, optional. In this case, however, the requirement isn't optional: either Table or AutoIndex, one of those requirements must be there. Furthermore, I need to declare globals depending on which library is used and I'm not sure I can do that in static ifs.

I suppose I could use static ifs for alternate versions of DamageEvent, though.

Anachron 10-22-2009 07:42 AM

Collapse JASS:
//*  Damage modifiers are implemented as structs that extend the
//*  system's DamageModifier struct. A damage modifier is created
//*  for a specific unit and can modify both the damage that the
//*  unit deals and receives (using the damage and damaged methods
//*  respectively, see the interface defined at the end of the
//*  calibration section).
Shouldn't it be UNIT_TYPE specific?

Further on, there is no example code.
No matter how good your documentation is, an example code is always on top when it comes to user friendly.

Some good stuff you did:
- Automatic Trigger Refresh
- Versions for Table, AutoIndex
- Versions for IDDS, LLDD

Edit:
Collapse JASS:
// In wc3, damage events sometimes occur when no real damage is dealt,
        // for example when some spells are cast that don't really deal damage,
        // also some systems use very small damages to test for valid targets,
        // so this system will only consider damage events where damage is
        // higher than this threashold value.
        private constant real DAMAGE_THRESHOLD = 0.05
Damage event bigger then DAMAGE_THRESHOLD AND smaller then 0 should be considered.

Edit2:
Collapse JASS:
public struct shield extends shield_template
        private ShieldRelay relay
        
        static method create takes unit u, integer priority returns shield
Is there any reason why you wrote that public to the struct, but not the public to the static?


Edit3:
Collapse JASS:
        method damaged takes unit damageSource, real damage returns real //returned real says how much damage to prevent

What do you mean with prevent? Elaborate please.

Anitarf 10-22-2009 10:01 AM

Quote:

Originally Posted by Anachron
Shouldn't it be UNIT_TYPE specific?

No.

Quote:

Further on, there is no example code.
No matter how good your documentation is, an example code is always on top when it comes to user friendly.
There is an example in the DamageModifiers documentation.

Quote:

Damage event bigger then DAMAGE_THRESHOLD AND smaller then 0 should be considered.
Why?

Quote:

Is there any reason why you wrote that public to the struct, but not the public to the static?
Methods without a prefix are already considered public.

Quote:

What do you mean with prevent? Elaborate please.
No. Read the documentation.

Anachron 10-22-2009 04:35 PM

Quote:

No.

Why not? I would rather set the data for a unittype instead for every single unit.

Quote:

There is an example in the DamageModifiers documentation.
Ehrm,... You can't call that example.

Quote:

Why?
Because else it doesn't register negative damage events?

Quote:

Methods without a prefix are already considered public.
Sure, but structs without a prefix are already considered as public too.

Quote:

No. Read the documentation.
I did but still its unclear for me.

Rising_Dusk 10-22-2009 05:30 PM

Quote:

Originally Posted by Anachron
Why not? I would rather set the data for a unittype instead for every single unit.

I would rather, as a user, have control at the unit level than at the unittype level.
Quote:

Originally Posted by Anachron
Because else it doesn't register negative damage events?

No DDS supports negative damage, nor should they. If you want to heal a unit, negative damage is not the way to do it.

Anitarf 10-22-2009 05:30 PM

Quote:

Originally Posted by Anachron
Why not? I would rather set the data for a unittype instead for every single unit.

I wouldn't. How am I supposed to make a buff that modifies incoming damage if I can't apply it to a specific unit?

Quote:

Ehrm,... You can't call that example.
I just did.

Quote:

Because else it doesn't register negative damage events?
Okay, I'll fix it when you manage to produce a negative damage event.

Quote:

Sure, but structs without a prefix are already considered as public too.
There is a difference between a struct with a public prefix and a struct without one. There is no such diference when it comes to struct methods.

Quote:

I did but still its unclear for me.
My point was that I don't care, the documentation clearly states that I do not intend to provide any further support for that library, this includes answering your vague questions about it.

Anachron 10-23-2009 07:45 AM

To Dusk:


Quote:

I would rather, as a user, have control at the unit level than at the unittype level.

You got me wrong. You should be able to handle it on unit level too, but maybe the user doesn't want to just set all the data for every single unit, so you can add a small (optional) addon (module) to store stuff in tables and just set the unit data on entering the playable map area to the data you saved to be basic for any unit of that type?

Quote:

No DDS supports negative damage, nor should they. If you want to heal a unit, negative damage is not the way to do it.
Is there any other way as doing an triggered heal that has a damagetype/weapontype? I for example use it (native UnitDamageTarget) to heal units too. That allows me to even block heals from units.




to Anitarf:


Quote:

I wouldn't. How am I supposed to make a buff that modifies incoming damage if I can't apply it to a specific unit?
Please read the first answer to Rising_Dusk.

Quote:

I just did.
Its not looking as helping anyone who got questions, really.

Quote:

Okay, I'll fix it when you manage to produce a negative damage event.
Collapse JASS:
native UnitDamageTarget             takes unit whichUnit, widget target, real amount, boolean attack, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns boolean

Quote:

There is a difference between a struct with a public prefix and a struct without one. There is no such diference when it comes to struct methods.
Which different? I didn't heard of any. (Wanna learn! :))

Quote:

My point was that I don't care, the documentation clearly states that I do not intend to provide any further support for that library, this includes answering your vague questions about it.
If you don't care about users to get to know to your system who need help in your stuff, how can you submit it then? Its a insolence to provide such advanced stuff without further help.


Earth-Fury 10-23-2009 08:31 AM

Quote:

Originally Posted by Anachron
Which different? I didn't heard of any. (Wanna learn! :))

Quote:

Originally Posted by Anitarf
Read the documentation.


Though he was referring to his system's documentation, I am using that quote to refer to vJass' documentation.

If you want to learn, start with English.

Anachron 10-23-2009 08:43 AM

Quote:

Though he was referring to his system's documentation, I am using that quote to refer to vJass' documentation.
I am quite sure you learned vJass by getting help from other people, not from reading the documentation.

Quote:

If you want to learn, start with English.
Its not my fault that in school English isn't teached well, I already in a good english course but still my english isn't that good as yours.

So what about it?

Anitarf 10-23-2009 08:47 AM

Quote:

Its not looking as helping anyone who got questions, really.
If you have specific questions, then ask away.

Quote:

Collapse JASS:
native UnitDamageTarget             takes unit whichUnit, widget target, real amount, boolean attack, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns boolean

Have you tested that? When I tried that with a negative value, I got a damage event with a total of 0 damage. Unless you can actually produce a negative damage event in a test map, shut up.

Quote:

If you don't care about users to get to know to your system who need help in your stuff, how can you submit it then? Its a insolence to provide such advanced stuff without further help.
Again, read the documentation. It clearly states that that is a compatibility library. I don't need to provide support for that, because noone is supposed to keep using it, it is just meant to provide backwards compatibility to old resources. If you really care so much about it, go read the ADamage documentation.

Anachron 10-23-2009 08:52 AM

Quote:

Have you tested that? When I tried that with a negative value, I got a damage event with a total of 0 damage. Unless you can actually produce a negative damage event in a test map, shut up.
You could also use any negative attack (UnitDamage - 1000), I am quite sure it will heal in every testmap.

Earth-Fury 10-23-2009 08:55 AM

Quote:

Originally Posted by Anachron
You could also use any negative attack (UnitDamage - 1000), I am quite sure it will heal in every testmap.

Quote:

Originally Posted by Anitarf
Unless you can actually produce a negative damage event in a test map, shut up.


grim001 10-23-2009 09:32 AM

Ok, I have spent some time looking at this, and I think it's pretty cool. I will probably use it myself for some things eventually, and not need to finish DamageMod, since I really hate working on that thing.

Questions/Suggestions:
  • I see that you've made improvements in the area of damage prevention. I'm not going to bother to test that it works perfectly, because that is very time consuming, and I'm sure you've tested it quite a lot. Do you know if there's any possible situation where it can still fail, other than receiving damage that exceeds the life bonus value?
  • You don't keep the unit's life proportional when the bonus HP is added, which make life bar flickering an issue in some situations. Is there any way to fix that without introducing other problems?
  • Is it safe to use UnitDamageTarget within the damage detection callbacks? If not, you could provide a safe version.
  • I'd personally prefer if the interface events were named onDamage, onDamaged, it reflects the naming scheme that's been commonly used for events and looks prettier.

Criticisms:
  • You should have the object merger call enabled by default, that is pretty much how all other scripts have been doing it. Also, Earth-Fury recently told me something about how the ObjectMerger works that I have confirmed through testing. If you save the map with the object merger call, disable it, then save the map again, the ability will not exist. You have to run object merger call, then close and re-open the map for it to become permanent. You might want to update your documentation to reflect that.
  • It is nice to provide compatibility, but there is such a thing as going overboard. The number of scripts here is going to confuse and scare away some people. If you think about it, versions of DamageEvent do not need to exist for IDDS and LLDD. People who have those scripts in their library and don't switch to DamageEvent probably prefer that syntax, and it would be lame to use two different methods of damage detection in one map.
  • What is needed instead are IDDS and LLDD versions of DamageModifiers. In this case, you can even use static ifs to make it all into one script, which optionally supports either DamageEvent, IDDS, or LLDD. It would not throw the proper compile error if you lacked any damage detection system, but that is less confusing than the multitudes of scripts that exist right now.
  • Although Table is a little bit of a syntax upgrade for end users, it is not much of a boon in system making, now that it doesn't serve the purpose of consolidating gamecaches. If you turn your table version into a hashtable version, you can then make DamageModifiers use hashtables by default, and optionally utilize AutoIndex if it's in the map.
  • A delay is created the first time the extra life ability is used. You can optionally require AbilityPreload to preload the ability.
  • The final result would be one version of DamageEvent, and one version of DamageModifiers that optionally requires DamageEvent, IDDS, LLDD, AutoIndex, and AbilityPreload.

About using extends:
It is the truth that many people will avoid this system just because they either don't like or don't understand using extends. But aside from that, there is the legitimate problem that you can't extend more than one interface. That means you can't mix your damage/damaged events with structs that extend other interfaces, which would be a very useful thing to do. Imagine mixing those events with a struct extending from a buff system, for example.

It used to be impossible to do anything about that, but now that modules have become more powerful, something can be done. First you would have to port the damage modifiers over to using function interfaces rather than extends, which will be preferable and more understandable for many people to use. It would then be possible to create a module which restores the current functionality, with the added benefit of not needing to extend anything.

Well, how do you port the current functionality to function interfaces? The way you are currently using extends is to provide three things to the user: a way to associate damage/damaged functions with units, a way to associate priorities with those functions, and a way to facilitate attaching data to that specific DamageModifier. So what if you do it this way?:

Expand JASS:

Say you ported it to this setup, how do you restore the current functionality using a module? Well, it would be best to let me handle that, since I am making a system which creates modules for this sort of thing, and supports every approved damage detection system. If you want to discuss how it works you can find me on IRC. But suffice to say it would look something like this for the user:
Expand JASS:

Anitarf 10-23-2009 03:14 PM

Quote:

Originally Posted by grim001
  • I see that you've made improvements in the area of damage prevention. I'm not going to bother to test that it works perfectly, because that is very time consuming, and I'm sure you've tested it quite a lot. Do you know if there's any possible situation where it can still fail, other than receiving damage that exceeds the life bonus value?

As far as I know, the code accounts for all eventualities (aside for, as you mentioned, the bonus hp ability being inadequate). I haven't tested all possible cases systematically, but I have tested several complicated situations and the code performed as expected (although I had to spend some time figuring out myself what exactly to expect).

Quote:

  • You don't keep the unit's life proportional when the bonus HP is added, which make life bar flickering an issue in some situations. Is there any way to fix that without introducing other problems?

That's one compromise I had to make in order to ensure that players get bounty correctly whenever the damage is supposed to kill a unit. The system always keeps the unit's life at the correct absolute value, rather than a relative value, to make sure that when the damage resolves, the unit gets killed by the original damage packet no matter how much the damage was increased/decreased; this also simplifies the rest of the code, making it easier to ensure robustness in other cases.

You will only notice the flickering regularly if most of the damage packets in the map are higher than the unit's max hp, which is the only case when the bonus hp ability is used.

Quote:

  • Is it safe to use UnitDamageTarget within the damage detection callbacks? If not, you could provide a safe version.

It is safe. The system can accept additional damage packets both before and after the original damage packet resolves.

Quote:

  • I'd personally prefer if the interface events were named onDamage, onDamaged, it reflects the naming scheme that's been commonly used for events and looks prettier.

You're right, I will change this.

Quote:

  • You should have the object merger call enabled by default, that is pretty much how all other scripts have been doing it. Also, Earth-Fury recently told me something about how the ObjectMerger works that I have confirmed through testing. If you save the map with the object merger call, disable it, then save the map again, the ability will not exist. You have to run object merger call, then close and re-open the map for it to become permanent. You might want to update your documentation to reflect that.

My, mistake, it was supposed to be enabled but I forgot when copying it to the post. I didn't know about the object merger issues, never experienced the problem. I'll fix that too.

Quote:

  • It is nice to provide compatibility, but there is such a thing as going overboard. The number of scripts here is going to confuse and scare away some people. If you think about it, versions of DamageEvent do not need to exist for IDDS and LLDD. People who have those scripts in their library and don't switch to DamageEvent probably prefer that syntax, and it would be lame to use two different methods of damage detection in one map.

Well, my concern are public resources. A user could download two spells, one that uses IDDS and one that uses DamageEvent. From the JESP manifest onward we have been striving to write spells and systems that can be used even by people who lack the skill to modify them, so there's no guarantee that our user could edit either of these two spells so they would use only one DD system.

The idea was that in such cases, DamageEvent could interface with the other damage detection system instead of wasting resources with it's own damage event trigger. I agree that the method of accomplishing this (having three different versions of the library) is awkward and I intend to change it to a single library with optional requirements instead.

Quote:

  • What is needed instead are IDDS and LLDD versions of DamageModifiers. In this case, you can even use static ifs to make it all into one script, which optionally supports either DamageEvent, IDDS, or LLDD. It would not throw the proper compile error if you lacked any damage detection system, but that is less confusing than the multitudes of scripts that exist right now.

The problem is that it's DamageEvent that requires DamageModifiers, not the other way around. That's because DamageEvent calls DamageModifiers first, before any other response, and DamageModifiers then returns the modified damage back to the damage detection system. It's a two-way communication initiated by the damage detection system, so DamageModifiers can only work with DamageEvent or a modified third party DD system.

Quote:

  • Although Table is a little bit of a syntax upgrade for end users, it is not much of a boon in system making, now that it doesn't serve the purpose of consolidating gamecaches. If you turn your table version into a hashtable version, you can then make DamageModifiers use hashtables by default, and optionally utilize AutoIndex if it's in the map.

Well, I like using Table in lieu of native hashtables because it's a lot more readable, making it easier for me to develop and maintain code. However, having a single version of DamageModifiers instead of two might be worth the change, especially since maintaining a single copy of the library is easier in the long run; I'll think about that.

Quote:

  • A delay is created the first time the extra life ability is used. You can optionally require AbilityPreload to preload the ability.

Agreed, will add that.

Quote:

About using extends:
It is the truth that many people will avoid this system just because they either don't like or don't understand using extends. But aside from that, there is the legitimate problem that you can't extend more than one interface. That means you can't mix your damage/damaged events with structs that extend other interfaces, which would be a very useful thing to do. Imagine mixing those events with a struct extending from a buff system, for example.
I think the first statement is a poor argument; it is after all also the truth that many people will avoid this system because it's in vJass and not GUI.

I can see the problem with extends, however, could you not use delegate to work around that? I've seen some people have been enthusiastic recently about the module-based coding style, but I still prefer the multiple struct approach myself. A damage modifier, for example, makes more sense to me as a struct itself (which can then be a member or even a delegate of other structs) than a property/module of any struct. I don't think either approach is functionally superior so mine should be a perfectly valid stylistic choice and I'd like to keep it as it is, unless you disagree.

Karawasa 10-23-2009 11:52 PM

Quote:

Originally Posted by Anitarf
Have you tested that? When I tried that with a negative value, I got a damage event with a total of 0 damage. Unless you can actually produce a negative damage event in a test map, shut up.


If you want a map go play Element TD. You can heal units with negative damage; that's exactly how the healing creeps work.

Rising_Dusk 10-23-2009 11:53 PM

You really shouldn't do that.

grim001 10-24-2009 02:51 AM

Quote:

Originally Posted by Anitarf
That's one compromise I had to make in order to ensure that players get bounty correctly whenever the damage is supposed to kill a unit.

Would it be possible to only do this when you know the damage will be fatal?

Quote:

Originally Posted by Anitarf
The problem is that it's DamageEvent that requires DamageModifiers, not the other way around.

However, you can change the design a bit to get around this by calling RunDamageModifiers from within DamageModifiers using a function registered with any damage detection system. The only problem would be that the final modified damage value wouldn't be passed through to other events, however how can correct that for IDDS by using the SetDamage function it includes. LLDD relies on the native event responses, but you could make a function that returns the modified damage value.

Quote:

Originally Posted by Anitarf
I think the first statement is a poor argument; it is after all also the truth that many people will avoid this system because it's in vJass and not GUI.

It's not an argument; it annoys me that people fear extends. But making it understandable for a wider audience can only be considered an upside. I found the function interface example from my last post to be pretty elegant, too.

Quote:

Originally Posted by Anitarf
I can see the problem with extends, however, could you not use delegate to work around that?

No, delegate can't be used for event handling. If you put a delegate DamageModifier struct inside another struct, that doesn't accomplish anything useful. You still can't move the events into the parent struct, they still need cyclical references to access data from one another.

Quote:

Originally Posted by Anitarf
I've seen some people have been enthusiastic recently about the module-based coding style, but I still prefer the multiple struct approach myself.

Extends is not supposed to be used just to add events to units, it is for specializations of object-types: for example, a specific kind of buff. (It took me a while to realize this, but there is a reason some of my old interface-based systems ever saw the light of day, it is because they were so impractical to use.) It absolutely makes sense to have events from multiple sources within one struct. Take this for example:
Expand JASS:
Without modules, the BuffSystem would have to support periodic events and damage detection on its own, which is not modular at all. In fact it's horrible and leads to 2000 line monolithic systems. If you thought that separate structs were a good idea, why did you include damage detection support in ABuff? You could have used a separate struct for those events.

Here's what it would have to look like without modules (this is painful to code):
Expand JASS:
At 32 lines vs 54 lines (excluding whitespace), it winds up taking almost twice as many lines of code.

Quote:

Originally Posted by Anitarf
A damage modifier, for example, makes more sense to me as a struct itself (which can then be a member or even a delegate of other structs) than a property/module of any struct.

A damage modifier is not anything but a unit event that returns a value. Taking this position to the extreme, we would see that any event can be handled using an interface. Why is it that no one uses inheritance for things that can be handled with function interfaces? It's because example A makes way more sense than example B:

Example A:
Expand JASS:
Example B:
Expand JASS:
The above example is lame, because it shows the improper use of extends just to bind events to a unit. Systems that do this become a pain in the ass to use in conjunction with other systems that do the same thing because they are mutually exclusive from the same struct.

Anyway, whether you want to change the basic way the system works is up to you, it is still approvable either way once the other issues are dealt with.

Anitarf 10-24-2009 06:02 PM

Quote:

Originally Posted by grim001
Would it be possible to only do this when you know the damage will be fatal?

I'd need to change how the core works. Right now, it doesn't care if the damage will be fatal at all, it just modifies the unit's life by the damage modifier and makes sure it remembers if there are any leftovers (since it can't set the life above the unit's maxlife or below 0.406 which would kill the unit), if another damage event occurs in response then those leftovers are carried over and added to the new event's modifier. It also keeps track of the total damage dealt, if that exceeds the unit's max life then the survival ability is added. This explanation is too long. it just makes sure the modifiers are applied to the unit's life correctly without killing the unit in the process. Actually tracking what the unit's life should be and whether the damage will kill it would be a lot more complicated and less robust. It probably is technically possible, but I really don't want to go down that road.

Quote:

However, you can change the design a bit to get around this by calling RunDamageModifiers from within DamageModifiers using a function registered with any damage detection system. The only problem would be that the final modified damage value wouldn't be passed through to other events, however how can correct that for IDDS by using the SetDamage function it includes. LLDD relies on the native event responses, but you could make a function that returns the modified damage value.
That would be an uglier solution in my opinion. I want the modifiers to be distinct from the regular event responses, rather than just being another one of them, which brings me to your next point:

Quote:

No, delegate can't be used for event handling....
Quote:

Extends is not supposed to be used just to add events to units,...
Hey, I agree, but:
Quote:

A damage modifier is not anything but a unit event that returns a value.
And has a priority. And the returned value modifies the damage. It's a lot more complicated than just a simple event listener; if you want that then use DamageEvent. I consider DamageModifier much more a part of an event than a response to it, and a more complicated object than a mere response, that's why responses are a function interface and modifiers are an interface.

Besides, there's no functional difference, it's just syntax. You could still easily write a wrapper library that would provide a modifyIncomingDamage module and a modifyOutgoingDamage module while using DamageModifiers internally.


Edit: Okay, I fixed up DamageEvent, now it's a single library. I still need to consider whether to join the two versions of DamageModifiers in a similar fashion, when I decide that I'll also fix up the other errors (ability preloading, objectmerger call, method names). Also, I removed the ADamage compatibility library, I'll post it in the ADamage thread instead once this gets approved and that gets graveyarded.

grim001 10-25-2009 12:20 AM

Quote:

Originally Posted by Anitarf
Actually tracking what the unit's life should be and whether the damage will kill it would be a lot more complicated and less robust. It probably is technically possible, but I really don't want to go down that road.

Well, I don't blame you, I tried to do that with DamageMod and it was extremely difficult, and I never quite got it working perfectly.

Quote:

Originally Posted by Anitarf
You could still easily write a wrapper library that would provide a modifyIncomingDamage module and a modifyOutgoingDamage module while using DamageModifiers internally.

It is not quite possible. It's because DamageModifier structs are analogous to a specific-unit-event, whereas the function interface setup is analogous to an any-unit-event. It would require vex to add onCreate, allow onCreate within modules, and allow onDestroy within modules. It would also require over twice as much code and an extra TriggerEvaluate per event.

Like I said, I can't force you to change it, but I don't see any situation where it is more elegant, I only see lots of situations where it is more of a hassle, and usually takes twice as much typing.

Anyway, let us know once you've made all the changes you want to make.

Anitarf 10-26-2009 12:25 AM

I ended up deciding to keep two versions of DamageModifiers for now, I might merge them in the future though if I find more reasons to do it. I otherwise fixed the rest of the small issues DamageModifiers had (method names, ability preloading, objectmerger calls) as well as adding some xedamage support to DamageEvent (it now automatically ignores xedamage's dummy events).

Quote:

Originally Posted by grim001
It is not quite possible.

I don't really know since I didn't look much into modules yet, but I got the impression from your first post that you could do it if I were using the function interface format (function AddOnDamageModifier(unit, DamageFunc, priority) --> integer). Since I know it's possible to write a wrapper library that translates my interface to a function interface I assumed making the module would be possible with the current syntax as well.

grim001 10-26-2009 02:58 AM

Well, now that I think about it... even as function interface, it still works as a specific unit event. So an automatically functioning module isn't possible either way without those additional module features.

It'd still take an extra triggerevaluate (DamageMod instance method --> module static method --> instance method) compared to the function interface approach (module static method --> instance method), but oh well.

So if you're done with all the changes you want to make, I'll try to look at it tonight...

grim001 10-26-2009 11:56 AM

OK, I tested it and everything works fine, I only have two comments:
  • You use xe's preload function in a struct onInit method, so the preloading unit won't exist yet. This is more like a problem with xe, so I made a post in the xe thread asking for vex to fix it.
  • I think it would be better to merge the Table and AutoIndex versions together, although it would be nice if "requires libraryA or libraryB" was possible.

Anyway, I think this is ready for approval now, so I'll approve it and graveyard ADamage.

Mr.Malte 11-01-2009 08:21 PM

I use this in a starcraft map and make protoss shields with it.
But the function that blocks the damage only fires when the unit has already been attacked one time.
here is the function:

Collapse JASS:
scope Energyshield initializer init
    function onDamage takes unit damaged, unit source, real damage returns nothing
        local real angle
        local unit u
        local integer ID = 'e001'
        
        if Basics_GetUnitRace(damaged) != PROTOSS or GetUnitState(damaged,UNIT_STATE_MANA) < 2.5 or IsUnitPaused(damaged) then
            return
        endif
        
        set angle = Atan2(GetUnitY(source)-GetUnitY(damaged),GetUnitX(source)-GetUnitX(damaged))
        if damage > GetUnitState(damaged,UNIT_STATE_MANA) then
            call SetUnitState(damaged,UNIT_STATE_LIFE,GetUnitState(damaged,UNIT_STATE_LIFE)+damage-GetUnitState(damaged,UNIT_STATE_MANA))
        else
            call SetUnitState(damaged,UNIT_STATE_LIFE,GetUnitState(damaged,UNIT_STATE_LIFE)+damage)
        endif
        call SetUnitState(damaged,UNIT_STATE_MANA,GetUnitState(damaged,UNIT_STATE_MANA)-damage)
    
        // GetUnitZ + Units fly height
        if GetUnitZ(source) - GetUnitZ(damaged) > 200. then
            set ID = 'e002'
        endif
        
        if IsUnitType(damaged,UNIT_TYPE_MECHANICAL) or IsUnitType(damaged,UNIT_TYPE_STRUCTURE) then
            set u = CreateUnit(Player(12),ID,GetUnitX(damaged)+40.*Cos(angle),GetUnitY(damaged)+40.*Sin(angle),angle*bj_RADTODEG)
            call SetUnitScale(u,1.2,1.2,1.3)
            call SetUnitFlyHeight(u,40.,0.)
        else
            set u = CreateUnit(Player(12),ID,GetUnitX(damaged)+26.*Cos(angle),GetUnitY(damaged)+26.*Sin(angle),angle*bj_RADTODEG)
        endif
        call UnitApplyTimedLife(u,'BTLF',.8)
    endfunction
    
    private function init takes nothing returns nothing
        call RegisterDamageResponse(onDamage)
    endfunction
endscope

Anitarf 11-01-2009 08:42 PM

Your code doesn't work the first time because you can't set a unit's hp above it's max hp, so when you try to increase the unit's life to nullify the damage the life doesn't get increased and so the damage isn't "prevented" like you think it should be.

This is only one out of many problems we encounter when trying to prevent damage; that's precisely why I wrote the DamageModifiers library, so it handles all the nuances of damage prevention for you. You should use DamageModifiers instead of a DamageEvent response.

Sinnergy 11-03-2009 10:22 PM

perfect.
Just a question, do I need to trigger all damage via trigger when using the system?

Anitarf 11-03-2009 10:47 PM

Quote:

Originally Posted by Sinnergy
Just a question, do I need to trigger all damage via trigger when using the system?

No. You can deal damage with attacks and spells as well.

Sinnergy 11-04-2009 01:00 AM

So does the system has the ability to detect attack damage and spell damages?

grim001 11-04-2009 01:07 AM

It registers damage detection triggers, of course it can detect the amount of damage dealt. But it doesn't differentiate between spells and attacks.

Mr.Malte 11-07-2009 01:04 PM

edit: Can I also prevent the damage differently than creating structs for each unit?
edit 2: Can't you make somtehing like this:

Collapse JASS:
// Gets data from events like GetEventDamage and adds the survival ability e.g
// if necessary
function ModifyDamage takes unit who, real byValue returns nothing

which can be only used in damage events?

But coded by you (used some map-specific funcs. Also I didn't all the research and don't know what to check)

Anitarf 11-07-2009 02:01 PM

No.

grim001 11-07-2009 02:42 PM

Quote:

Originally Posted by Mr.Malte
Can't you make somtehing like this:


Making it work that way is actually really difficult and hasn't been done flawlessly before. Maybe sometime in the future.

Mr.Malte 11-07-2009 05:40 PM

But this:

Collapse JASS:
library ModifyDamage requires TimerUtils
    //! external ObjectMerger w3a AIl1 DMsa anam "LifeBonus" ansf "(DamageModifiers)" Ilif 1 100000 aite 0
    private struct DamageTaken
        real healAfterwards = 0.
        unit who
    endstruct
    private function AfterDamage takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local DamageTaken a = GetTimerData(t)
        call ReleaseTimer(t)
        if GetUnitAbilityLevel(a.who,'DMsa') > 0 then
            call UnitRemoveAbility(a.who,'DMsa')
        endif
        call SetUnitState(a.who,UNIT_STATE_LIFE,GetUnitState(a.who,UNIT_STATE_LIFE)+a.healAfterwards)
    endfunction
    function ModifyDamage takes real mod returns nothing
        local unit damaged = GetTriggerUnit()
        local real damage = GetEventDamage()
        local real life = GetUnitState(damaged,UNIT_STATE_LIFE)
        local real maxLife = GetUnitState(damaged,UNIT_STATE_MAX_LIFE)
        local timer t
        local DamageTaken a
        
        if life < damage and life - damage + mod > 0.405 then
            call BJDebugMsg("Prevent Death")
            set a = DamageTaken.create()
            set a.who = damaged
            set a.healAfterwards = damage - mod
            call UnitAddAbility(damaged,'DMsa')
            set t = NewTimer()
            call SetTimerData(t,a)
            call TimerStart(t,0.00,false,function AfterDamage)
        endif
        
        if life + mod > maxLife then
            set a = DamageTaken.create()
            set a.who = damaged
            set a.healAfterwards = life + mod - maxLife
            set t = NewTimer()
            call SetTimerData(t,a)
            call TimerStart(t,0.00,false,function AfterDamage)
        else
            call SetUnitState(damaged,UNIT_STATE_LIFE,life+mod)
        endif

    endfunction
endlibrary

works perfectly well (ive tested it) and replaced my damage-reduction by hand easily.
What makes the thing complicated?
Did I forget anything?

grim001 11-07-2009 07:01 PM

I don't feel like getting into the details of how damage modification systems can fail, but there are about a dozen different ways. It gets more complicated if you allow it to increase damage dealt, keep bounties and kill triggers working correctly, avoid life bar flicker, and be called an unlimited number of times on the same unit.

Fire-Dragon-DoL 11-08-2009 04:52 PM

I'm having a problem with the external call (that should come in compile time right?)

It doesn't include any ability (and I haven't even created a single one, or even edited!)...

Can you post a map with the ability (also even the ability, just to allow me copy/paste) because i think it'easier than come up on why the merger doesn't work for me :P

Another little thing
Are there any possibilities for damage types implementation?Or at least something like this in your to-do list?Because damage types are very interesting

Anitarf 11-09-2009 12:41 PM

If the object merger call isn't working for you, you can create the ability in the object editor yourself instead, I think that's the fastest way. Just pick one of the "Item Life Bonus" abilities, make a custom ability based on it, change the life bonus value to some high number and copy the new ability's rawcode to the calibration section of DamageModifiers.

Sinnergy 01-18-2010 09:48 AM

ok I have this simple spell that just modifies the received damage of the targeted unit to deal more damage, but I'm just confused if I still need to this function function RunDamageModifiers takes nothing returns real
If I need to call that function, then where should I call it?
The spell code:

//! zinc
library BOY requires AutoIndex, SpellEvent, DamageModifiers, TimerUtils{
    constant integer spell = 'A01I';
    constant string sfx = "Abilities\\Spells\\NightElf\\Barkskin\\BarkSkinTarget.mdl";
   
    struct boyDamage extends DamageModifier{
        integer l;
        method onDamageTaken(unit c, real dmg)->real{
            return dmg*(0.13*l);
        }
    }
   
    struct data{
        unit u;
        effect fx;
        real ctr;
        boyDamage dm;
        method onDestroy(){
            DestroyEffect(fx);
            ctr = 0.0;
        }
    }
   
    function act(){
        unit c = SpellEvent.CastingUnit;
        unit u = SpellEvent.TargetUnit;
        timer t = NewTimer();
        data d = data.create();
        d.u = u;
        d.fx= AddSpecialEffectTarget(sfx,u,"origin");
        d.ctr = 0.0;
        d.dm= boyDamage.create(u,0);
        d.dm.l = GetUnitAbilityLevel(c,spell);
        SetTimerData(t,d);
        TimerStart(t,0.035,true,function(){
            timer t = GetExpiredTimer();
            data d = GetTimerData(t);
            if(d.ctr >= 8.0 || GetWidgetLife(d.u) <= 0.405){
                d.dm.destroy();
                ReleaseTimer(t);
                d.destroy();
            }
            d.ctr += 0.035;
            t = null;
        });
        t = null;
        u = null;
        c = null;
    }
   
    function onInit(){
        RegisterSpellEffectResponse(spell,act);
    }
}
//! endzinc


Anitarf 01-18-2010 02:16 PM

If you have the DamageEvent library in your map, you do not need to call RunDamageModifiers yourself because DamageEvent already does it.


All times are GMT. The time now is 08:42 AM.

Powered by vBulletin (Copyright ©2000 - 2019, Jelsoft Enterprises Ltd).
Hosted by www.OICcam.com
IT Support and Services provided by Executive IT Services