1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Head to the 33rd Modeling Contest Poll and drink to your heart's desire.
    Dismiss Notice
  3. Choose your means of doom in the 17th Mini Mapping Contest Poll.
    Dismiss Notice
  4. A slave to two rhythms, the 22nd Terraining Contest is here.
    Dismiss Notice
  5. The heavens smile on the old faithful. The 16th Techtree Contest has begun.
    Dismiss Notice
  6. The die is cast - the 6th Melee Mapping Contest results have been announced. Onward to the Hive Cup!
    Dismiss Notice
  7. The glory of the 20th Icon Contest is yours for the taking!
    Dismiss Notice
  8. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Damage Interface v1.9

Submitted by chopinski
This bundle is marked as pending. It has not been reviewed by a staff member yet.
Damage Interface v1.9 [Custom Evasion][Critical Strike][Spell Power][Life Steal][Spell Vamp]

Intro
Damage Interface provides an easy way to register, detect, and modify custom on damage events, like Critical Strikes and Evasion, as well as introduce new functionalities like Spell Power and Spell Vamp and simulate functionalities like Life Steal.

You will also have access to quick specific damage types registration in JASS, like On Attack Damage or On Spell Damage events.
How it Works?
By using the new damage natives that for now are only accessible to Jassers, I created custom events for Critical Strike and Evasion and provided a way to register functions to be executed when these custom events fire. Of course, given the circumstances these custom events will not fire for normal critical strikes and evasion in the game, so if you want to detect a critical strike for example, you will have to give a chance and a multiplier to the desired unit using the public API of each system.

Apart from Damage Interface library, all other libraries are modular and do not depend on each other.

Warning* For the Custom Evasion System, please be aware that a few hard coded functionalities in the game like bash, cleave, critical strikes, orb effects will still trigger damage events, so in order to make a full simulation of an Evasion event all of those functionalitites would have to be custom made as well. All that being said, this system by itself offers you the ability to create all of those custom system the way you want.

How To Import?
Simply copy the Damage Interface folder from the test map into your map. You also will need a Unit Indexer. In the test map I used the Unit Indexer created by @Bribe so credits to him, but any unit indexer will do the required job.
Requirements
Damage Interface requires patch 1.31+ and a Unit Indexer.
Systems
Code (vJASS):

library DamageInterface
    /* --------------------- DamageInterface v1.6 by Chopinski --------------------- */
    //! novjass
        Allows for easy registration of specific damage type events like on attack
        damage or on spell damage, etc...
 
        How To Use?
         It works very similar to the RegisterPlayerUnitEvent and SpellEffectEvent
         library's, it's intended to turn this?
 
         private function OnDamage takes nothing returns nothing
             local boolean isSpell = BlzGetEventAttackType() == ATTACK_TYPE_NORMAL
 
             if isSpell then
                 //do stuff
             endif
         endfunction
 
         private function Init takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DAMAGED)
            call TriggerAddCondition(t, Condition(function OnDamage))
         endfunction
 
         Into this:
 
         private function OnDamage takes nothing returns nothing
             //Do Stuff
         endfunction
 
         private function Init takes nothing returns nothing
            call RegisterSpellDamageEvent(function OnDamage)
         endfunction
 
         Added on v1.1 - Included functionality to allow registration of damage on before
         damage mitigation
 
         to register before damage mitigation simply do:
 
         private function OnDamage takes nothing returns nothing
            //Do Stuff
         endfunction
 
         private function Init takes nothing returns nothing
            call RegisterSpellDamagingEvent(function OnDamage)
         endfunction
 
         call RegisterSpellDamageEvent(function OnDamage)
            -> uses EVENT_PLAYER_UNIT_DAMAGED
 
         call RegisterSpellDamagingEvent(function OnDamage)
            -> uses EVENT_PLAYER_UNIT_DAMAGING

     v1.3:
            - Added a few global variables to assist reducing the amount of code when using this system
                unit    src         -> the source of damage
                unit    tgt         -> the target of damage
                boolean isEnemy     -> true if the source is an enemy of the target
                boolean isAlly      -> same as isEnemy but for allied units
                boolean isMelee     -> indicates if the source of damage is a melee unit
                boolean structure   -> indicates if the target of damage is a structure
                boolean magicImmune -> indicates if the target of damage is magic immune
                integer sIdx        -> represents the custom index of the source of damage
                integer tIdx        -> represents the custom index of the target of damage
                integer sId         -> represents the unit handle of the source of damage
                integer tId         -> represents the unit handle of the target of damage
                player  p           -> the owning player of the source
            - Renamed the Cleave event to Enhanced, as well observed by Bribe
            - Added a condition to the evaluation of an Attack Event to not run when an attack is evaded (Evasion System)
            - Changed the logic for the NeverMiss functionality to avoid a Condition run (Evasion System)
            - Corrected a spelling error on the RegisterCriticalStrikeEvent() function call (Critical System)
    v1.4:
        - Redone the globlas to use struct members and be more readable as suggested:
            - instead of using the src, tgt, etc... now you can use:
            - DamageI.source         -> the source of damage
            - DamageI.target         -> the target of damage
            - DamageI.sourcePlayer   -> the player who owns the source unit
            - DamageI.targetPlayer   -> the player who owns the target unit
            - DamageI.isEnemy        -> true if the source is an enemy of the target
            - DamageI.isAlly         -> same as isEnemy but for allied units
            - DamageI.isMelee        -> indicates if the source of damage is a melee unit
            - DamageI.isRanged       -> indicates if the source of damage is a ranged unit
            - DamageI.isAttack       -> true if the damage type is an attack damage
            - DamageI.isSpell        -> true if the damage type is an spell damage
            - DamageI.isPure         -> true if the damage type is pure damage
            - DamageI.isEnhanced     -> true if the damage type is enhanced
            - DamageI.sourceIsHero   -> indicates if the source of damage is a hero unit
            - DamageI.targetIsHero   -> indicates if the target of damage is a hero unit
            - DamageI.structure      -> indicates if the target of damage is a structure
            - DamageI.magicImmune    -> indicates if the target of damage is a magic immune unit
            - DamageI.sourceX        -> the X coordinate of the source unit
            - DamageI.sourceY        -> the Y coordinate of the source unit
            - DamageI.targetX        -> the X coordinate of the target unit
            - DamageI.targetY        -> the Y coordinate of the target unit
            - DamageI.sIdx           -> represents the custom index of the source of damage
            - DamageI.tIdx           -> represents the custom index of the target of damage
            - DamageI.sId            -> represents the unit handle of the source of damage
            - DamageI.tId            -> represents the unit handle of the target of damage
            - DamageI.damageType     -> the damage type of the damage event
            - DamageI.attackType     -> the attack type of the damage event
        Hopefully all those members will help the user to created code with less function call and
        less amount of code created overall.
    v1.5:
        - New Configurable Constant CACHE_EXTRA: if true struct members will be set for every event.
        - Code Cleaning
    //! endnovjass
    /* ----------------------------------- END ---------------------------------- */
    globals
        // Used by the Evasion system to avoid evaluating the Attack
        // trigger if an attack is evaded
        boolean evade = false
        // This constant is used to define if the system will cache
        // extra information from a Damage Event, like the unit
        // Custom value (UnitUserData), a unit Handle Id, and more
        // Additionaly you can see the CacheResponse method below
        // to have an idea and comment the members you want cached or not
        private constant boolean CACHE_EXTRA = true
    endglobals

    struct DamageI
        //Core evaluation triggers for after mitigation
        static trigger Attack     = CreateTrigger()
        static trigger Spell      = CreateTrigger()
        static trigger Pure       = CreateTrigger()
        static trigger Enhanced   = CreateTrigger()
        static trigger Damaged    = CreateTrigger()
        //Core evaluation triggers for before mitigation
        static trigger OnAttack   = CreateTrigger()
        static trigger OnSpell    = CreateTrigger()
        static trigger OnPure     = CreateTrigger()
        static trigger OnEnhanced = CreateTrigger()
        static trigger Damaging   = CreateTrigger()
        /* --------------- Members -------------- */
        readonly static damagetype damageType
        readonly static attacktype attackType
        readonly static unit       source
        readonly static unit       target
        readonly static player     sourcePlayer
        readonly static player     targetPlayer
        readonly static boolean    isEnemy
        readonly static boolean    isAlly
        readonly static boolean    isMelee
        readonly static boolean    isRanged
        readonly static boolean    isAttack
        readonly static boolean    isSpell
        readonly static boolean    isPure
        readonly static boolean    isEnhanced
        readonly static boolean    sourceIsHero
        readonly static boolean    targetIsHero
        readonly static boolean    structure
        readonly static boolean    magicImmune
        readonly static real       sourceX
        readonly static real       sourceY
        readonly static real       targetX
        readonly static real       targetY
        readonly static integer    sIdx
        readonly static integer    tIdx
        readonly static integer    sId
        readonly static integer    tId

        // Setting the members of the struct
        private static method CacheResponse takes unit src, unit tgt, damagetype dmg_type, attacktype atk_type returns nothing
            set damageType = dmg_type
            set attackType = atk_type
            set source     = src
            set target     = tgt
            set isAttack   = damageType == DAMAGE_TYPE_NORMAL
            set isSpell    = attackType == ATTACK_TYPE_NORMAL
            set isPure     = damageType == DAMAGE_TYPE_UNIVERSAL
            set isEnhanced = damageType == DAMAGE_TYPE_ENHANCED

            // You can comment the members you dont want to be cached
            // or set CACHE_EXTRA = false to not save them at all
            if CACHE_EXTRA then
                set sourcePlayer  = GetOwningPlayer(source)
                set targetPlayer  = GetOwningPlayer(target)
                set isEnemy       = IsUnitEnemy(target, sourcePlayer)
                set isAlly        = IsUnitAlly(target, sourcePlayer)
                set isMelee       = IsUnitType(source, UNIT_TYPE_MELEE_ATTACKER)
                set isRanged      = IsUnitType(source, UNIT_TYPE_RANGED_ATTACKER)
                set sourceIsHero  = IsUnitType(source, UNIT_TYPE_HERO)
                set targetIsHero  = IsUnitType(target, UNIT_TYPE_HERO)
                set structure     = IsUnitType(target, UNIT_TYPE_STRUCTURE)
                set magicImmune   = IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)
                set sourceX       = GetUnitX(source)
                set sourceY       = GetUnitY(source)
                set targetX       = GetUnitX(target)
                set targetY       = GetUnitY(target)
                set sIdx          = GetUnitUserData(source)
                set tIdx          = GetUnitUserData(target)
                set sId           = GetHandleId(source)
                set tId           = GetHandleId(target)
            endif
        endmethod

        private static method OnDamaging takes nothing returns nothing
            call CacheResponse(GetEventDamageSource(), BlzGetEventDamageTarget(), BlzGetEventDamageType(), BlzGetEventAttackType())

            if damageType != DAMAGE_TYPE_UNKNOWN then
                if isAttack then
                    call TriggerEvaluate(OnAttack)
                elseif isSpell then
                    call TriggerEvaluate(OnSpell)
                elseif isPure then
                    call TriggerEvaluate(OnPure)
                elseif isEnhanced then
                    call TriggerEvaluate(OnEnhanced)
                endif
            endif
        endmethod

        private static method OnDamage takes nothing returns nothing
            call CacheResponse(GetEventDamageSource(), BlzGetEventDamageTarget(), BlzGetEventDamageType(), BlzGetEventAttackType())
 
            if damageType != DAMAGE_TYPE_UNKNOWN then
                if isAttack and not evade then
                    call TriggerEvaluate(Attack)
                elseif isSpell then
                    call TriggerEvaluate(Spell)
                elseif isPure then
                    call TriggerEvaluate(Pure)
                elseif isEnhanced then
                    call TriggerEvaluate(Enhanced)
                endif
            endif

            set evade = false
        endmethod

        static method RegisterAttackDamage takes code c, boolean after returns nothing
            if after then
                call TriggerAddCondition(Attack, Filter(c))
            else
                call TriggerAddCondition(OnAttack, Filter(c))
            endif
        endmethod
 
        static method RegisterSpellDamage takes code c, boolean after returns nothing
            if after then
                call TriggerAddCondition(Spell, Filter(c))
            else
                call TriggerAddCondition(OnSpell, Filter(c))
            endif
        endmethod
 
        static method RegisterPureDamage takes code c, boolean after returns nothing
            if after then
                call TriggerAddCondition(Pure, Filter(c))
            else
                call TriggerAddCondition(OnPure, Filter(c))
            endif
        endmethod
 
        static method RegisterEnhancedDamage takes code c, boolean after returns nothing
            if after then
                call TriggerAddCondition(Enhanced, Filter(c))
            else
                call TriggerAddCondition(OnEnhanced, Filter(c))
            endif
        endmethod

        static method RegisterDamage takes code c, boolean after returns nothing
            if after then
                call TriggerAddCondition(Damaged, Filter(c))
            else
                call TriggerAddCondition(Damaging, Filter(c))
            endif
        endmethod
 
        static method onInit takes nothing returns nothing
            call TriggerRegisterAnyUnitEventBJ(Damaged, EVENT_PLAYER_UNIT_DAMAGED)
            call TriggerAddCondition(Damaged, Condition(function thistype.OnDamage))

            call TriggerRegisterAnyUnitEventBJ(Damaging, EVENT_PLAYER_UNIT_DAMAGING)
            call TriggerAddCondition(Damaging, Condition(function thistype.OnDamaging))
        endmethod
    endstruct
 
    /* -------------------------------------------------------------------------- */
    /*                               Public JASS API                              */
    /* -------------------------------------------------------------------------- */

    //After damage mitigation registration

    function RegisterAttackDamageEvent takes code c returns nothing
        call DamageI.RegisterAttackDamage(c, true)
    endfunction
 
    function RegisterSpellDamageEvent takes code c returns nothing
        call DamageI.RegisterSpellDamage(c, true)
    endfunction
 
    function RegisterPureDamageEvent takes code c returns nothing
        call DamageI.RegisterPureDamage(c, true)
    endfunction
 
    function RegisterEnhancedDamageEvent takes code c returns nothing
        call DamageI.RegisterEnhancedDamage(c, true)
    endfunction

    function RegisterDamageEvent takes code c returns nothing
        call DamageI.RegisterDamage(c, true)
    endfunction

    //Before damage mitigation registration

    function RegisterAttackDamagingEvent takes code c returns nothing
        call DamageI.RegisterAttackDamage(c, false)
    endfunction
 
    function RegisterSpellDamagingEvent takes code c returns nothing
        call DamageI.RegisterSpellDamage(c, false)
    endfunction
 
    function RegisterPureDamagingEvent takes code c returns nothing
        call DamageI.RegisterPureDamage(c, false)
    endfunction
 
    function RegisterEnhancedDamagingEvent takes code c returns nothing
        call DamageI.RegisterEnhancedDamage(c, false)
    endfunction

    function RegisterDamagingEvent takes code c returns nothing
        call DamageI.RegisterDamage(c, false)
    endfunction
endlibrary

 

Code (vJASS):

library Evasion requires DamageInterface
    /* ------------------------ Evasion v1.3 by Chopinski ----------------------- */
    //! novjass
 
        Evasion implements an easy way to register and detect a custom evasion event.
 
        It works by monitoring custom evasion and missing values given to units,
        and nulling damage when the odds given occurs.
 
        It will only detect custom evasion, so all evasion or miss values given to a
        unit must be done so using the public API provided by this system.
 
        *Evasion requires DamageEvents. Do not use TriggerSleepAction() with Evasion.
 
        The API:
            To register a function to run when an unit evades an attack simply do:
                call RegisterEvasionEvent(function YourFunction)
                    -> YourFunction will run when a unit evades an attack.
 
            function GetMissingUnit takes nothing returns unit
                -> Returns the unit missing the attack
 
            function GetEvadingUnit takes nothing returns unit
                -> Returns the unit evading the attack
 
            function GetEvadedDamage takes nothing returns real
                -> Returns the amount of evaded damage
 
            function GetUnitEvasionChance takes unit u returns real
                -> Returns this system amount of evasion chance given to a unit
 
            function GetUnitMissChance takes unit u returns real
                -> Returns this system amount of miss chance given to a unit
 
            function SetUnitEvasionChance takes unit u, real chance returns nothing
                -> Set's unit evasion chance to specified amount
 
            function SetUnitMissChance takes unit u, real chance returns nothing
                -> Set'
s unit miss chance to specified amount
 
            function UnitAddEvasionChance takes unit u, real chance returns nothing
                -> Add to a unit Evasion chance the specified amount
 
            function UnitAddMissChance takes unit u, real chance returns nothing
                -> Add to a unit Miss chance the specified amount
 
            function UnitAddEvasionChanceTimed takes unit u, real amount, real duration returns nothing
                -> Add to a unit Evasion chance the specified amount for a given period
 
            function UnitAddMissChanceTimed takes unit u, real amount, real duration returns nothing
                -> Add to a unit Miss chance the specified amount for a given period
 
            function MakeUnitNeverMiss takes unit u, boolean flag returns nothing
                -> Will make a unit never miss attacks no matter the evasion chance of the attacked unit
 
            function DoUnitNeverMiss takes unit u returns boolean
                -> Returns true if the unit will never miss an attack
 
    //! endnovjass
    /* ----------------------------------- END ---------------------------------- */

        private struct Evasion
            //Text size of Critical Event
            static constant real text_size = 0.019
            //--------------------------------------------------
            readonly static trigger Evasion = CreateTrigger()
            static timer t = CreateTimer()
            static integer didx = -1
            static thistype array data
            //--------------------------------------------------
            static unit EvasionSource
            static unit EvasionTarget
            static real EvadedDamage
            static real array EvasionChance
            static real array MissChance
            static integer array NeverMiss
            //--------------------------------------------------
 
            unit    u
            real    amount
            real    ticks
            boolean evasion
 
            method destroy takes nothing returns nothing
                if didx == -1 then
                    call PauseTimer(t)
                endif
 
                set this.u     = null
                set this.ticks = 0
                call this.deallocate()
            endmethod
 
            static method GetEvasionChance takes unit u returns real
                return EvasionChance[GetUnitUserData(u)]
            endmethod
 
            static method GetMissChance takes unit u returns real
                return MissChance[GetUnitUserData(u)]
            endmethod
 
            static method SetEvasionChance takes unit u, real value returns nothing
                set EvasionChance[GetUnitUserData(u)] = value
            endmethod
 
            static method SetMissChance takes unit u, real value returns nothing
                set MissChance[GetUnitUserData(u)] = value
            endmethod
 
            static method OnPeriod takes nothing returns nothing
                local integer  i = 0
                local thistype this
 
                loop
                    exitwhen i > didx
                        set this = data[i]
                        set this.ticks = this.ticks - 1
 
                        if this.ticks <= 0 then
                            if this.evasion then
                                call SetEvasionChance(this.u, GetEvasionChance(this.u) - this.amount)
                            else
                                call SetMissChance(this.u, GetMissChance(this.u) - this.amount)
                            endif
 
                            set  data[i] = data[didx]
                            set  didx    = didx - 1
                            set  i         = i - 1
                            call this.destroy()
                        endif
                    set i = i + 1
                endloop
            endmethod
 
            static method AddTimed takes unit u, real amount, real duration, boolean evasion returns nothing
                local thistype this = thistype.allocate()
 
                set this.u       = u
                set this.amount  = amount
                set this.ticks   = duration/0.03125000
                set this.evasion = evasion
                set didx         = didx + 1
                set data[didx]   = this
 
                if evasion then
                    call SetEvasionChance(u, GetEvasionChance(u) + amount)
                else
                    call SetMissChance(u, GetMissChance(u) + amount)
                endif
 
                if didx == 0 then
                    call TimerStart(t, 0.03125000, true, function thistype.OnPeriod)
                endif
            endmethod
 
            static method EvasionText takes unit whichUnit, string text, real duration, integer red, integer green, integer blue, integer alpha returns nothing
                local texttag tx = CreateTextTag()
 
                call SetTextTagText(tx, text, text_size)
                call SetTextTagPosUnit(tx, whichUnit, 0)
                call SetTextTagColor(tx, red, green, blue, alpha)
                call SetTextTagLifespan(tx, duration)
                call SetTextTagVelocity(tx, 0.0, 0.0355)
                call SetTextTagPermanent(tx, false)
 
                set tx = null
            endmethod
 
            static method OnDamage takes nothing returns nothing
                local unit     src    = GetEventDamageSource()
                local unit     tgt    = BlzGetEventDamageTarget()
                //----------------------------------------------
                local real     damage = GetEventDamage()
                //----------------------------------------------
                local integer  sIdx   = GetUnitUserData(src)
                local integer  tIdx   = GetUnitUserData(tgt)
                //----------------------------------------------

                if damage > 0 and not (NeverMiss[sIdx] > 0) then
                    set evade = GetRandomReal(0, 100) <= EvasionChance[tIdx] or GetRandomReal(0, 100) <= MissChance[sIdx]

                    if evade then
                        set EvasionSource = src
                        set EvasionTarget = tgt
                        set EvadedDamage  = damage
 
                        call TriggerEvaluate(Evasion)
                        call BlzSetEventWeaponType(WEAPON_TYPE_WHOKNOWS)
                        call BlzSetEventDamage(0)
                        call EvasionText(src, "miss", 1.5, 255, 0, 0, 255)
 
                        set EvasionSource = null
                        set EvasionTarget = null
                        set EvadedDamage  = 0.0
                    endif
                endif
            endmethod
 
            static method Register takes code c returns nothing
                call TriggerAddCondition(Evasion, Filter(c))
            endmethod
 
            static method onInit takes nothing returns nothing
                call RegisterAttackDamagingEvent(function thistype.OnDamage)
            endmethod
        endstruct
 
        /* -------------------------------------------------------------------------- */
        /*                               Public JASS API                              */
        /* -------------------------------------------------------------------------- */
 
        function RegisterEvasionEvent takes code c returns nothing
            call Evasion.Register(c)
        endfunction
 
        function GetMissingUnit takes nothing returns unit
            return Evasion.EvasionSource
        endfunction
 
        function GetEvadingUnit takes nothing returns unit
            return Evasion.EvasionTarget
        endfunction
 
        function GetEvadedDamage takes nothing returns real
            return Evasion.EvadedDamage
        endfunction
 
        function GetUnitEvasionChance takes unit u returns real
            return Evasion.GetEvasionChance(u)
        endfunction
 
        function GetUnitMissChance takes unit u returns real
            return Evasion.GetMissChance(u)
        endfunction
 
        function SetUnitEvasionChance takes unit u, real chance returns nothing
            call Evasion.SetEvasionChance(u, chance)
        endfunction
 
        function SetUnitMissChance takes unit u, real chance returns nothing
            call Evasion.SetMissChance(u, chance)
        endfunction
 
        function UnitAddEvasionChance takes unit u, real chance returns nothing
            call Evasion.SetEvasionChance(u, Evasion.GetEvasionChance(u) + chance)
        endfunction
 
        function UnitAddMissChance takes unit u, real chance returns nothing
            call Evasion.SetMissChance(u, Evasion.GetMissChance(u) + chance)
        endfunction
 
        function UnitAddEvasionChanceTimed takes unit u, real amount, real duration returns nothing
            call Evasion.AddTimed(u, amount, duration, true)
        endfunction
 
        function UnitAddMissChanceTimed takes unit u, real amount, real duration returns nothing
            call Evasion.AddTimed(u, amount, duration, false)
        endfunction
 
        function MakeUnitNeverMiss takes unit u, boolean flag returns nothing
            if flag then
                set Evasion.NeverMiss[GetUnitUserData(u)] = Evasion.NeverMiss[GetUnitUserData(u)] + 1
            else
                set Evasion.NeverMiss[GetUnitUserData(u)] = Evasion.NeverMiss[GetUnitUserData(u)] - 1
            endif
        endfunction
 
        function DoUnitNeverMiss takes unit u returns boolean
            return Evasion.NeverMiss[GetUnitUserData(u)] > 0
        endfunction
endlibrary
 

Code (vJASS):

library CriticalStrike requires DamageInterface optional Evasion
    /* ------------------------ CriticalStrike v1.3 by Chopinski ----------------------- */
    //! novjass
 
        CriticalStrike implements an easy way to register and detect a custom critical event.
        allows the manipulation of a unit critical strike chance and multiplier, as well as
        manipulating the critical damage dealt.
 
        It works by monitoring custom critical strike chance and multiplier values given to units.
 
        It will only detect custom critical strikes, so all critical chance given to a
        unit must be done so using the public API provided by this system.
 
        *CriticalStrike requires DamageEvents. Do not use TriggerSleepAction() with Evasion.
        It also requires optional Evasion so that this library is written after the Evasion
        library, so both custom events will not fire at the same time.
 
        The API:
            To register a function to run when an unit hits a critical strike simply do:
                call RegisterCriticalStrikeEvent(function YourFunction)
                    -> YourFunction will run when a unit hits a critical strike.
 
            function GetCriticalSource takes nothing returns unit
                -> Returns the unit hitting a critical strike.
 
            function GetCriticalTarget takes nothing returns unit
                -> Returns the unit being hit by a critical strike.
 
            function GetCriticalDamage takes nothing returns real
                -> Returns the critical strike damage amount.
 
            function GetUnitCriticalChance takes unit u returns real
                -> Returns the chance to hit a critical strike to specified unit.
 
            function GetUnitCriticalMultiplier takes unit u returns real
                -> Returns the chance to hit a critical strike to specified unit.
 
            function SetUnitCriticalChance takes unit u, real value returns nothing
                -> Set's the unit chance to hit a critical strike to specified value.
                -> 15.0 = 15%
 
            function SetUnitCriticalMultiplier takes unit u, real value returns nothing
                -> Set'
s the unit multiplier of damage when hitting a critical to value
                -> 1.0 = increases the multiplier by 1. all units have a multiplier of 1.0
                    by default, so by adding 1.0, for example, the critical damage will be
                    2x the normal damage
 
            function SetCriticalEventDamage takes real newValue returns nothing
                -> Modify the critical damage dealt to the specified value.
 
            function UnitAddCriticalStrike takes unit u, real chance, real multiplier returns nothing
                -> Adds the specified values of chance and multiplier to a unit
 
            function UnitAddCriticalStrikeTimed takes unit u, real chance, real multiplier, real duration returns nothing
                -> Adds the specified values of chance and multiplier to a unit for a given period
 
            function UnitAddCriticalChanceTimed takes unit u, real chance, real duration returns nothing
                -> Adds the specified values of critical chance to a unit for a given period
 
            function UnitAddCriticalMultiplierTimed takes unit u, real multiplier, real duration returns nothing
                -> Adds the specified values of critical multiplier to a unit for a given period
 
    //! endnovjass
    /* ----------------------------------- END ---------------------------------- */
        private struct CriticalStrike
            //Text size of Critical Event
            static constant real text_size = 0.019
            //--------------------------------------------------
            readonly static trigger Critical = CreateTrigger()
            static timer t = CreateTimer()
            static integer didx = -1
            static thistype array data
            //--------------------------------------------------
            static unit CriticalSource
            static unit CriticalTarget
            static real CriticalDamage
            static real array CriticalChance
            static real array CriticalMultiplier
            //--------------------------------------------------
 
            unit    u
            real    chance
            real    multiplier
            real    ticks
            integer types
 
            method destroy takes nothing returns nothing
                if didx == -1 then
                    call PauseTimer(t)
                endif
 
                set this.u     = null
                set this.ticks = 0
                call this.deallocate()
            endmethod
 
            static method GetChance takes unit u returns real
                return CriticalChance[GetUnitUserData(u)]
            endmethod
 
            static method GetMultiplier takes unit u returns real
                return CriticalMultiplier[GetUnitUserData(u)]
            endmethod
 
            static method SetChance takes unit u, real value returns nothing
                set CriticalChance[GetUnitUserData(u)] = value
            endmethod
 
            static method SetMultiplier takes unit u, real value returns nothing
                set CriticalMultiplier[GetUnitUserData(u)] = value
            endmethod
 
            static method AddCritical takes unit u, real chance, real multuplier returns nothing
                call SetChance(u, GetChance(u) + chance)
                call SetMultiplier(u, GetMultiplier(u) + multuplier)
            endmethod
 
            static method OnPeriod takes nothing returns nothing
                local integer  i = 0
                local thistype this
 
                loop
                    exitwhen i > didx
                        set this = data[i]
                        set this.ticks = this.ticks - 1
 
                        if this.ticks <= 0 then
                            if this.types == 0 then
                                call AddCritical(this.u, -this.chance, -this.multiplier)
                            elseif this.types == 1 then
                                call SetChance(this.u, GetChance(this.u) - this.chance)
                            else
                                call SetMultiplier(this.u, GetMultiplier(this.u) - this.multiplier)
                            endif
 
                            set  data[i] = data[didx]
                            set  didx    = didx - 1
                            set  i         = i - 1
                            call this.destroy()
                        endif
                    set i = i + 1
                endloop
            endmethod
 
            static method AddTimed takes unit u, real chance, real multiplier, real duration, integer types returns nothing
                local thistype this = thistype.allocate()
 
                set this.u           = u
                set this.chance      = chance
                set this.multiplier  = multiplier
                set this.ticks       = duration/0.03125000
                set this.types       = types
                set didx             = didx + 1
                set data[didx]       = this
 
                if types == 0 then
                    call AddCritical(u, chance, multiplier)
                elseif types == 1 then
                    call SetChance(u, GetChance(u) + chance)
                else
                    call SetMultiplier(u, GetMultiplier(u) + multiplier)
                endif
 
                if didx == 0 then
                    call TimerStart(t, 0.03125000, true, function thistype.OnPeriod)
                endif
            endmethod
 
            static method CriticalText takes unit whichUnit, string text, real duration, integer red, integer green, integer blue, integer alpha returns nothing
                local texttag tx = CreateTextTag()
 
                call SetTextTagText(tx, text, text_size)
                call SetTextTagPosUnit(tx, whichUnit, 0)
                call SetTextTagColor(tx, red, green, blue, alpha)
                call SetTextTagLifespan(tx, duration)
                call SetTextTagVelocity(tx, 0.0, 0.0355)
                call SetTextTagPermanent(tx, false)
 
                set tx = null
            endmethod
 
            static method OnDamage takes nothing returns nothing
                local unit     src          = GetEventDamageSource()
                local unit     tgt          = BlzGetEventDamageTarget()
                //----------------------------------------------
                local real     damage       = GetEventDamage()
                //----------------------------------------------
                local integer  idx          = GetUnitUserData(src)
                //----------------------------------------------
                local boolean  isEnemy      = IsUnitEnemy(tgt, GetOwningPlayer(src))
                local boolean  notStructure = not IsUnitType(tgt, UNIT_TYPE_STRUCTURE)
                local boolean  criticalHit  = (GetRandomReal(0, 100) <= CriticalChance[idx])
                local boolean  process      = damage > 0 and criticalHit and isEnemy and notStructure and CriticalMultiplier[idx] > 0
                //----------------------------------------------
 
                if process then
                    set CriticalSource = src
                    set CriticalTarget = tgt
                    set CriticalDamage = damage*(1 + CriticalMultiplier[idx])
 
                    call TriggerEvaluate(Critical)
 
                    //in case of damage modification
                    call BlzSetEventDamage(CriticalDamage)
 
                    if CriticalDamage > 0 then
                        call CriticalText(tgt, (I2S(R2I(CriticalDamage)) + "!"), 1.5, 255, 0, 0, 255)
                    endif
 
                    set CriticalSource = null
                    set CriticalTarget = null
                    set CriticalDamage = 0.0
                endif
            endmethod
 
            static method Register takes code c returns nothing
                call TriggerAddCondition(Critical, Filter(c))
            endmethod
 
            static method onInit takes nothing returns nothing
                call RegisterAttackDamageEvent(function thistype.OnDamage)
            endmethod
        endstruct
 
        /* -------------------------------------------------------------------------- */
        /*                               Public JASS API                              */
        /* -------------------------------------------------------------------------- */
 
        function RegisterCriticalStrikeEvent takes code c returns nothing
            call CriticalStrike.Register(c)
        endfunction
 
        function GetCriticalSource takes nothing returns unit
            return CriticalStrike.CriticalSource
        endfunction
 
        function GetCriticalTarget takes nothing returns unit
            return CriticalStrike.CriticalTarget
        endfunction
 
        function GetCriticalDamage takes nothing returns real
            return CriticalStrike.CriticalDamage
        endfunction
 
        function GetUnitCriticalChance takes unit u returns real
            return CriticalStrike.GetChance(u)
        endfunction
 
        function GetUnitCriticalMultiplier takes unit u returns real
            return CriticalStrike.GetMultiplier(u)
        endfunction
 
        function SetUnitCriticalChance takes unit u, real value returns nothing
            call CriticalStrike.SetChance(u, value)
        endfunction
 
        function SetUnitCriticalMultiplier takes unit u, real value returns nothing
            call CriticalStrike.SetMultiplier(u, value)
        endfunction
 
        function SetCriticalEventDamage takes real newValue returns nothing
            set CriticalStrike.CriticalDamage = newValue
        endfunction
 
        function UnitAddCriticalStrike takes unit u, real chance, real multiplier returns nothing
            call CriticalStrike.AddCritical(u, chance, multiplier)
        endfunction
 
        function UnitAddCriticalStrikeTimed takes unit u, real chance, real multiplier, real duration returns nothing
            call CriticalStrike.AddTimed(u, chance, multiplier, duration, 0)
        endfunction
 
        function UnitAddCriticalChanceTimed takes unit u, real chance, real duration returns nothing
            call CriticalStrike.AddTimed(u, chance, 0, duration, 1)
        endfunction
 
        function UnitAddCriticalMultiplierTimed takes unit u, real multiplier, real duration returns nothing
            call CriticalStrike.AddTimed(u, 0, multiplier, duration, 2)
        endfunction
    endlibrary
 

Code (vJASS):

library SpellPower requires DamageInterface
/* ------------------------ SpellPower v1.3 by Chopinski ----------------------- */
//! novjass

    SpellPower intends to simulate a system similiar to Ability Power from LoL or
    Spell Amplification from Dota 2.

    Whenever a units deals Spell damage, that dealt damage will be amplified by a flat
    and percentile amount that represents a unit spell power bonus.

    The formula for amplification is:
    final damage = (initial damage + flat bonus) * (1 + percent bonus)
    for percent bonus: 0.1 = 10% bonus

    *SpellPower requires DamageEvents. Do not use TriggerSleepAction() within triggers.

    The API:
        function GetUnitSpellPowerFlat takes unit u returns real
            -> Returns the Flat bonus of spell power of a unit

        function GetUnitSpellPowerPercent takes unit u returns real
            -> Returns the Percent bonus of spell power of a unit

        function SetUnitSpellPowerFlat takes unit u, real value returns nothing
            -> Set the Flat amount of Spell Power of a unit

        function SetUnitSpellPowerPercent takes unit u, real value returns nothing
            -> Set the Flat amount of Spell Power of a unit

        function UnitAddSpellPowerFlat takes unit u, real amount returns nothing
            -> Add to the Flat amount of Spell Power of a unit

        function UnitAddSpellPowerPercent takes unit u, real amount returns nothing
            -> Add to the Percent amount of Spell Power of a unit

        function UnitAddSpellPowerFlatTimed takes unit u, real amount, real duration returns nothing
            -> Add to the Flat amount of Spell Power of a unit for a given period

        function UnitAddSpellPowerPercentTimed takes unit u, real amount, real duration returns nothing
            -> Add to the Percent amount of Spell Power of a unit for a given period

        // The Following functions exists to facilitate the preview of spell damage dealt given an amount.
        // Usefull when combining with string modification of a spell description or prediction of damage.
 
        function GetSpellDamage takes real amount, unit u returns real
            -> Returns the amount of spell damage that would be dealt given an initial damage

        function AbilitySpellDamage takes unit u, integer abilityId, abilityreallevelfield field returns string
            -> Given an ability field, will return a string that represents the damage that would be dealt
               taking into consideration the spell power bonusses of a unit.

        function AbilitySpellDamageEx takes real amount, unit u returns string
            -> Similar to GetSpellDamage will return the damage that would be dealt but as a string

//! endnovjass
/* ----------------------------------- END ---------------------------------- */
    private struct SpellPower
        readonly static trigger SpellPower = CreateTrigger()
        //--------------------------------------------------
        static timer t = CreateTimer()
        static integer didx = -1
        static thistype array data
        //--------------------------------------------------
        static real array Flat
        static real array Percent
        //--------------------------------------------------

        unit    u
        real    amount
        real    ticks
        boolean flat

        method destroy takes nothing returns nothing
            if didx == -1 then
                call PauseTimer(t)
            endif

            set this.u     = null
            set this.ticks = 0
            call this.deallocate()
        endmethod

        static method GetFlat takes unit u returns real
            return Flat[GetUnitUserData(u)]
        endmethod

        static method GetPercent takes unit u returns real
            return Percent[GetUnitUserData(u)]
        endmethod

        static method SetFlat takes unit u, real value returns nothing
            set Flat[GetUnitUserData(u)] = value
        endmethod

        static method SetPercent takes unit u, real value returns nothing
            set Percent[GetUnitUserData(u)] = value
        endmethod

        static method OnPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this
 
            loop
                exitwhen i > didx
                    set this = data[i]
                    set this.ticks = this.ticks - 1

                    if this.ticks <= 0 then
                        if this.flat then
                            call SetFlat(this.u, GetFlat(this.u) - this.amount)
                        else
                            call SetPercent(this.u, GetPercent(this.u) - this.amount)
                        endif

                        set  data[i] = data[didx]
                        set  didx    = didx - 1
                        set  i         = i - 1
                        call this.destroy()
                    endif
                set i = i + 1
            endloop
        endmethod

        static method AddTimed takes unit u, real amount, real duration, boolean flat returns nothing
            local thistype this = thistype.allocate()

            set this.u      = u
            set this.amount = amount
            set this.ticks  = duration/0.03125000
            set this.flat   = flat
            set didx        = didx + 1
            set data[didx]  = this

            if flat then
                call SetFlat(u, GetFlat(u) + amount)
            else
                call SetPercent(u, GetPercent(u) + amount)
            endif
 
            if didx == 0 then
                call TimerStart(t, 0.03125000, true, function thistype.OnPeriod)
            endif
        endmethod

        static method OnDamage takes nothing returns nothing
            local unit     src    = GetEventDamageSource()
            //----------------------------------------------
            local real     damage = GetEventDamage()
            //----------------------------------------------
            local integer  idx    = GetUnitUserData(src)
            //----------------------------------------------

            if damage > 0  then
                call BlzSetEventDamage((damage + Flat[idx]) * (1 + Percent[idx]))
            endif

            set src = null
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellDamageEvent(function thistype.OnDamage)
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                               Public JASS API                              */
    /* -------------------------------------------------------------------------- */

    function GetUnitSpellPowerFlat takes unit u returns real
        return SpellPower.GetFlat(u)
    endfunction

    function GetUnitSpellPowerPercent takes unit u returns real
        return SpellPower.GetPercent(u)
    endfunction

    function SetUnitSpellPowerFlat takes unit u, real value returns nothing
        call SpellPower.SetFlat(u, value)
    endfunction

    function SetUnitSpellPowerPercent takes unit u, real value returns nothing
        call SpellPower.SetPercent(u, value)
    endfunction

    function UnitAddSpellPowerFlat takes unit u, real amount returns nothing
        call SpellPower.SetFlat(u, SpellPower.GetFlat(u) + amount)
    endfunction

    function UnitAddSpellPowerPercent takes unit u, real amount returns nothing
        call SpellPower.SetPercent(u, SpellPower.GetPercent(u) + amount)
    endfunction

    function UnitAddSpellPowerFlatTimed takes unit u, real amount, real duration returns nothing
        call SpellPower.AddTimed(u, amount, duration, true)
    endfunction

    function UnitAddSpellPowerPercentTimed takes unit u, real amount, real duration returns nothing
        call SpellPower.AddTimed(u, amount, duration, false)
    endfunction

    // The Following functions exists to facilitate the preview of spell damage dealt given an amount.
    // Usefull when combining with string modification of a spell description or prediction of damage.
 
    function GetSpellDamage takes real amount, unit u returns real
        return ((amount + SpellPower.GetFlat(u)) * (1 + SpellPower.GetPercent(u)))
    endfunction

    function AbilitySpellDamage takes unit u, integer abilityId, abilityreallevelfield field returns string
        return I2S(R2I((BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilityId), field, GetUnitAbilityLevel(u, abilityId) - 1) + SpellPower.GetFlat(u)) * (1 + SpellPower.GetPercent(u))))
    endfunction

    function AbilitySpellDamageEx takes real amount, unit u returns string
        return I2S(R2I((amount + SpellPower.GetFlat(u)) * (1 + SpellPower.GetPercent(u))))
    endfunction

endlibrary
 

Code (vJASS):

library LifeSteal requires DamageInterface
/* ------------------------ LifeSteal v1.3 by Chopinski ----------------------- */
//! novjass

    LifeSteal intends to simulate the Life Steal system in warcraft, and allow you
    to easily change the life steal amount of any unit.

    Whenever a unit deals Physical damage, and it has a value of life steal given by
    this system, it will heal based of this value and the damage amount.

    The formula for life steal is:
    heal = damage * life steal
    fror life steal: 0.1 = 10%

    *LifeSteal requires DamageEvents. Do not use TriggerSleepAction() within triggers.

    The API:
        function SetUnitLifeSteal takes unit u, real amount returns nothing
            -> Set the Life Steal amount for a unit

        function GetUnitLifeSteal takes unit u returns real
            -> Returns the Life Steal amount of a unit

        function UnitAddLifeSteal takes unit u, real amount returns nothing
            -> Add to the Life Steal amount of a unit the given amount

        function UnitAddLifeStealTimed takes unit u, real amount, real duration returns nothing
            -> Add to the Life Steal amount of a unit the given amount for a given period

//! endnovjass
/* ----------------------------------- END ---------------------------------- */
    private struct LifeSteal
        static timer t = CreateTimer()
    //--------------------------------------------------
    static string sfx = "Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl"
        //--------------------------------------------------
        static integer didx = -1
        static thistype array data
        //--------------------------------------------------
    static real array LifeSteal
    //--------------------------------------------------

        unit u
        real amount
        real ticks

        method destroy takes nothing returns nothing
            if didx == -1 then
                call PauseTimer(t)
            endif

            set this.u     = null
            set this.ticks = 0
            call this.deallocate()
        endmethod

        static method Get takes unit u returns real
            return LifeSteal[GetUnitUserData(u)]
        endmethod

        static method Set takes unit u, real value returns nothing
            set LifeSteal[GetUnitUserData(u)] = value
        endmethod

        static method OnPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this
 
            loop
                exitwhen i > didx
                    set this = data[i]
                    set this.ticks = this.ticks - 1

                    if this.ticks <= 0 then
                        call Set(this.u, Get(this.u) - this.amount)

                        set  data[i] = data[didx]
                        set  didx    = didx - 1
                        set  i         = i - 1
                        call this.destroy()
                    endif
                set i = i + 1
            endloop
        endmethod

        static method AddTimed takes unit u, real amount, real duration returns nothing
            local thistype this = thistype.allocate()

            set this.u      = u
            set this.amount = amount
            set this.ticks  = duration/0.03125000
            set didx        = didx + 1
            set data[didx]  = this

 
            call Set(u, Get(u) + amount)
 
            if didx == 0 then
                call TimerStart(t, 0.03125000, true, function thistype.OnPeriod)
            endif
        endmethod

        static method OnDamage takes nothing returns nothing
            local unit    src          = GetEventDamageSource()
            local unit    tgt          = BlzGetEventDamageTarget()
            //----------------------------------------------
            local real    dmg          = GetEventDamage()
            //----------------------------------------------
            local integer idx          = GetUnitUserData(src)
            //----------------------------------------------
            local boolean notStructure = not IsUnitType(tgt, UNIT_TYPE_STRUCTURE)
            local boolean process      = dmg > 0 and LifeSteal[idx] > 0 and notStructure
            //----------------------------------------------

            if process then
                call SetWidgetLife(src, (GetWidgetLife(src) + (dmg * LifeSteal[idx])))
                call DestroyEffect(AddSpecialEffectTarget(sfx, src, "origin"))
            endif

            set src = null
            set tgt = null
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterAttackDamageEvent(function thistype.OnDamage)
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                               Public JASS API                              */
    /* -------------------------------------------------------------------------- */

    function SetUnitLifeSteal takes unit u, real amount returns nothing
        call LifeSteal.Set(u, amount)
    endfunction

    function GetUnitLifeSteal takes unit u returns real
        return LifeSteal.Get(u)
    endfunction

    function UnitAddLifeSteal takes unit u, real amount returns nothing
        call LifeSteal.Set(u, LifeSteal.Get(u) + amount)
    endfunction

    function UnitAddLifeStealTimed takes unit u, real amount, real duration returns nothing
        call LifeSteal.AddTimed(u, amount, duration)
    endfunction
endlibrary
 

Code (vJASS):

library SpellVamp requires DamageInterface
/* ------------------------ SpellVamp v1.3 by Chopinski ----------------------- */
//! novjass

    SpellVamp intends to introduce to warcraft a healing based on Spell damage, like
    in LoL or Dota 2.

    Whenever a unit deals Spell damage, and it has a value of spell vamp given by
    this system, it will heal based of this value and the damage amount.

    The formula for spell vamp is:
    heal = damage * lspell vamp
    fror spell vamp: 0.1 = 10%

    *SpellVamp requires DamageEvents. Do not use TriggerSleepAction() within triggers.

    The API:
        function SetUnitSpellVamp takes unit u, real amount returns nothing
            -> Set the Spell Vamp amount for a unit
 
        function GetUnitSpellVamp takes unit u returns real
            -> Returns the Spell Vamp amount of a unit
 
        function UnitAddSpellVamp takes unit u, real amount returns nothing
            -> Add to the Spell Vamp amount of a unit the given amount
 
        function UnitAddSpellVampTimed takes unit u, real amount, real duration returns nothing
            -> Add to the Spell Vamp amount of a unit the given amount for a given period

//! endnovjass
/* ----------------------------------- END ---------------------------------- */
    private struct SpellVamp
        static timer t = CreateTimer()
        //--------------------------------------------------
        static integer didx = -1
        static thistype array data
        //--------------------------------------------------
        static real array SpellVamp
        //--------------------------------------------------

        unit u
        real amount
        real ticks

        method destroy takes nothing returns nothing
            if didx == -1 then
                call PauseTimer(t)
            endif

            set this.u     = null
            set this.ticks = 0
            call this.deallocate()
        endmethod

        static method Get takes unit u returns real
            return SpellVamp[GetUnitUserData(u)]
        endmethod

        static method Set takes unit u, real value returns nothing
            set SpellVamp[GetUnitUserData(u)] = value
        endmethod

        static method OnPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this
 
            loop
                exitwhen i > didx
                    set this = data[i]
                    set this.ticks = this.ticks - 1

                    if this.ticks <= 0 then
                        call Set(this.u, Get(this.u) - this.amount)

                        set  data[i] = data[didx]
                        set  didx    = didx - 1
                        set  i         = i - 1
                        call this.destroy()
                    endif
                set i = i + 1
            endloop
        endmethod

        static method AddTimed takes unit u, real amount, real duration returns nothing
            local thistype this = thistype.allocate()

            set this.u      = u
            set this.amount = amount
            set this.ticks  = duration/0.03125000
            set didx        = didx + 1
            set data[didx]  = this

 
            call Set(u, Get(u) + amount)
 
            if didx == 0 then
                call TimerStart(t, 0.03125000, true, function thistype.OnPeriod)
            endif
        endmethod

        static method OnDamage takes nothing returns nothing
            local unit    src          = GetEventDamageSource()
            local unit    tgt          = BlzGetEventDamageTarget()
            //----------------------------------------------
            local integer idx          = GetUnitUserData(src)
            //----------------------------------------------
            local real    dmg          = GetEventDamage()
            local real    sp           = SpellVamp[idx]
            //----------------------------------------------
            local boolean notStructure = not IsUnitType(tgt, UNIT_TYPE_STRUCTURE)
            local boolean process      = dmg > 0 and sp > 0 and notStructure
            //----------------------------------------------

            if process then
                call SetWidgetLife(src, (GetWidgetLife(src) + (dmg * sp)))
            endif

            set src = null
            set tgt = null
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellDamageEvent(function thistype.OnDamage)
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                               Public JASS API                              */
    /* -------------------------------------------------------------------------- */

    function SetUnitSpellVamp takes unit u, real amount returns nothing
        call SpellVamp.Set(u, amount)
    endfunction

    function GetUnitSpellVamp takes unit u returns real
        return SpellVamp.Get(u)
    endfunction

    function UnitAddSpellVamp takes unit u, real amount returns nothing
        call SpellVamp.Set(u, SpellVamp.Get(u) + amount)
    endfunction

    function UnitAddSpellVampTimed takes unit u, real amount, real duration returns nothing
        call SpellVamp.AddTimed(u, amount, duration)
    endfunction
endlibrary
 

Code (vJASS):

library DamageInterface requires optional Table
    /* --------------------- DamageInterface v1.9 by Chopinski --------------------- */
    // Allows for easy registration of specific damage type events like on attack
    // damage or on spell damage, etc...
 
    // How To Use?
    //     It works very similar to the RegisterPlayerUnitEvent and SpellEffectEvent
    //     library's, it's intended to turn this?
 
    //     private function OnDamage takes nothing returns nothing
    //         local boolean isSpell = BlzGetEventAttackType() == ATTACK_TYPE_NORMAL
 
    //         if isSpell then
    //             //do stuff
    //         endif
    //     endfunction
 
    //     private function Init takes nothing returns nothing
    //     local trigger t = CreateTrigger()
    //     call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DAMAGED)
    //     call TriggerAddCondition(t, Condition(function OnDamage))
    //     endfunction
 
    //     Into this:
 
    //     private function OnDamage takes nothing returns nothing
    //         //Do Stuff
    //     endfunction
 
    //     private function Init takes nothing returns nothing
    //     call RegisterSpellDamageEvent(function OnDamage)
    //     endfunction

    //     Added on v1.1 - Included functionality to allow registration of damage on before
    //     damage mitigation

    //     to register before damage mitigation simply do:

    //     private function OnDamage takes nothing returns nothing
    //     //Do Stuff
    //     endfunction

    //     private function Init takes nothing returns nothing
    //     call RegisterSpellDamagingEvent(function OnDamage)
    //     endfunction

    //     call RegisterSpellDamageEvent(function OnDamage)
    //     -> uses EVENT_PLAYER_UNIT_DAMAGED

    //     call RegisterSpellDamagingEvent(function OnDamage)
    //     -> uses EVENT_PLAYER_UNIT_DAMAGING
 
    /* ----------------------------------- END ---------------------------------- */
    globals
        // This constant is used to define if the system will cache
        // extra information from a Damage Event, like the unit
        // Custom value (UnitUserData), a unit Handle Id, and more
        // Additionaly you can see the CacheResponse method below
        // to have an idea and comment the members you want cached or not
        private constant boolean CACHE_EXTRA = true
    endglobals

    struct DamageI extends array
        //Hashtables for after and before events
        static if LIBRARY_Table then
            private static HashTable afterTable
            private static HashTable beforeTable
        else
            private static hashtable afterTable = InitHashtable()
            private static hashtable beforeTable = InitHashtable()
        endif

        // Core evaluation triggers
        static trigger Damaged  = CreateTrigger()
        static trigger Damaging = CreateTrigger()

        /* --------------- Members -------------- */
        readonly static damagetype damagetype
        readonly static attacktype attacktype
        readonly static unit       source
        readonly static unit       target
        readonly static player     sourcePlayer
        readonly static player     targetPlayer
        readonly static boolean    isEnemy
        readonly static boolean    isAlly
        readonly static boolean    isMelee
        readonly static boolean    isRanged
        readonly static boolean    isAttack
        readonly static boolean    isSpell
        readonly static boolean    sourceIsHero
        readonly static boolean    targetIsHero
        readonly static boolean    structure
        readonly static boolean    magicImmune
        readonly static real       sourceX
        readonly static real       sourceY
        readonly static real       targetX
        readonly static real       targetY
        readonly static integer    sIdx
        readonly static integer    tIdx
        readonly static integer    sId
        readonly static integer    tId

        // Setting the members of the struct
        private static method cache takes unit src, unit tgt, damagetype dmg_type, attacktype atk_type returns nothing
            set damagetype = dmg_type
            set attacktype = atk_type
            set source     = src
            set target     = tgt
            set isAttack   = damagetype == DAMAGE_TYPE_NORMAL
            set isSpell    = attacktype == ATTACK_TYPE_NORMAL

            // You can comment the members you dont want to be cached
            // or set CACHE_EXTRA = false to not save them at all
            if CACHE_EXTRA then
                set sourcePlayer  = GetOwningPlayer(source)
                set targetPlayer  = GetOwningPlayer(target)
                set isEnemy       = IsUnitEnemy(target, sourcePlayer)
                set isAlly        = IsUnitAlly(target, sourcePlayer)
                set isMelee       = IsUnitType(source, UNIT_TYPE_MELEE_ATTACKER)
                set isRanged      = IsUnitType(source, UNIT_TYPE_RANGED_ATTACKER)
                set sourceIsHero  = IsUnitType(source, UNIT_TYPE_HERO)
                set targetIsHero  = IsUnitType(target, UNIT_TYPE_HERO)
                set structure     = IsUnitType(target, UNIT_TYPE_STRUCTURE)
                set magicImmune   = IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)
                set sourceX       = GetUnitX(source)
                set sourceY       = GetUnitY(source)
                set targetX       = GetUnitX(target)
                set targetY       = GetUnitY(target)
                set sIdx          = GetUnitUserData(source)
                set tIdx          = GetUnitUserData(target)
                set sId           = GetHandleId(source)
                set tId           = GetHandleId(target)
            endif
        endmethod

        private static method onDamaging takes nothing returns nothing
            local integer i
            local integer j

            call cache(GetEventDamageSource(), BlzGetEventDamageTarget(), BlzGetEventDamageType(), BlzGetEventAttackType())

            if damagetype != DAMAGE_TYPE_UNKNOWN then
                set i = GetHandleId(attacktype)
                set j = GetHandleId(damagetype)

                static if LIBRARY_Table then
                    if beforeTable[i].trigger[j] != null then
                        call TriggerEvaluate(beforeTable[i].trigger[j])
                    endif

                    if beforeTable[i].trigger[0] != null then
                        call TriggerEvaluate(beforeTable[i].trigger[0])
                    endif

                    if beforeTable[0].trigger[j] != null then
                        call TriggerEvaluate(beforeTable[0].trigger[j])
                    endif
                else
                    if HaveSavedHandle(beforeTable, i, j) then
                        call TriggerEvaluate(LoadTriggerHandle(beforeTable, i, j))
                    endif

                    if HaveSavedHandle(beforeTable, i, 0) then
                        call TriggerEvaluate(LoadTriggerHandle(beforeTable, i, 0))
                    endif

                    if HaveSavedHandle(beforeTable, 0, j) then
                        call TriggerEvaluate(LoadTriggerHandle(beforeTable, 0, j))
                    endif
                endif
            endif
        endmethod

        private static method onDamage takes nothing returns nothing
            local integer i
            local integer j

            call cache(GetEventDamageSource(), BlzGetEventDamageTarget(), BlzGetEventDamageType(), BlzGetEventAttackType())

            if damagetype != DAMAGE_TYPE_UNKNOWN then
                set i = GetHandleId(attacktype)
                set j = GetHandleId(damagetype)

                static if LIBRARY_Table then
                    if afterTable[i].trigger[j] != null then
                        call TriggerEvaluate(afterTable[i].trigger[j])
                    endif

                    if afterTable[i].trigger[0] != null then
                        call TriggerEvaluate(afterTable[i].trigger[0])
                    endif

                    if afterTable[0].trigger[j] != null then
                        static if LIBRARY_Evasion then
                            if isAttack then
                                if not Evasion.evade then
                                    call TriggerEvaluate(afterTable[0].trigger[j])
                                endif
                            else
                                call TriggerEvaluate(afterTable[0].trigger[j])
                            endif
                        else
                            call TriggerEvaluate(afterTable[0].trigger[j])
                        endif
                    endif
                else
                    if HaveSavedHandle(afterTable, i, j) then
                        call TriggerEvaluate(LoadTriggerHandle(afterTable, i, j))
                    endif

                    if HaveSavedHandle(afterTable, i, 0) then
                        call TriggerEvaluate(LoadTriggerHandle(afterTable, i, 0))
                    endif

                    if HaveSavedHandle(afterTable, 0, j) then
                        static if LIBRARY_Evasion then
                            if isAttack then
                                if not Evasion.evade then
                                    call TriggerEvaluate(LoadTriggerHandle(afterTable, 0, j))
                                endif
                            else
                                call TriggerEvaluate(LoadTriggerHandle(afterTable, 0, j))
                            endif
                        else
                            call TriggerEvaluate(LoadTriggerHandle(afterTable, 0, j))
                        endif
                    endif
                endif
            endif
        endmethod

        static method register takes attacktype attack, damagetype damage, code c, boolean after returns nothing
            local integer i = GetHandleId(attack)
            local integer j = GetHandleId(damage)

            if after then
                static if LIBRARY_Table then
                    if afterTable[i].trigger[j] == null then
                        set afterTable[i].trigger[j] = CreateTrigger()
                    endif
                    call TriggerAddCondition(afterTable[i].trigger[j], Filter(c))
                else
                    if not HaveSavedHandle(afterTable, i, j) then
                        call SaveTriggerHandle(afterTable, i, j, CreateTrigger())
                    endif
                    call TriggerAddCondition(LoadTriggerHandle(afterTable, i, j), Filter(c))
                endif
            else
                static if LIBRARY_Table then
                    if beforeTable[i].trigger[j] == null then
                        set beforeTable[i].trigger[j] = CreateTrigger()
                    endif
                    call TriggerAddCondition(beforeTable[i].trigger[j], Filter(c))
                else
                    if not HaveSavedHandle(beforeTable, i, j) then
                        call SaveTriggerHandle(beforeTable, i, j, CreateTrigger())
                    endif
                    call TriggerAddCondition(LoadTriggerHandle(beforeTable, i, j), Filter(c))
                endif
            endif
        endmethod
 
        static method onInit takes nothing returns nothing
            static if LIBRARY_Table then
                set afterTable = HashTable.create()
                set beforeTable = HashTable.create()
            endif

            call TriggerRegisterAnyUnitEventBJ(Damaged, EVENT_PLAYER_UNIT_DAMAGED)
            call TriggerAddCondition(Damaged, Condition(function thistype.onDamage))

            call TriggerRegisterAnyUnitEventBJ(Damaging, EVENT_PLAYER_UNIT_DAMAGING)
            call TriggerAddCondition(Damaging, Condition(function thistype.onDamaging))
        endmethod
    endstruct
 
    /* -------------------------------------------------------------------------- */
    /*                               Public JASS API                              */
    /* -------------------------------------------------------------------------- */

    //After damage mitigation registration

    function RegisterAttackDamageEvent takes code c returns nothing
        call DamageI.register(null, DAMAGE_TYPE_NORMAL, c, true)
    endfunction
 
    function RegisterSpellDamageEvent takes code c returns nothing
        call DamageI.register(ATTACK_TYPE_NORMAL, null, c, true)
    endfunction

    function RegisterDamageEvent takes attacktype attack, damagetype damage, code c returns nothing
        call DamageI.register(attack, damage, c, true)
    endfunction

    function RegisterAnyDamageEvent takes code c returns nothing
        call TriggerAddCondition(DamageI.Damaged, Filter(c))
    endfunction

    //Before damage mitigation registration

    function RegisterAttackDamagingEvent takes code c returns nothing
        call DamageI.register(null, DAMAGE_TYPE_NORMAL, c, false)
    endfunction
 
    function RegisterSpellDamagingEvent takes code c returns nothing
        call DamageI.register(ATTACK_TYPE_NORMAL, null, c, false)
    endfunction

    function RegisterDamagingEvent takes attacktype attack, damagetype damage, code c returns nothing
        call DamageI.register(attack, damage, c, false)
    endfunction

    function RegisterAnyDamagingEvent takes code c returns nothing
        call TriggerAddCondition(DamageI.Damaging, Filter(c))
    endfunction
endlibrary
 

Code (vJASS):

library Evasion requires DamageInterface
    /* ------------------------ Evasion v1.9 by Chopinski ----------------------- */
    // Evasion implements an easy way to register and detect a custom evasion event.

    // It works by monitoring custom evasion and missing values given to units,
    // and nulling damage when the odds given occurs.

    // It will only detect custom evasion, so all evasion or miss values given to a
    // unit must be done so using the public API provided by this system.

    // *Evasion requires DamageInterface. Do not use TriggerSleepAction() with Evasion.

    // The API:
    //     function RegisterEvasionEvent(function YourFunction)
    //         -> YourFunction will run when a unit evades an attack.

    //     function GetMissingUnit takes nothing returns unit
    //         -> Returns the unit missing the attack

    //     function GetEvadingUnit takes nothing returns unit
    //         -> Returns the unit evading the attack

    //     function GetEvadedDamage takes nothing returns real
    //         -> Returns the amount of evaded damage

    //     function GetUnitEvasionChance takes unit u returns real
    //         -> Returns this system amount of evasion chance given to a unit

    //     function GetUnitMissChance takes unit u returns real
    //         -> Returns this system amount of miss chance given to a unit

    //     function SetUnitEvasionChance takes unit u, real chance returns nothing
    //         -> Sets unit evasion chance to specified amount

    //     function SetUnitMissChance takes unit u, real chance returns nothing
    //         -> Sets unit miss chance to specified amount

    //     function UnitAddEvasionChance takes unit u, real chance returns nothing
    //         -> Add to a unit Evasion chance the specified amount

    //     function UnitAddMissChance takes unit u, real chance returns nothing
    //         -> Add to a unit Miss chance the specified amount

    //     function MakeUnitNeverMiss takes unit u, boolean flag returns nothing
    //         -> Will make a unit never miss attacks no matter the evasion chance of the attacked unit

    //     function DoUnitNeverMiss takes unit u returns boolean
    //         -> Returns true if the unit will never miss an attack
        /* ----------------------------------- END ---------------------------------- */
        //region System
        struct Evasion
            static unit             source
            static unit             target
            static real             damage
            static real    array    evasion
            static real    array    miss
            static integer array    neverMiss
            readonly static boolean evade     = false
            readonly static trigger trigger   = CreateTrigger()
            static constant real    TEXT_SIZE = 0.016
 
            static method getEvasionChance takes unit u returns real
                return evasion[GetUnitUserData(u)]
            endmethod
 
            static method getMissChance takes unit u returns real
                return miss[GetUnitUserData(u)]
            endmethod
 
            static method setEvasionChance takes unit u, real value returns nothing
                set evasion[GetUnitUserData(u)] = value
            endmethod
 
            static method setMissChance takes unit u, real value returns nothing
                set miss[GetUnitUserData(u)] = value
            endmethod
 
            static method text takes unit whichUnit, string text, real duration, integer red, integer green, integer blue, integer alpha returns nothing
                local texttag tx = CreateTextTag()
           
                call SetTextTagText(tx, text, TEXT_SIZE)
                call SetTextTagPosUnit(tx, whichUnit, 0)
                call SetTextTagColor(tx, red, green, blue, alpha)
                call SetTextTagLifespan(tx, duration)
                call SetTextTagVelocity(tx, 0.0, 0.0355)
                call SetTextTagPermanent(tx, false)
           
                set tx = null
            endmethod
 
            static method onDamage takes nothing returns nothing
                local real amount = GetEventDamage()

                set evade = false
                if amount > 0 and not (neverMiss[DamageI.sIdx] > 0) then
                    set evade = GetRandomReal(0, 100) <= evasion[DamageI.tIdx] or GetRandomReal(0, 100) <= miss[DamageI.sIdx]
                    if evade then
                        set source = DamageI.source
                        set target = DamageI.target
                        set damage = amount
 
                        call TriggerEvaluate(trigger)
                        call BlzSetEventDamage(0)
                        call BlzSetEventWeaponType(WEAPON_TYPE_WHOKNOWS)
                        call text(source, "miss", 1.5, 255, 0, 0, 255)
 
                        set source = null
                        set target = null
                        set damage = 0.0
                    endif
                endif
            endmethod
 
            static method register takes code c returns nothing
                call TriggerAddCondition(trigger, Filter(c))
            endmethod
 
            static method onInit takes nothing returns nothing
                call RegisterAttackDamagingEvent(function thistype.onDamage)
            endmethod
        endstruct
        //endregion
 
        /* -------------------------------------------------------------------------- */
        /*                               Public JASS API                              */
        /* -------------------------------------------------------------------------- */
 
        function RegisterEvasionEvent takes code c returns nothing
            call Evasion.register(c)
        endfunction
 
        function GetMissingUnit takes nothing returns unit
            return Evasion.source
        endfunction
 
        function GetEvadingUnit takes nothing returns unit
            return Evasion.target
        endfunction
 
        function GetEvadedDamage takes nothing returns real
            return Evasion.damage
        endfunction
 
        function GetUnitEvasionChance takes unit u returns real
            return Evasion.getEvasionChance(u)
        endfunction
 
        function GetUnitMissChance takes unit u returns real
            return Evasion.getMissChance(u)
        endfunction
 
        function SetUnitEvasionChance takes unit u, real chance returns nothing
            call Evasion.setEvasionChance(u, chance)
        endfunction
 
        function SetUnitMissChance takes unit u, real chance returns nothing
            call Evasion.setMissChance(u, chance)
        endfunction
 
        function UnitAddEvasionChance takes unit u, real chance returns nothing
            call Evasion.setEvasionChance(u, Evasion.getEvasionChance(u) + chance)
        endfunction
 
        function UnitAddMissChance takes unit u, real chance returns nothing
            call Evasion.setMissChance(u, Evasion.getMissChance(u) + chance)
        endfunction
 
        function MakeUnitNeverMiss takes unit u, boolean flag returns nothing
            if flag then
                set Evasion.neverMiss[GetUnitUserData(u)] = Evasion.neverMiss[GetUnitUserData(u)] + 1
            else
                set Evasion.neverMiss[GetUnitUserData(u)] = Evasion.neverMiss[GetUnitUserData(u)] - 1
            endif
        endfunction
 
        function DoUnitNeverMiss takes unit u returns boolean
            return Evasion.neverMiss[GetUnitUserData(u)] > 0
        endfunction
    endlibrary
 

Code (vJASS):

library CriticalStrike requires DamageInterface optional Evasion
    /* ------------------------ CriticalStrike v1.9 by Chopinski ----------------------- */
    // CriticalStrike implements an easy way to register and detect a custom critical event.
    // allows the manipulation of a unit critical strike chance and multiplier, as well as
    // manipulating the critical damage dealt.

    // It works by monitoring custom critical strike chance and multiplier values given to units.

    // It will only detect custom critical strikes, so all critical chance given to a
    // unit must be done so using the public API provided by this system.

    // *CriticalStrike requires DamageInterface. Do not use TriggerSleepAction() with Evasion.
    // It also requires optional Evasion so that this library is written after the Evasion
    // library, so both custom events will not fire at the same time.

    // The API:
    //     function RegisterCriticalStrikeEvent(function YourFunction)
    //         -> YourFunction will run when a unit hits a critical strike.

    //     function GetCriticalSource takes nothing returns unit
    //         -> Returns the unit hitting a critical strike.
 
    //     function GetCriticalTarget takes nothing returns unit
    //         -> Returns the unit being hit by a critical strike.
 
    //     function GetCriticalDamage takes nothing returns real
    //         -> Returns the critical strike damage amount.
 
    //     function GetUnitCriticalChance takes unit u returns real
    //         -> Returns the chance to hit a critical strike to specified unit.
 
    //     function GetUnitCriticalMultiplier takes unit u returns real
    //         -> Returns the chance to hit a critical strike to specified unit.
 
    //     function SetUnitCriticalChance takes unit u, real value returns nothing
    //         -> Set's the unit chance to hit a critical strike to specified value.
    //         -> 15.0 = 15%
 
    //     function SetUnitCriticalMultiplier takes unit u, real value returns nothing
    //         -> Set's the unit multiplier of damage when hitting a critical to value
    //         -> 1.0 = increases the multiplier by 1. all units have a multiplier of 1.0
    //             by default, so by adding 1.0, for example, the critical damage will be
    //             2x the normal damage

    //     function SetCriticalEventDamage takes real newValue returns nothing
    //         -> Modify the critical damage dealt to the specified value.
 
    //     function UnitAddCriticalStrike takes unit u, real chance, real multiplier returns nothing
    //         -> Adds the specified values of chance and multiplier to a unit
    /* ----------------------------------- END ---------------------------------- */
    //region System
    struct CriticalStrike
        static constant real    TEXT_SIZE = 0.016
        readonly static trigger trigger = CreateTrigger()
        static unit             source
        static unit             target
        static real             damage
        static real array       chance
        static real array       multiplier

        static method getChance takes unit u returns real
            return chance[GetUnitUserData(u)]
        endmethod

        static method getMultiplier takes unit u returns real
            return multiplier[GetUnitUserData(u)]
        endmethod

        static method setChance takes unit u, real value returns nothing
            set chance[GetUnitUserData(u)] = value
        endmethod

        static method setMultiplier takes unit u, real value returns nothing
            set multiplier[GetUnitUserData(u)] = value
        endmethod

        static method add takes unit u, real chance, real multuplier returns nothing
            call setChance(u, getChance(u) + chance)
            call setMultiplier(u, getMultiplier(u) + multuplier)
        endmethod

        static method text takes unit whichUnit, string text, real duration, integer red, integer green, integer blue, integer alpha returns nothing
            local texttag tx = CreateTextTag()
       
            call SetTextTagText(tx, text, TEXT_SIZE)
            call SetTextTagPosUnit(tx, whichUnit, 0)
            call SetTextTagColor(tx, red, green, blue, alpha)
            call SetTextTagLifespan(tx, duration)
            call SetTextTagVelocity(tx, 0.0, 0.0355)
            call SetTextTagPermanent(tx, false)
       
            set tx = null
        endmethod

        static method onDamage takes nothing returns nothing
            local real amount = GetEventDamage()

            if amount > 0 and GetRandomReal(0, 100) <= chance[DamageI.sIdx] and DamageI.isEnemy and not DamageI.structure and multiplier[DamageI.sIdx] > 0 then
                set source = DamageI.source
                set target = DamageI.target
                set damage = amount*(1 + multiplier[DamageI.sIdx])

                call TriggerEvaluate(trigger)
                call BlzSetEventDamage(damage) // in case of damage modification

                if damage > 0 then
                    call text(target, (I2S(R2I(damage)) + "!"), 1.5, 255, 0, 0, 255)
                endif

                set source = null
                set target = null
                set damage = 0.0
            endif
        endmethod

        static method register takes code c returns nothing
            call TriggerAddCondition(trigger, Filter(c))
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterAttackDamageEvent(function thistype.onDamage)
        endmethod
    endstruct
    //endregion
 
    /* -------------------------------------------------------------------------- */
    /*                               Public JASS API                              */
    /* -------------------------------------------------------------------------- */

    function RegisterCriticalStrikeEvent takes code c returns nothing
        call CriticalStrike.register(c)
    endfunction

    function GetCriticalSource takes nothing returns unit
        return CriticalStrike.source
    endfunction

    function GetCriticalTarget takes nothing returns unit
        return CriticalStrike.target
    endfunction

    function GetCriticalDamage takes nothing returns real
        return CriticalStrike.damage
    endfunction

    function GetUnitCriticalChance takes unit u returns real
        return CriticalStrike.getChance(u)
    endfunction

    function GetUnitCriticalMultiplier takes unit u returns real
        return CriticalStrike.getMultiplier(u)
    endfunction

    function SetUnitCriticalChance takes unit u, real value returns nothing
        call CriticalStrike.setChance(u, value)
    endfunction

    function SetUnitCriticalMultiplier takes unit u, real value returns nothing
        call CriticalStrike.setMultiplier(u, value)
    endfunction

    function SetCriticalEventDamage takes real newValue returns nothing
        set CriticalStrike.damage = newValue
    endfunction

    function UnitAddCriticalStrike takes unit u, real chance, real multiplier returns nothing
        call CriticalStrike.add(u, chance, multiplier)
    endfunction
endlibrary
 

Code (vJASS):

library SpellPower requires DamageInterface
    /* ------------------------ SpellPower v1.9 by Chopinski ----------------------- */
    // SpellPower intends to simulate a system similiar to Ability Power from LoL or
    // Spell Amplification from Dota 2.

    // Whenever a units deals Spell damage, that dealt damage will be amplified by a flat
    // and percentile amount that represents a unit spell power bonus.

    // The formula for amplification is:
    // final damage = (initial damage + flat bonus) * (1 + percent bonus)
    // for percent bonus: 0.1 = 10% bonus

    // *SpellPower requires DamageInterface. Do not use TriggerSleepAction() within triggers.

    // The API:
    //     function GetUnitSpellPowerFlat takes unit u returns real
    //         -> Returns the Flat bonus of spell power of a unit

    //     function GetUnitSpellPowerPercent takes unit u returns real
    //         -> Returns the Percent bonus of spell power of a unit

    //     function SetUnitSpellPowerFlat takes unit u, real value returns nothing
    //         -> Set's the Flat amount of Spell Power of a unit

    //     function SetUnitSpellPowerPercent takes unit u, real value returns nothing
    //         -> Set's the Flat amount of Spell Power of a unit

    //     function UnitAddSpellPowerFlat takes unit u, real amount returns nothing
    //         -> Add to the Flat amount of Spell Power of a unit

    //     function UnitAddSpellPowerPercent takes unit u, real amount returns nothing
    //         -> Add to the Percent amount of Spell Power of a unit

    //     function GetSpellDamage takes real amount, unit u returns real
    //         -> Returns the amount of spell damage that would be dealt given an initial damage
    /* ----------------------------------- END ---------------------------------- */
    //region System
    struct SpellPower
        static real array flat
        static real array percent

        static method getFlat takes unit u returns real
            return flat[GetUnitUserData(u)]
        endmethod

        static method getPercent takes unit u returns real
            return percent[GetUnitUserData(u)]
        endmethod

        static method setFlat takes unit u, real value returns nothing
            set flat[GetUnitUserData(u)] = value
        endmethod

        static method setPercent takes unit u, real value returns nothing
            set percent[GetUnitUserData(u)] = value
        endmethod

        static method onDamage takes nothing returns nothing
            local real damage = GetEventDamage()

            if damage > 0 then
                call BlzSetEventDamage((damage + flat[DamageI.sIdx])*(1 + percent[DamageI.sIdx]))
            endif
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellDamageEvent(function thistype.onDamage)
        endmethod
    endstruct
    //endregion
 
    /* -------------------------------------------------------------------------- */
    /*                               Public JASS API                              */
    /* -------------------------------------------------------------------------- */

    function GetUnitSpellPowerFlat takes unit u returns real
        return SpellPower.getFlat(u)
    endfunction

    function GetUnitSpellPowerPercent takes unit u returns real
        return SpellPower.getPercent(u)
    endfunction

    function SetUnitSpellPowerFlat takes unit u, real value returns nothing
        call SpellPower.setFlat(u, value)
    endfunction

    function SetUnitSpellPowerPercent takes unit u, real value returns nothing
        call SpellPower.setPercent(u, value)
    endfunction

    function UnitAddSpellPowerFlat takes unit u, real amount returns nothing
        call SpellPower.setFlat(u, SpellPower.getFlat(u) + amount)
    endfunction

    function UnitAddSpellPowerPercent takes unit u, real amount returns nothing
        call SpellPower.setPercent(u, SpellPower.getPercent(u) + amount)
    endfunction

    function GetSpellDamage takes real amount, unit u returns real
        return ((amount + SpellPower.getFlat(u))*(1 + SpellPower.getPercent(u)))
    endfunction
endlibrary
 

Code (vJASS):

library LifeSteal requires DamageInterface
    /* ------------------------ LifeSteal v1.9 by Chopinski ----------------------- */
    // LifeSteal intends to simulate the Life Steal system in warcraft, and allow you
    // to easily change the life steal amount of any unit.

    // Whenever a unit deals Physical damage, and it has a value of life steal given by
    // this system, it will heal based of this value and the damage amount.

    // The formula for life steal is:
    // heal = damage * life steal
    // fror life steal: 0.1 = 10%

    // *LifeSteal requires DamageInterface. Do not use TriggerSleepAction() within triggers.

    // The API:
    //     function SetUnitLifeSteal takes unit u, real amount returns nothing
    //         -> Set the Life Steal amount for a unit

    //     function GetUnitLifeSteal takes unit u returns real
    //         -> Returns the Life Steal amount of a unit

    //     function UnitAddLifeSteal takes unit u, real amount returns nothing
    //         -> Add to the Life Steal amount of a unit the given amount
    /* ----------------------------------- END ---------------------------------- */
    //region System
    struct LifeSteal
        static string     effect = "Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl"
        static real array amount

        static method Get takes unit u returns real
            return amount[GetUnitUserData(u)]
        endmethod

        static method Set takes unit u, real value returns nothing
            set amount[GetUnitUserData(u)] = value
        endmethod

        static method onDamage takes nothing returns nothing
            local real damage = GetEventDamage()

            if damage > 0 and amount[DamageI.sIdx] > 0 and not DamageI.structure then
                call SetWidgetLife(DamageI.source, (GetWidgetLife(DamageI.source) + (damage * amount[DamageI.sIdx])))
                call DestroyEffect(AddSpecialEffectTarget(effect, DamageI.source, "origin"))
            endif
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterAttackDamageEvent(function thistype.onDamage)
        endmethod
    endstruct
    //endregion

    /* -------------------------------------------------------------------------- */
    /*                               Public JASS API                              */
    /* -------------------------------------------------------------------------- */

    function SetUnitLifeSteal takes unit u, real amount returns nothing
        call LifeSteal.Set(u, amount)
    endfunction

    function GetUnitLifeSteal takes unit u returns real
        return LifeSteal.Get(u)
    endfunction

    function UnitAddLifeSteal takes unit u, real amount returns nothing
        call LifeSteal.Set(u, LifeSteal.Get(u) + amount)
    endfunction
endlibrary
 

Code (vJASS):

library SpellVamp requires DamageInterface
    /* ------------------------ SpellVamp v1.9 by Chopinski ----------------------- */
    // SpellVamp intends to introduce to warcraft a healing based on Spell damage, like
    // in LoL or Dota 2.

    // Whenever a unit deals Spell damage, and it has a value of spell vamp given by
    // this system, it will heal based of this value and the damage amount.

    // The formula for spell vamp is:
    // heal = damage * lspell vamp
    // fror spell vamp: 0.1 = 10%

    // *SpellVamp requires DamageInterface. Do not use TriggerSleepAction() within triggers.

    // The API:
    //     function SetUnitSpellVamp takes unit u, real amount returns nothing
    //         -> Set the Spell Vamp amount for a unit
 
    //     function GetUnitSpellVamp takes unit u returns real
    //         -> Returns the Spell Vamp amount of a unit
 
    //     function UnitAddSpellVamp takes unit u, real amount returns nothing
    //         -> Add to the Spell Vamp amount of a unit the given amount
    /* ----------------------------------- END ---------------------------------- */
    //region System
    struct SpellVamp
        static real array amount

        static method Get takes unit u returns real
            return amount[GetUnitUserData(u)]
        endmethod

        static method Set takes unit u, real value returns nothing
            set amount[GetUnitUserData(u)] = value
        endmethod

        static method onDamage takes nothing returns nothing
            local real damage = GetEventDamage()

            if damage > 0 and amount[DamageI.sIdx] > 0 and not DamageI.structure then
                call SetWidgetLife(DamageI.source, (GetWidgetLife(DamageI.source) + (damage * amount[DamageI.sIdx])))
            endif
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellDamageEvent(function thistype.onDamage)
        endmethod
    endstruct
    //endregion

    /* -------------------------------------------------------------------------- */
    /*                               Public JASS API                              */
    /* -------------------------------------------------------------------------- */

    function SetUnitSpellVamp takes unit u, real amount returns nothing
        call SpellVamp.Set(u, amount)
    endfunction

    function GetUnitSpellVamp takes unit u returns real
        return SpellVamp.Get(u)
    endfunction

    function UnitAddSpellVamp takes unit u, real amount returns nothing
        call SpellVamp.Set(u, SpellVamp.Get(u) + amount)
    endfunction
endlibrary
 

Code (vJASS):

library DamageInterfaceUtils requires Evasion, CriticalStrike, LifeSteal, SpellPower, SpellVamp
    /* ------- Utility Library for all the Damage Interface Custom Events ------- */
    /* ---------------------------- v1.9 by Chopinski --------------------------- */
    // JASS API:
    //     Evasion System:
    //         function UnitAddEvasionChanceTimed takes unit u, real amount, real duration returns nothing
    //             -> Add to a unit Evasion chance the specified amount for a given period
 
    //         function UnitAddMissChanceTimed takes unit u, real amount, real duration returns nothing
    //             -> Add to a unit Miss chance the specified amount for a given period

    //     Critical Strike System:
    //         function UnitAddCriticalStrikeTimed takes unit u, real chance, real multiplier, real duration returns nothing
    //             -> Adds the specified values of chance and multiplier to a unit for a given period
   
    //         function UnitAddCriticalChanceTimed takes unit u, real chance, real duration returns nothing
    //             -> Adds the specified values of critical chance to a unit for a given period
   
    //         function UnitAddCriticalMultiplierTimed takes unit u, real multiplier, real duration returns nothing
    //             -> Adds the specified values of critical multiplier to a unit for a given period

    //     Spell Power System:
    //         function UnitAddSpellPowerFlatTimed takes unit u, real amount, real duration returns nothing
    //             -> Add to the Flat amount of Spell Power of a unit for a given period

    //         function UnitAddSpellPowerPercentTimed takes unit u, real amount, real duration returns nothing
    //             -> Add to the Percent amount of Spell Power of a unit for a given period

    //         function AbilitySpellDamage takes unit u, integer abilityId, abilityreallevelfield field returns string
    //             -> Given an ability field, will return a string that represents the damage that would be dealt
    //             taking into consideration the spell power bonusses of a unit.

    //         function AbilitySpellDamageEx takes real amount, unit u returns string
    //             -> Similar to GetSpellDamage will return the damage that would be dealt but as a string

    //     Life Steal System:
    //         function UnitAddLifeStealTimed takes unit u, real amount, real duration returns nothing
    //             -> Add to the Life Steal amount of a unit the given amount for a given period

    //     Spell Vamp System:
    //         function UnitAddSpellVampTimed takes unit u, real amount, real duration returns nothing
    //             -> Add to the Spell Vamp amount of a unit the given amount for a given period
    /* ----------------------------------- END ---------------------------------- */
 
    /* -------------------------------------------------------------------------- */
    /*                                Evasion utils                               */
    /* -------------------------------------------------------------------------- */
    //region Evasion
    private struct EvasionUtils extends Evasion
        static thistype array data
        static integer        didx  = -1
        static timer          timer = CreateTimer()

        unit    unit
        real    amount
        real    ticks
        boolean type

        method remove takes integer i returns integer
            if type then
                call setEvasionChance(unit, getEvasionChance(unit) - amount)
            else
                call setMissChance(unit, getMissChance(unit) - amount)
            endif

            set data[i] = data[didx]
            set didx    = didx - 1
            set unit    = null
            set ticks   = 0

            if didx == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this
       
            loop
                exitwhen i > didx
                    set this = data[i]

                    if ticks <= 0 then
                        set i = remove(i)
                    endif
                    set ticks = ticks - 1
                set i = i + 1
            endloop
        endmethod

        static method addTimed takes unit u, real amount, real duration, boolean evasion returns nothing
            local thistype this = thistype.allocate()

            set .unit      = u
            set .amount    = amount
            set .ticks     = duration/0.03125000
            set .type      = evasion
            set didx       = didx + 1
            set data[didx] = this

            if type then
                call setEvasionChance(u, getEvasionChance(u) + amount)
            else
                call setMissChance(u, getMissChance(u) + amount)
            endif
       
            if didx == 0 then
                call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
            endif
        endmethod
    endstruct
    //endregion

    /* -------------------------------------------------------------------------- */
    /*                            Critical Strike Utils                           */
    /* -------------------------------------------------------------------------- */
    //region Critical Strike
    private struct CriticalUtils extends CriticalStrike
        static thistype array data
        static integer        didx  = -1
        static timer          timer = CreateTimer()

        unit    unit
        real    crit
        real    multi
        real    ticks
        integer type

        method remove takes integer i returns integer
            if type == 0 then
                call add(unit, -crit, -multi)
            elseif type == 1 then
                call setChance(unit, getChance(unit) - crit)
            else
                call setMultiplier(unit, getMultiplier(unit) - multi)
            endif

            set data[i] = data[didx]
            set didx    = didx - 1
            set unit    = null
            set ticks   = 0

            if didx == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this
       
            loop
                exitwhen i > didx
                    set this = data[i]

                    if ticks <= 0 then
                        set i = remove(i)
                    endif
                    set ticks = ticks - 1
                set i = i + 1
            endloop
        endmethod

        static method addTimed takes unit u, real chance, real multiplier, real duration, integer types returns nothing
            local thistype this = thistype.allocate()

            set .unit      = u
            set .crit      = chance
            set .multi     = multiplier
            set .ticks     = duration/0.03125000
            set .type      = types
            set didx       = didx + 1
            set data[didx] = this

            if types == 0 then
                call add(u, crit, multi)
            elseif types == 1 then
                call setChance(u, getChance(u) + crit)
            else
                call setMultiplier(u, getMultiplier(u) + multi)
            endif
       
            if didx == 0 then
                call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
            endif
        endmethod
    endstruct
    //endregion

    /* -------------------------------------------------------------------------- */
    /*                              Spell Power Utils                             */
    /* -------------------------------------------------------------------------- */
    //region Spell Power
    private struct SpellPowerUtils extends SpellPower
        static thistype array data
        static integer        didx  = -1
        static timer          timer = CreateTimer()

        unit    unit
        real    amount
        real    ticks
        boolean isFlat

        method remove takes integer i returns integer
            if isFlat then
                call setFlat(unit, getFlat(unit) - amount)
            else
                call setPercent(unit, getPercent(unit) - amount)
            endif

            set data[i] = data[didx]
            set didx    = didx - 1
            set unit    = null
            set ticks   = 0

            if didx == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this
       
            loop
                exitwhen i > didx
                    set this = data[i]

                    if ticks <= 0 then
                        set i = remove(i)
                    endif
                    set ticks = ticks - 1
                set i = i + 1
            endloop
        endmethod

        static method addTimed takes unit u, real amount, real duration, boolean isFlat returns nothing
            local thistype this = thistype.allocate()

            set .unit      = u
            set .amount    = amount
            set .ticks     = duration/0.03125000
            set .isFlat    = isFlat
            set didx       = didx + 1
            set data[didx] = this

            if isFlat then
                call setFlat(u, getFlat(u) + amount)
            else
                call setPercent(u, getPercent(u) + amount)
            endif
       
            if didx == 0 then
                call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
            endif
        endmethod
    endstruct
    //endregion

    /* -------------------------------------------------------------------------- */
    /*                              Life Steal Utils                              */
    /* -------------------------------------------------------------------------- */
    //region Life Steal
    private struct LifeStealUtils extends LifeSteal
        static thistype array data
        static integer        didx  = -1
        static timer          timer = CreateTimer()

        unit unit
        real lifeSteal
        real ticks

        method remove takes integer i returns integer
            call Set(unit, Get(unit) - lifeSteal)

            set data[i] = data[didx]
            set didx    = didx - 1
            set unit    = null
            set ticks   = 0

            if didx == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this
       
            loop
                exitwhen i > didx
                    set this = data[i]

                    if ticks <= 0 then
                        set i = remove(i)
                    endif
                    set ticks = ticks - 1
                set i = i + 1
            endloop
        endmethod

        static method addTimed takes unit u, real amount, real duration returns nothing
            local thistype this = thistype.allocate()

            set .unit      = u
            set .lifeSteal = amount
            set .ticks     = duration/0.03125000
            set didx       = didx + 1
            set data[didx] = this

            call Set(u, Get(u) + lifeSteal)
       
            if didx == 0 then
                call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
            endif
        endmethod
    endstruct
    //endregion

    /* -------------------------------------------------------------------------- */
    /*                              Spell Vamp Utils                              */
    /* -------------------------------------------------------------------------- */
    //region Spell Vamp
    private struct SpellVampUtils extends SpellVamp
        static thistype array data
        static integer        didx  = -1
        static timer          timer = CreateTimer()

        unit unit
        real spellVamp
        real ticks

        method remove takes integer i returns integer
            call Set(unit, Get(unit) - spellVamp)

            set data[i] = data[didx]
            set didx    = didx - 1
            set unit    = null
            set ticks   = 0

            if didx == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this
       
            loop
                exitwhen i > didx
                    set this = data[i]

                    if ticks <= 0 then
                        set i = remove(i)
                    endif
                    set ticks = ticks - 1
                set i = i + 1
            endloop
        endmethod

        static method addTimed takes unit u, real amount, real duration returns nothing
            local thistype this = thistype.allocate()

            set .unit      = u
            set .spellVamp = amount
            set .ticks     = duration/0.03125000
            set didx       = didx + 1
            set data[didx] = this

            call Set(u, Get(u) + spellVamp)
       
            if didx == 0 then
                call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
            endif
        endmethod
    endstruct
    //endregion

    /* -------------------------------------------------------------------------- */
    /*                               Public JASS API                              */
    /* -------------------------------------------------------------------------- */

    function UnitAddEvasionChanceTimed takes unit u, real amount, real duration returns nothing
        call EvasionUtils.addTimed(u, amount, duration, true)
    endfunction

    function UnitAddMissChanceTimed takes unit u, real amount, real duration returns nothing
        call EvasionUtils.addTimed(u, amount, duration, false)
    endfunction

    function UnitAddCriticalStrikeTimed takes unit u, real chance, real multiplier, real duration returns nothing
        call CriticalUtils.addTimed(u, chance, multiplier, duration, 0)
    endfunction

    function UnitAddCriticalChanceTimed takes unit u, real chance, real duration returns nothing
        call CriticalUtils.addTimed(u, chance, 0, duration, 1)
    endfunction

    function UnitAddCriticalMultiplierTimed takes unit u, real multiplier, real duration returns nothing
        call CriticalUtils.addTimed(u, 0, multiplier, duration, 2)
    endfunction

    function UnitAddSpellPowerFlatTimed takes unit u, real amount, real duration returns nothing
        call SpellPowerUtils.addTimed(u, amount, duration, true)
    endfunction

    function UnitAddSpellPowerPercentTimed takes unit u, real amount, real duration returns nothing
        call SpellPowerUtils.addTimed(u, amount, duration, false)
    endfunction

    function AbilitySpellDamage takes unit u, integer abilityId, abilityreallevelfield field returns string
        return I2S(R2I((BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilityId), field, GetUnitAbilityLevel(u, abilityId) - 1) + SpellPower.getFlat(u)) * (1 + SpellPower.getPercent(u))))
    endfunction

    function AbilitySpellDamageEx takes real amount, unit u returns string
        return I2S(R2I((amount + SpellPower.getFlat(u)) * (1 + SpellPower.getPercent(u))))
    endfunction

    function UnitAddLifeStealTimed takes unit u, real amount, real duration returns nothing
        call LifeStealUtils.addTimed(u, amount, duration)
    endfunction

    function UnitAddSpellVampTimed takes unit u, real amount, real duration returns nothing
        call SpellVampUtils.addTimed(u, amount, duration)
    endfunction
endlibrary
 
Changelog
(v1.0)
  • Submission
(v1.1)
  • Core System renamed to Damage Interface
  • Included the ability to register damage before mitigation
(v1.2)
  • Add a configuration constant for the text size of Critical Strike and Evasion. Also increased the default text size from 0.016 to 0.019.
  • Attack Projectlies that do not play sound on death (hit) and melee attack will no longer play a sound when a unit evades an attack.
(v1.3)
  • Added a few global variables to assist reducing the amount of code when using this system

    • src -> the source of damage
    • tgt -> the target of damage
    • isEnemy -> true if the source is an enemy of the target
    • isAlly -> same as isEnemy but for allied units
    • isMelee -> indicates if the source of damage is a melee unit
    • structure -> indicates if the target of damage is a structure
    • magicImmune -> indicates if the target of damage is magic immune
    • sIdx -> represents the custom index of the source of damage
    • tIdx -> represents the custom index of the target of damage
    • sId -> represents the unit handle of the source of damage
    • tId -> represents the unit handle of the target of damage
    • p -> the owning player of the source
  • Renamed the Cleave event to Enhanced, as well observed by @Bribe
    • Added a condition to the evaluation of an Attack Event to not run when an attack is evaded (Evasion System)
  • Changed the logic for the NeverMiss functionality to avoid a Condition run (Evasion System)
  • Corrected a spelling error on the RegisterCriticalStrikeEvent() function call (Critical System)
v(1.4)
  • Redone the globlas to use struct members and to be more readable as suggested
    • DamageI.source -> the source of damage
    • DamageI.target -> the target of damage
    • DamageI.sourcePlayer -> the player who owns the source unit
    • DamageI.targetPlayer -> the player who owns the target unit
    • DamageI.isEnemy -> true if the source is an enemy of the target
    • DamageI.isAlly -> same as isEnemy but for allied units
    • DamageI.isMelee -> indicates if the source of damage is a melee unit
    • DamageI.isRanged -> indicates if the source of damage is a ranged unit
    • DamageI.isAttack -> true if the damage type is an attack damage
    • DamageI.isSpell -> true if the damage type is an spell damage
    • DamageI.isPure -> true if the damage type is pure damage
    • DamageI.isEnhanced -> true if the damage type is enhanced
    • DamageI.sourceIsHero -> indicates if the source of damage is a hero unit
    • DamageI.targetIsHero -> indicates if the target of damage is a hero unit
    • DamageI.structure -> indicates if the target of damage is a structure
    • DamageI.magicImmune -> indicates if the target of damage is a magic immune unit
    • DamageI.sourceX -> the X coordinate of the source unit
    • DamageI.sourceY -> the Y coordinate of the source unit
    • DamageI.targetX -> the X coordinate of the target unit
    • DamageI.targetY -> the Y coordinate of the target unit
    • DamageI.sIdx -> represents the custom index of the source of damage
    • DamageI.tIdx -> represents the custom index of the target of damage
    • DamageI.sId -> represents the unit handle of the source of damage
    • DamageI.tId -> represents the unit handle of the target of damage
    • DamageI.damageType -> the damage type of the damage event
    • DamageI.attackType -> the attack type of the damage event
  • Hopefully all those members will help the user to create code with less function calls and less amount of code created overall.
(v1.5)
  • New Configurable Constant CACHE_EXTRA: if true struct members will be set for every event.
  • Code Cleaning
(v1.6)
  • Fixed a minor bug on a global initialization
(v1.7)
  • System Overhauled for more flexibility and opportunities.
  • function RegisterDamageEvent takes attacktype attack, damagetype damage, code c returns nothing
    and
    function RegisterDamagingEvent takes attacktype attack, damagetype damage, code c returns nothing
    now takes the attack type and damage type as parameters for specialization. Example:
    • call RegisterDamageEvent(ATTACK_TYPE_HERO, DAMAGE_TYPE_FIRE, function OnDamage)
      .
      • function OnDamage
        will execute whenever the Attack Type of the damage event is equal to
        ATTACK_TYPE_HERO
        and the Damage Type is equal to
        DAMAGE_TYPE_FIRE
    • call RegisterDamageEvent(ATTACK_TYPE_HERO, null, function OnDamage)
      .
      • function OnDamage
        will run whenever the Attack Type of the damage event is equal to
        ATTACK_TYPE_HERO
        (
        null
        for the damage type parameter means that it doesn't matter)
    • call RegisterDamageEvent(null, DAMAGE_TYPE_COLD, function OnDamage
      .
      • function OnDamage
        will run whenever the Damage Type of the damage event is equal to
        DAMAGE_TYPE_COLD
        (
        null
        for the attack type parameter means that it doesn't matter)
  • Use
    function RegisterAnyDamageEvent takes code c returns nothing
    to register functions to any damage event or the equivalent
    call RegisterDamageEvent(null, null, function OnDamage)
  • The following functions were kept to keep backward compatibility:
    • function RegisterAttackDamageEvent takes code c returns nothing
    • function RegisterSpellDamageEvent takes code c returns nothing
    • function RegisterPureDamageEvent takes code c returns nothing
    • function RegisterEnhancedDamageEvent takes code c returns nothing
    • function RegisterEnhancedDamageEvent takes code c returns nothing
    • function RegisterSpellDamagingEvent takes code c returns nothing
    • function RegisterPureDamagingEvent takes code c returns nothing
    • function RegisterEnhancedDamagingEvent takes code c returns nothing
  • New
    library DamageInterfaceUtils
    containing all the extra functionalities of every system that were previously implemented on each system core implementation.
  • Some Code cleaning and optimization.
  • library DamageInterface
    uses optionally Bribe's Table Library. If not found it will use hashtables.
(v1.8)
  • Fixed a bug for the
    RegisterSpellDamageEvent
    not respecting correct flow of the system
  • Removed functions due to alternative registration (Attack and Spell kept because they are the most commonly used):
    • Removed:
      function RegisterPureDamageEvent takes code c returns nothing
      Equivalent:
      RegisterDamageEvent(null, DAMAGE_TYPE_UNIVERSAL, function OnDamage)
    • Removed:
      function RegisterEnhancedDamageEvent takes code c returns nothing
      Equivalent:
      RegisterDamageEvent(null, DAMAGE_TYPE_ENHANCED, function OnDamage)

    • Removed:
      function RegisterPureDamagingEvent takes code c returns nothing
      Equivalent:
      RegisterDamagingEvent(null, DAMAGE_TYPE_UNIVERSAL, function OnDamage)

    • Removed:
      function RegisterEnhancedDamagingEvent takes code c returns nothing
      Equivalent:
      RegisterDamagingEvent(null, DAMAGE_TYPE_ENHANCED, function OnDamage)
  • Removed an global variable used by the Evasion system, It's now a member
  • Code cleaning and formatting.
(v1.9)
  • Removed the members
    isPure
    and
    isEnhanced
Contents

Damage Interface v1.9 (Map)

  1. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,533
    Resources:
    9
    Models:
    1
    Icons:
    2
    Maps:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    9
    I would consider this to be more of a damage interface system than just a plain damage event system, due to the additional functionalities that this system offers that is not exclusively related to damage. That being said, what happened to
    EVENT_PLAYER_UNIT_DAMAGING
    ?
     
  2. chopinski

    chopinski

    Joined:
    May 16, 2012
    Messages:
    447
    Resources:
    6
    Spells:
    6
    Resources:
    6
    Agree on the interface point, will change its name if required. About the damaging event, during my experiments with maps development I found myself using most of the time the damaged event, since for the most functionalities it makes more sense, For Example, Life Steal, Spell Vamp only make sense after damage mitigation, for critical strikes I wanted to change the approach to make critical represent the actual amount of damage dealt. It's pretty common to see in custom maps a unit hit a 100k critical strike and dealing only 10 damage, it feels wrong. For Evasion it's irrelevant which one to use, since damage is always set to 0 if given chance to evade occurs. The only applicable scenarios would be on Spell Power Lib and in the core structure of detecting damage, but only as an option. thinking on "A Unit Takes Damage" event will more likely be translated to a "A Unit is Damaged" than "A Unit Is Damaging". After this opinions, if its required to implement Before and After events, I will do, the down side would be continent size function names hehe.
     
    Last edited: Apr 23, 2020
  3. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,533
    Resources:
    9
    Models:
    1
    Icons:
    2
    Maps:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    9
    Understood. Still, being able to access the event
    EVENT_PLAYER_UNIT_DAMAGING
    with this system would be useful to some folks who would like to have more control over damage instances.

    It doesn't even need to be extensively long. Probably just cnp
    DamageEvents.OnDamage
    callback function or alter function directly to process different event ids.
     
  4. chopinski

    chopinski

    Joined:
    May 16, 2012
    Messages:
    447
    Resources:
    6
    Spells:
    6
    Resources:
    6
    Ok, will make the required changes. thx for the feedback.

    Edit: System updated as required.
     
    Last edited: Apr 23, 2020
  5. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,533
    Resources:
    9
    Models:
    1
    Icons:
    2
    Maps:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    9
    Miss display looks too small to catch with the naked eye. When evading an attack, the weapon sound still plays.
    Oh, and NewBonus appears to not handle extremely large values properly. (possible overflow leading to negative damage).
     
  6. chopinski

    chopinski

    Joined:
    May 16, 2012
    Messages:
    447
    Resources:
    6
    Spells:
    6
    Resources:
    6
    Ok, I'll add a configuration constant that represents the text size for Evasion and Critical Strike. As for the weapon sound, I'll try to find a solution for that, not sure how now though.

    Well, that's the ability integer field limitation. What is possible to do is limit the max bonus amount. For ability integer field values, what's the maximum value? 2147483647?

    I'll do all that tomorrow, 2 pm here right now, but thx anyway.
     
  7. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,533
    Resources:
    9
    Models:
    1
    Icons:
    2
    Maps:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    9
    Try BlzSetEventWeaponType in a damaging event.

    Iirc, by extensive testing, it was capable of going up to 2^32 - 1 before underflowing. Needs more testing, though. Stick with 2147483647.
     
  8. jeff1326

    jeff1326

    Joined:
    Apr 29, 2020
    Messages:
    1
    Resources:
    0
    Resources:
    0
    All your 3 packages is awesome !!

    Can you add example how to use it with GUI ? I would like to use it in my map :D
     
  9. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,364
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    This is only compatible with JASS. You would need to use something like Damage Engine to have such events possible in GUI.
     
  10. chopinski

    chopinski

    Joined:
    May 16, 2012
    Messages:
    447
    Resources:
    6
    Spells:
    6
    Resources:
    6
    As @Bribe said, this is only for Jassers, but I'm planning on creating each module in this system pack separately for GUI users, so if you patient, it will come out.
     
  11. AGD

    AGD

    Joined:
    Mar 29, 2016
    Messages:
    565
    Resources:
    14
    Spells:
    8
    Tutorials:
    1
    JASS:
    5
    Resources:
    14
    The name Evasion is misleading. Maybe name it damage block or something similar, because that's what it really does. Right now, the supposed evaded attack still triggers attack modifiers (crit, bash, etc) and orb effects and even fires a damage event, which is not really evasion.
     
  12. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,364
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Also the form of nullifying the attack sound isn't as sophisticated as what I use for the Peasant in the Damage Engine demo map. The armor type of the target unit can also be nullified.
     
  13. chopinski

    chopinski

    Joined:
    May 16, 2012
    Messages:
    447
    Resources:
    6
    Spells:
    6
    Resources:
    6
    That's why it's a custom event, and that's why this is for jassers, who can control these situations better. There's no way of blocking attack modifiers as far as i know, unless it's triggered, and that's where this system can come in handy.

    Simpler is better imo. Projectiles with death sounds will still play it in your system even if both are nullified. Melee attacks will play no sound as intended. Someone playing will not be able to say that both are null or only one, so what's your point, what other advantages nullifying both fields offer?
     
  14. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,364
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    There's a subtle impact sound which plays when anything hits a unit's armor, even if the impacting weapon or missile has no sound of its own.
     
  15. chopinski

    chopinski

    Joined:
    May 16, 2012
    Messages:
    447
    Resources:
    6
    Spells:
    6
    Resources:
    6
    Really? In my tests I couldn't hear a thing. Can you tell an example so I can go and test it?
     
  16. AGD

    AGD

    Joined:
    Mar 29, 2016
    Messages:
    565
    Resources:
    14
    Spells:
    8
    Tutorials:
    1
    JASS:
    5
    Resources:
    14
    I'm not asking you to replicate evasion actually cause afaik, it's currently undoable. But the point still stands that the name/description given by the system should correctly describe what it does, which is the concern. You can't make a custom event for example that uses EVENT_PLAYER_UNIT_ATTACKED behind the scenes and disguise it as a damage detection system, and say that the users can deal with the issues due to having jass at their disposal.
     
  17. chopinski

    chopinski

    Joined:
    May 16, 2012
    Messages:
    447
    Resources:
    6
    Spells:
    6
    Resources:
    6
    Understood. Will rename it to something else.
     
  18. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,364
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    I had just been testing on my demo map with the peasant attacking friendly units. I would recommend having your headphones on though, as again it's quite subtle.

    That is, unless that got changed in a recent patch. It was some months ago when I was last playing around with it.
     
  19. chopinski

    chopinski

    Joined:
    May 16, 2012
    Messages:
    447
    Resources:
    6
    Spells:
    6
    Resources:
    6
    I tested your map and there are absolutely no difference. I have a jbl tune500bt, which is quite descent headphone, with music turned off and sound effect at 100% and couldn't notice anything different about your method and the one I implemented.