Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
Three GUI Damage systems for the community of The Hive,
Seven vJass Damage systems for the JASS-heads on their pedestals high,
Nine competing Damage systems, doomed to die,
One for Bribe on his dark throne
In the Land of the Hive where the Workshop lies.
One Damage Engine to rule them all, One Damage Engine to find them,
One Damage Engine to bring them all and in cross-compatibility unite them.
Whether you're looking for a simple DDS (Damage Detection System), need to modify damage or even if you want a complex network of damage pre-and post-processing, this resource is for you. Damage Engine is the most adapted DDS in existence to take full advantage of the new damage detection natives, and is constantly evaluated and scrutinized by both the community and myself for performance improvements, bug fixes and new features alike.
What started with humble beginnings to bring a Unit Indexer-based version of DDS to GUI users to improve on the previous standard (at the time it was Weep's GDDS) would eventually evolve to incorporate other aspects of damage systems out there. @looking_for_help had identified an extremely useful technique with negative spell resistance being used to detect spell damage, and for a time his Physical Damage Detection system became the new standard. It wouldn't be until much later when Damage Engine would resurface and use the same technique in order to be more useful for the community.
Fast forward to 2020, and you'll find not only that cross-compatibility with Weep's and LFH's systems are incorporated, but the most popular DDS systems in existence - even vJass ones - are fully supported via stripped-down API wrappers. All of the functionality of prior DDS systems has been infused into Damage Engine 5.7, and as such the transition to a "one size fits all" DDS is complete. I hope you find this to be useful, and I also hope that it will help you in developing your map to be more dynamic what than you had previously thought possible.
Features
Legacy Code & Requirements
How it works
How to install/upgrade
Video Guides
FAQs
Thanks
Damage Type, Attack Type, Weapon Type, Armor Type and Defense Type detection and modification to alter/understand the fundamentals of WarCraft 3's damage processing;
Access to the view and change the damage amount before and/or after reduction by armor, armor type and shields.
Fully cross-compatible with every major DDS - vJass and GUI alike.
Correctly distribute/negate Spirit Link damage.
Differentiate between Ranged, Melee, Spell or other types of attack.
Does not require any Object Editor data nor Unit Indexer.
As of 5.4, it is now completely recursion-proof.
Other features:
Damage Blocking, reducing, amplifying and/or conversion to healing
Does not require you to know nor use Jass NewGen Pack nor any custom script
Custom DamageType association available to be set before and/or during damage event
Detect when multiple targets were hit simultaneously via DamageEventAOE > 1.
Detect when the same unit was hit simultaneously by the same source via DamageEventLevel > 1.
Detect damage: use the event "OnDamageEventEqual to <any value>". You have access to the following variables for reference:
DamageEventSource - the unit dealing the damage
DamageEventTarget - the unit receiving the damage
DamageEventAmount - the amount of damage the unit will receive
DamageEventPrevAmt - the amount of damage prior to being modified by the game engine.
DamageEventAttackT - the attack type (Chaos, Spells, Pierce, etc.)
DamageEventWeaponT - the weapon type determines if an attack plays some kind of sound on attack (ie. Metal Heavy Bash). It is always NONE for spells and almost always NONE for ranged attacks.
DamageEventArmorT - the armor type (Flesh, Stone, Ethereal, etc.)
DamageEventDefenseT - the defense type (Hero, Fortified, Unarmored, etc.)
DamageEventArmorPierced - if DAMAGE_TYPE_NORMAL, how much armor was set to be ignored by the user.
IsDamageSpell, IsDamageRanged, IsDamageMelee - determine the source category of damage dealt.
IsDamageCode - Determine if the damage was dealt natively or by the user. This can give incorrect readings if the user has not identified to Damage Engine that it is code damage. Therefore I recommend setting NextDamageType prior to dealing damage.
DamageEventType - An integer identifier that can be referenced by you for whatever extra differentiation you need. You can also create your own special damage types and add them to the definitions in the Damage Event Config trigger. If you the unit should explode on death by that damage, use a damage type integer less than 0. If you want the damage to ignore all modifications, use DamageTypePure.
To change damage before it's processed by the WarCraft 3 engine: use the event "PreDamageEvent Becomes Equal to <any value>". Whether you just want to use one monolithic trigger for all damage modification like I do in the demo map, or use the different modifier events here, is up to you.
To interact with damage after it's been factored for armor and resistances, use "ArmorDamageEvent Becomes Equal to <any value>". This is typically useful for custom shields. If you fully block or absorb DamageEventAmount (setting it to 0 or less), this event doesn't run.
To set the DamageEventType before dealing triggered Damage, use:
- Set NextDamageType = DamageTypeWhatever
- Unit - Cause...
You can modify the following variables from a "PreDamageEvent" trigger:
DamageEventOverride - You can set this if you want to remind yourself not to modify the damage further. If you use the UNKOWN damage type from a Unit - Damage Target native or set "NextDamageType = DamageTypePure" before that function, damage modification is skipped.
To catch a unit the moment before it would die from damage, use LethalDamageEvent Becomes Equal to 1.00. Instead of modifying the DamageEventAmount here, modify LethalDamageHP to let the system know how much life to keep the unit alive with. Or you can just reference that LethalDamageHP value in order to know how much "overkill" damage was dealt.
To catch a unit as soon as the damage is applied against its Hit Points, use AfterDamageEvent Equal to 1.00.
Usage of the "<Event> becomes EQUAL to <value>" can be extrapolated as per the below:
EQUAL works as it always has - will run for any damage.
NOT EQUAL only runs for code damage.
LESS THAN only runs for damage from attacks but not coded attacks.
LESS THAN OR EQUAL only runs for melee attacks but not coded attacks.
GREATER THAN OR EQUAL only runs for ranged attacks but not coded attacks.
GREATER THAN only runs for Spell damage but not coded spell damage.
Damage Engine 5 and higher requires the latest Warcraft 3 patch (currently 1.32).
I have created a Pastebin for all information pertaining to Damage Engine 3.8, including the link to download it, via: Damage Engine 3.8.0.0 | HIVE.
As of 20 June 2020, JNGP users who are still on WarCraft 3 1.26 can benefit from Damage Engine 3A, which integrates many of the design choices added in various stages of Damage Engine 5. This is a special update which only requires the JASS script be replaced (no new GUI variables added). Can be found here: Damage Engine 3A.0.0.0 and 3.8.0.0 | HIVE
1
Unit attacks or casts a spell. The pure damage is assessed at this point - damage dice, evasion, critical strike. There is currently no event to affect or modify these at source.
→ →
2
The projectile or weapon hits the target unit
→ →
3
EVENT_UNIT_DAMAGING is fired before any modifications to the damage are made.
→ ↓
↓ ←
6
User changes to DamageEventAmount and/or to DamageEventDamageT/AttackT/WeaponT are saved into the WC3 engine.
← ←
5
If any recursive damage is detected from any of those events, it is postponed and the current damage will continue first.
← ←
4
Damage Engine deploys the PreDamageEvent
7
WarCraft 3 processes the user damage.
→ →
8
WarCraft 3 then distributes the user damage into Spirit Link (before armor)
→ →
9
WarCraft 3 runs any interrupting events, such as spirit link or defensive damage like Thorns Aura
→ ↓
↓ ←
12
The EVENT_UNIT_DAMAGED event runs. This is the original event we have always had access to. Damage Engine will either keep using the original variables from the DAMAGING event, or if there was Spirit Link/defensive damage it will freshly retrieve the event values from WC3 and only retain DamageEventPrevAmt.
← ←
11
Once any potential recursive WarCraft 3 damage is processed, the armor/mana shield modifications to the damage are performed.
← ←
10
If such events such as Spirit Link were detected, they fire their own EVENT_UNIT_DAMAGING & EVENT_UNIT_DAMAGED, and DamageEngine processes it along the way for the user.
13
If the damage is above zero, ArmorDamageEvent will run. If any recursive damage is detected, it is postponed.
→ →
14
The user can make modification to the damage here with the after-damage amount.
→ →
15
The user can access DamageEventPrevAmount if they want to know the damage amount before user changes/WarCraft 3 changes.
→ ↓
↓ ←
18
The user can specify whether or not to change LethalDamageHP in order to stop the unit from dying.
← ←
17
If the damage is still above zero, check if the damage is lethal. If so, run LethalDamageEvent 1.00. If any recursive damage is detected, it is postponed.
← ←
16
If the user wants the value that DamageEngine used to have with DamageEventPrevAmt (after WarCraft 3 processing but before user changes) they multiply DamageEventPrevAmt x DamageScalingWC3.
19
Once all modification is done, run OnDamageEvent. If any recursive damage is detected, it is postponed.
→ →
20
After a new damage instance is detected, or the 0.00 timer expires, run AfterDamageEvent. If any recursive damage is detected, it is postponed.
→ →
21
Run all potential recursive Unit - Damage Target function calls in chronological order (first in, first out).
√
How to install Damage Engine:
Use WarCraft 3 Version 1.32
If you're upgrading from 3.8 or prior, please delete the entire "Damage Engine" category from your map.
Copy & Paste the Damage Engine category from the attached map to your own map.
How do I upgrade to the latest Damage Engine?
- Depending on the complexity, you'll either need to re-copy the Damage Engine category or just the Damage Engine script. Generally, you can use this as a guide:
Damage Engine _._._.x - only requires copying of the JASS script
Damage Engine _._.x._ - generally only needs copying of the JASS script, but read the patch notes in case there are changes you may want to make to your own code in order to utilize the changes.
Damage Engine _.x._._ - delete your current Damage Engine category or manually add the missing variables to your Variable Editor, and update your JASS script.
Damage Engine x._._._ - this occurs very infrequently. Typically requires changes to the way Damage Engine needs to be installed and used.
Notes about upgrading from Damage Engine 4.0 or prior:
Revert any custom Object Editor changes made (such as Life Drain reversal, Spell Damage Reduction inversion, etc).
You can safely delete the custom spells "Spell Damage Detection" and "Cheat Death Ability"
You can delete the Unit Indexer trigger if you do not use it or would prefer to use a different indexer.
You should delete any "Mana Shield fix" or "Finger of Death fix" you may have imported, as well as revert any Object Editor data that was required to make it work, as these are no longer needed.
!!!DEPRECATED FEATURE!!! As of 5.4, do not bother to take any recursive damage mitigation - Damage Engine will now handle all of that for you!
!!!DEPRECATED FEATURE!!!Do not use "ClearDamageEvent" - it does nothing. Just delete it.
!!!DEPRECATED FEATURE!!!Do not use "NextDamageOverride" - set NextDamageType = DamageTypePure instead.
!!!CHANGED FEATURE!!!If the system detects code damage and the user did not specify it as any particular DamageEvenType, the system will assign it DamageTypeCode automatically.
!!!CHANGED FEATURE!!!DamageModifierEvent 1.00-3.00 now run prior to armor reduction. This enables the user to modify the damage before armor and resistance changes are applied - also before Mana Shield and Anti-Magic shell kick in, so no more need for special triggers.
Q: Why am I getting a bunch of 'trigger was disabled' errors when I save my map?
A: This issue is not unique to Damage Engine, but to all vJass resources. Blizzard has taken the very confusing decision to make JassHelper 'disabled' by default, meaning that every new map that uses a vJass resource has to manually enable JassHelper. Please see this thread if you want to know where to find the Enable JassHelper option.
.
Q: How can I detect when a unit gets damaged?
A: Create a trigger with the event: "Game - Value of Real Variable <DamageEvent> becomes Equal to 1.00".
Use the following custom variables to reference the event responses:
DamageEventSource - the unit dealing the damage
DamageEventTarget - the unit getting damaged
DamageEventAmount - how much damage is being dealt
DamageEventAttackT - which attack type was used by DamageEventSource to damage DamageEventTarget
DamageEventDamageT - which damage type was used to damage the target (ie. UNIVERSAL for ultimate damage, or NORMAL for damage that gets reduced by armor).
DamageEventDefenseT - which defense type does the target unit have (ie. Hero, Fortified, Unarmored).
DamageEventArmorT - which armor type does the target unit have (ie. Flesh, Ethereal, Stone).
DamageEventPrevAmt - what the value of the damage was before being modified by armor, ethereal/item bonuses or user changes.
.
Q: How do I modify the damage that is dealt to a unit?
A: Create a trigger with the event: "Game - Value of Real Variable <PreDamageEvent> becomes Equal to <any value>".
You can change the following variables to affect the damage that will be dealt:
DamageEventAmount - how much damage will be dealt (before armor reductions)
DamageEventAttackT - which attack type will be used by DamageEventSource to damage DamageEventTarget
DamageEventDamageT - which damage type will be used to damage the target.
DamageEventDefenseT - which defense type should the target unit have during this attack.
DamageEventArmorT - which armor type should the target unit have during this attack.
DamageEventArmorPierced - how much armor value to ignore when dealing this damage (applies to DAMAGE_TYPE_NORMAL only, otherwise all armor is ignored).
.
Q: How do I deal Pure damage to a unit (bypassing armor/skipping user modification)?
A: Use the following actions:
Set NextDamageType = DamageTypePure
Unit - Cause Source to damage Target for Amount using attack type Spells and damage type Universal
.
Q: How do I protect against recursive damage?
A: Damage Engine 5.4 and above is completely recursion-proof, using vJass hooks to detect registered Damage Event triggers.
.
Q: I've been using <insert Damage system here>. Can I use those with Damage Engine?
A: Better - Damage Engine has integrated cross-compatibility with all other approved Damage systems and even the major ones from outside of Hiveworkhop.
.
Q: Can I cause an attack to 'Miss' its target?
A: Kind of. Ranged attacks will still explode on the target, and on-hit effects will still apply, but you can do the following:
Use the event "PreDamageEvent becomes Equal to 1.00"
Use the following actions:
Set DamageEventAmount = 0.00
Set DamageEventArmorT = ARMOR_TYPE_NONE
Set DamageEventWeaponT = WEAPON_TYPE_NONE
Setting the weapon type and armor type to none like the above will stop any on-hit sounds from playing. When the Peasant attacks in the demo map, this is the trick I'm using. Instead of saying "MISSED!" I have the Floating Text saying "FAIL!" because - again - it's not exactly a "miss".
Thank you to Blizzard for continuing to work on this amazing game to give us awesome new natives that have the best possible control over incoming damage. Damage Engine brings that power to GUI. Also, a very special thank you to @KILLCIDE for getting me motivated to start up the 5.0 project in the first place.
Thank you to the users of this system who have helped me mold this project into the stable, powerful form it is in today!
For versions 3.8 and prior:
Thank you @looking_for_help for finding the spell damage detection method used in Damage Engine 3 - it was the greatest find since the undefend bug.
Thanks to Jesus4Lyf and @Nestharus for building the inspiration that originally led me to create DamageEngine.
Thank you Wietlol and looking_for_help for challenging me on this project to integrate solutions to problems I had not been made aware of, such as the importance of an After-Damage Event.
Thanks to @Spellbound for several crucial bug reports.
Damage Engine Config
Damage Engine vJass
Damage Engine Lua
Changelog
Damage Engine Config
Events
Map initialization
Game - DamageModifierEvent becomes Greater than 0.00
Game - LethalDamageEvent becomes Less than or equal to 0.00
Game - DamageEvent becomes Not equal to 0.00
Game - AfterDamageEvent becomes Less than 0.00
Game - AOEDamageEvent becomes Greater than or equal to 0.00
Game - SourceDamageEvent becomes Equal to 0.00
Game - PreDamageEvent becomes Equal to 0.00
Game - ArmorDamageEvent becomes Equal to 0.00
Game - ZeroDamageEvent becomes Equal to 0.00
Conditions
Actions
-------- You can add extra classifications here if you want to differentiate between your triggered damage --------
-------- Use DamageTypeExplosive (or any negative value damage type) if you want a unit killed by that damage to explode --------
-------- - --------
-------- The pre-defined type Code might be set by Damage Engine if Unit - Damage Target is detected and the user didn't define a type of their own. --------
-------- "Pure" is especially important because it overrides both the Damage Engine as well as WarCraft 3 damage modification. --------
-------- I therefore gave the user "Explosive Pure" in case one wants to combine the functionality of the two. --------
Lua 1.0.2.3 - Fixed to match adjustment made in vJass version 5.4.2.3.
Lua 1.0.2.2 - Fixed to match adjustment made in vJass version 5.4.2.2.
Lua 1.0.2.1 - Fixed to match adjustment made in vJass version 5.4.2.1.
Lua 1.0.2.0 - Added support for Lua Fast Triggers ([Lua] Ridiculously Fast Triggers). Fixed an issue where the AfterDamageEvent wasn't always timed the correct way like it was in the JASS verion.
Lua 1.0.1.0 - Fixed encapsulation issue and recursion issue with DamageEngine_inception. Now hooks UnitDamageTarget.
Lua 1.0.0.0 - Release based on Damage Engine 5.4.2.0
5.9.0.0 - Added the following clearer event names to make things less confusing for new users:
PreDamageEvent - can be used in place of DamageModifierEvent pre-armor modification
ArmorDamageEvent - can be used in place of DamageModifierEvent post-armor modification
OnDamageEvent - can be used instead of a non-zero DamageEvent
ZeroDamageEvent - can be used instead of a zero damage DamageEvent
SourceDamageEvent - runs at the same time as AOEDamageEvent, but doesn't need to hit multiple units.
Added "DamageFilterRunChance" - odds for the trigger to BE run (works inversely to DamageFilterFailChance).
Shortened the Configuration trigger so that it focuses primarily on what the user can modify.
Organized all variables into categories to help users better understand what does what.
Installation or updating from a previous version will require re-copying the entire Damage Engine folder.
Updated the demo map's text tag production to include a new custom update for ArcingTextTag, thanks to @Ugabunda and @Kusanagi Kuro.
Side note - the Demo Map's triggers have been heavily cleaned up and will now no longer cause crashes when being imported into a new map.
5.8.0.0 -
Added a new functionality to the AOEDamageEvent, which allows it to fire even when only one unit is hit, if the AOEDamageEvent is registered as "Not Equal" instead of "Equal to". This is useful in a very specific scenario where an AfterDamageEvent may not be able to be relied upon to properly deallocate data from a running instance that needs to be able to function when multiple units are hit, but also needs to not fail when only one unit is hit.
Added additional filters for GUI users to avoid using trigger conditions in even more scenarios:
DamageFilterSource/TargetI (has item)
DamageFilterSource/TargetA (has ability)
DamageFilterSource/TargetC (has a certain classification, like hero or Tauren)
DamageFilterFailChance - odds for the trigger to not be run (ideal for critical strike or evasion)
Provided a way for GUI to un-register a Damage Event by setting "RemoveDamageEvent" to true from within their trigger's actions.
Moved the CreateTimer/CreateTrigger/CreateGroup calls that had been included in the globals block down to the onInit block as per request of @Ricola3D
Minor performance improvements thanks to @BLOKKADE
5.7.1.2 - Fixed an issue with armor penetration sometimes bugging out.
5.7.1.1 - Fixed an issue with the eventFilter not retaining its value during an AOE event. Fixed an issue with eventFilter when USE_MELEE_RANGE was set to false. Both issues reported by @lolreported
5.7.1.0 -
I have fixed several potential points of failure reported by multiple users that stopped Damage Engine from working for the rest of the game. In any case there should no longer be ANY SITUATION where Damage Engine just "stops working", even in ways I can't predict. The most likely issue was with DamageScalingWC3/User having possible 0-division errors.
Fixed the "configured" issue reported by @lolreported where manual configuration didn't work unless the damage and attack type checks were initialized tto -1.
In addition to these fixed bugs, I have added several more static ifs so that advanced users have more control over getting rid of features they might not care about.
5.7.0.3 - Fixed the issue reported by @KitsuneTailsPrower wherein the DamageInterface extension wasn't working. This needed to be fixed both in the Damage Engine source as well as the DamageInterface plugin itself.
5.7.0.2 - Fixed the issue reported by @Wazzz where the armor reduction wasn't taken into consideration. Actually there was a much bigger flaw that I had overlooked that was prompting this.
5.7.0.1 - Improved the logic of the 0 damage event.
5.7.0.0:
Usage of the "DamageEvent becomes EQUAL to 1.00" now can be extrapolated further than ever before:
EQUAL works as it always has.
NOT EQUAL only runs for code damage.
LESS THAN only runs for damage from attacks.
LESS THAN OR EQUAL only runs for melee attacks.
GREATER THAN OR EQUAL only runs for ranged attacks.
GREATER THAN only runs for Spell damage.
Fully adapted Damage Engine to work with every other major DDS
Rewrote the internal script to use vJass structs: Damage and DamageTrigger.
Changed some of the vJass API. You can find the API listed in the Damage Engine trigger. Notably:
I removed UnitDamageTargetEx. You can replace this with Damage.apply.
The reason for this change is because of cross-compatibility. Rising Dusk's IDDS uses a different set of parameters for this function, so I removed it from my library to allow this to work seamlessly.
The reason for the above two changes is, like for UnitDamageTargetEx, because Rising Dusk's IDDS uses them. I don't want to make the same mistake I did with Table's API and preferred to walk back my API choices before things got out of hand again.
Various performance tweaks, bug fixes and re-commenting of variables.
Recursive damage is now processed more intelligently and possibly be more performance-friendly.
Adapted the cross-compatibility libraries for Weep and LFH's systems to use textmacros to insert themselves into Damage Engine (this might have been in the 5.6 update, but these two updates were both tailored to achieve similar goals).
5.6.2.0 - Fixed a bug with Damage modification and laid groundwork for a bunch of optional vJass compatibility addons.
5.6.1.0 - Patchwork on Melee/Ranged detection, recursion tracking. Also added the ability to modify damage from a DamageEvent to make it easier on beginners.
5.6.0.1 - Fixed an issue where the DamageEventType for recursive damage that used NextDamageType would always default to DamageTypeCode.
5.6.0.0
Rewrote a significant amount of the internal code so that struct and GUI syntax both work - and are synchronized with each other. vJass-exclusive users can disable the GUI synchronization if they don't use any GUI damage events in their maps.
Four new variables must be added to your variable editor:
boolean NextDamageIsAttack
boolean NextDamageIsMelee
boolean NextDamageIsRanged
integer NextDamageWeaponT
Struct syntax should not need too much introduction - Damage.index is the triggering event ID, and the syntax is Damage.index.amount/source/target/etc. To initialize a JASS damage event, use:
JASS:
function RegisterDamageEvent takes code c, string eventName, real value returns nothing
The string is simplified: "Modifier", "" (for simple DamageEvent), "After", "Lethal", "AOE"
Finally, a neat QOL improvement is that I have assigned weight to each of the events. When registering an event, you can include numbers different than 1, 2, 3 or 4 (for modification) or just the plain "1" for the others. Now you can set your own sequencing and use 1.5, 3.14 or even -1 to give an even more extreme priority to something. Lower numbers run first, higher ones last.
5.5.0.0 - Added support for the new native BlzGetEventIsAttack via "IsDamageAttack". Also updated the Config trigger to make importing a bit easier
5.4.2.3 - Fixed a mis-flag of the IsDamageSpell value when not being actual spell damage.
5.4.2.2 - Actually fixed the Cold Arrows issue (division by 0.00001 or something ...somehow... either way, it is fixed).
5.4.2.1 - A fix which should hopefully quell the recent reports of damage events failing in complex situations involving Cold Arrows.
5.4.2.0 - A ton of fixes to repair the issues plaguing Sunken City and anyone else who might be pushing this engine well beyond what I imagined it would be used for. Also added a couple of variables intended to be used to ricochet damage: CONVERTED_ATTACK_TYPE and CONVERTED_DAMAGE_TYPE. Check the demo map for how they can be used in a Unit - Damage Target action.
5.4.1.0 - The "Inception" update. Advanced users can interact with Damage Engine via custom script to set DamageEvent_inception = true to allow their damage to potentially go recursive (to a fixed extent).
5.4.0.1 - Hotfixed that modifiers 2 and 3 weren't running.
5.4.0.0 - By using an innovative approach of hooking TriggerRegisterVariableEvent, I've permanently eliminated all risks of recursion in the engine.
5.3.0.1 - Fixed unexpected behavior with DamageTypePure when it is affected by Anti-Magic Shell or Mana Shield. DamageTypePure now no longer ignores DamageScalingWC3.
5.3.0.0 - Fixed an issue with AfterDamageEvent sometimes being delayed. Added DamageScalingUser to track the ratio of user modified damage, as well as DamageEventArmorPierced which allows the user to define how much armor to ignore when working with DAMAGE_TYPE_NORMAL.
5.2.0.1 - Fixed an issue where with the final unit in a Spirit Link chain or the retaliating damage of Thorns/Carapace would not deploy an AfterDamageEvent. Also fixed an issue where AfterDamageEvent would still fire from DAMAGE_TYPE_UNKNOWN if it was greater than 0. Simply copy over the JASS from this post in order to update your own implementation.
5.2.0.0
Now features DamageEventArmorT and DamageEventDefenseT, which pull from the target unit's Object Editor data in order to allow you more complete access to detect and even MODIFY those defenses. Changes must be made in a DamageModifierEvent, and they are reverted as soon as armor is calculated.
Re-introduced AfterDamageEvent, which is the moment after the unit's life is changed, but before any recursive damage has run.
5.1.3.1 - Bug fixes and performance improvements. No, really. Fixed an issue with the DAMAGED event running even though it was turned off (who would've guessed that?)
5.1.3.0 - Engine re-tooling to improve accuracy and get Spirit Link/Defensive damage out of hardcoded mode - it will now work even in circumstances I haven't tested for (in case those weren't the only issues). Also fixed the Is Unit Moving resource.
5.1.2.1 - Fixed an issue with Spiked Carapace and Thorns Aura where the melee attacker would not get recorded correctly. This required the same fix as I needed to apply for the initial target in a spirit link chain.
5.1.2.0 - Tweaked recursion and fixed a few bugs. Re-introduced the zero damage event now that patch 1.31.1 brought it back.
5.1.1.1 - Minor tweak to Spirit Link in rare situation.
5.1.1.0 - Fixed issues related to Spirit Link. Now works intuitively as expected.
5.1.0.0 - Crazy improvements - check out the details in "How to upgrade from Damage Engine 4.0 or earlier" and "How to use Damage Engine 5.1"
5.0.0.0 - Oh man. Where do I even start?
You can now set/compare DamageEventDamageT, DamageEventAttackT or DamageEventWeaponT
No longer needs Unit Indexer nor any custom Object Editor data nor modifications.
Requires WarCraft 3 1.31 or higher.
Changed vanilla JASS code to vJass to clean up a lot of things such as making certain variables private.
Look for more details in "How to upgrade from Damage Engine 4.0 or earlier"
4.0.0.0 - Never officially released, but was the first iteration which used SetEventDamage.
For 3.8.0.0 and prior, see: Damage Engine 3.8.0.0 | HIVE
23:20, 11th Jan 2015, BPower
Criticism:
On the one hand Damage Engine offers extremely high utility to every spell, system and map maker
independent of the coding style ( GUI, JASS, vJass, .... ) on the other hand it's very easy to import and...
Yes that should still work, however with 5.7 I have included built-in conditions that won't "replace" anything you do however will make things much more efficient if you take advantage of them.
What filters are built in easily into GUI?
Spell, Melee, Ranged, Code, Attack and "anything".
Which ones require custom script?
Unit filtering
Unit types
Buff on unit
All of the above for source and target
Additionally, I've added a filter for DamageEventType and DamageEventAmount (minimum) to the fray.
Having these filters will cut back on trigger evaluations for very conplex maps.
Additionally, coming in a future patch, a DAMAGE_ENGINE_LITE mode which cuts out the pre-armor event introduced with 1.31. This doesn't make it backwards-compatible but DOES make it run a lot less code per event. Busy maps could see a difference in framerate.
In the introduction to the engine you mention that it is "recursion proof". I guess that means that a damage-order within a trigger shouldn't make the same trigger activate itself and deal the damage 2x (or infitine times) to a unit. However, in the below trigger the damage is dealt twice to enemies.
A bit of explanation on the trigger below:
- I'm using a backstab ability which deals damage when my unit is behind an enemy
- The backstab should be triggered by autoattacks, "normal" spells and "finisher spells"
- A backstab will grant "Combo Points" to the caster. However, these Combo Points should only be granted on auto-attacks and "normal" abilities - not "finisher abilities".
- The way I differentiate between the two types of abilities is with "damage type"; "normal" damage type is used for the "normal" abilities and "enhanced" damage type is used for the finisher
- Currently the enhanced damage is dealt twice however, which I assume shouldn't be possible
So my questions:
- Is this a bug?
- Is there a better way than damage type to tell the engine which abilties you want to include for "extra damage" and which ones you want to exclude?
Thank you!
Edit: In case you have reforged, I attached the map.
Backstab with combo
Events
Game - DamageModifierEvent becomes Equal to 1.00
Conditions
(DamageEventSource has buff Backstab ) Equal to True
(DamageEventTarget belongs to an enemy of (Owner of DamageEventSource).) Equal to True
Actions
Set VariableSet Backstab_Caster = DamageEventSource
Set VariableSet Backstab_Target = DamageEventTarget
Set VariableSet Backstab_Point[1] = (Position of Backstab_Caster)
Set VariableSet Backstab_Point[2] = (Position of Backstab_Target)
Set VariableSet Backstab_Angle = (Angle from Backstab_Point[1] to Backstab_Point[2])
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Acos((Cos(((Angle from Backstab_Point[1] to Backstab_Point[2]) - (Facing of Backstab_Target)))))) Less than 90.00
Then - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
DamageEventDamageT Equal to DAMAGE_TYPE_ENHANCED
Then - Actions
Unit - Cause Backstab_Caster to damage Backstab_Target, dealing ((Real((Agility of Backstab_Caster (Include bonuses)))) x (0.25 x (Real((Level of Backstab for Backstab_Caster))))) damage of attack type Hero and damage type Enhanced
Special Effect - Create a special effect attached to the chest of Backstab_Target using war3mapImported\stampedemissiledeath.mdx
Special Effect - Destroy (Last created special effect)
Floating Text - Create floating text that reads |cffff0000Backstab!... at Backstab_Point[2] with Z offset 0.00, using font size 6.50, color (100.00%, 100.00%, 0.00%), and 0.00% transparency
Floating Text - Change (Last created floating text): Disable permanence
Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
Floating Text - Change the lifespan of (Last created floating text) to 0.65 seconds
Floating Text - Change the fading age of (Last created floating text) to 0.50 seconds
Else - Actions
Trigger - Turn on Combo Point Loop <gen>
Unit - Cause Backstab_Caster to damage Backstab_Target, dealing ((Real((Agility of Backstab_Caster (Include bonuses)))) x (0.25 x (Real((Level of Backstab for Backstab_Caster))))) damage of attack type Hero and damage type Normal
Special Effect - Create a special effect attached to the chest of Backstab_Target using war3mapImported\stampedemissiledeath.mdx
Special Effect - Destroy (Last created special effect)
Floating Text - Create floating text that reads |cffff0000Backstab!... at Backstab_Point[2] with Z offset 0.00, using font size 6.50, color (100.00%, 100.00%, 0.00%), and 0.00% transparency
Floating Text - Change (Last created floating text): Disable permanence
Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
Floating Text - Change the lifespan of (Last created floating text) to 0.65 seconds
Floating Text - Change the fading age of (Last created floating text) to 0.50 seconds
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
ComboPointCounter Less than 5
Then - Actions
Set VariableSet ComboPointCounter = (ComboPointCounter + 1)
Floating Text - Create floating text that reads (Combo Points: + (String(ComboPointCounter))) at Backstab_Point[1] with Z offset 0.00, using font size 6.50, color (100.00%, 100.00%, 0.00%), and 0.00% transparency
Floating Text - Change (Last created floating text): Disable permanence
Floating Text - Change the lifespan of (Last created floating text) to 0.90 seconds
Floating Text - Change the fading age of (Last created floating text) to 0.50 seconds
Unit - Create 1 Dummy for (Owner of Backstab_Caster) at Backstab_Point[1] facing Default building facing degrees
Set VariableSet Backstab_ComboCaster = (Last created unit)
Unit - Add Combo Points Buff to Backstab_ComboCaster
Unit - Set level of Combo Points Buff for Backstab_ComboCaster to ComboPointCounter
Unit - Order Backstab_ComboCaster to Human Priest - Inner Fire Backstab_Caster
Unit - Add a 1.00 second Generic expiration timer to Backstab_ComboCaster
Else - Actions
Unit - Create 1 Dummy for (Owner of Backstab_Caster) at Backstab_Point[1] facing Default building facing degrees
Set VariableSet Backstab_ComboCaster = (Last created unit)
Unit - Add Combo Points Buff to Backstab_ComboCaster
Unit - Set level of Combo Points Buff for Backstab_ComboCaster to ComboPointCounter
Unit - Order Backstab_ComboCaster to Human Priest - Inner Fire Backstab_Caster
Unit - Add a 1.00 second Generic expiration timer to Backstab_ComboCaster
You mean like this? Mmm this still causes the damage to deal twice. And it's not exactly what I want, because both DAMAGE_TYPEs (normal and enhanced) should go through if the "behind the enemy" condition applies. But Finisher-abilities (with enhanced damage) should just deal additional backstab damage, while Normal-abilities should do the additional mechanic in the lower code.
I added an image to maybe explain the problem better. Also cleaned the code to have it more visible here.
Backstab with combo Copy
Events
Game - DamageModifierEvent becomes Equal to 1.00
Conditions
(DamageEventSource has buff Backstab ) Equal to True
(DamageEventTarget belongs to an enemy of (Owner of DamageEventSource).) Equal to True
DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
Actions
Set VariableSet Backstab_Caster = DamageEventSource
Set VariableSet Backstab_Target = DamageEventTarget
Set VariableSet Backstab_Point[1] = (Position of Backstab_Caster)
Set VariableSet Backstab_Point[2] = (Position of Backstab_Target)
Set VariableSet Backstab_Angle = (Angle from Backstab_Point[1] to Backstab_Point[2])
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Acos((Cos(((Angle from Backstab_Point[1] to Backstab_Point[2]) - (Facing of Backstab_Target)))))) Less than 105.00
Then - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
DamageEventDamageT Equal to DAMAGE_TYPE_ENHANCED
Then - Actions
Unit - Cause Backstab_Caster to damage Backstab_Target, dealing ((Real((Agility of Backstab_Caster (Include bonuses)))) x (0.25 x (Real((Level of Backstab for Backstab_Caster))))) damage of attack type Hero and damage type Normal
Else - Actions
Trigger - Turn on Combo Point Loop <gen>
Unit - Cause Backstab_Caster to damage Backstab_Target, dealing ((Real((Agility of Backstab_Caster (Include bonuses)))) x (0.25 x (Real((Level of Backstab for Backstab_Caster))))) damage of attack type Hero and damage type Normal
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
ComboPointCounter Less than 5
Then - Actions
Set VariableSet ComboPointCounter = (ComboPointCounter + 1)
Unit - Create 1 Dummy for (Owner of Backstab_Caster) at Backstab_Point[1] facing Default building facing degrees
Set VariableSet Backstab_ComboCaster = (Last created unit)
Unit - Add Combo Points Buff to Backstab_ComboCaster
Unit - Set level of Combo Points Buff for Backstab_ComboCaster to ComboPointCounter
Unit - Order Backstab_ComboCaster to Human Priest - Inner Fire Backstab_Caster
Unit - Add a 1.00 second Generic expiration timer to Backstab_ComboCaster
Else - Actions
Unit - Create 1 Dummy for (Owner of Backstab_Caster) at Backstab_Point[1] facing Default building facing degrees
Set VariableSet Backstab_ComboCaster = (Last created unit)
Unit - Add Combo Points Buff to Backstab_ComboCaster
Unit - Set level of Combo Points Buff for Backstab_ComboCaster to ComboPointCounter
Unit - Order Backstab_ComboCaster to Human Priest - Inner Fire Backstab_Caster
Unit - Add a 1.00 second Generic expiration timer to Backstab_ComboCaster
Oh hi bribe, Im currently having trouble with lags everytime an unit casts a DPS spell (i.e. Shadow Strike), I disable DE trigger and the lag gone. Is there anyway to spot what cause it? Im currently using DE ver 3.7, does upgrading to 3.8 help?
TY so much! love your works
Oh hi bribe, Im currently having trouble with lags everytime an unit casts a DPS spell (i.e. Shadow Strike), I disable DE trigger and the lag gone. Is there anyway to spot what cause it? Im currently using DE ver 3.7, does upgrading to 3.8 help?
TY so much! love your works
5.7 is here! There's a lot to say about it but I don't have a lot of time right now.
Mainly I'd ask everyone to please test it as thoroughly as possible - especially if you've previously reported bugs with the system.
Additionally, I've written in cross-compatibility scripts based on documented API of every other Damage system, but I've not tested these. They should work, however there could be syntax errors I've missed or I might be missing a function/gotten a parameter wrong here or there.
5.7 is here! There's a lot to say about it but I don't have a lot of time right now.
Mainly I'd ask everyone to please test it as thoroughly as possible - especially if you've previously reported bugs with the system.
Additionally, I've written in cross-compatibility scripts based on documented API of every other Damage system, but I've not tested these. They should work, however there could be syntax errors I've missed or I might be missing a function/gotten a parameter wrong here or there.
Something I haven't gotten the chance to talk about yet, so I'll go over it now, is that I chose to revert certain vJass API decisions of mine in favor of avoiding naming conflicts with other damage libraries.
However, in your case you would simply go back to using Flux's Illusion while at the same time update Flux's library to the one shown in the Damage Engine compatibility section:
so I wanna update my damage engine 5.4 to latest version but not sure what this really mean.
Do I need to delete variable and old folders and replace it with new one or can still keep it and just add in the new one cuz if I delete it, I'll have to fix all my triggers.
I think that Bribe meant for users to simply replace the code for the new one and not to create multiple copies of them. Anyway, you can create a backup of your map and then replace the system code for the new one and test, so you will be sure your triggers work as before, and if not report bugs here so Bribe can access them.
Do I need to delete variable and old folders and replace it with new one or can still keep it and just add in the new one cuz if I delete it, I'll have to fix all my triggers.
Latest Damage Engine version is 5.7.0.0, but the download also includes a LUA version map uploaded Aug 6, 2019 as version 1.0.2.3 but titled "Lua Damage Engine based on 5.4.2.3".
Are the features or performance different between the Lua and JASS versions?
Latest Damage Engine version is 5.7.0.0, but the download also includes a LUA version map uploaded Aug 6, 2019 as version 1.0.2.3 but titled "Lua Damage Engine based on 5.4.2.3".
Are the features or performance different between the Lua and JASS versions?
The Lua version is very much out of date at this point as I haven't worked on it in quite some time.
Once I can be sure the bugs and what-nor are resolved with 5.7, I'll get to work on bringing the Lua version up to speed.
Lua doesn't need as much TLC as Damage Engine vJass however as many of the new (potential) performance gains already existed in Lua by default thanks to the way Damage Engine just calls functions instead of evaluating/executing them.
I can confirm that damage now registers when utilising a dummy to cast a spell within the DamageEvent. I suspect the recursion issue I ran into was likely linked to it... I'll need to see whether I can replicate it with 5.7.0.0 in the near future.
Something I haven't gotten the chance to talk about yet, so I'll go over it now, is that I chose to revert certain vJass API decisions of mine in favor of avoiding naming conflicts with other damage libraries.
However, in your case you would simply go back to using Flux's Illusion while at the same time update Flux's library to the one shown in the Damage Engine compatibility section:
Thank you for the report! I have updated each of the compatibility scripts as I noticed they were using placeholder API. You don't need to update the Damage Engine code however.
Ok I am pleased to announce that ALL Compatibility Scripts are now out of alpha-phase and each one are able to compile and (to the best of my knoledge) run as expected.
Getting my plugin for Nestharus's DDS to compile is probably my finest accomplishment.
I'm considering updating, but in terms of performance, would you say 5.7.0.0 is faster than version 5.4.2.3 I'm currently using?
I'm thinking that the more recent versions introduces a lot of overhead due to the GUI <-> vJASS synchronization (I know this can be disabled) and having the Damage Engine compatible with other Damage Engines.
Currently I'm using one function for each Damage Event, which I understand is the best thing to do in terms of performance, I would expect the same thing applies to the recent version in case I would update?
I'm considering updating, but in terms of performance, would you say 5.7.0.0 is faster than version 5.4.2.3 I'm currently using?
I'm thinking that the more recent versions introduces a lot of overhead due to the GUI <-> vJASS synchronization (I know this can be disabled) and having the Damage Engine compatible with other Damage Engines.
Currently I'm using one function for each Damage Event, which I understand is the best thing to do in terms of performance, I would expect the same thing applies to the recent version in case I would update?
Honestly? Any performance gains might be negligible for you. The difference in performance could be plus or minus, depending on if your main triggers will be adapted to take advantage of the new speed gains (for example, if you only detect physical damage or spell damage in some, that would benefit you there).
I've never done benchmarks on any version of DamageEngine, but I run all of my tests on a low-perfomance laptop and it does just fine.
Was just testing the new 5.7.0, really excited about the new IsAttack functionality. That's gonna be real convenient for mapmakers. Lol'd at the "One ring to rule them all" reference in the comments.
I believe your "Ranged" trigger example should be "DamageModifierEvent becomes Greater than 'OR EQUAL TO' 1.00".
Right now it's just greater than, and it was confusing me why units weren't taking reduced damage from ranged attacks while moving, but they were from spell damage.
Also, maybe update your "Opening Text" trigger with details on how the Keeper blocks all incoming damage. Not a big deal, it's pretty evident what it does by looking at the "Keeper" trigger.
Was just testing the new 5.7.0, really excited about the new IsAttack functionality. That's gonna be real convenient for mapmakers. Lol'd at the "One ring to rule them all" reference in the comments.
I believe your "Ranged" trigger example should be "DamageModifierEvent becomes Greater than 'OR EQUAL TO' 1.00".
Right now it's just greater than, and it was confusing me why units weren't taking reduced damage from ranged attacks while moving, but they were from spell damage.
Also, maybe update your "Opening Text" trigger with details on how the Keeper blocks all incoming damage. Not a big deal, it's pretty evident what it does by looking at the "Keeper" trigger.
I am VERY pleased to announce the release of Damage Engine 3A, which can be found below (strictly a vJass MAJOR upgrade for those whose WarCraft 3 is out of date).
This is akin to Apple releasing a new iPhone 5-sized phone, or re-adding the headphone jack to their models.
I will keep the pastebin link supplied with both 3.8 as well as any future variants of 3A that might arise, but here's the code if you don't want to look too far:
JASS:
//===========================================================================
// Damage Engine 3A.0.0.0 - a new type of Damage Engine for users who don't
// have access to the latest version of WarCraft 3, which incorporates new
// features to inherited from Damage Engine 5.7 by hooking TriggerRegisterVariableEvent.
// However, it requires having JassHelper installed.
//
// Stuff that doesn't work:
// - Pre-armor modification
// - Damage/Attack/Weapotype detection/modification
// - Armor/defense detection/modification
// - Melee/ranged detection
// - Filters for u.
// - Spirit link won't interact with custom damage.
// - Still needs workarounds for Anti-Magic Shell/Mana Shield/Life Drain/etc.
//
// Stuff that is changed from how it worked with 3.8:
// - Recursive damage now uses the Damage Engine 5 anti-recursion method. So
// all recursive damage will be postponed until the sequence has completed.
// - No more need for using ClearDamageEvent
// - No more need to disable the DamageEventTrigger in order to avoid things
// going recursively.
//
library DamageEngine
globals
private timer alarm = CreateTimer()
private boolean alarmSet = false
//Values to track the original pre-spirit Link/defensive damage values
private Damage lastInstance = 0
private boolean canKick = false
//These variables coincide with Blizzard's "limitop" type definitions so as to enable users (GUI in particular) with some nice performance perks.
public constant integer FILTER_ATTACK = 0 //LESS_THAN
public constant integer FILTER_OTHER = 2 //EQUAL
public constant integer FILTER_SPELL = 4 //GREATER_THAN
public constant integer FILTER_CODE = 5 //NOT_EQUAL
public constant integer FILTER_MAX = 6
private integer eventFilter = FILTER_OTHER
private constant integer LIMBO = 16 //When manually-enabled recursion is enabled via DamageEngine_recurion, the engine will never go deeper than LIMBO.
public boolean inception = false //When true, it allows your trigger to potentially go recursive up to LIMBO. However it must be set per-trigger throughout the game and not only once per trigger during map initialization.
private boolean dreaming = false
private integer sleepLevel = 0
private group proclusGlobal = CreateGroup() //track sources of recursion
private group fischerMorrow = CreateGroup() //track targets of recursion
private boolean kicking = false
private boolean eventsRun = false
private unit protectUnit = null
private real protectLife = 0.00
private boolean blocked = false
private keyword run
private keyword trigFrozen
private keyword levelsDeep
private keyword inceptionTrig
private keyword checkLife
private keyword lifeTrigger
endglobals
private function CheckAddUnitToEngine takes unit u returns boolean
if GetUnitAbilityLevel(u, 'Aloc') > 0 then
elseif not TriggerEvaluate(gg_trg_Damage_Engine_Config) then
//Add some more elseifs to rule out stuff you don't want to get registered, such as:
//elseif IsUnitType(u, UNIT_TYPE_STRUCTURE) then
else
return true
endif
return false
endfunction
struct DamageTrigger extends array
//The below variables are constant
readonly static thistype MOD = 1
readonly static thistype DAMAGE = 5
readonly static thistype ZERO = 6
readonly static thistype AFTER = 7
readonly static thistype AOE = 9
private static integer count = 9
static thistype lastRegistered = 0
private static thistype array trigIndexStack
static thistype eventIndex = 0
static boolean array filters
readonly string eventStr
readonly real weight
readonly boolean configured
boolean usingGUI
//The below variables are private
private thistype next
private trigger rootTrig
boolean trigFrozen //Whether the trigger is currently disabled due to recursion
integer levelsDeep //How deep the user recursion currently is.
boolean inceptionTrig //Added in 5.4.2 to simplify the inception variable for very complex DamageEvent trigger.
static method operator enabled= takes boolean b returns nothing
if b then
call EnableTrigger(udg_DamageEventTrigger)
else
call DisableTrigger(udg_DamageEventTrigger)
endif
endmethod
static method operator enabled takes nothing returns boolean
return IsTriggerEnabled(udg_DamageEventTrigger)
endmethod
static method setGUIFromStruct takes boolean full returns nothing
set udg_DamageEventAmount = Damage.index.damage
set udg_DamageEventType = Damage.index.userType
set udg_DamageEventOverride = Damage.index.override
if full then
set udg_DamageEventSource = Damage.index.sourceUnit
set udg_DamageEventTarget = Damage.index.targetUnit
set udg_DamageEventPrevAmt = Damage.index.prevAmt
set udg_IsDamageSpell = Damage.index.isSpell
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_GDD()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_05()
endif
endmethod
static method getVerboseStr takes string eventName returns string
if eventName == "Modifier" or eventName == "Mod" then
return "udg_DamageModifierEvent"
endif
return "udg_" + eventName + "DamageEvent"
endmethod
private static method getStrIndex takes string var, real lbs returns thistype
local integer root = R2I(lbs)
if var == "udg_DamageModifierEvent" then
set root= MOD
elseif var == "udg_DamageEvent" then
if root == 2 or root == 0 then
set root= ZERO
else
set root= DAMAGE //Above 0.00 but less than 2.00, generally would just be 1.00
endif
elseif var == "udg_AfterDamageEvent" then
set root = AFTER
elseif var == "udg_AOEDamageEvent" then
set root = AOE
else
set root = 0
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_GDD()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_05()
endif
return root
endmethod
private method toggleAllFilters takes boolean flag returns nothing
set filters[this + FILTER_ATTACK] = flag
set filters[this + FILTER_OTHER] = flag
set filters[this + FILTER_SPELL] = flag
set filters[this + FILTER_CODE] = flag
endmethod
method operator filter= takes integer f returns nothing
set this = this*FILTER_MAX
if f == FILTER_OTHER then
call this.toggleAllFilters(true)
else
if f == FILTER_ATTACK then
set filters[this + FILTER_ATTACK] = true
else
set filters[this + f] = true
endif
endif
endmethod
static method registerVerbose takes trigger whichTrig, string var, real lbs, boolean GUI, integer filt returns thistype
local thistype index= getStrIndex(var, lbs)
local thistype i = 0
local thistype id = 0
if index == 0 then
return 0
elseif lastRegistered.rootTrig == whichTrig and lastRegistered.usingGUI then
set filters[lastRegistered*FILTER_MAX + filt] = true //allows GUI to register multiple different types of Damage filters to the same trigger
return 0
endif
if trigIndexStack[0] == 0 then
set count = count + 1 //List runs from index 10 and up
set id = count
else
set id = trigIndexStack[0]
set trigIndexStack[0] = trigIndexStack[id]
endif
set lastRegistered = id
set id.filter = filt
set id.rootTrig = whichTrig
set id.usingGUI = GUI
set id.weight = lbs
set id.eventStr = var
loop
set i = index.next
exitwhen i == 0 or lbs < i.weight
set index = i
endloop
set index.next = id
set id.next = i
//call BJDebugMsg("Registered " + I2S(id) + " to " + I2S(index) + " and before " + I2S(i))
return lastRegistered
endmethod
static method registerTrigger takes trigger t, string var, real lbs returns thistype
return registerVerbose(t, DamageTrigger.getVerboseStr(var), lbs, false, FILTER_OTHER)
endmethod
private static thistype prev = 0
static method getIndex takes trigger t, string eventName, real lbs returns thistype
local thistype index = getStrIndex(getVerboseStr(eventName), lbs)
loop
set prev = index
set index = index.next
exitwhen index == 0 or index.rootTrig == t
endloop
return index
endmethod
static method unregister takes trigger t, string eventName, real lbs, boolean reset returns boolean
local thistype index = getIndex(t, eventName, lbs)
if index == 0 then
return false
endif
set prev.next = index.next
set trigIndexStack[index] = trigIndexStack[0]
set trigIndexStack[0] = index
if reset then
set index.configured = false
set index = index*FILTER_MAX
call index.toggleAllFilters(false)
endif
return true
endmethod
static method damageUnit takes unit u, real life returns nothing
call SetWidgetLife(u, RMaxBJ(life, 0.41))
if life <= 0.405 then
if udg_DamageEventType < 0 then
call SetUnitExploded(u, true)
endif
//Kill the unit
set DamageTrigger.enabled = false
call UnitDamageTarget(udg_DamageEventSource, u, -999, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
set DamageTrigger.enabled = true
endif
endmethod
static method checkLife takes nothing returns boolean
if protectUnit != null then
if Damage.lifeTrigger != null then
call DestroyTrigger(Damage.lifeTrigger)
set Damage.lifeTrigger = null
endif
if GetUnitAbilityLevel(protectUnit, udg_DamageBlockingAbility) > 0 then
call UnitRemoveAbility(protectUnit, udg_DamageBlockingAbility)
call SetWidgetLife(protectUnit, protectLife)
elseif udg_IsDamageSpell or blocked then
call DamageTrigger.damageUnit(protectUnit, protectLife)
endif
if blocked then
set blocked = false
endif
set protectUnit = null
return true
endif
return false
endmethod
method run takes nothing returns nothing
local integer cat = this
local Damage d = Damage.index
if cat == MOD or not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
set dreaming = true
//call BJDebugMsg("Start of event running")
loop
set this = this.next
exitwhen this == 0
if cat == MOD then
exitwhen d.override or udg_DamageEventOverride
exitwhen this.weight >= 4.00 and udg_DamageEventAmount <= 0.00
endif
set eventIndex = this
if not this.trigFrozen and filters[this*FILTER_MAX + eventFilter] and IsTriggerEnabled(this.rootTrig) then
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_05()
if TriggerEvaluate(this.rootTrig) then
call TriggerExecute(this.rootTrig)
endif
if cat == MOD then
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_05()
if this.usingGUI then
set d.damage = udg_DamageEventAmount
set d.userType = udg_DamageEventType
set d.override = udg_DamageEventOverride
elseif this.next == 0 or this.next.usingGUI then //Might offer a slight performance improvement
call setGUIFromStruct(false)
endif
endif
call checkLife()
endif
endloop
//call BJDebugMsg("End of event running")
set dreaming = false
endif
endmethod
static method finish takes nothing returns nothing
if checkLife() and not blocked and udg_DamageEventAmount != 0.00 then
call DamageTrigger.AFTER.run()
endif
endmethod
static trigger array autoTriggers
static boolexpr array autoFuncs
static integer autoN = 0
static method operator [] takes code c returns trigger
local integer i = 0
local boolexpr b = Filter(c)
loop
if i == autoN then
set autoTriggers[i] = CreateTrigger()
set autoFuncs[i] = b
call TriggerAddCondition(autoTriggers[i], b)
exitwhen true
endif
set i = i + 1
exitwhen b == autoFuncs[i]
endloop
return autoTriggers[i]
endmethod
endstruct
struct Damage extends array
readonly unit sourceUnit //stores udg_DamageEventSource
readonly unit targetUnit //stores udg_DamageEventTarget
real damage //stores udg_DamageEventAmount
readonly real prevAmt //stores udg_DamageEventPrevAmt
integer userType //stores udg_DamageEventType
readonly boolean isCode
readonly boolean isSpell //stores udg_IsDamageSpell
boolean override
readonly static unit aoeSource = null
readonly static Damage index = 0
private static Damage damageStack = 0
private static integer count = 0 //The number of currently-running queued or sequential damage instances
private Damage stackRef
private DamageTrigger recursiveTrig
static trigger lifeTrigger = null //private
static method operator source takes nothing returns unit
return udg_DamageEventSource
endmethod
static method operator target takes nothing returns unit
return udg_DamageEventTarget
endmethod
static method operator amount takes nothing returns real
return Damage.index.damage
endmethod
static method operator amount= takes real r returns nothing
set Damage.index.damage = r
endmethod
private static method onAOEEnd takes nothing returns nothing
if udg_DamageEventAOE > 1 then
call DamageTrigger.AOE.run()
endif
set udg_DamageEventAOE = 0
set udg_DamageEventLevel = 0
set udg_EnhancedDamageTarget = null
set aoeSource = null
call GroupClear(udg_DamageEventAOEGroup)
endmethod
static method finish takes nothing returns nothing
local Damage i = 0
local integer exit
if canKick then
set canKick = false
set kicking = true
call DamageTrigger.finish()
if damageStack != 0 then
loop
set exit = damageStack
set sleepLevel = sleepLevel + 1
loop
set eventFilter = FILTER_CODE
set Damage.index = i.stackRef
call DamageTrigger.setGUIFromStruct(true)
call DamageTrigger.MOD.run()
call DamageTrigger.DAMAGE.run()
if udg_DamageEventAmount != 0.00 then
call DamageTrigger.damageUnit(udg_DamageEventTarget, GetWidgetLife(udg_DamageEventTarget) - udg_DamageEventAmount)
call DamageTrigger.AFTER.run()
endif
set i = i + 1
exitwhen i == exit
endloop
exitwhen i == damageStack
endloop
loop
set i = i - 1
set i.stackRef.recursiveTrig.trigFrozen = false
set i.stackRef.recursiveTrig.levelsDeep = 0
exitwhen i == 0
endloop
set damageStack = 0
endif
set dreaming = false
set sleepLevel = 0
call GroupClear(proclusGlobal)
call GroupClear(fischerMorrow)
set kicking = false
//call BJDebugMsg("Cleared up the groups")
endif
endmethod
private static method wakeUp takes nothing returns nothing
set alarmSet = false
set dreaming = false
set DamageTrigger.enabled = true
call finish()
call onAOEEnd()
set Damage.count = 0
set Damage.index = 0
set udg_DamageEventTarget = null
set udg_DamageEventSource = null
endmethod
private static method createLifeTrigger takes unit u, limitop op, real amount returns nothing
if not blocked then
set lifeTrigger = CreateTrigger()
call TriggerAddCondition(lifeTrigger, Filter(function DamageTrigger.finish))
call TriggerRegisterUnitStateEvent(lifeTrigger, u, UNIT_STATE_LIFE, op, amount)
endif
set protectUnit = u
endmethod
private method mitigate takes real newAmount, boolean recursive returns nothing
local real prevLife
local real life
local unit u = targetUnit
local real prevAmount = prevAmt
set life = GetWidgetLife(u)
if not isSpell then
if newAmount != prevAmount then
set life = life + prevAmount - newAmount
if GetUnitState(u, UNIT_STATE_MAX_LIFE) < life then
set protectLife = life - prevAmount
call UnitAddAbility(u, udg_DamageBlockingAbility)
endif
call SetWidgetLife(u, RMaxBJ(life, 0.42))
endif
call createLifeTrigger(u, LESS_THAN, RMaxBJ(0.41, life - prevAmount/2.00))
else
set protectLife = GetUnitState(u, UNIT_STATE_MAX_LIFE)
set prevLife = life
if life + prevAmount*0.75 > protectLife then
set life = RMaxBJ(protectLife - prevAmount/2.00, 1.00)
call SetWidgetLife(u, life)
set life = (life + protectLife)/2.00
else
set life = life + prevAmount*0.50
endif
set protectLife = prevLife - (prevAmount - (prevAmount - newAmount))
call createLifeTrigger(u, GREATER_THAN, life)
endif
set u = null
endmethod
private method getSpellAmount takes real amt returns real
local integer i = 6
local real mult = 1.00
set isSpell = amt < 0.00
if isSpell then
set amt = -amt
if IsUnitType(target, UNIT_TYPE_ETHEREAL) and not IsUnitType(target, UNIT_TYPE_HERO) then
set mult = mult*udg_DAMAGE_FACTOR_ETHEREAL //1.67
endif
if GetUnitAbilityLevel(target, 'Aegr') > 0 then
set mult = mult*udg_DAMAGE_FACTOR_ELUNES //0.80
endif
if udg_DmgEvBracers != 0 and IsUnitType(target, UNIT_TYPE_HERO) then
//Inline of UnitHasItemOfTypeBJ without the potential handle ID leak.
loop
set i = i - 1
if GetItemTypeId(UnitItemInSlot(target, i)) == udg_DmgEvBracers then
set mult = mult*udg_DAMAGE_FACTOR_BRACERS //0.67
exitwhen true
endif
exitwhen i == 0
endloop
endif
return amt*mult
endif
return amt
endmethod
private method addRecursive takes nothing returns boolean
if this.damage != 0.00 then
set this.recursiveTrig = DamageTrigger.eventIndex
if not this.isCode then
set this.isCode = true
endif
set inception = inception or DamageTrigger.eventIndex.inceptionTrig
if kicking and IsUnitInGroup(this.sourceUnit, proclusGlobal) and IsUnitInGroup(this.targetUnit, fischerMorrow) then
if inception and not DamageTrigger.eventIndex.trigFrozen then
set DamageTrigger.eventIndex.inceptionTrig = true
if DamageTrigger.eventIndex.levelsDeep < sleepLevel then
set DamageTrigger.eventIndex.levelsDeep = DamageTrigger.eventIndex.levelsDeep + 1
if DamageTrigger.eventIndex.levelsDeep >= LIMBO then
set DamageTrigger.eventIndex.trigFrozen = true
endif
endif
else
set DamageTrigger.eventIndex.trigFrozen = true
endif
endif
set damageStack.stackRef = this
set damageStack = damageStack + 1
//call BJDebugMsg("damageStack: " + I2S(damageStack) + " levelsDeep: " + I2S(DamageTrigger.eventIndex.levelsDeep) + " sleepLevel: " + I2S(sleepLevel))
return true
endif
set inception = false
return false
endmethod
private static method onDamageResponse takes nothing returns boolean
local Damage d = Damage.count + 1
set Damage.count = d
set d.sourceUnit = GetEventDamageSource()
set d.targetUnit = GetTriggerUnit()
set d.damage = d.getSpellAmount(GetEventDamage())
set d.prevAmt = d.damage
set d.userType = udg_NextDamageType
set d.isCode = udg_NextDamageType != 0 or udg_NextDamageOverride or dreaming
set d.override = udg_NextDamageOverride
set udg_NextDamageOverride = false
set udg_NextDamageType = 0
call finish() //in case the unit state event failed and the 0.00 second timer hasn't yet expired
if dreaming then
if d.addRecursive() then
set blocked = true
call d.mitigate(0.00, true)
else
set Damage.count = d - 1
endif
return false
endif
//Added 25 July 2017 to detect AOE damage or multiple single-target damage
if alarmSet then
if d.sourceUnit != aoeSource then
call onAOEEnd()
set aoeSource = d.sourceUnit
elseif d.targetUnit == udg_EnhancedDamageTarget then
set udg_DamageEventLevel= udg_DamageEventLevel + 1
elseif not IsUnitInGroup(d.targetUnit, udg_DamageEventAOEGroup) then
set udg_DamageEventAOE = udg_DamageEventAOE + 1
endif
else
call TimerStart(alarm, 0.00, false, function Damage.wakeUp)
set alarmSet = true
set aoeSource = d.sourceUnit
set udg_EnhancedDamageTarget= d.targetUnit
endif
set Damage.index = d
call DamageTrigger.setGUIFromStruct(true)
call GroupAddUnit(udg_DamageEventAOEGroup, udg_DamageEventTarget)
call GroupAddUnit(proclusGlobal, udg_DamageEventSource)
call GroupAddUnit(fischerMorrow, udg_DamageEventTarget)
if udg_DamageEventAmount == 0.00 then
call DamageTrigger.ZERO.run()
set canKick = true
call finish()
else
if d.isCode then
set eventFilter = FILTER_CODE
elseif udg_IsDamageSpell then
set eventFilter = FILTER_SPELL
else
set eventFilter = FILTER_ATTACK
endif
call DamageTrigger.MOD.run()
call DamageTrigger.DAMAGE.run()
//The damage amount is finalized.
call d.mitigate(udg_DamageEventAmount, false)
set canKick = true
endif
return false
endmethod
static method createDamageTrigger takes nothing returns nothing //private
set udg_DamageEventTrigger = CreateTrigger()
call TriggerAddCondition(udg_DamageEventTrigger, Filter(function thistype.onDamageResponse))
endmethod
static method setup takes nothing returns boolean //private
local integer i = udg_UDex
local unit u
if udg_UnitIndexEvent == 1.00 then
set u = udg_UDexUnits[i]
if CheckAddUnitToEngine(u) then
set udg_UnitDamageRegistered[i] = true
call TriggerRegisterUnitEvent(udg_DamageEventTrigger, u, EVENT_UNIT_DAMAGED)
call UnitAddAbility(u, udg_SpellDamageAbility)
call UnitMakeAbilityPermanent(u, true, udg_SpellDamageAbility)
endif
set u = null
else
set udg_HideDamageFrom[i] = false
if udg_UnitDamageRegistered[i] then
set udg_UnitDamageRegistered[i] = false
set udg_DamageEventsWasted = udg_DamageEventsWasted + 1
if udg_DamageEventsWasted == 32 then //After 32 registered units have been removed...
set udg_DamageEventsWasted = 0
//Rebuild the mass EVENT_UNIT_DAMAGED trigger:
call DestroyTrigger(udg_DamageEventTrigger)
call createDamageTrigger()
set i = udg_UDexNext[0]
loop
exitwhen i == 0
if udg_UnitDamageRegistered[i] then
call TriggerRegisterUnitEvent(udg_DamageEventTrigger, udg_UDexUnits[i], EVENT_UNIT_DAMAGED)
endif
set i = udg_UDexNext[i]
endloop
endif
endif
endif
return false
endmethod
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_DMGPKG()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_05()
endstruct
public function RegisterFromHook takes trigger whichTrig, string var, limitop op, real value returns nothing
call DamageTrigger.registerVerbose(whichTrig, var, value, true, GetHandleId(op))
endfunction
hook TriggerRegisterVariableEvent RegisterFromHook
function TriggerRegisterDamageEngineEx takes trigger whichTrig, string eventName, real value, integer f returns DamageTrigger
return DamageTrigger.registerVerbose(whichTrig, DamageTrigger.getVerboseStr(eventName), value, false, f)
endfunction
function TriggerRegisterDamageEngine takes trigger whichTrig, string eventName, real value returns DamageTrigger
return DamageTrigger.registerTrigger(whichTrig, eventName, value)
endfunction
function RegisterDamageEngineEx takes code c, string eventName, real value, integer f returns DamageTrigger
return TriggerRegisterDamageEngineEx(DamageTrigger[c], eventName, value, f)
endfunction
//Similar to TriggerRegisterDamageEvent, although takes code instead of trigger as the first argument.
function RegisterDamageEngine takes code c, string eventName, real value returns DamageTrigger
return RegisterDamageEngineEx(c, eventName, value, FILTER_OTHER)
endfunction
endlibrary
function InitTrig_Damage_Engine takes nothing returns nothing
local unit u = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA), 'uloc', 0, 0, 0)
local integer i = bj_MAX_PLAYERS //Fixed in 3.8
//Create this trigger with UnitIndexEvents in order to add and remove units
//as they are created or removed.
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 1.00)
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 2.00)
call TriggerAddCondition(t, Filter(function Damage.setup))
set t = null
//Run the configuration actions to set all configurables:
call ExecuteFunc("Trig_Damage_Engine_Config_Actions")
//Create trigger for storing all EVENT_UNIT_DAMAGED events.
call Damage.createDamageTrigger()
//Disable SpellDamageAbility for every player.
loop
set i = i - 1
call SetPlayerAbilityAvailable(Player(i), udg_SpellDamageAbility, false)
exitwhen i == 0
endloop
//Preload abilities.
call UnitAddAbility(u, udg_DamageBlockingAbility)
call UnitAddAbility(u, udg_SpellDamageAbility)
call RemoveUnit(u)
set u = null
endfunction
Updated to 5.7.0.1 which is strictly a performance improvement for the 0 damage event (nothing else). This improves performance even if you aren't using that event, so I will recommend this update to all users.
I've updated the demo map to reflect the aforementioned change requests and also beautified the map a little bit. Also I disabled the Recursion trigger which I only had enabled as a stress test.
I've cleaned up the main post quite a bit into organized tabs.
I am VERY pleased to announce the release of Damage Engine 3A, which can be found below (strictly a vJass MAJOR upgrade for those whose WarCraft 3 is out of date).
Thanks for supporting legacy editors (like myself that currently mods in 1.30.4),
I'll surely adapt now that I can use your system as updated + (v)JASS for my upcoming resources and this makes me proud of it.
However, I'm in doubt if there's a way we can get the actual unit damage even with "bonuses" without sorting into convoluted ways to do it?
Cause right now I can only think of achieving it by tracking the damage done by the unit on damage event and using a unit indexer to store it.
But that would need a unit to hit some unit to get the damage done which is not convenient right? Any ideas to achieve this flawlessly?
Thanks for supporting legacy editors (like myself that currently mods in 1.30.4),
I'll surely adapt now that I can use your system as updated + (v)JASS for my upcoming resources and this makes me proud of it.
However, I'm in doubt if there's a way we can get the actual unit damage even with "bonuses" without sorting into convoluted ways to do it?
Cause right now I can only think of achieving it by tracking the damage done by the unit on damage event and using a unit indexer to store it.
But that would need a unit to hit some unit to get the damage done which is not convenient right? Any ideas to achieve this flawlessly?
Well in your case you can benefit from the natives introduced in 1.29 for SetEventDamage. I am not sure if EVENT_PLAYER_UNIT_DAMAGED was a thing in 1.29 but if not then there is an easily-modifiable version of 3A I could fix up in your case which would get rid of the dependency on the cheat death ability and other weird HP modification stuff.
Well in your case you can benefit from the natives introduced in 1.29 for SetEventDamage. I am not sure if EVENT_PLAYER_UNIT_DAMAGED was a thing in 1.29 but if not then there is an easily-modifiable version of 3A I could fix up in your case which would get rid of the dependency on the cheat death ability and other weird HP modification stuff.
What I've meant is to get a unit's attack damage (that includes the bonus damage gained from abilities/items/and such...)
without making the unit hit someone just to track the damage it dealt. Let's say I want to create a projectile ability
that deals the actual unit's physical damage to the target. I don't know if someone already achieved this kind of mechanic
though I'm not sure if they can without sorting into damage event somehow or mapping the bonuses a unit acquired.
I'm aware of the new native GetUnitBaseDamage() but it only works for white damage though.
And I'm not interested of using the new natives yet as I want my resources to be compatible in both sides...
I'm just glad that you've managed to incorporate some DamageEngine 5+ features to 3A variant that is doable without breaking compatibility.
I think I have to live with the dependencies for now as it's up to the user for which DamageEngine version they have in their maps.
As long it's cross-compatible: let's say I'm using this 3A variant's API and a user that have DamageEngine 5+ wouldn't have a problem with it.
What I've meant is to get a unit's attack damage (that includes the bonus damage gained from abilities/items/and such...)
without making the unit hit someone just to track the damage it dealt. Let's say I want to create a projectile ability
that deals the actual unit's physical damage to the target. I don't know if someone already achieved this kind of mechanic
though I'm not sure if they can without sorting into damage event somehow or mapping the bonuses a unit acquired.
I'm aware of the new native GetUnitBaseDamage() but it only works for white damage though.
And I'm not interested of using the new natives yet as I want my resources to be compatible in both sides...
I'm just glad that you've managed to incorporate some DamageEngine 5+ features to 3A variant that is doable without breaking compatibility.
I think I have to live with the dependencies for now as it's up to the user for which DamageEngine version they have in their maps.
As long it's cross-compatible: let's say I'm using this 3A variant's API and a user that have DamageEngine 5+ wouldn't have a problem with it.
Without the natives for BlzGetUnitDamage/Dice/Sides (I think that's what they are called, I'd have to double check) you cannot determine a unit's base damage. You can get an average DPS from a unit's non-spell attacks over a prolonged fight, however this will not help if the unit had very recently picked up a damage enhancing item.
So if your goal is to be compatible with users while assessing a unit's physical damage in the process, my recommendation is to incorporate two variants in your map and separate which option is enabled via a Static If. This of course requires you to save two copies of your map - one for compatibility and the other with the new features:
Variant 1: Track the unit's non-spell damage per second and use that to determine the ancillary damage from this projectile.
Variant 2: Use the new natives and calculate the unit's base damage with its dice each time (so as to give variety to the damage amount in relation to the swinginess of the damage itself)
Variant 1 is for pre-1.32 users and is much less useful whereas Variant 2 will be much more useful and accurate, but without compatibility with old versions of WC3.
Without the natives for BlzGetUnitDamage/Dice/Sides (I think that's what they are called, I'd have to double check) you cannot determine a unit's base damage. You can get an average DPS from a unit's non-spell attacks over a prolonged fight, however this will not help if the unit had very recently picked up a damage enhancing item.
I'm thinking of a weird workaround using a modular illusion system
with damage modification + a premade hidden unit that have unlimited
health/regen to take the damage of this illusion and let them perform
this damage event outside the playable map. As hero illusion wouldn't
show in the top-left interface icon except for the minimap hero icon
that could be taken care of. It could be possible for this system to get
the pre-damage done before reductions take place right? I only find
bonus damage from externals like auras for example which the
illusion may not get because it's outside the map. I'll check this out.
Regarding the two variants for compatibility and features,
I've been thinking of implementing static ifs for new natives but
I'm afraid that it would still make the map unopenable because
It's saved on latest editor regardless of codes. Not sure with it,
But I'll consider to take this neat advice to get the advantage and
to remove the limits in expanded range of possibilities we have today.
I can only use 1.31.1 editor as I have it on my desktop. My internet
sucks and can't make it to download 30+ GB but I'll replace this crap
someday and might switch to mod in reforged editor as I'm also
craving for the new natives to incorporate in my spell-making vocabulary.
I'm thinking of a weird workaround using a modular illusion system
with damage modification + a premade hidden unit that have unlimited
health/regen to take the damage of this illusion and let them perform
this damage event outside the playable map. As hero illusion wouldn't
show in the top-left interface icon except for the minimap hero icon
that could be taken care of. It could be possible for this system to get
the pre-damage done before reductions take place right? I only find
bonus damage from externals like auras for example which the
illusion may not get because it's outside the map. I'll check this out.
Regarding the two variants for compatibility and features,
I've been thinking of implementing static ifs for new natives but
I'm afraid that it would still make the map unopenable because
It's saved on latest editor regardless of codes. Not sure with it,
But I'll consider to take this neat advice to get the advantage and
to remove the limits in expanded range of possibilities we have today.
I can only use 1.31.1 editor as I have it on my desktop. My internet
sucks and can't make it to download 30+ GB but I'll replace this crap
someday and might switch to mod in reforged editor as I'm also
craving for the new natives to incorporate in my spell-making vocabulary.
The problem of your idea will be damage variation with dice and side numbers. This will make calculations imprecise since every time you try to get the amount of damage dealt it might be different because of this 2 values that adds a random aspect to the final result, unless the unit have this numbers nullified. Unfortunately, for now I think the only way to do that precisely would be to keep track of bonusses added and removed until blizzard decides to stop being lazy and actually give us natives that manipulate the bonus values. I somewhat was able to keep track of bonusses using my library New Bonus but that requires that map maker to add and remove everything through the code.
I think it wouldn't be a problem though, based on 'how it works' tab
Bribe stated there that raw damage is already processed as dice and sides
are already computed... before EVENT_UNIT_DAMAGING runs.
EDIT:
I'm also fine with that as I'm aiming for a natural damage which
the function should return so dices and sides are intented to be part of it...
I don't know if this is just me, but so far when I've tried using the ArmorPierced feature, I haven't seen it make any difference. Even in the provided examples I don't seem to see any evidence of it ignoring values of armor on the target. Is this a problem other people have been having?
I don't know if this is just me, but so far when I've tried using the ArmorPierced feature, I haven't seen it make any difference. Even in the provided examples I don't seem to see any evidence of it ignoring values of armor on the target. Is this a problem other people have been having?
I have double checked the code and this should still be working. Make sure the following applies for you:
Your ArmorPierced value needs to be set *before* DamageModifierEvent 4.00. So 3.999999 or less and you can set that value, anything later or using any other event variable name will fail and not allow that to work.
ArmorPierced also only applies with the Normal damage type, so if you use any other damage type like Universal or Magic then the unit could have -10039423 armor or 39043 armor and the outcome is the same.
I have double checked the code and this should still be working. Make sure the following applies for you:
Your ArmorPierced value needs to be set *before* DamageModifierEvent 4.00. So 3.999999 or less and you can set that value, anything later or using any other event variable name will fail and not allow that to work.
ArmorPierced also only applies with the Normal damage type, so if you use any other damage type like Universal or Magic then the unit could have -10039423 armor or 39043 armor and the outcome is the same.
Yeah, I tried that, it was both according to the demonstration provided with the Knight, as well as my own test with a Grunt.
The DamageModifierEvent, following the Knight's example, was "DamageModifierEvent becomes Less than 1.00", which I will try with "Equal to 1.00" instead.
For the Knight the DamageEventType was set to DamageTypeCriticalStrike which I am admittedly unsure as to what that does, but in the reports it seems to indicate that it is dealing Damage_Type_Normal, and I tried setting it as such for the Grunt tests, too, without the DamageTypeCriticalStrike.
Strangely enough, when testing the Grunt against the nearby Ogre Mauler with Reports on, it kept showing the damage the Ogre Mauler was dealing to me, even when I attacked it. For some reason it mixed up their values.
The tests for DamageModiferEvent being equal to 1.00 didn't help unfortunately, sort of did the same thing. I'll provide a screenshot of the report stats I'm looking at.
If I already have 1.31.1, does it mean that I can only use DamageEngine v5.4.2.3?
I've downloaded the latest (v5.7.0.1) but I can't open it. Is it because of the new native
implemented specifically from what you've stated in 5.5+ version of it?
^ It is because the map is saved in reforged editor that makes it unopenable.
Is implementing this function at the top of the (v)JASS script that is v5.7.0.1 would enable
me to use the latest library since I think that's only the reason what makes it incompatible?
^ Should be implemented inside the DamageEngine library, not just on the top of the script.
How about providing us a demo map of 5.7.0.1 that is saved on 1.31.1 and having that
script already in the top of the library to make the demo map openable somehow... Or it's not possible?
^ I apologize for this one as I've just realized this shouldn't be asked.
This would only force you to downgrade which I think is not necessary for just the sake of it.
There are bugs fixed after DamageEngine v5.4.2.3 that makes me hesitate to use it than the latest one.
Nevermind about this, I've managed to get the latest DamageEngine version work with
1.31.1 editor without problems as of now; by only requiring the user a few minutes to achieve it
by using v5.4.3.2 as a demo map for importing, recreating the new system variables
then implementing the custom function you've just made to replicate the new native into the library.
Now I can use this as a counterpart for 3A in legacy variant and this one on a
reforged variant for my resources as I would like to consider this advice of yours.
Yeah, I tried that, it was both according to the demonstration provided with the Knight, as well as my own test with a Grunt.
The DamageModifierEvent, following the Knight's example, was "DamageModifierEvent becomes Less than 1.00", which I will try with "Equal to 1.00" instead.
For the Knight the DamageEventType was set to DamageTypeCriticalStrike which I am admittedly unsure as to what that does, but in the reports it seems to indicate that it is dealing Damage_Type_Normal, and I tried setting it as such for the Grunt tests, too, without the DamageTypeCriticalStrike.
Strangely enough, when testing the Grunt against the nearby Ogre Mauler with Reports on, it kept showing the damage the Ogre Mauler was dealing to me, even when I attacked it. For some reason it mixed up their values.
The tests for DamageModiferEvent being equal to 1.00 didn't help unfortunately, sort of did the same thing. I'll provide a screenshot of the report stats I'm looking at.
I ran some very quick tests on it (I didn't test it as thoroughly yet) but it seems to still be working. I can see in your screenshot that you are using a slightly older version of my testmap. Are you on the latest version?
I ran some very quick tests on it (I didn't test it as thoroughly yet) but it seems to still be working. I can see in your screenshot that you are using a slightly older version of my testmap. Are you on the latest version?
I think I just ran the latest patch now, just in case that was it, but am still encountering the same problem. I've also been using the latest version I can from here, using the non-lua version. I'm not sure why it seems to be behaving in this way for me, am I correct in believing Armor Pierced is supposed to ignore a specific amount of armor? So, for example, if the target has 10 armor, and you're set to pierce 5 armor, it should deal damage as if the target has 5 armor?
I think I just ran the latest patch now, just in case that was it, but am still encountering the same problem. I've also been using the latest version I can from here, using the non-lua version. I'm not sure why it seems to be behaving in this way for me, am I correct in believing Armor Pierced is supposed to ignore a specific amount of armor? So, for example, if the target has 10 armor, and you're set to pierce 5 armor, it should deal damage as if the target has 5 armor?
That is the way it's supposed to work yes. In my brief testing I had debugged the value of the armor prior to being changed and after being changed, and the target's armor was stating that it was being modified by -50 and +50 respectively.
If there is still a problem I can imagine it's due to the way I switched from vJass to GUI and vice versa in the code itself. But if it's not that, I would have to do some more stringent testing such as measuring a unit with no damage dice and then testing it with and without the armor modification to see if there's fluctuation (ie. maybe Blizzard doesn't recognize armor changes anymore if they're done via the DAMAGING event). I hope it's just something simple like my code is wrong, because that's something I can actually fix.
That is the way it's supposed to work yes. In my brief testing I had debugged the value of the armor prior to being changed and after being changed, and the target's armor was stating that it was being modified by -50 and +50 respectively.
If there is still a problem I can imagine it's due to the way I switched from vJass to GUI and vice versa in the code itself. But if it's not that, I would have to do some more stringent testing such as measuring a unit with no damage dice and then testing it with and without the armor modification to see if there's fluctuation (ie. maybe Blizzard doesn't recognize armor changes anymore if they're done via the DAMAGING event). I hope it's just something simple like my code is wrong, because that's something I can actually fix.
It's definitely a strange area where admittedly I was, in part, hoping my assessment of what Armor Pierced was supposed to do. One thing I have found strange, too, is when I download the latest version of this mechanic, there's quite a few lines in the GUI that are disabled within active triggers. I'm not sure what causes this, but one of the areas is the Report trigger, where it doesn't seem to want to show the damage reduced or the damage increased.
Perhaps even more bizarrely is that when I have report active, in hindsight it only seems to function when one of my units is being damaged. So, if I deal damage to one of my own units, it shows the report as per the usual, but when I deal damage to the Neutral Hostile Creeps, it doesn't show at all. It sort of just occurred to me that might be why it's doing it.
Also also, I found that the map preferences were set to "default map status" rather than the latest version, which may explain why I was encountering a bizarre scenario where the Kobold Geomancer was dealing bonus damage to my Knight, despite the Knight having Heavy Armor and the Geomancer dealing Piercing damage. Still, even when setting the map preference to latest version, it still seemed to encounter the same issues.
@Wazzz,
There might be something wrong on your end.
These features that you're having problems with are working fine. (at least in my part)
In this case do these steps first before jumping into that conclusion which may help.
Make sure to re-download. After that replace the script inside of the DamageEngine
inside the demo map with this script instead: (only debugs the piercing armor part)
After that set the value of DamageEventArmorPierced variable to somewhere high (99999) just to
make sure the changed value is noticeable during testing, ensuring that setArmor() is called and: call BlzSetUnitArmor(udg_DamageEventTarget, BlzGetUnitArmor(udg_DamageEventTarget) + pierce)
should work properly just like in my results:
(the knight already killed the geomancer in one blow due to critical strike + 99999 armor being pierced)
Note that only simple debug was made to identify if these involved functions work properly on your end.
If none of these displays, I'm afraid it's on your end IF Bribe can't reproduce the problem you're having.
@Wazzz,
There might be something wrong on your end.
These features that you're having problems with are working fine. (at least in my part)
In this case do these steps first before jumping into that conclusion which may help.
Make sure to re-download. After that replace the script inside of the DamageEngine
inside the demo map with this script instead: (only debugs the piercing armor part)
After that set the value of DamageEventArmorPierced variable to somewhere high (99999) just to
make sure the changed value is noticeable during testing, ensuring that setArmor() is called and: call BlzSetUnitArmor(udg_DamageEventTarget, BlzGetUnitArmor(udg_DamageEventTarget) + pierce)
should work properly just like in my results:
(the knight already killed the geomancer in one blow due to critical strike + 99999 armor being pierced)
Note that only simple debug was made to identify if these involved functions work properly on your end.
If none of these displays, I'm afraid it's on your end IF Bribe can't reproduce the problem you're having.
It's a very bizarre occurrence for me, where sometimes the After: shows 5.000 for when the Knight is hit, and other times it shows 0.000 for when the Kobold is hit, but on the Critical Strike it just comes up the same. So it seems for some reason, when I run it, the armor value isn't being changed, and yet it works for other people.
What version are you running? I'm not quite sure why it isn't working for me on the latest version of Warcraft, albeit I am not running it on Public Test Realm or anything like that.
This is not bizarre but normal as setArmor() is called on many events.
Probably it returns 0 because the unit is not referenced on that event.
Anyway that's not the problem... but this part:
What do you mean on Critical Strike it doesn't show? You mean if red text spawns there's no -99999 value displayed?
Can you remove the "random integer" condition on Knight's damage script to simulate this everytime and type report?
You must give detail on this one as seeing that the problem is just on somewhere that specific part. Didn't read in-depth
about the code of Damage Engine as a whole so I can't tell the execution of things here and why they should fail in case...
What version are you running? I'm not quite sure why it isn't working for me on the latest version of Warcraft, albeit I am not running it on Public Test Realm or anything like that.
I'm even using 1.31.1 which is the latest legacy patch before reforged.
If the system is problematic for you, other users that are using reforged should be problematic too.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.