1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  3. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still haven't received your rank award? Then please contact the administration.
    Dismiss Notice
  4. Let your favorite entries duke it out in the 15th Techtree Contest Poll.
    Dismiss Notice
  5. Weave light to take you to your highest hopes - the 6th Special Effect Contest is here!
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Damage Engine 5.7.1.1

Submitted by Bribe
This bundle is marked as approved. It works and satisfies the submission rules.
Damage Engine 5.7.1.1

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.


    • 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 "DamageEvent Equal to 1.00". 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 "DamageModifierEvent Becomes Equal to 1.00, 2.00 or 3.00". 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 "DamageModifierEvent Becomes Equal to 4.00". 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 "DamageModifierEvent" 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 1.00" 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:

      Code (vJASS):

      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 DamageModifierEvents up to 3.99
    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, DamageModifierEvent 4.00 and up 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 DamageEvent 1.00. 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: 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 <DamageModifierEvent> becomes Equal to 1.00".
      • 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 "DamageModifierEvent 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
      • Events
        • Map initialization
      • Conditions
      • Actions
        • -------- - --------
        • -------- New in Damage Engine 5.7 - you can use the below to automatically assign conditions --------
        • -------- - --------
        • -------- Equal to - Same as no conditions, works as it always has --------
        • -------- Less than - Same as IsDamageAttack Equal to True --------
        • -------- Less than or equal to - Same as IsDamageMelee Equal to True --------
        • -------- Greater than or equal to - Same as IsDamageRanged Equal to True --------
        • -------- Greater than - Same as IsDamageSpell Equal to True --------
        • -------- Not Equal to - Same as IsDamageCode Equal to True --------
        • -------- - --------
        • -------- 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
        • -------- - --------
        • -------- Added 25 July 2017 to allow detection of things like Bash or Pulverize or AOE spread --------
        • -------- - --------
        • Set VariableSet DamageEventAOE = 1
        • Set VariableSet DamageEventLevel = 1
        • -------- - --------
        • -------- In-game World Editor doesn't allow Attack Type and Damage Type comparisons. Therefore I need to code them as integers into GUI --------
        • -------- - --------
        • Set VariableSet ATTACK_TYPE_SPELLS = 0
        • Set VariableSet ATTACK_TYPE_NORMAL = 1
        • Set VariableSet ATTACK_TYPE_PIERCE = 2
        • Set VariableSet ATTACK_TYPE_SIEGE = 3
        • Set VariableSet ATTACK_TYPE_MAGIC = 4
        • Set VariableSet ATTACK_TYPE_CHAOS = 5
        • Set VariableSet ATTACK_TYPE_HERO = 6
        • -------- - --------
        • Set VariableSet DAMAGE_TYPE_UNKNOWN = 0
        • Set VariableSet DAMAGE_TYPE_NORMAL = 4
        • Set VariableSet DAMAGE_TYPE_ENHANCED = 5
        • Set VariableSet DAMAGE_TYPE_FIRE = 8
        • Set VariableSet DAMAGE_TYPE_COLD = 9
        • Set VariableSet DAMAGE_TYPE_LIGHTNING = 10
        • Set VariableSet DAMAGE_TYPE_POISON = 11
        • Set VariableSet DAMAGE_TYPE_DISEASE = 12
        • Set VariableSet DAMAGE_TYPE_DIVINE = 13
        • Set VariableSet DAMAGE_TYPE_MAGIC = 14
        • Set VariableSet DAMAGE_TYPE_SONIC = 15
        • Set VariableSet DAMAGE_TYPE_ACID = 16
        • Set VariableSet DAMAGE_TYPE_FORCE = 17
        • Set VariableSet DAMAGE_TYPE_DEATH = 18
        • Set VariableSet DAMAGE_TYPE_MIND = 19
        • Set VariableSet DAMAGE_TYPE_PLANT = 20
        • Set VariableSet DAMAGE_TYPE_DEFENSIVE = 21
        • Set VariableSet DAMAGE_TYPE_DEMOLITION = 22
        • Set VariableSet DAMAGE_TYPE_SLOW_POISON = 23
        • Set VariableSet DAMAGE_TYPE_SPIRIT_LINK = 24
        • Set VariableSet DAMAGE_TYPE_SHADOW_STRIKE = 25
        • Set VariableSet DAMAGE_TYPE_UNIVERSAL = 26
        • -------- - --------
        • -------- The below variables don't affect damage amount, but do affect the sound played --------
        • -------- They also give important information about the type of attack used. --------
        • -------- They can differentiate between ranged and melee for units who are both --------
        • -------- - --------
        • Set VariableSet WEAPON_TYPE_NONE = 0
        • -------- Metal Light/Medium/Heavy --------
        • Set VariableSet WEAPON_TYPE_ML_CHOP = 1
        • Set VariableSet WEAPON_TYPE_MM_CHOP = 2
        • Set VariableSet WEAPON_TYPE_MH_CHOP = 3
        • Set VariableSet WEAPON_TYPE_ML_SLICE = 4
        • Set VariableSet WEAPON_TYPE_MM_SLICE = 5
        • Set VariableSet WEAPON_TYPE_MH_SLICE = 6
        • Set VariableSet WEAPON_TYPE_MM_BASH = 7
        • Set VariableSet WEAPON_TYPE_MH_BASH = 8
        • Set VariableSet WEAPON_TYPE_MM_STAB = 9
        • Set VariableSet WEAPON_TYPE_MH_STAB = 10
        • -------- Wood Light/Medium/Heavy --------
        • Set VariableSet WEAPON_TYPE_WL_SLICE = 11
        • Set VariableSet WEAPON_TYPE_WM_SLICE = 12
        • Set VariableSet WEAPON_TYPE_WH_SLICE = 13
        • Set VariableSet WEAPON_TYPE_WL_BASH = 14
        • Set VariableSet WEAPON_TYPE_WM_BASH = 15
        • Set VariableSet WEAPON_TYPE_WH_BASH = 16
        • Set VariableSet WEAPON_TYPE_WL_STAB = 17
        • Set VariableSet WEAPON_TYPE_WM_STAB = 18
        • -------- Claw Light/Medium/Heavy --------
        • Set VariableSet WEAPON_TYPE_CL_SLICE = 19
        • Set VariableSet WEAPON_TYPE_CM_SLICE = 20
        • Set VariableSet WEAPON_TYPE_CH_SLICE = 21
        • -------- Axe Medium --------
        • Set VariableSet WEAPON_TYPE_AM_CHOP = 22
        • -------- Rock Heavy --------
        • Set VariableSet WEAPON_TYPE_RH_BASH = 23
        • -------- - --------
        • -------- Since GUI still doesn't provide Defense Type and Armor Types, I needed to include the below --------
        • -------- - --------
        • Set VariableSet ARMOR_TYPE_NONE = 0
        • Set VariableSet ARMOR_TYPE_FLESH = 1
        • Set VariableSet ARMOR_TYPE_METAL = 2
        • Set VariableSet ARMOR_TYPE_WOOD = 3
        • Set VariableSet ARMOR_TYPE_ETHEREAL = 4
        • Set VariableSet ARMOR_TYPE_STONE = 5
        • -------- - --------
        • Set VariableSet DEFENSE_TYPE_LIGHT = 0
        • Set VariableSet DEFENSE_TYPE_MEDIUM = 1
        • Set VariableSet DEFENSE_TYPE_HEAVY = 2
        • Set VariableSet DEFENSE_TYPE_FORTIFIED = 3
        • Set VariableSet DEFENSE_TYPE_NORMAL = 4
        • Set VariableSet DEFENSE_TYPE_HERO = 5
        • Set VariableSet DEFENSE_TYPE_DIVINE = 6
        • Set VariableSet DEFENSE_TYPE_UNARMORED = 7
        • -------- - --------
        • Set VariableSet DamageFilterAttackT = -1
        • Set VariableSet DamageFilterDamageT = -1
        • -------- - --------
        • Custom script: call DamageEngine_DebugStr()


  • Code (vJASS):

    //===========================================================================
    //
    //  Damage Engine 5.7.1.1 - update requires replacing the JASS script.
    //
    /*
        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.
    */

    //! novjass
    JASS API (work in progress - I have a lot of documentation to go through):
        struct Damage extends array
            readonly static unit  source        //stores udg_DamageEventSource
            readonly static unit  target        //stores udg_DamageEventTarget
            static real           amount        //stores udg_DamageEventAmount
            readonly unit         sourceUnit    //stores udg_DamageEventSource by index
            readonly unit         targetUnit    //stores udg_DamageEventTarget by index
            real                  damage        //stores udg_DamageEventAmount by index
            readonly real         prevAmt       //stores udg_DamageEventPrevAmt by index
            attacktype            attackType    //stores udg_DamageEventAttackT by index
            damagetype            damageType    //stores udg_DamageEventDamageT by index
            weapontype            weaponType    //stores udg_DamageEventWeaponT by index
            integer               userType      //stores udg_DamageEventType by index
            readonly integer      eFilter       //replaces the previous eventFilter variable
            readonly boolean      isAttack      //stores udg_IsDamageAttack by index
            readonly boolean      isCode        //stores udg_IsDamageCode by index
            readonly boolean      isMelee       //stores udg_IsDamageMelee by index
            readonly boolean      isRanged      //stores udg_IsDamageRanged by index
            readonly boolean      isSpell       //stores udg_IsDamageSpell by index
            real                  armorPierced  //stores udg_DamageEventArmorPierced by index
            integer               armorType     //stores udg_DamageEventArmorT by index
            integer               defenseType   //stores udg_DamageEventDefenseT by index
         
            static boolean operator enabled
            - Set to false to disable the damage event triggers/false to reverse that
         
            static method apply takes unit src, unit tgt, real amt, boolean a, boolean r, attacktype at, damagetype dt, weapontype wt returns Damage
            - Same arguments as "UnitDamageTarget" but has the benefit of being performance-friendly during recursive events.
            - Will automatically cause the damage to be registered as Code damage.
         
            static method applySpell takes unit src, unit tgt, real amt, damagetype dt returns Damage
            - A simplified version of the above function that autofills in the booleans, attack type and weapon type.
            static method applyAttack takes unit src, unit tgt, real amt, boolean ranged, attacktype at, weapontype wt returns Damage
            - A different variation of the above which autofills the "attack" boolean and sets the damagetype to DAMAGE_TYPE_NORMAL.
        struct DamageTrigger extends array
            method operator filter= takes integer filter returns nothing
            // Apply primary filters such as DamageEngine_FILTER_MELEE/RANGED/SPELL which are based off of limitop handles to enable easier access for GUI folks
            // Full filter list:
            - global integer DamageEngine_FILTER_ATTACK
            - global integer DamageEngine_FILTER_MELEE
            - global integer DamageEngine_FILTER_OTHER
            - global integer DamageEngine_FILTER_RANGED
            - global integer DamageEngine_FILTER_SPELL
            - global integer DamageEngine_FILTER_CODE  
     
            boolean configured //set to True after configuring any filters listed below.
     
            method configure takes nothing returns nothing
            // Apply custom filters after setting any desired udg_DamageFilter variables (for GUI).
            // Alternatively, vJass users can set these instead. Just be mindful to set the variable
            // "configured" to true after settings these.
            unit    source
            unit    target
            integer sourceType
            integer targetType
            integer sourceBuff
            integer targetBuff
            real    damageMin
            integer attackType
            integer damageType
            integer userType
     
            //The string in the aruments below requires the following API:
            //  "" for standard damage event
            //  "Modifier(or Mod if you prefer)/After/Lethal/AOE" for the others
            static method getIndex takes trigger t, string eventName, real value returns integer
            static method registerTrigger takes trigger whichTrig, string var, real weight returns nothing
            static method unregister takes trigger t, string eventName, real value, boolean reset returns boolean
     
            static method operator [] takes code c returns trigger
            // Converts a code argument to a trigger, while checking if the same code had already been registered before.
        //The accepted strings here use the same criteria as DamageTrigger.getIndex/registerTrigger/unregister
        function TriggerRegisterDamageEngineEx takes trigger whichTrig, string eventName, real value, integer f returns nothing
        function TriggerRegisterDamageEngine takes trigger whichTrig, string eventName, real value returns nothing
        function RegisterDamageEngineEx takes code c, string eventName, real value, integer f returns nothing
        function RegisterDamageEngine takes code c, string eventName, real value returns nothing
    //! endnovjass
    //===========================================================================
    library DamageEngine
    globals
        private constant boolean USE_GUI        = true      //If you don't use any of the GUI events, set to false to slightly improve performance
                                                           
        private constant boolean USE_SCALING    = USE_GUI   //If you don't need or want to use DamageScalingUser/WC3 then set this to false
        private constant boolean USE_EXTRA      = true      //If you don't use DamageEventLevel or AOEDamageEvent, set this to false
        private constant boolean USE_ARMOR_MOD  = true      //If you do not modify nor detect armor/defense, set this to false
        private constant boolean USE_MELEE_RANGE= true      //If you do not detect melee nor ranged damage, set this to false
        private constant boolean USE_LETHAL     = true      //If you do not use LethalDamageEvent nor negative damage (explosive) types, set this to false
       
        private constant integer LIMBO          = 16        //When manually-enabled recursion is enabled via DamageEngine_recurion, the engine will never go deeper than LIMBO.
       
        public constant integer TYPE_CODE       = 1         //Must be the same as udg_DamageTypeCode, or 0 if you prefer to disable the automatic flag.
        public constant integer TYPE_PURE       = 2         //Must be the same as udg_DamageTypePure
        private constant real   DEATH_VAL       = 0.405     //In case Blizz ever changes this, it'll be a quick fix here.
        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         = true
        private boolean         totem           = false
        private boolean array   attacksImmune
        private boolean array   damagesImmune
        //Made global in order to use enable/disable behavior.
        private trigger         t1              = CreateTrigger()
        private trigger         t2              = CreateTrigger()
        private trigger         t3              = CreateTrigger() //Catches, stores recursive events
        //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_MELEE    = 1     //LESS_THAN_OR_EQUAL
        public constant integer FILTER_OTHER    = 2     //EQUAL
        public constant integer FILTER_RANGED   = 3     //GREATER_THAN_OR_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
        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 keyword         run
        private keyword         trigFrozen
        private keyword         levelsDeep
        private keyword         inceptionTrig
       
        private boolean         hasLethal       = false
    endglobals
    native UnitAlive takes unit u returns boolean
    //GUI Vars:
    /*
        Retained from 3.8 and prior:
        ----------------------------
        unit            udg_DamageEventSource
        unit            udg_DamageEventTarget
        unit            udg_EnhancedDamageTarget
        group           udg_DamageEventAOEGroup
        integer         udg_DamageEventAOE
        integer         udg_DamageEventLevel
        real            udg_DamageModifierEvent
        real            udg_DamageEvent
        real            udg_AfterDamageEvent
        real            udg_DamageEventAmount
        real            udg_DamageEventPrevAmt
        real            udg_AOEDamageEvent
        boolean         udg_DamageEventOverride
        boolean         udg_NextDamageType
        boolean         udg_DamageEventType
        boolean         udg_IsDamageSpell
        //Added in 5.0:
        boolean          udg_IsDamageMelee
        boolean          udg_IsDamageRanged
        unit             udg_AOEDamageSource
        real             udg_LethalDamageEvent
        real             udg_LethalDamageHP
        real             udg_DamageScalingWC3
        integer          udg_DamageEventAttackT
        integer          udg_DamageEventDamageT
        integer          udg_DamageEventWeaponT
        //Added in 5.1:
        boolean          udg_IsDamageCode
        //Added in 5.2:
        integer          udg_DamageEventArmorT
        integer          udg_DamageEventDefenseT
        //Addded in 5.3:
        real             DamageEventArmorPierced
        real             udg_DamageScalingUser
        //Added in 5.4.2 to allow GUI users to re-issue the exact same attack and damage type at the attacker.
        attacktype array udg_CONVERTED_ATTACK_TYPE
        damagetype array udg_CONVERTED_DAMAGE_TYPE
        //Added after Reforged introduced the new native BlzGetDamageIsAttack
        boolean         udg_IsDamageAttack
        //Added in 5.6 to give GUI users control over the "IsDamageAttack", "IsDamageRanged" and "DamageEventWeaponT" field
        boolean         udg_NextDamageIsAttack  //The first boolean value in the UnitDamageTarget native
        boolean         udg_NextDamageIsMelee   //Flag the damage classification as melee
        boolean         udg_NextDamageIsRanged  //The second boolean value in the UnitDamageTarget native
        integer         udg_NextDamageWeaponT   //Allows control over damage sound effect
        //Added in 5.7 to enable efficient, built-in filtering (see the below "checkConfiguration" method - I recommend commenting-out anything you don't need in your map)
        integer udg_DamageFilterAttackT
        integer udg_DamageFilterDamageT     //filter for a specific attack/damage type
        unit    udg_DamageFilterSource
        unit    udg_DamageFilterTarget      //filter for a specific source/target
        integer udg_DamageFilterSourceT
        integer udg_DamageFilterTargetT     //unit type of source/target
        integer udg_DamageFilterType        //which DamageEventType was used
        integer udg_DamageFilterSourceB
        integer udg_DamageFilterTargetB     //if source/target has a buff
        real    udg_DamageFilterMinAmount   //only allow a minimum damage threshold
    */

    struct DamageTrigger extends array
       
        //Map-makers should comment-out any booleans they will never need to check for.
        method checkConfiguration takes nothing returns boolean
            if this.userType != 0 and udg_DamageEventType != this.userType then
            elseif this.source != null and this.source != udg_DamageEventSource then
            elseif this.target != null and this.target != udg_DamageEventTarget then
            elseif this.attackType >= 0 and this.attackType != udg_DamageEventAttackT then
            elseif this.damageType >= 0 and this.damageType != udg_DamageEventDamageT then
            elseif this.sourceType != 0 and GetUnitTypeId(udg_DamageEventSource) != this.sourceType then
            elseif this.targetType != 0 and GetUnitTypeId(udg_DamageEventTarget) != this.targetType then
            elseif this.sourceBuff != 0 and GetUnitAbilityLevel(udg_DamageEventSource, this.sourceBuff) == 0 then
            elseif this.targetBuff != 0 and GetUnitAbilityLevel(udg_DamageEventTarget, this.targetBuff) == 0 then
            elseif udg_DamageEventAmount > this.damageMin then
                return true
            endif
            return false
        endmethod
       
        //The below variables are constant
        readonly static thistype        MOD             = 1
        readonly static thistype        SHIELD          = 4
        readonly static thistype        DAMAGE          = 5
        readonly static thistype        ZERO            = 6
        readonly static thistype        AFTER           = 7
        readonly static thistype        LETHAL          = 8
        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
        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.
        unit    source
        unit    target
        integer sourceType
        integer targetType
        integer sourceBuff
        integer targetBuff
        real    damageMin
        integer attackType
        integer damageType
        integer userType
        method configure takes nothing returns nothing
            set this.attackType         = udg_DamageFilterAttackT
            set this.damageType         = udg_DamageFilterDamageT
            set this.source             = udg_DamageFilterSource
            set this.target             = udg_DamageFilterTarget
            set this.sourceType         = udg_DamageFilterSourceT
            set this.targetType         = udg_DamageFilterTargetT
            set this.sourceBuff         = udg_DamageFilterSourceB
            set this.targetBuff         = udg_DamageFilterTargetB
            set this.userType           = udg_DamageFilterType
            set this.damageMin          = udg_DamageFilterMinAmount
     
            set udg_DamageFilterAttackT =-1
            set udg_DamageFilterDamageT =-1
            set udg_DamageFilterSource  = null
            set udg_DamageFilterTarget  = null
            set udg_DamageFilterSourceT = 0
            set udg_DamageFilterTargetT = 0
            set udg_DamageFilterType    = 0
            set udg_DamageFilterSourceB = 0
            set udg_DamageFilterTargetB = 0
            set udg_DamageFilterMinAmount=0.00
     
            set this.configured         = true
        endmethod
        static method setGUIFromStruct takes boolean full returns nothing
            set udg_DamageEventAmount       = Damage.index.damage
            set udg_DamageEventAttackT      = GetHandleId(Damage.index.attackType)
            set udg_DamageEventDamageT      = GetHandleId(Damage.index.damageType)
            set udg_DamageEventWeaponT      = GetHandleId(Damage.index.weaponType)
            set udg_DamageEventType         = Damage.index.userType
    static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            set udg_DamageEventArmorPierced = Damage.index.armorPierced
            set udg_DamageEventArmorT       = Damage.index.armorType
            set udg_DamageEventDefenseT     = Damage.index.defenseType
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            if full then
                set udg_DamageEventSource   = Damage.index.sourceUnit
                set udg_DamageEventTarget   = Damage.index.targetUnit
                set udg_DamageEventPrevAmt  = Damage.index.prevAmt
                set udg_IsDamageAttack      = Damage.index.isAttack
                set udg_IsDamageCode        = Damage.index.isCode
                set udg_IsDamageSpell       = Damage.index.isSpell
    static if USE_MELEE_RANGE then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                set udg_IsDamageMelee       = Damage.index.isMelee
                set udg_IsDamageRanged      = Damage.index.isRanged
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            endif
        endmethod
        static method setStructFromGUI takes nothing returns nothing
            set Damage.index.damage        = udg_DamageEventAmount
            set Damage.index.attackType    = ConvertAttackType(udg_DamageEventAttackT)
            set Damage.index.damageType    = ConvertDamageType(udg_DamageEventDamageT)
            set Damage.index.weaponType    = ConvertWeaponType(udg_DamageEventWeaponT)
            set Damage.index.userType      = udg_DamageEventType
    static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            set Damage.index.armorPierced  = udg_DamageEventArmorPierced
            set Damage.index.armorType     = udg_DamageEventArmorT
            set Damage.index.defenseType   = udg_DamageEventDefenseT
    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
                if root >= 4 then
                    set root= SHIELD //4.00 or higher
                else  
                    set root= MOD    //Less than 4.00
                endif
            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_LethalDamageEvent" then
                set root    = LETHAL
            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_MELEE]    = flag
            set filters[this + FILTER_OTHER]    = flag
            set filters[this + FILTER_RANGED]   = 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
                    set filters[this + FILTER_MELEE]    = true
                    set filters[this + FILTER_RANGED]   = 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 not hasLethal and index == LETHAL then
                set hasLethal = true
            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
           
            //Next 2 lines added to fix a bug when using manual vJass configuration,
            //discovered and solved by lolreported
            set id.attackType          = -1
            set id.damageType          = -1
     
            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
                call index.configure()
                set index.configured    = false
                set index               = index*FILTER_MAX
                call index.toggleAllFilters(false)
            endif
            return true
        endmethod
        method run takes nothing returns nothing
            local integer cat = this
            local Damage d = Damage.index
    static if USE_GUI then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            local boolean structUnset = false
            local boolean guiUnset = false
            local boolean mod = cat <= DAMAGE
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            if dreaming then
                return    
            endif          
            set dreaming = true
            call DisableTrigger(t1)
            call DisableTrigger(t2)
            call EnableTrigger(t3)
            //call BJDebugMsg("Start of event running")
            loop                                  
                set this = this.next
                exitwhen this == 0
                exitwhen cat == MOD and (udg_DamageEventOverride or udg_DamageEventType == TYPE_PURE)
                exitwhen cat == SHIELD and udg_DamageEventAmount <= 0.00
    static if USE_LETHAL then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                exitwhen cat == LETHAL and udg_LethalDamageHP > DEATH_VAL
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
             
                set eventIndex = this
                if not this.trigFrozen and filters[this*FILTER_MAX + d.eFilter] and IsTriggerEnabled(this.rootTrig) and (not this.configured or this.checkConfiguration()) then
    static if USE_GUI then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                    if mod then
                        if this.usingGUI then
                            if guiUnset then
                                set guiUnset = false
                                call setGUIFromStruct(false)
                            endif
                            //! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_PDD()
                        elseif structUnset then
                            set structUnset = false
                            call setStructFromGUI()
                        endif
                    endif
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                    //! 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()
                   
                    //JASS users who do not use actions can modify the below block to just evaluate.
                    //It should not make any perceptable difference in terms of performance.
                    if TriggerEvaluate(this.rootTrig) then
                        call TriggerExecute(this.rootTrig)
                    endif
                    //! 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()
    static if USE_GUI then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                    if mod then
                        if this.usingGUI then
                            //! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_PDD()
                            if cat != MOD then
                                set d.damage        = udg_DamageEventAmount
                            else
                                set structUnset = true
                            endif
                        elseif cat != MOD then
                            set udg_DamageEventAmount = d.damage
                        else
                            set guiUnset = true
                        endif
                    endif
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                endif
            endloop
    static if USE_GUI then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            if structUnset then
                call setStructFromGUI()
            endif
            if guiUnset then
                call setGUIFromStruct(false)
            endif
    else// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            call setGUIFromStruct(false)
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            //call BJDebugMsg("End of event running")
            call DisableTrigger(t3)
            call EnableTrigger(t1)
            call EnableTrigger(t2)
            set dreaming                                = false
        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
    //! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_01()
    //! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_02()
    //! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_03()
    //! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_04()
    //! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_05()
    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
        attacktype              attackType    //stores udg_DamageEventAttackT
        damagetype              damageType    //stores udg_DamageEventDamageT
        weapontype              weaponType    //stores udg_DamageEventWeaponT
        integer                 userType      //stores udg_DamageEventType
        readonly boolean        isAttack      //stores udg_IsDamageAttack
        readonly boolean        isCode        //stores udg_IsDamageCode
        readonly boolean        isSpell       //stores udg_IsDamageSpell
    static if USE_MELEE_RANGE then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
        readonly boolean        isMelee       //stores udg_IsDamageMelee
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
        readonly boolean        isRanged      //stores udg_IsDamageRanged
        readonly integer        eFilter       //stores the previous eventFilter variable
       
    static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
        real                    armorPierced  //stores udg_DamageEventArmorPierced
        integer                 armorType     //stores udg_DamageEventArmorT
        integer                 defenseType   //stores udg_DamageEventDefenseT
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
        readonly static Damage  index       = 0
        private static Damage   damageStack = 0
        private static Damage   prepped     = 0
        private static integer  count = 0 //The number of currently-running queued or sequential damage instances
        private Damage          stackRef
        private DamageTrigger   recursiveTrig
        private integer         prevArmorT
        private integer         prevDefenseT
        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
       
    static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
        private static method setArmor takes boolean reset returns nothing
            local real pierce
            local integer at
            local integer dt
            if reset then
                set pierce =  udg_DamageEventArmorPierced
                set at     =  Damage.index.prevArmorT
                set dt     =  Damage.index.prevDefenseT
            else
                set pierce = -udg_DamageEventArmorPierced
                set at     =  udg_DamageEventArmorT
                set dt     =  udg_DamageEventDefenseT
            endif
            if pierce != 0.00 then
                call BlzSetUnitArmor(udg_DamageEventTarget, BlzGetUnitArmor(udg_DamageEventTarget) + pierce)
            endif
            if Damage.index.prevArmorT != udg_DamageEventArmorT then
                call BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_ARMOR_TYPE, at)
            endif
            if Damage.index.prevDefenseT != udg_DamageEventDefenseT then
                call BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_DEFENSE_TYPE, dt)
            endif
        endmethod
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
    static if USE_EXTRA then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
        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 udg_AOEDamageSource         = null
            call GroupClear(udg_DamageEventAOEGroup)
        endmethod
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
       
        private static method afterDamage takes nothing returns nothing
            if udg_DamageEventPrevAmt != 0.00 and udg_DamageEventDamageT != 0 then
                call DamageTrigger.AFTER.run()
                set udg_DamageEventDamageT  = 0
                set udg_DamageEventPrevAmt  = 0.00
            endif
        endmethod
        private method doPreEvents takes boolean natural returns boolean
    static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            set this.armorType      = BlzGetUnitIntegerField(this.targetUnit, UNIT_IF_ARMOR_TYPE)
            set this.defenseType    = BlzGetUnitIntegerField(this.targetUnit, UNIT_IF_DEFENSE_TYPE)
            set this.prevArmorT     = this.armorType
            set this.prevDefenseT   = this.defenseType
            set this.armorPierced   = 0.00
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            set Damage.index        = this
            call DamageTrigger.setGUIFromStruct(true)
           
            call GroupAddUnit(proclusGlobal, udg_DamageEventSource)
            call GroupAddUnit(fischerMorrow, udg_DamageEventTarget)
            //! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_01()
            //! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_02()
            //! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_03()
            //! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_04()
            //! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_05()
            if udg_DamageEventAmount != 0.00 then
                set udg_DamageEventOverride = udg_DamageEventDamageT == 0
                call DamageTrigger.MOD.run()
    static if not USE_GUI then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                call DamageTrigger.setGUIFromStruct(false)
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                if natural then
                    call BlzSetEventAttackType(this.attackType)
                    call BlzSetEventDamageType(this.damageType)
                    call BlzSetEventWeaponType(this.weaponType)
                    call BlzSetEventDamage(udg_DamageEventAmount)
                endif
    static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                call setArmor(false)
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                return false
            endif
            return true
        endmethod
        private static method unfreeze takes nothing returns nothing
            local Damage i = damageStack
            loop
                exitwhen i == 0
                set i                                    = i - 1
                set i.stackRef.recursiveTrig.trigFrozen  = false
                set i.stackRef.recursiveTrig.levelsDeep  = 0
            endloop                          
            call EnableTrigger(t1)
            call EnableTrigger(t2)
            set kicking                                 = false
            set damageStack                             = 0
            set prepped                                 = 0
            set dreaming                                = false
            set sleepLevel                              = 0
            call GroupClear(proclusGlobal)
            call GroupClear(fischerMorrow)
            //call BJDebugMsg("Cleared up the groups")
        endmethod
        static method finish takes nothing returns nothing
            local Damage i                                  = 0
            local integer exit                        
            if eventsRun then                        
                set eventsRun                               = false
                call afterDamage()
            endif
            if canKick and not kicking then
                if damageStack != 0 then
                    set kicking                             = true
                    loop
                        set sleepLevel                      = sleepLevel + 1
                        set exit                            = damageStack
                        loop
                            set prepped                     = i.stackRef
                            if UnitAlive(prepped.targetUnit) then //Added just in case dead units had issues.
                                call prepped.doPreEvents(false) //don't evaluate the pre-event
                                if prepped.damage > 0.00 then
                                    call DisableTrigger(t1) //Force only the after armor event to run.
                                    call EnableTrigger(t2)  //in case the user forgot to re-enable this
                                    set totem               = true
                                    call UnitDamageTarget(prepped.sourceUnit, prepped.targetUnit, prepped.damage, prepped.isAttack, prepped.isRanged, prepped.attackType, prepped.damageType, prepped.weaponType)
                                else
                                    //No new events run at all in this case
                                    if udg_DamageEventDamageT != 0 then
                                        call DamageTrigger.DAMAGE.run()
                                    endif
                                    if prepped.damage < 0.00 then
                                        //No need for BlzSetEventDamage here
                                        call SetWidgetLife(prepped.targetUnit, GetWidgetLife(prepped.targetUnit) - prepped.damage)
                                    endif
    static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                                    call setArmor(true)
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                                endif
                                call afterDamage()
                            endif
                            set i = i + 1
                            exitwhen i == exit
                        endloop
                        exitwhen i == damageStack
                    endloop
                endif
                call unfreeze()
            endif
        endmethod
        private static method failsafeClear takes nothing returns nothing
    static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            call setArmor(true)
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            set canKick         = true
            set kicking         = false
            set totem           = false
            if udg_DamageEventDamageT != 0 then
                call DamageTrigger.DAMAGE.run()
                set eventsRun   = true
            endif
            call finish()
        endmethod
        static method operator enabled= takes boolean b returns nothing
            if b then
                if dreaming then
                    call EnableTrigger(t3)
                else
                    call EnableTrigger(t1)
                    call EnableTrigger(t2)
                endif
            else
                if dreaming then
                    call DisableTrigger(t3)
                else
                    call DisableTrigger(t1)
                    call DisableTrigger(t2)
                endif
            endif
        endmethod
        static method operator enabled takes nothing returns boolean
            return IsTriggerEnabled(t1)
        endmethod
       
        private static boolean arisen = false
       
        private static method getOutOfBed takes nothing returns nothing
            if totem then
                call failsafeClear() //WarCraft 3 didn't run the DAMAGED event despite running the DAMAGING event.
            else
                set canKick     = true
                set kicking     = false
                call finish()
            endif
    static if USE_EXTRA then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            call onAOEEnd()
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            set arisen = true
        endmethod
       
        private static method wakeUp takes nothing returns nothing
            set dreaming = false
            set Damage.enabled = true
            call ForForce(bj_FORCE_PLAYER[0], function thistype.getOutOfBed) //Moved to a new thread in case of a thread crash
            if not arisen then
                //call BJDebugMsg("DamageEngine issue: thread crashed!")
                call unfreeze()
            else
                set arisen = false
            endif
            set Damage.count    = 0
            set Damage.index    = 0
            set alarmSet        = false
            //call BJDebugMsg("Timer wrapped up")
        endmethod
        private method addRecursive takes nothing returns nothing
            if this.damage != 0.00 then
                set this.recursiveTrig = DamageTrigger.eventIndex
                if not this.isCode then
                    set this.isCode = true
                    set this.userType = TYPE_CODE
                endif
                set inception = inception or DamageTrigger.eventIndex.inceptionTrig
                if kicking and IsUnitInGroup(this.sourceUnit, proclusGlobal) and IsUnitInGroup(this.targetUnit, fischerMorrow) then
                    if not inception then
                        set DamageTrigger.eventIndex.trigFrozen = true
                    elseif 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
                    endif
                endif
                set damageStack.stackRef = this
                set damageStack = damageStack + 1
                //call BJDebugMsg("damageStack: " + I2S(damageStack) + " levelsDeep: " + I2S(DamageTrigger.eventIndex.levelsDeep) + " sleepLevel: " + I2S(sleepLevel))
            endif
            set inception = false
        endmethod
        private static method clearNexts takes nothing returns nothing
            set udg_NextDamageIsAttack      = false
            set udg_NextDamageType          = 0
            set udg_NextDamageWeaponT       = 0
    static if USE_MELEE_RANGE then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            set udg_NextDamageIsMelee       = false
            set udg_NextDamageIsRanged      = false
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
        endmethod
        static method create takes unit src, unit tgt, real amt, boolean a, attacktype at, damagetype dt, weapontype wt returns Damage
            local Damage d      = Damage.count + 1
            set Damage.count    = d
            set d.sourceUnit    = src
            set d.targetUnit    = tgt
            set d.damage        = amt
            set d.prevAmt       = amt
                         
            set d.attackType    = at
            set d.damageType    = dt
            set d.weaponType    = wt
                         
            set d.isAttack      = udg_NextDamageIsAttack or a
            set d.isSpell       = d.attackType == null and not d.isAttack
            return d
        endmethod
        private static method createFromEvent takes nothing returns Damage
            local Damage d                  = create(GetEventDamageSource(), GetTriggerUnit(), GetEventDamage(), BlzGetEventIsAttack(), BlzGetEventAttackType(), BlzGetEventDamageType(), BlzGetEventWeaponType())
            set d.isCode                    = udg_NextDamageType != 0 or udg_NextDamageIsAttack or udg_NextDamageIsRanged or udg_NextDamageIsMelee or d.damageType == DAMAGE_TYPE_MIND or udg_NextDamageWeaponT != 0 or (d.damage != 0.00 and d.damageType == DAMAGE_TYPE_UNKNOWN)
     
            if d.isCode then
                if udg_NextDamageType != 0 then
                    set d.userType          = udg_NextDamageType
                else
                    set d.userType          = TYPE_CODE
                endif
    static if USE_MELEE_RANGE then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                set d.isMelee               = udg_NextDamageIsMelee
                set d.isRanged              = udg_NextDamageIsRanged
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                set d.eFilter               = FILTER_CODE
                if udg_NextDamageWeaponT != 0 then
                    set d.weaponType        = ConvertWeaponType(udg_NextDamageWeaponT)
                    set udg_NextDamageWeaponT = 0
                endif
            else
                set d.userType              = 0
                if d.damageType == DAMAGE_TYPE_NORMAL and d.isAttack then
    static if USE_MELEE_RANGE then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                    set d.isMelee           = IsUnitType(d.sourceUnit, UNIT_TYPE_MELEE_ATTACKER)
                    set d.isRanged          = IsUnitType(d.sourceUnit, UNIT_TYPE_RANGED_ATTACKER)
                    if d.isMelee and d.isRanged then
                        set d.isMelee       = d.weaponType != null  // Melee units play a sound when damaging
                        set d.isRanged      = not d.isMelee         // In the case where a unit is both ranged and melee, the ranged attack plays no sound.
                    endif
                    if d.isMelee then
                        set d.eFilter       = FILTER_MELEE
                    elseif d.isRanged then
                        set d.eFilter       = FILTER_RANGED
                    else
                        set d.eFilter       = FILTER_ATTACK
                    endif
    else
                    set d.eFilter           = FILTER_ATTACK
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                else
                    if d.isSpell then
                        set d.eFilter       = FILTER_SPELL
                    else
                        set d.eFilter       = FILTER_OTHER
                    endif
    static if USE_MELEE_RANGE then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                    set d.isMelee           = false
                    set d.isRanged          = false
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                endif
            endif
            call clearNexts()
            return d
        endmethod
        private static method onRecursion takes nothing returns boolean //New in 5.7
            local Damage d  = Damage.createFromEvent()
            call d.addRecursive()
            call BlzSetEventDamage(0.00)
            return false
        endmethod
        private static method onDamaging takes nothing returns boolean
            local Damage d              = Damage.createFromEvent()
            //call BJDebugMsg("Pre-damage event running for " + GetUnitName(GetTriggerUnit()))
            if alarmSet then
                if totem then //WarCraft 3 didn't run the DAMAGED event despite running the DAMAGING event.
                    if d.damageType == DAMAGE_TYPE_SPIRIT_LINK or d.damageType == DAMAGE_TYPE_DEFENSIVE or d.damageType == DAMAGE_TYPE_PLANT then
                        set totem       = false
                        set lastInstance= Damage.index
                        set canKick     = false
                    else
                        call failsafeClear() //Not an overlapping event - just wrap it up
                    endif
                else
                    call finish() //wrap up any previous damage index
                endif
               
    static if USE_EXTRA then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                if d.sourceUnit != udg_AOEDamageSource then
                    call onAOEEnd()
                    set udg_AOEDamageSource = 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
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            else
                call TimerStart(alarm, 0.00, false, function Damage.wakeUp)
                set alarmSet                = true
    static if USE_EXTRA then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                set udg_AOEDamageSource     = d.sourceUnit
                set udg_EnhancedDamageTarget= d.targetUnit
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            endif
    static if USE_EXTRA then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            call GroupAddUnit(udg_DamageEventAOEGroup, d.targetUnit)
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            if d.doPreEvents(true) then
                call DamageTrigger.ZERO.run()
                set canKick                 = true
                call finish()
            endif
            set totem                       = lastInstance == 0 or attacksImmune[udg_DamageEventAttackT] or damagesImmune[udg_DamageEventDamageT] or not IsUnitType(udg_DamageEventTarget, UNIT_TYPE_MAGIC_IMMUNE)
            return false
        endmethod
        private static method onDamaged takes nothing returns boolean
            local real r                    = GetEventDamage()
            local Damage d                  = Damage.index
            //call BJDebugMsg("Second damage event running for " + GetUnitName(GetTriggerUnit()))
            if prepped > 0 then
                set prepped                 = 0
            elseif dreaming or d.prevAmt == 0.00 then
                return false
            elseif totem then
                set totem                   = false
            else
                //This should only happen for stuff like Spirit Link or Thorns Aura/Carapace
                call afterDamage()
                set Damage.index            = lastInstance
                set lastInstance            = 0
                set d                       = Damage.index
                set canKick                 = true
                call DamageTrigger.setGUIFromStruct(true)
            endif
    static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            call setArmor(true)
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
           
    static if USE_SCALING then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            if udg_DamageEventAmount != 0.00 and r != 0.00 then
                set udg_DamageScalingWC3    = r / udg_DamageEventAmount
            elseif udg_DamageEventAmount > 0.00 then
                set udg_DamageScalingWC3    = 0.00
            else                      
                set udg_DamageScalingWC3    = 1.00
                if udg_DamageEventPrevAmt == 0.00 then
                    set udg_DamageScalingUser = 0.00
                else
                    set udg_DamageScalingUser = udg_DamageEventAmount/udg_DamageEventPrevAmt
                endif
            endif                    
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            set udg_DamageEventAmount       = r
            set d.damage                    = r
     
            //! 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()
     
            if udg_DamageEventAmount > 0.00 then
                call DamageTrigger.SHIELD.run()
    static if not USE_GUI then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                set udg_DamageEventAmount = d.damage
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
    static if USE_LETHAL then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                if hasLethal or udg_DamageEventType < 0 then
                    set udg_LethalDamageHP = GetWidgetLife(udg_DamageEventTarget) - udg_DamageEventAmount
                    if udg_LethalDamageHP <= DEATH_VAL then
                        if hasLethal then
                            call DamageTrigger.LETHAL.run()
               
                            set udg_DamageEventAmount = GetWidgetLife(udg_DamageEventTarget) - udg_LethalDamageHP
                            set d.damage = udg_DamageEventAmount
                        endif
                        if udg_DamageEventType < 0 and udg_LethalDamageHP <= DEATH_VAL then
                            call SetUnitExploded(udg_DamageEventTarget, true)
                        endif
                    endif
                endif
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
    static if USE_SCALING then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                if udg_DamageEventPrevAmt == 0.00 or udg_DamageScalingWC3 == 0.00 then
                    set udg_DamageScalingUser = 0.00
                else
                    set udg_DamageScalingUser = udg_DamageEventAmount/udg_DamageEventPrevAmt/udg_DamageScalingWC3
                endif
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
            endif
            if udg_DamageEventDamageT != 0 then
                call DamageTrigger.DAMAGE.run()
            endif
            call BlzSetEventDamage(udg_DamageEventAmount)
            set eventsRun                   = true
            if udg_DamageEventAmount == 0.00 then
                call finish()
            endif
            return false
        endmethod
        static method apply takes unit src, unit tgt, real amt, boolean a, boolean r, attacktype at, damagetype dt, weapontype wt returns Damage
            local Damage d
            if udg_NextDamageType == 0 then
               set udg_NextDamageType = TYPE_CODE
            endif
            if dreaming then
                set d              = create(src, tgt, amt, a, at, dt, wt)
                set d.isCode       = true
                set d.eFilter      = FILTER_CODE
                             
                set d.userType     = udg_NextDamageType
    static if USE_MELEE_RANGE then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                if not d.isSpell then
                    set d.isRanged = udg_NextDamageIsRanged or r
                    set d.isMelee  = not d.isRanged
                endif
    endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
                call d.addRecursive()
            else
                call UnitDamageTarget(src, tgt, amt, a, r, at, dt, wt)
                set d = Damage.index
                call finish()
            endif
            call clearNexts()
            return d
        endmethod
        static method applySpell takes unit src, unit tgt, real amt, damagetype dt returns Damage
            return apply(src, tgt, amt, false, false, null, dt, null)
        endmethod
        static method applyAttack takes unit src, unit tgt, real amt, boolean ranged, attacktype at, weapontype wt returns Damage
            return apply(src, tgt, amt, true, ranged, at, DAMAGE_TYPE_NORMAL, wt)
        endmethod
        //===========================================================================
        private static method onInit takes nothing returns nothing
            call TriggerRegisterAnyUnitEventBJ(t1, EVENT_PLAYER_UNIT_DAMAGING)
            call TriggerAddCondition(t1, Filter(function Damage.onDamaging))
     
            call TriggerRegisterAnyUnitEventBJ(t2, EVENT_PLAYER_UNIT_DAMAGED)
            call TriggerAddCondition(t2, Filter(function Damage.onDamaged))
     
            //For recursion
            call TriggerRegisterAnyUnitEventBJ(t3, EVENT_PLAYER_UNIT_DAMAGING)
            call TriggerAddCondition(t3, Filter(function Damage.onRecursion))
            call DisableTrigger(t3)
     
            //For preventing Thorns/Defensive glitch.
            //Data gathered from https://www.hiveworkshop.com/threads/repo-in-progress-mapping-damage-types-to-their-abilities.316271/
            set attacksImmune[0]  = false   //ATTACK_TYPE_NORMAL
            set attacksImmune[1]  = true    //ATTACK_TYPE_MELEE  
            set attacksImmune[2]  = true    //ATTACK_TYPE_PIERCE  
            set attacksImmune[3]  = true    //ATTACK_TYPE_SIEGE  
            set attacksImmune[4]  = false   //ATTACK_TYPE_MAGIC  
            set attacksImmune[5]  = true    //ATTACK_TYPE_CHAOS  
            set attacksImmune[6]  = true    //ATTACK_TYPE_HERO    
     
            set damagesImmune[0]  = true    //DAMAGE_TYPE_UNKNOWN      
            set damagesImmune[4]  = true    //DAMAGE_TYPE_NORMAL          
            set damagesImmune[5]  = true    //DAMAGE_TYPE_ENHANCED        
            set damagesImmune[8]  = false   //DAMAGE_TYPE_FIRE            
            set damagesImmune[9]  = false   //DAMAGE_TYPE_COLD              
            set damagesImmune[10] = false   //DAMAGE_TYPE_LIGHTNING        
            set damagesImmune[11] = true    //DAMAGE_TYPE_POISON          
            set damagesImmune[12] = true    //DAMAGE_TYPE_DISEASE          
            set damagesImmune[13] = false   //DAMAGE_TYPE_DIVINE            
            set damagesImmune[14] = false   //DAMAGE_TYPE_MAGIC            
            set damagesImmune[15] = false   //DAMAGE_TYPE_SONIC            
            set damagesImmune[16] = true    //DAMAGE_TYPE_ACID            
            set damagesImmune[17] = false   //DAMAGE_TYPE_FORCE            
            set damagesImmune[18] = false   //DAMAGE_TYPE_DEATH            
            set damagesImmune[19] = false   //DAMAGE_TYPE_MIND              
            set damagesImmune[20] = false   //DAMAGE_TYPE_PLANT            
            set damagesImmune[21] = false   //DAMAGE_TYPE_DEFENSIVE        
            set damagesImmune[22] = true    //DAMAGE_TYPE_DEMOLITION      
            set damagesImmune[23] = true    //DAMAGE_TYPE_SLOW_POISON      
            set damagesImmune[24] = false   //DAMAGE_TYPE_SPIRIT_LINK      
            set damagesImmune[25] = false   //DAMAGE_TYPE_SHADOW_STRIKE    
            set damagesImmune[26] = true    //DAMAGE_TYPE_UNIVERSAL
        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 DebugStr takes nothing returns nothing
            local integer i                      = 0
            loop
                set udg_CONVERTED_ATTACK_TYPE[i] = ConvertAttackType(i)
                exitwhen i == 6
                set i                            = i + 1
            endloop
            set i                                = 0
            loop
                set udg_CONVERTED_DAMAGE_TYPE[i] = ConvertDamageType(i)
                exitwhen i == 26
                set i                            = i + 1
            endloop
            set udg_AttackTypeDebugStr[0]        = "SPELLS"   //ATTACK_TYPE_NORMAL in JASS
            set udg_AttackTypeDebugStr[1]        = "NORMAL"   //ATTACK_TYPE_MELEE in JASS
            set udg_AttackTypeDebugStr[2]        = "PIERCE"
            set udg_AttackTypeDebugStr[3]        = "SIEGE"
            set udg_AttackTypeDebugStr[4]        = "MAGIC"
            set udg_AttackTypeDebugStr[5]        = "CHAOS"
            set udg_AttackTypeDebugStr[6]        = "HERO"
            set udg_DamageTypeDebugStr[0]        = "UNKNOWN"
            set udg_DamageTypeDebugStr[4]        = "NORMAL"
            set udg_DamageTypeDebugStr[5]        = "ENHANCED"
            set udg_DamageTypeDebugStr[8]        = "FIRE"
            set udg_DamageTypeDebugStr[9]        = "COLD"
            set udg_DamageTypeDebugStr[10]       = "LIGHTNING"
            set udg_DamageTypeDebugStr[11]       = "POISON"
            set udg_DamageTypeDebugStr[12]       = "DISEASE"
            set udg_DamageTypeDebugStr[13]       = "DIVINE"
            set udg_DamageTypeDebugStr[14]       = "MAGIC"
            set udg_DamageTypeDebugStr[15]       = "SONIC"
            set udg_DamageTypeDebugStr[16]       = "ACID"
            set udg_DamageTypeDebugStr[17]       = "FORCE"
            set udg_DamageTypeDebugStr[18]       = "DEATH"
            set udg_DamageTypeDebugStr[19]       = "MIND"
            set udg_DamageTypeDebugStr[20]       = "PLANT"
            set udg_DamageTypeDebugStr[21]       = "DEFENSIVE"
            set udg_DamageTypeDebugStr[22]       = "DEMOLITION"
            set udg_DamageTypeDebugStr[23]       = "SLOW_POISON"
            set udg_DamageTypeDebugStr[24]       = "SPIRIT_LINK"
            set udg_DamageTypeDebugStr[25]       = "SHADOW_STRIKE"
            set udg_DamageTypeDebugStr[26]       = "UNIVERSAL"
            set udg_WeaponTypeDebugStr[0]        = "NONE"    //WEAPON_TYPE_WHOKNOWS in JASS
            set udg_WeaponTypeDebugStr[1]        = "METAL_LIGHT_CHOP"
            set udg_WeaponTypeDebugStr[2]        = "METAL_MEDIUM_CHOP"
            set udg_WeaponTypeDebugStr[3]        = "METAL_HEAVY_CHOP"
            set udg_WeaponTypeDebugStr[4]        = "METAL_LIGHT_SLICE"
            set udg_WeaponTypeDebugStr[5]        = "METAL_MEDIUM_SLICE"
            set udg_WeaponTypeDebugStr[6]        = "METAL_HEAVY_SLICE"
            set udg_WeaponTypeDebugStr[7]        = "METAL_MEDIUM_BASH"
            set udg_WeaponTypeDebugStr[8]        = "METAL_HEAVY_BASH"
            set udg_WeaponTypeDebugStr[9]        = "METAL_MEDIUM_STAB"
            set udg_WeaponTypeDebugStr[10]       = "METAL_HEAVY_STAB"
            set udg_WeaponTypeDebugStr[11]       = "WOOD_LIGHT_SLICE"
            set udg_WeaponTypeDebugStr[12]       = "WOOD_MEDIUM_SLICE"
            set udg_WeaponTypeDebugStr[13]       = "WOOD_HEAVY_SLICE"
            set udg_WeaponTypeDebugStr[14]       = "WOOD_LIGHT_BASH"
            set udg_WeaponTypeDebugStr[15]       = "WOOD_MEDIUM_BASH"
            set udg_WeaponTypeDebugStr[16]       = "WOOD_HEAVY_BASH"
            set udg_WeaponTypeDebugStr[17]       = "WOOD_LIGHT_STAB"
            set udg_WeaponTypeDebugStr[18]       = "WOOD_MEDIUM_STAB"
            set udg_WeaponTypeDebugStr[19]       = "CLAW_LIGHT_SLICE"
            set udg_WeaponTypeDebugStr[20]       = "CLAW_MEDIUM_SLICE"
            set udg_WeaponTypeDebugStr[21]       = "CLAW_HEAVY_SLICE"
            set udg_WeaponTypeDebugStr[22]       = "AXE_MEDIUM_CHOP"
            set udg_WeaponTypeDebugStr[23]       = "ROCK_HEAVY_BASH"
            set udg_DefenseTypeDebugStr[0]       = "LIGHT"
            set udg_DefenseTypeDebugStr[1]       = "MEDIUM"
            set udg_DefenseTypeDebugStr[2]       = "HEAVY"
            set udg_DefenseTypeDebugStr[3]       = "FORTIFIED"
            set udg_DefenseTypeDebugStr[4]       = "NORMAL"   //Typically deals flat damage to all armor types
            set udg_DefenseTypeDebugStr[5]       = "HERO"
            set udg_DefenseTypeDebugStr[6]       = "DIVINE"
            set udg_DefenseTypeDebugStr[7]       = "UNARMORED"
            set udg_ArmorTypeDebugStr[0]         = "NONE"      //ARMOR_TYPE_WHOKNOWS in JASS, added in 1.31
            set udg_ArmorTypeDebugStr[1]         = "FLESH"
            set udg_ArmorTypeDebugStr[2]         = "METAL"
            set udg_ArmorTypeDebugStr[3]         = "WOOD"
            set udg_ArmorTypeDebugStr[4]         = "ETHEREAL"
            set udg_ArmorTypeDebugStr[5]         = "STONE"
        endfunction
        //===========================================================================
        //
        // Setup of automatic events from GUI and custom ones from JASS alike
        //
        //===========================================================================
        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
        //For GUI to tap into more powerful vJass event filtering:
        //! textmacro DAMAGE_TRIGGER_CONFIG
            if not DamageTrigger.eventIndex.configured then
        //! endtextmacro
        //! textmacro DAMAGE_TRIGGER_CONFIG_END
                call DamageTrigger.eventIndex.configure()
                if not DamageTrigger.eventIndex.checkConfiguration() then
                    return
                endif
            endif
        //! endtextmacro
    endlibrary
     


  • Code (Lua):

    --[[
    ===========================================================================
     Lua Version
     Damage Engine lets you detect, amplify, block or nullify damage. It even
     lets you detect if the damage was physical or from a spell. Just reference
     DamageEventAmount/Source/Target or the boolean IsDamageSpell, to get the
     necessary damage event data.

     - Detect damage (after it was dealt to the unit): use the event "DamageEvent Equal to 1.00"
     - To change damage before it is dealt: use the event "DamageModifierEvent Equal to 1.00"
     - Detect spell damage: use the condition "IsDamageSpell Equal to True"
     - Detect zero-damage: use the event "DamageEvent Equal to 2.00"

     You can specify the DamageEventType before dealing triggered damage:
     - Set NextDamageType = DamageTypeWhatever
     - Unit - Cause...

     You can modify the DamageEventAmount and the DamageEventType from a "DamageModifierEvent Equal to 1.00" trigger.
     - If the amount is modified to negative, it will count as a heal.
     - If the amount is set to 0, no damage will be dealt.

     If you need to reference the original in-game damage, use the variable "DamageEventPrevAmt".
    GUI Vars:

       Retained from 3.8 and prior:
       ----------------------------
       unit           udg_DamageEventSource
       unit           udg_DamageEventTarget
       unit           udg_EnhancedDamageTarget
       group          udg_DamageEventAOEGroup
       integer        udg_DamageEventAOE
       integer        udg_DamageEventLevel
       real           udg_DamageModifierEvent
       real           udg_DamageEvent
       real           udg_AfterDamageEvent
       real           udg_DamageEventAmount
       real           udg_DamageEventPrevAmt
       real           udg_AOEDamageEvent
       boolean        udg_DamageEventOverride
       boolean        udg_NextDamageType
       boolean        udg_DamageEventType
       boolean        udg_IsDamageSpell

       Added in 5.0:
       boolean        udg_IsDamageMelee
       boolean        udg_IsDamageRanged
       unit           udg_AOEDamageSource
       real           udg_LethalDamageEvent
       real           udg_LethalDamageHP
       real           udg_DamageScalingWC3
       integer        udg_DamageEventAttackT
       integer        udg_DamageEventDamageT
       integer        udg_DamageEventWeaponT

       Added in 5.1:
       boolean        udg_IsDamageCode

       Added in 5.2:
       integer        udg_DamageEventArmorT
       integer        udg_DamageEventDefenseT

       Addded in 5.3:
       real           DamageEventArmorPierced
       real           udg_DamageScalingUser

       Added in 5.4.2 to allow GUI users to re-issue the exact same attack and damage type at the attacker.
       attacktype array udg_CONVERTED_ATTACK_TYPE
       damagetype array udg_CONVERTED_DAMAGE_TYPE

    =============================================================================
    --]]

    do
       local alarm       = CreateTimer()
       local alarmSet    = false

       --Values to track the original pre-spirit Link/defensive damage values
       local canKick     = true
       local totem       = false
       local armorType   = 0
       local defenseType = 0
       local prev        = {}

       --Stuff to track recursive UnitDamageTarget calls.
       local eventsRun   = false
       local kicking     = false
       local stack       = {}

       --Added in 5.4 to silently eliminate infinite recursion.
       local userTrigs   = 9
       local eventTrig   = 0
       local nextTrig    = {}
       local userTrig    = {}
       local trigFrozen  = {}

       --Added/re-tooled in 5.4.1 to allow forced recursion (for advanced users only).
       local levelsDeep     = {}   --How deep the user recursion currently is.
       local LIMBO          = 16   --Recursion will never go deeper than LIMBO.
       DamageEngine_inception= false --You must set DamageEngine_inception = true before dealing damage to utlize this.
                              --When true, it allows your trigger to potentially go recursive up to LIMBO.
       local dreaming       = false
       local fischerMorrow  = {} --track targets of recursion
       local inceptionTrig  = {}   --Added in 5.4.2 to simplify the inception variable for very complex DamageEvent trigger.
       local proclusGlobal  = {} --track sources of recursion
       local sleepLevel     = 0

       --Improves readability in the code to have these as named constants.
       local event = {
          mod      = 1,
          shield   = 4,
          damage   = 5,
          zero     = 6,
          after    = 7,
          lethal   = 8,
          aoe      = 9
       }

       local function runTrigs(i)
          local cat = i
          dreaming = true
          --print("Running " .. cat)
          while (true) do
             i = nextTrig[i]
             if (i == 0)
               or (cat == event.mod and (udg_DamageEventOverride or udg_DamageEventType*udg_DamageEventType == 4))
               or (cat == event.shield and udg_DamageEventAmount <= 0.00)
               or (cat == event.lethal and udg_LethalDamageHP > 0.405) then
                break
             end
             if not trigFrozen[i] then
                eventTrig = i
                if RunTrigger then --Added 10 July 2019 to enable FastTriggers mode.
                   RunTrigger(userTrig[i])
                elseif IsTriggerEnabled(userTrig[i])
                  and TriggerEvaluate(userTrig[i]) then
                   TriggerExecute(userTrig[i])
                end
                --print("Ran " .. i)
             end
          end
          --print("Ran")
          dreaming = false
       end

       local function onAOEEnd()
          if udg_DamageEventAOE > 1 then
             runTrigs(event.aoe)
             udg_DamageEventAOE   = 1
          end
          udg_DamageEventLevel    = 1
          udg_EnhancedDamageTarget= nil
          udg_AOEDamageSource     = nil
          GroupClear(udg_DamageEventAOEGroup)
       end

       local function afterDamage()
          if udg_DamageEventPrevAmt ~= 0.00 and udg_DamageEventDamageT ~= udg_DAMAGE_TYPE_UNKNOWN then
             runTrigs(event.after)
          end
       end

       local oldUDT = UnitDamageTarget

       local function finish()
          if eventsRun then
             --print "events ran"
             eventsRun = false
             afterDamage()
          end
          if canKick and not kicking then
             local n = #stack
             if n > 0 then
                kicking = true
                --print("Clearing Recursion: " .. n)
                local i = 0
                local open
                repeat
                   sleepLevel = sleepLevel + 1
                   repeat
                      i = i + 1 --Need to loop bottom to top to make sure damage order is preserved.
                      open = stack[i]
                      udg_NextDamageType = open.type
                      --print("Stacking on " .. open.amount)
                      oldUDT(open.source, open.target, open.amount, true, false, open.attack, open.damage, open.weapon)
                      afterDamage()
                   until (i == n)
                   --print("Exit at: " .. i)
                   n = #stack
                until (i == n)
                --print("Terminate at: " .. i)
                sleepLevel = 0
                repeat
                   open = stack[i].trig
                   stack[i] = nil
                   proclusGlobal[open] = nil
                   fischerMorrow[open] = nil
                   trigFrozen[open] = false -- Only re-enable recursive triggers AFTER all damage is dealt.
                   levelsDeep[open] = 0 --Reset this stuff if the user tried some nonsense
                   --print("unfreezing " .. open)
                   i = i - 1
                until (i == 0)
                kicking = false
             end
          end
       end

       function UnitDamageTarget(src, tgt, amt, a, r, at, dt, wt)
          if udg_NextDamageType == 0 then
             udg_NextDamageType = udg_DamageTypeCode
          end
          local b = false
          if dreaming then
             if amt ~= 0.00 then
                -- Store triggered, recursive damage into a stack.
                -- This damage will be fired after the current damage instance has wrapped up its events.
                stack[#stack + 1] = {
                   type     = udg_NextDamageType,
                   source   = src,
                   target   = tgt,
                   amount   = amt,
                   attack   = at,
                   damage   = dt,
                   weapon   = wt,
                   trig     = eventTrig
                }
                --print("increasing damage stack: " .. #stack)
     
                -- Next block added in 5.4.1 to allow *some* control over whether recursion should kick
                -- in. Also it's important to track whether the source and target were both involved at
                -- some earlier point, so this is a more accurate and lenient method than before.
                DamageEngine_inception = DamageEngine_inception or inceptionTrig[eventTrig]
     
                local sg = proclusGlobal[eventTrig]
                if not sg then
                   sg = {}
                   proclusGlobal[eventTrig] = sg
                end
                sg[udg_DamageEventSource] = true
     
                local tg = fischerMorrow[eventTrig]
                if not tg then
                   tg = {}
                   fischerMorrow[eventTrig] = tg
                end
                tg[udg_DamageEventTarget] = true
     
                if kicking and sg[src] and tg[tgt] then
                   if DamageEngine_inception and not trigFrozen[eventTrig] then
                      inceptionTrig[eventTrig] = true
                      if levelsDeep[eventTrig] < sleepLevel then
                         levelsDeep[eventTrig] = levelsDeep[eventTrig] + 1
                         if levelsDeep[eventTrig] >= LIMBO then
                            --print("freezing inception trig: " .. eventTrig)
                            trigFrozen[eventTrig] = true
                         end
                      end
                   else
                      --print("freezing standard trig: " .. eventTrig)
                      trigFrozen[eventTrig] = true
                   end
                end
             end
          else
             b = oldUDT(src, tgt, amt, a, r, at, dt, wt)
          end
          --print("setting inception to false")
          DamageEngine_inception = false
          udg_NextDamageType = 0
          if b and not dreaming then
             finish() -- Wrap up the outstanding damage instance right away.
          end
          return b
       end

       local function resetArmor()
          if udg_DamageEventArmorPierced ~= 0.00 then
             BlzSetUnitArmor(udg_DamageEventTarget, BlzGetUnitArmor(udg_DamageEventTarget) + udg_DamageEventArmorPierced)
          end
          if armorType ~= udg_DamageEventArmorT then
             BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_ARMOR_TYPE, armorType) --revert changes made to the damage instance
          end
          if defenseType ~= udg_DamageEventDefenseT then
             BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_DEFENSE_TYPE, defenseType)
          end
       end

       local function failsafeClear()
          --print("Damage from " .. GetUnitName(udg_DamageEventSource) .. " to " .. GetUnitName(udg_DamageEventTarget) .. " has been messing up Damage Engine.")
          --print(udg_DamageEventAmount .. " " .. " " .. udg_DamageEventPrevAmt .. " " .. udg_AttackTypeDebugStr[udg_DamageEventAttackT] .. " " .. udg_DamageTypeDebugStr[udg_DamageEventDamageT])
          resetArmor()
          canKick = true
          totem = false
          udg_DamageEventAmount = 0.00
          udg_DamageScalingWC3  = 0.00
          if udg_DamageEventDamageT ~= udg_DAMAGE_TYPE_UNKNOWN then
             runTrigs(event.damage) --Run the normal on-damage event based on this failure.
             eventsRun = true --Run the normal after-damage event based on this failure.
          end
          finish()
       end

       local function calibrateMR()
          udg_IsDamageMelee         = false
          udg_IsDamageRanged        = false
          udg_IsDamageSpell         = udg_DamageEventAttackT == 0 --In Patch 1.31, one can just check the attack type to find out if it's a spell.
          if udg_DamageEventDamageT == udg_DAMAGE_TYPE_NORMAL and not udg_IsDamageSpell then --This damage type is the only one that can get reduced by armor.
             udg_IsDamageMelee      = IsUnitType(udg_DamageEventSource, UNIT_TYPE_MELEE_ATTACKER)
             udg_IsDamageRanged     = IsUnitType(udg_DamageEventSource, UNIT_TYPE_RANGED_ATTACKER)
             if udg_IsDamageMelee and udg_IsDamageRanged then
                udg_IsDamageMelee   = udg_DamageEventWeaponT > 0-- Melee units play a sound when damaging
                udg_IsDamageRanged  = not udg_IsDamageMelee    -- In the case where a unit is both ranged and melee, the ranged attack plays no sound.
             end                                       -- The Huntress has a melee sound for her ranged projectile, however it is only an issue
          end                                          --if she also had a melee attack, because by default she is only UNIT_TYPE_RANGED_ATTACKER.
       end

       local t1 = CreateTrigger()
       TriggerRegisterAnyUnitEventBJ(t1, EVENT_PLAYER_UNIT_DAMAGING)
       TriggerAddCondition(t1, Filter(function()
          local src = GetEventDamageSource()
          local tgt = BlzGetEventDamageTarget()
          local amt = GetEventDamage()
          local at = BlzGetEventAttackType()
          local dt = BlzGetEventDamageType()
          local wt = BlzGetEventWeaponType()

          --print "First damage event running"

          if not kicking then
             if alarmSet then
                if totem then
                   if dt ~= DAMAGE_TYPE_SPIRIT_LINK and dt ~= DAMAGE_TYPE_DEFENSIVE and dt ~= DAMAGE_TYPE_PLANT then
                      -- if 'totem' is still set and it's not due to spirit link distribution or defense retaliation,
                      -- the next function must be called as a debug. This reverts an issue I created in patch 5.1.3.
                      failsafeClear()
                   else
                      totem       = false
                      canKick     = false
                      prev.type   = udg_DamageEventType      -- also store the damage type.
                      prev.amount = udg_DamageEventAmount
                      prev.preAmt = udg_DamageEventPrevAmt   -- Store the actual pre-armor value.
                      prev.pierce = udg_DamageEventArmorPierced
                      prev.armor  = udg_DamageEventArmorT
                      prev.preArm = armorType
                      prev.defense= udg_DamageEventDefenseT
                      prev.preDef = defenseType
                      prev.code   = udg_IsDamageCode        -- store this as well.
                   end
                end
                if src ~= udg_AOEDamageSource then -- Source has damaged more than once
                   onAOEEnd() -- New damage source - unflag everything
                   udg_AOEDamageSource = src
                elseif tgt == udg_EnhancedDamageTarget then
                   udg_DamageEventLevel= udg_DamageEventLevel + 1  -- The number of times the same unit was hit.
                elseif not IsUnitInGroup(tgt, udg_DamageEventAOEGroup) then
                   udg_DamageEventAOE  = udg_DamageEventAOE + 1   -- Multiple targets hit by this source - flag as AOE
                end
             else
                TimerStart(alarm, 0.00, false, function()
                   alarmSet = false --The timer has expired. Flag off to allow it to be restarted when needed.
                   finish() --Wrap up any outstanding damage instance
                   onAOEEnd() --Reset things so they don't perpetuate for AoE/Level target detection
                end)
                alarmSet                = true
                udg_AOEDamageSource     = src
                udg_EnhancedDamageTarget= tgt
             end
             GroupAddUnit(udg_DamageEventAOEGroup, tgt)
          end
          udg_DamageEventType           = udg_NextDamageType
          udg_IsDamageCode              = udg_NextDamageType ~= 0
          udg_DamageEventOverride       = dt == nil -- Got rid of NextDamageOverride in 5.1 for simplicity
          udg_DamageEventPrevAmt        = amt
          udg_DamageEventSource         = src
          udg_DamageEventTarget         = tgt
          udg_DamageEventAmount         = amt
          udg_DamageEventAttackT        = GetHandleId(at)
          udg_DamageEventDamageT        = GetHandleId(dt)
          udg_DamageEventWeaponT        = GetHandleId(wt)

          calibrateMR() -- Set Melee and Ranged settings.

          udg_DamageEventArmorT         = BlzGetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_ARMOR_TYPE) -- Introduced in Damage Engine 5.2.0.0
          udg_DamageEventDefenseT       = BlzGetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_DEFENSE_TYPE)
          armorType                     = udg_DamageEventArmorT
          defenseType                   = udg_DamageEventDefenseT
          udg_DamageEventArmorPierced   = 0.00
          udg_DamageScalingUser         = 1.00
          udg_DamageScalingWC3          = 1.00

          if amt ~= 0.00 then
             if not udg_DamageEventOverride then
                runTrigs(event.mod)
     
                -- All events have run and the pre-damage amount is finalized.
                BlzSetEventAttackType(ConvertAttackType(udg_DamageEventAttackT))
                BlzSetEventDamageType(ConvertDamageType(udg_DamageEventDamageT))
                BlzSetEventWeaponType(ConvertWeaponType(udg_DamageEventWeaponT))
                if udg_DamageEventArmorPierced ~= 0.00 then
                   BlzSetUnitArmor(udg_DamageEventTarget, BlzGetUnitArmor(udg_DamageEventTarget) - udg_DamageEventArmorPierced)
                end
                if armorType ~= udg_DamageEventArmorT then
                   BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_ARMOR_TYPE, udg_DamageEventArmorT) -- Introduced in Damage Engine 5.2.0.0
                end
                if defenseType ~= udg_DamageEventDefenseT then
                   BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_DEFENSE_TYPE, udg_DamageEventDefenseT) -- Introduced in Damage Engine 5.2.0.0
                end
                BlzSetEventDamage(udg_DamageEventAmount)
             end
             totem = true
             -- print("Ready to deal " .. udg_DamageEventAmount)
          else
             runTrigs(event.zero)
             canKick = true
             finish()
          end
          return false
       end))

       local t2 = CreateTrigger()
       TriggerRegisterAnyUnitEventBJ(t2, EVENT_PLAYER_UNIT_DAMAGED)
       TriggerAddCondition(t2, Filter(function()
          if udg_DamageEventPrevAmt == 0.00 then
             return false
          end
          --print "Second event running"
          if totem then
             totem = false   --This should be the case in almost all circumstances
          else
             afterDamage() --Wrap up the outstanding damage instance
             canKick                = true
             --Unfortunately, Spirit Link and Thorns Aura/Spiked Carapace fire the DAMAGED event out of sequence with the DAMAGING event,
             --so I have to re-generate a buncha stuff here.
             udg_DamageEventSource      = GetEventDamageSource()
             udg_DamageEventTarget      = GetTriggerUnit()
             udg_DamageEventAmount      = prev.amount
             udg_DamageEventPrevAmt     = prev.preAmt
             udg_DamageEventAttackT     = GetHandleId(BlzGetEventAttackType())
             udg_DamageEventDamageT     = GetHandleId(BlzGetEventDamageType())
             udg_DamageEventWeaponT     = GetHandleId(BlzGetEventWeaponType())
             udg_DamageEventType        = prev.type
             udg_IsDamageCode           = prev.code
             udg_DamageEventArmorT      = prev.armor
             udg_DamageEventDefenseT    = prev.defense
             udg_DamageEventArmorPierced= prev.pierce
             armorType                  = prev.preArm
             defenseType                = prev.preDef
             calibrateMR() --Apply melee/ranged settings once again.
          end
          resetArmor()
          local r = GetEventDamage()
          if udg_DamageEventAmount ~= 0.00 and r ~= 0.00 then
             udg_DamageScalingWC3 = r/udg_DamageEventAmount
          else
             if udg_DamageEventAmount > 0.00 then
                udg_DamageScalingWC3 = 0.00
             else
                udg_DamageScalingWC3 = 1.00
             end
             udg_DamageScalingUser = udg_DamageEventAmount/udg_DamageEventPrevAmt
          end
          udg_DamageEventAmount = udg_DamageEventAmount*udg_DamageScalingWC3

          if udg_DamageEventAmount > 0.00 then
             --This event is used for custom shields which have a limited hit point value
             --The shield here kicks in after armor, so it acts like extra hit points.
             runTrigs(event.shield)
             udg_LethalDamageHP = GetWidgetLife(udg_DamageEventTarget) - udg_DamageEventAmount
             if udg_LethalDamageHP <= 0.405 then
                runTrigs(event.lethal) -- added 10 May 2019 to detect and potentially prevent lethal damage. Instead of
                -- modifying the damage, you need to modify LethalDamageHP instead (the final HP of the unit).
     
                udg_DamageEventAmount = GetWidgetLife(udg_DamageEventTarget) - udg_LethalDamageHP
                if udg_DamageEventType < 0 and udg_LethalDamageHP <= 0.405 then
                   SetUnitExploded(udg_DamageEventTarget, true)   --Explosive damage types should blow up the target.
                end
             end
             udg_DamageScalingUser = udg_DamageEventAmount/udg_DamageEventPrevAmt/udg_DamageScalingWC3
          end
          BlzSetEventDamage(udg_DamageEventAmount)   --Apply the final damage amount.
          if udg_DamageEventDamageT ~= udg_DAMAGE_TYPE_UNKNOWN then
             runTrigs(event.damage)
          end
          eventsRun = true
          --print(canKick)
          if udg_DamageEventAmount == 0.00 then
             finish()
          end
          return false
       end))

       onGlobalInit(function()
          local i
          for i = 0, 6 do udg_CONVERTED_ATTACK_TYPE[i] = ConvertAttackType(i) end
          for i = 0, 26 do udg_CONVERTED_DAMAGE_TYPE[i] = ConvertDamageType(i) end

          udg_AttackTypeDebugStr[0] = "SPELLS"   -- ATTACK_TYPE_NORMAL in JASS
          udg_AttackTypeDebugStr[1] = "NORMAL"   -- ATTACK_TYPE_MELEE in JASS
          udg_AttackTypeDebugStr[2] = "PIERCE"
          udg_AttackTypeDebugStr[3] = "SIEGE"
          udg_AttackTypeDebugStr[4] = "MAGIC"
          udg_AttackTypeDebugStr[5] = "CHAOS"
          udg_AttackTypeDebugStr[6] = "HERO"

          udg_DamageTypeDebugStr[0]  = "UNKNOWN"
          udg_DamageTypeDebugStr[4]  = "NORMAL"
          udg_DamageTypeDebugStr[5]  = "ENHANCED"
          udg_DamageTypeDebugStr[8]  = "FIRE"
          udg_DamageTypeDebugStr[9]  = "COLD"
          udg_DamageTypeDebugStr[10] = "LIGHTNING"
          udg_DamageTypeDebugStr[11] = "POISON"
          udg_DamageTypeDebugStr[12] = "DISEASE"
          udg_DamageTypeDebugStr[13] = "DIVINE"
          udg_DamageTypeDebugStr[14] = "MAGIC"
          udg_DamageTypeDebugStr[15] = "SONIC"
          udg_DamageTypeDebugStr[16] = "ACID"
          udg_DamageTypeDebugStr[17] = "FORCE"
          udg_DamageTypeDebugStr[18] = "DEATH"
          udg_DamageTypeDebugStr[19] = "MIND"
          udg_DamageTypeDebugStr[20] = "PLANT"
          udg_DamageTypeDebugStr[21] = "DEFENSIVE"
          udg_DamageTypeDebugStr[22] = "DEMOLITION"
          udg_DamageTypeDebugStr[23] = "SLOW_POISON"
          udg_DamageTypeDebugStr[24] = "SPIRIT_LINK"
          udg_DamageTypeDebugStr[25] = "SHADOW_STRIKE"
          udg_DamageTypeDebugStr[26] = "UNIVERSAL"

          udg_WeaponTypeDebugStr[0]  = "NONE"    -- WEAPON_TYPE_WHOKNOWS in JASS
          udg_WeaponTypeDebugStr[1]  = "METAL_LIGHT_CHOP"
          udg_WeaponTypeDebugStr[2]  = "METAL_MEDIUM_CHOP"
          udg_WeaponTypeDebugStr[3]  = "METAL_HEAVY_CHOP"
          udg_WeaponTypeDebugStr[4]  = "METAL_LIGHT_SLICE"
          udg_WeaponTypeDebugStr[5]  = "METAL_MEDIUM_SLICE"
          udg_WeaponTypeDebugStr[6]  = "METAL_HEAVY_SLICE"
          udg_WeaponTypeDebugStr[7]  = "METAL_MEDIUM_BASH"
          udg_WeaponTypeDebugStr[8]  = "METAL_HEAVY_BASH"
          udg_WeaponTypeDebugStr[9]  = "METAL_MEDIUM_STAB"
          udg_WeaponTypeDebugStr[10] = "METAL_HEAVY_STAB"
          udg_WeaponTypeDebugStr[11] = "WOOD_LIGHT_SLICE"
          udg_WeaponTypeDebugStr[12] = "WOOD_MEDIUM_SLICE"
          udg_WeaponTypeDebugStr[13] = "WOOD_HEAVY_SLICE"
          udg_WeaponTypeDebugStr[14] = "WOOD_LIGHT_BASH"
          udg_WeaponTypeDebugStr[15] = "WOOD_MEDIUM_BASH"
          udg_WeaponTypeDebugStr[16] = "WOOD_HEAVY_BASH"
          udg_WeaponTypeDebugStr[17] = "WOOD_LIGHT_STAB"
          udg_WeaponTypeDebugStr[18] = "WOOD_MEDIUM_STAB"
          udg_WeaponTypeDebugStr[19] = "CLAW_LIGHT_SLICE"
          udg_WeaponTypeDebugStr[20] = "CLAW_MEDIUM_SLICE"
          udg_WeaponTypeDebugStr[21] = "CLAW_HEAVY_SLICE"
          udg_WeaponTypeDebugStr[22] = "AXE_MEDIUM_CHOP"
          udg_WeaponTypeDebugStr[23] = "ROCK_HEAVY_BASH"

          udg_DefenseTypeDebugStr[0] = "LIGHT"
          udg_DefenseTypeDebugStr[1] = "MEDIUM"
          udg_DefenseTypeDebugStr[2] = "HEAVY"
          udg_DefenseTypeDebugStr[3] = "FORTIFIED"
          udg_DefenseTypeDebugStr[4] = "NORMAL"
          udg_DefenseTypeDebugStr[5] = "HERO"
          udg_DefenseTypeDebugStr[6] = "DIVINE"
          udg_DefenseTypeDebugStr[7] = "UNARMORED"

          udg_ArmorTypeDebugStr[0] = "NONE"
          udg_ArmorTypeDebugStr[1] = "FLESH"
          udg_ArmorTypeDebugStr[2] = "METAL"
          udg_ArmorTypeDebugStr[3] = "WOOD"
          udg_ArmorTypeDebugStr[4] = "ETHEREAL"
          udg_ArmorTypeDebugStr[5] = "STONE"
       end)

       function DamageEngine_SetupEvent(whichTrig, var, val)
          --print("Setup event: " .. var)
          local mx = 1
          local off = 0
          local ex = 0
          if var == "udg_DamageModifierEvent" then --event.mod 1-4 -> Events 1-4
             if (val < 3) then
                ex = val + 1
             end
             mx = 4
          elseif var == "udg_DamageEvent" then --event.damage 1,2 -> Events 5,6
             mx = 2
             off = 4
          elseif var == "udg_AfterDamageEvent" then --event.after -> Event 7
             off = 6
          elseif var == "udg_LethalDamageEvent" then --event.lethal -> Event 8
             off = 7
          elseif var == "udg_AOEDamageEvent" then --event.aoe -> Event 9
             off = 8
          else
             return false
          end
          local i
          if userTrigs == 9 then
             nextTrig[1] = 2
             nextTrig[2] = 3
             trigFrozen[2] = true
             trigFrozen[3] = true
             for i = 3, 9 do nextTrig[i] = 0 end
          end
          i = math.max(math.min(val, mx), 1) + off
          --print("Root index: " .. i .. " nextTrig: " .. nextTrig[i] .. " exit: " .. ex)
          repeat
             val = i
             i = nextTrig[i]
          until (i == ex)
          userTrigs = userTrigs + 1   --User list runs from index 10 and up
          nextTrig[val] = userTrigs
          nextTrig[userTrigs] = ex
          userTrig[userTrigs] = whichTrig
          levelsDeep[userTrigs] = 0
          trigFrozen[userTrigs] = false
          inceptionTrig[userTrigs] = false
          --print("Registered " .. userTrigs .. " to " .. val)
          return true
       end

       onRegisterVar(function(trig, var, val)
          DamageEngine_SetupEvent(trig, var, math.floor(val))
       end)
    end
     
    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.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:

    Code (vJASS):

    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:


  • Code (vJASS):

    // Weep's GDD, made fully Damage Engine compatible. Version 2.1.1.0
    // Original: https://www.hiveworkshop.com/threads/gui-friendly-damage-detection-v1-2-1.149098/
    //! textmacro DAMAGE_EVENT_VARS_PLUGIN_GDD
        set udg_GDD_DamageSource = udg_DamageEventSource
        set udg_GDD_DamagedUnit = udg_DamageEventTarget
        set udg_GDD_Damage = udg_DamageEventAmount
    //! endtextmacro
    //! textmacro DAMAGE_EVENT_REG_PLUGIN_GDD
        if var == "udg_GDD_Event" then
            set root = DamageTrigger.DAMAGE
        endif
    //! endtextmacro
     


  • Code (vJASS):

    library DamageEvent requires DamageEngine
    // looking_for_help's PDD (both versions), made fully Damage Engine compatible. Version 3.0.4.0
    // Original GUI: https://www.hiveworkshop.com/threads/physical-damage-detection-for-gui-v1-3-0-0.231846/
    // Original vJass: https://www.hiveworkshop.com/threads/system-physical-damage-detection.228456/
    //Un-comment the below block if you wish to remove the vJass syntax.
    ///*
    globals
        constant integer PHYSICAL = 0
        constant integer SPELL = 1
        constant integer CODE = 2
    endglobals
    struct PDDS extends array
        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.amount
        endmethod
        static method operator amount= takes real r returns nothing
            set Damage.amount = r
        endmethod
        static method operator damageType takes nothing returns integer
            if udg_IsDamageCode then
                return CODE
            elseif udg_IsDamageSpell then
                return SPELL
            endif
            return PHYSICAL
        endmethod
    endstruct
    function AddDamageHandler takes code c returns nothing
        call RegisterDamageEngine(c, "Mod", 4.00)
    endfunction
    function RemoveDamageHandler takes code c returns nothing
        call DamageTrigger.unregister(DamageTrigger[c], "", 1.00, true)
    endfunction
    function GetUnitLife takes unit u returns real
        return GetWidgetLife(u)
    endfunction
    function SetUnitLife takes unit u, real r returns nothing
        call SetWidgetLife(u, r)
    endfunction
    function GetUnitMaxLife takes unit u returns real
        return GetUnitState(u, UNIT_STATE_MAX_LIFE)
    endfunction
    function UnitDamageTargetEx takes unit src, unit tgt, real amt, boolean a, boolean r, attacktype at, damagetype dt, weapontype wt returns nothing
        call Damage.apply(src, tgt, amt, a, r, at, dt, wt)
    endfunction
    //*/
    //Un-comment the above block if you wish to remove the vJass syntax.
    endlibrary
    //Un-comment the below block if you wish to remove the GUI syntax.
    ///*
    //! textmacro DAMAGE_EVENT_VARS_PLUGIN_PDD
        set udg_PDD_source = udg_DamageEventSource
        set udg_PDD_target = udg_DamageEventTarget
        if udg_IsDamageCode then
            set udg_PDD_damageType = udg_PDD_CODE
        elseif udg_IsDamageSpell then
            set udg_PDD_damageType = udg_PDD_SPELL
        else
            set udg_PDD_damageType = udg_PDD_PHYSICAL
        endif
    //! endtextmacro
    //! textmacro DAMAGE_EVENT_FILTER_PLUGIN_PDD
        if this.eventStr == "udg_PDD_damageEventTrigger" then
            set udg_PDD_amount = udg_DamageEventAmount
        endif
    //! endtextmacro
    //! textmacro DAMAGE_EVENT_MOD_PLUGIN_PDD
        if this.eventStr == "udg_PDD_damageEventTrigger" then
            set udg_DamageEventAmount = udg_PDD_amount
        endif
    //! endtextmacro
    //! textmacro DAMAGE_EVENT_REG_PLUGIN_GDD
        if var == "udg_PDD_damageEventTrigger" then
            set root = DamageTrigger.SHIELD
            if udg_PDD_CODE == 0 then
                set udg_PDD_SPELL = 1
                set udg_PDD_CODE = 2
            endif
        endif
    //! endtextmacro
    //*/
    //Un-comment the above block if you wish to remove the GUI syntax.
     



  • Code (vJASS):

    library_once DamageEvent requires DamageEngine
    // Flux's DamagePackage, made fully Damage Engine compatible. Version 1.0.0.0
    // Original: https://www.hiveworkshop.com/threads/damagepackage.287101/
    endlibrary
    library_once DamageModify uses DamageEvent
    endlibrary
    globals
        constant integer DAMAGE_TYPE_PHYSICAL = 1
        constant integer DAMAGE_TYPE_MAGICAL = 2
    endglobals
    //! textmacro DAMAGE_EVENT_STRUCT_PLUGIN_DMGPKG
    static method operator type takes nothing returns integer
        if udg_IsDamageSpell then
            return DAMAGE_TYPE_MAGICAL
        endif
        return DAMAGE_TYPE_PHYSICAL
    endmethod
    static method registerTrigger takes trigger t returns nothing
        call DamageTrigger.registerTrigger(t, "", 1.00)
    endmethod
    static method unregisterTrigger takes trigger t returns nothing
        call DamageTrigger.unregister(t, "", 1.00, true)
    endmethod
    static method register takes code c returns nothing
        call registerTrigger(DamageTrigger[c])
    endmethod
    static method registerModifierTrigger takes trigger t returns nothing
        call DamageTrigger.registerTrigger(t, "Mod", 4.00)
    endmethod
    static method unregisterModifierTrigger takes trigger t returns nothing
        call DamageTrigger.unregister(t, "Mod", 4.00, true)
    endmethod
    static method registerModifier takes code c returns nothing
        call registerModifierTrigger(DamageTrigger[c])
    endmethod
    static method lockAmount takes nothing returns nothing
        set udg_DamageEventOverride = true
    endmethod
    //! endtextmacro
     


  • Code (vJASS):

    library Damage requires DamageEngine
    // Jesus4Lyf's DAMAGE library, made fully Damage Engine compatible. Version 1.0.0.0
    // Original: https://www.thehelper.net/threads/damage.117041/
        public function RegisterEvent takes trigger whichTrigger returns nothing
            call TriggerRegisterDamageEngine(whichTrigger, "Mod", 4.00)
        endfunction
        public function RegisterZeroEvent takes trigger whichTrigger returns nothing
            call TriggerRegisterDamageEngine(whichTrigger, "", 0.00)
        endfunction
        public function GetType takes nothing returns damagetype
            return Damage.index.damageType
        endfunction
        public function IsAttack takes nothing returns boolean
            return udg_IsDamageAttack
        endfunction
        public function Block takes real value returns nothing
            set Damage.amount = Damage.amount - value
        endfunction
        public function BlockAll takes nothing returns nothing
            set Damage.amount = 0.00
            set udg_DamageEventOverride = true
        endfunction
        public function EnableEvent takes boolean b returns nothing
            set Damage.enabled = b
        endfunction
     
        function UnitDamageTargetEx takes unit src, unit tgt, real amt, boolean a, boolean r, attacktype at, damagetype dt, weapontype wt returns nothing
            call Damage.apply(src, tgt, amt, a, r, at, dt, wt)
        endfunction
     
        //! textmacro Damage__DealTypeFunc takes NAME, TYPE
            public function $NAME$ takes unit source, unit target, real amount returns nothing
                call Damage.apply(source, target, amount, false, false, null, $TYPE$, null)
            endfunction
            public function Is$NAME$ takes nothing returns boolean
                return Damage.index.damageType == $TYPE$
            endfunction
        //! endtextmacro
        //! runtextmacro Damage__DealTypeFunc("Pure","DAMAGE_TYPE_UNIVERSAL")
        //! runtextmacro Damage__DealTypeFunc("Spell","DAMAGE_TYPE_MAGIC")
        public function Physical takes unit source, unit target, real amount, attacktype whichType, boolean attack, boolean ranged returns nothing
            call Damage.apply(source, target, amount, attack, ranged, whichType, DAMAGE_TYPE_NORMAL, null)
        endfunction
        public function IsPhysical takes nothing returns boolean
            return Damage.index.damageType == DAMAGE_TYPE_NORMAL
        endfunction
    endlibrary
     


  • Code (vJASS):

    library IntuitiveDamageSystem requires DamageEngine
    // Rising_Dusk's IDDS - made fully Damage Engine compatible. Version 1.0.0.1
    // Original: http://www.wc3c.net/showthread.php?t=100618
    globals
        private  integer       DamageTypeCount     = 4
        constant integer       DAMAGE_TYPE_ATTACK  = 100
        constant integer       DAMAGE_TYPE_IGNORED = 101
        constant integer       DAMAGE_TYPE_SPELL   = 102
        constant integer       DAMAGE_TYPE_EXTRA   = 103
    endglobals
        function RegisterDamageType takes nothing returns integer
            local integer i = DamageTypeCount
            set DamageTypeCount = DamageTypeCount + 1
            return 100 + i
        endfunction
        function IgnoreHigherPriority takes nothing returns nothing
            set udg_DamageEventOverride = true
        endfunction
        function SetDamage takes real dmg returns nothing
            set Damage.amount = dmg //Unlike with IDDS this WILL actually change the damage dealt
        endfunction
        function SetDamageType takes integer t returns nothing
            set Damage.index.userType = t
        endfunction
        function GetTriggerDamageSource takes nothing returns unit
            return udg_DamageEventSource
        endfunction
        function GetTriggerDamageTarget takes nothing returns unit
            return udg_DamageEventTarget
        endfunction
        function GetTriggerDamageBase takes nothing returns real
            return udg_DamageEventPrevAmt
        endfunction
        function GetTriggerDamage takes nothing returns real
            return Damage.amount
        endfunction
        function GetTriggerDamageType takes nothing returns integer
            return udg_DamageEventType
        endfunction
     
        function GetTriggerPriority takes trigger t returns integer
            local DamageTrigger index = DamageTrigger.getIndex(t, "Mod", 4.00)
            if index == 0 then
                return -1
            endif
            return R2I(index.weight - 4.00)
        endfunction
     
        function TriggerRegisterDamageEvent takes trigger t, integer priority returns nothing
            call TriggerRegisterDamageEngine(t, "Mod", 4.00 + priority)
        endfunction
     
        function TriggerUnregisterDamageEvent takes trigger t returns nothing
            call DamageTrigger.unregister(t, "Mod", 4.00, true)
        endfunction
     
        function TriggerSetPriority takes trigger t, integer priority returns nothing
            call DamageTrigger.unregister(t, "Mod", 4.00, false)
            call TriggerRegisterDamageEvent(t, priority)
        endfunction
     
        //The below function will actually use both DamageEngine's defined types as well as those of IDDS itself.
        function UnitDamageTargetEx takes unit s, unit t, real d, attacktype at, integer dt, boolean ca returns nothing
            if dt == DAMAGE_TYPE_ATTACK then
                set udg_NextDamageIsAttack = true
            endif
            set udg_NextDamageType = dt
            set Damage.enabled = dt != DAMAGE_TYPE_IGNORED
            if ca then
                call Damage.apply(s, t, d, udg_NextDamageIsAttack, false, at, DAMAGE_TYPE_NORMAL, null)
            else
                call Damage.apply(s, t, d, udg_NextDamageIsAttack, false, at, DAMAGE_TYPE_UNIVERSAL, null)
            endif
            set Damage.enabled = true
        endfunction
    endlibrary
     


  • Code (vJASS):

    library StructuredDD requires DamageEngine
    // Cokemonkey11's StructuredDD and DamageType - made fully Damage Engine compatible. Version 1.0.0.0
    // Originals: https://www.hiveworkshop.com/threads/system-structureddd-structured-damage-detection.216968/
    // https://www.hiveworkshop.com/threads/system-damagetype-structureddd-extension.228883/
        struct StructuredDD extends array
            static method addHandler takes code c returns nothing
                call RegisterDamageEngine(c, "Mod", 4.00)
            endmethod
        endstruct
    endlibrary
    library DamageType requires StructuredDD
        struct DamageType extends array
            static constant integer NULLED =-1
            static constant integer ATTACK = 0
            static constant integer SPELL  = 1
            static constant integer CODE   = 2
     
            static method get takes nothing returns integer
                if udg_IsDamageCode then
                    return CODE
                elseif udg_IsDamageSpell then
                    return SPELL
                elseif udg_DamageEventAmount == 0.00 then
                    return NULLED
                endif
                return ATTACK
            endmethod
            static method dealCodeDamage takes unit s, unit t, real d returns nothing
                call Damage.apply(s, t, d, true, false, null, DAMAGE_TYPE_UNIVERSAL, null)
            endmethod
        endstruct
    endlibrary
     


  • Code (vJASS):

    library DamageInterface requires DamageEngine
    // chopinkski's Damage Interface, made fully Damage Engine compatible. Version 1.0.0.0
    // This resource inspired me to build new events into GUI and integrate other built-in filters
    // Original: https://www.hiveworkshop.com/threads/damage-interface-v1-3.324257/
    //! textmacro DAMAGE_EVENT_USER_STRUCT_PLUGIN_01
    globals
        private keyword fillCache
    endglobals
    struct DamageI extends array
        readonly static damagetype damageType
        readonly static attacktype attackType
        readonly static unit       source
        readonly static unit       target
        readonly static player     sourcePlayer
        readonly static player     targetPlayer
        readonly static boolean    isEnemy
        readonly static boolean    isAlly
        readonly static boolean    isMelee
        readonly static boolean    isRanged
        readonly static boolean    isAttack
        readonly static boolean    isSpell
        readonly static boolean    isPure
        readonly static boolean    isEnhanced
        readonly static boolean    sourceIsHero
        readonly static boolean    targetIsHero
        readonly static boolean    structure
        readonly static boolean    magicImmune
        readonly static real       sourceX
        readonly static real       sourceY
        readonly static real       targetX
        readonly static real       targetY
        readonly static integer    sIdx
        readonly static integer    tIdx
        readonly static integer    sId
        readonly static integer    tId
     
        static method fillCache takes nothing returns nothing
            set damageType = Damage.index.damageType
            set attackType = Damage.index.attackType
            set source     = udg_DamageEventSource
            set target     = udg_DamageEventTarget
            set isAttack   = damageType == DAMAGE_TYPE_NORMAL
            set isSpell    = attackType == ATTACK_TYPE_NORMAL
            set isPure     = damageType == DAMAGE_TYPE_UNIVERSAL
            set isEnhanced = damageType == DAMAGE_TYPE_ENHANCED
            // You can comment-out the variables you dont want to be cached
            set sourcePlayer  = GetOwningPlayer(source)
            set targetPlayer  = GetOwningPlayer(target)
            set isEnemy       = IsUnitEnemy(target, sourcePlayer)
            set isAlly        = IsUnitAlly(target, sourcePlayer)
            set isMelee       = IsUnitType(source, UNIT_TYPE_MELEE_ATTACKER)
            set isRanged      = IsUnitType(source, UNIT_TYPE_RANGED_ATTACKER)
            set sourceIsHero  = IsUnitType(source, UNIT_TYPE_HERO)
            set targetIsHero  = IsUnitType(target, UNIT_TYPE_HERO)
            set structure     = IsUnitType(target, UNIT_TYPE_STRUCTURE)
            set magicImmune   = IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)
            set sourceX       = GetUnitX(source)
            set sourceY       = GetUnitY(source)
            set targetX       = GetUnitX(target)
            set targetY       = GetUnitY(target)
            set sIdx          = GetUnitUserData(source)
            set tIdx          = GetUnitUserData(target)
            set sId           = GetHandleId(source)
            set tId           = GetHandleId(target)
        endmethod
    endstruct
    //! endtextmacro
    //! textmacro DAMAGE_EVENT_PRE_VARS_PLUGIN_01
        call DamageI.fillCache()
    //! endtextmacro
    //! textmacro DAMAGE_EVENT_VARS_PLUGIN_01
        call DamageI.fillCache()
    //! endtextmacro
     
    //After armor events:
     
        function RegisterAttackDamageEvent takes code c returns nothing
            call RegisterDamageEngineEx(c, "Mod", 4.00, DamageEngine_FILTER_ATTACK)
        endfunction
     
        function RegisterSpellDamageEvent takes code c returns nothing
            call RegisterDamageEngineEx(c, "Mod", 4.00, DamageEngine_FILTER_SPELL)
        endfunction
     
        function RegisterPureDamageEvent takes code c returns nothing
            local DamageTrigger dt = RegisterDamageEngine(c, "Mod", 4.00)
            set dt.damageType = GetHandleId(DAMAGE_TYPE_UNIVERSAL)
            set dt.configured = true
        endfunction
     
        function RegisterEnhancedDamageEvent takes code c returns nothing
            local DamageTrigger dt = RegisterDamageEngine(c, "Mod", 4.00)
            set dt.damageType = GetHandleId(DAMAGE_TYPE_ENHANCED)
            set dt.configured = true
        endfunction
     
        function RegisterDamageEvent takes code c returns nothing
            call RegisterDamageEngine(c, "Mod", 4.00)
        endfunction
     
    //Before armor events:
     
        function RegisterAttackDamagingEvent takes code c returns nothing
            call RegisterDamageEngineEx(c, "Mod", 1.00, DamageEngine_FILTER_ATTACK)
        endfunction
     
        function RegisterSpellDamagingEvent takes code c returns nothing
            call RegisterDamageEngineEx(c, "Mod", 1.00, DamageEngine_FILTER_SPELL)
        endfunction
     
        function RegisterPureDamagingEvent takes code c returns nothing
            local DamageTrigger dt = RegisterDamageEngine(c, "Mod", 1.00)
            set dt.damageType = GetHandleId(DAMAGE_TYPE_UNIVERSAL)
            set dt.configured = true
        endfunction
     
        function RegisterEnhancedDamagingEvent takes code c returns nothing
            local DamageTrigger dt = RegisterDamageEngine(c, "Mod", 1.00)
            set dt.damageType = GetHandleId(DAMAGE_TYPE_ENHANCED)
            set dt.configured = true
        endfunction
     
        function RegisterDamagingEvent takes code c returns nothing
            call RegisterDamageEngine(c, "Mod", 1.00)
        endfunction
    endlibrary
     


  • Code (vJASS):

    library DDS requires DamageEngine
    // Nestharus's Damage system, partially* made Damage Engine compatible. Version 1.0.0.0
    // Original: https://github.com/nestharus/JASS/tree/master/jass/Systems/DDS
    //*Incorporates the core elements of Nestharus's DDS's API
    //Event registration only works via Module interface as yucky Trigger syntax is ignored.
    private struct S extends array
        static constant integer SPELL    = 0
        static constant integer PHYSICAL = 1
        static constant integer CODE     = 2
    endstruct
    struct DDS extends array
        readonly static S Archetype = 0
        static method operator archetype takes nothing returns integer
            if udg_IsDamageCode then
                return S.CODE
            elseif udg_IsDamageSpell then
                return S.SPELL
            endif
            return S.PHYSICAL
        endmethod
        static method operator damageCode= takes integer i returns nothing
            set udg_NextDamageType = udg_DamageTypeCode
        endmethod
        static method operator damageCode takes nothing returns integer
            return GetUnitUserData(udg_DamageEventTarget)
        endmethod
        static method operator source takes nothing returns unit
            return udg_DamageEventSource
        endmethod
        static method operator sourceId takes nothing returns integer
            return GetUnitUserData(udg_DamageEventSource)
        endmethod
        static method operator target takes nothing returns unit
            return udg_DamageEventTarget
        endmethod
        static method operator targetId takes nothing returns integer
            return GetUnitUserData(udg_DamageEventTarget)
        endmethod
        static method operator damage takes nothing returns real
            return Damage.amount
        endmethod
        static method operator damage= takes real r returns nothing
            set Damage.amount = r
        endmethod
        static method operator damageOriginal takes nothing returns real
            return udg_DamageEventPrevAmt
        endmethod
        static method operator damageModifiedAmount takes nothing returns real
            return Damage.amount - udg_DamageEventPrevAmt
        endmethod
        static method operator sourcePlayer takes nothing returns player
            return GetOwningPlayer(Damage.source)
        endmethod
        static method operator targetPlayer takes nothing returns player
            return GetOwningPlayer(Damage.target)
        endmethod
        method operator enabled= takes boolean b returns nothing
            set Damage.enabled = b
        endmethod
        method operator enabled takes nothing returns boolean
            return Damage.enabled
        endmethod
    endstruct
    module DDS
      private static delegate DDS dds = 0
      static if thistype.onDamage.exists then
        private static method preOnDamage takes nothing returns nothing
            call thistype(DDS.targetId).onDamage()
        endmethod
      endif
      static if thistype.onDamageOutgoing.exists then
        private static method preOnDamageOutgoing takes nothing returns nothing
            call thistype(DDS.sourceId).onDamageOutgoing()
        endmethod
      endif
      static if not thistype.onDamageBefore.exists and not thistype.onDamageAfter.exists and not thistype.onDamage.exists and not thistype.onDamageOutgoing.exists then
      else
        private static method onInit takes nothing returns nothing
          static if thistype.onDamageBefore.exists then
            call RegisterDamageEngine(function thistype.onDamageBefore, "Mod", 1.00)
          endif
          static if thistype.onDamageAfter.exists then
            call RegisterDamageEngine(function thistype.onDamageAfter, "Mod", 4.10)
          endif
          static if thistype.onDamage.exists then
            call RegisterDamageEngine(function thistype.preOnDamage, "Mod", 4.00)
          endif
          static if thistype.onDamageOutgoing.exists then
            call RegisterDamageEngine(function thistype.preOnDamageOutgoing, "Mod", 4.00)
          endif
        endmethod
      endif
    endmodule
    endlibrary
     




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 5.7.1.1 (Map)

Lua Damage Engine 1.0.2.3 (Map)

Reviews
Moderator
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...
  1. 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 use.

    Overall very read-able and well documentated code.
    Also the demo map is good enough to help users to quickly learn the API provided by Damage Engine.

    I don't want to diminish the finished performance of established damage detection systems
    by other authors namely Nestharus ( DDS ), Cokemonkey ( StructuredDD ) and looking_for_help ( PDD ).
    Quite the opposite is the case, these sometimes can fit better to what a user expects from a damage system.

    Still I want to recommend Damage Engine in the first place for every user, looking for a damage detection system.

    After having a chat with IcemanBo and PurgeandFire we decided to give Damage Engine a moderator rating of 6/5


    Director's Cut
     
  2. Bannar

    Bannar

    Joined:
    Mar 19, 2008
    Messages:
    3,088
    Resources:
    20
    Spells:
    5
    Tutorials:
    1
    JASS:
    14
    Resources:
    20
    Bribe.. now I'm mad on you..
    WHY HAVEN"T YOU RELEASED IT EALIER?

    I would make use of it in my project for hero contest, since I already made use of IsUnitMoving and Unit Indexer (you would take credits for all the systems =)). GDD is fine too, it uses same feature: Event - Value of Real variable and was here for years. Some things get old, new ones must replace them, like your's table did with Vexorian's one.
    Great additional to unit indexer, allowing user to stick with one model of system and still gives efficient way to detect damage event.

    Outstanding work done for GUI section. Deserves dc considering other GUI friendly systems related by you.
     
  3. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,003
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Thanks for teaching me that
    • Custom script: endfunction

    trick :p

    Nice System :)
    Now, GUIers have an 'Event' method :p
    A Unit Indexing System
    An IsUnitMoving System
    And a Damage Detection System ^^

    I wonder what's next..
    BoundSentinel?
    PowerupSentinel?
    xe? xD
     
  4. Jazztastic

    Jazztastic

    Joined:
    Apr 4, 2011
    Messages:
    901
    Resources:
    7
    Spells:
    6
    Tutorials:
    1
    Resources:
    7
    Golly wizz, everytime I think it just cant get any better, it does.

    Now I can finally make passive abilities based on attack.
     
  5. Adiktuz

    Adiktuz

    Joined:
    Oct 16, 2008
    Messages:
    9,674
    Resources:
    23
    Models:
    2
    Packs:
    1
    Maps:
    1
    Spells:
    16
    Tutorials:
    1
    JASS:
    2
    Resources:
    23
    Weep's DDS also allows that... ^_^

    anyway, this system is so cool!!! and its short!!! now it's a lot easier for GUIers to use a DDS...
     
  6. RiotApe

    RiotApe

    Joined:
    Jul 17, 2011
    Messages:
    51
    Resources:
    0
    Resources:
    0
    sorry for sounding like a noob, but what are damage detection systems for? why can't we use the event function, "Unit - Takes Damage"?
     
  7. Adiktuz

    Adiktuz

    Joined:
    Oct 16, 2008
    Messages:
    9,674
    Resources:
    23
    Models:
    2
    Packs:
    1
    Maps:
    1
    Spells:
    16
    Tutorials:
    1
    JASS:
    2
    Resources:
    23
    Why? because Unit - Takes Damage is a SPECIFIC UNIT EVENT, meaning you will need to register all possible units on the map... And it can cause leaks once the units die since the event stays...

    That is where DDS comes in, they automate the registration for you, so that you can have a generic damage event (in this system for example, your generic damage event will be Game - DamageEvent becomes Equal to 1.00)... And they also avoid the leaks via several methods... (like in this case, once 15 units are removed from the detection list, they recreate the trigger to clear the events that are not needed anymore)
     
  8. RiotApe

    RiotApe

    Joined:
    Jul 17, 2011
    Messages:
    51
    Resources:
    0
    Resources:
    0
    oh finally, that made perfect sense, thanks dude! though, i was wondering if there's a system that can detect if the damage was dealt by an attack, or if it was dealt by a spell?

    anyways, cool system, i think i'll use it :D

    10/10
     
  9. Adiktuz

    Adiktuz

    Joined:
    Oct 16, 2008
    Messages:
    9,674
    Resources:
    23
    Models:
    2
    Packs:
    1
    Maps:
    1
    Spells:
    16
    Tutorials:
    1
    JASS:
    2
    Resources:
    23
    Nestharus' system can do that, though you need vJASS knowledge...
     
  10. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,003
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Maybe you could a ZeroDamage Event (Just for the heck of it)

    I know GUIers could easily check if the amount was 0, but that would cause a huge amount of trigger executions and if/else evaluations for nothing :p
     
  11. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,283
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    A Zero damage event would probably be better suited from Damage Modifier because that system has a much higher chance of blocking. How often is a 0 damage event going to happen naturally in a game? I can only think if something is attacking divine armor?
     
  12. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,003
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Possibly from Faerie Fire too :p
     
  13. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,283
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    I'll have to check that out. By my preliminary checks I've seen that both Faerie Fire and Critical Strike could cause this event, so if these are still true in the latest patch then I should certainly seperate these out.

    That's really weird and counter-intuitive that Faerie Fire and/or Critical Strike procure a 0 damage event.
     
  14. EloTheMan

    EloTheMan

    Joined:
    Mar 18, 2009
    Messages:
    471
    Resources:
    0
    Resources:
    0
    GUI was really lacking a Damage functions, but there is one thing I was wondering about this trigger.

    How does "Game - UnitIndexEvent becomes Equal to 1.00" work as a damage event?
     
  15. Adiktuz

    Adiktuz

    Joined:
    Oct 16, 2008
    Messages:
    9,674
    Resources:
    23
    Models:
    2
    Packs:
    1
    Maps:
    1
    Spells:
    16
    Tutorials:
    1
    JASS:
    2
    Resources:
    23
    First and Foremost, the event which works as a Damage detection is

    "Game - UnitDamageEvent becomes Equal to 1.00" (which you should already know if you read the HOW TO USE part on the first post...)

    the function "Game - UnitIndexEvent becomes Equal to 1.00" is used for the set-up and maintenance of this system...

    First the system runs when a unit is indexed and deindexed by the UnitIndexer (which sets the UnitIndexEvent to 1 or 2)

    Now the system checks if a damage event trigger is present, if not it creates one and registers some functions to it...

    Next it will check if the unit is already registered within that trigger and if not, it registers the Unit gets damage event for that unit into the trigger (making that trigger run when the unit gets damaged)...

    Now the function linked to this Damage event trigger is the one which causes the variable UnitDamageEvent to be equal to 1.00 and then sets the variables thus enabling you to catch the damage event by using the Event,

    "Game - UnitDamageEvent becomes Equal to 1.00"

    This system also recreates the damage event trigger every 15 deindexed units to remove already useless damage events (since there is no function for deregistering events)...
     
  16. EloTheMan

    EloTheMan

    Joined:
    Mar 18, 2009
    Messages:
    471
    Resources:
    0
    Resources:
    0
    Reading triggers shouldn't be done at 2 in the morning lol, well things got a lot simpler now.

    Thanks :D
     
  17. Adiktuz

    Adiktuz

    Joined:
    Oct 16, 2008
    Messages:
    9,674
    Resources:
    23
    Models:
    2
    Packs:
    1
    Maps:
    1
    Spells:
    16
    Tutorials:
    1
    JASS:
    2
    Resources:
    23
    ooh... Didn't know, were from diff time zones so it showed as 8:11am on my comp... ^_^
     
  18. I don't understand these damage systems. Or the indexing.

    Can't you just take the "when a unit is attacked" and then use floating text to display the damage? Or is this about controlling the damage?

    Explanation?
     
  19. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,283
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    "Unit is attacked" does not include "unit is attacked by spell/by triggers". You also can't get the damage from that event.

    "Unit is attacked" is also an abusable event because it doesn't fire the instant the projectile leaves the unit or when the sword strikes its target, it fires the instant the unit's animation starts playing, so it is unreliable and abusable by spamming the "stop" button.

    "Unit is damaged" fires when the sword connects with its target or when the missile hits its mark. It is the only way to get the amount of damage.

    This system is necessary because there is no "any unit takes damage" event.