• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Physical Damage Detection for GUI v1.3.0.0

1. Description

This is a Damage Detection System (DDS) that can differentiate between Physical, Spell and Code damage. It also allows you to modify the damage applied on the target, e.g. increase, decrease, invert or block the damage completly. Finally you can also allocate attacks directly from a running damage event so that its very easy to reflect all spell damage for example.

The system is written in pure JASS and realizes damage handler functions with a variable event. This means that GUI users can easily use this and code their whole damage handler functions in GUI. There is no need for custom scripts with the only exception of allocating attacks from a running damage event (Compare the OnDamage trigger).

There is also a vJASS Version of this system available.


2. System Code and how to use

Enclosed is the system code and an example OnDamage trigger. Just use the variable event "damageEventTrigger becomes equal to 1.0" to add a new damage handler. The OnDamage trigger shown here is from the testmap and shows how easy you can detect the damage type and modify the incoming damage. In this testmap, the following damage modifiers were used:

  • The Archmage deals 0 physical damage.
  • The Mountain King deals double spell damage.
  • The Blademaster heals 50 hitpoints with every hit.
  • The Kodo Beast will reflect 50 damage.

JASS:
////////////////////////////////////////////////////////////////////////////////////
//
//  Physical Damage Detection Engine GUI v 1.3.0.0
//  ----------------------------------------------
//  By looking_for_help aka eey
//
//  This system is able to detect, modify and deal damage. It can differentiate
//  between physical and spell damage, as well as block, reduce or increase the
//  applied damage to a desired value. You can also allocate damage events from
//  running damage events.
//
//  This is the GUI version of the system, meaning that you can use this with the
//  standard editor and basically without any knowledge of JASS.
//
////////////////////////////////////////////////////////////////////////////////////
//
//  Implementation
//  --------------
//  1.  Create all variables that this system uses (compare the variable editor).
//      You don't have to set them to specific values, they get initialized
//      automatically by the system so just create them.
//  2.  Copy this trigger to your map. You can then add damage handlers by using
//      the event "value of real variable becomes equal to 1" with the variable
//      udg_PDD_damageEventTrigger. Compare the OnDamage trigger for an example usage.
//  3.  Copy the two custom abilities to your map and make sure they have the
//      correct ID. You can specify the ID in the function InitGlobalVariables
//      above.
//  4.  Go to the locust swarm ability and invert its damage return portion
//      from (default) 0.75 to -0.75.
//  5.  Remove the spell damage reduction ability from the spell damage reduction
//      items you use (runed bracers). You can configure the resistance of this
//      item in the InitGlobalVariables function, modifying the global variable
//      udg_PDD_BRACERS_SPELL_DMG_RED.
//
////////////////////////////////////////////////////////////////////////////////////
//
//  Important Notes
//  ---------------
//  1.  Life Drain does not work with this system, so you should use a triggered
//      version of this spell if you want to use it.
//  2.  Same for Finger of Death, if you want to use this spell bug free with this
//      system, you should use a triggered version of it.
//  3.  If you use damage modifiers by setting the damage amount variable, you have
//      to use GetUnitLife, GetUnitMaxLife and SetUnitLife instead of GetWidgetLife,
//      GetUnitState for UNIT_STATE_MAX_LIFE and SetWidgetLife in your whole map to
//      ensure there occure no bugs.
//  4.  The boolean udg_PDD_SPELL_RESIST_AUTO_DETECT is only neccessary set to true
//      if you want to use a customized damage table with spell damage resistance
//      above 100%. If this is not the case, it should be set to false, as the
//      system is faster then and still works correct.
//  5.  As already mentioned you can't use the spell reduction ability when using this
//      system (runed bracers and elunes grace). If you want to use them, you can
//      trigger them by using the damage modifiers. Runed bracers is already considered
//      in this system, so you don't have to code it.
//
////////////////////////////////////////////////////////////////////////////////////
//
//  System API
//  ----------
//  real damageEventTrigger
//      - Use the event "value of real variable becomes equal to 1" to detect a damage
//        event. In this event you can use the following API. Compare the OnDamage
//        trigger for an example usage.
//
//  unit target
//      - In this global unit variable, the damaged unit is saved. Don't write any-
//        thing into this variable, use it as readonly.
//
//  unit source
//      - In this global unit variable, the damage source is saved. Don't write any-
//        thing into this variable, use it as readonly.
//
//  real amount
//      - In this global real variable, the amount of damage is saved. This amount
//        can be modified by simply setting it to the desired amount that should be
//        applied to the target. Set the amount to 0.0 to block the damage completly.
//        Negative values will result in heal.
//
//  integer damageType
//      - In this global integer variable, the damage type of the current damage is
//        saved. Use it to differentiate between physical, spell and code damage. The
//        variable can takes the values PHYSICAL == 0, SPELL == 1 and CODE == 2. Don't
//        write anything into this variable, use it as readonly.      
//
//  function GetUnitLife takes unit u returns real
//      - Use this function instead of the GetWidgetLife native. It ensures that you
//        get the correct health value, even when damage modifiers are applied. If
//        you don't use damage modifiers at all, you don't need this function.
//
//  function GetUnitMaxLife takes unit u returns real
//      - Use this function instead of the GetUnitState(u, UNIT_STATE_MAX_LIFE) native.
//        It will return the correct value, even when damage modifiers are applied. If
//        you don't use damage modifiers at all, you don't need this function.
//
//  function SetUnitLife takes unit u, real newLife returns nothing
//      - Use this function instead of the SetWidgetLife(u, newLife) native if you use
//        damage modifiers in your map. Same rules as for the GetUnitMaxLife and the
//        GetUnitMaxLife functions.
//
//  function UnitDamageTargetEx takes unit localSource, unit localTarget, real localAmount, boolean attack, boolean ranged, attacktype localAttackType, damagetype localDamageType, weapontype localWeaponType returns boolean
//      - Use this function to deal damage from a running damage event. It works exactly
//        the same as the native UnitDamageTarget but is recursion safe so that you can
//        realize damage reflection for example with this. Don't ever use this function
//        outside of an onDamage event.
//
//  function AddDamageHandler takes code damageHandler returns nothing
//      - Allows you to add a damage handler function to the system. This is not
//        required for GUI users, only for vanilla JASS users. GUI users should
//        use the real variable damageEventTrigger to add damage handlers to this
//        system as explained above.
//
//  function RemoveDamageHandler takes code damageHandler returns nothing
//      - Allows you to remove a damage handler function from the system dynamic-
//        ally. If you added the same handler function multiple times to the sys-
//        tem, this function will remove all of these equal functions. This is not
//        required for GUI users, only for vanilla JASS users.
//
//////////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////////
//  Configurable globals
//////////////////////////////////////////////////////////////////////////////////

function InitGlobalVariables takes nothing returns nothing
    // Put here the correct ability IDs
    set udg_PDD_DAMAGE_TYPE_DETECTOR = 'A000'
    set udg_PDD_SET_MAX_LIFE = 'A001'
 
    // Here you can configure some stuff, read the documentation for further info
    set udg_PDD_SPELL_DMG_REDUCTION_ITEM = 'brac'
    set udg_PDD_SPELL_RESIST_AUTO_DETECT = false
    set udg_PDD_ETHEREAL_DAMAGE_FACTOR = 1.66
    set udg_PDD_BRACERS_SPELL_DMG_RED = 0.33
    set udg_PDD_TRIGGER_CLEANUP_PERIOD = 60.0
endfunction

//////////////////////////////////////////////////////////////////////////////////
//  End of configurable globals
//////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////
//  User functions
//////////////////////////////////////////////////////////////////////////////////

function AddDamageHandler takes code func returns nothing
    local integer id = GetHandleId(Condition(func))
    local integer index = 0
 
    // Loop to manage equal damage handlers
    loop
        exitwhen ( LoadTriggerConditionHandle(udg_PDD_h, id, index) == null )
        set index = index + 1
    endloop
    // Store the desired damage handler function
    call SaveTriggerConditionHandle(udg_PDD_h, id, index, TriggerAddCondition(udg_PDD_damageHandler, Filter(func)))
endfunction

function RemoveDamageHandler takes code func returns nothing
    local integer id = GetHandleId(Condition(func))
    local integer index = 0
 
    // Loop through all equal damage handlers
    loop
        exitwhen ( LoadTriggerConditionHandle(udg_PDD_h, id, index) == null )
        call TriggerRemoveCondition(udg_PDD_damageHandler, LoadTriggerConditionHandle(udg_PDD_h, id, index))
        set index = index + 1
    endloop
 
    // Clean things up
    call FlushChildHashtable(udg_PDD_h, id)
endfunction

function GetUnitLife takes unit u returns real
    local boolean duringModification
    local integer uId = GetHandleId(u)
    local real health
    local real storedHealth = LoadReal(udg_PDD_h, uId, 2)
    local real storedDamage = LoadReal(udg_PDD_h, uId, 1)
 
    // Check if the unit is being rescued from damage
    set duringModification = GetUnitAbilityLevel(u, udg_PDD_SET_MAX_LIFE) > 0
    if duringModification then
        call UnitRemoveAbility(u, udg_PDD_SET_MAX_LIFE)
    endif

    // Get the correct health value of the unit
    if storedHealth != 0.0 then
        set health = storedHealth - storedDamage
    else
        set health = GetWidgetLife(u) - storedDamage
    endif

    // Restore the rescue ability and return
    if duringModification then
        call UnitAddAbility(u, udg_PDD_SET_MAX_LIFE)
    endif
    return health
endfunction

function GetUnitMaxLife takes unit u returns real
    local real maxHealth
 
    // Check if the unit is being rescued from damage
    if GetUnitAbilityLevel(u, udg_PDD_SET_MAX_LIFE) > 0 then
        call UnitRemoveAbility(u, udg_PDD_SET_MAX_LIFE)
        set maxHealth = GetUnitState(u, UNIT_STATE_MAX_LIFE)
        call UnitAddAbility(u, udg_PDD_SET_MAX_LIFE)
        return maxHealth
    endif
 
    // If the unit isn't being rescued, use the standard native
    return GetUnitState(u, UNIT_STATE_MAX_LIFE)
endfunction

function SetUnitLife takes unit u, real newLife returns nothing
    local integer targetId
    local integer oldTimerId
    local real oldHealth

    // Check if the unit is being rescued from damage
    if GetUnitAbilityLevel(u, udg_PDD_SET_MAX_LIFE) > 0 then
        call UnitRemoveAbility(u, udg_PDD_SET_MAX_LIFE)
        call SetWidgetLife(u, newLife)
        call UnitAddAbility(u, udg_PDD_SET_MAX_LIFE)
     
        // Get the unit specific timer information
        set targetId = GetHandleId(u)
        set oldHealth = LoadReal(udg_PDD_h, targetId, 0)
     
        // Update the unit specific timer information
        if oldHealth != 0.0 then
            set oldTimerId = LoadInteger(udg_PDD_h, targetId, 3)
            call SaveReal(udg_PDD_h, targetId, 2, newLife)
            call SaveReal(udg_PDD_h, targetId, 0, newLife)
            call SaveReal(udg_PDD_h, oldTimerId, 4, newLife)
        endif
        return
    endif
 
    // If the unit isn't being rescued, use the standard native
    call SetWidgetLife(u, newLife)
endfunction

function UnitDamageTargetEx takes unit localSource, unit localTarget, real localAmount, boolean attack, boolean ranged, attacktype localAttackType, damagetype localDamageType, weapontype localWeaponType returns boolean
    // Avoid infinite loop due to recursion
    if udg_PDD_damageType == udg_PDD_CODE then
        return false
    endif
 
    // Avoid allocating attacks on units that are about to be killed
    if ( localTarget == udg_PDD_target and GetUnitLife(localTarget) - udg_PDD_amount < udg_PDD_UNIT_MIN_LIFE ) then
        return false
    endif
 
    // Store all damage parameters determined by the user
    set udg_PDD_allocatedAttacks = udg_PDD_allocatedAttacks + 1
    call SaveUnitHandle(udg_PDD_h, udg_PDD_allocatedAttacks, 0, localSource)
    call SaveUnitHandle(udg_PDD_h, udg_PDD_allocatedAttacks, 1, localTarget)
    call SaveReal(udg_PDD_h, udg_PDD_allocatedAttacks, 2, localAmount)
    call SaveBoolean(udg_PDD_h, udg_PDD_allocatedAttacks, 3, attack)
    call SaveBoolean(udg_PDD_h, udg_PDD_allocatedAttacks, 4, ranged)
    call SaveInteger(udg_PDD_h, udg_PDD_allocatedAttacks, 5, GetHandleId(localAttackType))
    call SaveInteger(udg_PDD_h, udg_PDD_allocatedAttacks, 6, GetHandleId(localDamageType))
    call SaveInteger(udg_PDD_h, udg_PDD_allocatedAttacks, 7, GetHandleId(localWeaponType))

    // Return true if the damage was allocated
    return true
endfunction

////////////////////////////////////////////////////////////////////////////////////
//  Sub functions
////////////////////////////////////////////////////////////////////////////////////

function DealFixDamage takes unit source, unit target, real pureAmount returns nothing
    local real MAX_DAMAGE = 1000000.0
    local real beforeHitpoints
    local real newHitpoints

    // Ensure the amount is positive
    if pureAmount < 0 then
        set pureAmount = -pureAmount
    endif

    // Save the targets hitpoints
    set beforeHitpoints = GetWidgetLife(target)
    set newHitpoints = beforeHitpoints - pureAmount

    // Apply the desired, fixed amount
    if newHitpoints >= udg_PDD_UNIT_MIN_LIFE then
        call SetUnitState(target, UNIT_STATE_LIFE, newHitpoints)
    else
        if ( IsUnitType(target, UNIT_TYPE_ETHEREAL) == false ) then
            call SetWidgetLife(target, 1.0)
            call UnitDamageTarget(source, target, MAX_DAMAGE, true, false, udg_PDD_ATTACK_TYPE_UNIVERSAL, DAMAGE_TYPE_UNIVERSAL, null)
            call SetWidgetLife(target, 0.0)
        else
            call UnitRemoveAbility(target, udg_PDD_DAMAGE_TYPE_DETECTOR)
            call SetWidgetLife(target, 1.0)
            call UnitDamageTarget(source, target, MAX_DAMAGE, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, null)
            call SetWidgetLife(target, 0.0)
        endif
    endif
endfunction

function GetUnitSpellResistance takes unit u returns real
    local real originalHP
    local real beforeHP
    local real afterHP
    local real resistance
    local real DUMMY_DAMAGE = 100
    local real DUMMY_FACTOR = 0.01
 
    // Deal spell damage in order to get the units resistance
    call UnitRemoveAbility(udg_PDD_target, udg_PDD_DAMAGE_TYPE_DETECTOR)
    set originalHP = GetWidgetLife(udg_PDD_target)
    call UnitAddAbility(udg_PDD_target, udg_PDD_SET_MAX_LIFE)
    call SetWidgetLife(udg_PDD_target, 20000.0)
    set beforeHP = GetWidgetLife(udg_PDD_target)
    call DisableTrigger(udg_PDD_damageEvent)
    call UnitDamageTarget(udg_PDD_source, udg_PDD_target, DUMMY_DAMAGE, true, false, null, DAMAGE_TYPE_UNIVERSAL, null)
    call EnableTrigger(udg_PDD_damageEvent)
    set afterHP = GetWidgetLife(udg_PDD_target)
    call UnitRemoveAbility(udg_PDD_target, udg_PDD_SET_MAX_LIFE)
    call SetWidgetLife(udg_PDD_target, originalHP)
    call UnitAddAbility(udg_PDD_target, udg_PDD_DAMAGE_TYPE_DETECTOR)
    call UnitMakeAbilityPermanent(udg_PDD_target, true, udg_PDD_DAMAGE_TYPE_DETECTOR)

    // Calculate this resistance
    set resistance = DUMMY_FACTOR*(beforeHP - afterHP)

    // If the resistance was greater than 100%, return it
    if resistance > 1.0 then
        return resistance
    else
        return 1.0
    endif
endfunction

function UnitHasItemOfType takes unit u, integer itemId returns integer
    local integer index = 0
    local item indexItem

    // Check if the target has a spell damage reducing item
    loop
        set indexItem = UnitItemInSlot(u, index)
        if ( indexItem != null ) and ( GetItemTypeId(indexItem) == itemId ) then
            set indexItem = null
            return index + 1
        endif

        set index = index + 1
        exitwhen index >= bj_MAX_INVENTORY
    endloop
    set indexItem = null
    return 0
endfunction


////////////////////////////////////////////////////////////////////////////////////
//  Damage Engine
////////////////////////////////////////////////////////////////////////////////////

function AfterDamage takes nothing returns nothing
    local timer time = GetExpiredTimer()
    local integer id = GetHandleId(time)
    local unit localSource = LoadUnitHandle(udg_PDD_h, id, 0)
    local unit localTarget = LoadUnitHandle(udg_PDD_h, id, 1)
    local real pureAmount = LoadReal(udg_PDD_h, id, 2)
    local real amount = LoadReal(udg_PDD_h, id, 3)
    local real originalHealth = LoadReal(udg_PDD_h, id, 4)

    // If the damage was modified, restore units health
    if originalHealth != 0.0 then
        call UnitRemoveAbility(localTarget, udg_PDD_SET_MAX_LIFE)
        call SetWidgetLife(localTarget, originalHealth)
    endif

    // Apply the desired amount of damage
    if amount > 0.0 then
        call DisableTrigger(udg_PDD_damageEvent)
        call DealFixDamage(localSource, localTarget, amount)
        call EnableTrigger(udg_PDD_damageEvent)
    else
        call SetWidgetLife(localTarget, originalHealth - amount)
    endif
 
    // Clean things up
    call FlushChildHashtable(udg_PDD_h, id)
    call FlushChildHashtable(udg_PDD_h, GetHandleId(localTarget))
    call DestroyTimer(time)
    set time = null
    set localSource = null
    set localTarget = null
endfunction

function DamageEngine takes nothing returns nothing
    local timer time
    local integer id
    local real health
    local real rawAmount
    local real originalHealth
    local integer targetId
    local integer oldTimerId
    local real oldHealth
    local real oldOriginalHealth
    local real oldAmount
  
    set rawAmount = GetEventDamage()
    if rawAmount == 0.0 then
        return
    endif
    set udg_PDD_source = GetEventDamageSource()
    set udg_PDD_target = GetTriggerUnit()
 
    // Determine the damage type
    if rawAmount > 0.0 then
        if udg_PDD_damageType != udg_PDD_CODE then
            set udg_PDD_damageType = udg_PDD_PHYSICAL
        endif
        set udg_PDD_amount = rawAmount
    else
        if udg_PDD_damageType != udg_PDD_CODE then
            set udg_PDD_damageType = udg_PDD_SPELL
        endif
        set udg_PDD_amount = -rawAmount
    endif
 
    // Register spell reduction above 100%
    if udg_PDD_SPELL_RESIST_AUTO_DETECT then
        set udg_PDD_amount = GetUnitSpellResistance(udg_PDD_target)*udg_PDD_amount
    else
        if ( IsUnitType(udg_PDD_target, UNIT_TYPE_ETHEREAL) and IsUnitEnemy(udg_PDD_target, GetOwningPlayer(udg_PDD_source)) and rawAmount < 0.0 ) then
            set udg_PDD_amount = udg_PDD_ETHEREAL_DAMAGE_FACTOR*udg_PDD_amount
        endif
    endif
 
    // Register spell damage reducing items like runed bracers
    if ( IsUnitType(udg_PDD_target, UNIT_TYPE_HERO) and UnitHasItemOfType(udg_PDD_target, udg_PDD_SPELL_DMG_REDUCTION_ITEM) > 0 ) and rawAmount < 0.0 then
        set udg_PDD_amount = (1 - udg_PDD_BRACERS_SPELL_DMG_RED)*udg_PDD_amount
    endif
 
    // Save health and damage variables
    if udg_PDD_damageType != udg_PDD_CODE then
        call UnitRemoveAbility(udg_PDD_target, udg_PDD_SET_MAX_LIFE)
    endif
    set udg_PDD_pureAmount = udg_PDD_amount
    set originalHealth = GetWidgetLife(udg_PDD_target)
    set oldTimerId = 0
 
    // Call damage handlers
    set udg_PDD_damageEventTrigger = 0.0
    set udg_PDD_damageEventTrigger = 1.0
    set udg_PDD_damageEventTrigger = 0.0

    // If the damage was modified, apply the rescue ability
    if udg_PDD_amount != udg_PDD_pureAmount then
        call UnitAddAbility(udg_PDD_target, udg_PDD_SET_MAX_LIFE)
        call SetWidgetLife(udg_PDD_target, GetWidgetLife(udg_PDD_target) + udg_PDD_pureAmount)
    endif
 
    // Check if a previous timer didn't yet expire
    set targetId = GetHandleId(udg_PDD_target)
    set oldHealth = LoadReal(udg_PDD_h, targetId, 0)
 
    // If this is the case, update the timer information
    if oldHealth != 0.0 then
        set oldTimerId = LoadInteger(udg_PDD_h, targetId, 3)
        set oldOriginalHealth = LoadReal(udg_PDD_h, targetId, 2)
        set oldAmount = LoadReal(udg_PDD_h, targetId, 1)
        set originalHealth = oldOriginalHealth - oldAmount
        call SaveReal(udg_PDD_h, oldTimerId, 4, oldOriginalHealth)
    endif

    // Call after damage event if damage was spell, modified, code or parallel
    if ( rawAmount < 0.0 or udg_PDD_pureAmount != udg_PDD_amount or oldTimerId != 0 or udg_PDD_allocatedAttacks > 0 ) then
        set time = CreateTimer()
        set id = GetHandleId(time)
     
        // Save handles for after damage event
        call SaveUnitHandle(udg_PDD_h, id, 0, udg_PDD_source)
        call SaveUnitHandle(udg_PDD_h, id, 1, udg_PDD_target)
        call SaveReal(udg_PDD_h, id, 2, udg_PDD_pureAmount)
        call SaveReal(udg_PDD_h, id, 3, udg_PDD_amount)
        call SaveReal(udg_PDD_h, id, 4, originalHealth)

        // Save this extra to manage parallel damage instances
        call SaveReal(udg_PDD_h, targetId, 0, GetWidgetLife(udg_PDD_target))
        call SaveReal(udg_PDD_h, targetId, 1, udg_PDD_amount)
        call SaveReal(udg_PDD_h, targetId, 2, originalHealth)
        call SaveInteger(udg_PDD_h, targetId, 3, id)

        // Avoid healing of negative spell damage
        if rawAmount < 0.0 then
            call DisableTrigger(udg_PDD_damageEvent)
            call DealFixDamage(udg_PDD_source, udg_PDD_target, -rawAmount)
            if ( originalHealth - udg_PDD_amount < udg_PDD_UNIT_MIN_LIFE ) then
                call UnitRemoveAbility(udg_PDD_target, udg_PDD_SET_MAX_LIFE)
                call DealFixDamage(udg_PDD_source, udg_PDD_target, udg_PDD_amount)
            endif
            call EnableTrigger(udg_PDD_damageEvent)
        endif
     
        // Guarantee unit exploding
        if originalHealth - udg_PDD_amount < udg_PDD_UNIT_MIN_LIFE then
            if rawAmount > 0.0 then
                call UnitRemoveAbility(udg_PDD_target, udg_PDD_SET_MAX_LIFE)
                call SetWidgetLife(udg_PDD_target, udg_PDD_UNIT_MIN_LIFE)
            endif
        endif
     
        // Start the after damage event
        call TimerStart(time, 0.0, false, function AfterDamage)
    endif

    // Handle allocated attacks from UnitDamageTargetEx
    if udg_PDD_totalAllocs == 0 then
        set udg_PDD_totalAllocs = udg_PDD_allocatedAttacks
    endif
    if udg_PDD_allocatedAttacks > 0 then
        set udg_PDD_allocatedAttacks = udg_PDD_allocatedAttacks - 1
        set udg_PDD_allocCounter = udg_PDD_allocCounter + 1
        call TriggerEvaluate(udg_PDD_runAllocatedAttacks)
    endif

    // Reset all required variables
    set udg_PDD_damageType = -1
    set udg_PDD_totalAllocs = 0
    set udg_PDD_allocCounter = -1
endfunction


////////////////////////////////////////////////////////////////////////////////////
//  Initialization
////////////////////////////////////////////////////////////////////////////////////

function RestoreTriggers takes nothing returns nothing
    local unit enumUnit = GetEnumUnit()

    // Re-register units that are alive
    if GetUnitTypeId(enumUnit) != 0 then
        call TriggerRegisterUnitEvent(udg_PDD_damageEvent, enumUnit, EVENT_UNIT_DAMAGED)
    endif
    set enumUnit = null
endfunction

function ClearMemory_Actions takes nothing returns nothing
    local group g = CreateGroup()
    local code c = function DamageEngine
 
    // Reset the damage event
    call GroupEnumUnitsInRect(g, GetWorldBounds(), null)
    call ResetTrigger(udg_PDD_damageEvent)
    call DestroyTrigger(udg_PDD_damageEvent)
    set udg_PDD_damageEvent = null
 
    // Rebuild it then
    set udg_PDD_damageEvent = CreateTrigger()
    call TriggerAddCondition(udg_PDD_damageEvent, Filter(c))
    call ForGroup(g, function RestoreTriggers)
 
    // Clean things up
    call DestroyGroup(g)
    set g = null
    set c = null
endfunction

function MapInit takes nothing returns nothing
    local unit enumUnit = GetEnumUnit()

    // Register units on map initialization
    call UnitAddAbility(enumUnit, udg_PDD_DAMAGE_TYPE_DETECTOR)
    call UnitMakeAbilityPermanent(enumUnit, true, udg_PDD_DAMAGE_TYPE_DETECTOR)
    call TriggerRegisterUnitEvent(udg_PDD_damageEvent, enumUnit, EVENT_UNIT_DAMAGED)
    set enumUnit = null
endfunction

function UnitEntersMap takes nothing returns nothing
    local unit triggerUnit = GetTriggerUnit()

    // Register units that enter the map
    if ( GetUnitAbilityLevel(triggerUnit, udg_PDD_DAMAGE_TYPE_DETECTOR) < 1 ) then
        call UnitAddAbility(triggerUnit, udg_PDD_DAMAGE_TYPE_DETECTOR)
        call UnitMakeAbilityPermanent(triggerUnit, true, udg_PDD_DAMAGE_TYPE_DETECTOR)
        call TriggerRegisterUnitEvent(udg_PDD_damageEvent, triggerUnit, EVENT_UNIT_DAMAGED)
    endif
    set triggerUnit = null
endfunction

function RunAllocatedAttacks takes nothing returns nothing
    local integer localAllocAttacks = udg_PDD_allocatedAttacks + 1

    // Calculate the correct sequence of allocated attacks
    set localAllocAttacks = localAllocAttacks - udg_PDD_totalAllocs + 1 + 2*udg_PDD_allocCounter
 
    // Deal code damage if the unit isn't exploding
    set udg_PDD_damageType = udg_PDD_CODE
    if GetUnitLife(LoadUnitHandle(udg_PDD_h, localAllocAttacks, 1)) >= udg_PDD_UNIT_MIN_LIFE then
        call UnitDamageTarget(LoadUnitHandle(udg_PDD_h, localAllocAttacks, 0), LoadUnitHandle(udg_PDD_h, localAllocAttacks, 1), LoadReal(udg_PDD_h, localAllocAttacks, 2), LoadBoolean(udg_PDD_h, localAllocAttacks, 3), LoadBoolean(udg_PDD_h, localAllocAttacks, 4), ConvertAttackType(LoadInteger(udg_PDD_h, localAllocAttacks, 5)), ConvertDamageType(LoadInteger(udg_PDD_h, localAllocAttacks, 6)), ConvertWeaponType(LoadInteger(udg_PDD_h, localAllocAttacks, 7)))
    else
        call FlushChildHashtable(udg_PDD_h, localAllocAttacks - 1)
    endif
 
    // Clean things up
    call FlushChildHashtable(udg_PDD_h, localAllocAttacks)
endfunction

function InitTrig_DamageEvent takes nothing returns nothing
    local group g = CreateGroup()
    local region r = CreateRegion()
    local trigger UnitEnters = CreateTrigger()
    local trigger ClearMemory = CreateTrigger()
    local code cDamageEngine = function DamageEngine
    local code cUnitEnters = function UnitEntersMap
    local code cClearMemory = function ClearMemory_Actions
    local code cRunAllocatedAttacks = function RunAllocatedAttacks
 
    // Initialize global variables
    set udg_PDD_h = InitHashtable()
    set udg_PDD_damageEvent = CreateTrigger()
    set udg_PDD_damageHandler = CreateTrigger()
    set udg_PDD_damageType = -1
    set udg_PDD_allocatedAttacks = 0
    set udg_PDD_runAllocatedAttacks = CreateTrigger()
 
    // Initialize global configurable constants
    call InitGlobalVariables()
 
    // Initialize global fixed constants
    set udg_PDD_PHYSICAL = 0
    set udg_PDD_SPELL = 1
    set udg_PDD_CODE = 2
    set udg_PDD_UNIT_MIN_LIFE = 0.406
    set udg_PDD_ATTACK_TYPE_UNIVERSAL = ConvertAttackType(7)
    set udg_PDD_totalAllocs = 0
    set udg_PDD_allocCounter = -1
    set udg_PDD_damageEventTrigger = 0.0
 
    // Register units on map initialization
    call TriggerRegisterVariableEvent(udg_PDD_damageHandler, "udg_PDD_damageEventTrigger", EQUAL, 1.0)
    call TriggerAddCondition(udg_PDD_damageEvent, Filter(cDamageEngine))
    call GroupEnumUnitsInRect(g, GetWorldBounds(), null)
    call ForGroup(g, function MapInit)
 
    // Register units that enter the map
    call RegionAddRect(r, GetWorldBounds())
    call TriggerRegisterEnterRegion(UnitEnters, r, null)
    call TriggerAddCondition(UnitEnters, Filter(cUnitEnters))
 
    // Register trigger for allocated attacks
    call TriggerAddCondition(udg_PDD_runAllocatedAttacks, Filter(cRunAllocatedAttacks))
 
    // Clear memory leaks
    call TriggerRegisterTimerEvent(ClearMemory, udg_PDD_TRIGGER_CLEANUP_PERIOD, true)
    call TriggerAddCondition(ClearMemory, Filter(cClearMemory))

    // Clean things up
    call DestroyGroup(g)
    set UnitEnters = null
    set ClearMemory = null
    set cDamageEngine = null
    set cUnitEnters = null
    set cClearMemory = null
    set cRunAllocatedAttacks = null
    set g = null
    set r = null
endfunction


  • OnDamage
    • Events
      • Game - PDD_damageEventTrigger becomes Equal to 1.00
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • 'IF'-Conditions
          • PDD_damageType Equal to PDD_PHYSICAL
        • 'THEN'-Actions
          • -------- Actions for PHYSICAL damage --------
          • -------- Block all physical damage from the Archmage --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • 'IF'-Conditions
              • PDD_source Equal to Archmage 0001 <gen>
            • 'THEN'-Actions
              • Set PDD_amount = 0.00
            • 'ELSE'-Actions
          • -------- Let the Blademaster heal 50 hitpoints on every attack --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • 'IF'-Conditions
              • PDD_source Equal to Blademaster 0000 <gen>
            • 'THEN'-Actions
              • Set PDD_amount = -50.00
            • 'ELSE'-Actions
          • Game - Display to (All players) for 10.00 seconds the text: ((Name of PDD_source) + ( damages + ((Name of PDD_target) + ( with damage: + (|cffff0000 + (String(PDD_amount)))))))
          • -------- End of Actions for PHYISCAL damage --------
        • 'ELSE'-Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • 'IF'-Conditions
              • PDD_damageType Equal to PDD_SPELL
            • 'THEN'-Actions
              • -------- Actions for SPELL damage --------
              • -------- Double all spell damage from the Mountain King --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • 'IF'-Conditions
                  • PDD_source Equal to Mountainking 0002 <gen>
                • 'THEN'-Actions
                  • Set PDD_amount = (2.00 x PDD_amount)
                • 'ELSE'-Actions
              • Game - Display to (All players) for 10.00 seconds the text: ((Name of PDD_source) + ( damages + ((Name of PDD_target) + ( with damage: + (|cff6495ed + (String(PDD_amount)))))))
              • -------- End of Actions for SPELL damage --------
            • 'ELSE'-Actions
              • -------- Actions for CODE damage --------
              • Game - Display to (All players) for 10.00 seconds the text: ((Name of PDD_source) + ( damages + ((Name of PDD_target) + ( with damage: + (|cff32cd32 + (String(PDD_amount)))))))
              • -------- End of Actions for CODE damage --------
      • -------- Reflect 50 damage from the Kodo back on the damage source --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • 'IF'-Conditions
          • PDD_target Equal to Kodo-Beast 0006 <gen>
        • 'THEN'-Actions
          • Custom script: call UnitDamageTargetEx(udg_PDD_target, udg_PDD_source, 50.0, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null)
        • 'ELSE'-Actions


real PDD_damageEventTrigger
Use the event "value of real variable becomes equal to 1.0" to detect a damage event. In this event you can use the following API. Compare the OnDamage trigger for an example usage.

unit PDD_target
In this global unit variable, the damaged unit is saved. Don't write anything into this variable, use it as readonly.

unit PDD_source
In this global unit variable, the damage source is saved. Don't write anything into this variable, use it as readonly.

real PDD_amount
In this global real variable, the amount of damage is saved. This amount can be modified by simply setting it to the desired amount that should be applied to the target. Set the amount to 0.0 to block the damage completly. Negative values will result in heal.

integer PDD_damageType
In this global integer variable, the damage type of the current damage is saved. Use it to differentiate between physical, spell and code damage. The variable can takes the values PHYSICAL == 0, SPELL == 1 and CODE == 2. Don't write anything into this variable, use it as readonly.

function UnitDamageTargetEx takes unit localSource, unit localTarget, real localAmount, boolean attack, boolean ranged, attacktype localAttackType, damagetype localDamageType, weapontype localWeaponType returns boolean
Use this function to allocate attacks from a running damage event. You can use it in exactly the same way as the standard native UnitDamageTarget. The function is designed recursive, so you can allocate as many attacks as you want. Do not use this function outside of a damage event.

function GetUnitLife takes unit u returns real
Use this function instead of the GetWidgetLife native. It ensures that you get the correct health value, even when damage modifiers are applied. If you don't use damage modifiers at all, you don't need this function.

function GetUnitMaxLife takes unit u returns real
Use this function instead of the GetUnitState(u, UNIT_STATE_MAX_LIFE) native. It will return the correct value, even when damage modifiers are applied. If you don't use damage modifiers at all, you don't need this function.

function SetUnitLife takes unit u, real newLife returns nothing
Use this function instead of the SetWidgetLife(u, newLife) native if you use damage modifiers in your map. Same rules as for the GetUnitMaxLife and the GetUnitMaxLife functions.

function AddDamageHandler takes code damageHandler returns nothing
Allows you to add a damage handler function to the system. This is not required for GUI users, only for vanilla JASS users. GUI users should use the real variable damageEventTrigger to add damage handlers to this system as explained above.

function RemoveDamageHandler takes code damageHandler returns nothing
Allows you to remove a damage handler function from the system dynamically. If you added the same handler function multiple times to the system, this function will remove all of these equal functions. This is not required for GUI users, only for vanilla JASS users.



3. Implementation and important notes

Follow these steps to implement the system into your map.


  • Create all variables that this system uses. You can use the "VariableCreator" trigger from the testmap to automatically let it create all variables. Make sure you checked "Automatically create unknown variables" under the WorldEditor preferences, then copy the VariableCreator trigger into your map and save it. After the variables are created delete the VariableCreator trigger and continue.
  • Copy the DamageEvent trigger to your map. You can then add damage handlers by using the event "value of real variable becomes equal to 1" with the variable PDD_damageEventTrigger. Compare the OnDamage trigger for an example usage.
  • Copy the two custom abilities to your map and make sure they have the correct ID. You can specify the ID in the function InitGlobalVariables below.
  • Go to the locust swarm ability and invert its damage return portion from (default) 0.75 to -0.75.
  • Remove the spell damage reduction ability from the spell damage reduction items you use (runed bracers). You can configure the resistance of this item in the InitGlobalVariables function, modifying the global variable udg_PDD_BRACERS_SPELL_DMG_RED.



  • Life Drain does not work with this system, so you should use a triggered version of this spell if you want to use it.
  • Same for Finger of Death, if you want to use this spell bug free with this system, you should use a triggered version of it.
  • If you use damage modifiers by setting the damage amount variable, you have to use GetUnitLife, GetUnitMaxLife and SetUnitLife instead of GetWidgetLife, GetUnitState for UNIT_STATE_MAX_LIFE and SetWidgetLife.
  • The boolean udg_PDD_SPELL_RESIST_AUTO_DETECT is only neccessary set to true if you want to use a customized damage table with spell damage resistance above 100%. If this is not the case, it should be set to false, as the system is faster then and still works correct.
  • As already mentioned you can't use the spell reduction ability when using this system (runed bracers and elunes grace). If you want to use them, you can trigger them by using the damage modifiers. Runed bracers is already considered in this system, so you don't have to code it.



4. Screenshots

Enclosed are some screenshots of the testmap showing the detection of damage via text messages. Physical damage is displayed in red, spell damage in blue and code damage in green.


Spell Damage
pdd-spell-damage-png.326453


Physical Damage
pdd-physical-damage-png.326454


Code Damage
upload_2019-6-26_11-22-12-png.326452

physicaldamage.jpg

spelldamage.jpg

codedamage.jpg

spellandphysicaldamage.jpg



5. Special thanks

My special thanks go to Cokemonkey11 and Nestharus for giving tipps, finding bugs, discussing and, by doing so, helping me to develope this system.

Hope you enjoy it!

Greetings,
lfh

Keywords:
Damage Detection System, GUI, DDS, Physical, Spell, DamageType, detect damage type, detect physical damage, block damage, modify damage
Contents

Physical Damage Detection (Map)

Reviews
05:09, 30th Mar 2013 Magtheridon96: Approved. The code is fine and readable and you seem to be maintaining the resource. I recommend this to anyone who wants nice and smooth damage detection with type support included right out of the box...

Moderator

M

Moderator

05:09, 30th Mar 2013
Magtheridon96: Approved.

The code is fine and readable and you seem to be maintaining the resource. I recommend this to anyone who wants nice and smooth damage detection with type support included right out of the box.

edit: This is too good for me to leave at 4/5. Promoted to 5/5 - Highly Recommended.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Also I think people that want this GUI wants to use a GUI DDS too.

People asked me to do this. People that use only GUI ;)

sorry, thought it was nest's damage event that's why

Nest's system can't be used with vanilla JASS nor with a GUI interface ;)

Actually the code is very sophisticated and it took months to get the system into this state.
 
Create all variables that this system uses (compare the variable editor). You don't have to set them to specific values, they get initialized automatically by the system so just create them.

What you can do is create a GUI trigger that uses all the variables in Set statements.
Disable it and tell them to copy that trigger to their map while having (Create unknown variables) set to true. Then they would have to delete it.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
What you can do is create a GUI trigger that uses all the variables in Set statements.
Disable it and tell them to copy that trigger to their map while having (Create unknown variables) set to true. Then they would have to delete it.

Thats actually a good idea. I did this and updated the system and the documentation.

I also deleted an important line for debuging reasons in Version 1.0.0.0 and forgot to put it back lol. Therefore I made a mini update.

UPDATE Version 1.0.0.1:
- Added a VariableCreator trigger which creates automatically all required variables
- Fixed a minor bug as explained above
- Updated the documentation and uploaded a fixed testmap
 
Last edited:
Level 3
Joined
Apr 2, 2013
Messages
41
how can i dectect extra dmg like Cleaving Attack or Barrage ,and Spell dmg by trigger (attack_type_normal ,damage_type_magic) ?
 
Level 33
Joined
Apr 24, 2012
Messages
5,113
It can only detect what is the main type.Cleaving Attack or Barrage are considered Physical while the damage done by code or Trigger is Code.There is no such thing how to detect the exact damage type and attack type.But you can detect it if you register all units then save its Damage Type and Attack Type via typecasting, though its bad
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
how can i dectect extra dmg like Cleaving Attack or Barrage ,and Spell dmg by trigger (attack_type_normal ,damage_type_magic) ?

Unfortunatly, as Almia said, you can only detect the basic type (physical or spell).

Allocated damage (from damage handler functions) is considered as code damage.

But detecting spell damage by triggers is trivially easy (in general all damage dealt by triggers is easy to detect) - just set a global integer variable, e.g. integer myDamageType to a predefined value before you deal damage and then check for the variable in the damage response function.
 
Level 3
Joined
Apr 2, 2013
Messages
41
Can you give me example like Rune Bracer ability ( can reduce spell dmg and trigger spell dmg ) .I really need this , Thanks
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Can you give me example like Rune Bracer ability ( can reduce spell dmg and trigger spell dmg ) .I really need this , Thanks

Sure, for example like this:

  • OnDamage
    • Events
      • Game - damageEventTrigger becomes Equal to 1.00
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • 'IF'-Conditions
          • damageType Equal to SPELL
        • 'THEN'-Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • 'IF'-Conditions
              • (target has yourItem) Equal to True
              • ((Unit-type of target) is a Hero) Equal to True
            • 'THEN'-Actions
              • Set amount = (0.50 x amount)
            • 'ELSE'-Actions
        • 'ELSE'-Actions
In this example, yourItem reduces any spell damage by 50%.


Or did you really mean the item runed bracers? Because this item is already considered in the system and you can use it directly.
You would have to remove the spell damage reducing ability of it, of course, but it will work as intended.
 
WoW man, firstly I was thinking to this is some edited version of Bribes damage engine, wow I just tested it and it is just wow wow. Just kidding, I really find this extra extra useful. It is strange that it is not that popular. For me this is best and easiest to immport and use damage engine.
I dont know how to see if damage is spell in Bribes system, also this by Nesteharus(I will never learn to spell his nick xD) is hardly coded and impossible to use by Gui coders, but your man it is perfectly good and useful.
Good job. Just to ask what is API and is therre any known bugs that should be fixed?
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
WoW man, firstly I was thinking to this is some edited version of Bribes damage engine, wow I just tested it and it is just wow wow. Just kidding, I really find this extra extra useful.

Thanks :)

Good job. Just to ask what is API

Well, API is an abbreviation for Application Programming Interface, as Maker posted. So, in easy words, its just the (new) functionality an application gives to you. Of course one want to use this functionality, so the API tells how to use it.

For example, there is no native event "Any unit takes damage", but this library provides an event "damageEventTrigger becomes Equal to 1.0" for exactly that purpose - therefore this is part of the systems API.

and is therre any known bugs that should be fixed?

No, I don't know of any bugs. If you use the system correct, as described in the documentation, it should work without any problems.
 
When I was experimenting in game, changing items for demon hunter casting spirit link on him, and many more things from some unknows reason when water element was attacking him it was dealing about 10-20 damage but text was only showing 4-6 damage. Also does this bracer for spell damage reduction, reduce damage taken by spells for 66% or 33%, I think it writes 33 but when I cast blizzard on hero with this item it takes 11.5 damage and with out that item 30 damage.
 
Level 16
Joined
Jul 31, 2012
Messages
2,217
I am still using this system [a lot] and i only found a bug (don't know if it is much a big bug :p) if you make an edited firebolt ability with negative damage, the damage goes to positive, i had to rechange it to negative in the damagedetect trigger and worked fine ;)
And some thing to point:
This kind of system (and this one especially) can be highly useful in most spells and very wtf useful :p in making an mui spell
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Hi,

please be more specific, your post doesn't contain enough information to let me help you.

  • Do you use the GUI or the vJass version of the system?
  • What you mean with "enable and then activate"? Is the trigger enabled or not?
  • What means "serious errors"? Does the map crash, does it give compile errors?
  • Which error messages do you (exactly) get?


EDIT:

As you didn't reply for nearly two weeks now (although being online) and don't allow private messages, I guess this is solved.
The system itself can't crash anyway so I guess the problems were somewhere else.
 
Last edited:
Level 11
Joined
Mar 27, 2011
Messages
293
Hi,

please be more specific, your post doesn't contain enough information to let me help you.

  • Do you use the GUI or the vJass version of the system?
  • What you mean with "enable and then activate"? Is the trigger enabled or not?
  • What means "serious errors"? Does the map crash, does it give compile errors?
  • Which error messages do you (exactly) get?


EDIT:

As you didn't reply for nearly two weeks now (although being online) and don't allow private messages, I guess this is solved.
The system itself can't crash anyway so I guess the problems were somewhere else.


Sorry. Works as follows:

If I disable the trigger of his application and then re-activate it the World Edit stops me activate it because of serious errors.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Ok, I still dont know what you mean with "serious errors". If you want help then you should answer my questions:

  • Do you use the GUI or the vJass version of the system?
  • What you mean with "enable and then activate"? Is the trigger enabled or not?
  • What means "serious errors"? Does the map crash, does it give compile errors?
  • Which error messages do you (exactly) get?
 

sentrywiz

S

sentrywiz

I like the system as I like the other system. But my question is:

-Can I use other events like "Unit starts the effect of an ability" and use the system to detect the damage? Since I'd want to create triggered spells and cause heal/damage through them. Or as you mentioned, that counts as code damage?
 
Level 33
Joined
Apr 24, 2012
Messages
5,113
I like the system as I like the other system. But my question is:

-Can I use other events like "Unit starts the effect of an ability" and use the system to detect the damage? Since I'd want to create triggered spells and cause heal/damage through them. Or as you mentioned, that counts as code damage?

You should use the DamageEvent to detect your spell's damage. A buff will do.
 

sentrywiz

S

sentrywiz

You should use the DamageEvent to detect your spell's damage. A buff will do.

I can't use the damage event when I am using another event like unit casts/channels/starts the effect of an ability. That's why I asked.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
I like the system as I like the other system. But my question is:

-Can I use other events like "Unit starts the effect of an ability" and use the system to detect the damage? Since I'd want to create triggered spells and cause heal/damage through them. Or as you mentioned, that counts as code damage?

The damage from your triggered spells will get detected correctly as spell damage. Code damage describes damage from a running damage Event like reflection. So yes, you can use the system.
 
Level 16
Joined
May 2, 2011
Messages
1,345
I must look at this sometime
-----------------------------------
Ok I have just tried it
its awsome. I did bonus damage by paladin to undead units, and it worked well (just targetting enemies)
I also have tried it with elune's grace and it seem to work with its effect on peircing attacks but not with spell. Not sure, but i think elune grace magic reduction (not spell) still works with this system
Brilliant work Looking_For_Help
I am wondering if there are any other particular abilities that does not work with this system apart from these two
5/5
Edit: is anti magic shell affected by this?
Edit: also, as for rune bracers, we should just change the variable to whatever percentage we want yes? the "rune bracers damage reduction" variable or something
Edit: and what about other abilities hardened skin etc
i need to expirement all that

Edit: ok, just tested it with this trigger
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • target Equal to Death Knight 0032 <gen>
      • (source is A melee attacker) Equal to True
    • Then - Actions
      • Set Strength = (Real((Strength of target (Include bonuses))))
      • Set Agility = (Real((Agility of target (Include bonuses))))
      • Set Intellegence = (Real((Intelligence of target (Include bonuses))))
      • Set Damage_Blocked = Strength
      • Set Chance_To_Block = ((30.00 x Intellegence) + (20.00 + Agility))
      • Set RandomInteger = (Random integer number between 1 and 10000)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Real(RandomInteger)) Less than or equal to Chance_To_Block
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • amount Greater than or equal to Damage_Blocked
            • Then - Actions
              • Set amount = (amount - Damage_Blocked)
            • Else - Actions
              • Set amount = 0.00
        • Else - Actions
    • Else - Actions
and it seems that the damage reduction implemented after armor reduction :(

I tried putting strength of hero 20 while grunt do 20 to 23 damage and they always do 0 damage when the armor of hero is seven which reduces 30% whcih means it reduces about 6 of grunt attack so grunt actually deals 14-17 and then it blocks 20 so total damage delt is 0 !!!! I tried with demon hunter it wasnt 0 damage cuz he deal 42 to somthing

Im I wrong?

Edit: and what i dont like about this is that the normal damage block set by blizzard is taken from the damage before the armor reduction, so if
an attack has 20 damage
armor is 7 with 30% reduction
block ablity blocking 10 damage
final damage reduced will be (20-10)X.7 = .7X10= 7
Edit: So if we want it like orignial one we should do something like:
attack 20
reduction armor factor .7
Block by trigger = Damage_Blocked
final damage will be .7X20 -DamageBlocked = 7 means that Damage Blocked = 7; or effectively Damage_Blocked should in fact be Armor Reduction Factor times* the original damage factor.... ummm now thats not so pleasant for me (I have to figure a way to find armor reduction blablabla -.-')
Edit: and I think it should be same with paladins, as the bonus damage (But I have to double check) will be after armor reduction, so if bonus damage was hundred and paladin did 10 damage armor reduce 50% it will be 105 instead 55 !!! D:
Edit: Hang on, I am not sure if this is actually a bad point because, when I think of it, it seems that reduction after armor reduction can possible be inverted while reduction after armor factor reduction cannot NVM I think just multiplying it by the factor the other way arround psshhhh
 
Last edited:
Level 14
Joined
Dec 12, 2012
Messages
1,007
Hi,

search this function in the system code (its the very first function):

JASS:
function InitGlobalVariables takes nothing returns nothing
    // Put here the correct ability IDs
    set udg_DAMAGE_TYPE_DETECTOR = 'A000'
    set udg_SET_MAX_LIFE = 'A001'
   
    // Here you can configure some stuff, read the documentation for further info
    set udg_SPELL_DAMAGE_REDUCTION_ITEM = 'brac'
    set udg_SPELL_RESISTANCE_AUTO_DETECT = false
    set udg_ETHEREAL_DAMAGE_FACTOR = 1.66
    set udg_BRACERS_SPELL_DAMAGE_REDUCTION = 0.33
    set udg_TRIGGER_CLEANUP_PERIOD = 60.0
endfunction

Here you can see the variable set udg_ETHEREAL_DAMAGE_FACTOR = 1.66. Change it to the value you want to have.
 
Level 5
Joined
Aug 15, 2012
Messages
139
I have a problem with this. I added this to a map that has thousands of triggers. It seems that they are colliding with this system. Because often, when damage is dealt, OnDamage trigger runs two times. It happens randomly. Do you know where should i search for the solution? I mean what type of triggers my force this system to work unproperly?

Also i have realized that it was like this, when i casted a triggered DoT spell that deals *magic* damage (it ticked *two* times):
Dread Mage damages Slave with damage: 20
Dread Mage damages Slave with damage: 20
Dread Mage damages Slave with damage: 20
Dread Mage damages Slave with damage: 20
It looks like it tries to recognize the damage type two times. One result is correct, and the other one is always physical damage. Still i don't know what may be the reason.
 
Last edited:
Level 14
Joined
Dec 12, 2012
Messages
1,007
It looks like it tries to recognize the damage type two times. One result is correct, and the other one is always physical damage. Still i don't know what may be the reason.

Alright, I found the problem.

Its just that you display the spell damage twice! One time you display it in blue color but then you display it again in red color, so it just looks like its recognized wrong.

Fix the display in your OnDamage trigger and everything will work fine.
 
Level 18
Joined
Nov 21, 2012
Messages
835
This system is broken

Sorry to say that but your system is not working property.
In 1st few days it looks ok, but after several games players found bugs.

I use Bribe's unit indexer, plus I had his Damage Engine. All works fine many months.
Tempted with your system ability (alows to detect triggered spells + in-game spells vs phisical dmg) I installed this system. But unfortunately about 5-10 bugs on 1h game appears. Sometimes spell damage not hurt the target but heals him. Thats the problem..biiig..

Now I must back to old Bribe's DDS. It tooks half a day or more..
My map is HordeVsAllianceWetlands. Decided to write here to warn all about this bug.
regards, Zibi
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Sorry to say that but your system is not working property.
In 1st few days it looks ok, but after several games players found bugs.

I use Bribe's unit indexer, plus I had his Damage Engine. All works fine many months.
Tempted with your system ability (alows to detect triggered spells + in-game spells vs phisical dmg) I installed this system. But unfortunately about 5-10 bugs on 1h game appears. Sometimes spell damage not hurt the target but heals him. Thats the problem..biiig..

If you use it properly it will work. The system is already used in a couple of maps and until now it worked fine for all, once they installed it correctly. You can send me the (bugged) map if you want and I can take a look at it.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Hi,

Hi,
..., im sure system was installed right, followed with all tips you included, Zibi

no, you forgot the last, but important tip.

Hi,
I already back to Bribe's DDS, but send private msg to lfh with old map with this System;
as i said bugs not appear always, only sometimes but thats not acceptable, im sure system was installed right, followed with all tips you included, Zibi

it took me only 2 minutes to find the problem. You use the runed bracers item just as it is, which you must not do when using this system. Instead you have to trigger the spell damage reduction in a OnDamage Trigger (which is trivially easy with this system). You can take a look at the testmap on how to implement runed bracers to your map when using this system (just remove the spell damage reduction ability from those items).

Edit:
Ok, after checking again this wasn't the problem for the healing. The problem was the use of GetUnitState while using damage modifiers. As this is not stated clear enough for GUI users in the documentation, I will consider updating the docu to avoid this problem in future.
 
Last edited:
Level 3
Joined
Oct 13, 2009
Messages
29
Thank LFH very much, finally I found a DDS that suit with me, and here is a way to make it compatible with Weep's DDS (very famous in TheHelper.net, I have used it for a long time, so I need to make LFH's DDS compatible with my old code instead re-coding all):

Create a new trigger, change its name to GUI Friendly Damage Detection, and Convert it to custom text, copy all code below into it:

JASS:
//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_source
    set udg_GDD_DamagedUnit = udg_target
    set udg_GDD_Damage = udg_amount
    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_damageEventTrigger", EQUAL, 1.00)
    //call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 2)
    call TriggerAddCondition(t, Filter(function GDD_Event))
    set t = null
endfunction

Work like a proxy, but better than re-coding. Thank Bribe for the original function.
 
Top