System Unit Event

Nestharus

o-o
Reaction score
84
Unit Event
v1.0.2.1
By Nestharus

Introduction
Unit Event is a lot like AutoEvent in that it adds new unit events to the game that aren't normally possible. For example, resurrection and reincarnating events.

This one is the most efficient Unit Event library to date (not much competition though is there? o-o). For example, AutoEvent uses hooks to handle some of its events as well as a slew of timers and loops. This one does no such thing : ).

  • events are death, remove, decay, explode, resurrect, reincarnate, animate
  • It has extremely efficient event handling
  • Only works with indexed units
  • Will eventually have an AutoEvent API to support things running on that system : D.

System Code
JASS:
library UnitEvent initializer init uses UnitIndexer, GameTime
//Unit Indexer- thehelper.net/forums/showthread.php?t=153034
//Game Time-    thehelper.net/forums/showthread.php?t=134432

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Unit Event ~~ By Nestharus ~~ Version 1.0.2.2 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Description:
//      Unit Event is a system that handles events that aren't normally possible in Warcraft 3.
//      It retrieves them by using undefend.
//
//      This system will only fire units that are indexed. Any unit that is not indexed is treated
//      as an unexisting unit.
//
//      Events: death, remove, decay, explode, resurrect, reincarnate, animate
//
//API:
//////////////////////////////////////////////////////////////
//      -function IsUnitDead takes integer index returns boolean
//          Returns a boolean determining whether the unit is dead or not.
//
//      -function IsUnitReincarnating takes integer index returns boolean
//          Returns a boolean determining whether the unit is reincarnating or not.
//
//      -function IsUnitAnimated takes integer index returns boolean
//          Returns a boolean determining whether a unit is animated or not.
//
//Events:
//////////////////////////////////////////////////////////////
//  Each event is a struct that extends array. 
//
//  -API
//      -static method add takes boolexpr c returns nothing
//          Adds code to be run at event.
//
//      -function GetEventUnit takes nothing returns unit
//          Returns the triggering unit.
//
//      -function GetEventUnitId takes nothing returns integer
//          Returns the index of the tiggering unit.
//
//  -Structs
//      -UnitDeathEvent
//          Fires whenever a unit dies.
//
//      -UnitRemoveEvent
//          Fires whenever a unit is removed.
//
//      -UnitDecayEvent
//          Fires whenever a unit decays.
//
//      -UnitExplodeEvent
//          Fires whenever a unit's animation expires.
//
//      -UnitResurrectEvent
//          Fires whenever a unit is resurrected.
//
//      -UnitReincarnateEvent
//          Fires whenever a unit is reincarnated.
//
//      -UnitAnimateEvent
//          Fires whenever a unit is animated.
//
//Unit Event Struct Module:
//////////////////////////////////////////////////////////////
//  A module that will automatically call unit event methods for you as well
//  as automatically check against a unit indexer filter. It runs completely off of static ifs,
//  so there are minimal calls and there is minimal code.
//
//  module UnitEventStruct
//      readonly unit eventUnit
//          The indexed event unit of the struct. Use this over unit when dealing with the
//          events.
//
//      Interface:
//          private method unitDeath takes nothing returns nothing
//              Fires when an indexed unit dies. Will only fire
//              if the struct is allocated.
//
//              Structs are allocated when the indexing filter of the struct
//              returns true. If the indexing filter doesn't exist, a struct
//              is always allocated.
//
//          private method unitRemove takes nothing returns nothing
//              Fires when an indexed unit is removed. Will only fire
//              if the struct is allocated.
//
//              Structs are allocated when the indexing filter of the struct
//              returns true. If the indexing filter doesn't exist, a struct
//              is always allocated.
//
//          private method unitDecay takes nothing returns nothing
//              Fires when an indexed unit decays. Will only fire
//              if the struct is allocated.
//
//              Structs are allocated when the indexing filter of the struct
//              returns true. If the indexing filter doesn't exist, a struct
//              is always allocated.
//
//          private method unitExplode takes nothing returns nothing
//              Fires when an indexed unit explodes. Will only fire
//              if the struct is allocated.
//
//              Structs are allocated when the indexing filter of the struct
//              returns true. If the indexing filter doesn't exist, a struct
//              is always allocated.
//
//          private method unitResurrect takes nothing returns nothing
//              Fires when an indexed unit is resurrected. Will only fire
//              if the struct is allocated.
//
//              Structs are allocated when the indexing filter of the struct
//              returns true. If the indexing filter doesn't exist, a struct
//              is always allocated.
//
//          private method unitReincarnate takes nothing returns nothing
//              Fires when an indexed unit reincarnates. Will only fire
//              if the struct is allocated.
//
//              Structs are allocated when the indexing filter of the struct
//              returns true. If the indexing filter doesn't exist, a struct
//              is always allocated.
//
//          private method unitAnimate takes nothing returns nothing
//              Fires when an indexed unit is animated. Will only fire
//              if the struct is allocated.
//
//              Structs are allocated when the indexing filter of the struct
//              returns true. If the indexing filter doesn't exist, a struct
//              is always allocated.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    native UnitAlive takes unit id returns boolean
    globals
        //events
        private trigger onDeath = CreateTrigger()
        private trigger onRemove = CreateTrigger()
        private trigger onDecay = CreateTrigger()
        private trigger onExplode = CreateTrigger()
        private trigger onResurrect = CreateTrigger()
        private trigger onReincarnate = CreateTrigger()
        private trigger onAnimate = CreateTrigger()
        
        private integer array events
        private integer eventIndex = 0
        
        private trigger undefend = CreateTrigger()
        private trigger death = CreateTrigger()
        
        private boolean array animated
        private boolean array dead
        private boolean array reincarnating
        private real array timeStamp
        private unit array holdUnit
        private boolean array allocated
        private boolean array exploded
        
        //this is auto determined
        private real decayTime = 1000
        private unit decayUnit = null
    endglobals
    
    struct UnitDeathEvent extends array
        public static method add takes boolexpr c returns nothing
            call TriggerAddCondition(onDeath, c)
        endmethod
    endstruct
    struct UnitRemoveEvent extends array
        public static method add takes boolexpr c returns nothing
            call TriggerAddCondition(onRemove, c)
        endmethod
    endstruct
    struct UnitDecayEvent extends array
        public static method add takes boolexpr c returns nothing
            call TriggerAddCondition(onDecay, c)
        endmethod
    endstruct
    struct UnitExplodeEvent extends array
        public static method add takes boolexpr c returns nothing
            call TriggerAddCondition(onExplode, c)
        endmethod
    endstruct
    struct UnitResurrectEvent extends array
        public static method add takes boolexpr c returns nothing
            call TriggerAddCondition(onResurrect, c)
        endmethod
    endstruct
    struct UnitReincarnateEvent extends array
        public static method add takes boolexpr c returns nothing
            call TriggerAddCondition(onReincarnate, c)
        endmethod
    endstruct
    struct UnitAnimateEvent extends array
        public static method add takes boolexpr c returns nothing
            call TriggerAddCondition(onAnimate, c)
        endmethod
    endstruct
    
    function GetEventUnit takes nothing returns unit
        return holdUnit[events[eventIndex]]
    endfunction
    
    function GetEventUnitId takes nothing returns integer
        return events[eventIndex]
    endfunction
    
    function IsUnitDead takes integer index returns boolean
        return dead[index]
    endfunction
    
    function IsUnitReincarnating takes integer index returns boolean
        return reincarnating[index]
    endfunction
    
    function IsUnitAnimated takes integer index returns boolean
        return (animated[index])
    endfunction
    
    private function OnUndefend takes nothing returns boolean
        local unit u = GetFilterUnit()
        local integer i = GetUnitUserData(u)
        if (holdUnit<i> == u and allocated<i>) then
            if (GetUnitAbilityLevel(u, &#039;OUIN&#039;) == 0) then
                set allocated<i> = false
                set reincarnating<i> = false
                set animated<i> = false
                if (not exploded<i>) then
                    set eventIndex = eventIndex + 1
                    set events[eventIndex] = i
                    if (dead<i> and GetElapsedGameTime()-timeStamp<i> &gt;= decayTime) then
                        set dead<i> = false
                        call TriggerEvaluate(onDecay)
                    else
                        set dead<i> = false
                        call TriggerEvaluate(onRemove)
                    endif
                else
                    set dead<i> = false
                    set exploded<i> = false
                endif
                set eventIndex = eventIndex - 1
                set holdUnit<i> = null
                call UnlockUnitIndex(i)
            elseif (UnitAlive(u)) then
                if (reincarnating<i>) then
                    set eventIndex = eventIndex + 1
                    set events[eventIndex] = i
                    call TriggerEvaluate(onReincarnate)
                    set reincarnating<i> = false
                    set eventIndex = eventIndex - 1
                elseif (dead<i>) then
                    set dead<i> = false
                    set eventIndex = eventIndex + 1
                    set events[eventIndex] = i
                    if (IsUnitType(u, UNIT_TYPE_SUMMONED)) then
                        set animated<i> = true
                        call TriggerEvaluate(onAnimate)
                    else
                        call TriggerEvaluate(onResurrect)
                    endif
                    set eventIndex = eventIndex - 1
                endif
            else
                set reincarnating<i> = true
            endif
        elseif (u == decayUnit and GetElapsedGameTime() != 0) then
            set decayTime = GetElapsedGameTime()-.9375 //cleaning takes up to 3 passes standard
                                                       //cleaning in around .3125 intervals
                                                       //.3125 * 3
            set decayUnit = null
        endif
        
        set u = null
        return false
    endfunction
    
    private function OnDeath takes nothing returns boolean
        local unit u = GetTriggerUnit()
        local integer i = GetUnitUserData(u)
        if (holdUnit<i> == u) then
            set dead<i> = true
            set reincarnating<i> = false
            set eventIndex = eventIndex + 1
            set events[eventIndex] = i
            if (not animated<i>) then
                set timeStamp<i> = GetElapsedGameTime()
                call TriggerEvaluate(onDeath)
            else
                set exploded<i> = true
                set animated<i> = false
                call TriggerEvaluate(onExplode)
            endif
            set eventIndex = eventIndex - 1
        endif
        set u = null
        return false
    endfunction
    
    private function OnEnter takes nothing returns boolean
        call LockUnitIndex(GetIndexedUnitId())
        set allocated[GetIndexedUnitId()] = true
        set holdUnit[GetIndexedUnitId()]  = GetIndexedUnit()
        return false
    endfunction
    
    private struct UnitEventX extends array
        private static method onInit takes nothing returns nothing
            local integer i = 15
            local boolexpr bc = Condition(function OnUndefend)
            
            loop
                call TriggerRegisterPlayerUnitEvent(undefend, Player(i), EVENT_PLAYER_UNIT_ISSUED_ORDER, bc)
                call TriggerRegisterPlayerUnitEvent(death, Player(i), EVENT_PLAYER_UNIT_DEATH, null)
                exitwhen i == 0
                set i = i - 1
            endloop
            
            call TriggerAddCondition(death, Condition(function OnDeath))
            call UnitIndexEvent.add(Condition(function OnEnter))
            
            set bc = null
        endmethod
    endstruct
    
    //must be done at init or it will still be indexed in the enumeration
    private function init takes nothing returns nothing
        local rect r = GetWorldBounds()
        
        call EnableUnitIndexing(false)
        set decayUnit = CreateUnit(Player(14), &#039;hpea&#039;, GetRectMaxX(r), GetRectMaxY(r), 0)
        call KillUnit(decayUnit)
        call UnitAddAbility(decayUnit, &#039;OUIN&#039;)
        call ShowUnit(decayUnit, false)
        call EnableUnitIndexing(true)
        
        call RemoveRect(r)
        set r = null
    endfunction
    
    //! textmacro UNIT_EVENT_STRUCT_INIT
        private static method enteringEvent takes nothing returns boolean
            set thistype(GetIndexedUnitId()).allocated = thistype(GetIndexedUnitId()).indexAllocated
            return false
        endmethod
        
        private static method onInit takes nothing returns nothing
            static if thistype.unitDeath.exists then
                call UnitDeathEvent.add(Condition(function thistype.onDeathEvent))
            endif
            static if thistype.unitRemove.exists then
                call UnitRemoveEvent.add(Condition(function thistype.onRemoveEvent))
            endif
            static if thistype.unitDecay.exists then
                call UnitDecayEvent.add(Condition(function thistype.onDecayEvent))
            endif
            static if thistype.unitExplode.exists then
                call UnitExplodeEvent.add(Condition(function thistype.onExplodeEvent))
            endif
            static if thistype.unitResurrect.exists then
                call UnitResurrectEvent.add(Condition(function thistype.onResurrectEvent))
            endif
            static if thistype.unitReincarnate.exists then
                call UnitReincarnateEvent.add(Condition(function thistype.onReincarnateEvent))
            endif
            static if thistype.unitAnimate.exists then
                call UnitAnimateEvent.add(Condition(function thistype.onAnimateEvent))
            endif
            call UnitIndexEvent.add(Condition(function thistype.enteringEvent))
        endmethod
    //! endtextmacro
    
    module UnitEventStruct
        implement UnitIndexStruct //results in no code if not used, so might as well
        private boolean allocated //because indexAllocated runs first
        
        static if thistype.unitDeath.exists then
            private static method onDeathEvent takes nothing returns boolean
                if (thistype(GetEventUnitId()).allocated) then
                    call thistype(GetEventUnitId()).unitDeath()
                endif
                return false
            endmethod
        endif
        
        static if thistype.unitRemove.exists then
            private static method onRemoveEvent takes nothing returns boolean
                if (thistype(GetEventUnitId()).allocated) then
                    set thistype(GetEventUnitId()).allocated = false
                    call thistype(GetEventUnitId()).unitRemove()
                endif
                return false
            endmethod
        endif
        
        static if thistype.unitDecay.exists then
            private static method onDecayEvent takes nothing returns boolean
                if (thistype(GetEventUnitId()).allocated) then
                    set thistype(GetEventUnitId()).allocated = false
                    call thistype(GetEventUnitId()).unitDecay()
                endif
                return false
            endmethod
        endif
        
        static if thistype.unitExplode.exists then
            private static method onExplodeEvent takes nothing returns boolean
                if (thistype(GetEventUnitId()).allocated) then
                    set thistype(GetEventUnitId()).allocated = false
                    call thistype(GetEventUnitId()).unitExplode()
                endif
                return false
            endmethod
        endif
        
        static if thistype.unitResurrect.exists then
            private static method onResurrectEvent takes nothing returns boolean
                if (thistype(GetEventUnitId()).allocated) then
                    call thistype(GetEventUnitId()).unitResurrect()
                endif
                return false
            endmethod
        endif
        
        static if thistype.unitReincarnate.exists then
            private static method onReincarnateEvent takes nothing returns boolean
                if (thistype(GetEventUnitId()).allocated) then
                    call thistype(GetEventUnitId()).unitReincarnate()
                endif
                return false
            endmethod
        endif
        
        static if thistype.unitAnimate.exists then
            private static method onAnimateEvent takes nothing returns boolean
                if (thistype(GetEventUnitId()).allocated) then
                    call thistype(GetEventUnitId()).unitAnimate()
                endif
                return false
            endmethod
        endif
        
        static if thistype.unitDeath.exists then
            //! runtextmacro UNIT_EVENT_STRUCT_INIT()
        elseif thistype.unitRemove.exists then
            //! runtextmacro UNIT_EVENT_STRUCT_INIT()
        elseif thistype.unitDecay.exists then
            //! runtextmacro UNIT_EVENT_STRUCT_INIT()
        elseif thistype.unitExplode.exists then
            //! runtextmacro UNIT_EVENT_STRUCT_INIT()
        elseif thistype.unitResurrect.exists then
            //! runtextmacro UNIT_EVENT_STRUCT_INIT()
        elseif thistype.unitReincarnate.exists then
            //! runtextmacro UNIT_EVENT_STRUCT_INIT()
        elseif thistype.unitAnimate.exists then
            //! runtextmacro UNIT_EVENT_STRUCT_INIT()
        endif
        
        public method operator eventUnit takes nothing returns unit
            return holdUnit[this]
        endmethod
    endmodule
endlibrary
</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>


Demo Code
JASS:
struct UnitEventDemo extends array
    private static real x
    private static real y
    
    private method unitDeath takes nothing returns nothing
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, I2S(this)+&quot;:&quot;+ GetUnitName(eventUnit) + &quot; died&quot;)
    endmethod
    
    private method unitRemove takes nothing returns nothing
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, I2S(this)+&quot;:&quot;+ GetUnitName(eventUnit) + &quot; removed&quot;)
    endmethod
    
    private method unitDecay takes nothing returns nothing
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, I2S(this)+&quot;:&quot;+ GetUnitName(eventUnit) + &quot; decayed&quot;)
    endmethod
    
    private method unitExplode takes nothing returns nothing
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, I2S(this)+&quot;:&quot;+ GetUnitName(eventUnit) + &quot; exploded&quot;)
    endmethod
    
    private method unitResurrect takes nothing returns nothing
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, I2S(this)+&quot;:&quot;+ GetUnitName(eventUnit) + &quot; resurrected&quot;)
    endmethod
    
    private method unitReincarnate takes nothing returns nothing
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, I2S(this)+&quot;:&quot;+ GetUnitName(eventUnit) + &quot; reincarnated&quot;)
    endmethod
    
    private method unitAnimate takes nothing returns nothing
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, I2S(this)+&quot;:&quot;+ GetUnitName(eventUnit) + &quot; animated&quot;)
    endmethod
    
    private static method unitIndexFilter takes unit u returns boolean
        //normally
        //return (GetUnitTypeId(u) == &#039;hkni&#039;)
        
        if (GetUnitTypeId(u) == &#039;hkni&#039;) then
            call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, GetUnitName(u) + &quot; was filtered&quot;)
            return false
        endif
        return true
    endmethod
    
    private static method remove takes nothing returns nothing
        call RemoveUnit(CreateUnit(Player(0), &#039;hkni&#039;, x, y, 0))
    endmethod
    
    private static method onInit takes nothing returns nothing
        local unit u
        
        set x = GetRectCenterX(bj_mapInitialPlayableArea)
        set y = GetRectCenterY(bj_mapInitialPlayableArea)
        call PanCameraTo(x, y)
        
        call CreateUnit(Player(0), &#039;hfoo&#039;, x, y, 0)
        call CreateUnit(Player(0), &#039;hpea&#039;, x, y, 0)
        call CreateUnit(Player(0), &#039;hfoo&#039;, x, y, 0)
        call CreateUnit(Player(0), &#039;hpea&#039;, x, y, 0)
        call CreateUnit(Player(0), &#039;hfoo&#039;, x, y, 0)
        call CreateUnit(Player(0), &#039;hpea&#039;, x, y, 0)
        set u = CreateUnit(Player(0), &#039;Hpal&#039;, x, y, 0)
        call SetHeroLevel(u, 10, false)
        call SetHeroAgi(u, 8000, true)
        call SetHeroInt(u, 8000, true)
        call SetHeroStr(u, 8000, true)
        set u = CreateUnit(Player(0), &#039;Udea&#039;, x, y, 0)
        call SetHeroLevel(u, 10, false)
        call SetHeroAgi(u, 8000, true)
        call SetHeroInt(u, 8000, true)
        call SetHeroStr(u, 8000, true)
        set u = CreateUnit(Player(0), &#039;Otch&#039;, x, y, 0)
        call SetHeroLevel(u, 10, false)
        
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, &quot;Creating and Removing a unit in 5 seconds\n\n&quot;)
        call TimerStart(CreateTimer(), 5, false, function thistype.remove)
        
        set u = null
    endmethod
    
    implement UnitEventStruct
endstruct
 

Attachments

  • test map.zip
    22.8 KB · Views: 230

Nestharus

o-o
Reaction score
84
You have so much free time. Or you're not busy at work.

Yes I do, but because of that I can come up with some really creative designs ; P.

Like the core that makes Unit Event so awesome : D

JASS:
            call EnableUnitIndexing(false)
            set decayUnit = CreateUnit(Player(14), &#039;hpea&#039;, GetRectMaxX(r), GetRectMaxY(r), 0)
            call KillUnit(decayUnit)
            call UnitAddAbility(decayUnit, &#039;OUIN&#039;)
            call ShowUnit(decayUnit, false)
            call EnableUnitIndexing(true)


and on remove
JASS:
        elseif (u == decayUnit and GetElapsedGameTime() != 0) then
            set decayTime = GetElapsedGameTime()-.9375 //cleaning takes up to 3 passes standard
                                                       //cleaning in around .3125 intervals
                                                       //.3125 * 3
            set decayUnit = null


which lets me do this for checking instead of hooking RemoveUnit

on remove
JASS:
                    if (dead<i> and GetElapsedGameTime()-timeStamp<i> &gt;= decayTime) then
                        set dead<i> = false
                        call TriggerEvaluate(onDecay)
                    else
                        set dead<i> = false
                        call TriggerEvaluate(onRemove)
                    endif
</i></i></i></i>


Furthermore, my solution for speeding up explosions and for determining whether something is an explosion or a removal.

on death
JASS:
            else
                set exploded<i> = true
                set animated<i> = false
                call TriggerEvaluate(onExplode)
</i></i>


on remove
JASS:
if (not exploded<i>) then
</i>



reincarnation is good too : D.
 

SanKakU

Member
Reaction score
21
please submit original names for your attachments and map names.
also, your test map was not very exciting...now i have to read the code to figure anything out.

interesting...why do you use your own game timer? i'm not saying whether it is good or bad, i'm just curious.

what is the unit animated event? you are making units die faster? what?
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top