• 🏆 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!

New Damage System

Status
Not open for further replies.
Level 24
Joined
Aug 1, 2013
Messages
4,657
Hi all.

I was working on a new Damage System so I could have full possibilities with stuff related to damage.

I created a damage structure that resulted in this:
(I use regular real variables to simulate events because it can be used in GUI as well.)
JASS:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  
//  Damage Structure
//  
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
library adeEngine
    
    function ApplyDamage takes unit source, unit target, real amount, attacktype at, damagetype dt returns nothing
        call UnitRemoveAbility(target, udg_ADE_ABILITY_REVERT_SPELL_DMG)
        call DisableTrigger(udg_ADE_Trigger_On_Damage)
        call UnitDamageTarget(source, target, amount, true, false, at, dt, WEAPON_TYPE_WHOKNOWS)
        call EnableTrigger(udg_ADE_Trigger_On_Damage)
        call UnitAddAbility(target, udg_ADE_ABILITY_REVERT_SPELL_DMG)
    endfunction
    
    function ADE_CreateNewIndex takes nothing returns integer
        set udg_ADE_NewIndex = 0
        
        loop
            exitwhen not udg_ADE_Param_Index_Exists[udg_ADE_NewIndex]
            set udg_ADE_NewIndex = udg_ADE_NewIndex + 1
        endloop
        set udg_ADE_Param_Index_Exists[udg_ADE_NewIndex] = true
        set udg_ADE_Param_Source[udg_ADE_NewIndex] = null
        set udg_ADE_Param_Target[udg_ADE_NewIndex] = null
        set udg_ADE_Param_Amount[udg_ADE_NewIndex] = 0
        set udg_ADE_Param_Modifier[udg_ADE_NewIndex] = 1
        set udg_ADE_Param_ActualAmount[udg_ADE_NewIndex] = 0
        set udg_ADE_Param_Blocked[udg_ADE_NewIndex] = false
        set udg_ADE_Param_PreventFatalDamage[udg_ADE_NewIndex] = false
        set udg_ADE_Param_AttackType[udg_ADE_NewIndex] = 0
        set udg_ADE_Param_DamageType[udg_ADE_NewIndex] = 0
        set udg_ADE_Param_HitType[udg_ADE_NewIndex] = 0
        
        set udg_ADE_Event_Damage_Created = 1
        set udg_ADE_Event_Damage_Created = 0
        
        return udg_ADE_NewIndex
    endfunction
    
    function ADE_InvokeDamage takes integer index returns boolean
        local integer oldIndex = udg_ADE_Index
        local boolean result = false
        local real health
        local real factor
        
        set udg_ADE_Index = index
        
        set udg_ADE_Event_OnDamage_Before = 1
        set udg_ADE_Event_OnDamage_Before = 0
        
        set udg_ADE_Param_Amount[udg_ADE_Index] = udg_ADE_Param_Amount[udg_ADE_Index] * udg_ADE_Param_Modifier[udg_ADE_Index]
        if udg_ADE_Param_Blocked[udg_ADE_Index] then
            set udg_ADE_Event_Attack_Blocked = 1
            set udg_ADE_Event_Attack_Blocked = 0
        else
            set health = GetWidgetLife(udg_ADE_Param_Target[udg_ADE_Index])
            set factor = GetAttackAndDamageTypeFactor(udg_ADE_Param_Target[udg_ADE_Index], ConvertAttackType(udg_ADE_Param_AttackType[udg_ADE_Index]), ConvertDamageType(udg_ADE_Param_DamageType[udg_ADE_Index]))
            if health <= udg_ADE_Param_Amount[udg_ADE_Index] * factor then
                set udg_ADE_Event_Fatal_Damage = 1
                set udg_ADE_Event_Fatal_Damage = 0
                
                if udg_ADE_Param_PreventFatalDamage[udg_ADE_Index] then
                    set udg_ADE_Param_Amount[udg_ADE_Index] = (health-0.9) / factor
                endif
            endif
            
            if not udg_ADE_Param_Blocked[udg_ADE_Index] then
                
                call ApplyDamage(udg_ADE_Param_Source[udg_ADE_Index], udg_ADE_Param_Target[udg_ADE_Index], udg_ADE_Param_Amount[udg_ADE_Index], ConvertAttackType(udg_ADE_Param_AttackType[udg_ADE_Index]), ConvertDamageType(udg_ADE_Param_DamageType[udg_ADE_Index]))
                
                set udg_ADE_Param_ActualAmount[udg_ADE_Index] = health - GetWidgetLife(udg_ADE_Param_Target[udg_ADE_Index])
                
                set udg_ADE_Event_OnDamage_After = 1
                set udg_ADE_Event_OnDamage_After = 0
                
                set result = true
            else
                set udg_ADE_Event_Attack_Blocked = 1
                set udg_ADE_Event_Attack_Blocked = 0
            endif
        endif
        
        set udg_ADE_Event_Damage_Ended = 1
        set udg_ADE_Event_Damage_Ended = 0
        
        set udg_ADE_Param_Index_Exists[udg_ADE_Index] = false
        set udg_ADE_Param_Source[udg_ADE_Index] = null
        set udg_ADE_Param_Target[udg_ADE_Index] = null
        set udg_ADE_Param_Amount[udg_ADE_Index] = 0
        set udg_ADE_Param_Modifier[udg_ADE_Index] = 1
        set udg_ADE_Param_ActualAmount[udg_ADE_Index] = 0
        set udg_ADE_Param_Blocked[udg_ADE_Index] = false
        set udg_ADE_Param_PreventFatalDamage[udg_ADE_Index] = false
        set udg_ADE_Param_AttackType[udg_ADE_Index] = 0
        set udg_ADE_Param_DamageType[udg_ADE_Index] = 0
        set udg_ADE_Param_HitType[udg_ADE_Index] = 0
        
        set udg_ADE_Index = oldIndex
        return result
    endfunction
    
endlibrary
(When the system works 100%, I will place the ApplyDamage's code at it's return addresses like I should.

The 0.9 on fatal damage is because the units still die when a value like 0.5 is used, but I dont want to do 1 because that would display 2 health in WC3.)

After a while, someone suggested to make it detect damage as well so I tried adding some stuff.
The more I came to think of nice stuff, the better the system became.

Right now, I think that the system is pretty much finished but there might be a few things that could be better than what they are currently.
So if anyone has any comments on the coding, please tell them.

So after a while, this is the current completed version of the damage system together with a JASS Variable list:
JASS:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  
//  Damage Structure
//  
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
library adeEngine
    
    globals
        
        real            udg_ADE_MinimumHealth               = 0.415
        boolean         udg_ADE_TookFatalDamage             = false
        boolean array   udg_ADE_Param_IsDetected
        
    endglobals
    
    function GetSpellDamageFactor takes unit whichUnit returns real
        local real damage
        local real oldHealth = GetWidgetLife(whichUnit)
        call UnitAddAbility(whichUnit, udg_ADE_ABILITY_CHEAT_DEATH)
        
        call SetWidgetLife(whichUnit, 250000)
        
        call DisableTrigger(udg_ADE_Trigger_On_Damage)
        set udg_ADE_PreventCodedDamage = true
        call UnitDamageTarget(whichUnit, whichUnit, 100, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, null)
        set udg_ADE_PreventCodedDamage = false
        call EnableTrigger(udg_ADE_Trigger_On_Damage)
        
        set damage = 250000 - GetWidgetLife(whichUnit)
        call UnitRemoveAbility(whichUnit, udg_ADE_ABILITY_CHEAT_DEATH)
        call SetWidgetLife(whichUnit, oldHealth)
        return damage / 100
    endfunction
    
    function ApplyDamage takes unit source, unit target, real amount, attacktype at, damagetype dt returns nothing
        call UnitRemoveAbility(target, udg_ADE_ABILITY_REVERT_SPELL_DMG)
        call DisableTrigger(udg_ADE_Trigger_On_Damage)
        set udg_ADE_PreventCodedDamage = true
        call UnitDamageTarget(source, target, amount, true, false, at, dt, null)
        set udg_ADE_PreventCodedDamage = false
        call EnableTrigger(udg_ADE_Trigger_On_Damage)
        call UnitAddAbility(target, udg_ADE_ABILITY_REVERT_SPELL_DMG)
    endfunction
    
    function GetDamageTypeFactor takes unit whichUnit, damagetype whichDamageType returns real
        local real damage
        local real oldHealth = GetWidgetLife(whichUnit)
        call UnitAddAbility(whichUnit, udg_ADE_ABILITY_CHEAT_DEATH)
        
        call SetWidgetLife(whichUnit, 250000)
        
        call ApplyDamage(whichUnit, whichUnit, 100, udg_ATTACK_TYPE_100, whichDamageType)
        
        set damage = 250000 - GetWidgetLife(whichUnit)
        call UnitRemoveAbility(whichUnit, udg_ADE_ABILITY_CHEAT_DEATH)
        call SetWidgetLife(whichUnit, oldHealth)
        return damage / 100
    endfunction
    
    function GetAttackTypeFactor takes unit whichUnit, attacktype whichAttackType returns real
        local real damage
        local real oldHealth = GetWidgetLife(whichUnit)
        call UnitAddAbility(whichUnit, udg_ADE_ABILITY_CHEAT_DEATH)
        
        call SetWidgetLife(whichUnit, 250000)
        
        call ApplyDamage(whichUnit, whichUnit, 100, whichAttackType, DAMAGE_TYPE_UNIVERSAL)
        
        set damage = 250000 - GetWidgetLife(whichUnit)
        call UnitRemoveAbility(whichUnit, udg_ADE_ABILITY_CHEAT_DEATH)
        call SetWidgetLife(whichUnit, oldHealth)
        return damage / 100
    endfunction
    
    function GetAttackAndDamageTypeFactor takes unit whichUnit, attacktype whichAttackType, damagetype whichDamageType returns real
        local real damage
        local real oldHealth = GetWidgetLife(whichUnit)
        call UnitAddAbility(whichUnit, udg_ADE_ABILITY_CHEAT_DEATH)
        
        call SetWidgetLife(whichUnit, 250000)
        
        call ApplyDamage(whichUnit, whichUnit, 100, whichAttackType, whichDamageType)
        
        set damage = 250000 - GetWidgetLife(whichUnit)
        call UnitRemoveAbility(whichUnit, udg_ADE_ABILITY_CHEAT_DEATH)
        call SetWidgetLife(whichUnit, oldHealth)
        return damage / 100
    endfunction
    
    function ADE_CreateNewIndex takes nothing returns integer
        set udg_ADE_NewIndex = 0
        
        loop
            exitwhen not udg_ADE_Param_Index_Exists[udg_ADE_NewIndex]
            set udg_ADE_NewIndex = udg_ADE_NewIndex + 1
        endloop
        set udg_ADE_Param_Index_Exists[udg_ADE_NewIndex] = true
        set udg_ADE_Param_Source[udg_ADE_NewIndex] = null
        set udg_ADE_Param_Target[udg_ADE_NewIndex] = null
        set udg_ADE_Param_Amount[udg_ADE_NewIndex] = 0
        set udg_ADE_Param_Modifier[udg_ADE_NewIndex] = 1
        set udg_ADE_Param_ActualAmount[udg_ADE_NewIndex] = 0
        set udg_ADE_Param_Blocked[udg_ADE_NewIndex] = false
        set udg_ADE_Param_PreventFatalDamage[udg_ADE_NewIndex] = false
        set udg_ADE_Param_AttackType[udg_ADE_NewIndex] = 0
        set udg_ADE_Param_DamageType[udg_ADE_NewIndex] = 0
        set udg_ADE_Param_HitType[udg_ADE_NewIndex] = 0
        set udg_ADE_Param_IsDetected[udg_ADE_NewIndex] = false
        
        set udg_ADE_Event_Damage_Created = 1
        set udg_ADE_Event_Damage_Created = 0
        
        return udg_ADE_NewIndex
    endfunction
    
    function ADE_InvokeDamage takes integer index returns boolean
        local integer oldIndex = udg_ADE_Index
        local boolean result = false
        local real health
        local real mana
        local real factor
        local boolean fatalDamage = false
        local unit oldSource = udg_ADE_Param_Source[udg_ADE_Index]
        
        set udg_ADE_Index = index
        
        set udg_ADE_Event_OnDamage_Before = 1
        set udg_ADE_Event_OnDamage_Before = 0
        
        set udg_ADE_Param_Amount[udg_ADE_Index] = udg_ADE_Param_Amount[udg_ADE_Index] * udg_ADE_Param_Modifier[udg_ADE_Index]
        if udg_ADE_Param_Blocked[udg_ADE_Index] then
            set udg_ADE_Event_Attack_Blocked = 1
            set udg_ADE_Event_Attack_Blocked = 0
        else
            set health = GetWidgetLife(udg_ADE_Param_Target[udg_ADE_Index])
            set factor = GetAttackAndDamageTypeFactor(udg_ADE_Param_Target[udg_ADE_Index], ConvertAttackType(udg_ADE_Param_AttackType[udg_ADE_Index]), ConvertDamageType(udg_ADE_Param_DamageType[udg_ADE_Index]))
            set fatalDamage = health <= udg_ADE_Param_Amount[udg_ADE_Index] * factor
            if fatalDamage then
                set udg_ADE_Event_Fatal_Damage = 1
                set udg_ADE_Event_Fatal_Damage = 0
                
                if udg_ADE_Param_PreventFatalDamage[udg_ADE_Index] then
                    set udg_ADE_Param_Amount[udg_ADE_Index] = (health-0.9) / factor
                    set fatalDamage = false
                endif
            endif
            
            if not udg_ADE_Param_Blocked[udg_ADE_Index] then
                if fatalDamage and udg_ADE_Param_IsDetected[udg_ADE_Index] and udg_ADE_Param_Source[udg_ADE_Index] == oldSource then
                    call SetWidgetLife(udg_ADE_Param_Target[udg_ADE_Index], udg_ADE_MinimumHealth)
                    set udg_ADE_Param_ActualAmount[udg_ADE_Index] = health
                else
                    set fatalDamage = false
                    set health = GetWidgetLife(udg_ADE_Param_Target[udg_ADE_Index])
                    set mana = GetUnitState(udg_ADE_Param_Target[udg_ADE_Index], UNIT_STATE_MANA)
                    call ApplyDamage(udg_ADE_Param_Source[udg_ADE_Index], udg_ADE_Param_Target[udg_ADE_Index], udg_ADE_Param_Amount[udg_ADE_Index], ConvertAttackType(udg_ADE_Param_AttackType[udg_ADE_Index]), ConvertDamageType(udg_ADE_Param_DamageType[udg_ADE_Index]))
                    set udg_ADE_Param_ActualAmount[udg_ADE_Index] = (health - GetWidgetLife(udg_ADE_Param_Target[udg_ADE_Index]))  + (mana - GetUnitState(udg_ADE_Param_Target[udg_ADE_Index], UNIT_STATE_MANA))
                endif
                set oldSource = null
                
                set udg_ADE_Event_OnDamage_After = 1
                set udg_ADE_Event_OnDamage_After = 0
                
                set result = true
            else
                set fatalDamage = false
                set udg_ADE_Event_Attack_Blocked = 1
                set udg_ADE_Event_Attack_Blocked = 0
            endif
        endif
        
        set udg_ADE_Event_Damage_Ended = 1
        set udg_ADE_Event_Damage_Ended = 0
        
        set udg_ADE_TookFatalDamage = fatalDamage
        
        set udg_ADE_Param_Index_Exists[udg_ADE_Index] = false
        set udg_ADE_Param_Source[udg_ADE_Index] = null
        set udg_ADE_Param_Target[udg_ADE_Index] = null
        set udg_ADE_Param_Amount[udg_ADE_Index] = 0
        set udg_ADE_Param_Modifier[udg_ADE_Index] = 1
        set udg_ADE_Param_ActualAmount[udg_ADE_Index] = 0
        set udg_ADE_Param_Blocked[udg_ADE_Index] = false
        set udg_ADE_Param_PreventFatalDamage[udg_ADE_Index] = false
        set udg_ADE_Param_AttackType[udg_ADE_Index] = 0
        set udg_ADE_Param_DamageType[udg_ADE_Index] = 0
        set udg_ADE_Param_HitType[udg_ADE_Index] = 0
        
        set udg_ADE_Index = oldIndex
        return result
    endfunction
    
endlibrary


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  
//  Damage Detection
//  
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
globals
    
    trigger             udg_ADE_Trigger_On_Damage               = null
    real                udg_ADE_Reset_Interval                  = 60.

    boolean             udg_ADE_DontCheckForCheatDeath          = false
    boolean             udg_ADE_PreventCodedDamage              = false
    integer             udg_ADE_AbortNextDamage                 = 0
    
    timer array         udg_ADE_TimerArray
    unit array          udg_ADE_UnitArray
    real array          udg_ADE_HealthArray
    
endglobals

function ADE_Remove_Standard_Damage takes nothing returns nothing
    local integer id = GetTimerData(GetExpiredTimer())
    local integer unitId = GetUnitUserData(udg_ADE_UnitArray[id])
    
    set udg_ADE_DontCheckForCheatDeath = true
    call UnitRemoveAbility(udg_ADE_UnitArray[id], udg_ADE_ABILITY_CHEAT_DEATH)
    call SetWidgetLife(udg_ADE_UnitArray[id], udg_ADE_HealthArray[id])
    set udg_ADE_DontCheckForCheatDeath = false
    
    call ReleaseIndexedTimer(udg_ADE_TimerArray[unitId])
    set udg_ADE_TimerArray[unitId] = null
    set udg_ADE_UnitArray[id] = null
    set udg_ADE_HealthArray[id] = 0.
endfunction

function ADE_Taking_Damage takes nothing returns boolean
    local unit target
    local integer newIndex
    local real amount
    local integer id
    local integer targetId
    local timer t
    
    set amount = GetEventDamage()
    if amount == 0. then
        return false
    endif
    set target = GetTriggerUnit()
    set targetId = GetUnitUserData(target)
    
    if udg_ADE_TimerArray[targetId] != null then
        set t = udg_ADE_TimerArray[targetId]
        set udg_ADE_TimerArray[targetId] = null
        set id = GetTimerData(t)
        call UnitRemoveAbility(target, udg_ADE_ABILITY_CHEAT_DEATH)
        call SetWidgetLife(target, udg_ADE_HealthArray[id])
    else
        set t = NewIndexedTimer()
        set id = GetTimerData(t)
    endif
    
    if udg_ADE_AbortNextDamage > 0 then
        set udg_ADE_AbortNextDamage = udg_ADE_AbortNextDamage -1
    else
        if amount < 0. then
            //Spell Damage
            set amount = -amount
            set newIndex = ADE_CreateNewIndex()
            set udg_ADE_Param_Source[newIndex] = GetEventDamageSource()
            set udg_ADE_Param_Target[newIndex] = target
            set udg_ADE_Param_Amount[newIndex] = amount / -GetSpellDamageFactor(target)
            set udg_ADE_Param_AttackType[newIndex] = udg_ATTACK_TYPE_NORMAL
            set udg_ADE_Param_DamageType[newIndex] = udg_DAMAGE_TYPE_MAGIC
            set udg_ADE_Param_HitType[newIndex] = udg_HIT_TYPE_SPELLDAMAGE
            call ADE_InvokeDamage(newIndex)
        else
            //Basic attack
            set newIndex = ADE_CreateNewIndex()
            set udg_ADE_Param_Source[newIndex] = GetEventDamageSource()
            set udg_ADE_Param_Target[newIndex] = target
            set udg_ADE_Param_Amount[newIndex] = amount / GetDamageTypeFactor(target, DAMAGE_TYPE_NORMAL)
            set udg_ADE_Param_AttackType[newIndex] = udg_ATTACK_TYPE_CHAOS
            set udg_ADE_Param_DamageType[newIndex] = udg_DAMAGE_TYPE_NORMAL
            set udg_ADE_Param_HitType[newIndex] = udg_HIT_TYPE_BASICATTACK
            set udg_ADE_Param_IsDetected[newIndex] = true
            call ADE_InvokeDamage(newIndex)
        endif
    endif
    
    if udg_ADE_TookFatalDamage then
        call ReleaseIndexedTimer(t)
        set t = null
        set udg_ADE_TimerArray[targetId] = null
        set udg_ADE_UnitArray[id] = null
        set udg_ADE_HealthArray[id] = 0.
    else
        set udg_ADE_TimerArray[targetId] = t
        set t = null
        call TimerStart(udg_ADE_TimerArray[targetId], 0, false, function ADE_Remove_Standard_Damage)
        set udg_ADE_DontCheckForCheatDeath = true
        set udg_ADE_UnitArray[id] = target
        set udg_ADE_HealthArray[id] = GetWidgetLife(target)
        
        call UnitAddAbility(target, udg_ADE_ABILITY_CHEAT_DEATH)
        call SetWidgetLife(target, GetWidgetLife(target) + amount)
        set udg_ADE_DontCheckForCheatDeath = false
    endif
    
    set target = null
    return false
endfunction

function ADE_Add_Unit takes nothing returns boolean
    local unit whichUnit = GetFilterUnit()
    call TriggerRegisterUnitEvent(udg_ADE_Trigger_On_Damage, whichUnit, EVENT_UNIT_DAMAGED)
    call UnitAddAbility(whichUnit, udg_ADE_ABILITY_REVERT_SPELL_DMG)
    set whichUnit = null
    return false
endfunction

function ADE_Init_Damage_System takes nothing returns nothing
    local group g = CreateGroup()
    local unit FoG
    
    call DestroyTrigger(udg_ADE_Trigger_On_Damage)
    set udg_ADE_Trigger_On_Damage = CreateTrigger()
    
    call GroupEnumUnitsInRect(g, GetWorldBounds(), null)
    loop
        set FoG = FirstOfGroup(g)
        exitwhen FoG == null
        call GroupRemoveUnit(g, FoG)
        
        call TriggerRegisterUnitEvent(udg_ADE_Trigger_On_Damage, FoG, EVENT_UNIT_DAMAGED)
        call UnitAddAbility(FoG, udg_ADE_ABILITY_REVERT_SPELL_DMG)
    endloop
    call DestroyGroup(g)
    set g = null
    
    call TriggerAddCondition(udg_ADE_Trigger_On_Damage, Filter(function ADE_Taking_Damage))
    
    call DestroyTimer(GetExpiredTimer())
endfunction

function ADE_System_Initialize takes nothing returns nothing
    local region rectRegion = CreateRegion()
    local trigger t = CreateTrigger()
    
    call TimerStart(CreateTimer(), udg_ADE_Reset_Interval, true, function ADE_Init_Damage_System)
    call ADE_Init_Damage_System()
    
    set t = CreateTrigger()
    call RegionAddRect(rectRegion, GetWorldBounds())
    call TriggerRegisterEnterRegion(t, rectRegion, Filter(function ADE_Add_Unit))
    
    set rectRegion = null
    set t = null
    call DestroyTimer(GetExpiredTimer())
endfunction


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//===========================================================================================================================
//===========================================================================================================================
//===========================================================================================================================
function InitTrig_ADE_System takes nothing returns nothing
    
    set udg_ATTACK_TYPE_NORMAL                  = 0
    set udg_ATTACK_TYPE_MELEE                   = 1
    set udg_ATTACK_TYPE_PIERCE                  = 2
    set udg_ATTACK_TYPE_SIEGE                   = 3
    set udg_ATTACK_TYPE_MAGIC                   = 4
    set udg_ATTACK_TYPE_CHAOS                   = 5
    set udg_ATTACK_TYPE_HERO                    = 6
    set udg_ATTACK_TYPE_DOOM                    = 7
    
    set udg_DAMAGE_TYPE_UNKNOWN                 = 0
    set udg_DAMAGE_TYPE_NORMAL                  = 4
    set udg_DAMAGE_TYPE_ENHANCED                = 5
    set udg_DAMAGE_TYPE_FIRE                    = 8
    set udg_DAMAGE_TYPE_COLD                    = 9
    set udg_DAMAGE_TYPE_LIGHTNING               = 10
    set udg_DAMAGE_TYPE_POISON                  = 11
    set udg_DAMAGE_TYPE_DISEASE                 = 12
    set udg_DAMAGE_TYPE_DIVINE                  = 13
    set udg_DAMAGE_TYPE_MAGIC                   = 14
    set udg_DAMAGE_TYPE_SONIC                   = 15
    set udg_DAMAGE_TYPE_ACID                    = 16
    set udg_DAMAGE_TYPE_FORCE                   = 17
    set udg_DAMAGE_TYPE_DEATH                   = 18
    set udg_DAMAGE_TYPE_MIND                    = 19
    set udg_DAMAGE_TYPE_PLANT                   = 20
    set udg_DAMAGE_TYPE_DEFENSIVE               = 21
    set udg_DAMAGE_TYPE_DEMOLITION              = 22
    set udg_DAMAGE_TYPE_SLOW_POISON             = 23
    set udg_DAMAGE_TYPE_SPIRIT_LINK             = 24
    set udg_DAMAGE_TYPE_SHADOW_STRIKE           = 25
    set udg_DAMAGE_TYPE_UNIVERSAL               = 26
    
    set udg_HIT_TYPE_BASICATTACK                = 1
    set udg_HIT_TYPE_SPELLDAMAGE                = 2
    set udg_HIT_TYPE_PUREDAMAGE                 = 3
    
    set udg_ADE_Index                           = 0
    set udg_ADE_Param_CodedHitType              = udg_HIT_TYPE_SPELLDAMAGE
    
    call TimerStart(CreateTimer(), 0, false, function ADE_System_Initialize)
    
endfunction
JASS:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  
//  Native Hooks
//  
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
globals
    
    unit array          udg_ADE_CodedPointDamage_Source
    real array          udg_ADE_CodedPointDamage_Radius
    real array          udg_ADE_CodedPointDamage_X
    real array          udg_ADE_CodedPointDamage_Y
    real array          udg_ADE_CodedPointDamage_Amount
    integer array       udg_ADE_CodedPointDamage_AttackType
    integer array       udg_ADE_CodedPointDamage_DamageType
    integer array       udg_ADE_CodedPointDamage_HitType
    
endglobals

function ADE_RemoveCheatDeath takes unit whichUnit returns nothing
    local integer unitId = GetUnitUserData(whichUnit)
    local integer id
    
    if udg_ADE_TimerArray[unitId] != null then
        if UnitRemoveAbility(whichUnit, udg_ADE_ABILITY_CHEAT_DEATH) then
            set id = GetTimerData(udg_ADE_TimerArray[unitId])
            
            set udg_ADE_DontCheckForCheatDeath = true
            call SetWidgetLife(whichUnit, udg_ADE_HealthArray[id])
            set udg_ADE_DontCheckForCheatDeath = false
            
            call ReleaseIndexedTimer(udg_ADE_TimerArray[unitId])
            set udg_ADE_UnitArray[id] = null
            set udg_ADE_HealthArray[id] = 0.
            set udg_ADE_TimerArray[unitId] = null
        endif
    endif
endfunction

//Set life hooks.
function ADE_SetWidgetLife takes widget whichWidget, real newLife returns nothing
    if not udg_ADE_DontCheckForCheatDeath then
        call ADE_RemoveCheatDeath(Handle2Unit(whichWidget))
    endif
    return
endfunction
hook SetWidgetLife ADE_SetWidgetLife

function ADE_SetUnitLifePercentBJ takes unit whichUnit, real percent returns nothing
    call ADE_RemoveCheatDeath(whichUnit)
endfunction
hook SetUnitLifePercentBJ ADE_SetUnitLifePercentBJ

function ADE_SetUnitLifeBJ takes unit whichUnit, real newValue returns nothing
    call ADE_RemoveCheatDeath(whichUnit)
endfunction
hook SetUnitLifeBJ ADE_SetUnitLifeBJ

//Get life hooks.
function ADE_GetWidgetLife takes widget whichWidget returns real
    if not udg_ADE_DontCheckForCheatDeath then
        call ADE_RemoveCheatDeath(Handle2Unit(whichWidget))
    endif
    return 0.
endfunction
hook GetWidgetLife ADE_GetWidgetLife

function ADE_GetUnitLifePercent takes unit whichUnit returns real
    call ADE_RemoveCheatDeath(whichUnit)
    return 0.
endfunction
hook GetUnitLifePercent ADE_GetUnitLifePercent

//Unit state hooks.
function ADE_GetUnitStateSwap takes unitstate whichState, unit whichUnit returns real
    if whichState == UNIT_STATE_MAX_LIFE or whichState == UNIT_STATE_LIFE then
        call ADE_RemoveCheatDeath(whichUnit)
    endif
    return 0.
endfunction
hook GetUnitStateSwap ADE_GetUnitStateSwap


//Damage hooks.
function ADE_ApplyCodedDamage_Target takes unit whichUnit, widget target, real amount, boolean attack, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns boolean
    local integer nextIndex
    if udg_ADE_PreventCodedDamage then
        return false
    endif
    
    set nextIndex = ADE_CreateNewIndex()
    set udg_ADE_Param_Source[nextIndex] = whichUnit
    set udg_ADE_Param_Target[nextIndex] = Handle2Unit(target)
    set udg_ADE_Param_Amount[nextIndex] = amount
    set udg_ADE_Param_AttackType[nextIndex] = ConvertAttackTypeReverse(attackType)
    set udg_ADE_Param_DamageType[nextIndex] = ConvertDamageTypeReverse(damageType)
    set udg_ADE_Param_HitType[nextIndex] = udg_ADE_Param_CodedHitType
    call ADE_InvokeDamage(nextIndex)
    
    set udg_ADE_Param_CodedHitType = udg_HIT_TYPE_SPELLDAMAGE
    set udg_ADE_AbortNextDamage = udg_ADE_AbortNextDamage +1
    return false
endfunction
hook UnitDamageTarget ADE_ApplyCodedDamage_Target

function ADE_ApplyCodedDamage_TargetBJ takes unit whichUnit, unit target, real amount, attacktype attackType, damagetype damageType returns boolean
    local integer nextIndex
    
    set nextIndex = ADE_CreateNewIndex()
    set udg_ADE_Param_Source[nextIndex] = whichUnit
    set udg_ADE_Param_Target[nextIndex] = Handle2Unit(target)
    set udg_ADE_Param_Amount[nextIndex] = amount
    set udg_ADE_Param_AttackType[nextIndex] = ConvertAttackTypeReverse(attackType)
    set udg_ADE_Param_DamageType[nextIndex] = ConvertDamageTypeReverse(damageType)
    set udg_ADE_Param_HitType[nextIndex] = udg_ADE_Param_CodedHitType
    call ADE_InvokeDamage(nextIndex)
    
    set udg_ADE_Param_CodedHitType = udg_HIT_TYPE_SPELLDAMAGE
    set udg_ADE_AbortNextDamage = udg_ADE_AbortNextDamage +1
    return false
endfunction
hook UnitDamageTargetBJ ADE_ApplyCodedDamage_TargetBJ

function ADE_ApplyCodedDamage_PointCallback takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetTimerData(t)
    local group g = CreateGroup()
    local unit FoG
    local integer nextIndex
    local integer amount = 0
    
    call GroupEnumUnitsInRange(g, udg_ADE_CodedPointDamage_X[id], udg_ADE_CodedPointDamage_Y[id], udg_ADE_CodedPointDamage_Radius[id], null)
    loop
        set FoG = FirstOfGroup(g)
        exitwhen FoG == null
        call GroupRemoveUnit(g, FoG)
        
        set nextIndex = ADE_CreateNewIndex()
        set udg_ADE_Param_Source[nextIndex] = udg_ADE_CodedPointDamage_Source[id]
        set udg_ADE_Param_Target[nextIndex] = FoG
        set udg_ADE_Param_Amount[nextIndex] = udg_ADE_CodedPointDamage_Amount[id]
        set udg_ADE_Param_AttackType[nextIndex] = udg_ADE_CodedPointDamage_AttackType[id]
        set udg_ADE_Param_DamageType[nextIndex] = udg_ADE_CodedPointDamage_DamageType[id]
        set udg_ADE_Param_HitType[nextIndex] = udg_ADE_CodedPointDamage_HitType[id]
        call ADE_InvokeDamage(nextIndex)
        
        set amount = amount + 1
    endloop
    call DestroyGroup(g)
    set g = null
    
    set udg_ADE_CodedPointDamage_Source[id] = null
    set udg_ADE_Param_CodedHitType = udg_HIT_TYPE_SPELLDAMAGE
    set udg_ADE_AbortNextDamage = udg_ADE_AbortNextDamage + amount
    
    call ReleaseIndexedTimer(t)
    set t = null
endfunction

function ADE_ApplyCodedDamage_Point takes unit whichUnit, real delay, real radius, real x, real y, real amount, boolean attack, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns boolean
    local timer t = NewIndexedTimer()
    local integer id = GetTimerData(t)
    call TimerStart(t, delay, false, function ADE_ApplyCodedDamage_PointCallback)
    
    set udg_ADE_CodedPointDamage_Source[id] = whichUnit
    set udg_ADE_CodedPointDamage_Radius[id] = radius
    set udg_ADE_CodedPointDamage_X[id] = x
    set udg_ADE_CodedPointDamage_Y[id] = y
    set udg_ADE_CodedPointDamage_Amount[id] = amount
    set udg_ADE_CodedPointDamage_AttackType[id] = ConvertAttackTypeReverse(attackType)
    set udg_ADE_CodedPointDamage_DamageType[id] = ConvertDamageTypeReverse(damageType)
    set udg_ADE_CodedPointDamage_HitType[id] = udg_ADE_Param_CodedHitType
    
    set udg_ADE_Param_CodedHitType = udg_HIT_TYPE_SPELLDAMAGE
    set t = null
    return false
endfunction
hook UnitDamagePoint ADE_ApplyCodedDamage_Point

function ADE_ApplyCodedDamage_PointLoc takes unit whichUnit, real delay, real radius, location loc, real amount, attacktype attackType, damagetype damageType returns boolean
    local timer t = NewIndexedTimer()
    local integer id = GetTimerData(t)
    call TimerStart(t, delay, false, function ADE_ApplyCodedDamage_PointCallback)
    
    set udg_ADE_CodedPointDamage_Source[id] = whichUnit
    set udg_ADE_CodedPointDamage_Radius[id] = radius
    set udg_ADE_CodedPointDamage_X[id] = GetLocationX(loc)
    set udg_ADE_CodedPointDamage_Y[id] = GetLocationY(loc)
    set udg_ADE_CodedPointDamage_Amount[id] = amount
    set udg_ADE_CodedPointDamage_AttackType[id] = ConvertAttackTypeReverse(attackType)
    set udg_ADE_CodedPointDamage_DamageType[id] = ConvertDamageTypeReverse(damageType)
    set udg_ADE_CodedPointDamage_HitType[id] = udg_ADE_Param_CodedHitType
    
    set udg_ADE_Param_CodedHitType = udg_HIT_TYPE_SPELLDAMAGE
    set t = null
    return false
endfunction
hook UnitDamagePointLoc ADE_ApplyCodedDamage_PointLoc

I have also made a Combat text extension so you can easily visually see the damage:
JASS:
globals
    
    integer array       udg_ADE_CombatText_Color_Blue
    integer array       udg_ADE_CombatText_Color_Green
    integer array       udg_ADE_CombatText_Color_Red
    string array        udg_ADE_CombatText_String
    
endglobals

library combatText
    
    function CreateCombatText takes string text, real size, real x, real y, integer red, integer green, integer blue returns nothing
        local texttag tt = CreateTextTag()
        local real angle = GetRandomReal(0, 90) + GetRandomReal(0, 90)
        
        call SetTextTagText(tt, text, size)
        call SetTextTagPos(tt, x, y, 40)
        call SetTextTagColor(tt, red, green, blue, 255)
        call SetTextTagVelocity(tt, 0.071 * Cos(angle * bj_DEGTORAD), 0.071 * Sin(angle * bj_DEGTORAD))
        call SetTextTagPermanent(tt, false)
        call SetTextTagLifespan(tt, 1.3)
        call SetTextTagFadepoint(tt, 0.9)
        
        set tt = null
    endfunction
    
endlibrary

function ADE_CreateCombatText takes nothing returns boolean
    local real size
    
    if udg_ADE_Param_Blocked[udg_ADE_Index] then
        set size = 0.0192
        if udg_ADE_CombatText_String[udg_ADE_Index] == null then
            set udg_ADE_CombatText_String[udg_ADE_Index] = "blocked"
        endif
        if udg_ADE_CombatText_Color_Red[udg_ADE_Index] > 255 then
            set udg_ADE_CombatText_Color_Red[udg_ADE_Index] = 155
            set udg_ADE_CombatText_Color_Green[udg_ADE_Index] = 155
            set udg_ADE_CombatText_Color_Blue[udg_ADE_Index] = 155
        endif
    else
        set size = 0.0158 + 0.00006 * udg_ADE_Param_ActualAmount[udg_ADE_Index]
        set udg_ADE_CombatText_String[udg_ADE_Index] = I2S(R2I(udg_ADE_Param_ActualAmount[udg_ADE_Index]))
        if udg_ADE_CombatText_Color_Red[udg_ADE_Index] > 255 then
            //three of my favorite colors:
            if udg_ADE_Param_DamageType[udg_ADE_Index] == udg_DAMAGE_TYPE_NORMAL then
                set udg_ADE_CombatText_Color_Red[udg_ADE_Index] = 255
                set udg_ADE_CombatText_Color_Green[udg_ADE_Index] = 128
                set udg_ADE_CombatText_Color_Blue[udg_ADE_Index] = 0
            elseif udg_ADE_Param_DamageType[udg_ADE_Index] == udg_DAMAGE_TYPE_MAGIC then
                set udg_ADE_CombatText_Color_Red[udg_ADE_Index] = 255
                set udg_ADE_CombatText_Color_Green[udg_ADE_Index] = 0
                set udg_ADE_CombatText_Color_Blue[udg_ADE_Index] = 255
            elseif udg_ADE_Param_DamageType[udg_ADE_Index] == udg_DAMAGE_TYPE_UNIVERSAL then
                set udg_ADE_CombatText_Color_Red[udg_ADE_Index] = 255
                set udg_ADE_CombatText_Color_Green[udg_ADE_Index] = 255
                set udg_ADE_CombatText_Color_Blue[udg_ADE_Index] = 255
            endif
        endif
    endif
    if size > 0.345 then
        set size = 0.345
    endif
    
    call CreateCombatText(udg_ADE_CombatText_String[udg_ADE_Index], size, GetWidgetX(udg_ADE_Param_Target[udg_ADE_Index]), GetWidgetY(udg_ADE_Param_Target[udg_ADE_Index]), udg_ADE_CombatText_Color_Red[udg_ADE_Index], udg_ADE_CombatText_Color_Green[udg_ADE_Index], udg_ADE_CombatText_Color_Blue[udg_ADE_Index])
    
    return false
endfunction

function ADE_InitCombatText takes nothing returns boolean
    
    set udg_ADE_CombatText_String[udg_ADE_NewIndex]         = null
    set udg_ADE_CombatText_Color_Red[udg_ADE_NewIndex]      = 256
    set udg_ADE_CombatText_Color_Green[udg_ADE_NewIndex]    = 256
    set udg_ADE_CombatText_Color_Blue[udg_ADE_NewIndex]     = 256
    
    return false
endfunction

//===========================================================================
function InitTrig_ADE_Combat_Text_Extension takes nothing returns nothing
    local trigger t
    
    set t = CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_ADE_Event_Damage_Ended", EQUAL, 0)
    call TriggerAddCondition(t, Filter(function ADE_CreateCombatText))
    
    set t = CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_ADE_Event_Damage_Created", EQUAL, 0)
    call TriggerAddCondition(t, Filter(function ADE_InitCombatText))
    
    set t = null
endfunction

There are also a few spells that show you how the system should be used.
Originally, the system was not intended to do the detection of damage so there is a template for physical spell damage for example:
  • MySpell
    • Events
    • Conditions
    • Actions
      • -------- PHYSICAL SPELL DAMAGE TEMPLATE --------
      • Custom script: call ADE_CreateNewIndex()
      • Set ADE_Param_Amount[ADE_NewIndex] = 100.00
      • Set ADE_Param_AttackType[ADE_NewIndex] = ATTACK_TYPE_MELEE
      • Set ADE_Param_DamageType[ADE_NewIndex] = DAMAGE_TYPE_NORMAL
      • Set ADE_Param_HitType[ADE_NewIndex] = HIT_TYPE_SPELLDAMAGE
      • Set ADE_Param_Source[ADE_NewIndex] = (Triggering unit)
      • Set ADE_Param_Target[ADE_NewIndex] = (Target unit of ability being cast)
      • Custom script: call ADE_InvokeDamage(udg_ADE_NewIndex)
This trigger could right now simply be replaced by the simple GUI function:
  • Unit - Cause (Triggering unit) to damage (Target unit of ability being cast), dealing 100.00 damage of attack type Normal and damage type Normal
Which will do EXACTLY the same.

Also, the paladin is invulnerable for fatal damage (damage that would reduce health below 0).
  • Paladin Block Fatal Damage
    • Events
      • Game - ADE_Event_Fatal_Damage becomes Equal to 0.00
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of ADE_Param_Target[ADE_Index]) Equal to Paladin
        • Then - Actions
          • Game - Display to (All players) the text: paladin took fatal ...
          • Set ADE_Param_PreventFatalDamage[ADE_Index] = True
        • Else - Actions
The Far Seer will heal himself for 50% of the damage that has been dealt:
  • Far Seer Life Steal
    • Events
      • Game - ADE_Event_OnDamage_After becomes Equal to 0.00
    • Conditions
    • Actions
      • Custom script: local effect localEffect
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of ADE_Param_Source[ADE_Index]) Equal to Far Seer
          • ADE_Param_DamageType[ADE_Index] Equal to DAMAGE_TYPE_NORMAL
        • Then - Actions
          • -------- Somehow, heal doesnt play death animation when immediately destroyed. --------
          • -------- Could be that it has no death animation. --------
          • Special Effect - Create a special effect attached to the origin of ADE_Param_Source[ADE_Index] using Abilities\Spells\Human\Heal\HealTarget.mdl
          • Custom script: set localEffect = bj_lastCreatedEffect
          • Unit - Set life of ADE_Param_Source[ADE_Index] to ((Life of ADE_Param_Source[ADE_Index]) + (0.50 x ADE_Param_ActualAmount[ADE_Index]))
          • Wait 2.00 seconds
          • Custom script: call DestroyEffect(localEffect)
          • Custom script: set localEffect = null
        • Else - Actions
Sorry for the wait and stuff but that special effect just has no death animation I guess.

And the Paladin takes recoil damage on his basic attacks equal to 30% of the damage dealt:
  • Paladin Basic Attack Recoil
    • Events
      • Game - ADE_Event_OnDamage_After becomes Equal to 0.00
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of ADE_Param_Source[ADE_Index]) Equal to Paladin
          • ADE_Param_HitType[ADE_Index] Equal to HIT_TYPE_BASICATTACK
        • Then - Actions
          • Unit - Cause ADE_Param_Source[ADE_Index] to damage ADE_Param_Source[ADE_Index], dealing (0.30 x ADE_Param_ActualAmount[ADE_Index]) damage of attack type Normal and damage type Normal
        • Else - Actions
If there are any suggestions for either new functionalities or renames, then please tell them.

(The GetDamageTypeFactor() etc, are the functions that slow down this system dramatically.
I made those to get as close as possible to the original damage which could be very crucial in some spells. (just for perfection)
But as you might understand, it is actually required for detection of damage.)


JASS:
globals
    hashtable udg_TypeCastHashtable = InitHashtable()
endglobals

library typecasting
    
    //BooleanExpr
    function Integer2BooleanExpr takes integer i returns boolexpr
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadBooleanExprHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2BooleanExpr takes handle h returns boolexpr
        return Integer2BooleanExpr(GetHandleId(h))
    endfunction
    function BooleanExpr2Handle takes boolexpr b returns handle
        return b
    endfunction
    
    //Button
    function Integer2Button takes integer i returns button
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadButtonHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Button takes handle h returns button
        return Integer2Button(GetHandleId(h))
    endfunction
    function Button2Handle takes button b returns handle
        return b
    endfunction
    
    //DefeatCondition
    function Integer2DefeatCondition takes integer i returns defeatcondition
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadDefeatConditionHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2DefeatCondition takes handle h returns defeatcondition
        return Integer2DefeatCondition(GetHandleId(h))
    endfunction
    function DefeatCondition2Handle takes defeatcondition d returns handle
        return d
    endfunction
    
    //Destructable
    function Integer2Destructable takes integer i returns destructable
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadDestructableHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Destructable takes handle h returns destructable
        return Integer2Destructable(GetHandleId(h))
    endfunction
    function Destructable2Handle takes destructable d returns handle
        return d
    endfunction
    
    //Dialog
    function Integer2Dialog takes integer i returns dialog
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadDialogHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Dialog takes handle h returns dialog
        return Integer2Dialog(GetHandleId(h))
    endfunction
    function Dialog2Handle takes dialog d returns handle
        return d
    endfunction
    
    //Effect
    function Integer2Effect takes integer i returns effect
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadEffectHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Effect takes handle h returns effect
        return Integer2Effect(GetHandleId(h))
    endfunction
    function Effect2Handle takes effect e returns handle
        return e
    endfunction
    
    //FogModifier
    function Integer2FogModifier takes integer i returns fogmodifier
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadFogModifierHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2FogModifier takes handle h returns fogmodifier
        return Integer2FogModifier(GetHandleId(h))
    endfunction
    function FogModifier2Handle takes fogmodifier f returns handle
        return f
    endfunction
    
    //FogState
    function Integer2FogState takes integer i returns fogstate
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadFogStateHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2FogState takes handle h returns fogstate
        return Integer2FogState(GetHandleId(h))
    endfunction
    function FogState2Handle takes fogstate f returns handle
        return f
    endfunction
    
    //Force
    function Integer2Force takes integer i returns force
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadForceHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Force takes handle h returns force
        return Integer2Force(GetHandleId(h))
    endfunction
    function Force2Handle takes force f returns handle
        return f
    endfunction
    
    //Group
    function Integer2Group takes integer i returns group
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadGroupHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Group takes handle h returns group
        return Integer2Group(GetHandleId(h))
    endfunction
    function Group2Handle takes group g returns handle
        return g
    endfunction
    
    //Hashtable
    function Integer2Hashtable takes integer i returns hashtable
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadHashtableHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Hashtable takes handle h returns hashtable
        return Integer2Hashtable(GetHandleId(h))
    endfunction
    function Hashtable2Handle takes hashtable h returns handle
        return h
    endfunction
    
    //Image
    function Integer2Image takes integer i returns image
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadImageHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Image takes handle h returns image
        return Integer2Image(GetHandleId(h))
    endfunction
    function Image2Handle takes image i returns handle
        return i
    endfunction
    
    //Item
    function Integer2Item takes integer i returns item
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadItemHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Item takes handle h returns item
        return Integer2Item(GetHandleId(h))
    endfunction
    function Item2Handle takes item i returns handle
        return i
    endfunction
    
    //ItemPool
    function Integer2ItemPool takes integer i returns itempool
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadItemPoolHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2ItemPool takes handle h returns itempool
        return Integer2ItemPool(GetHandleId(h))
    endfunction
    function ItemPool2Handle takes itempool i returns handle
        return i
    endfunction
    
    //Leaderboard
    function Integer2Leaderboard takes integer i returns leaderboard
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadLeaderboardHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Leaderboard takes handle h returns leaderboard
        return Integer2Leaderboard(GetHandleId(h))
    endfunction
    function Leaderboard2Handle takes leaderboard l returns handle
        return l
    endfunction
    
    //Lightning
    function Integer2Lightning takes integer i returns lightning
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadLightningHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Lightning takes handle h returns lightning
        return Integer2Lightning(GetHandleId(h))
    endfunction
    function Lightning2Handle takes lightning l returns handle
        return l
    endfunction
    
    //Location
    function Integer2Location takes integer i returns location
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadLocationHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Location takes handle h returns location
        return Integer2Location(GetHandleId(h))
    endfunction
    function Location2Handle takes location l returns handle
        return l
    endfunction
    
    //Multiboard
    function Integer2Multiboard takes integer i returns multiboard
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadMultiboardHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Multiboard takes handle h returns multiboard
        return Integer2Multiboard(GetHandleId(h))
    endfunction
    function Multiboard2Handle takes multiboard m returns handle
        return m
    endfunction
    
    //MultiboardItem
    function Integer2MultiboardItem takes integer i returns multiboarditem
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadMultiboardItemHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2MultiboardItem takes handle h returns multiboarditem
        return Integer2MultiboardItem(GetHandleId(h))
    endfunction
    function MultiboardItem2Handle takes multiboarditem m returns handle
        return m
    endfunction
    
    //Player
    function Integer2Player takes integer i returns player
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadPlayerHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Player takes handle h returns player
        return Integer2Player(GetHandleId(h))
    endfunction
    function Player2Handle takes player p returns handle
        return p
    endfunction
    
    //Quest
    function Integer2Quest takes integer i returns quest
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadQuestHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Quest takes handle h returns quest
        return Integer2Quest(GetHandleId(h))
    endfunction
    function Quest2Handle takes quest q returns handle
        return q
    endfunction
    
    //QuestItem
    function Integer2QuestItem takes integer i returns questitem
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadQuestItemHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2QuestItem takes handle h returns questitem
        return Integer2QuestItem(GetHandleId(h))
    endfunction
    function QuestItem2Handle takes questitem q returns handle
        return q
    endfunction
    
    //Rect
    function Integer2Rect takes integer i returns rect
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadRectHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Rect takes handle h returns rect
        return Integer2Rect(GetHandleId(h))
    endfunction
    function Rect2Handle takes rect r returns handle
        return r
    endfunction
    
    //Region
    function Integer2Region takes integer i returns region
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadRegionHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Region takes handle h returns region
        return Integer2Region(GetHandleId(h))
    endfunction
    function Region2Handle takes region r returns handle
        return r
    endfunction
    
    //Sound
    function Integer2Sound takes integer i returns sound
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadSoundHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Sound takes handle h returns sound
        return Integer2Sound(GetHandleId(h))
    endfunction
    function Sound2Handle takes sound s returns handle
        return s
    endfunction
    
    //TextTag
    function Integer2TextTag takes integer i returns texttag
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadTextTagHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2TextTag takes handle h returns texttag
        return Integer2TextTag(GetHandleId(h))
    endfunction
    function TextTag2Handle takes texttag t returns handle
        return t
    endfunction
    
    //Timer
    function Integer2Timer takes integer i returns timer
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadTimerHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Timer takes handle h returns timer
        return Integer2Timer(GetHandleId(h))
    endfunction
    function Timer2Handle takes timer t returns handle
        return t
    endfunction
    
    //TimerDialog
    function Integer2TimerDialog takes integer i returns timerdialog
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadTimerDialogHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2TimerDialog takes handle h returns timerdialog
        return Integer2TimerDialog(GetHandleId(h))
    endfunction
    function TimerDialog2Handle takes timerdialog t returns handle
        return t
    endfunction
    
    //Trackable
    function Integer2Trackable takes integer i returns trackable
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadTrackableHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Trackable takes handle h returns trackable
        return Integer2Trackable(GetHandleId(h))
    endfunction
    function Trackable2Handle takes trackable t returns handle
        return t
    endfunction
    
    //Trigger
    function Integer2Trigger takes integer i returns trigger
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadTriggerHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2WTrigger takes handle h returns trigger
        return Integer2Trigger(GetHandleId(h))
    endfunction
    function Trigger2Handle takes trigger t returns handle
        return t
    endfunction
    
    //TriggerAction
    function Integer2TriggerAction takes integer i returns triggeraction
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadTriggerActionHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2TriggerAction takes handle h returns triggeraction
        return Integer2TriggerAction(GetHandleId(h))
    endfunction
    function TriggerAction2Handle takes triggeraction t returns handle
        return t
    endfunction
    
    //TriggerCondition
    function Integer2TriggerCondition takes integer i returns triggercondition
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadTriggerConditionHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2TriggerCondition takes handle h returns triggercondition
        return Integer2TriggerCondition(GetHandleId(h))
    endfunction
    function TriggerCondition2Handle takes triggercondition t returns handle
        return t
    endfunction
    
    //TriggerEvent
    function Integer2TriggerEvent takes integer i returns event
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadTriggerEventHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2TriggerEvent takes handle h returns event
        return Integer2TriggerEvent(GetHandleId(h))
    endfunction
    function TriggerEvent2Handle takes event e returns handle
        return e
    endfunction
    
    //Ubersplat
    function Integer2Ubersplat takes integer i returns ubersplat
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadUbersplatHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Ubersplat takes handle h returns ubersplat
        return Integer2Ubersplat(GetHandleId(h))
    endfunction
    function Ubersplat2Handle takes ubersplat u returns handle
        return u
    endfunction
    
    //Unit
    function Integer2Unit takes integer i returns unit
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadUnitHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Unit takes handle h returns unit
        return Integer2Unit(GetHandleId(h))
    endfunction
    function Unit2Handle takes unit u returns handle
        return u
    endfunction
    
    //UnitPool
    function Integer2UnitPool takes integer i returns unitpool
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadUnitPoolHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2UnitPool takes handle h returns unitpool
        return Integer2UnitPool(GetHandleId(h))
    endfunction
    function UnitPool2Handle takes unitpool u returns handle
        return u
    endfunction
    
    //Widget
    function Integer2Widget takes integer i returns widget
        call SaveFogStateHandle(udg_TypeCastHashtable, 0, 0, ConvertFogState(i))
        return LoadWidgetHandle(udg_TypeCastHashtable, 0, 0)
    endfunction
    function Handle2Widget takes handle h returns widget
        return Integer2Widget(GetHandleId(h))
    endfunction
    function Widget2Handle takes widget w returns handle
        return w
    endfunction
    
endlibrary
JASS:
library convertion
    function ConvertAttackTypeReverse takes attacktype at returns integer
        if at == ATTACK_TYPE_NORMAL then
            return 0
        elseif at == ATTACK_TYPE_MELEE then
            return 1
        elseif at == ATTACK_TYPE_PIERCE then
            return 2
        elseif at == ATTACK_TYPE_SIEGE then
            return 3
        elseif at == ATTACK_TYPE_MAGIC then
            return 4
        elseif at == ATTACK_TYPE_CHAOS then
            return 5
        elseif at == ATTACK_TYPE_HERO then
            return 6
        endif
        return 0
    endfunction

    function ConvertDamageTypeReverse takes damagetype dt returns integer
        if dt == DAMAGE_TYPE_UNKNOWN then
            return 0
        elseif dt == DAMAGE_TYPE_NORMAL then
            return 4
        elseif dt == DAMAGE_TYPE_ENHANCED then
            return 5
        elseif dt == DAMAGE_TYPE_FIRE then
            return 8
        elseif dt == DAMAGE_TYPE_COLD then
            return 9
        elseif dt == DAMAGE_TYPE_LIGHTNING then
            return 10
        elseif dt == DAMAGE_TYPE_POISON then
            return 11
        elseif dt == DAMAGE_TYPE_DISEASE then
            return 12
        elseif dt == DAMAGE_TYPE_DIVINE then
            return 13
        elseif dt == DAMAGE_TYPE_MAGIC then
            return 14
        elseif dt == DAMAGE_TYPE_SONIC then
            return 15
        elseif dt == DAMAGE_TYPE_ACID then
            return 16
        elseif dt == DAMAGE_TYPE_FORCE then
            return 17
        elseif dt == DAMAGE_TYPE_DEATH then
            return 18
        elseif dt == DAMAGE_TYPE_MIND then
            return 19
        elseif dt == DAMAGE_TYPE_PLANT then
            return 20
        elseif dt == DAMAGE_TYPE_DEFENSIVE then
            return 21
        elseif dt == DAMAGE_TYPE_DEMOLITION then
            return 22
        elseif dt == DAMAGE_TYPE_SLOW_POISON then
            return 23
        elseif dt == DAMAGE_TYPE_SPIRIT_LINK then
            return 24
        elseif dt == DAMAGE_TYPE_SHADOW_STRIKE then
            return 25
        elseif dt == DAMAGE_TYPE_UNIVERSAL then
            return 26
        endif
        return 0
    endfunction
endlibrary
JASS:
library timerIndex uses TimerUtils
//*********************************************************************
//* TimerIndex 1.0
//* ----------
//*
//*  This library creates a unique index for the TimerData of timerUtils.
//*  A timer that is created with an index must also be released as an indexed timer.
//*
    
    globals
        integer udg_NextTimerIndex = 0
        boolean array udg_TimerIndex_Occupied
    endglobals
    
    function ReleaseIndexedTimer takes timer t returns nothing
        set udg_TimerIndex_Occupied[GetTimerData(t)] = false
        call ReleaseTimer(t)
    endfunction
    function NewIndexedTimer takes nothing returns timer
        loop
            set udg_NextTimerIndex = udg_NextTimerIndex + 1
            if udg_NextTimerIndex > 8191 then
                set udg_NextTimerIndex = 1
            endif
            
            exitwhen not udg_TimerIndex_Occupied[udg_NextTimerIndex]
        endloop
        
        set udg_TimerIndex_Occupied[udg_NextTimerIndex] = true
        return NewTimerEx(udg_NextTimerIndex)
    endfunction
    
endlibrary
JASS:
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//*  To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//*  To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass)   More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//*  * Attaching
//*  * Recycling (with double-free protection)
//*
//* set t=NewTimer()      : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x)   : Get a timer (alternative to CreateTimer), call
//*                            Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t)       : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2)     : Attach value 2 to timer
//* GetTimerData(t)       : Get the timer's value.
//*                         You can assume a timer's value is 0
//*                         after NewTimer.
//*
//* Multi-flavor:
//*    Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************

//================================================================
    globals
        //How to tweak timer utils:
        // USE_HASH_TABLE = true  (new blue)
        //  * SAFEST
        //  * SLOWEST (though hash tables are kind of fast)
        //
        // USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true  (orange)
        //  * kinda safe (except there is a limit in the number of timers)
        //  * ALMOST FAST
        //
        // USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
        //  * THE FASTEST (though is only  faster than the previous method
        //                  after using the optimizer on the map)
        //  * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
        //                     work)
        //
        private constant boolean USE_HASH_TABLE      = true
        private constant boolean USE_FLEXIBLE_OFFSET = false

        private constant integer OFFSET     = 0x100000
        private          integer VOFFSET    = OFFSET
              
        //Timers to preload at map init:
        private constant integer QUANTITY   = 256
        
        //Changing this  to something big will allow you to keep recycling
        // timers even when there are already AN INCREDIBLE AMOUNT of timers in
        // the stack. But it will make things far slower so that's probably a bad idea...
        private constant integer ARRAY_SIZE = 8190

    endglobals

    //==================================================================================================
    globals
        private integer array data[ARRAY_SIZE]
        private hashtable     ht
    endglobals
    
    

    //It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
    function SetTimerData takes timer t, integer value returns nothing
        static if(USE_HASH_TABLE) then
            // new blue
            call SaveInteger(ht,0,GetHandleId(t), value)
            
        elseif (USE_FLEXIBLE_OFFSET) then
            // orange
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-VOFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            set data[GetHandleId(t)-VOFFSET]=value
        else
            // new red
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-OFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            set data[GetHandleId(t)-OFFSET]=value
        endif        
    endfunction

    function GetTimerData takes timer t returns integer
        static if(USE_HASH_TABLE) then
            // new blue
            return LoadInteger(ht,0,GetHandleId(t) )
            
        elseif (USE_FLEXIBLE_OFFSET) then
            // orange
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-VOFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            return data[GetHandleId(t)-VOFFSET]
        else
            // new red
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-OFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            return data[GetHandleId(t)-OFFSET]
        endif        
    endfunction

    //==========================================================================================
    globals
        private timer array tT[ARRAY_SIZE]
        private integer tN = 0
        private constant integer HELD=0x28829022
        //use a totally random number here, the more improbable someone uses it, the better.
        
        private boolean       didinit = false
    endglobals
    private keyword init

    //==========================================================================================
    // I needed to decide between duplicating code ignoring the "Once and only once" rule
    // and using the ugly textmacros. I guess textmacros won.
    //
    //! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
    // On second thought, no.
    //! endtextmacro

    function NewTimerEx takes integer value returns timer
        if (tN==0) then
            if (not didinit) then 
                //This extra if shouldn't represent a major performance drawback
                //because QUANTITY rule is not supposed to be broken every day. 
                call init.evaluate()
                set tN = tN - 1
            else
                //If this happens then the QUANTITY rule has already been broken, try to fix the
                // issue, else fail.
                debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
                set tT[0]=CreateTimer()
                static if( not USE_HASH_TABLE) then
                    debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
                    static if( USE_FLEXIBLE_OFFSET) then
                        if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
                            //all right, couldn't fix it
                            call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
                            return null
                        endif
                    else
                        if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
                            //all right, couldn't fix it
                            call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
                            return null
                        endif
                    endif
                endif
            endif
        else
            set tN=tN-1
        endif
        call SetTimerData(tT[tN],value)
     return tT[tN]
    endfunction
    
    function NewTimer takes nothing returns timer
        return NewTimerEx(0)
    endfunction


    //==========================================================================================
    function ReleaseTimer takes timer t returns nothing
        if(t==null) then
            debug call BJDebugMsg("Warning: attempt to release a null timer")
            return
        endif
        if (tN==ARRAY_SIZE) then
            debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")

            //stack is full, the map already has much more troubles than the chance of bug
            call DestroyTimer(t)
        else
            call PauseTimer(t)
            if(GetTimerData(t)==HELD) then
                debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
                return
            endif
            call SetTimerData(t,HELD)
            set tT[tN]=t
            set tN=tN+1
        endif    
    endfunction

    private function init takes nothing returns nothing
     local integer i=0
     local integer o=-1
     local boolean oops = false
        if ( didinit ) then
            return
        else
            set didinit = true
        endif
     
        static if( USE_HASH_TABLE ) then
            set ht = InitHashtable()
            loop
                exitwhen(i==QUANTITY)
                set tT[i]=CreateTimer()
                call SetTimerData(tT[i], HELD)
                set i=i+1
            endloop
            set tN = QUANTITY
        else
            loop
                set i=0
                loop
                    exitwhen (i==QUANTITY)
                    set tT[i] = CreateTimer()
                    if(i==0) then
                        set VOFFSET = GetHandleId(tT[i])
                        static if(USE_FLEXIBLE_OFFSET) then
                            set o=VOFFSET
                        else
                            set o=OFFSET
                        endif
                    endif
                    if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
                        exitwhen true
                    endif
                    if (GetHandleId(tT[i])-o>=0)  then
                        set i=i+1
                    endif
                endloop
                set tN = i
                exitwhen(tN == QUANTITY)
                set oops = true
                exitwhen not USE_FLEXIBLE_OFFSET
                debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")               
            endloop
            
            if(oops) then
                static if ( USE_FLEXIBLE_OFFSET) then
                    debug call BJDebugMsg("The problem has been fixed.")
                    //If this message doesn't appear then there is so much
                    //handle id fragmentation that it was impossible to preload
                    //so many timers and the thread crashed! Therefore this
                    //debug message is useful.
                elseif(DEBUG_MODE) then
                    call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
                    call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
                    call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
                    call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
                endif
            endif
        endif

    endfunction

endlibrary
  • Unit Indexer
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Custom script: call ExecuteFunc("InitializeUnitIndexer")
      • Custom script: endfunction
      • -------- --------
      • -------- This is the most important function - it provides an index for units as they enter the map --------
      • -------- --------
      • Custom script: function IndexUnit takes nothing returns boolean
      • Custom script: local integer pdex = udg_UDex
      • -------- --------
      • -------- You can use the boolean UnitIndexerEnabled to protect some of your undesirable units from being indexed --------
      • -------- - Example: --------
      • -------- -- Set UnitIndexerEnabled = False --------
      • -------- -- Unit - Create 1 Dummy for (Triggering player) at TempLoc facing 0.00 degrees --------
      • -------- -- Set UnitIndexerEnabled = True --------
      • -------- --------
      • -------- You can also customize the following block - if conditions are false the (Matching unit) won't be indexed. --------
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • UnitIndexerEnabled Equal to True
        • Then - Actions
          • -------- --------
          • -------- Generate a unique integer index for this unit --------
          • -------- --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • UDexRecycle Equal to 0
            • Then - Actions
              • Set UDex = (UDexGen + 1)
              • Set UDexGen = UDex
            • Else - Actions
              • Set UDex = UDexRecycle
              • Set UDexRecycle = UDexNext[UDex]
          • -------- --------
          • -------- Link index to unit, unit to index --------
          • -------- --------
          • Set UDexUnits[UDex] = (Matching unit)
          • Unit - Set the custom value of UDexUnits[UDex] to UDex
          • -------- --------
          • -------- Use a doubly-linked list to store all active indexes --------
          • -------- --------
          • Set UDexPrev[UDexNext[0]] = UDex
          • Set UDexNext[UDex] = UDexNext[0]
          • Set UDexNext[0] = UDex
          • -------- --------
          • -------- Fire index event for UDex --------
          • -------- --------
          • Set UnitIndexEvent = 0.00
          • Set UnitIndexEvent = 1.00
          • Set UnitIndexEvent = 0.00
          • Custom script: set udg_UDex = pdex
        • Else - Actions
      • Custom script: return false
      • Custom script: endfunction
      • -------- --------
      • -------- The next function is called each time a unit enters the map --------
      • -------- --------
      • Custom script: function IndexNewUnit takes nothing returns boolean
      • Custom script: local integer pdex = udg_UDex
      • Custom script: local integer ndex
      • -------- --------
      • -------- Recycle indices of units no longer in-play every (15) units created --------
      • -------- --------
      • Set UDexWasted = (UDexWasted + 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • UDexWasted Equal to 15
        • Then - Actions
          • Set UDexWasted = 0
          • Set UDex = UDexNext[0]
          • Custom script: loop
          • Custom script: exitwhen udg_UDex == 0
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Custom value of UDexUnits[UDex]) Equal to 0
            • Then - Actions
              • -------- --------
              • -------- Remove index from linked list --------
              • -------- --------
              • Custom script: set ndex = udg_UDexNext[udg_UDex]
              • Custom script: set udg_UDexNext[udg_UDexPrev[udg_UDex]] = ndex
              • Custom script: set udg_UDexPrev[ndex] = udg_UDexPrev[udg_UDex]
              • Set UDexPrev[UDex] = 0
              • -------- --------
              • -------- Fire deindex event for UDex --------
              • -------- --------
              • Set UnitIndexEvent = 2.00
              • Set UnitIndexEvent = 0.00
              • -------- --------
              • -------- Recycle the index for later use --------
              • -------- --------
              • Set UDexUnits[UDex] = No unit
              • Set UDexNext[UDex] = UDexRecycle
              • Set UDexRecycle = UDex
              • Custom script: set udg_UDex = ndex
            • Else - Actions
              • Set UDex = UDexNext[UDex]
          • Custom script: endloop
          • Custom script: set udg_UDex = pdex
        • Else - Actions
      • -------- --------
      • -------- Handle the entering unit (Matching unit) --------
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Custom value of (Matching unit)) Equal to 0
        • Then - Actions
          • Custom script: call IndexUnit()
        • Else - Actions
      • Custom script: return false
      • Custom script: endfunction
      • -------- --------
      • -------- The next function initializes the core of the system --------
      • -------- --------
      • Custom script: function InitializeUnitIndexer takes nothing returns nothing
      • Custom script: local integer i = 0
      • Custom script: local region re = CreateRegion()
      • Custom script: local rect r = GetWorldBounds()
      • Set UnitIndexerEnabled = True
      • Custom script: call RegionAddRect(re, r)
      • Custom script: call TriggerRegisterEnterRegion(CreateTrigger(), re, Filter(function IndexNewUnit))
      • Custom script: call RemoveRect(r)
      • Custom script: set re = null
      • Custom script: set r = null
      • Custom script: loop
      • Custom script: call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, Player(i), Filter(function IndexUnit))
      • Custom script: set i = i + 1
      • Custom script: exitwhen i == 16
      • Custom script: endloop
      • -------- --------
      • -------- This is the "Unit Indexer Initialized" event, use it instead of "Map Initialization" for best results --------
      • -------- --------
      • Set UnitIndexEvent = 3.00
      • Set UnitIndexEvent = 0.00


I forgot to do the credits:
- to lookin_for_help for the idea of the reversed spell damage and making artillery damage explode units.
- to Nestharus and edo494 for helping with the concept and structure.
- to Bribe for his Unit Indexer.

I hope that I have said everything that I wanted to say... I actually start to forget things when things get so complicated as this.

Map attached in comment.
 
Last edited:
Level 24
Joined
Aug 1, 2013
Messages
4,657
Sry but nobody needs a new DDS... There're enough which do a perfect job.
Check the Spells Section, pick one of them and don't waste your time with a new implementation :)
Uhm... nope.
I did check the spell section, and when I was there, I could only find bugged damage systems.
I dont waste my time if something perfect already exists... but there wasn't even one.

For example:
lfh's PDD does not do a perfect job.
But neither does mine. (the proc speed is low)

In his version, you are forced to trigger all the Spell Reduction abilities.
In mine, you can still but you can also use the normal ones.

In his version, you are forced to use his custom script for triggered damage...
One thing though... GUI friendly?
If you are using custom scripts to call a function with handle parameters... then your system is NOT GUI friendly.
Mine is.
In fact, mine works 100% with the UnitDamageTarget, UnitDamageTargetBJ (GUI), UnitDamagePoint and UnitDamagePointLoc (GUI).
So if you actually have a map in which you have a lot of those things, you can implement mine and your map will still work perfectly and the system will detect the damage also perfectly.

Also mine can tell the difference between Physical triggered damage and basic attacks.
(with the GUI UnitDamageTargetBJ function)

His version will break when you apply damage during the onDamage event.
Mine will not.

His version does not know the difference between 0 damage and blocked damage.
In my version, there is a very big difference and there are definately spells that depend on it.
My version also has a very simple option to prevent fatal damage (damage that would reduce health below 0).
My version has events for blocked damage, before damage and after damage.
(to the ones that think about that the after damage event should run in reverse order...)

There is one thing to start with.
This is NOT an event system.
So there is nothing that I should do to change the events themselves.
Also, this is the easiest way for GUI users to simulate an event and as we all know, this is a GUI friendly resource.

But next to that, there is a difference between an event that stops and an onAfter event.
What I use are two separate events: onDamageBefore and onDamageAfter.
That is the EXACT SAME as "A unit begins casting an ability" and "A unit finishes casting an ability".
The onDamageBefore event fires before the damage has been taken, while the target is still alive, when you can change the damage amount, attack type, damage type, blocked flag, even the source and target. (Did not test that though. That is one of the things that I would like to hear from you.)
The onDamageAfter event is used to apply spell effects like Bash' stun or Poison.
You know that the attack has landed successfully and therefor you have a perfect event to create effects that would occur on those situations.
Also, this is the event where you know how much damage is actually dealt. (Reduced by armor and attack type.)

So to clarify, these are DIFFERENT events with DIFFERENT actions and DIFFERENT purpose.
These are NOT a creation of an event that should "deallocate" on the onDamageAfter event.
If you want to do that, you will create an Event system which allows you to add event responses to the beginning or on the end of the event.
Or you take one that can have priorities if you want to.
I have an event system which can do both, but it is not GUI friendly and it is still a bit slow (and pretty much under development).
So if you come to me with the bullshit about that I have to run the onDamageAfter event in reverse order, then I will give you bullshit back because you do NOT want a DDS but an event system.

Thanks,


In my version, you are able to change the damage and attack type of a damage.
In my version, you are able to know how much damage the attacker wanted to deal.
(With one exception and that is the damage factor of attack types in basic attacks.)

So I think it is safe to say that your statement about lfh's PDD is wrong.

About Nes'...
I am not really sure but I think that there are also a few of these differences between his and mine, but the biggest difference is that mine is easy to implement in your map.
He needs a full guide on how you implement his system.
And I gave up when I saw how much it was.
But another big difference is that his is vJASS and mine is GUI (GUI friendly)
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
you know he could've used hook too(hooks arent generally allowed, dont even know why), so GUI compatibility is almost nonexistant(literally one line)

GUI friendly != GUI. Yours is vJass just as much(hooks, libraries, globals all are vJass)
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
But mine can be used in GUI.
Most of my systems are GUI friendly and written in JASS (vJASS but I dont use structs so I still call it JASS)
But as much as I have seen, I thought Nes' wasnt GUI friendly.

I tested hooks myself and even when I put a random inside a real parameter, they have exactly the same value (tested it 2000 times and all exactly the same)
I assume that the code will be less efficient with hooks because everywhere that function is called, the hook is applied as well.
But except for that... I cant see why we shouldnt use it.
If hooks are not allowed, then there is no GUI compatibility with the regular damage actions.
But I also showed a less GUI friendly way in GUI though.

But anyway, if I am not allowed to use hooks, why is IcemanBo?
When I took a look at GetUnitScale, I saw the hook function for the first time and tested it immediately.
I don't see why not to use it.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
structs arent the only thing in vJass, and I thought you would say that.

Generally disallowed, like many other things(interfaces for instance), cause they generate very shite code(trigger evaluations/executions), including hooks, but I didnt say you shouldnt use them
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
This system is not working.

You claim this is GUI friendly while it requires vJass. A system that requires vJass is not GUI friendly because most GUI users don't (want to) use vJass at all. Using a single custom script line is perfectly fine for GUI users, thats something that has to be used in many cases anyway (just look at your special effect example).

Spell damage isn't working at all: Place an AM in the map and attack any unit with blizzard - it will display some damage but no damage is dealt. Instead it will heal targets.

Further you claim that in PDD you have to hardcode all bracers items while in yours you don't have to. Did you even try that out? There is a reason why it is neccessary to do so in PDD and any other DDS that uses the bracers trick and there is no way around this. Picking up a normal bracers item in your map breaks spell damage even more: The first damage is detected as physical (although it should be spell), the remaining damage heals the target instead of doing damage.

Artillery doesn't explode units correctly when damage is modified (no matter if damage is increased or decreased).

Also sometimes units randomly disappear from the testmap?


Given all those problems it doesn't really make sense at this point to further discuss this system. There has to be fixed a lot.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
This system is not working.
I didnt expect it to be yet.

You claim this is GUI friendly while it requires vJass. A system that requires vJass is not GUI friendly because most GUI users don't (want to) use vJass at all. Using a single custom script line is perfectly fine for GUI users, thats something that has to be used in many cases anyway (just look at your special effect example).
When using a custom script it is fine.
But when you are doing a custom script for something that can be done in GUI then not.
I prefer to not call a function in GUI when I have to use handle pointers in it.

Spell damage isn't working at all: Place an AM in the map and attack any unit with blizzard - it will display some damage but no damage is dealt. Instead it will heal targets.
Last time that I tested the spell damage, it worked perfectly...
Right now, it indeed doesnt.
When you comment the hooks on GetWidgetLife(), then it works.
So it originally worked, but when I added the hooks, (from which it got too complicated for me to actually do everything from that point,) it seems that the damage is negated because of the Cheat Death Ability being removed and life set back before the (inverted) damage is applied... means heal.

Further you claim that in PDD you have to hardcode all bracers items while in yours you don't have to. Did you even try that out? There is a reason why it is neccessary to do so in PDD and any other DDS that uses the bracers trick and there is no way around this. Picking up a normal bracers item in your map breaks spell damage even more: The first damage is detected as physical (although it should be spell), the remaining damage heals the target instead of doing damage.
I did test it in a pretty early version (0.3), and thought it worked... and actually went back to see what I did there because it did work.
I will try to fnd out what I changed and if I can get it working.
I've attached the 0.3 map to show you that I am not talking bull shit. (I might but then that 0.3 version was bugged in that aspect which I doubt.)

Artillery doesn't explode units correctly when damage is modified (no matter if damage is increased or decreased).
Yea, those things are the things that I dont think about when thinking of a system... I think.
But I will try to fix it.

Also sometimes units randomly disappear from the testmap?
0.o, I didn't notice that.
Neither is there any script at all that removes a unit.
It can kill them if that is what you meant (probably the same GetWidgetLife hook problem but am not sure).

Given all those problems it doesn't really make sense at this point to further discuss this system. There has to be fixed a lot.
There are bugs, but they will be fixed.
Thanks for the review.

Also found another thing that I will have to try out (checking the return value of UnitDamageTarget in the actual damage of the system).
 

Attachments

  • Alternative Damage Engine 0.3.w3x
    44 KB · Views: 87
Level 14
Joined
Dec 12, 2012
Messages
1,007
I didnt expect it to be yet.

Well you first posted it in the resource submission forum. But also in the lab, at least the basics should work, especially since you asked for feedback. Giving feedback for a system that doesn't work at all is quite pointless.

So I guess its best if you rework this.


0.o, I didn't notice that.
Neither is there any script at all that removes a unit.
It can kill them if that is what you meant (probably the same GetWidgetLife hook problem but am not sure).

It seems that this happens to units that don't have 100% life at map start...
 
I suggest re-writing this in JASS, I for one will never use it because there is already better alternatives already done in GUI and JASS. Though I would consider trying this system out if it wasn't Vjass. =) Oh, not to mention you have to compete with Nestharius who is a Vjass monster (monster as in the best) . However good luck.

What's the point of yours? The other approved damage systems aren't bugged at all, working quite well actually. Considering all you have to do is add one condition and one action to make your own spell damage reduction ability..... How exactly is that bugged?

Might check back later when there's been a lot of updates.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
yea, Nes writes monstrocities, things that take hours to get working, and provite no benefits whatsoever for their complexity.

If this goes to pure Jass, you effectivelly drop all GUI support, and you basically get yet another generic DDS, with the one difference that this can say when attack was blocked
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Spell damage isn't working at all: Place an AM in the map and attack any unit with blizzard - it will display some damage but no damage is dealt. Instead it will heal targets.
Ok... I think I explained already but now I do it again :D
When the damage is applied and detected, (non triggered damage,) the system creates a timer that will expire in 0 seconds. When it expires, it sets the health of the unit back to what it was when the timer started and it will destroy itself.
The problem is that when I store the old health in the hashtable, I use a GetWidgetLife() function that is hooked and removes the Cheat Death solution immediately.
Then the life is set back and after that, the original damage is applied.
When spell damage is applied, the damage is still reverted and will result in negative damage = healing.
In 0.8, I turned the exception boolean to true in that place to avoid this problem.

Further you claim that in PDD you have to hardcode all bracers items while in yours you don't have to. Did you even try that out? There is a reason why it is neccessary to do so in PDD and any other DDS that uses the bracers trick and there is no way around this. Picking up a normal bracers item in your map breaks spell damage even more: The first damage is detected as physical (although it should be spell), the remaining damage heals the target instead of doing damage.
Ok... This is kind of my dumb mistake.
When you deal spell damage through the WC3 damage engine, the damage get reverted through the custom ability that all units have.
Then it gets reduced by spell damage that has a few factors like all other attack types.
(For example, it deals 75% damage to heroes (by default).)
So, when dealing 100 damage, the dealt damage is -75.
Because of one of the reasons why I made this system, I want to know how much damage there is in the object editor (simply said), which is 100, I simulate damage and find the factor and use that to change the amount.
The factor is the same 75% but not the -100% of the Spell Damage Reduction Ability because that one was removed inside the factor calculation.
And instead, they used the runed bracers that the hero appearently had.
So the factor is not 0.75 but 0.75 * 0.33 = 0.2475.
Then I revert the damage from 75 / 0.2475 = 303.030303.
Then I apply that amount of damage to the target which gets reduced to 33% again (100) and then to 75% (75) which results that the runed bracers didnt work any more.
Now I created an exceptional simulation for the Spell Damage Detection so that the Reversion is not removed and only the attack type and a -1 factor are applied.

Means, Runed Bracers work in 0.8

Artillery doesn't explode units correctly when damage is modified (no matter if damage is increased or decreased).
I think I will have to credit you (lfh) again for this...
As there is no way how to detect if a unit is artillery, the only solution that I can see is to let the original attacker apply the killing blow which does result in explosion.

Also sometimes units randomly disappear from the testmap?
This one is interesting.
When units are placed on the map, they are created through triggers.
When units have less than 100% health in their stats, that is also applied through triggers using the SetUnitState() and GetUnitState() functions.
Because I hook those and check for a timer in a hashtable, I point to a variable that is not yet initialized. (Non-initialized variables are not the same as null pointers.) This will result in a thread crash which stops all remaining actions (all remaining unit-placing actions) so when a unit has his values modified, all units placed after those will not be created.

Because that function is not used in either GUI (GetUnitStateSwap()) nor JASS (GetWidgetLife()), that hook can simply be removed.

I do see that hooks can be annoying indeed.

Also found another thing that I will have to try out (checking the return value of UnitDamageTarget in the actual damage of the system).
It seems to be true in every single case.
Dunno why.

Well you first posted it in the resource submission forum. But also in the lab, at least the basics should work, especially since you asked for feedback. Giving feedback for a system that doesn't work at all is quite pointless.

I did post it in the submission forum but realised that it was not its place there.
So I asked SCN to move it but by that time, it was moved already :D

I suggest re-writing this in JASS, I for one will never use it because there is already better alternatives already done in GUI and JASS. Though I would consider trying this system out if it wasn't Vjass. =) Oh, not to mention you have to compete with Nestharius who is a Vjass monster (monster as in the best) . However good luck.
Well... without hooks there is no way that this will work the same but yea I will be making a vanilla JASS version of it for which I tell people to use the custom way of dealing damage.
Then, the system does not needs hooks so it can be Vanilla JASS so people without JNGP can use it as well.
But still, I think that library tags and global tags are not really vJASS.
They are just to clean up code instead of writing your full map in the header file or using an enormous list of variables.
Structs or Scopes do change the code (hooks as well but you know what I mean).

What's the point of yours? The other approved damage systems aren't bugged at all, working quite well actually. Considering all you have to do is add one condition and one action to make your own spell damage reduction ability..... How exactly is that bugged?
The most seen problem is when you deal damage during the onDamage event.
I almost lost my head when I came to that problem using a DDS from the spell section.

What happens is that the global variables will be overwritten by the new ones so when the onDamage event stops, you have modified variables what you didnt want.

This problem can be solved in two ways.
One is indexing (which I use and is a bit easier to modify (for example using in extensions like the combat text)).
Here you have arrays of the variables which cannot be overwritten so easily.
Another solution is caching the variables. (I tought I have seen one DDS use that but couldn't find that one any more.)
What you do in here is saving the globals in local variables which you set them back to when the onDamage function ends.
(I use this to have the right index for the loops during the onDamage.)

That is the biggest problem of all which can bug out entire spells when you do stuff like splash damage for example.
When you have neither of those two solutions, your system either bugs or works with delays.

yea, Nes writes monstrocities, things that take hours to get working, and provite no benefits whatsoever for their complexity.
Well... there are quite some benefits but it takes us hours to understand.
We are just talking a different language than he does, which is bad for both of us unfortunately.

If this goes to pure Jass, you effectivelly drop all GUI support, and you basically get yet another generic DDS, with the one difference that this can say when attack was blocked
Not really.
The only thing of vJASS that I use is the hooks (seriously, library tags and global tags can simply be replaced with trigger calls and GUI variables).
So the only thing that I would have to remove is the hooks.

The system worked fine before I added the hooks.
All you had to do was replace all the "UnitDamageTarget" actions to a script similar to the example and everything should work fine.
However, the hook are just one feature that this system would even work 100% if you place it in a massive map with numberless custom spells that do stuff with damage (as long as there is no other DDS in it because we all know what happens then).

But yea, I would have GUI support... kind of.
It is usable in GUI but you would not be able to use those UnitDamageTarget actions (so simply) any more.



But anyway, 0.8 in attachments.
Changes:
- Fixed Runed Bracers.
- Fixed Spell Damage Healing.
- Fixed removed units on map init.
- Fixed artillery explodes.
- Replaced hashtable with arrays.
- Added Bribe's GUI Unit Indexer.

Upcoming:
- Mana Shield Support. (Get to think of some more spells that would be changed.)
- Vanilla JASS version for people without JNGP.

Editted first post's triggers and map link as well.
 

Attachments

  • Alternative Damage Engine 0.8 [testmap].w3x
    51.8 KB · Views: 102
Level 23
Joined
Apr 16, 2012
Messages
4,041
Globals block could be considered Jass-ish, cause there is one single globals block, but library is not Jass, hence it is vJass + it does change the code(names of functions namely).

I wasnt talking about understanding his code, but implementing(copying), also I dont think that any single resource he made is worth the hassle you have to go through, and the code bloat you have to go through(UnitIndexer has like what, 15 requirements?) to get it working, when you could've just hooked unit creations, and have to pay the price of trigger eval, but having the advantage that the code has like 15 lines, literally? compared to his, which could be fast, but the code amount boils it down a lot too, and is like 25k lines of code just to index few units, and takes hours upon hours of copying and looking for his code to get it working.

Replacable vJass features are no less vJass features than nonreplacable vJass features.

I dont see why you should migrate it tho, if it works, let them copy it. Not using JNGP is really their own fault at this point in time.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
but library is not Jass, hence it is vJass + it does change the code(names of functions namely).
Lol, I thought only Scopes did that... but I place a "unique" 3-character prefix on it so it is kind of a unique name already :D

Replacable vJass features are no less vJass features than nonreplacable vJass features.
Still, making a Vanilla JASS version when you have used Structs in vJASS version... is quite a lot of work to make it right.
Making a Vanilla JASS version when you have only used library and global tags? Easy.

Not using JNGP is really their own fault at this point in time.
Haha, that is actually very true. It just has too much good stuff.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Means, Runed Bracers work in 0.8

No they don't. First damage is false detected as physical.

Also damage reduction doesn't work. Setting the damage globally to a fixed value of 10 for example should set every damage to that value. Thats not working.

I do see that hooks can be annoying indeed.

One (out of many reasons) why hooks are considered bad practise.

But still, I think that library tags and global tags are not really vJASS.

Its not about what you think here. Its about the fact that your code will not compile using a vanilla jass editor.

Most people that use GUI don't have JNGP installed and also don't want to install that tool. Therefore it doesn't really matter how "few" vJass features you are using. A single vJass feature like a simple globals block breaks compatibility with the vanilla editor and is therefore basically unusable for most GUIers.

In fact the approach to use only very few vJass librarys combines the worse out of the two worlds: Lengthy and hard-to-read code due to missing abstraction mechanisms (general problem of Jass code) and no backwards compatibility to the default editor (general problem of vJass code).

Take the advantages of one world and accept its disadvantages - but taking both disadvantages and no advantages is pointless.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
No they don't. First damage is false detected as physical.
First damage is false detected as physical?
Sorry but I have no idea what you mean.
I test the map and I use a basic attack and it deals physical damage.
I test the map again and I use blizzard on one unit and it deals magic damage.
I really have no idea what else you could mean.

Also damage reduction doesn't work. Setting the damage globally to a fixed value of 10 for example should set every damage to that value. Thats not working.
Then you are doing something wrong because it works for me.
What you could have done wrong is that you
- dont use "Game - ADE_Event_OnDamage_Before becomes Equal to 0.00" as event,
- use "Set ADE_Param_ActualAmount[ADE_Index]" instead of "Set ADE_Param_Amount[ADE_Index]"
- do not count on it that this damage will still be reduced by armor, runed bracers and attack type (except for basic attacks)
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
First damage is false detected as physical?
Sorry but I have no idea what you mean.

Cast blizzard on a hero. Damage is displayed in blue. Now pick up a brazzers item, damage will be displayed in red.

Thats logically not possible because the damage should be the same all the time.

Btw if I pick up a bracers item it increases all physical damage, so there is something broken too.

Then you are doing something wrong because it works for me.

Thats the trigger I used (all other test triggers disabled in the map):

  • SetAllDamage
    • Events
      • Game - ADE_Event_OnDamage_Before becomes Equal to 0.00
    • Conditions
    • Actions
      • Set ADE_Param_ActualAmount[ADE_Index] = 10.00
Using ADE_Param_ActualAmount or ADE_Param_Amount makes no difference, both are just not working.


Idea:

  1. Think about some points that make your system special/unique compared to the existing DDS.
  2. Make a high quality implementation of these ideas.
  3. Write a good documentation that explains how to use your system.

That will make things a lot more easier to discuss.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Cast blizzard on a hero. Damage is displayed in blue. Now pick up a brazzers item, damage will be displayed in red.

Thats logically not possible because the damage should be the same all the time.
It is indeed not logically possible but it seems that it is.
This is a bug of blizzard that the Spell Damage Reduction ability that you gain overwrites your current Spell Damage Reduction... which means that if you pick up one with 99% and then one with 1%, you only reduce 1% and if you pick up one with 1% and then one with 99%, then you reduce 99%.
So Blizzard didn't create any logic for which order is used in priorities of non-stacking effects... I know that it is practically impossible to have any of those with different values (Elune's Grace + Runed Bracers) combined in regular WC3, but they could have tried at least.

I can re-add the custom Spell Damage Reduction which will work then again, but I have to know when a unit gains the ability "Spell Damage Reduction".
I can re-add when a unit drops or takes an item, but when such an ability is added via triggers... I think I will have to make people re-add it themselves.
I will make an exception for items, but all others will have to be done manually.

Btw if I pick up a bracers item it increases all physical damage, so there is something broken too.
Wow... people said that null as attacktype or damagetype was equal to 100% (0 reduction)...
Guess I will never trust people any more.

Thats the trigger I used (all other test triggers disabled in the map):

  • SetAllDamage
    • Events
      • Game - ADE_Event_OnDamage_Before becomes Equal to 0.00
    • Conditions
    • Actions
      • Set ADE_Param_ActualAmount[ADE_Index] = 10.00
Using ADE_Param_ActualAmount or ADE_Param_Amount makes no difference, both are just not working.

I used this trigger and it worked:
  • Untitled Trigger 003
    • Events
      • Game - ADE_Event_OnDamage_Before becomes Equal to 0.00
    • Conditions
    • Actions
      • Set ADE_Param_Amount[ADE_Index] = 10.00
The actual amount is used in the after damage event.
Then the amount is the damage before reduction of damage and attack type
and the actual amount is the difference between life before and life after.
But as I mentioned before... I might have to change some names, but I don't know how to call stuff.

Idea:
  1. Think about some points that make your system special/unique compared to the existing DDS.
  2. Make a high quality implementation of these ideas.
  3. Write a good documentation that explains how to use your system.

1) I want one that reverts the damage as it was before it was dealt (at least as close as possible)
So when you add 50 damage through triggers, that damage will be reduced by armor while in most (if not all) other DDS, you add it and it will not be reduced by armor.
Also, I want a before damage event and an after damage event, so I know if an attack succeeded.
I also want a simple option to block damage or detect(+prevent) fatal damage.
2) That is what I am trying right now.
3) First I try to have a working system before I start making documentation for something that will have to be changed anyway.



Also, does anyone know what the hell the gameplay constant "Gameplay - Magic Immune Resist Damage" means?
I tried using a spell on a dryad but they just dont take any damage.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
But mine can be used in GUI.
vJass resources can also be used with GUI code using custom script.
It's just a pain in the *** to write it and making mistakes is preprogrammed.

On top I would like to mention, that there is not much sense in writting everything in
GUI generated variables and JASS style, then using a keyword like library
Either go full plain JASS or use the full extend of vJass features.
I think it depends much, which part of the community you want to adress.

Now I comment about your instancing, as it is not obviously clear in the first post.

You pass in a timer and create an unique id saved on the timer handle id.
API: CreateTimerIndex(timer)

1. First of all the allocation is not very optimized, using a loop to find a free instance.
2. Using it for more than one system, may result in an overflow.
3. Allocation should be done within every code snippet itself ( exceptions may be Unit/ItemUserData )
4. Data can be easily overwritten, as there is zero safety mechanism to protect yourself from it.
5. Timer recycling may come useful, if massive timer usage is required.

You leak timer handles here and there.

You said to me, that noone complained about your timer index system,
but I also don't see the code here posted. I guess noone even realised it is required at all.
Just post it and see what happens.

Don't get me wrong, I'm always happy when someone explores tricky aspects of wc3 coding.
A good damage system, is surely one of the most difficult and useful type of systems we can have.
And I hope you can make your system run smooth and without critical errors.
Critique is hard in the comments, but also true.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
vJass resources can also be used with GUI code using custom script.
It's just a pain in the *** to write it and making mistakes is preprogrammed.
I was explaining the difference between a GUI resource and a GUI friendly resource.
Even writing in vJASS can still be GUI friendly.

On top I would like to mention, that there is not much sense in writting everything in
GUI generated variables and JASS style, then using a keyword like library
Either go full plain JASS or use the full extend of vJass features.
I think it depends much, which part of the community you want to adress.
Well... GUI users can use this system even when they have 0 experience with JASS or vJASS.
However, for those (like me) who write their own maps in JASS-only, they have their variables inside global blocks.
So there are people who want their variables set up differently but both use a vJASS system.
Those who have no JNGP are an exception and they need a plain JASS version.
But I will just tell them to get JNPG because systems are not that much approved when they are not written in library tags (at least the functions that you as user use)

Now I comment about your instancing, as it is not obviously clear in the first post.

You pass in a timer and create an unique id saved on the timer handle id.
API: CreateTimerIndex(timer)

1. First of all the allocation is not very optimized, using a loop to find a free instance.
2. Using it for more than one system, may result in an overflow.
3. Allocation should be done within every code snippet itself ( exceptions may be Unit/ItemUserData )
4. Data can be easily overwritten, as there is zero safety mechanism to protect yourself from it.
5. Timer recycling may come useful, if massive timer usage is required.
1) So lets imagine a map:
I create 10000 timers and give them each an integer.
Every time, I do one single step in the loop.
So there is not much of a problem.
Once I reach the 8191st timer, I go back to 1.
Then I find that 1 is already open and so I will be stepping through the loop one time again.
Until there is a timer that is not de-indexed and I will be taking 2 steps (cmon there is no way that you create 8200 timers during one timer's duration.)
2) o.0 wut?
There is no way that you will get 8200 timers with an index by having two systems.
Especially systems wouldnt be so much asking of it.
I use 1 (sometimes more but almost never) more than one indexed timer in this system.
How can 2 systems "overflow" the TimerIndex?
3) I do that right?
4) Data.. can be... overwritten?
You mean, I have to create an event when a timer is de-indexed and it will clear all arrays that every used creates by himself?
Sounds like you have to do it yourself in the first place and so why would you need an event while you can empty them yourself?
5) I dont really know what timer recycling is so...

You leak timer handles here and there.
I what?
Ok in "ADE_Remove_Standard_Damage" it should be obvious that I forgot to remove local timer t. (here)
In ADE_Taking_Damage, I indeed forgot to set t to null inside the if fatalDamage block. (there)

You said to me, that noone complained about your timer index system,
but I also don't see the code here posted. I guess noone even realised it is required at all.
Just post it and see what happens.
It is in the hidden tags in the first post.
Equal as TypeCasting, Reversed Convertions and Bribe's Unit Indexer.
But I can understand that people have read over it.
Edo did mention the TypeCasting though.

Don't get me wrong, I'm always happy when someone explores tricky aspects of wc3 coding.
A good damage system, is surely one of the most difficult and useful type of systems we can have.
And I hope you can make your system run smooth and without critical errors.
Critique is hard in the comments, but also true.
Yea, the biggest problem which I cannot fix yet is that it is slow.
The damage simulation to revert the damage detected or used anywhere else to find attacktype/damagetype factors is very slow.
(Forgetting to turn off the combat text also is very stupid when testing efficiency.
However, I am trying to learn a bit how vJASS works so I can read the data from an MPQ (object editor) and save that data in a hashtable (maybe arrays after that) to have an accessable object editor in triggers.
Critique is to improve... but first, I do need something that can be improved ;)
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
It is indeed not logically possible but it seems that it is.
This is a bug of blizzard that the Spell Damage Reduction ability that you gain overwrites your current Spell Damage Reduction... which means that if you pick up one with 99% and then one with 1%, you only reduce 1% and if you pick up one with 1% and then one with 99%, then you reduce 99%.
So Blizzard didn't create any logic for which order is used in priorities of non-stacking effects... I know that it is practically impossible to have any of those with different values (Elune's Grace + Runed Bracers) combined in regular WC3, but they could have tried at least.

Its not a bug.

Runed bracers don't stack, thats just how it is. There are items that stack and items that don't. Runed bracers are in no way an exception to this, there are numerous other items that don't stack. Thats just how Blizzard designed the game and its our job as modders to deal with that.

Saying that everything would be better if Blizzard had done this differently doesn't help.

I can re-add the custom Spell Damage Reduction which will work then again, but I have to know when a unit gains the ability "Spell Damage Reduction".
I can re-add when a unit drops or takes an item, but when such an ability is added via triggers... I think I will have to make people re-add it themselves.
I will make an exception for items, but all others will have to be done manually.

That kills the purpose of such a mechanism.

Remember, you said people will have less (i.e zero) trouble using runed bracers in their map compared to other DDS. With your method its much more work and much more complicated to integrate runed bracers to your map because you have to setup exceptions for map specific items and let people re-add the bracers ability themself.

Thats a whole lot more work than just adding a single 5-line trigger that hardcodes your runed bracers (like it is done in all other DDS). There is a reason why it was solved this way. Its just the easiest, fastest and most robust way.

If you find a way thats even more convenient, ok. But the suggested method adds a lot complexity and doesn't solve the problem.

Well... GUI users can use this system even when they have 0 experience with JASS or vJASS.
However, for those (like me) who write their own maps in JASS-only, they have their variables inside global blocks.
So there are people who want their variables set up differently but both use a vJASS system.
Those who have no JNGP are an exception and they need a plain JASS version.

I don't think people without JNGP are the exception, especially if they are GUI users.
Basically all GUIers that contacted me about PDD used to use plain Jass and the standard editor. And if you use library tags and globals blocks then you are not coding Jass-only.

But I will just tell them to get JNPG because systems are not that much approved when they are not written in library tags (at least the functions that you as user use)

Thats not true.
A vanilla Jass system is just as good as a vJass system, if implemented well. But a Jass system that has vJass dependencies is doint something wrong.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
You found the here and there :)

I don't say you need it in your system, but still
I'll quickly tell you what timer ( handle ) recycling means.

It means that you pre-create an given stack ( 0 - x ) of timers ( handles ) on map init. Let's say 150 timers.
  • By function call you can pop the first timer from the stack. In other words the function call
    returns a timer and removes it from the stack.
  • Once no longer needed you push the timer back to the stack.
This is the actual recycling process.

Benefits: speed and overall used memory.
Why: natives which create handles turned out to be very slow ( especially CreateUnit )
That's one of the reason we have dummy recycler libraries aswell.

I mentioned TimerUtils in our previous chat, because it recylces timers.
API:
NewTimer()// returns timer from the stack
SetTimerData(97882882)// Attach any data on that timer
NewTimerEx(986882)// Combines the two functions above.
ReleaseTimer(timer)// pushes the timer back to the stack.

I think it's together with Table the most famous snippet in wc3 vJass coding.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Its not a bug.

Runed bracers don't stack, thats just how it is. There are items that stack and items that don't. Runed bracers are in no way an exception to this, there are numerous other items that don't stack. Thats just how Blizzard designed the game and its our job as modders to deal with that.
Well... you are right, it is not a bug, it is a glitch.
Runed Bracers don't stack. Indeed. But when things dont stack, you are bound to a priority algorithm that WC3 DOESNT HAVE!
Go make a new map and remove initial triggers (duh), then make 2 items, one with 1% Spell Damage Reduction and one with 99%.
Then use blizzard on that unit and you see nice damage.
Then you pick up 99% and you see only 1% damage.
Then you pick up 1% and you see 99% damage.
Then you pick up another 99% and you see 1% damage.
This proves that there is no script that detects the highest (or lowest) Spell Damage Reduction and therefor the amount is uncontrolled.
So in short, this glitches spell damage because runed bracers doesnt stack.
If that is just how Blizzard designed it, then I wonder how they ever created HotS. Really that is not something that you design but that is something that you have forgotten to design. Yet modders still have to deal with that.

Saying that everything would be better if Blizzard had done this differently doesn't help.
But that doesnt make it less true.

That kills the purpose of such a mechanism.

Remember, you said people will have less (i.e zero) trouble using runed bracers in their map compared to other DDS. With your method its much more work and much more complicated to integrate runed bracers to your map because you have to setup exceptions for map specific items and let people re-add the bracers ability themself.
In what kind of weird situation do you need it per specific item?
Tell me what is wrong with this?
JASS:
function SpellDamageReductionItemFix takes nothing returns boolean
    local unit u = GetTriggerUnit()
    call UnitRemoveAbility(u, udg_ADE_ABILITY_REVERT_SPELL_DMG)
    call UnitAddAbility(u, udg_ADE_ABILITY_REVERT_SPELL_DMG)
    set u = null
    return false
endfunction

//===========================================================================
function InitTrig_SpellDamageReductionItemFix takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_PICKUP_ITEM)
    call TriggerAddCondition(t, Filter(function SpellDamageReductionItemFix))
    set t = null
endfunction

I don't think people without JNGP are the exception, especially if they are GUI users.
Basically all GUIers that contacted me about PDD used to use plain Jass and the standard editor. And if you use library tags and globals blocks then you are not coding Jass-only.
And I will tell them to download JNGP.
It will make their life much better in the first place.
And right now, there are much less people that havent got it and there are much more systems that do require it.

I don't say you need it in your system, but still
I'll quickly tell you what timer ( handle ) recycling means.

It means that you pre-create an given stack ( 0 - x ) of timers ( handles ) on map init. Let's say 150 timers.
  • By function call you can pop the first timer from the stack. In other words the function call
    returns a timer and removes it from the stack.
  • Once no longer needed you push the timer back to the stack.
This is the actual recycling process.

Benefits: speed and overall used memory.
Why: natives which create handles turned out to be very slow ( especially CreateUnit )
That's one of the reason we have dummy recycler libraries aswell.
So it is like using the same timer over and over again?

I mentioned TimerUtils in our previous chat, because it recylces timers.
API:
NewTimer()// returns timer from the stack
SetTimerData(97882882)// Attach any data on that timer
NewTimerEx(986882)// Combines the two functions above.
ReleaseTimer(timer)// pushes the timer back to the stack.
So it doesnt automatically create a unique index between 1 and 8190 but it gives me the same potential as UnitUserData?

I think it's together with Table the most famous snippet in wc3 vJass coding.
I try to avoid the need of table with this :D
As we all know, hashtables are slow, but a hashable that has a lot of data is even slower.
So in fact, table is slower than hashtable (also because it adds a calculation to the save and load).
In a normal DDS, there is a hashtable.
In mine, I want it with an array and use a timer index that is compatible with JASS arrays.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
So it is like using the same timer over and over again?
Yes
So it doesnt automatically create a unique index between 1 and 8190 but it gives me the same potential as UnitUserData?
Mmh basically correct. I give you a small example.
JASS:
struct Hello
    static method actionFunc takes nothing returns nothing
        local Hello id = GetTimerData(GetExpiredTimer())// Retrieve previously stored data
        call ReleaseTimer(GetExpiredTimer())
        call id.destroy()// destructor for struct Hello.
    endmethod

    // static method create is automatically created for structs by the JassHelper.
    // returns a unique instance for the struct.
    static method example takes nothing returns nothing
        local Hello id = Hello.create()// Returns new instance for Hello.
        call TimerStart(NewTimerEx(id), false, 0, function Hello.actionFunc)
    endmethod
endstruct
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Well... you are right, it is not a bug, it is a glitch.
...
This proves that there is no script that detects the highest (or lowest) Spell Damage Reduction and therefor the amount is uncontrolled.

No, runed bracers are just not meant to stack and are just not meant to exist with different reductions.

Thats one of the reasons why it makes even more sense to trigger all bracers items on your map (like it is done in all DDS). Because you have to trigger them anyway whenever you want them to be stackable (meaning: everytime when you have more than exactly one bracers item).

You want to fix a problem of existing DDS that isn't even DDS related.

In what kind of weird situation do you need it per specific item?

Now think again what you are doing here.
You remove the existing ability and overwrite it with the pre-defined ability. Whats the result? All bracers item will have the same reduction (namely none, except for the spell-inverting 200% reduction).

Don't you think its easier, safer, more efficient (etc.) to just go to the object editor and remove the ability from the item (like in all DDS)? The reduction has to be triggered anyway by the user, with your approach too, so no advantages with your method.


And I will tell them to download JNGP.
It will make their life much better in the first place.
And right now, there are much less people that havent got it and there are much more systems that do require it.

Thats not acceptable.
If you use vJass elements then its a vJass system and by that has to follow coding conventions. Otherwise it should be a plain Jass system such that GUI users can use it without any problems.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
and are just not meant to exist with different reductions.
Yet it does.
Elunes Grace for example reduces the Spell Damage by 20%.

Thats one of the reasons why it makes even more sense to trigger all bracers items on your map (like it is done in all DDS). Because you have to trigger them anyway whenever you want them to be stackable (meaning: everytime when you have more than exactly one bracers item).
And that is what Nes and I have discussed and we both agree that triggering it is something that people should do regardless of havin a DDS or not.

You want to fix a problem of existing DDS that isn't even DDS related.
I want to fix a problem that is created by the DDS.
So it is in some way DDS related.

Now think again what you are doing here.
You remove the existing ability and overwrite it with the pre-defined ability. Whats the result? All bracers item will have the same reduction (namely none, except for the spell-inverting 200% reduction).
All I do is set the priority of my Spell Damage Reduction over the runed bracers, so the runed bracers are applied in the actual damage and it still detects it as spell damage.

Don't you think its easier, safer, more efficient (etc.) to just go to the object editor and remove the ability from the item (like in all DDS)? The reduction has to be triggered anyway by the user, with your approach too, so no advantages with your method.
It is safer, and more efficient but not easier for some people.
And in my approach, you do not have to trigger it.
That is why I try to do this approach.

Thats not acceptable.
If you use vJass elements then its a vJass system and by that has to follow coding conventions. Otherwise it should be a plain Jass system such that GUI users can use it without any problems.
When I will see someone without JNPG, I will message you... and I am sure that it will take a really long time.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
didnt read the whole thing just yet, but noticed you said wc3 has no priorities for runed bracers, but it does.

It depends on order of acquisations, so first/last acquired applies, regardless of what you have(check playdota.com, even critical ability with item will stack depending on which you acquire first, according to Icefrog, and in Dota map[may be different in different maps])
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
I just wanna say a couple of things : ).

Firstly, even my DDS is bugged at the moment since it uses health events. I have to go back to timers on it or find an alternative approach : (. The health events don't detect *very* small changes in health.

Secondly, a good API listing and a quick demo to show how something works is the quickest way to get people to use your thing. How good it actually is comes second. When I was a beginner, I wanted to find the very best, but in the end it boiled down to what I could actually understand quickly. For Save/Load, that was Acehart's. His had a quick demo. I didn't even care about the API Listing to be honest, lol.

The more complex a resource is, the more demos are required to demonstrate what it can do, how to use the various things in the API, etc. Keep this in mind.

What is true of all very popular resources? They have good demos that show how to use them and they have a very low learning curve.


As to my own stuff, I'm sorry that you have had such terrible experiences with them in the past. The newer ones include all required resources in a single folder so that you don't have to hunt them down : ). Also, keep in mind when you are counting lines to decide whether or not to use a resource, that you need 300-400 very, very large scripts before it starts to make an impact on your map. Code length doesn't harm file size, but rather maintainability ^_-. I also write tutorials on how to use them now since they pretty much requires a BS in Computer Science or equivalent experience >.<. In the future, I plan to include the resources as script files and then to include these with the //! include statement. This way you can put your map in a folder and move all required scripts into that folder. You can then include the libraries from within the map (the specific ones you need). Update? Overwrite. Copying a resource's requirements more than once? Overwrite. It should be much easier : ). This will also let you work on things like GitHub more easily since the coder can just code in files rather than the map.


Nice try on the system. I look forward to seeing how you improve it from here =).
 
Yet it does.
Elunes Grace for example reduces the Spell Damage by 20%.


And that is what Nes and I have discussed and we both agree that triggering it is something that people should do regardless of havin a DDS or not.


I want to fix a problem that is created by the DDS.
So it is in some way DDS related.


All I do is set the priority of my Spell Damage Reduction over the runed bracers, so the runed bracers are applied in the actual damage and it still detects it as spell damage.


It is safer, and more efficient but not easier for some people.
And in my approach, you do not have to trigger it.
That is why I try to do this approach.


When I will see someone without JNPG, I will message you... and I am sure that it will take a really long time.


Then you just contradicted yourself for your whole reason to making this as well guess what, time to message him. It didn't take that long eh? I don't use JNPG. :grin:

didnt read the whole thing just yet, but noticed you said wc3 has no priorities for runed bracers, but it does.

It depends on order of acquisations, so first/last acquired applies, regardless of what you have(check playdota.com, even critical ability with item will stack depending on which you acquire first, according to Icefrog, and in Dota map[may be different in different maps])

Critical strike doesn't stack, it technically stacks in term of chance however at the same time it doesn't stack. Unless Icefrog coded it in Dota then it is the same as every other map.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Yet it does.
Elunes Grace for example reduces the Spell Damage by 20%.

Is there a Blizzard map were both items exist?

And that is what Nes and I have discussed and we both agree that triggering it is something that people should do regardless of havin a DDS or not.

It is safer, and more efficient but not easier for some people.
And in my approach, you do not have to trigger it.
That is why I try to do this approach.

Can you see the contradiction?

Btw, in PDD you also don't have to trigger it. Its working by default, cause the standard case of just one bracers item is already considered in the system.

All I do is set the priority of my Spell Damage Reduction over the runed bracers, so the runed bracers are applied in the actual damage and it still detects it as spell damage.

And thats effectivly the same as if you just remove it in the object editor. Same effect.


When I will see someone without JNPG, I will message you... and I am sure that it will take a really long time.

You know, you can of course just ignore all constructive criticism, thats up to you.

I thought you want feedback to actually improve something, if not thats ok too, but then I'm done here.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
didnt read the whole thing just yet, but noticed you said wc3 has no priorities for runed bracers, but it does.
So it does not.
Everytime that you gain a Spell Damage Reduction ability, you overwrite the existing value independent of what value that is.
When removing one... I dont really know how that works out but you might have no Spell Reduction Ability at all or you have a pretty random one again. (Depending on which order they are added.)

What is true of all very popular resources? They have good demos that show how to use them and they have a very low learning curve.
That is true, but we are not here to use this resource but to understand how it works.
To use it, it should be easy and simple.
To understand it, you have to go through all pieces of development issues which are rarely inside the documentation. (Why there are fixes needed for something that used to be good.)

Also, keep in mind when you are counting lines to decide whether or not to use a resource, that you need 300-400 very, very large scripts before it starts to make an impact on your map. Code length doesn't harm file size, but rather maintainability ^_-.
I try to make a script that would never need a change as long as WC3 doesnt change.
All map dependant things or things that could change by other systems should be separate from the System and b called Cofiguration, Initialization, or anything like that.
For example the hooks. They take a lot of lines of code for little features.
But once they work (and they do afaik) then they dont need maintainance.
(ofcourse until WC3 changes, which I doubt it will be very soon)

Nice try on the system.
But not nice enough appearently.

Then you just contradicted yourself for your whole reason to making this
I contradicted myself?
I try to have the original Runed Bracers working on a map with a DDS that uses a 2.00 Spell Damage Reduction.
Runed Bracers work as well as every other item when you have that ItemSpellDamageReduction fix. (Maybe have to add another event to it for units losing an item though.)

as well guess what, time to message him. It didn't take that long eh? I don't use JNPG. :grin:
Wow, that I forgot you... -_-
In my time here, I might have seen 4 to 6 people that do not have it.
All other 200 people didnt have problems implementing TimerUtils, NewTable, Any of my resources (that all require vJASS) or any other thing that uses any of those.

Ofcourse that is because I actively joined only August 2014 and vJASS exists so long that most people know what it is (and have it because they were told to download it) and that there are very few new people joining.

Is there a Blizzard map were both items exist?
Elune's Grace is not an item but rather a normal ability but that doesnt matter.
What matters is that they dont have an algorithm... which also tells me that there might be other things that do not stack have no priority algoritm either.

Can you see the contradiction?
Yea kind of but I meant that people should choose wether they want to trigger Spell Damage Reduction or not. If they dont want to, then they dont.
And that all Spell Damage Reduction Items work with the normal Spell Damage Reduction ability.

And thats effectivly the same as if you just remove it in the object editor. Same effect.
Not entirely.
When the actual damage is dealt, the Spell Damage Revert Ability is removed, so it will use the Runed Bracers again if you have that item, which reduced the actual damage dealt properly.
When you remove it from the object editor, you have no runed bracers and so that damage will not be reduced.

You know, you can of course just ignore all constructive criticism, thats up to you.
Hey, there is not really constructive critisism in "GUI friendly means no vJASS."
It is a vJASS System but it is a GUI friendly system.
How is that hard to understand?
Every feature that it has is easily usable in GUI, it even uses the exact same GUI actions.

I thought you want feedback to actually improve something, if not thats ok too, but then I'm done here.
I do want constructive critisism, but that includes:
  • Showing resources that would help making it better.
    For example how TimerUtils can replace TimerIndex.
  • How data structures can be improved.
  • How things should be explained (and/or named).
  • How
  • What kind of things exist that break a DDS.
    For example, Mana Shield.
  • etc.
But discussing if vJASS means that the resource CANNOT be GUI friendly or wether Spell Damage Reduction's priority can be called forgotten or properly designed, that is something that I cannot call constructive critisism.

EDIT:
I am considering a version where spell damage is detected via Xonok's Damaging Spell Detection.
That will force all spells to be made like that but it will be something nice and easy to implement.
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
I don't think that's necessary, because it would take lots of configuration for something that lfh's system does by default with just 1 ability.
What I would like someone to do is make a proper damage detection system that handles only the core of damage detection along with type detection and that either doesn't break UnitDamageTarget or replaces it with a function that works in every single case with no difference for handling based on whether it was called from a damage handler or not. Furthermore, it should have some way to prevent rewriting of values when a damage handler is nested in another.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
I don't think that's necessary, because it would take lots of configuration for something that lfh's system does by default with just 1 ability.
Yea it is indeed a very stupid idea :D

What I would like someone to do is make a proper damage detection system that handles only the core of damage detection along with type detection and that either doesn't break UnitDamageTarget or replaces it with a function that works in every single case with no difference for handling based on whether it was called from a damage handler or not. Furthermore, it should have some way to prevent rewriting of values when a damage handler is nested in another.
So a DDS that:
- Detects damage.
- Uses UnitDamageTarget and UnitDamagePoint.
- Indexes the data (caching can be done as well but I think it is stupid because it takes a lot of work to add fields to it and just compare that to how easy it is to add fields like I have done in the combat text).
- Damage type detection (note that ATTACK_TYPE_MELEE is different from Physical Damage. That is why I use an alternative field called Hittype that changes accordingly to what you want).

Mine is actually meant to do all of those.
But I think that fatal damage and blocked damage is included in that way.
After all, now that I use the original damage as final blow on fatal damage, it is pretty impossible to have that as an extention in a quick view.
Blocked damage is also pretty crucial.
Other stuff, for example Combat Text, can just be switched on and off or not even been imported into your map in the first place.
But it helps a lot with debugging and stuff and it is just a good thing to have anyway :D.


I've updated the system again:
- Uses TimerUtils.
- Uses a different TimerIndex that is made for TimerUtils.
- Added ATTACK_TYPE_100 again because null doesnt use 100%.
- Fixed timer leaks.
- Added item Spell Damage Reduction fix.
- Moved hooks to a different trigger (file) because they are a pain in the (_._) to read.
- Added a check if the target has been changed on fatal damage (wether the the original attack should still be used or not).

Im afraid that Mana Shield is beyond fixing.
The fatal damage detection is almost impossible to have and therefor a unit with low health will still be detected as fatal damage and will have his health reduced to 0.415.
Next to that, I would need hooks for Mana setters and getters as well to get the proper values.
 

Attachments

  • Alternative Damage Engine 0.9 [testmap].w3x
    60.6 KB · Views: 83
Level 21
Joined
Mar 27, 2012
Messages
3,232
Well, my use-case is atypical anyway. I don't care about or use:
Artillery attacks
Some object editor abilities for their stats(mana shield, defend, etc)
Whether damage is code
Having damage handlers unless they support priorities(come on, lfh...)
Pretty much anything that I can make better myself.

In many cases I feel that the desire to appeal to as many users as possible is impeding actual performance and good features in general.
 
Well, non JNGP users includes Mac and if I recall Windows 8 users.

In Mac, only through a method mentioned by Purge that allows VJass compilation in Warcraft on Mac, which is difficult than the just ready to use Windows.

Also, some users are more than unwilling to use JNGP due to Virus threat (no matter how you tell them).
=====================================
For a damage detection, I was thinking of Bribe's style :
Define own damage types (fire, ice, wind, strike, etc)
Create affinities based on damage types (weak fire, resist ice, etc)
Define armor through a DDS (fire resistance : 8, ice resistance : 92, etc)
Create custom damage-armor-affinity relation system through all mentioned above

Of course these are just what hits my brain.

What I could think of that alters damage, mainly spells :
Mana Shield
Defend
Resistant Skin -> Duration
Elune's Grave
Runed Bracers
Evasion (though this is very very obvious)
Critical Strike (easily detectable, and can easily be triggered as well)
Magic Shield (null magics, ain't it obvious? :D)

Also, does this one fix the Life Drain issue, it's a famous one in LFH DDS, and if I recall, Nestharus as well. Bribe doesn't have this issue since his doesn't detect the difference in between physical and spell.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Well, non JNGP users includes Mac and if I recall Windows 8 users.

In Mac, only through a method mentioned by Purge that allows VJass compilation in Warcraft on Mac, which is difficult than the just ready to use Windows.

Also, some users are more than unwilling to use JNGP due to Virus threat (no matter how you tell them).
But I assume that it doesnt matter any more because I am using TimerUtils now... And that requires vJASS as well.
But that will come later when the system works.

For a damage detection, I was thinking of Bribe's style :
Define own damage types (fire, ice, wind, strike, etc)
Create affinities based on damage types (weak fire, resist ice, etc)
Define armor through a DDS (fire resistance : 8, ice resistance : 92, etc)
Create custom damage-armor-affinity relation system through all mentioned above
Those are part of a custom stat system that can change for every map.
I think I could test what happens if I use non-existant attack and damage types.
What this system does allow is make your own Hit types.
I have created three myself which are very basic to what a map needs but you can create more by just making a new global variable and set it to a unique amount compared to the others.

What I would need to test is what happens when I use ConvertAttackType(12345).
But I think it is definately possible.
However the damage reduction should be made by the user himself:
  • Damage Reduction
    • Events
      • Game - ADE_Event_OnDamage_Before becomes Equal to 0.00
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • ADE_Param_DamageType[ADE_Index] Equal to DAMAGE_TYPE_FIRE
        • Then - Actions
          • Set ADE_Param_Modifier[ADE_Index] = (ADE_Param_Modifier[ADE_Index] x 2.00)
        • Else - Actions
Fire damage deals twice the amount.
(However if you do want to do it like that I suggest you to have an event system that supports priority because this will glitch when you change the damage type on the same event on a trigger that is made after this one.)
I first had a lot more events but that wasnt a good solution.

Also, the armor types...
Do you think they are part of a DDS or part of a Stat System?
I do understand why a DDS would be good to have them but they actually belong to a stat system.
Or better yet: How do you think I would be implementing that?
I use custom stats using a hashtable as something similar to the object editor where I place all my stuff like Spell Power, Magic Resistance, Cooldown Reduction, etc. all stored at the Unit-Type as one key and a field index as the other (spell power = 1, magic resistance = 2 (for example))
Then when a unit is created, I load the data from the hashtable and save those in arrays for optimized access, stored on the UnitUserData using a unit indexer.

But I am not sure wether that would be how everyone wants it.

Of course these are just what hits my brain.

What I could think of that alters damage, mainly spells :
Mana Shield
Defend
Resistant Skin -> Duration
Elune's Grave
Runed Bracers
Evasion (though this is very very obvious)
Critical Strike (easily detectable, and can easily be triggered as well)
Magic Shield (null magics, ain't it obvious? :D)
  • Mana Shield (Beyond fixing)
  • Defend
    I am not sure if this one will break the system because the results with and without DDS are the same. (This may be an exception because the attacker and footman were both my units. The same how Spirit link doesnt work.)
  • Resistant Skin Duration?
    How does the duration bug?
    When he is immune to the damage, the damage is removed so there is no onDamage event either.
    The duration has nothing to do with the DDS I think.
  • Elune's Grace + Runed Bracers
    You would have to reset the Spell Damage Reverter for the affected unit but then they work.
    (I am trying to reset it in most cases of how you can add those.)
  • Evasion... no I do not understand why.
    An evaded attack means no damage means no onDamage means no bug.
  • Critical strike.
    Detectable? No. Bugged? Neither.
    Critical strike is pretty undetectable as it does not leave a buff, does not have an order, isnt a spellcast, etc.
    However, critical strike is applied before the onDamage event fires.
    When that happens the original damage (according to the attack- and damagetype factor includes the damage of the critical strike and therefor that amount and multier is still applied in the damage by the system.
  • Magic Shield
    Same as all other damage nullifiers: It removes the damage so there is no onDamage event.
    This is not necessarily a glitch but it will be when you create effects based on blocked damage or onDamage no matter wether they are blocked or not.
    But in that case, you will probably have these abilities triggered as well as every other ability that you use.

Also, does this one fix the Life Drain issue, it's a famous one in LFH DDS, and if I recall, Nestharus as well. Bribe doesn't have this issue since his doesn't detect the difference in between physical and spell.
No.
Although the original Spell Damage Reduction ability will in almost every case work, this system still uses that same ability to revert damage.
So the Life Drain damage (and therefor health gain) is reverted.
 
I slashed the Resistant Skin since I know it doesn't bug (hence I typed Duration). Sorry for miscommunication.

Yeah, that would vary from map to map, but it couldn't hurt to have it as an addon, or separated resources.

For Mana Shield, the best solution would be having them triggered completely. Though to handle the default Mana Shield, we can compare the victim's mana shield level and their mana comparison after hit and before hit (but that wouldn't mean it's flawless). Perhaps that could go somewhere into detection of actual damage?

Anyway, what Mana Shield results in damage? 0 damage? I haven't test it myself.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Would it be worth to check if you need the Cheat Death Abilty instead of just giving it?

JASS:
        if amount < hp - udg_HPThreshold and amount > -hp - udg_HPThreshold and amount < GetUnitState(target, UNIT_STATE_MAX_LIFE) - hp then
            //Dont start timer.
            call SetWidgetLife(target, hp +amount)
        else
            //give Cheat Death Ability and start 0s timer.
            //<stuff>
        endif
 
Level 2
Joined
Aug 11, 2009
Messages
22
When I import this system to my map following the instructions in the test map, it doesn't compile properly. It tells me: library redeclared: adeEngine
I'm using the newest version of JNGP.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
That means that you have two libraries called adeEngine...
I am pretty 99.999% sure that noone ever used it before.

On the other hand... if you downloaded the 0.9 testmap, and you didnt modify it, then it works.
You have somehow copied the ADE System trigger or managed to make a library using the same name.
Either rename or remove one of them.
 
Level 2
Joined
Aug 11, 2009
Messages
22
You were right. I copied it to another trigger by accident :p Stupid me. However, it goes on. Once I remove the double, it tells me that the timer functions are undeclared. So I copied the libraries aswell. But now I get this: Undeclared variable udg_ADE_Param_CodedHitType on several lines.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
As lfh mentioned this is not (yet) to be used in maps, only for test purposes. Thats why the 0. version and [testmap].

If you do want to test it, you probably didnt copy the GUI variable trigger to your map or havent got the option to automatically create unknown variables while pasting trigger data on.
Or you havent copied and enabled the vJASS variable trigger.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
im afraid that Mana Shield is beyond fixing.
The fatal damage detection is almost impossible to have and therefor a unit with low health will still be detected as fatal damage and will have his health reduced to 0.415.
Next to that, I would need hooks for Mana setters and getters as well to get the proper values.

I was able to fix mana shield in DamageEngine 3.4 by setting the damage absorbed % to 0 and then adjusting the damage event amount and unit mana based on if the unit has the mana shield buff.
 
Status
Not open for further replies.
Top