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

[JASS] Timers and MUI

Status
Not open for further replies.
Level 24
Joined
Feb 28, 2007
Messages
3,480
Short question. How do I make this MUI?
JASS:
scope Blink initializer Init
    globals
        private unit caster
    endglobals

    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == 'A00E'
    endfunction
    
    private function RemoveBlink takes nothing returns nothing
        call UnitRemoveAbility(caster,'A00N')
        set caster = null
    endfunction

    private function Actions takes nothing returns nothing
        local location spellLoc = GetSpellTargetLoc()
        local timer t = NewTimer()
        set caster = GetSpellAbilityUnit()
        call UnitAddAbility(caster,'A00N')
        call IssuePointOrderLoc(caster,"blink",spellLoc)
        call RemoveLocation(spellLoc)
        call TimerStart(t,0.35,false,function RemoveBlink)
    endfunction

    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t,Condition(function Conditions))
        call TriggerAddAction(t,function Actions)
        set t = null
    endfunction
endscope
 
Level 15
Joined
Oct 16, 2010
Messages
941
You could try adding each unit to a group and saving a real value of 0 to it. Then set up a periodic that runs every .05 seconds, and whenever it procs pick every unit in the group and add .05 to the saved real (to keep track of the time). If the units saved real is .35 or higher then remove the unit from the group, reset the real to 0, and remove the blink ability.

Or you could try creating a seperate timer for every unit and saving the timer to the unit or vise versa.
 
Level 15
Joined
Oct 16, 2010
Messages
941
You could do it with hashtables or you could just create a 1 var struct.

EDIT - here's an example that would work:

JASS:
scope Blink initializer Init
    globals
        private constant   timer        TIMER                   = CreateTimer()
        private            hashtable    HASH                    = InitHashtable() 
        private            group        GROUP                   = CreateGroup()
        private constant   real         REMOVAL_TIME            = .35
        private constant   real         TICK                    = .05
        private constant   integer      ABILITY_ID              = 'A00E'
        private constant   integer      BLINK_ID                = 'A00N'
    endglobals
    
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == ABILITY_ID
    endfunction

    private function Actions takes nothing returns nothing
        local unit caster = GetSpellAbilityUnit()
        
        call UnitAddAbility(caster, BLINK_ID)
        call IssuePointOrder(caster, "blink", GetSpellTargetX(), GetSpellTargetY())
        
        call GroupAddUnit(GROUP, caster)
        call SaveReal(HASH, GetHandleId(caster), 1, 0)
        
        set caster = null
    endfunction

    private function Periodic takes nothing returns nothing
        local unit picked = GetEnumUnit()
        call SaveReal(HASH, GetHandleId(picked), 1, (LoadReal(HASH, GetHandleId(picked), 1) + 1))
        if (LoadReal(HASH, GetHandleId(picked), 1) >= REMOVAL_TIME) then
            call UnitRemoveAbility(picked, BLINK_ID)
            call GroupRemoveUnit(GROUP, picked)
        endif
    endfunction
    
    private function Pick takes nothing returns nothing
        call ForGroup(GROUP, function Periodic)
    endfunction
    
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t,Condition(function Conditions))
        call TriggerAddAction(t,function Actions)
        call TimerStart(TIMER, TICK, true, function Pick)
        set t = null
    endfunction
endscope
 
For structs, this should work:
JASS:
scope Blink initializer Init   
    struct Blink
        unit caster
        
        private static method RemoveBlink takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = GetTimerData(t) //get the data from the timer
            call UnitRemoveAbility(this.caster,'A00N')
            call ReleaseTimer(t) //release the timer
            call this.destroy() //destroy the instance
            set t = null
        endmethod
        
        static method create takes unit u returns thistype
            local thistype this = thistype.allocate()
            local timer t   = NewTimer()
            set this.caster = u //assign the data to the caster
            call SetTimerData(t,this) //attach the data to the timer
            call TimerStart(t,0.35,false,function RemoveBlink)
            set t = null
            return this
        endmethod
    endstruct

    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == 'A00E'
    endfunction

    private function Actions takes nothing returns nothing
        local location spellLoc = GetSpellTargetLoc()
        call UnitAddAbility(caster,'A00N')
        call IssuePointOrderLoc(caster,"blink",spellLoc)
        call Blink.create(GetTriggerUnit()) // call the create function
        call RemoveLocation(spellLoc)
        set spellloc = null
    endfunction

    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t,Condition(function Conditions))
        call TriggerAddAction(t,function Actions)
        set t = null
    endfunction
endscope

If you have a unit indexing system, this can be improved to ignore the struct completely by just creating a unit array and assigning the timer data to their custom value. (since you only need to store the unit)
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
What's with all of these enormously over complicated answers.

JASS:
        local timer t = NewTimer()
        set caster = GetSpellAbilityUnit()
        call UnitAddAbility(caster,'A00N')

Oh look, he uses the function NewTimer(), meaning (most probably) that he also has access to the function SetTimerData.

You can go ahead and get rid of the caster global. I've added a wrapper struct to tie unit-data with integer values and then made some changes to your Actions function and RemoveBlink function.

JASS:
    private struct casterwrapper
        unit u = null
        static method create takes unit u returns thistype
            local thistype d = allocate()
            set d.u = u
            return d
        endmethod
    endstruct    

    private function RemoveBlink takes nothing returns nothing
        local casterwrapper d = casterwrapper(GetTimerData(GetExpiredTimer()))
        call UnitRemoveAbility(d.u, 'A00N')
        call ReleaseTimer(GetExpiredTimer())
        call d.destroy()
    endfunction

    private function Actions takes nothing returns nothing
        local location spellLoc = GetSpellTargetLoc()
        local timer t = NewTimer()
        local unit caster = GetTriggerUnit() // returns the same thing as GetSpellAbilityUnit
        local casterwrapper d = casterwrapper.create(caster)

        call SetTimerData(t, d)
        call UnitAddAbility(caster,'A00N')
        call IssuePointOrderLoc(caster,"blink",spellLoc)
        call RemoveLocation(spellLoc)

        call TimerStart(t,0.35,false,function RemoveBlink)
        set spellLoc = null
        set t = null
        set caster = null
    endfunction

Note that you forgot to null your variables at the end of your Actions function. This would cause memory leaks. The casterwrapper is merely an integer so it does not have to be "nulled", but I do remember to destroy it in the RemoveBlink function to clean up the indexing reference.

In your RemoveBlink function you also forgot to release the timer (which you initialize using NewTimer()). The timer that you run is not a repeating timer, so as soon as RemoveBlink is executed it will no longer be needed.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,199
Definitely use his over mine. It is alot more efficient with structs than hashtables.

Actually I am not sure. As the timer encoding algorthim is not free. HashTables also have an efficiency of O(1) for this sort of task. Thus has someone actually tested if hashtable storage is slower than timer encoding? What I am refering to is encoding the struct onto the hashtable, obviously for data lookup structs will be faster.

Last I heard was that hashtables run at the time cost of a few array lookups, which would be extreemly hard to beat with any form of encoding algorthim.

PurgeandFire111, does that script not require a third party library as well? You did not define it as a requirement but fortunatly he seems to have it in his map already.
 
Actually I am not sure. As the timer encoding algorthim is not free. HashTables also have an efficiency of O(1) for this sort of task. Thus has someone actually tested if hashtable storage is slower than timer encoding? What I am refering to is encoding the struct onto the hashtable, obviously for data lookup structs will be faster.

Last I heard was that hashtables run at the time cost of a few array lookups, which would be extreemly hard to beat with any form of encoding algorthim.

PurgeandFire111, does that script not require a third party library as well? You did not define it as a requirement but fortunatly he seems to have it in his map already.

Either way, you have to attach the struct instance to the handle id of the timer, so it comes down to a difference of the id-0x100000 stored in an array or the function call of SaveInteger(). (1st method is faster)

For the second part, yes, it requires TimerUtils. He used NewTimer() in his code, so I figured he had that in his map. ;D
 
I assume it backups then to hashtable if the array gets exceeded (possible with badly made or large maps).

Yep. Although, it doesn't automatically change it. Instead, it just shows an error message. If you get that message, then you either are doing something wrong, or you need to switch to the hashtable version. (he uses a bunch of static-ifs to check which version you want to use)
 
Status
Not open for further replies.
Top