• Check out the results of the Techtree Contest #19!
  • 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!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • Create a void inspired texture for Warcraft 3 and enter Hive's 34th Texturing Contest: Void! Click here to enter!
  • The Hive's 22nd Icon Contest: Creep Abilities is now concluded, time to vote for your favourite set of icons! Click here to vote!

Damage Engine 5.A.0.0

This bundle is marked as director's cut. It exceeds all expectations and excels in every regard.
Damage Engine

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.)
    • DamageEventDamageT - the damage type - for comprehensive info, click here.
    • 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:
    • DamageEventAmount
    • DamageEventAttackT/DamageT/WeaponT/ArmorT/DefenseT
    • DamageEventArmorPierced
    • DamageEventType
    • 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).
  • For people who did not update their game to Reforged / have not updated to Reforged yet. Alternatively, adding the below JASS script to the top of the DamageEngine library SHOULD enable it to work too:

    JASS:
    function BlzGetEventIsAttack takes nothing returns boolean
         return BlzGetEventDamageType() == DAMAGE_TYPE_NORMAL
    endfunction
  • 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:
  1. Use WarCraft 3 Version 1.32
  2. If you're upgrading from 3.8 or prior, please delete the entire "Damage Engine" category from your map.
  3. 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. --------
      • -------- - --------
      • Set VariableSet DamageTypePureExplosive = -2
      • Set VariableSet DamageTypeExplosive = -1
      • Set VariableSet DamageTypeCode = 1
      • Set VariableSet DamageTypePure = 2
      • -------- - --------
      • Set VariableSet DamageTypeHeal = 3
      • Set VariableSet DamageTypeBlocked = 4
      • Set VariableSet DamageTypeReduced = 5
      • -------- - --------
      • Set VariableSet DamageTypeCriticalStrike = 6
      • -------- - --------
      • Custom script: call DamageEngine_DebugStr()

BribeFromTheHive/DamageEngine

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.
    • TriggerRegisterDamageEvent -> TriggerRegisterDamageEngine
    • RegisterDamageEvent -> RegisterDamageEngine
      • 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


Cross-Compatibility:

BribeFromTheHive/DamageEngine

Keywords:unit indexer, damage detection, damage event, weep, nestharus, looking_for_help, EVENT_PLAYER_UNIT_DAMAGED, damage engine, spell, physical, EVENT_PLAYER_UNIT_DAMAGED, attacktype, damagetype, weapontype, armortype, defensetype, BlzSetEventDamage
Previews
Contents

Damage Engine Demo Map (Map)

Lua Damage Engine 2.0.0.0 (Map)

Reviews
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...
I think it's based on the boolean isAttack in UnitDamageTarget. If it is true, BlzGetEventIsAttack is true.
unfortunately i dont think that is the case. If i remember from my testings the boolean value in the damage native is pretty much useless and the EventIsAttack antive was not considering it. Could be wrong though.
 
I downloaded the Damage System map and tried using the various units in the map.
In most cases it displays a texttag with the damage amount on it as intended, but I noticed some inconsistencies:
  1. Casting Frost Nova on an enemy unit shows no damage tag even though it does deal damage to the unit(s).
  2. Attacking a unit with the Dryad only displays the Slow Poison damage over time, but not the initial attack damage dealt.
  3. Sea Witch's Frost Arrow damage is displayed with auto-cast off, but turning it on removes the damage display.
  4. No damage is shown when Firelord's Incinerate ability has auto-cast toggled on, but with auto-cast off it displays a texttag again.
  5. Letting the Phoenix hit an enemy unit with its Phoenix Fire ability does not display any initial damage and only shows damage over time effect.
  6. Any hero with Orb of Corruption, Orb of Frost or Orb of Venom will also lose their damage display (as well as Orb of Slow, but only when the 25% chance to slow hits)
 
I downloaded the Damage System map and tried using the various units in the map.
In most cases it displays a texttag with the damage amount on it as intended, but I noticed some inconsistencies:
  1. Casting Frost Nova on an enemy unit shows no damage tag even though it does deal damage to the unit(s).
  2. Attacking a unit with the Dryad only displays the Slow Poison damage over time, but not the initial attack damage dealt.
  3. Sea Witch's Frost Arrow damage is displayed with auto-cast off, but turning it on removes the damage display.
  4. No damage is shown when Firelord's Incinerate ability has auto-cast toggled on, but with auto-cast off it displays a texttag again.
  5. Letting the Phoenix hit an enemy unit with its Phoenix Fire ability does not display any initial damage and only shows damage over time effect.
  6. Any hero with Orb of Corruption, Orb of Frost or Orb of Venom will also lose their damage display (as well as Orb of Slow, but only when the 25% chance to slow hits)
I'm pretty sure the Demo Triggers folder is meant to be used as an example that you can follow in your own map. It's not like you're intended to just copy and paste his demonstrations.

Anyway, I imagine this trigger would work for all cases:
  • Damage As Text
    • Events
      • Game - DamageEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • Set VariableSet DamageString = (String(DamageEventAmount))
      • Game - Display to (All players) the text: (String(DamageEventAmount))
You can do whatever you want with the DamageString variable, display it in Floating Text, use a 3rd party Text system like the one in the demo, etc.
 
I'm pretty sure the Demo Triggers folder is meant to be used as an example that you can follow in your own map. It's not like you're intended to just copy and paste his demonstrations.

Anyway, I imagine this trigger would work for all cases:
  • Damage As Text
    • Events
      • Game - DamageEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • Set VariableSet DamageString = (String(DamageEventAmount))
      • Game - Display to (All players) the text: (String(DamageEventAmount))
You can do whatever you want with the DamageString variable, display it in Floating Text, use a 3rd party Text system like the one in the demo, etc.
Yes, the demo triggers just being examples is a fair point, but it does not change the fact that I expect them to display correct information.

What if I wanted to display the damage output of all spells and attacks? Then both the demo trigger and the one you've supplied me with here fails to display the correct damage output for the Dryad. They will both output "4.0" when the correct output should be "14.0" (attack damage) + "4.0" (slow poison).
If you try to cast Frost Nova with the trigger you've supplied me with here then it does not produce an output at all, but Frost Nova does in fact deal damage in reality.
 
Yes, the demo triggers just being examples is a fair point, but it does not change the fact that I expect them to display correct information.

What if I wanted to display the damage output of all spells and attacks? Then both the demo trigger and the one you've supplied me with here fails to display the correct damage output for the Dryad. They will both output "4.0" when the correct output should be "14.0" (attack damage) + "4.0" (slow poison).
If you try to cast Frost Nova with the trigger you've supplied me with here then it does not produce an output at all, but Frost Nova does in fact deal damage in reality.
Try messing around with different Events. The one I used was what I've personally never had any issues with.

There's always the possibility that there's an actual bug in the system but I find that unlikely.
 
Try messing around with different Events. The one I used was what I've personally never had any issues with.

There's always the possibility that there's an actual bug in the system but I find that unlikely.
I sort of figured it out. I tried all the events and none of them worked with Frost Nova or Dryad, but I was using the "Lua Damage Engine 2.0.w3x" since the map I wanted to use this system for is a LUA map.

After trying all the events I switched to the other map that's included in the same downloaded .zip. And everything works as expected in the "Damage Engine 5.A.0.0.w3x" version of the map (even the demo / example triggers). Therefore I suspect there's a bug in the LUA version of the map as I expect the events to behave the same in both maps.
 
For anyone else wanting to use the LUA version of this library I've found out that the library errors in 2 places during events like Frost Nova, Frost Arrow, Slow Poison, Incinerate (and many more).
Lua:
local function failsafeClear()
    setArmor(true) -- 1st error occurs inside this call
    canKick = true
    kicking, totem = nil, nil
    runEvent(_DAMAGED) -- 2nd error occurs inside this call
    eventsRun = true
    finish()
end
1st error if _USE_ARMOR_MOD config is set to true:
Lua:
local function setArmor(reset)
    if _USE_ARMOR_MOD then
        if reset then -- We already know reset = true from failsafeClear()
            pierce = current.armorPierced -- 1st error occurs here as "current" is nil
            -- ... more code that never gets to run
        end
    end
end
2nd error always occurs no matter what config you have:
Lua:
local function damageOrAfter() return current.damageType == DAMAGE_TYPE_UNKNOWN end -- 2nd error occurs here as "current" is nil
breakCheck[_DAMAGED]    = damageOrAfter
local function runEvent(head)
    local check = breakCheck[head] or defaultCheck
    if dreaming or check() then -- "dreaming" = false here so it proceeds to "check()"
        return
    end
    -- ... more code that never gets to run
end

The easiest soultion seems to be modifying the failsafeClear function to this:
Lua:
local function failsafeClear()
    if current ~= nil then
        setArmor(true)
    end
    canKick = true
    kicking, totem = nil, nil
    if current ~= nil then
        runEvent(_DAMAGED)
    end
    eventsRun = true
    finish()
end

The JASS version of the library already checks for udg_DamageEventDamageT != 0 before running DAMAGE.run() within the failsafeClear method and it skips the issue with USE_ARMOR_MOD because it only references non-nullable global variables.
 
Thanks for the detailed feedback, that's great. Lua Damage Engine has a lot of issues beyond that, and if I ever code in Lua again I'll certainly look into it, but I am waiting for Sumneko to make some needed QoL changes to LLS. I also can't use TypeScript2Lua because it does not do metatables natively, which resources like Hook and GlobalVariableRemapper need.
 
That's fair. I've actually been using this damage engine for several years now in a TypeScript project, but it was based off of an older version of this library (originally I rewrote the GUI version to TypeScript) and coming back to modding I wanted to see if there was a newer version I could use. Since it's a Lua map that's the one I tried to use, but it turned out to have issues. Since hooks and global variable remapping aren't useful for me they're not really a concern and fixing these nil references + adding a basic event table I can reference seems to make it work well for me. At least the little bit I've tested it so far.
 
Is the variable DamageEventPrevAmt is deprecated or still in use? I actually don't know what it does now.
Under the "features" in the description, it say "DamageEventPrevAmt: the amount of damage prior to being modified by the game engine".
I don't know if I've really used it, but I can see many good usecases for this value.

In other words: This is a "read only" value that might be useful for "boost/reduce base value of the attack".

From what I can see in the vjass code, it seems to work like the following:
If I am correct:

You have a unit dealing 100 damage, with bonus 25% of target's missing hp and a 25% chance to critical hit for 2x damage.

You don't want this low-health bonus to also critial hit and don't really want to think about the order of triggers that much because it can get confusing and also change over time if more triggers are added.

Then you can in your critical hit trigger have this if(random(0,1) > 0.25) { DamageEventAmount = DamageEventAmount + DamageEventPrevAmt } (adds the "base value" of the attack to the current damage. Possible to be more sophisticated, but this is an example)
Execute trigger be DamageEventAmount = DamageEventAmount + 0.25 * (Unit State Max Health for (udg_DamageEventTarget) - Unit State Current Health for (udg_DamageEventTarget))

Because the crit-trigger use DamageEventPrevAmt instead of the DamageEventAmount directly, it don't care if the "execute trigger" is run before or after.

If source deals 100 damage, target has 123 missing hp, and source crits: (100 + 0.25 * 123 + 100) = 230.75
If crit used DamageEventAmount directly with the same values it'd deal either: (100 + 0.25 * 123) * 2 = 261.5 OR (100 * 2) + 0.25 * 123 = 230.75 depending on how you setup the event trigger's "value" defining the order of operation.
 
how does Armor Pierce work in this? To test, I created a knight and removed the proc rate from his ability.

call UnitDamageTargetBJ(knight, u, 100., ATTACK_TYPE_MELEE, DAMAGE_TYPE_NORMAL)

with 1000 Armor Pen deals 73 and some change damage. This doesn't seem right. Even weirder, when you change the maps' Armor Constant from 0.06, the damage dealt here changes? Changing to .02 resulted in 89 damage dealt. Setting Armor Constant to negative values actually increases the damage they take, (154 @ -6%). What is intriguing me is that there is no way to grab this Constant afaik, but changing it does appear to be doing something (more precisely, in the opposite direction of what you'd expect in some tests).

(removed a ton here, I think my issue was calculating eHP based off the % rather than just interpreting stuff like -20% Reduction as taking 20% more. Also, seems WC3's negative armor "cap" is -20, at 170% taken)

=============
my suggestions;



Negative Armor (for custom Armor Constants) is problematic, if my understanding is correct. Iirc, The formula for negative armor 2-0.94^( -armor ) is independent of Armor Constant. This means in maps with lower than 6%, that negative Armor is still as effective as in base WC3, and vice versa. Functionality that would allow users to correct this would be nice (in my map, however, I pretty much just factored for this so "fixing" it isn't necessary).

If a cap on the penalty from negative Armor exists (or even if it doesn't), functionality that allows you to have either behavior would be nice as well.
 
Last edited:
it is a bit unfortunate that "special" damage can't be handled by the system currently. ATTACK_TYPE_NORMAL and DAMAGE_TYPE_MAGIC gave a damageType reduced by %SpellResist and Armor. This damage doesn't apply Armor Pierce. Can rework some effects to account for this, but it is a bummer nonetheless. I get approaching from WC3s angle of attack types being relegated to the damage table, but for custom maps that don't care about it, you are essentially using attack/damage types for deciding what resists to apply. A flag to apply ArmorPierce regardless of damageType would probably suffice.
 
it is a bit unfortunate that "special" damage can't be handled by the system currently. ATTACK_TYPE_NORMAL and DAMAGE_TYPE_MAGIC gave a damageType reduced by %SpellResist and Armor. This damage doesn't apply Armor Pierce. Can rework some effects to account for this, but it is a bummer nonetheless. I get approaching from WC3s angle of attack types being relegated to the damage table, but for custom maps that don't care about it, you are essentially using attack/damage types for deciding what resists to apply. A flag to apply ArmorPierce regardless of damageType would probably suffice.
armor doesn't apply at all unless you're using the physical damage type. To get around this, @SpasMaster uses physical damage types for every spell, and it allows WarCraft 3's armor to act as magic reduction.

Another solution is to code more robust magic reduction or amplification, perhaps with a UX mod achieved using frames so the user knows visually how much magic volatility they have.
 
Is Pure damage truly unstoppable except with Invulnerability? Does Banish or Spell Immunity interact with Pure?
DAMAGE_TYPE_UNIVERSAL can hit spell immune targets, so yes invulnerability is the only way to stop it.

I can't remember if Banish works to amplify spell damage from UNIVERSAL. If the attack type is MAGIC it should amplify in principle.
 
Hello Mr Bribe,

I'm getting confused by the behavior of IsDamageAttack.

Context:

I'm trying to make an ability than damage enemies and is considered as an attack (which allow to trigger on attack hit effects).
My function calls UnitDamageTarget to damage the targets with the following parameter.
JASS:
call UnitDamageTarget(druid, u, dmg, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_ENHANCED, WEAPON_TYPE_CLAW_HEAVY_SLICE)

To my understanding, UnitDamageTarget has the following parameters:
  • 1/ unit source - who does the damage
  • 2/ unit target - who receive the damage
  • 3/ real amount - how much damage
  • 4/ boolean attack - is the damage from an attack ?
  • 5/ boolean ranged - is the damage from a ranged attack/spell ?
  • 6/ attacktype attackType - which attack type
  • 7/ damagetype damageType - which damage type
  • 8/ weapongtype weaponType - which weapon type

With number 4/ what's interesting here.
My function was triggered, and properly called with parameter 4/ set to true. Damage of type DAMAGE_TYPE_ENHANCED is correctly applied.

However, after testing, I find:
  • IsDamageCode = true. Expected as I'm calling from Jass function.
  • IsDamageAttack = false. This is breaking my brain right now.
  • IsDamageSpell = true. Edit: Expected as ATTACK_TYPE_NORMAL is spell in GUI. Tried with ATTACK_TYPE_HERO to make sure, IsDamageSpell = false but IsDamageAttack = false still.

IsDamageAttack.PNG

Maybe it's just that my brain stopped working at the late hour :grin:
Am I missing something obvious ? :confused:2
Isn't the 4th parameter what I think it is ? :confused:2



PS: here is the trigger used for testing.
  • Events
    • Game - ArmorDamageEvent becomes Equal to 1.00
  • Conditions
    • DamageEventAmount Greater than 0.00
  • Actions
    • Game - Display to (All players) for 3.00 seconds the text: ((Name of DamageEventSource) + ( is damaging + (Name of DamageEventTarget)))
    • If (All conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • IsDamageCode to True
    • Then - Actions
      • Game - Display to (All players) for 3.00 seconds the text: CodeDamage
    • Else - Actions
      • Game - Display to (All players) for 3.00 seconds the text: Not Code Damage
    • If (All conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • IsDamageAttack to True
    • Then - Actions
      • Game - Display to (All players) for 3.00 seconds the text: Attack Damage
    • Else - Actions
      • Game - Display to (All players) for 3.00 seconds the text: Not Attack Damage
    • If (All conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • IsDamageSpell to True
    • Then - Actions
      • Game - Display to (All players) for 3.00 seconds the text: Spell Damage
    • Else - Actions
      • Game - Display to (All players) for 3.00 seconds the text: Not Spell Damage
    • Game - Display to (All players) for 3.00 seconds the text: 'Damage Type: + ((String(DamageEventDamageT)) + ( (Enhanced is: + ((String(DAMAGE_TYPE_ENHANCED)) + ) ))))
 
Last edited:
DAMAGE_TYPE_UNIVERSAL can hit spell immune targets, so yes invulnerability is the only way to stop it.

I can't remember if Banish works to amplify spell damage from UNIVERSAL. If the attack type is MAGIC it should amplify in principle.
If a unit is in Ethereal form (Banished) it doesn't seem to take damage of Universal type.
 
DAMAGE_TYPE_UNIVERSAL can hit spell immune targets, so yes invulnerability is the only way to stop it.

I can't remember if Banish works to amplify spell damage from UNIVERSAL. If the attack type is MAGIC it should amplify in principle.
This should be sufficient for my need. Thank you 🙏
 
I'm getting confused by the behavior of IsDamageAttack.
Never even thought to test damaging a unit with true for isAttack, (something about my WC3 instinct makes me not surprised this doesn't work), but the way you fix is by setting a flag before dealing the damage, then modifying the condition for onHit effects to be;
if ( FLAG or BlzGetEventIsAttack() ) then
 
Never even thought to test damaging a unit with true for isAttack, (something about my WC3 instinct makes me not surprised this doesn't work), but the way you fix is by setting a flag before dealing the damage, then modifying the condition for onHit effects to be;
if ( FLAG or BlzGetEventIsAttack() ) then
Thanks for the reply.
I forgot to edit my answer, but found that setting udg_NextDamageIsAttack = true before calling the function will do the trick, doing effectively what you're describing inside the damage engine.

I'm still confused on what the 4th argument of UnitDamageTarget is doing, but at least I can do what i wanted :grin:
 
I just don't get it. Why is it so hard to track a critical hit on an item or blademaster?
Why don't these 2 filters work? What are they for if they don't filter crits?

  • TEST
    • Events
      • Game - PreDamageEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • Custom script: //! runtextmacro DAMAGE_TRIGGER_CONFIG()
      • Set VariableSet DamageFilterDamageT = DamageTypeCriticalStrike
      • Custom script: //! runtextmacro DAMAGE_TRIGGER_CONFIG_END()
  • TEST1
    • Events
      • Game - PreDamageEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • Custom script: //! runtextmacro DAMAGE_TRIGGER_CONFIG()
      • Set VariableSet DamageFilterType = DamageTypeCriticalStrike
      • Custom script: //! runtextmacro DAMAGE_TRIGGER_CONFIG_END()
The only overly convoluted and inefficient way I could think of to track crits was by monitoring a unit’s average damage through separate variables and detecting sudden spikes...
 
I just don't get it. Why is it so hard to track a critical hit on an item or blademaster?
Why don't these 2 filters work? What are they for if they don't filter crits?

  • TEST
    • Events
      • Game - PreDamageEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • Custom script: //! runtextmacro DAMAGE_TRIGGER_CONFIG()
      • Set VariableSet DamageFilterDamageT = DamageTypeCriticalStrike
      • Custom script: //! runtextmacro DAMAGE_TRIGGER_CONFIG_END()
  • TEST1
    • Events
      • Game - PreDamageEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • Custom script: //! runtextmacro DAMAGE_TRIGGER_CONFIG()
      • Set VariableSet DamageFilterType = DamageTypeCriticalStrike
      • Custom script: //! runtextmacro DAMAGE_TRIGGER_CONFIG_END()
The only overly convoluted and inefficient way I could think of to track crits was by monitoring a unit’s average damage through separate variables and detecting sudden spikes...
1. I think the "DamageFilter..." variables don't work anymore, based on what I've tested.
2. The Damage Engine can't track criticals from vanilla abilities (Critical Strike of units and items) because there's no specific event or condition for the proc of a vanilla Crit; it can only track user-made criticals using the engine. Technically, what you're doing by monitoring a unit's average damage to detect sudden spikes is a workaround, but can become very complex when there are other damage-modifying abilities involved.
 
1. I think the "DamageFilter..." variables don't work anymore, based on what I've tested.
2. The Damage Engine can't track criticals from vanilla abilities (Critical Strike of units and items) because there's no specific event or condition for the proc of a vanilla Crit; it can only track user-made criticals using the engine. Technically, what you're doing by monitoring a unit's average damage to detect sudden spikes is a workaround, but can become very complex when there are other damage-modifying abilities involved.
In all other respects the filters work well, so my questions are directly to Bribe

 
Hi guys, how is it possible to add extra features into the system?

I'm looking to add a life steal, evasion, spell vamp, armor penetration, spell penetration,
You could use
New Bonus [vJASS][LUA] that have some of that, but I had some issues with that system because I wanted some of my own bonuses not present in that system and wanted them to be handled in a similar way, so I wrote my own system I call ExtendableBonusSystem

Using the extendable bonus system (or make it yourself) and damage engine, you could make a bonus and use damage engines armor penetration:

The other bonuses is possible to add in a fairly similar way

I make 2 real variable arrays: armor_penetration_percent and armor_penetration_flat, set up the bonus (jass), the "functional part" of the bonus (GUI-code) and then add said bonus to a unit.

call AddUnitBonus(u, BonusArmorPenetrationPercent.typeid, 0.20) to ignore 20% of enemy armor

call LinkBonusToItem(u, BonusArmorPenetrationPercent.typeid, 0.20, i) link same bonus to an item worn by a hero, removed when dropped.

or link it to a buff. Can also just ignore "flat amount" of armor.

JASS:
library ExtendableBonusesArmorPen requires ExtendableBonusSystem, Table
    globals
        // optional alias for `BonusArmorPenetrationPercent.typeid` and `BonusArmorPenetrationFlat.typeid`, 
        // used when get, set, add bonuses
        integer BONUS_ARMOR_PEN_FLAT
        integer BONUS_ARMOR_PEN_PERCENT
    endglobals

    // ---- Armor Penetration ----
    struct BonusArmorPenetrationFlat extends ExtendableBonusSystem_ExtendableBonusPlugin
        method Get takes unit u returns real
            return udg_armor_penetration_flat[GetUnitUserData(u)]
        endmethod
        method Set takes unit u, real value returns nothing
            set udg_armor_penetration_flat[GetUnitUserData(u)] = value
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_ARMOR_PEN_FLAT = ExtendableBonus.Register(BonusArmorPenetrationFlat.allocate())
        endmethod
    endstruct
    struct BonusArmorPenetrationPercent extends ExtendableBonusSystem_ExtendableBonusPlugin
        method Get takes unit u returns real
            return udg_armor_penetration_percent[GetUnitUserData(u)]
        endmethod
        method Set takes unit u, real value returns nothing
            set udg_armor_penetration_percent[GetUnitUserData(u)] = value
        endmethod
        static method onInit takes nothing returns nothing
            set BONUS_ARMOR_PEN_PERCENT = ExtendableBonus.Register(BonusArmorPenetrationPercent.allocate())
        endmethod
    endstruct
endlibrary
  • ArmorPenetration
    • Events
      • Game - DamageModifierEvent becomes Equal to 3.75
    • Conditions
      • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
      • DamageEventAttackT Not equal to ATTACK_TYPE_MAGIC
    • Actions
      • Custom script: set udg_DamageEventArmorPierced = udg_DamageEventArmorPierced + BlzGetUnitArmor(udg_DamageEventTarget) * udg_armor_penetration_percent[GetUnitUserData(udg_DamageEventSource)] + udg_armor_penetration_flat[GetUnitUserData(udg_DamageEventSource)]
 
Does anyone know how to add crowd control system of chopinski to damage tag of bribe? Whenever i use something from there, they turn up as "blocked!"

I would like to make a damage tag for them with "Crowd Control" , but i cannot properly find the way to do it...
  • Damage Tag
    • Events
      • Game - OnDamageEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • JustShielded Equal to True
        • Then - Actions
          • Set VariableSet JustShielded = False
          • Skip remaining actions
        • Else - Actions
      • Set VariableSet ReportLife = (Life of DamageEventTarget)
      • Game - Display to (All players) the text: (String((Mana of DamageEventTarget)))
      • Set VariableSet DmgStr = |cffffffff
      • Set VariableSet DmgDuration = 1.00
      • Set VariableSet DmgSize = 1.00
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • DamageEventAmount Less than 0.01
          • DamageEventAmount Greater than -0.01
        • Then - Actions
          • -------- A block or near-block --------
          • Set VariableSet DmgSize = 0.60
          • Set VariableSet DmgDuration = 0.60
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Unit-type of DamageEventSource) Equal to Peasant
            • Then - Actions
              • Set VariableSet DmgStr = |c00900081EVASION!|R
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • DamageScalingWC3 Equal to 0.00
                • Then - Actions
                  • Set VariableSet DmgStr = (|c00AAAAAABlocked + ((String((Integer((DamageEventPrevAmt x DamageScalingUser))))) + !|r))
                • Else - Actions
                  • Set VariableSet DmgStr = (|c00AAAAAABlocked + ((String((Integer((DamageEventPrevAmt x DamageScalingWC3))))) + !|r))
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • DamageEventAmount Less than -0.99
            • Then - Actions
              • -------- Heal --------
              • Set VariableSet DmgSize = 0.80
              • Set VariableSet DmgDuration = 0.80
              • Set VariableSet DmgStr = (|cff00ff00+ + ((String((Integer((0.00 - DamageEventAmount))))) + !|r))
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • DamageScalingUser Greater than or equal to 1.50
                • Then - Actions
                  • -------- User-triggered Critical Strike --------
                  • Set VariableSet DmgSize = 1.50
                  • Set VariableSet DmgDuration = 1.50
                  • Set VariableSet DmgStr = (|cffff0000 + ((String((Integer(DamageEventAmount)))) + !|r))
                • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • DamageEventAmount Greater than 0.99
                    • Then - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • DamageScalingUser Less than 0.60
                        • Then - Actions
                          • -------- User reduced damage --------
                          • Set VariableSet DmgSize = 0.70
                          • Set VariableSet DmgDuration = 0.70
                          • Set VariableSet DmgStr = (|cff808000 + ((String((Integer(DamageEventAmount)))) + |r))
                        • Else - Actions
                          • -------- Normal sequence --------
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • IsDamageSpell Equal to True
                            • Then - Actions
                              • Set VariableSet DmgStr = (|c0000FFFF + ((String((Integer(DamageEventAmount)))) + |r))
                            • Else - Actions
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • IsDamageRanged Equal to True
                                • Then - Actions
                                  • Set VariableSet DmgStr = (|cffffff00 + ((String((Integer(DamageEventAmount)))) + |r))
                                • Else - Actions
                                  • Set VariableSet DmgStr = (DmgStr + ((String((Integer(DamageEventAmount)))) + |r))
                    • Else - Actions
      • Custom script: call ArcingTextTag.createEx(udg_DmgStr, udg_DamageEventTarget, udg_DmgDuration, udg_DmgSize, GetLocalPlayer())
 
Does anyone know how to add crowd control system of chopinski to damage tag of bribe? Whenever i use something from there, they turn up as "blocked!"

I would like to make a damage tag for them with "Crowd Control" , but i cannot properly find the way to do it...
  • Damage Tag
    • Events
      • Game - OnDamageEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • JustShielded Equal to True
        • Then - Actions
          • Set VariableSet JustShielded = False
          • Skip remaining actions
        • Else - Actions
      • Set VariableSet ReportLife = (Life of DamageEventTarget)
      • Game - Display to (All players) the text: (String((Mana of DamageEventTarget)))
      • Set VariableSet DmgStr = |cffffffff
      • Set VariableSet DmgDuration = 1.00
      • Set VariableSet DmgSize = 1.00
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • DamageEventAmount Less than 0.01
          • DamageEventAmount Greater than -0.01
        • Then - Actions
          • -------- A block or near-block --------
          • Set VariableSet DmgSize = 0.60
          • Set VariableSet DmgDuration = 0.60
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Unit-type of DamageEventSource) Equal to Peasant
            • Then - Actions
              • Set VariableSet DmgStr = |c00900081EVASION!|R
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • DamageScalingWC3 Equal to 0.00
                • Then - Actions
                  • Set VariableSet DmgStr = (|c00AAAAAABlocked + ((String((Integer((DamageEventPrevAmt x DamageScalingUser))))) + !|r))
                • Else - Actions
                  • Set VariableSet DmgStr = (|c00AAAAAABlocked + ((String((Integer((DamageEventPrevAmt x DamageScalingWC3))))) + !|r))
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • DamageEventAmount Less than -0.99
            • Then - Actions
              • -------- Heal --------
              • Set VariableSet DmgSize = 0.80
              • Set VariableSet DmgDuration = 0.80
              • Set VariableSet DmgStr = (|cff00ff00+ + ((String((Integer((0.00 - DamageEventAmount))))) + !|r))
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • DamageScalingUser Greater than or equal to 1.50
                • Then - Actions
                  • -------- User-triggered Critical Strike --------
                  • Set VariableSet DmgSize = 1.50
                  • Set VariableSet DmgDuration = 1.50
                  • Set VariableSet DmgStr = (|cffff0000 + ((String((Integer(DamageEventAmount)))) + !|r))
                • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • DamageEventAmount Greater than 0.99
                    • Then - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • DamageScalingUser Less than 0.60
                        • Then - Actions
                          • -------- User reduced damage --------
                          • Set VariableSet DmgSize = 0.70
                          • Set VariableSet DmgDuration = 0.70
                          • Set VariableSet DmgStr = (|cff808000 + ((String((Integer(DamageEventAmount)))) + |r))
                        • Else - Actions
                          • -------- Normal sequence --------
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • IsDamageSpell Equal to True
                            • Then - Actions
                              • Set VariableSet DmgStr = (|c0000FFFF + ((String((Integer(DamageEventAmount)))) + |r))
                            • Else - Actions
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • IsDamageRanged Equal to True
                                • Then - Actions
                                  • Set VariableSet DmgStr = (|cffffff00 + ((String((Integer(DamageEventAmount)))) + |r))
                                • Else - Actions
                                  • Set VariableSet DmgStr = (DmgStr + ((String((Integer(DamageEventAmount)))) + |r))
                    • Else - Actions
      • Custom script: call ArcingTextTag.createEx(udg_DmgStr, udg_DamageEventTarget, udg_DmgDuration, udg_DmgSize, GetLocalPlayer())

I think it's because cc trigger a 0 dmg event when applying themselves, like most debuff.
Source:
UnknownThis is always a zero damage event.
Used when most debuffs are applied, also when Stuns wear off.
Used by almost all Orbs - even Orb of Frost.
Same as Universal

If you want to filter them, I guess you could check for damage type unknown and display the "Crowd control" tag here.
I don't know how that would interact with orbs tho.
 
how would I add it to work based on the spell I cast?
I want to add spell damage to my map, so I assume I use a variable to track spell damage
Then I want spells to have different coefficients to make it more balanced
so for example Flamestrike since its AOE, might only get 50% of my spell damage, but fireball will get would get 150% of it, how would I implement this system to know which spell was cast
 
Negative Armor (for custom Armor Constants) is problematic, if my understanding is correct. Iirc, The formula for negative armor 2-0.94^( -armor ) is independent of Armor Constant. This means in maps with lower than 6%, that negative Armor is still as effective as in base WC3, and vice versa. Functionality that would allow users to correct this would be nice (in my map, however, I pretty much just factored for this so "fixing" it isn't necessary).

If a cap on the penalty from negative Armor exists (or even if it doesn't), functionality that allows you to have either behavior would be nice as well.

I am super late to respond to this one, but I just recently noticed your comment and since what is said here affects me and my project so I am hoping to get some clarity from Bribe on the topic.

For Sunken City, I have the damage reduction per point of Armor changed to 4% down from 6%.
So I ran a test where unit A attacks unit B.
  • Unit A has an item that gives +2 Armor Penetration (and nothing else)
  • Unit B has 0 Armor

With my 4% damage reduction Gameplay Constant, unit A hits for 55 damage without the item and 61 when it equips the item.
When I reset the damage reduction to 6% (default), unit A hits for 55 damage without the item and 61 when it equips the item.

So no change.

@Bribe for ArmorPenetration to work in the DamageEngine in the first place, I'd assume it must have the Armor formula configured somewhere.
If it does then the 6% constant should be something that can be made to be configurable by the users in the cases where they adjusted the damage reduction value, is that not the case?

Do you think that's something that can be added/improved?

Because if not, in my case, I'd have to either use the default value of 6% instead of the 4% that I've been using so far - and this would have tremendous impact on the game's balance. OR... I'd have to not use ArmorPenetration as a stat/effect anymore. Which would be a great shame, because it's such a nice feature that the Damage Engine brings to the table.

I'd be curious to hear your thoughts and insight on the matter. <3
 
how does Armor Pierce work in this? To test, I created a knight and removed the proc rate from his ability.

call UnitDamageTargetBJ(knight, u, 100., ATTACK_TYPE_MELEE, DAMAGE_TYPE_NORMAL)

with 1000 Armor Pen deals 73 and some change damage. This doesn't seem right. Even weirder, when you change the maps' Armor Constant from 0.06, the damage dealt here changes? Changing to .02 resulted in 89 damage dealt. Setting Armor Constant to negative values actually increases the damage they take, (154 @ -6%). What is intriguing me is that there is no way to grab this Constant afaik, but changing it does appear to be doing something (more precisely, in the opposite direction of what you'd expect in some tests).

(removed a ton here, I think my issue was calculating eHP based off the % rather than just interpreting stuff like -20% Reduction as taking 20% more. Also, seems WC3's negative armor "cap" is -20, at 170% taken)

=============
my suggestions;



Negative Armor (for custom Armor Constants) is problematic, if my understanding is correct. Iirc, The formula for negative armor 2-0.94^( -armor ) is independent of Armor Constant. This means in maps with lower than 6%, that negative Armor is still as effective as in base WC3, and vice versa. Functionality that would allow users to correct this would be nice (in my map, however, I pretty much just factored for this so "fixing" it isn't necessary).

If a cap on the penalty from negative Armor exists (or even if it doesn't), functionality that allows you to have either behavior would be nice as well.
I think this is going to extremely complicate things.

1. Damage Engine will suddenly change the amount of damage people are used to in their maps (ok a constant could remedy that in theory).

2. The users have to configure the armor constant per-attack).

3. DAMAGE_TYPE_NORMAL is the only damage type that allows armor to apply. If it is customized (or if the user wants custom armor handling), then Damage Engine needs to not use DAMAGE_TYPE_NORMAL.

@SpasMaster , it is possible to avoid having to switch all of your magic attack types to DAMAGE_TYPE_NORMAL if you have a trigger that does all armor calculations for you before the damage is applied. This trigger should run after all other PreDamageEvent triggers have run.
I just don't get it. Why is it so hard to track a critical hit on an item or blademaster?
Why don't these 2 filters work? What are they for if they don't filter crits?

  • TEST
    • Events
      • Game - PreDamageEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • Custom script: //! runtextmacro DAMAGE_TRIGGER_CONFIG()
      • Set VariableSet DamageFilterDamageT = DamageTypeCriticalStrike
      • Custom script: //! runtextmacro DAMAGE_TRIGGER_CONFIG_END()
  • TEST1
    • Events
      • Game - PreDamageEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • Custom script: //! runtextmacro DAMAGE_TRIGGER_CONFIG()
      • Set VariableSet DamageFilterType = DamageTypeCriticalStrike
      • Custom script: //! runtextmacro DAMAGE_TRIGGER_CONFIG_END()
The only overly convoluted and inefficient way I could think of to track crits was by monitoring a unit’s average damage through separate variables and detecting sudden spikes...
Yes, that convoluted way is the only way I thought of, too, but it's extremely impractical because a unit could theoretically get a massive damage bump, or switch between attack 1 and attack 2, or they may have a very high crit rate and the average would be skewed. The only reliable crits are the ones you manually trigger.
how would I add it to work based on the spell I cast?
I want to add spell damage to my map, so I assume I use a variable to track spell damage
Then I want spells to have different coefficients to make it more balanced
so for example Flamestrike since its AOE, might only get 50% of my spell damage, but fireball will get would get 150% of it, how would I implement this system to know which spell was cast
I've given this feedback before to lots of users: you have to program a check per-spell-per-hero. If a hero has more than 2 types of spells that deal the same type of damage (e.g. Fire), then the detection becomes way more complicated.
Found a small bug. When a hero has a slow poison debuff that can't kill but tries to do damage, lethal damage can be triggered by mistake.
I'll need to investigate if there are damage types that can NEVER kill, such as SLOW_POISON for this spell, and then filter it if it can indeed never kill. I used to have a Finger of Death override in older Damage Engine versions to make sure the unit would still pop.
 
@SpasMaster , it is possible to avoid having to switch all of your magic attack types to DAMAGE_TYPE_NORMAL if you have a trigger that does all armor calculations for you before the damage is applied. This trigger should run after all other PreDamageEvent triggers have run.

Hmm, I must be missing where the switching magic attack types to DAMAGE_TYPE_NORMAL came from.

My inquiry was rather about the Damage Reduction per point of Armor constant value in Gameplay Constants and how it affects DamageEventArmorPierced.
Or well.. how it doesn't in this case.

This affects regular attacks, which have DAMAGE_TYPE_NORMAL and (in Sunken City) triggered ability damage which also uses DAMAGE_TYPE_NORMAL in the vast majority of cases.
 
Can this be used with versions prior to 1.32?
Yes.

This jass secion is needed for 1.31.1 (for versions 5.5+ of DamageEngine):
JASS:
function BlzGetEventIsAttack takes nothing returns boolean
    return BlzGetEventDamageType() == DAMAGE_TYPE_NORMAL
endfunction


I have not upgraded this in a while, but 5.7.1.2 is here for 1.31.1 with this added to the main jass-script:

For earlier versions (1.26+), you'd need to use:

EDIT: While playing with my kittens, I remembered that adding the function BlzGetEventIsAttack causes the map to NOT work on latest version, so the fix to support it on older versions AND newer versions is to replace the BlzGetEventIsAttack in the system with BlzGetEventDamageType() == DAMAGE_TYPE_NORMAL.
I have updated the pastebin
 
Last edited:
1772633211742.png

DamageFilterRunChance appears to be significantly lower than anticipated. For instance with the following trigger auto attacks end up running only at ~20% times instead of 30%. (Upd. maybe even lower at 1.00, however, it always procs as one may guess)

Not sure why exactly this may be the case, all the other times I used it, it worked perfectly so far, perhaps I'll just try to use some other random source.
 
Last edited:
View attachment 577370
DamageFilterRunChance appears to be significantly lower than anticipated. For instance with the following trigger auto attacks end up running only at ~20% times instead of 30%. (Upd. maybe even lower at 1.00, however, it always procs as one may guess)

Not sure why exactly this may be the case, all the other times I used it, it worked perfectly so far, perhaps I'll just try to use some other random source.
How many times did you sample this? Because random numbers, by their very nature, are bound to give "wierd" results now and then.

For example, if you tested this ability say, 15 times, there's still a ~17% chance you would only get 3 "procs" (20%), even though the true probability of a proc is 30%, and the expected result is 4.5.

tl;dr: random numbers are random. Make sure you have a lot of data if you're suspicious.
 
@Bribe just want to confirm, does lethal damage detection prevented from occurring against DamageTypePure?
I'm pretty sure Lethal damage takes priority so the LethalDamageHP will save someone from dying from pure damage. But I haven't tested this particular scenario with the aim of checking if something breaks, so you're welcome to try it out.
 
I'm pretty sure Lethal damage takes priority so the LethalDamageHP will save someone from dying from pure damage. But I haven't tested this particular scenario with the aim of checking if something breaks, so you're welcome to try it out.
Thank you, I also ran some tests just now and looks like it verifies as I hoped. Was checking if my understanding of things was right since there's interaction of these between two of my spells
 
Back
Top