• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[system] Damage Queue

JASS:
library DamageQueue /* v1.0.0.1
*************************************************************************************
*
*   Allows on to queue up damage events. ALWAYS use this instead of unitDamageTarget
*   within a DamageEvent.
*
*   ONLY use this on Damage Creators (sources of damage that generate damage).
*
*************************************************************************************
*
*   */uses/*
*
*       */ AdvDamageEvent       /*          hiveworkshop.com/forums/submissions-414/extension-advanced-damage-event-213572/
*       */ AVL                  /*          hiveworkshop.com/forums/jass-resources-412/snippet-avl-tree-203168/
*       */ DamagePriority       /*          hiveworkshop.com/forums/submissions-414/repo-damage-priority-213750/
*       */ DamageType           /*          hiveworkshop.com/forums/submissions-414/snippet-damagetype-213751/
*
************************************************************************************
*
*   struct DamageQueue extends array
*       readonly static DamageQueue first
*       readonly static boolean head
*       readonly DamageQueue next
*       readonly DamageType type
*       readonly real damage
*
*       static method add takes UnitIndex source, UnitIndex target, real damage, integer damageType, integer damageId, integer instance returns nothing
*       static method clear takes nothing returns nothing
*
************************************************************************************/
    private struct Tree extends array
        method lessThan takes DamageType value returns boolean
            return DamageType(this).invPriority > value.invPriority
        endmethod
        
        method greaterThan takes DamageType value returns boolean
            return DamageType(this).invPriority < value.invPriority
        endmethod
        
        implement AVL
    endstruct
    
    private module DamageQueue_pmod
        private static method onInit takes nothing returns nothing
            set damageTree = Tree.create()
        endmethod
    endmodule
    
    private struct DamageQueue_p extends array
        static thistype instanceCount = 0
        thistype last
        thistype first
        
        real damage
        integer damageType
        integer damageId
        UnitIndex source
        UnitIndex target
        integer instance
        
        static real array totalDamage
        static Tree damageTree
        
        boolean cleared
        
        static method allocate takes nothing returns thistype
            set instanceCount = instanceCount + 1
            return instanceCount
        endmethod
        
        static method add takes UnitIndex source, UnitIndex target, real damage, integer damageType, integer damageId, integer instance returns nothing
            local thistype this = allocate()
            
            set thistype(DamageEvent.depth).last = this
            
            set this.source = source
            set this.target = target
            set this.damage = damage
            set this.damageType = damageType
            set this.damageId = damageId
            set this.instance = instance
        endmethod
        static method execute takes nothing returns nothing
            local thistype last = thistype(DamageEvent.depth).last
            local thistype this = thistype(DamageEvent.depth).first
            if (not thistype(DamageEvent.depth).cleared) then
                loop
                    set this = this + 1
                    exitwhen integer(this) > integer(last)
                    if (0 == damageTree.search(damageType)) then
                        call damageTree.add(damageType)
                        set totalDamage[damageType] = 0
                    endif
                    call DamageEvent.unitDamageTarget(GetUnitById(source), GetUnitById(target), damage, damageType, damageId, instance)
                endloop
            endif
        endmethod
        static method clear takes nothing returns nothing
            set thistype(DamageEvent.depth).cleared = true
        endmethod
        
        implement DamageQueue_pmod
    endstruct
    
    private struct DamageQueueCreator extends array
        private static constant method PRIORITY takes nothing returns integer
            return DAMAGE_PRIORITY_SETUP
        endmethod
        
        private static method onDamage takes nothing returns nothing
            local DamageQueue_p head
            
            if (1 == depth) then
                call DamageQueue_p.damageTree.clear()
                set DamageQueue_p.instanceCount = 0
            endif
            
            set head = DamageQueue_p.allocate()
            
            set DamageQueue_p(depth).first = head
            set DamageQueue_p(depth).last = head
            
            set DamageQueue_p(depth).cleared = false
        endmethod
        
        implement DamageEvent
    endstruct
    
    private struct DamageQueueExecute extends array
        private static constant method PRIORITY takes nothing returns integer
            return DAMAGE_PRIORITY_EXECUTION
        endmethod
        
        private static method onDamage takes nothing returns nothing
            call DamageQueue_p.execute()
        endmethod
        
        implement DamageEvent
    endstruct
    
    private struct DamageQueueCancellor extends array
        private static constant method PRIORITY takes nothing returns integer
            return DAMAGE_PRIORITY_CLEANUP
        endmethod
        
        private static method onDamage takes nothing returns nothing
            if (1 == depth) then
                set modifiedAmount = 0
            else
                set DamageQueue_p.totalDamage[damageType] = DamageQueue_p.totalDamage[damageType] + modifiedAmount
            endif
        endmethod
        
        implement DamageEvent
    endstruct
    
    struct DamageQueue extends array
        static method add takes UnitIndex source, UnitIndex target, real damage, integer damageType, integer damageId, integer instance returns nothing
            call DamageQueue_p.add(source,  target, damage, damageType, damageId, instance)
        endmethod
        static method operator first takes nothing returns thistype
            return DamageQueue_p.damageTree.next
        endmethod
        static method clear takes nothing returns nothing
            call DamageQueue_p.clear()
        endmethod
        method operator next takes nothing returns thistype
            return Tree(this).next
        endmethod
        method operator head takes nothing returns boolean
            return Tree(this).head
        endmethod
        method operator type takes nothing returns DamageType
            return Tree(this).value
        endmethod
        method operator damage takes nothing returns real
            return DamageQueue_p.totalDamage[type]
        endmethod
    endstruct
endlibrary


Demonstrations of Excellent Custom Damage Modifier/Creator Implementations
JASS:
library HandDamageCreator uses DamageQueue, DamageSourceTypes, DamageSourceInstance
    struct HandDamageCreator extends array
        readonly DamageSource leftHand
        readonly DamageSource rightHand
        
        readonly thistype leftHandModifier
        readonly thistype rightHandModifier
        
        readonly DamageSource source
        
        private static method PRIORITY takes nothing returns integer
            return DAMAGE_PRIORITY_CREATOR
        endmethod

        private method onDamageOutgoing takes nothing returns nothing
            if (DamageSource(damageId).type == DamageSourceTypes.NULL) then
                call DamageQueue.add(sourceId, targetId, amount/2, DamageTypes.PHYSICAL, source, DamageSourceInstance.create(targetId, source))
            endif
        endmethod

        implement DamageModificationEffect
        
        private method index takes nothing returns nothing
            set leftHand = DamageSource.create(DamageSourceTypes.HANDS)
            set rightHand = DamageSource.create(DamageSourceTypes.HANDS)
            
            set leftHandModifier = apply(false)
            set leftHandModifier.source = leftHand
            
            set rightHandModifier = apply(false)
            set rightHandModifier.source = rightHand
        endmethod
        
        private method deindex takes nothing returns nothing
            call leftHand.destroy()
            call rightHand.destroy()
        endmethod
        
        implement UnitIndexStruct
    endstruct
endlibrary

JASS:
library PhysicalDamageModifier uses DamageQueue, DamageSource
    private struct PhysicalDamageModifier_p extends array
        real change
        
        private static method PRIORITY takes nothing returns integer
            return DAMAGE_PRIORITY_MODIFIER
        endmethod

        private method onDamageOutgoing takes nothing returns nothing
            if (damageType == DamageTypes.PHYSICAL and DamageSource(damageId).type == DamageSourceTypes.WEAPON) then
                set modifiedAmount = modifiedAmount + change
            endif
        endmethod
        
        private method onDamageIncoming takes nothing returns nothing
            if (damageType == DamageTypes.PHYSICAL and DamageSource(damageId).type == DamageSourceTypes.WEAPON) then
                set modifiedAmount = modifiedAmount + change
            endif
        endmethod
        
        implement DamageModificationEffect
    endstruct
    
    struct PhysicalDamageModifier extends array
        method operator change takes nothing returns real
            return PhysicalDamageModifier_p(this).change
        endmethod
        method operator change= takes real value returns nothing
            set PhysicalDamageModifier_p(this).change = value
        endmethod
        
        implement UnitIndexStructMethods
        
        method apply takes real change, boolean incoming returns thistype
            set this = PhysicalDamageModifier_p(this).apply(incoming)
            
            set this.change = change
            
            return this
        endmethod
        method destroy takes nothing returns nothing
            call PhysicalDamageModifier_p(this).destroy()
        endmethod
    endstruct
endlibrary

JASS:
library WeaponSourceEvasion uses DamageQueue, DamageSourceTypes, DamagePriority
    globals
        private real array evasion
    endglobals
    
    private struct WeaponSourceEvasionModifier_p extends array
        real change
        
        private static method PRIORITY takes nothing returns integer
            return DAMAGE_PRIORITY_PERCENT_MODIFIER
        endmethod
        
        private method onDamageIncoming takes nothing returns nothing
            if (DamageSource(damageId).type == DamageSourceTypes.WEAPON) then
                set evasion[targetId] = evasion[targetId] + change
            endif
        endmethod
        
        implement DamageModificationEffect
    endstruct
    
    struct WeaponSourceEvasionModifier extends array
        method operator change takes nothing returns real
            return WeaponSourceEvasionModifier_p(this).change
        endmethod
        method operator change= takes real value returns nothing
            set WeaponSourceEvasionModifier_p(this).change = value
        endmethod
        
        implement UnitIndexStructMethods
        
        method apply takes real change returns thistype
            set this = WeaponSourceEvasionModifier_p(this).apply(true)
            
            set this.change = change
            
            return this
        endmethod
        method destroy takes nothing returns nothing
            call WeaponSourceEvasionModifier_p(this).destroy()
        endmethod
    endstruct
    
    private struct WeaponSourceEvasion_p extends array
        private static method PRIORITY takes nothing returns integer
            return DAMAGE_PRIORITY_PERCENT_MODIFIER
        endmethod
        
        private static method onDamage takes nothing returns nothing
            if (DamageSource(damageId).type == DamageSourceTypes.WEAPON) then
                if (evasion[targetId] > GetRandomReal(0, 1)) then
                    call DamageQueue.clear()
                endif
                
                set evasion[targetId] = 0
            endif
        endmethod
    
        implement DamageEvent
    endstruct
endlibrary

JASS:
struct Tester extends array
    private static method onInit takes nothing returns nothing
        local unit u1 = CreateUnit(Player(2), 'hfoo', GetStartLocationX(0), GetStartLocationY(0) - 256, 270)
        local unit u2 = CreateUnit(Player(2), 'hrif', GetStartLocationX(0), GetStartLocationY(0) - 256, 270)
        local unit u3 = CreateUnit(Player(0), 'hgtw', GetStartLocationX(0), GetStartLocationY(0), 270)
        local unit u4 = CreateUnit(Player(0), 'hfoo', GetStartLocationX(0), GetStartLocationY(0), 270)
        
        call PhysicalDamageModifier[u4].apply(-10, true)        //-10 to incoming damage
        call WeaponSourceEvasionModifier[u2].apply(.5)          //50% chance to evade
    endmethod
endstruct
 
Last edited:
Top