1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. The Melee Mapping Contest #4: 2v2 - Results are out! Step by to congratulate the winners!
    Dismiss Notice
  3. We're hosting the 15th Mini-Mapping Contest with YouTuber Abelhawk! The contestants are to create a custom map that uses the hidden content within Warcraft 3 or is inspired by any of the many secrets within the game.
    Dismiss Notice
  4. The 20th iteration of the Terraining Contest is upon us! Join and create exquisite Water Structures for it.
    Dismiss Notice
  5. Check out the Staff job openings thread.
    Dismiss Notice

Damage Engine 5.1.3.1

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

New features in Damage Engine 5:
  • Damage Type, Attack Type and Weapon 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.
  • 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:

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.31).
  • I have left Damage Engine 3.8.0.0 attached here for those whose computers are incompatible with WarCraft 3 version 1.29 and higher.
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 @looking_for_help for finding the spell damage detection method used in Damage Engine 3 - it was the greatest find since the undefend bug. Thanks to Jesus4Lyf and @Nestharus for building the inspiration that originally led me to create DamageEngine. Thank you Wietlol and looking_for_help for challenging me on this project to integrate solutions to problems I had not been made aware of, such as the importance of an After-Damage Event. Thanks to @Spellbound for several crucial bug reports.

Thank you to the users of this system who have helped me mold this project into the stable, powerful form it is in today!

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
      • -------- - --------
      • Custom script: call DamageEngine_DebugStr()

Code (vJASS):

//===========================================================================
// 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's been dealt to the unit): use the event "DamageEvent Equal to 1.00"
// - To change damage before it's 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".
//
//===========================================================================
library DamageEngine initializer Init
globals
    private boolean started = false
    private integer recursion = -1
    private boolean recursive = false
    private boolean purge = false
    private timer ticker = CreateTimer()
    private trigger trig = CreateTrigger()
   
    private real previousAmount = 0.00      //Added to track the original modified damage pre-spirit Link
    private real previousValue = 0.00       //Added to track the original pure damage amount of Spirit Link
    private integer previousType = 0        //Track the type
    private boolean previousCode = false    //Was it caused by a trigger/script?
    private boolean preDamage = false
    private boolean holdClear = false
   
    private unit array lastSource    
    private unit array lastTarget    
    private real array lastAmount    
    private attacktype array lastAttackT    
    private damagetype array lastDamageT    
    private weapontype array lastWeaponT    
    private trigger array lastTrig    
    private integer array lastType    
endglobals
//GUI Vars:
/*
    trigger udg_DamageEventTrigger      //Different functionality from before in 5.1
   
    boolean udg_DamageEventOverride
    boolean udg_NextDamageType
    boolean udg_DamageEventType
    boolean udg_IsDamageCode            //New in 5.1 as per request from chopinski
    boolean udg_IsDamageSpell
    boolean udg_IsDamageMelee           //New in 5.0
    boolean udg_IsDamageRanged          //New in 5.0
   
    unit udg_DamageEventSource
    unit udg_DamageEventTarget
   
    real    udg_AOEDamageEvent
    integer udg_DamageEventAOE
    group   udg_DamageEventAOEGroup
    unit    udg_AOEDamageSource         //New in 5.0
    integer udg_DamageEventLevel
    unit    udg_EnhancedDamageTarget
   
    real udg_DamageEvent
    real udg_DamageModifierEvent
    real udg_LethalDamageEvent          //New in 5.0
   
    real udg_DamageEventAmount
    real udg_DamageEventPrevAmt
    real udg_LethalDamageHP             //New in 5.0
   
    integer udg_DamageEventAttackT      //New in 5.0
    integer udg_DamageEventDamageT      //New in 5.0
    integer udg_DamageEventWeaponT      //New in 5.0
*/

private function Error takes nothing returns nothing
    local string s = "WARNING: Recursion error when dealing damage! Prior to dealing damage from within a DamageEvent response trigger, do this:\n"
    set s = s + "Set DamageEventTrigger = (This Trigger)\n"
    set s = s + "Unit - Cause <Source> to damage <Target>...\n\n"
    set s = s + "Alternatively, just use the UNKNOWN damage type. It will skip recursive damage on its own without needing the \"Set\" line:\n"
    set s = s + "Unit - Cause <Source> to damage <Target>, dealing <Amount> damage of attack type <Attack Type> and damage type Unknown"
   
    call ClearTextMessages()
    call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.00, 0.00, 999.00, s)
endfunction
private function OnAOEEnd takes nothing returns nothing
    if udg_DamageEventAOE > 1 then
        set udg_AOEDamageEvent      = 0.00
        set udg_AOEDamageEvent      = 1.00
        set udg_DamageEventAOE      = 1
    endif
    set udg_DamageEventLevel        = 1
    set udg_EnhancedDamageTarget    = null
    set udg_AOEDamageSource         = null
    call GroupClear(udg_DamageEventAOEGroup)
endfunction
   
private function OnExpire takes nothing returns nothing
    set started = false //The timer has expired. Flag off to allow it to be restarted when needed.
   
    call OnAOEEnd() //Reset things so they don't perpetuate for AoE/Level target detection
endfunction
private function CalibrateMR takes nothing returns nothing
    set udg_IsDamageMelee           = false
    set udg_IsDamageRanged          = false
    set 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.
        set udg_IsDamageMelee       = IsUnitType(udg_DamageEventSource, UNIT_TYPE_MELEE_ATTACKER)
        set udg_IsDamageRanged      = IsUnitType(udg_DamageEventSource, UNIT_TYPE_RANGED_ATTACKER)
        if udg_IsDamageMelee and udg_IsDamageRanged then
            set udg_IsDamageMelee   = udg_DamageEventWeaponT > 0// Melee units play a sound when damaging
            set udg_IsDamageRanged  = not udg_IsDamageMelee     // In the case where a unit is both ranged and melee, the ranged attack plays no sound.
        endif                                                   // The Huntress has a melee sound for her ranged projectile, however it is only an issue
    endif                                                       //if she also had a melee attack, because by default she is only UNIT_TYPE_RANGED_ATTACKER.
endfunction
//Load the event responses into the Pre-Damage Modification trigger.
private function OnPreDamage takes nothing returns boolean
    local unit src      = GetEventDamageSource()
    local unit tgt      = BlzGetEventDamageTarget()
    local real amt      = GetEventDamage()
    local attacktype at = BlzGetEventAttackType()
    local damagetype dt = BlzGetEventDamageType()
    local weapontype wt = BlzGetEventWeaponType()
   
    if udg_NextDamageType == 0 and (udg_DamageEventTrigger != null or recursive) then
        set udg_NextDamageType      = udg_DamageTypeCode
    endif
    if recursive then
        if amt != 0.00 then
            if recursion < 16 then  //when 16 events are run recursively from one damage instance, it's a safe bet that something has gone wrong.
                set recursion = recursion + 1
               
                //Store recursive damage into a queue from index "recursion" (0-15)
                //This damage will be fired after the current damage instance has wrapped up its events.
                //This damage can only be caused by triggers.
                set lastAmount[recursion]   = amt
                set lastSource[recursion]   = src
                set lastTarget[recursion]   = tgt
                set lastAttackT[recursion]  = at
                set lastDamageT[recursion]  = dt
                set lastWeaponT[recursion]  = wt                
                set lastTrig[recursion]     = udg_DamageEventTrigger
                set lastType[recursion]     = udg_NextDamageType
            else
                //Delete or comment-out the next line to disable the in-game recursion crash warning
                call Error()
            endif
        endif
        set udg_NextDamageType          = 0
        set udg_DamageEventTrigger      = null
        call BlzSetEventDamage(0.00) //queue the damage instance instead of letting it run recursively
    else
        if not purge then
            //Added 25 July 2017 to detect AOE damage or multiple single-target damage
            if started then
                if src != udg_AOEDamageSource then //Source has damaged more than once
                   
                    call OnAOEEnd() //New damage source - unflag everything
                    set udg_AOEDamageSource = src
                elseif tgt == udg_EnhancedDamageTarget then
                    set udg_DamageEventLevel= udg_DamageEventLevel + 1  //The number of times the same unit was hit.
                elseif not IsUnitInGroup(tgt, udg_DamageEventAOEGroup) then
                    set udg_DamageEventAOE  = udg_DamageEventAOE + 1    //Multiple targets hit by this source - flag as AOE
                endif
                if preDamage then
                    set preDamage           = false
                    set previousAmount      = udg_DamageEventAmount
                    set previousValue       = udg_DamageEventPrevAmt    //Store the actual pre-armor value.
                    set previousType        = udg_DamageEventType       //also store the damage type.
                    set previousCode        = udg_IsDamageCode          //store this as well.
                    set holdClear           = true
                endif
            else
                call TimerStart(ticker, 0.00, false, function OnExpire)
                set started                 = true
                set udg_AOEDamageSource     = src
                set udg_EnhancedDamageTarget= tgt
            endif
            call GroupAddUnit(udg_DamageEventAOEGroup, tgt)
        endif
       
        set udg_DamageEventType             = udg_NextDamageType
        if udg_NextDamageType != 0 then
            set udg_DamageEventType         = udg_NextDamageType
            set udg_NextDamageType          = 0
            set udg_IsDamageCode            = true //New in 5.1 - requested by chopinski to allow user to detect Code damage
               
            set udg_DamageEventTrigger      = null
        endif  
        set udg_DamageEventOverride         = dt == null or amt == 0.00 or udg_DamageEventType*udg_DamageEventType == 4 //Got rid of NextDamageOverride in 5.1 for simplicity
        set udg_DamageEventPrevAmt          = amt
       
        set udg_DamageEventSource           = src    
        set udg_DamageEventTarget           = tgt        
        set udg_DamageEventAmount           = amt
        set udg_DamageEventAttackT          = GetHandleId(at)
        set udg_DamageEventDamageT          = GetHandleId(dt)
        set udg_DamageEventWeaponT          = GetHandleId(wt)
       
        call CalibrateMR() //Set Melee and Ranged settings.
       
        //Ignores event on various debuffs like Faerie Fire - alternatively,
        //the user can exploit UNKNOWN damage type to avoid damage detection.
        if not udg_DamageEventOverride then
            set recursive = true
           
            set udg_DamageModifierEvent = 0.00
            set udg_DamageModifierEvent = 1.00  //I recommend using this for changing damage types or for when you need to do things that should override subsequent damage modification.
           
            set udg_DamageEventOverride = udg_DamageEventOverride or udg_DamageEventType*udg_DamageEventType == 4
            if not udg_DamageEventOverride then
                set udg_DamageModifierEvent = 2.00  //This should involve damage calculation based on multiplication/percentages.
                set udg_DamageModifierEvent = 3.00  //This should just be addition or subtraction at this point.
            endif
           
            set recursive = false
        endif
       
        //All events have run and the pre-damage amount is finalized.
        call BlzSetEventAttackType(ConvertAttackType(udg_DamageEventAttackT))
        call BlzSetEventDamageType(ConvertDamageType(udg_DamageEventDamageT))
        call BlzSetEventWeaponType(ConvertWeaponType(udg_DamageEventWeaponT))
        call BlzSetEventDamage(udg_DamageEventAmount)
        set preDamage = true
        //call BJDebugMsg("Ready to deal " + R2S(udg_DamageEventAmount))
    endif
    set src = null
    set tgt = null
    return false
endfunction
//The traditional on-damage response, where armor reduction has already been factored in.
private function OnDamage takes nothing returns boolean
    local real r = GetEventDamage()
    local integer i
    if recursive then
        return false
    endif
    //call BJDebugMsg("Second event running")
    if preDamage then
        set preDamage = false   //This should be the case in almost all circumstances
    else
        set holdClear                   = false
       
        //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.
        set udg_DamageEventAmount       = previousAmount
        set udg_DamageEventPrevAmt      = previousValue
        set udg_DamageEventType         = previousType
        set udg_IsDamageCode            = previousCode
        set udg_DamageEventSource       = GetEventDamageSource()
        set udg_DamageEventTarget       = BlzGetEventDamageTarget()
        set udg_DamageEventAttackT      = GetHandleId(BlzGetEventAttackType())
        set udg_DamageEventDamageT      = GetHandleId(BlzGetEventDamageType())
        set udg_DamageEventWeaponT      = GetHandleId(BlzGetEventWeaponType())
       
        call CalibrateMR() //Apply melee/ranged settings once again.
    endif
   
    set recursive = true
    if udg_DamageEventPrevAmt == 0.00 then
        set udg_DamageEvent = 0.00
        set udg_DamageEvent = 2.00
    else    
        if udg_DamageEventAmount != 0.00 then
            set udg_DamageScalingWC3 = r / udg_DamageEventAmount
        else
            set udg_DamageScalingWC3 = 0.00
        endif
       
        //DamageEventAmount remains unmodified by in-game damage processing for DamageTypePure.
        //Damage may have been further adjusted (ie. unit armor or armor type reduction)
        //Do not adjust in case damage was below zero because WC3 will have converted it to zero.
        if udg_DamageScalingWC3 > 0.00 and not udg_DamageEventOverride then
            set udg_DamageEventAmount = r
        endif
       
        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.
            set udg_DamageModifierEvent = 0.00
            set udg_DamageModifierEvent = 4.00
           
            set udg_LethalDamageHP = GetWidgetLife(udg_DamageEventTarget) - udg_DamageEventAmount
            if udg_LethalDamageHP <= 0.405 then
               
                set udg_LethalDamageEvent = 0.00    //New - added 10 May 2019 to detect and potentially prevent lethal damage. Instead of
                set udg_LethalDamageEvent = 1.00    //modifying the damage, you need to modify LethalDamageHP instead (the final HP of the unit).
               
                set udg_DamageEventAmount = GetWidgetLife(udg_DamageEventTarget) - udg_LethalDamageHP
                if udg_DamageEventType < 0 and udg_LethalDamageHP <= 0.405 then
                    call SetUnitExploded(udg_DamageEventTarget, true)   //Explosive damage types should blow up the target.
                endif
            endif
        endif
        call BlzSetEventDamage(udg_DamageEventAmount)   //Apply the final damage amount.
        //if recursion > -1 then
            //call BJDebugMsg("Dealing " + R2S(udg_DamageEventAmount))
        //endif
        if udg_DamageEventDamageT != udg_DAMAGE_TYPE_UNKNOWN then
            set udg_DamageEvent = 0.00
            set udg_DamageEvent = 1.00
        endif
    endif
    set recursive = false
    if recursion > -1 then
        if not holdClear and not purge then
            set purge = true
            set i = -1
            //call BJDebugMsg("Clearing Recursion: " + I2S(recursion))
            loop
                exitwhen i >= recursion
                set i = i + 1 //Need to loop bottom to top to make sure damage order is preserved.
               
                set udg_NextDamageType = lastType[i]
                if lastTrig[i] != null then
                    call DisableTrigger(lastTrig[i])//Since the damage is run sequentially now, rather than recursively, the system needs to disable the user's trigger for them.
                endif
                //call BJDebugMsg("Stacking on " + R2S(lastAmount[i]))
                call UnitDamageTarget(lastSource[i], lastTarget[i], lastAmount[i], true, false, lastAttackT[i], lastDamageT[i], lastWeaponT[i])
            endloop
            loop
                exitwhen i <= -1
                if lastTrig[i] != null then
                    call EnableTrigger(lastTrig[i]) //Only re-enable recursive triggers AFTER all damage is dealt.
                endif
                set i = i - 1
            endloop
            //call BJDebugMsg("Cleared Recursion: " + I2S(recursion))
            set recursion = -1 //Can only be set after all the damage has successfully ended.
            set purge = false
        endif
    //else
        //call BJDebugMsg("Not Clearing Recursion: " + I2S(recursion) + ", HoldClear: " + I2S(IntegerTertiaryOp(holdClear, 1, 0)) + ", Purge: " + I2S(IntegerTertiaryOp(purge, 1, 0)))
    endif
    return false
endfunction
//===========================================================================
private function Init takes nothing returns nothing
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_DAMAGING) //The new 1.31 event which fires before damage.
    call TriggerAddCondition(trig, Filter(function OnPreDamage))
   
    set trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_DAMAGED) //Thanks to this I no longer have to have 1 event for all units in the map.
    call TriggerAddCondition(trig, Filter(function OnDamage))
endfunction
public function DebugStr takes nothing returns nothing
    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"
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_DamageEventTrigger == null then
        set udg_DamageEventTrigger = GetTriggeringTrigger() //Directly access the user's calling trigger
    endif
    if udg_NextDamageType == 0 then
       set udg_NextDamageType = udg_DamageTypeCode
    endif
    call UnitDamageTarget(src, tgt, amt, a, r, at, dt, wt)
    return recursive
endfunction
endlibrary
 


How to install or upgrade to Damage Engine 5:
  1. Use WarCraft 3 Version 1.31
  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 "Damage Engine 5.1.3.1" map to your own map.
Notes about upgrading to 5.1.3.1 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!!! AfterDamageEvent is no longer used. Use DamageEvent Equal to 1.00 instead.
  • !!!DEPRECATED FEATURE!!! Do not Turn off your trigger prior to dealing damage. Instead, Set DamageEventTrigger = (This trigger), then deal the damage. Alternatively, use DAMAGE_TYPE_UNKNOWN* from the Unit - Damage Target function.
    • * This will ignore the "DamageEvent Equal to 1.00" and only trigger "LethalDamageEvent Equal to 1.00" and "DamageModifierEvent Equal to 4.00".
  • !!!DEPRECATED FEATURE!!! Do not use "ClearDamageEvent" - it does nothing. Just delete it.
  • !!!DEPRECATED FEATURE!!! Do not use "NextDamageOverride" - set NextDamageType = DamageTypePure instead.
  • !!!CHANGED FEATURE!!! Certain things will define DamageEventType automatically - the system will apply DamageTypeCode/Reduced/Blocked automatically.
  • !!!CHANGED FEATURE!!! Damage events now run in a queue, rather than recursively, so all damage you try to run recursively will instead be postponed until after the ongoing damage is completed. I don't think anyone ever wanted it running recursively, anyway.
  • !!!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.
How to install 3.8.0.0


Necessary: Copy & paste the Unit Indexer trigger into your map.
Necessary: Copy & paste the Damage Engine trigger into your map
Necessary: Copy the Cheat Death Ability from Object Editor, and set it to the variable DamageBlockingAbility.
Necessary: Copy the Spell Damage Detector Ability from Object Editor, and set it to the variable SpellDamageAbility.
Recommended: Mana Shield abilities need their Damage Absorbed % reduced to 0.00. The test map has this implemented already.
Recommended: Anti-Magic Shell (amount-based) has to be triggered. I included the dummy ability, as well as the trigger, in the test map.
Recommended: Locust Swarm needs its Damage Return Factor changed from 0.75 to -0.75. The test map has this implemented already.
Recommended: Unlike in PDD, do not remove the Spell Damage Reduction ability from your Runed Bracers. Instead, modify the spell damage ability (in Object Editor, go to spells tab, then to special from the left column, then under items change Spell Damage Reduction's Damage Reduction) to 1.67 instead of .33. The test map has this implemented already. If you are switching from PDD and already had that ability removed, just implement the cross-compatibility PDD script I included below.
Recommended: Spell damage is converted into negative damage values without this system, so I recommend not using the event "Unit Takes Damage" and, instead, using Damage Engine to process those events (DamageEvent becomes Equal to 1.00).
Recommended: Life Drain doesn't heal the caster. To fix this, I have created a mostly-perfect trigger which heals the caster to the correct amount at the correct time. You can find this trigger in the test map.
Note: You can configure the script to detect certain conditions that may affect its spell resistance (ie. a different item than runed bracers or different variations of Elune's Grace). You can use a "DamageModifierEvent Equal to 1.00" trigger to adjust the damage amount manually, just by checking if DamageEventTarget has a certain item or ability.
Note: There is no need for Get/SetUnitLife functions, nor UnitDamageTargetEx. The system uses a Unit State event to detect when the unit's life has been adjusted for damage, and then an "AfterDamageEvent Equal to 1.00" event is fired.


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:

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
    //Damage Engine 5.1 now runs recursive events in a queue, so this function is simplified.

    set udg_PDD_source = udg_DamageEventSource
    set udg_PDD_target = udg_DamageEventTarget
    set udg_PDD_amount = udg_DamageEventAmount
    //Extract the damage type from what we already have to work with.
    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
 

Code (vJASS):

//Physical Damage Detection plugin for Damage Engine 5.0 and prior.
//
//A few important notes:
//- Spell reduction is handled multiplicatively in DamageEngine, instead of additively like in PDD.
//- Just like in PDD, make sure you've modified the Runed Bracers item to remove the Spell Damage Reduction ability
//- Move any UnitDamageTargetEx calls to an "AfterDamageEvent Equal to 1.00" trigger
//- You do not need custom Get/Set-Unit(Max)Life functions, but I included them for ease of import.
//
//I will add more disclaimers, tips or fixes if anyone runs into issues!

function GetUnitLife takes unit u returns real
    return GetWidgetLife(u)
endfunction
function SetUnitLife takes unit u, real r returns nothing
    call SetWidgetLife(u, r)
endfunction
function GetUnitMaxLife takes unit u returns real
    return GetUnitState(u, UNIT_STATE_MAX_LIFE)
endfunction
function UnitDamageTargetEx takes unit source, unit target, boolean b, real amount, boolean attack, boolean ranged, attacktype a, damagetype d, weapontype w returns boolean
    local integer ptype = udg_PDD_damageType
    //To keep this functioning EXACTLY the same as it does in PDD, you need to move it
    //to an AfterDamageEvent Equal to 1.00 trigger. It's up to you.
    if ptype != udg_PDD_CODE then
        set udg_PDD_damageType = udg_PDD_CODE
        call UnitDamageTarget(source, target, amount, attack, ranged, a, d, w)
        call TriggerEvaluate(udg_ClearDamageEvent)
        set udg_PDD_damageType = ptype
        return true
    endif
    return false
endfunction
function PDD_OnDamage takes nothing returns boolean
    //cache previously-stored values in the event of recursion.
    local unit source = udg_PDD_source
    local unit target = udg_PDD_target
    local real amount = udg_PDD_amount
    local integer typ = udg_PDD_damageType
    //set variables to their event values.
    set udg_PDD_source = udg_DamageEventSource
    set udg_PDD_target = udg_DamageEventTarget
    set udg_PDD_amount = udg_DamageEventAmount
    //Extract the damage type from what we already have to work with.
    if udg_IsDamageSpell then
        set udg_PDD_damageType = udg_PDD_SPELL
    elseif udg_PDD_damageType != udg_PDD_CODE then
        set udg_PDD_damageType = udg_PDD_PHYSICAL
    endif
    //Fire PDD event.
    set udg_PDD_damageEventTrigger = 0.00
    set udg_PDD_damageEventTrigger = 1.00
    //Hey, this works and I'm really happy about that!
    set udg_DamageEventAmount = udg_PDD_amount
    //reset things just in case of a recursive event.
    set udg_PDD_damageEventTrigger = 0.00
    set udg_PDD_source = source
    set udg_PDD_target = target
    set udg_PDD_amount = amount
    set udg_PDD_damageType = typ
    set source = null
    set target = null
    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 TriggerAddCondition(t, Filter(function GDD_Event))
    set t = null
endfunction
 

Code (vJASS):
//GDD Compatibility for Damage Engine 5.0 and prior
//globals
//    real udg_GDD_Event = 0
//    real udg_GDD_Damage = 0
//    unit udg_GDD_DamagedUnit = null
//    unit udg_GDD_DamageSource = null
//endglobals
//===========================================================================
function GDD_Event takes nothing returns boolean
    local unit s = udg_GDD_DamageSource
    local unit t = udg_GDD_DamagedUnit
    local real v = udg_GDD_Damage
    set udg_GDD_DamageSource = udg_DamageEventSource
    set udg_GDD_DamagedUnit = udg_DamageEventTarget
    set udg_GDD_Damage = udg_DamageEventAmount
    set udg_GDD_Event = 1
    set udg_GDD_Event = 0
    set udg_GDD_DamageSource = s
    set udg_GDD_DamagedUnit = t
    set udg_GDD_Damage = v
    set s = null
    set t = null
    return false
endfunction
//===========================================================================
function InitTrig_GUI_Friendly_Damage_Detection takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1)
    call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 2)
    call TriggerAddCondition(t, Filter(function GDD_Event))
    set t = null
endfunction

How to Use


How to Use Damage Engine 5.1

  • Detect damage: use the event "DamageEvent Equal to 1.00". You have access to the following variables for reference:
    • DamageEventSource - the unit dealing the damage
    • DamageEventTarget - the unit receiving the damage
    • DamageEventAmount - the amount of damage the unit will receive
    • DamageEventPrevAmt - the amount of damage prior to being modified by the game engine.
    • DamageEventAttackT - the attack type (Chaos, Spells, Pierce, etc.)
    • DamageEventDamageT - the damage type - for comprehensive info, click here.
    • DamageEventWeaponT - the weapon type determines if an attack plays some kind of sound on attack (ie. Metal Heavy Bash). It is always NONE for spells and almost always NONE for ranged attacks.
    • 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
    • DamageEventAttack/Damage/WeaponT
    • 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.

Change log
1.0.0.0 - Release of Damage Event
2.0.0.0 - Release of Damage Engine which combines Damage Modifier
2.1.0.0 - Circumvented a very lame wc3 bug that's making the timer expire twice. It's still expiring twice but there's nothing I can do to figure out why.
2.2.0.0 - Fixed the double expire bug completely and fixed a bug that could give weird life/max life readings.
2.2.1.0 - Fixed a really nasty bug with the engine corrupting Unit Indexer after recreating the Damage Event trigger.
3.0.0.0 - Fixed everything and added Spell Damage Detection!
3.0.1.0 - Fixed triggered damage bug when it was set very high, and fixed recursion so it stops at 16 recursive events instead of at a potentially-buggy time.
3.0.2.0 - Fixed bug that ethereal units didn't have spell damage amplification and that Elune's Grace wasn't reducing spell damage.
3.0.3.0 - Added a get unit life/max life function and fixed a bug where heroes were getting their damage amped too much from banish.
3.1.0.0 - no longer uses a timer. Also, got rid of the override/explodes booleans. Use DamageEventType instead.
3.1.1.0 - uses a timer in addition to the event in case the event didn't go off.
3.1.2.0 - has improved unit state event handling for better results.
3.1.3.0 - fixed a potential recursion issue introduced when I switched to unit state events.
3.1.3.1 - improved the accuracy of spell damage affecting a unit state change.
3.2.0.0 - added a bunch of optional textmacros for an upcoming optional vJass plugin.
3.3.0.0 - got rid of the textmacros. Now requires you to use the variable "NextDamageType" before dealing triggered damage. Code now uses as little custom script as possible.
3.4.0.0 - integrated Mana Shield processing. Requires the user to set Damage Absorbed % to 0.00 for both Mana Shield abilities and for all 3 level
3.5.0.0 - decoupled Mana Shield from DamageEngine and split DamageModification into 4 parts. Added a custom Anti-Magic Shell to the test map which can be used as a template for any kind of shield. Fixed a recursion issue. Re-applied backwards-compatibility with scripts that used DamageEventOverride.
3.5.1.0 - Fixed a bug introduced with a previous damage processing update. Thanks to kruzerg and Spellbound for finding this bug with the system!
3.5.2.0 - Fixed a bug with tornado slow aura and made a couple minor improvements.
3.5.3.0 - In the previous update I disabled the zero-damage event from working normally. This update reverts that, as I found there was a bug with recursion with that event which I have now fixed. I also made several other minor improvements.
3.5.3.1 - Fixed an extremely-rare issue with recursive damage modifier events not guaranteed to fire if they were greater than 1.
3.5.4.0 - Fixed a bug with recursive events not firing 100% of the time. Added a boolean called NextDamageOverride that will let you skip the main 3 damage modifier events and built-in spell damage modifier, allowing you to deal "true" damage (which can still be reduced by shields). Added a feature to ClearDamageEvent to set NextDamageOverride to false and NextDamageType to 0 in case the damage engine didn't run due to spell immunity or invulnerability.
3.5.4.1 - Fixed a bug introduced with the ClearDamageEvent change preventing both NextDamageX from working together. So as to not introduce potentially-new bugs I will only do bug fixes in the near future.
3.6.0.0 - Converted everything to JASS, removed numerous extra variables, added a HideDamageFrom boolean array so users can opt to not run DamageEvents and AfterDamageEvents from those units. DamageModifierEvents will still run.
3.6.0.1 - Fixed a potential handle ID issue if the user was using the compatibility setting with looking_for_help's PDD. Changed the compatibility script for PDD to compensate for it being integrated into Damage Engine. Updated the demo map behavior for the Knight so he now deals 4x instead of takes it.
3.6.1.0 - Fixed a major recursion misflag caused by storing RecursionN to a local before potentially modifying RecursionN in the CheckForDamage function. Thanks to ssbbssc2 and Jampion for pointing this out!
3.7.0.0 - Added some new variables to detect when a unit has been damaged by the same source multiple times in one instance (udg_DamageEventLevel tracks how many times this has happened in this moment). The other variables detect AOE damage and provide an event after all the AOE damage has been fully applied.
3.8.0.0 - Fixed issues plaguing users of patch 1.29 by updating Damage Engine and Unit Indexer to compensate for the player slot state increase.
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?)

Keywords:unit indexer, damage detection, damage event, weep, nestharus, looking_for_help, EVENT_PLAYER_UNIT_DAMAGED, damage engine, spell, physical
Contents

Damage Engine Testmap (Map)

Damage Engine 5.1.3.1 (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,086
    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,008
    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:
    895
    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,008
    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:
    7,825
    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,008
    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:
    7,825
    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:
    468
    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:
    468
    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. sentrywiz

    sentrywiz

    Joined:
    Feb 10, 2009
    Messages:
    1,875
    Resources:
    16
    Maps:
    10
    Tutorials:
    6
    Resources:
    16
    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:
    7,825
    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.