• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

Problem with TimerStart

Status
Not open for further replies.
Level 10
Joined
Jan 24, 2009
Messages
606
Hi... I am having a problem with the native TimerStart.
here's the code:
JASS:
call TimerStart(aTimer, 0.10, false, function LA(caster, aGroup, time, EffectX, EffectY, EFFECT))

Here is the function LA:
JASS:
        static method LA takes unit u, group whichGroup, real Time,  real x, real y, string whichString returns nothing
        local action new = action.allocate()
            call DestroyEffectTimed(AddSpecialEffect(whichString, x, y), Time)
            call GroupEnumUnitsInRange(whichGroup, x, y, 150, null)
            loop
                set new.u = FirstOfGroup(whichGroup)
                exitwhen new.u == null
                if IsUnitEnemy(new.u, GetOwningPlayer(u)) == true then
                    call UnitDamageTarget(u, new.u, damage(), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
                endif
                set new.u = null
            endloop
            if(DESTROY_DESTRUCTABLE) then
                
            endif
        endmethod

Its basically the first time I'm using structs, and I really hope theres nothing wrong with my struct, even though it is quite bad.
The error I get is this: Line 454: Syntax Error, unexpected "("?
 
Level 10
Joined
Jan 24, 2009
Messages
606
Damn... Then I have an Idea....

Would this work??

JASS:
private function LoopActions takes nothing returns nothing
    local unit caster = GetTriggerUnit()
    local location loc = GetSpellTargetLoc()
    local real CasterX = GetUnitX(caster)
    local real CasterY = GetUnitY(caster)
    local real EffectX = GetLocationX(loc)
    local real EffectY = GetLocationY(loc)
    call LA(caster, aGroup, time, EffectX, EffectY, EFFECT)
endfunction

JASS:
call TimerStart(aTimer, 0.10, false, function LoopActions)

Its probably bad coding though...
 
You have to use structs. Think of using a struct as a "container" for whatever you need to pass through. I'll guide you step by step.

Okay, so first we need to make a struct to contain all the variables we need to pass. According to your parameters, we need:
  • The Caster ; Type: Unit
  • Some Group ; Type: Group
  • Time ; Type: Real
  • X-Coordinate ; Type: Real
  • Y-Coordinate ; Type: Real
  • Effect Path ; Type: String

Thus, your members should look like this:
JASS:
struct EffectSpellStuff
    unit caster
    group aGroup
    real time
    real EffectX
    real EffectY
    string EffectPath
endstruct

Now let's basically eliminate what we know won't need to be passed. Assuming the time is constant, we can remove that. Same for the EffectPath. The group can be removed as well since we can just use a normal group.

Okay, so let's say you have a standard spell scope:
JASS:
scope StandardSpell initializer Init
    globals
        private constant integer RAWCODE = 'A064' //modify this to your spell's rawcode
    endglobals

    private function Conditions takes nothing returns boolean
        if GetSpellAbilityId() == RAWCODE then
            
        endif
        return false
    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))
    endfunction
endscope

Now add your struct, and your constants:
JASS:
scope EffectSpellThing initializer Init
    globals
        private constant integer RAWCODE = 'A064' //modify this to your spell's rawcode
        private constant string EFFECT_PATH = "Abilities\\Spells\\Human\\MarkOfChaos\\MarkOfChaosTarget.mdl"
        private constant real EFFECT_TIME = 2
        private group aGroup = CreateGroup()
        
        private constant boolean DESTROY_DESTRUCTABLE = false
    endglobals
    
    private function damage takes nothing returns real
        return 150.
    endfunction
    
    private struct EffectSpell
        unit caster
        real EffectX
        real EffectY
        static method LA takes nothing returns nothing
            local unit first 
            call DestroyEffectTimed(AddSpecialEffect(EFFECT_PATH,.EffectX,.EffectY),EFFECT_TIME)
            call GroupEnumUnitsInRange(aGroup,.EffectX,.EffectY,150,null)
            loop
                set first = FirstOfGroup(aGroup)
                exitwhen first == null
                if IsUnitEnemy(first,GetOwningPlayer(.caster)) then
                    call UnitDamageTarget(caster,first,damage(),true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
                endif
                call GroupRemoveUnit(aGroup,first)
            endloop
            if DESTROY_DESTRUCTABLE then
            
            endif
        endmethod
        
        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            set .caster = GetTriggerUnit()
            set .EffectX = GetSpellTargetX()
            set .EffectY = GetSpellTargetY()
            return this
        endmethod
    endstruct

    private function Conditions takes nothing returns boolean
        if GetSpellAbilityId() == RAWCODE then
            call EffectSpell.create()
        endif
        return false
    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))
    endfunction
endscope

Okay, now we are trying to execute LA after 0.10 seconds with transferring the data. At the moment, LA will create syntax errors because we can't refer to them directly like that. "this" doesn't exist yet. Thus, we must use something like TimerUtils:
JASS:
scope EffectSpellThing initializer Init
    globals
        private constant integer RAWCODE = 'A064' //modify this to your spell's rawcode
        private constant string EFFECT_PATH = "Abilities\\Spells\\Human\\MarkOfChaos\\MarkOfChaosTarget.mdl"
        private constant real EFFECT_TIME = 2
        private group aGroup = CreateGroup()
        
        private constant boolean DESTROY_DESTRUCTABLE = false
    endglobals
    
    private function damage takes nothing returns real
        return 150.
    endfunction
    
    private struct EffectSpell
        unit caster
        real EffectX
        real EffectY
        static method LA takes nothing returns nothing
            local timer t = GetExpiredTimer() //retrieve the timer
            local thistype this = GetTimerData(t) //get the data
            local unit first 
            call DestroyEffectTimed(AddSpecialEffect(EFFECT_PATH,.EffectX,.EffectY),EFFECT_TIME)
            call GroupEnumUnitsInRange(aGroup,.EffectX,.EffectY,150,null)
            loop
                set first = FirstOfGroup(aGroup)
                exitwhen first == null
                if IsUnitEnemy(first,GetOwningPlayer(.caster)) then
                    call UnitDamageTarget(caster,first,damage(),true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
                endif
                call GroupRemoveUnit(aGroup,first)
            endloop
            if DESTROY_DESTRUCTABLE then
            
            endif
        endmethod
        
        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            local timer t = NewTimer() //This will create a new timer, or reuse an old one
            set .caster = GetTriggerUnit()
            set .EffectX = GetSpellTargetX()
            set .EffectY = GetSpellTargetY()
            call SetTimerData(t,this) //Attach the struct's data to the timer
            call TimerStart(t,0.10,false,function thistype.LA) //Start the timer
            return this
        endmethod
    endstruct

    private function Conditions takes nothing returns boolean
        if GetSpellAbilityId() == RAWCODE then
            call EffectSpell.create()
        endif
        return false
    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))
    endfunction
endscope

Now you have that, which is something like how you would do it. Although it isn't completely optimized, it should get the job done.

Good luck, hopefully this explains it a bit. =)

NewTimer() - This is a function of TimerUtils, which will check if there are any timers that were recycled to be available for use. If there isn't, then it will create a new timer.

SetTimerData(timer,data) - This is a function of TimerUtils, which will "attach" the struct instance to the timer. This way, you can retrieve the data in the callback using GetTimerData(t).

GetTimerData(timer) - This is a function of TimerUtils, which will retrieve the struct instance attached to the timer. Basically, GetTimerData(GetExpiredTimer()).

ReleaseTimer(timer) - This is a function of TimerUtils, which will recycle the timer for later use. If the timer stack is full, it will destroy the timer. (which doesn't happen very often as long as you code well)
 
Status
Not open for further replies.
Top