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. Lead your forces to battle in the 15th Techtree Contest. The call is yours, commander!
    Dismiss Notice
  5. The reforging of the races is complete. Come see the 14th Techtree Contest Results.
    Dismiss Notice
  6. It's time to choose your horse in the race - the 32nd Modeling Contest Poll is up!
    Dismiss Notice
  7. 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.6.2.0

Submitted by Bribe
This bundle is marked as approved. It works and satisfies the submission rules.
Damage Engine 5.6.2.0
The most powerful - yet easy to use - Damage Engine. Ever.
Developed for GUI, even with new support for JASS...
and with the speed of native JASS - or Lua - script.

New features in Damage Engine 5:
  • 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.
  • 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.
  • For more information, check the How to upgrade and How to use sections below.
  • For even more information, check out the video my daughter and I made below:
  • For even more information, check out the Damage Engine 5.4.1 "deep dive":

Featured uses for Damage Engine:

  • Bone Armor 1.14.1 by @Devalut beautifully shows how powerful the DamageModifier 4.00 processing can be.
  • Sunken City v2.4.4b by @SpasMaster shows how Damage Engine can be used throughout a map, visually evident by the abundant text tags.
Notable features retained from earlier versions of Damage Engine:
  • Damage Blocking, reducing, amplifying and/or conversion to healing;
  • Does not require you to know nor use Jass NewGen Pack nor any custom script;
  • Included cross-compatibility with looking_for_help's PDD and Weep's GDD;
  • 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.
Notes:
  • 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.
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!

Before diving in, I'd like to walk you through the complex process of how this engine and WarCraft 3 interact together to process damage:

Click if you dare


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 1.00 through 3.00

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 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).

√




FAQs

  • 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 Weep's GDD or looking_for_help's PDD. Can I use those with Damage Engine?
  • A: Better - Damage Engine has integrated cross-compatibility with those two resources. Use one of the provided custom JASS scripts instead of using their scripts in order to make the variables work with Damage Engine.
    .

  • 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".
      .
  • Q: How do I upgrade to the latest Damage Engine?
  • A: Depending on the complexity, you'll either need to re-copy the Damage Engine category or just the Damage Engine script. Generally, I do this:
    • 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._._ - copy over the entire Damage Engine category as variables were added. The Config trigger is typically updated at the same time.
    • Damage Engine x._._._ - this occurs very infrequently. Typically requires changes to the way Damage Engine needs to be installed and used.


Damage Engine Triggers
  • Damage Engine Config
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- - --------
      • -------- You can add extra classifications here if you want to differentiate between your triggered damage --------
      • -------- Use DamageTypeExplosive (or any negative value damage type) if you want a unit killed by that damage to explode --------
      • -------- - --------
      • -------- The pre-defined type Code might be set by Damage Engine if Unit - Damage Target is detected and the user didn't define a type of their own. --------
      • -------- "Pure" is especially important because it overrides both the Damage Engine as well as WarCraft 3 damage modification. --------
      • -------- I therefore gave the user "Explosive Pure" in case one wants to combine the functionality of the two. --------
      • -------- - --------
      • Set DamageTypePureExplosive = -2
      • Set DamageTypeExplosive = -1
      • Set DamageTypeCode = 1
      • Set DamageTypePure = 2
      • -------- - --------
      • Set DamageTypeHeal = 3
      • Set DamageTypeBlocked = 4
      • Set DamageTypeReduced = 5
      • -------- - --------
      • Set DamageTypeCriticalStrike = 6
      • -------- - --------
      • -------- Added 25 July 2017 to allow detection of things like Bash or Pulverize or AOE spread --------
      • -------- - --------
      • Set DamageEventAOE = 1
      • Set 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 ATTACK_TYPE_SPELLS = 0
      • Set ATTACK_TYPE_NORMAL = 1
      • Set ATTACK_TYPE_PIERCE = 2
      • Set ATTACK_TYPE_SIEGE = 3
      • Set ATTACK_TYPE_MAGIC = 4
      • Set ATTACK_TYPE_CHAOS = 5
      • Set ATTACK_TYPE_HERO = 6
      • -------- - --------
      • Set DAMAGE_TYPE_UNKNOWN = 0
      • Set DAMAGE_TYPE_NORMAL = 4
      • Set DAMAGE_TYPE_ENHANCED = 5
      • Set DAMAGE_TYPE_FIRE = 8
      • Set DAMAGE_TYPE_COLD = 9
      • Set DAMAGE_TYPE_LIGHTNING = 10
      • Set DAMAGE_TYPE_POISON = 11
      • Set DAMAGE_TYPE_DISEASE = 12
      • Set DAMAGE_TYPE_DIVINE = 13
      • Set DAMAGE_TYPE_MAGIC = 14
      • Set DAMAGE_TYPE_SONIC = 15
      • Set DAMAGE_TYPE_ACID = 16
      • Set DAMAGE_TYPE_FORCE = 17
      • Set DAMAGE_TYPE_DEATH = 18
      • Set DAMAGE_TYPE_MIND = 19
      • Set DAMAGE_TYPE_PLANT = 20
      • Set DAMAGE_TYPE_DEFENSIVE = 21
      • Set DAMAGE_TYPE_DEMOLITION = 22
      • Set DAMAGE_TYPE_SLOW_POISON = 23
      • Set DAMAGE_TYPE_SPIRIT_LINK = 24
      • Set DAMAGE_TYPE_SHADOW_STRIKE = 25
      • Set 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 WEAPON_TYPE_NONE = 0
      • -------- Metal Light/Medium/Heavy --------
      • Set WEAPON_TYPE_ML_CHOP = 1
      • Set WEAPON_TYPE_MM_CHOP = 2
      • Set WEAPON_TYPE_MH_CHOP = 3
      • Set WEAPON_TYPE_ML_SLICE = 4
      • Set WEAPON_TYPE_MM_SLICE = 5
      • Set WEAPON_TYPE_MH_SLICE = 6
      • Set WEAPON_TYPE_MM_BASH = 7
      • Set WEAPON_TYPE_MH_BASH = 8
      • Set WEAPON_TYPE_MM_STAB = 9
      • Set WEAPON_TYPE_MH_STAB = 10
      • -------- Wood Light/Medium/Heavy --------
      • Set WEAPON_TYPE_WL_SLICE = 11
      • Set WEAPON_TYPE_WM_SLICE = 12
      • Set WEAPON_TYPE_WH_SLICE = 13
      • Set WEAPON_TYPE_WL_BASH = 14
      • Set WEAPON_TYPE_WM_BASH = 15
      • Set WEAPON_TYPE_WH_BASH = 16
      • Set WEAPON_TYPE_WL_STAB = 17
      • Set WEAPON_TYPE_WM_STAB = 18
      • -------- Claw Light/Medium/Heavy --------
      • Set WEAPON_TYPE_CL_SLICE = 19
      • Set WEAPON_TYPE_CM_SLICE = 20
      • Set WEAPON_TYPE_CH_SLICE = 21
      • -------- Axe Medium --------
      • Set WEAPON_TYPE_AM_CHOP = 22
      • -------- Rock Heavy --------
      • Set WEAPON_TYPE_RH_BASH = 23
      • -------- - --------
      • -------- Since GUI still doesn't provide Defense Type and Armor Types, I needed to include the below --------
      • -------- - --------
      • Set ARMOR_TYPE_NONE = 0
      • Set ARMOR_TYPE_FLESH = 1
      • Set ARMOR_TYPE_METAL = 2
      • Set ARMOR_TYPE_WOOD = 3
      • Set ARMOR_TYPE_ETHEREAL = 4
      • Set ARMOR_TYPE_STONE = 5
      • -------- - --------
      • Set DEFENSE_TYPE_LIGHT = 0
      • Set DEFENSE_TYPE_MEDIUM = 1
      • Set DEFENSE_TYPE_HEAVY = 2
      • Set DEFENSE_TYPE_FORTIFIED = 3
      • Set DEFENSE_TYPE_NORMAL = 4
      • Set DEFENSE_TYPE_HERO = 5
      • Set DEFENSE_TYPE_DIVINE = 6
      • Set DEFENSE_TYPE_UNARMORED = 7
      • -------- - --------
      • Custom script: call DamageEngine_DebugStr()

Code (vJASS):

//===========================================================================
//
//  Damage Engine 5.6.2.0 - update requires replacing the JASS script
//
//===========================================================================
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 integer CODE       = 1        //Must be the same as udg_DamageTypeCode, or 0 if you prefer to disable the automatic flag.
    private constant integer PURE       = 2        //Must be the same as udg_DamageTypePure
    private constant integer LIMBO      = 16       //When manually-enabled recursion is enabled via DamageEngine_recurion, the engine will never go deeper than LIMBO.
    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 integer lastInstance    = 0
    private boolean canKick         = true
    private boolean totem           = false
    //Added in 5.4 to silently eliminate infinite recursion.
    private integer userTrigs       = 9
    private integer eventTrig       = 0
    private integer array nextTrig
    private trigger array rootTrig
    private real array trigWeight
    private boolean array usingGUI
    private boolean array trigFrozen
 
    //Added/re-tooled in 5.4.1 to allow forced recursion (for advanced users only).
    private integer array      levelsDeep              //How deep the user recursion currently is.
    public boolean             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.
    private boolean            dreaming        = false
    private boolean array      inceptionTrig           //Added in 5.4.2 to simplify the inception variable for very complex DamageEvent trigger.
    private integer            sleepLevel      = 0
    private group              proclusGlobal   = CreateGroup() //track sources of recursion
    private group              fischerMorrow   = CreateGroup() //track targets of recursion
    //Improves readability in the code to have these as named constants.
    private constant integer   MOD_EVENT       = 1
    private constant integer   SHIELD_EVENT    = 4
    private constant integer   DAMAGE_EVENT    = 5
    private constant integer   ZERO_EVENT      = 6
    private constant integer   AFTER_EVENT     = 7
    private constant integer   LETHAL_EVENT    = 8
    private constant integer   AOE_EVENT       = 9
    //Stuff to track recursive UnitDamageTarget calls.
    private Damage prepped = 0
    private Damage array stackRef
    private integer damageStack     = 0
    private boolean kicking         = false
    private boolean eventsRun       = false
endglobals
//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
*/

//This struct has been added due to the potential need for additional security around damage instance properties.
//It will help me to keep things in this script better organized as well as make the API more vJass-friendly for
//advanced users coming from other vJass damage systems.
//Additionally, this opens up the EnhancedDamageEvent and AOEDamageEvent with very important access.
struct Damage extends array
    static Damage index = 0 //the value of the currently-running damage event index, or 0 if none are running
    //index 1 represents the values of the original damage instance.
    private static integer count = 0//The number of currently-running queued or sequential damage instances
    unit source                 //stores udg_DamageEventSource
    unit target                 //stores udg_DamageEventTarget
    real amount                 //stores udg_DamageEventAmount
    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
    boolean isAttack            //stores udg_IsDamageAttack
    boolean isCode              //stores udg_IsDamageCode
    boolean isMelee             //stores udg_IsDamageMelee
    boolean isRanged            //stores udg_IsDamageRanged
    boolean isSpell             //stores udg_IsDamageSpell
    real armorPierced           //stores udg_DamageEventArmorPierced
    integer armorType           //stores udg_DamageEventArmorT
    integer defenseType         //stores udg_DamageEventDefenseT
    private integer prevArmorT
    private integer prevDefenseT
    private integer userTrig  
 
    static method setGUIFromStruct takes boolean full returns nothing
        set udg_DamageEventAmount       = Damage.index.amount
        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
        set udg_DamageEventArmorPierced = Damage.index.armorPierced
        set udg_DamageEventArmorT       = Damage.index.armorType
        set udg_DamageEventDefenseT     = Damage.index.defenseType
        if full then
            set udg_DamageEventSource   = Damage.index.source
            set udg_DamageEventTarget   = Damage.index.target
            set udg_DamageEventPrevAmt  = Damage.index.prevAmt
 
            set udg_IsDamageAttack      = Damage.index.isAttack
            set udg_IsDamageCode        = Damage.index.isCode
            set udg_IsDamageMelee       = Damage.index.isMelee
            set udg_IsDamageRanged      = Damage.index.isRanged
            set udg_IsDamageSpell       = Damage.index.isSpell
        endif
    endmethod
    private static method setArmor takes boolean reset returns nothing
        local real pierce
        local integer at
        local integer dt
        if reset then
            set pierce =  Damage.index.armorPierced
            set at     =  Damage.index.prevArmorT
            set dt     =  Damage.index.prevDefenseT
        else
            set pierce = -Damage.index.armorPierced
            set at     =  Damage.index.armorType
            set dt     =  Damage.index.defenseType
        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
 
    private static method runTrigs takes integer i returns nothing
        local integer cat                           = i
        local Damage d                              = Damage.index
      static if USE_GUI then                      
        local boolean mod                           = cat <= DAMAGE_EVENT
      endif                                      
        if dreaming then                          
            return                                
        endif                                    
        set dreaming                                = true
        //call BJDebugMsg("Start of event running")
        loop                                      
            set i                                   = nextTrig[i]
            exitwhen i == 0
            exitwhen cat == MOD_EVENT and (udg_DamageEventOverride or udg_DamageEventType == PURE)
            exitwhen cat == SHIELD_EVENT and udg_DamageEventAmount <= 0.00
            exitwhen cat == LETHAL_EVENT and udg_LethalDamageHP > DEATH_VAL
 
            if not trigFrozen[i] and IsTriggerEnabled(rootTrig[i]) then
                set eventTrig                       = i
                if TriggerEvaluate(rootTrig[i]) then
                    call TriggerExecute(rootTrig[i])
                endif
              static if USE_GUI then
                if mod then
                    if usingGUI[i] then
                        if cat != MOD_EVENT then
                            set d.amount            = udg_DamageEventAmount
                        elseif nextTrig[i] == 0 or not usingGUI[nextTrig[i]] then //Might offer a slight performance improvement
                            set d.amount            = udg_DamageEventAmount
                            set d.attackType        = ConvertAttackType(udg_DamageEventAttackT)
                            set d.damageType        = ConvertDamageType(udg_DamageEventDamageT)
                            set d.weaponType        = ConvertWeaponType(udg_DamageEventWeaponT)
                            set d.userType          = udg_DamageEventType
                            set d.armorPierced      = udg_DamageEventArmorPierced
                            set d.armorType         = udg_DamageEventArmorT
                            set d.defenseType       = udg_DamageEventDefenseT
                        endif
                    elseif cat != MOD_EVENT then
                        set udg_DamageEventAmount   = d.amount
                    elseif nextTrig[i] == 0 or usingGUI[nextTrig[i]] then //Might offer a slight performance improvement
                        call setGUIFromStruct(false)
                    endif
                endif
              endif
            endif
        endloop
        //call BJDebugMsg("End of event running")
        set dreaming                                = false
    endmethod
    private static method onAOEEnd takes nothing returns nothing
        if udg_DamageEventAOE > 1 then
            call runTrigs(AOE_EVENT)
            set udg_DamageEventAOE      = 1
        endif
        set udg_DamageEventLevel        = 1
        set udg_EnhancedDamageTarget    = null
        set udg_AOEDamageSource         = null
        call GroupClear(udg_DamageEventAOEGroup)
    endmethod
    private static method afterDamage takes nothing returns nothing
        if udg_DamageEventPrevAmt != 0.00 and udg_DamageEventDamageT != udg_DAMAGE_TYPE_UNKNOWN then
            call runTrigs(AFTER_EVENT)
            set udg_DamageEventDamageT = udg_DAMAGE_TYPE_UNKNOWN
            set udg_DamageEventPrevAmt = 0.00
        endif
    endmethod
 
    static method finish takes nothing returns nothing
        local integer 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 exit                            = damageStack
                    set sleepLevel                      = sleepLevel + 1
                    loop
                        set prepped                     = stackRef[i]
                        call UnitDamageTarget(prepped.source, prepped.target, prepped.amount, prepped.isAttack, prepped.isRanged, prepped.attackType, prepped.damageType, prepped.weaponType)
                        call afterDamage()
                        set i                           = i + 1
                        exitwhen i == exit
                    endloop
                    exitwhen i == damageStack
                endloop
                loop
                    set i                               = i - 1
                    set trigFrozen[stackRef[i].userTrig]= false
                    set levelsDeep[stackRef[i].userTrig]= 0
                    exitwhen i == 0                  
                endloop                              
                set kicking                             = false
                set damageStack                         = 0
                set prepped                             = 0
                set sleepLevel                          = 0
            endif
            call GroupClear(proclusGlobal)
            call GroupClear(fischerMorrow)
            //call BJDebugMsg("Cleared up the groups")
        endif
    endmethod
    private static method failsafeClear takes nothing returns nothing
        call setArmor(true)
        set canKick                        = true
        set totem                          = false
        if udg_DamageEventDamageT != udg_DAMAGE_TYPE_UNKNOWN then
            call runTrigs(DAMAGE_EVENT)
            set eventsRun                  = true
        endif
        call finish()
    endmethod
    private static method wakeup takes nothing returns nothing
        set alarmSet                       = false
 
        if totem then
            call failsafeClear() //WarCraft 3 didn't run the DAMAGED event despite running the DAMAGING event.
        else
            set canKick                    = true
            call finish()
        endif
        call onAOEEnd()
        set Damage.count                   = 0
        set Damage.index                   = 0
    endmethod
 
    private static method onDamaging takes nothing returns boolean
        local Damage d                     = prepped
        //call BJDebugMsg("First damage event running for " + GetUnitName(GetTriggerUnit()))
        if d > 0 then
            set prepped                    = 0
        else
            set Damage.count               = Damage.count + 1
            set d                          = Damage.count
            set d.source                   = GetEventDamageSource()
            set d.target                   = GetTriggerUnit()
            set d.amount                   = GetEventDamage()
            set d.prevAmt                  = d.amount
         
            set d.attackType               = BlzGetEventAttackType()
            set d.damageType               = BlzGetEventDamageType()
            set d.weaponType               = BlzGetEventWeaponType()
 
            set d.isAttack                 = udg_NextDamageIsAttack or BlzGetEventIsAttack()
            set d.isSpell                  = d.attackType == null and not d.isAttack
            set d.isCode                   = udg_NextDamageType != 0 or udg_NextDamageIsAttack or udg_NextDamageIsRanged or udg_NextDamageIsMelee
            if d.isCode then
                if udg_NextDamageType != 0 then
                    set d.userType         = udg_NextDamageType
                else
                    set d.userType         = CODE
                endif
                set d.isMelee              = udg_NextDamageIsMelee
                set d.isRanged             = udg_NextDamageIsRanged
            else
                set d.userType             = 0
                if d.damageType == DAMAGE_TYPE_NORMAL and d.isAttack then
                    set d.isMelee          = IsUnitType(d.source, UNIT_TYPE_MELEE_ATTACKER)
                    set d.isRanged         = IsUnitType(d.source, 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 // The Huntress and Ballista have a melee sound for their attacks,
                else      // however they do not break the system as they are ranged-only.
                    set d.isMelee          = false
                    set d.isRanged         = false
                endif
            endif
            set udg_NextDamageIsAttack     = false
            set udg_NextDamageIsMelee      = false
            set udg_NextDamageIsRanged     = false
            set udg_NextDamageType         = 0
        endif
        if dreaming and d.amount != 0.00 then
            set d.userTrig                            = eventTrig
            if not d.isCode then
                set d.isCode                          = true
                set d.userType                        = CODE
            endif
            set inception                             = inception or inceptionTrig[eventTrig]
            if kicking and IsUnitInGroup(d.source, proclusGlobal) and IsUnitInGroup(d.target, fischerMorrow) then
                if inception and not trigFrozen[eventTrig] then
                    set inceptionTrig[eventTrig]      = true
                    if levelsDeep[eventTrig] < sleepLevel then
                        set levelsDeep[eventTrig]     = levelsDeep[eventTrig] + 1
                        if levelsDeep[eventTrig] >= LIMBO then
                            set trigFrozen[eventTrig] = true
                        endif
                    endif
                else
                    set trigFrozen[eventTrig]          = true
                endif
            endif
            call BlzSetEventDamage(0.00) //queue the damage instance instead of letting it run recursively
            set prepped                                 = 1
            set stackRef[damageStack]                   = d
            set damageStack                             = damageStack + 1
            //call BJDebugMsg("damageStack: " + I2S(damageStack) + " levelsDeep: " + I2S(levelsDeep[eventTrig]) + " sleepLevel: " + I2S(sleepLevel))
        endif
        set inception                                   = false
        call GroupAddUnit(proclusGlobal, d.source)
        call GroupAddUnit(fischerMorrow, d.target)
        if prepped > 0 then
            set prepped                                 = 0
        else
            if not dreaming and not kicking then
                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 and d.damageType != DAMAGE_TYPE_DEFENSIVE and d.damageType != DAMAGE_TYPE_PLANT then
                            call failsafeClear()
                        else
                            set totem                   = false
                            set lastInstance            = Damage.index
                            set canKick                 = false
                        endif
                    else
                        call finish()
                    endif
           
                    if d.source != udg_AOEDamageSource then
                        call onAOEEnd()
                        set udg_AOEDamageSource         = d.source
                    elseif d.target == udg_EnhancedDamageTarget then
                        set udg_DamageEventLevel        = udg_DamageEventLevel + 1
                    elseif not IsUnitInGroup(d.target, udg_DamageEventAOEGroup) then
                        set udg_DamageEventAOE          = udg_DamageEventAOE + 1
                    endif
                else
                    call TimerStart(alarm, 0.00, false, function Damage.wakeup)
                    set alarmSet                        = true
                    set udg_AOEDamageSource             = d.source
                    set udg_EnhancedDamageTarget        = d.target
                endif
                call GroupAddUnit(udg_DamageEventAOEGroup, d.target)
            endif
 
            set d.armorType                     = BlzGetUnitIntegerField(d.target, UNIT_IF_ARMOR_TYPE)
            set d.defenseType                   = BlzGetUnitIntegerField(d.target, UNIT_IF_DEFENSE_TYPE)
            set d.armorPierced                  = 0.00
 
            set Damage.index                    = d
            call setGUIFromStruct(true)
            set d.prevArmorT                    = udg_DamageEventArmorT
            set d.prevDefenseT                  = udg_DamageEventDefenseT
 
            if udg_DamageEventAmount != 0.00 then
                set udg_DamageEventOverride     = udg_DamageEventDamageT == null
                if not udg_DamageEventOverride then
                    call runTrigs(MOD_EVENT)
                  static if not USE_GUI then
                    call setGUIFromStruct(false)
                  endif
     
                    call BlzSetEventAttackType(d.attackType)
                    call BlzSetEventDamageType(d.damageType)
                    call BlzSetEventWeaponType(d.weaponType)
                    call BlzSetEventDamage(udg_DamageEventAmount)
                    call setArmor(false)
                endif
                set totem                        = true
            else
                call runTrigs(ZERO_EVENT)
                set canKick                      = true
                call finish()
            endif
        endif
        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 dreaming or d.prevAmt == 0.00 then
            return false
        endif
        if 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 d                         = Damage.index
            set canKick                   = true
            call setGUIFromStruct(true)
        endif
        call setArmor(true)
 
        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
            set udg_DamageScalingUser     = udg_DamageEventAmount / udg_DamageEventPrevAmt
        endif
        set udg_DamageEventAmount         = udg_DamageEventAmount*udg_DamageScalingWC3
        set d.amount                      = udg_DamageEventAmount
 
        if udg_DamageEventAmount > 0.00 then
            call runTrigs(SHIELD_EVENT)
          static if not USE_GUI then
            set udg_DamageEventAmount     = d.amount
          endif
            set udg_LethalDamageHP        = GetWidgetLife(udg_DamageEventTarget) - udg_DamageEventAmount
            if udg_LethalDamageHP <= DEATH_VAL then
                call runTrigs(LETHAL_EVENT)
       
                set udg_DamageEventAmount = GetWidgetLife(udg_DamageEventTarget) - udg_LethalDamageHP
                set d.amount              = udg_DamageEventAmount
                if udg_DamageEventType < 0 and udg_LethalDamageHP <= DEATH_VAL then
                    call SetUnitExploded(udg_DamageEventTarget, true)
                endif
            endif
            set udg_DamageScalingUser     = udg_DamageEventAmount/udg_DamageEventPrevAmt/udg_DamageScalingWC3
        endif
        if udg_DamageEventDamageT != udg_DAMAGE_TYPE_UNKNOWN then
            call runTrigs(DAMAGE_EVENT)
          static if not USE_GUI then
            set udg_DamageEventAmount     = d.amount
          endif
        endif
        call BlzSetEventDamage(udg_DamageEventAmount)
        set eventsRun                     = true
        if udg_DamageEventAmount == 0.00 then
            call finish()
        endif
        return false
    endmethod
    //===========================================================================
    private static method onInit takes nothing returns nothing
        local trigger trig = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_DAMAGING)
        call TriggerAddCondition(trig, Filter(function Damage.onDamaging))
        set trig           = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_DAMAGED)
        call TriggerAddCondition(trig, Filter(function Damage.onDamaged))
        set trig           = null
    endmethod
    static method setupEventRobust takes trigger whichTrig, string var, real weight, boolean GUI returns nothing
        local integer index = R2I(weight)
        local integer i     = 0
        if var == "udg_DamageModifierEvent" then
            if index >= 4 then
                set index   = SHIELD_EVENT //4.00 or higher
            else
                set index   = MOD_EVENT    //Less than 4.00
            endif
        elseif var == "udg_DamageEvent" then
            if weight == 2.00 or weight == 0.00 then
                set index   = ZERO_EVENT
            else
                set index   = DAMAGE_EVENT //Above 0.00 but less than 2.00, generally would just be 1.00
            endif
        elseif var == "udg_AfterDamageEvent" then
            set index       = AFTER_EVENT
        elseif var == "udg_LethalDamageEvent" then
            set index       = LETHAL_EVENT
        elseif var == "udg_AOEDamageEvent" then
            set index       = AOE_EVENT
        else
            return
        endif
 
        set userTrigs              = userTrigs + 1   //User list runs from index 10 and up
        set rootTrig[userTrigs]    = whichTrig
        set usingGUI[userTrigs]    = GUI
        set trigWeight[userTrigs]  = weight
 
        loop
            set i                  = nextTrig[index]
            exitwhen i == 0 or weight < trigWeight[i]
            set index              = i
        endloop
        set nextTrig[index]        = userTrigs
        set nextTrig[userTrigs]    = i
 
        //call BJDebugMsg("Registered " + I2S(userTrigs) + " to " + I2S(index) + " and before " + I2S(i))
    endmethod
    //! runtextmacro optional DAMAGE_EVENT_PLUGIN_01()
    //! runtextmacro optional DAMAGE_EVENT_PLUGIN_02()
    //! runtextmacro optional DAMAGE_EVENT_PLUGIN_03()
    //! runtextmacro optional DAMAGE_EVENT_PLUGIN_04()
    //! runtextmacro optional DAMAGE_EVENT_PLUGIN_05()
    //! runtextmacro optional DAMAGE_EVENT_PLUGIN_06()
    //! runtextmacro optional DAMAGE_EVENT_PLUGIN_07()
    //! runtextmacro optional DAMAGE_EVENT_PLUGIN_08()
    //! runtextmacro optional DAMAGE_EVENT_PLUGIN_09()
    //! runtextmacro optional DAMAGE_EVENT_PLUGIN_10()
endstruct
 
    public function Finish takes nothing returns nothing
        call Damage.finish()
    endfunction
 
    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
    //This function exists mainly to make it easier to switch from another DDS, like PDD.
    function UnitDamageTargetEx takes unit src, unit tgt, real amt, boolean a, boolean r, attacktype at, damagetype dt, weapontype wt returns boolean
        if udg_NextDamageType == 0 then
           set udg_NextDamageType = CODE
        endif
        call UnitDamageTarget(src, tgt, amt, a, r, at, dt, wt)
        return dreaming
    endfunction
    //===========================================================================
    //
    // Setup of automatic events from GUI and custom ones from JASS alike
    //
    //===========================================================================
    private function PreSetup takes trigger whichTrig, string var, limitop op, real value returns nothing
        call Damage.setupEventRobust(whichTrig, var, value, true)
    endfunction
    hook TriggerRegisterVariableEvent PreSetup
    function TriggerRegisterDamageEvent takes trigger whichTrig, string eventName, real value returns nothing
        //API:
        //"" for standard damage event
        //"Modifier/After/Lethal/AOE" for the others
        if eventName == "Modifier" then
            set eventName = "udg_DamageModifierEvent"
        else
            set eventName = "udg_" + eventName + "DamageEvent"
        endif
        call Damage.setupEventRobust(whichTrig, eventName, value, false)
    endfunction
    //Similar to TriggerRegisterDamageEvent, although takes code instead of trigger as the first argument.
    function RegisterDamageEvent takes code c, string eventName, real value returns nothing
        local trigger t = CreateTrigger()
        call TriggerAddCondition(t, Filter(c))
        call TriggerRegisterDamageEvent(t, eventName, value)
        set t = null
    endfunction
endlibrary
 


Lua Script

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
 


Example triggers in the test map

  • Set Damage
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • Game - Display to (All players) the text: (String((Mana of DamageEventTarget)))
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of DamageEventSource) Equal to Peasant
        • Then - Actions
          • Set DamageEventAmount = 0.00
          • Set DamageEventArmorT = ARMOR_TYPE_NONE
          • Set DamageEventWeaponT = WEAPON_TYPE_NONE
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • IsDamageSpell Equal to False
              • (Unit-type of DamageEventSource) Equal to Archimonde
              • DamageEventTarget Equal to EnhancedDamageTarget
            • Then - Actions
              • Set DamageEventAmount = (0.00 - DamageEventAmount)
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (DamageEventTarget is A Hero) Equal to True
                  • (DamageEventSource is Summoned) Equal to True
                • Then - Actions
                  • Set DamageEventAmount = (0.00 - DamageEventAmount)
                • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Unit-type of DamageEventSource) Equal to Knight
                      • (Random integer number between 1 and 10) Less than 6
                      • IsDamageSpell Equal to False
                    • Then - Actions
                      • -------- --------
                      • -------- Set DamageEventType to Critical Strike so we can show a critical strike text tag using the trigger "Critical Strike" --------
                      • -------- --------
                      • Set DamageEventType = DamageTypeCriticalStrike
                      • Set DamageEventAmount = (DamageEventAmount x 4.00)
                      • Set DamageEventArmorPierced = 50.00
                    • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (DamageEventTarget is Summoned) Equal to True
                    • Then - Actions
                      • -------- --------
                      • -------- Summoned units take 200% damage --------
                      • -------- They also retaliate with the damage amount when attacked. --------
                      • -------- --------
                      • Set DamageEventTrigger = (This trigger)
                      • Unit - Cause DamageEventTarget to damage DamageEventSource, dealing DamageEventAmount damage of attack type Spells and damage type Magic
                      • Set DamageEventAmount = (DamageEventAmount x 2.00)
                    • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • IsDamageRanged Equal to True
                    • Then - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • UnitMoving[(Custom value of DamageEventTarget)] Equal to True
                        • Then - Actions
                          • -------- --------
                          • -------- Units that are moving take 50% damage from ranged attacks --------
                          • -------- --------
                          • Set DamageEventAmount = (DamageEventAmount x 0.50)
                        • Else - Actions
                          • Set DamageEventDefenseT = DEFENSE_TYPE_UNARMORED
                          • Set DamageEventArmorPierced = (Armor of DamageEventTarget)
                    • Else - Actions

  • Damage Tag
    • Events
      • Game - DamageEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • Game - Display to (All players) the text: (String((Mana of DamageEventTarget)))
      • Set DmgStr = |cffffffff
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • DamageEventAmount Equal to 0.00
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Unit-type of DamageEventSource) Equal to Peasant
            • Then - Actions
              • Set DamageEventTarget = DamageEventSource
              • Set DmgStr = |c00AAAAAAFAIL!|r
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • DamageScalingWC3 Equal to 0.00
                • Then - Actions
                  • Set DmgStr = (|c00AAAAAABlocked + ((String((Integer(DamageEventPrevAmt)))) + !|r))
                • Else - Actions
                  • Set DmgStr = (|c00AAAAAABlocked + ((String((Integer((DamageEventPrevAmt x DamageScalingWC3))))) + !|r))
          • Custom script: call ArcingTextTag.create(udg_DmgStr, udg_DamageEventTarget)
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • DamageEventAmount Less than -0.99
            • Then - Actions
              • Set DmgStr = |cff00ff00+
              • Custom script: call ArcingTextTag.create(udg_DmgStr + I2S(R2I(-udg_DamageEventAmount)) + "|r", udg_DamageEventTarget)
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • DamageEventAmount Greater than or equal to (DamageEventPrevAmt x 1.50)
                • Then - Actions
                  • Set DmgStr = (|cffff0000 + ((String((Integer(DamageEventAmount)))) + !|r))
                  • Custom script: call ArcingTextTag.create(udg_DmgStr, udg_DamageEventTarget)
                • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • DamageEventAmount Greater than 0.99
                    • Then - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • DamageEventAmount Less than (DamageEventPrevAmt x 0.60)
                        • Then - Actions
                          • Set DmgStr = |cff808000
                        • Else - Actions
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • IsDamageSpell Equal to True
                            • Then - Actions
                              • Set DmgStr = |cff3264c8
                            • Else - Actions
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • IsDamageRanged Equal to True
                                • Then - Actions
                                  • Set DmgStr = |cffffff00
                                • Else - Actions
                      • Custom script: call ArcingTextTag.create(udg_DmgStr + I2S(R2I(udg_DamageEventAmount)) + "|r", udg_DamageEventTarget)
                    • Else - Actions
      • Set ReportLife = (Life of DamageEventTarget)


How to install or upgrade to Damage Engine 5:
  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.
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.
In September 2015, cross-compatibility with looking_for_help's Physical Damage Detection was added to the ranks of Weep's compatibility script (which had been added in 2012). Users do not have to change any of their custom triggers to switch from either system to Damage Engine. Simply delete the custom script in their systems and replace it with the following scripts:

How to Use

  • 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.
    • 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.


Cross-Compatibility

For looking_for_help's system, you can delete the entire custom script code in the "DamageEvent" trigger and replace it with the following:
Code (vJASS):
//PDD for Damage Engine 5.1:
 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
 //UnitDamageTargetEx is integrated directly into Damage Engine 5.1, so I removed this function.

 function PDD_OnDamage takes nothing returns boolean
     set udg_PDD_source = udg_DamageEventSource
     set udg_PDD_target = udg_DamageEventTarget
     set udg_PDD_amount = udg_DamageEventAmount
     if udg_IsDamageSpell then
         set udg_PDD_damageType = udg_PDD_SPELL
     elseif udg_IsDamageCode then
         set udg_PDD_damageType = udg_PDD_CODE
     else
         set udg_PDD_damageType = udg_PDD_PHYSICAL
     endif
     set udg_PDD_damageEventTrigger = 0.00
     set udg_PDD_damageEventTrigger = 1.00
     set udg_DamageEventAmount = udg_PDD_amount
     return false
 endfunction
 function InitTrig_DamageEvent takes nothing returns nothing
     set udg_PDD_PHYSICAL = 0
     set udg_PDD_SPELL = 1
     set udg_PDD_CODE = 2
     set udg_PDD_damageEvent = CreateTrigger()
     call TriggerRegisterVariableEvent(udg_PDD_damageEvent, "udg_DamageModifierEvent", EQUAL, 1.00)
     call TriggerAddCondition(udg_PDD_damageEvent, Filter(function PDD_OnDamage))
 endfunction
 

For Weep's system, you can delete the entire custom script code in the "GUI Friendly Damage Detection" trigger, and replace it with the following:
Code (vJASS):

 //GDD Compatibility for Damage Engine 5.1
 //===========================================================================
 function GDD_Event takes nothing returns boolean
     set udg_GDD_DamageSource = udg_DamageEventSource
     set udg_GDD_DamagedUnit = udg_DamageEventTarget
     set udg_GDD_Damage = udg_DamageEventAmount
     set udg_GDD_Event = 1.00
     set udg_GDD_Event = 0.00
     return false     //Code is much shorter due to not having to be concerned with recursion.
 endfunction
 //===========================================================================
 function InitTrig_GUI_Friendly_Damage_Detection takes nothing returns nothing
     local trigger t = CreateTrigger()
     call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.00)
     call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 2.00)
     call TriggerAddCondition(t, Filter(function GDD_Event))
     set t = null
 endfunction
 



Change log v4.0 and up

For 3.8.0.0 and prior, see: Damage Engine 3.8.0.0 | HIVE
4.0.0.0 - Never officially released, but was the first iteration which used SetEventDamage,
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"
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.1.1.0 - Fixed issues related to Spirit Link. Now works intuitively as expected.
5.1.1.1 - Minor tweak to Spirit Link in rare situation.
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.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.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.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.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.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.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.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.4.0.0 - By using an innovative approach of hooking TriggerRegisterVariableEvent, I've permanently eliminated all risks of recursion in the engine.
5.4.0.1 - Hotfixed that modifiers 2 and 3 weren't running.
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.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.2.1 - A fix which should hopefully quell the recent reports of damage events failing in complex situations involving Cold Arrows.
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.3 - Fixed a mis-flag of the IsDamageSpell value when not being actual spell damage.
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.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. Stay tuned for more information on this!​
5.6.0.1 - Fixed an issue where the DamageEventType for recursive damage that used NextDamageType would always default to DamageTypeCode.
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.2.0 - Fixed a bug with Damage modification and laid groundwork for a bunch of optional vJass compatibility addons.

Lua 1.0.0.0 - Release based on Damage Engine 5.4.2.0
Lua 1.0.1.0 - Fixed encapsulation issue and recursion issue with DamageEngine_inception. Now hooks UnitDamageTarget.
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.2.1 - Fixed to match adjustment made in vJass version 5.4.2.1.
Lua 1.0.2.2 - Fixed to match adjustment made in vJass version 5.4.2.2.
Lua 1.0.2.3 - Fixed to match adjustment made in vJass version 5.4.2.3.


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.6.2.0 (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,087
    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,005
    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:
    900
    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,005
    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,207
    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,005
    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,207
    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,207
    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.