[vJASS] Optimizing my spell

Level 10
Sep 12, 2008
Well, hello.
I've made a vjass spell, using a struct, after a year of being away from WC in general.. Been quite a hassle, but managed to put something together,
the problem is, I'm not sure if its as efficient as possible.

Anyhow, about the spell:
Its a single unit ability, called Chained Dagger.
Technically, once cast on a unit, it gets replaced with a new ability for duration seconds.
after the duration is passed, it restores the original ability.
Right now it works quite well, but theres a 0.5 second lag or something around that at the first cast of the spell.

heres the code:

library ChainedDagger initializer onInit/*
*/  uses                                /*
*/      SpellEvent                      /*
*/      TimerUtils
    struct ChainedDagger
        public static integer Ability_Identifier = 'A001'
        public static integer NewAbility_Identifier = 'A002'
        public static integer AuraSpellBook_Identifier = 'A005'
        public static timer Timer
        public static real TimerSpeed = 0.032
        public static thistype array Instances
        public static integer Counter = 0
        public unit caster
        public unit target
        public real duration
        public integer id
        public boolean started
        public static method create takes unit caster, unit target, real duration returns thistype
            local thistype d = thistype.allocate()
            set d.caster = caster
            set d.target = target
            set d.duration = duration
            set d.id = d.assignIdentifier()
            set d.started = false
            return d
        public method assignIdentifier takes nothing returns integer
            set Instances[ChainedDagger.Counter] = this
            set ChainedDagger.Counter = ChainedDagger.Counter + 1
            return ChainedDagger.Counter - 1
        static method operator [] takes integer id returns thistype
            if (id >= Counter) then
                return -1
            return Instances[id]
        public method startCast takes nothing returns nothing
            local player p = GetOwningPlayer(.caster)
            call SetPlayerAbilityAvailable(p, ChainedDagger.Ability_Identifier, false)
            call UnitAddAbility(.caster, ChainedDagger.NewAbility_Identifier)
            call SetUnitAbilityLevel(.caster, ChainedDagger.NewAbility_Identifier, GetUnitAbilityLevel(.caster, ChainedDagger.Ability_Identifier))
            call UnitAddAbility(.target, ChainedDagger.AuraSpellBook_Identifier)
            set .started = true
            set p = null
        public method stopCast takes nothing returns nothing
            local player p = GetOwningPlayer(.caster)
            call SetPlayerAbilityAvailable(p, ChainedDagger.Ability_Identifier, true)
            call UnitRemoveAbility(.caster, ChainedDagger.NewAbility_Identifier)
            call UnitRemoveAbility(.target, ChainedDagger.AuraSpellBook_Identifier)
            set p = null
            call .destroy()
        private method destroy takes nothing returns nothing
            //Fix the indexing system
            local integer cnt = 0
            local integer max = ChainedDagger.Counter
            set ChainedDagger[cnt].id = ChainedDagger[cnt].id - 1
            set cnt = cnt + 1
            exitwhen cnt <= max
            set ChainedDagger.Instances[.id] = -1
            //Remove the leaking data
            set .caster = null
            set .target = null
            set .started = false
        public static method onUpdate takes nothing returns nothing
            local integer data
            local thistype d
            if (ChainedDagger.Counter <= 0) then
                set ChainedDagger.Counter = 0
                call ReleaseTimer(ChainedDagger.Timer)
            set data = GetTimerData(ChainedDagger.Timer)
            set d = ChainedDagger[data]
            if (d.started == true) then
                set d.duration = d.duration - ChainedDagger.TimerSpeed
                if (d.duration <= 0) then
                    call d.stopCast()
            if (data < ChainedDagger.Counter - 1) then
                call SetTimerData(ChainedDagger.Timer, data + 1)
                call SetTimerData(ChainedDagger.Timer, 0)
        public static method onSpellCast takes nothing returns nothing
            local unit caster = SpellEvent.CastingUnit
            local unit target = SpellEvent.TargetUnit
            local real duration = 3 // 3 seconds so I could test the lag thingy, it only happens on the first run.
                                    // there's going to be an equation used by it, but should suffice for now.
            local thistype d = thistype.create(caster, target, duration)
            call d.startCast()
            if (ChainedDagger.Counter == 1) then
                set ChainedDagger.Timer = NewTimerEx(0)
                call TimerStart(ChainedDagger.Timer, ChainedDagger.TimerSpeed, true, function ChainedDagger.onUpdate)
            set caster = null
            set target = null
    public function onInit takes nothing returns nothing
        call RegisterSpellCastResponse(ChainedDagger.Ability_Identifier, ChainedDagger.onSpellCast)

P.S: I've always been bad at explaining stuff, tell me if I should give more details regarding something.. ><
Thanks for the help in advance.
Level 32
Mar 27, 2008
Try this;
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Custom script: local unit u = CreateUnit(Player(15), 'hfoo', 0.00, 0.00, 0.00)
      • Custom script: call UnitAddAbility(u, 'A002')
      • Custom script: call UnitAddAbility(u, 'A005')
      • Custom script: call RemoveUnit(u)
      • Custom script: set u = null
You must preload the abilities associated with that spell to prevent lag on first cast.
The lag would occur at the Loading Screen (increasing its Loading Speed by bit).
Rather than lag on first cast, better for you to load it in Loading Screen right ?

I did not test it yet (because I don't have vJASS) but I highly anticipate it would be this as the solution to the first cast lag issues.
It might be because of TimerUtils. Vexorian updated it so that it would do the init on the first "NewTimer" call rather than on actual initialization. This was because people were complaining about the initialization method--that it would not work if you use the function in a module initializer. Vexorian opted to do an evaluation upon first call rather than doing a module initializer, so that might be why you get the lag.

Perhaps try using this instead?

See if it fixes it.

Otherwise, it might be the ability as defskull said.
Level 10
Sep 12, 2008
Meh, apparently defskull's idea fixed it, thats kinda odd, never happend to me before,
also, switched to TimerUtilsEx..

Well, is there any way to improve the code? The build of the struct, etc..
That was the actual question, after all xD
Level 32
Mar 27, 2008
Meh, apparently defskull's idea fixed it, thats kinda odd, never happend to me before,
It only happens to abilities that has 4 levels and beyond.
Just like DotA, you preload abilities associated with that Hero when you picked the Hero, that is why there is a lag when a new Hero enters the map, it is actually preloading it.