1. 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
  2. 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 havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. We have recently started the 16th edition of the Mini Mapping Contest. The theme is mini RPG. Do check it out and have fun.
    Dismiss Notice
  4. Choose your ride to damnation in the 5th Special Effect Contest Poll.
    Dismiss Notice
  5. The winners of the 13th Techtree Contest have been announced!
    Dismiss Notice
  6. The 13th Music Contest Poll is up! Vote for the best tracks in this symphony of frost and flame.
    Dismiss Notice
  7. Race against the odds and Reforge, Don't Refund. The 14th Techtree Contest has begun!
    Dismiss Notice
  8. 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 3.8.0.0

  • Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,125
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Click here to go back to the current version of Damage Engine

    Special Thanks:

    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.


    How Damage Engine works

    
    1

    A unit enters the game and is processed by GUI Unit Indexer or GUI Unit Event.
    
    → →
    
    
    2

    Add an event for the unit to the monolithic DamageEventTrigger and add the Detect Spell Damage Ability to it.
    
    → →
    
    
    3

    WarCraft 3 detects incoming damage, distributes it into Spirit Link, then applies armor & shields
    
    → ↓
    
    
    ↓ ←
    
    
    6

    If any recursive damage is detected from any of those events, it is executed immediately. Any recursive events from those recursive events are also executed immediately.
    
    ← ←
    
    5

    If the damage is negative, flag IsDamageSpell as True and invert the damage back to a positive. Damage Engine deploys the DamageModifierEvents 1.00 through 3.00
    
    ← ←
    
    4

    The EVENT_UNIT_DAMAGED event runs for each Spirit Link instance or defensive retaliation damage instance, then for the final and original instance.
    
    7

    If DamageEventAmount is greater than 0.00, run the DamageModifierEvent 4.00. Same old recursive events apply as with 1.00-3.00.
    
    → →
    
    
    8

    Now that DamageEventAmount is finalized, run DamageEvent 1.00. Same old recursive events apply as with DamageModifier events.
    
    → →
    
    
    9

    The user can access DamageEventPrevAmount if they want to know the damage amount before user modifications (before in-game modifications was impossible to know).
    
    → ↓
    
    
    ↓ ←
    
    
    12

    Set a flag to indicate the removal of the Cheat Death Ability and start a timer as a failsafe.
    
    ← ←
    
    11

    If the target’s maximum HP is too low to handle the incoming damage, give it the Cheat Death Ability +500,000
    
    ← ←
    
    10

    Compensate for (DamageEventAmount - Life of DamageEventTarget) by increasing or decreasing the target unit’s HP.
    
    13

    The timer expires/a new damage event is run/the UNIT_STATE_EVENT detects the HP change/the user calls ClearDamageEvent (whichever happens first)
    
    → →
    
    
    14

    If the unit was wielding the Cheat Death Ability, remove it.
    
    → →
    
    
    15

    If the damage was above 0.00, run AfterDamageEvent 1.00. Same old recursive events apply as with all previous events.
    
    → ↓
    
    
    √
    
    
    18

    Once enough units have left, rebuilt the massive DamageEventTrigger and re-add events only for active units.
    
    ← ←
    
    17

    When a unit is removed from the game, increment a tracking integer to know how many units have been removed.
    
    ← ←
    
    16

    Once the timer expires (if it hadn’t already) or if a new DamageEventSource was applied, run AOEDamageEvent 1.00.



    How to use:



    How to install:




    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.

    • Damage Engine Config
      • Events
      • Conditions
      • Actions
        • -------- - --------
        • -------- This trigger's conditions let you filter out units you don't want detection for. --------
        • -------- NOTE: By default, units with Locust will not pass the check. --------
        • -------- TIP: The unit is called UDexUnits[UDex] and its custom value is UDex --------
        • -------- - --------
        • -------- Copy the Cheat Death Ability from Object Editor into your map and set the following variable respectively: --------
        • -------- - --------
        • Set DamageBlockingAbility = Cheat Death Ability (+500,000)
        • -------- - --------
        • -------- Copy the Detect Spell Damage Ability from Object Editor into your map and set the following variable respectively: --------
        • -------- - --------
        • Set SpellDamageAbility = Detect Spell Damage
        • -------- - --------
        • -------- 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 --------
        • -------- - --------
        • Set DamageTypeExplosive = -1
        • Set DamageTypeCriticalStrike = 1
        • Set DamageTypeHeal = 2
        • Set DamageTypeReduced = 3
        • Set DamageTypeBlocked = 4
        • -------- - --------
        • -------- Leave the next Set statement disabled if you modified the Spell Damage Reduction item ability to 1.67 reduction --------
        • -------- Otherwise, if you removed that ability from Runed Bracers, you'll need to enable this line: --------
        • -------- - --------
        • Set DmgEvBracers = Runed Bracers
        • -------- - --------
        • -------- Set the damage multiplication factor (1.00 being unmodified, increasing in damage over 1.00 and at 0 damage with 0.00) --------
        • -------- NOTE. With the default values, Runed Bracers is reduces 33%, Elune's Grace reduces 20% and Ethereal increases 67% --------
        • -------- - --------
        • Set DAMAGE_FACTOR_BRACERS = 0.67
        • Set DAMAGE_FACTOR_ELUNES = 0.80
        • Set DAMAGE_FACTOR_ETHEREAL = 1.67
        • -------- - --------
        • -------- Added 25 July 2017 to allow detection of things like Bash or Pulverize or AOE spread --------
        • -------- - --------
        • Set DamageEventAOE = 1
        • Set DamageEventLevel = 1


    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: use the event "DamageEvent Equal to 1.00"
    // - To change damage before it's dealt: use the event "DamageModifierEvent Equal to 1.00"
    // - Detect damage after it was applied, use the event "AfterDamageEvent 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" (an AfterDamageEvent will not fire for this)
    //
    // You can specify the DamageEventType before dealing triggered damage. To prevent an already-improbable error, I recommend running the trigger "ClearDamageEvent (Checking Conditions)" after dealing triggered damage from within a damage event:
    // - Set NextDamageType = DamageTypeWhatever
    // - Unit - Cause...
    // - Trigger - Run ClearDamageEvent (Checking Conditions)
    //
    // 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".
    //
    //===========================================================================
    // Programming note about "integer i" and "udg_DmgEvRecursionN": integer i
    // ranges from -1 upwards. "udg_DmgEvRecursionN" ranges from 0 upwards.
    // "integer i" is always 1 less than "udg_DmgEvRecursionN"
    //
    function DmgEvResetVars takes nothing returns nothing
        local integer i = udg_DmgEvRecursionN - 2
        set udg_DmgEvRecursionN = i + 1
        if i >= 0 then
            set udg_DamageEventPrevAmt  = udg_LastDmgPrevAmount[i]
            set udg_DamageEventAmount   = udg_LastDmgValue[i]
            set udg_DamageEventSource   = udg_LastDmgSource[i]
            set udg_DamageEventTarget   = udg_LastDmgTarget[i]
            set udg_IsDamageSpell       = udg_LastDmgWasSpell[i]
            set udg_DamageEventType     = udg_LastDmgPrevType[i]
        endif
    endfunction

    function CheckDamagedLifeEvent takes boolean clear returns nothing
        if clear then
            set udg_NextDamageOverride = false
            set udg_NextDamageType = 0
        endif
        if udg_DmgEvTrig != null then
            call DestroyTrigger(udg_DmgEvTrig)
            set udg_DmgEvTrig = null
            if udg_IsDamageSpell then
                call SetWidgetLife(udg_DamageEventTarget, RMaxBJ(udg_LastDamageHP, 0.41))
                if udg_LastDamageHP <= 0.405 then
                    if udg_DamageEventType < 0 then
                        call SetUnitExploded(udg_DamageEventTarget, true)
                    endif
                    //Kill the unit
                    call DisableTrigger(udg_DamageEventTrigger)
                    call UnitDamageTarget(udg_DamageEventSource, udg_DamageEventTarget, -999, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
                    call EnableTrigger(udg_DamageEventTrigger)
                endif
            elseif GetUnitAbilityLevel(udg_DamageEventTarget, udg_DamageBlockingAbility) > 0 then
                call UnitRemoveAbility(udg_DamageEventTarget, udg_DamageBlockingAbility)
                call SetWidgetLife(udg_DamageEventTarget, udg_LastDamageHP)
            endif
            if udg_DamageEventAmount != 0.00 and not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
                set udg_AfterDamageEvent = 0.00
                set udg_AfterDamageEvent = 1.00
                set udg_AfterDamageEvent = 0.00
            endif
            call DmgEvResetVars()
        endif
    endfunction
     
    function DmgEvOnAOEEnd takes nothing returns nothing
        if udg_DamageEventAOE > 1 then
            set udg_AOEDamageEvent = 0.00
            set udg_AOEDamageEvent = 1.00
            set udg_AOEDamageEvent = 0.00
            set udg_DamageEventAOE = 1
        endif
        set udg_DamageEventLevel = 1
        set udg_EnhancedDamageTarget = null
        call GroupClear(udg_DamageEventAOEGroup)
    endfunction
     
    function DmgEvOnExpire takes nothing returns nothing
        set udg_DmgEvStarted = false
        call CheckDamagedLifeEvent(true)
        //Reset things so they don't perpetuate for AoE/Level target detection
        call DmgEvOnAOEEnd()
        set udg_DamageEventTarget = null
        set udg_DamageEventSource = null
    endfunction

    function PreCheckDamagedLifeEvent takes nothing returns boolean
        call CheckDamagedLifeEvent(true)
        return false
    endfunction

    function OnUnitDamage takes nothing returns boolean
        local boolean override = udg_DamageEventOverride
        local integer i
        local integer e = udg_DamageEventLevel
        local integer a = udg_DamageEventAOE
        local string s
        local real prevAmount
        local real life
        local real prevLife
        local unit u
        local unit f
        call CheckDamagedLifeEvent(false) //in case the unit state event failed and the 0.00 second timer hasn't yet expired
        set i = udg_DmgEvRecursionN - 1 //Had to be moved here due to false recursion tracking
        if i < 0 then
            //Added 25 July 2017 to detect AOE damage or multiple single-target damage
            set u                       = udg_DamageEventTarget
            set f                       = udg_DamageEventSource
        elseif i < 16 then
            set udg_LastDmgPrevAmount[i]= udg_DamageEventPrevAmt
            set udg_LastDmgValue[i]     = udg_DamageEventAmount
            set udg_LastDmgSource[i]    = udg_DamageEventSource
            set udg_LastDmgTarget[i]    = udg_DamageEventTarget
            set udg_LastDmgWasSpell[i]  = udg_IsDamageSpell
            set udg_LastDmgPrevType[i]  = udg_DamageEventType
        else
            set s = "WARNING: Recursion error when dealing damage! Make sure when you deal damage from within a DamageEvent trigger, do it like this:\n\n"
            set s = s + "Trigger - Turn off (This Trigger)\n"
            set s = s + "Unit - Cause...\n"
            set s = s + "Trigger - Turn on (This Trigger)"
     
            //Delete the next couple of lines to disable the in-game recursion crash warnings
            call ClearTextMessages()
            call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.00, 0.00, 999.00, s)
            return false
        endif
        set udg_DmgEvRecursionN     = i + 2
        set prevAmount              = GetEventDamage()
        set udg_DamageEventTarget   = GetTriggerUnit()
        set udg_DamageEventSource   = GetEventDamageSource()
     
        set udg_DamageEventAmount   = prevAmount
     
        set udg_DamageEventType     = udg_NextDamageType
        set udg_NextDamageType      = 0
        set udg_DamageEventOverride = udg_NextDamageOverride
        set udg_NextDamageOverride  = false
     
        if i < 0 then
            //Added 25 July 2017 to detect AOE damage or multiple single-target damage
            if udg_DamageEventType == 0 then
                if f == udg_DamageEventSource then
                    //Source has damaged more than once
                    if IsUnitInGroup(udg_DamageEventTarget, udg_DamageEventAOEGroup) then
                        //Added 5 August 2017 to improve tracking of enhanced damage against, say, Pulverize
                        set udg_DamageEventLevel = udg_DamageEventLevel + 1
                        set udg_EnhancedDamageTarget = udg_DamageEventTarget
                    else
                        //Multiple targets hit by this source - flag as AOE
                        set udg_DamageEventAOE = udg_DamageEventAOE + 1
                    endif
                else
                    //New damage source - unflag everything
                    set u = udg_DamageEventSource
                    set udg_DamageEventSource = f
                    call DmgEvOnAOEEnd()
                    set udg_DamageEventSource = u
                endif
                call GroupAddUnit(udg_DamageEventAOEGroup, udg_DamageEventTarget)
            endif
            if not udg_DmgEvStarted then
                set udg_DmgEvStarted = true
                call TimerStart(udg_DmgEvTimer, 0.00, false, function DmgEvOnExpire)
            endif
        endif
     
        if prevAmount == 0.00 then
            if not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
                set udg_DamageEventPrevAmt = 0.00
                set udg_DamageEvent = 0.00
                set udg_DamageEvent = 2.00
                set udg_DamageEvent = 0.00
            endif
            call DmgEvResetVars()
        else
            set u = udg_DamageEventTarget
            set udg_IsDamageSpell = prevAmount < 0.00
            if udg_IsDamageSpell then
                set prevAmount = -udg_DamageEventAmount
                set life = 1.00
                if IsUnitType(u, UNIT_TYPE_ETHEREAL) and not IsUnitType(u, UNIT_TYPE_HERO) then
                    set life = life*udg_DAMAGE_FACTOR_ETHEREAL //1.67
                endif
                if GetUnitAbilityLevel(u, 'Aegr') > 0 then
                    set life = life*udg_DAMAGE_FACTOR_ELUNES //0.80
                endif
                if udg_DmgEvBracers != 0 and IsUnitType(u, UNIT_TYPE_HERO) then
                    //Inline of UnitHasItemOfTypeBJ without the potential handle ID leak.
                    set i = 6
                    loop
                        set i = i - 1
                        if GetItemTypeId(UnitItemInSlot(u, i)) == udg_DmgEvBracers then
                            set life = life*udg_DAMAGE_FACTOR_BRACERS //0.67
                            exitwhen true
                        endif
                        exitwhen i == 0
                    endloop
                endif
                set udg_DamageEventAmount = prevAmount*life
            endif
            set udg_DamageEventPrevAmt = prevAmount
            set udg_DamageModifierEvent = 0.00
            if not udg_DamageEventOverride then
                set udg_DamageModifierEvent = 1.00
                if not udg_DamageEventOverride then
                    set udg_DamageModifierEvent = 2.00
                    set udg_DamageModifierEvent = 3.00
                endif
            endif
            set udg_DamageEventOverride = override
            if udg_DamageEventAmount > 0.00 then
                set udg_DamageModifierEvent = 4.00
            endif
            set udg_DamageModifierEvent = 0.00
            if not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
                set udg_DamageEvent = 0.00
                set udg_DamageEvent = 1.00
                set udg_DamageEvent = 0.00
            endif
            call CheckDamagedLifeEvent(true) //in case the unit state event failed from a recursive damage event
     
            //All events have run and the damage amount is finalized.
            set life = GetWidgetLife(u)
            set udg_DmgEvTrig = CreateTrigger()
            call TriggerAddCondition(udg_DmgEvTrig, Filter(function PreCheckDamagedLifeEvent))
            if not udg_IsDamageSpell then
                if udg_DamageEventAmount != prevAmount then
                    set life = life + prevAmount - udg_DamageEventAmount
                    if GetUnitState(u, UNIT_STATE_MAX_LIFE) < life then
                        set udg_LastDamageHP = life - prevAmount
                        call UnitAddAbility(u, udg_DamageBlockingAbility)
                    endif
                    call SetWidgetLife(u, RMaxBJ(life, 0.42))
                endif
                call TriggerRegisterUnitStateEvent(udg_DmgEvTrig, u, UNIT_STATE_LIFE, LESS_THAN, RMaxBJ(0.41, life - prevAmount/2.00))
            else
                set udg_LastDamageHP = GetUnitState(u, UNIT_STATE_MAX_LIFE)
                set prevLife = life
                if life + prevAmount*0.75 > udg_LastDamageHP then
                    set life = RMaxBJ(udg_LastDamageHP - prevAmount/2.00, 1.00)
                    call SetWidgetLife(u, life)
                    set life = (life + udg_LastDamageHP)/2.00
                else
                    set life = life + prevAmount*0.50
                endif
                set udg_LastDamageHP = prevLife - (prevAmount - (prevAmount - udg_DamageEventAmount))
                call TriggerRegisterUnitStateEvent(udg_DmgEvTrig, u, UNIT_STATE_LIFE, GREATER_THAN, life)
            endif
        endif
        set u = null
        set f = null
        return false
    endfunction

    function CreateDmgEvTrg takes nothing returns nothing
        set udg_DamageEventTrigger = CreateTrigger()
        call TriggerAddCondition(udg_DamageEventTrigger, Filter(function OnUnitDamage))
    endfunction

    function SetupDmgEv takes nothing returns boolean
        local integer i = udg_UDex
        local unit u
        if udg_UnitIndexEvent == 1.00 then
            set u = udg_UDexUnits[i]
            if GetUnitAbilityLevel(u, 'Aloc') == 0 and TriggerEvaluate(gg_trg_Damage_Engine_Config) then
                set udg_UnitDamageRegistered[i] = true
                call TriggerRegisterUnitEvent(udg_DamageEventTrigger, u, EVENT_UNIT_DAMAGED)
                call UnitAddAbility(u, udg_SpellDamageAbility)
                call UnitMakeAbilityPermanent(u, true, udg_SpellDamageAbility)
            endif
            set u = null
        else
            set udg_HideDamageFrom[i] = false
            if udg_UnitDamageRegistered[i] then
                set udg_UnitDamageRegistered[i] = false
                set udg_DamageEventsWasted = udg_DamageEventsWasted + 1
                if udg_DamageEventsWasted == 32 then //After 32 registered units have been removed...
                    set udg_DamageEventsWasted = 0
           
                    //Rebuild the mass EVENT_UNIT_DAMAGED trigger:
                    call DestroyTrigger(udg_DamageEventTrigger)
                    call CreateDmgEvTrg()
                    set i = udg_UDexNext[0]
                    loop
                        exitwhen i == 0
                        if udg_UnitDamageRegistered[i] then
                            call TriggerRegisterUnitEvent(udg_DamageEventTrigger, udg_UDexUnits[i], EVENT_UNIT_DAMAGED)
                        endif
                        set i = udg_UDexNext[i]
                    endloop
                endif
            endif
        endif
        return false
    endfunction
     
    //===========================================================================
    function InitTrig_Damage_Engine takes nothing returns nothing
        local unit u = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA), 'uloc', 0, 0, 0)
        local integer i = bj_MAX_PLAYERS //Fixed in 3.8
     
        //Create this trigger with UnitIndexEvents in order add and remove units
        //as they are created or removed.
        local trigger t = CreateTrigger()
        call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 1.00)
        call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 2.00)
        call TriggerAddCondition(t, Filter(function SetupDmgEv))
        set t = null
     
        //Run the configuration trigger to set all configurables:
        if gg_trg_Damage_Engine_Config == null then
            //It's possible this InitTrig_ function ran first, in which case use ExecuteFunc.
            call ExecuteFunc("Trig_Damage_Engine_Config_Actions")
        else
            call TriggerExecute(gg_trg_Damage_Engine_Config)
        endif
     
        //Create trigger for storing all EVENT_UNIT_DAMAGED events.
        call CreateDmgEvTrg()
     
        //Create GUI-friendly trigger for cleaning up after UnitDamageTarget.
        set udg_ClearDamageEvent = CreateTrigger()
        call TriggerAddCondition(udg_ClearDamageEvent, Filter(function PreCheckDamagedLifeEvent))
     
        //Disable SpellDamageAbility for every player.
        loop
            set i = i - 1
            call SetPlayerAbilityAvailable(Player(i), udg_SpellDamageAbility, false)
            exitwhen i == 0
        endloop
     
        //Preload abilities.
        call UnitAddAbility(u, udg_DamageBlockingAbility)
        call UnitAddAbility(u, udg_SpellDamageAbility)
        call RemoveUnit(u)
        set u = null
    endfunction
     


    Cross-Compatibility with PDD and GDD


    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


    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



    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.
     

    Attached Files: