HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

How to optimize spell

10-30-2007, 10:24 PM#1
jw232
This spell is supposed to make the caster dash towards the target and hurt units that get in the way. It uses local handle variables. Now, I'm wondering if this spell could be more efficient if I used something like Vexorian's Caster System or vJass? I'll learn vJass if it's that much better...
Collapse JASS:
function dashMove takes nothing returns nothing
    local timer t=GetExpiredTimer()
    local real Velocity=40
    local real DamageRadius=100
    local unit cast=GetHandleUnit(t,"cast")
    local unit targ=GetHandleUnit(t,"targ")
    local group g=CreateGroup()
    local group done=GetHandleGroup(t,"done")
    local real angle=GetHandleReal(t,"angle")
    local real damage=100
    local real distance=GetHandleReal(t,"distance")+Velocity
    local real maxdistance=GetHandleReal(t,"maxdistance")
    local unit u
    
    call SetHandleReal(t,"distance",distance)
       
    call SetUnitX(cast,GetUnitX(cast)+Cos(bj_DEGTORAD*angle)*Velocity)
    call SetUnitY(cast,GetUnitY(cast)+Sin(bj_DEGTORAD*angle)*Velocity)
    call SetUnitFacing(cast,angle)
       
    call GroupEnumUnitsInRange(g,GetUnitX(cast),GetUnitY(cast),DamageRadius,null)
    set g=grouper(cast,g)
    loop
        set u=FirstOfGroup(g)
    exitwhen u==null
        call GroupRemoveUnit(g,u)
        if IsUnitInGroup(u,done)==false then
            call GroupAddUnit(done,u)
            call UnitDamageTargetBJ(cast,u,damage,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL)
        endif
    endloop
    
    call SetHandleHandle(t,"done",done)
    
    if distance>=maxdistance then
        call PauseTimer(t)
        call FlushHandleLocals(t)
        call DestroyTimer(t)
        call DestroyGroup(done)
    endif
    
    set t=null
    set cast=null
    set targ=null
    call DestroyGroup(g)
    set g=null
    set done=null
    set u=null
endfunction

function Trig_dash_Conditions takes nothing returns boolean
        return GetSpellAbilityId() == 'A03G'
endfunction

function Trig_dash_Actions takes nothing returns nothing
    local timer t=CreateTimer()
    local unit cast=GetTriggerUnit()
    local unit targ=GetSpellTargetUnit()
    local real angle=GetAngleBetweenPoints(GetUnitX(cast),GetUnitY(cast),GetUnitX(targ),GetUnitY(targ))
    local group done=CreateGroup()

    call SetHandleHandle(t,"cast",cast)
    call SetHandleHandle(t,"targ",targ)
    call SetHandleHandle(t,"done",done)
    call SetHandleReal(t,"angle",angle)
    call SetHandleReal(t,"maxdistance",600)
    call SetHandleReal(t,"distance",0)
    call TimerStart(t,.03,true,function dashMove)

    call TriggerSleepAction(0)
    set t=null
    set cast=null
    set targ=null
    set done=null
endfunction

//===========================================================================
function InitTrig_dash takes nothing returns nothing
    set gg_trg_dash = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_dash, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_dash, Condition( function Trig_dash_Conditions ) )
    call TriggerAddAction( gg_trg_dash, function Trig_dash_Actions )
endfunction
10-31-2007, 12:02 AM#2
Anopob
Well you could make the Trig_dash_Conditions "return CONDITION" instead of the whole messy if. Otherwise it is fine or you could try to learn vJass because I've heard it is so much better (and it probably is but I don't know).

P.S. For your first function you might also want to destroy "done" unit group, not just the "g" one.
10-31-2007, 12:47 AM#3
Ammorth
vJass would remove all those ugly SetHandleReal() and SetHandleHandle with 1 sexy SetHandleInteger(YourStruct). You can then save everything to your struct and get it all back later, increasing speed (since GameCache is slow).

You could then create a stack out of those structs and not even need gamecache. Just make the structs into a stack and loop through the stack. This would also benefit as it would make the spell run off a single timer, for all instances (big kodos in today's day of jass).
10-31-2007, 02:29 AM#4
jw232
Sounds good. So SetHandleInteger(YourStruct) doesn't involve gamecache?
10-31-2007, 03:51 AM#5
Pyrogasm
That specifically would use gamecache, but there are struct attachment systems out there that use global arrays and otherwise to avoid the gamecache.
10-31-2007, 04:29 AM#6
Ammorth
or include a next and previous value in your struct and then create a global struct variable that would hold the first struct in a stack.

I also created a simple stack feature which creates another struct which can be used as a simple stack.

Collapse JASS:
//! textmacro STACK takes TYPE, NAME
globals
    $NAME$Stack First$NAME$Stack
endglobals

    struct $NAME$Stack
        $NAME$Stack next
        $NAME$Stack prev
        $TYPE$ current
        
        static method create takes $TYPE$ cur returns $NAME$Stack
            local $NAME$Stack s = $NAME$Stack.allocate()
            set s.current = cur
            set s.next = First$NAME$Stack
            set First$NAME$Stack.prev = s
            return s
        endmethod
        
        method onDestroy takes nothing returns nothing
            if .prev == null then  // first of stack
                set First$NAME$Stack = .next
                set .next.prev = 0
            else  // middle or at end of stack
                set .next.prev = .prev
                set .prev.next = .next
            endif
            set .next = 0
            set .prev = 0
        endmethod
    endstruct
//! endtextmacro

so, to create a stack for a unit, you would do //! runtextmacro Stack(unit, Casters) and then you could do local CastersStack cs = CastersStack.create(SomeUnit) and it would add it to the stack. The same with structs.

Collapse JASS:
struct spelldata
    unit caster
    unit target
    integer level
    effect e

    // methods and what not
endstruct

//! call runtextmacro Stack(spelldata, Spell)

function loop takes nothing returns nothing
    local SpellStack ss = FirstSpellStack
    local spelldata sd
    loop
        exitwhen ss == null
        set sd = ss.current
        // do stuff with your spell struct sd
        set ss = ss.next
    endloop
endfunction

And you get a stack to loop all your spells though. Of course it would be more efficent to include the stack in the struct, but you should get the idea.
11-01-2007, 02:41 AM#7
jw232
Ok, thanks for the help. How should I start making this spell in vJass? Do I create the spelldata struct in the actions of this trigger? And then start a timer that executes the loop function you provided? Should I recycle timers using CSSafety? I also put some other questions in if you don't mind answering them.
Collapse JASS:
struct spelldata
    unit caster
    unit target
    integer level
    effect e

    //I don't know exactly what to put here, basically everything I was attaching to the timer before?
endstruct

//! call runtextmacro Stack(spelldata, Spell)

function loop takes nothing returns nothing
    local SpellStack ss = FirstSpellStack
    local spelldata sd
    loop
        exitwhen ss == null
        set sd = ss.current
        // do stuff with your spell struct sd
        // Do I put the stuff that dashMove does in here?
        set ss = ss.next
    endloop
endfunction

function Trig_dashstart_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A03G'
endfunction

function Trig_dashstart_Actions takes nothing returns nothing
//create struct here?
//start timer here? CSSafety?
endfunction

===========================================================================
function InitTrig_dashstart takes nothing returns nothing
    set gg_trg_dashstart = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_dashstart, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_dashstart, Condition( function Trig_dashstart_Conditions ) )
    call TriggerAddAction( gg_trg_dashstart, function Trig_dashstart_Actions )
endfunction
11-01-2007, 05:09 AM#8
Ammorth
A struct is basically a group of variables. The variables declared after the header "struct spelldata" would be them.

After that, you would put methods. Example:
Collapse JASS:
struct whatever
    unit caster

    static method create takes unit u returns whatever  //will replace the normal create function
        local whatever w = whatever.allocate() // use allocate within a create method
        set w.caster = u
        return u
    endmethod

    method ChangeCaster takes unit u returns nothing // normal methods also take the struct as an arguemen (expressed with a . in front of the variables).
        set .caster = u
    endmethod

    method onDestroy takes nothing returns nothing
        set .caster = null
    endmethod
endstruct

function test takes nothing returns nothing
    local whatever w = whatever.create(GetTriggeringUnit())
    call w.ChangeCaster(SomeOtherUnit)
    call w.destroy()
endfunction

Methods are used to modify values within the struct, or perform struct specific actions. You can also modify struct data directly by using set w.caster = SomeOtherUnit instead of the method.

The the loop function, it is setup to use the struct variable sd for the actions. There you would manipulate all your struct data and perform your movements. So basically yes.

The last comment, you would create the struct there and thats it. Since the spell is automatically added to the stack with the stack struct, it will be run off a global varaible. You may want to pause the timer when there are no spells to run, but its personal preference.

If you are using a stack, it is not required to use CSSafety or CSCache since it only requires 1 timer and all the data is in a global stack (but local to each struct).

I personally would recommend learning to code in vJass. It makes life a lot easier and your codes simpler, yet complex at the same time (more freedoms and more stream-lined/organized).

You can read the JassNewGen manual either in the NewGen Folder or online here.

Once you have converted your spell to vJass, post it here and I shall help you clean it up.

If you have any questions before then, ask; although I don't want to make the code for you. Read the manual and even other guides in the tutorial section first.
11-01-2007, 10:58 AM#9
Silvenon
Quote:
Originally Posted by Ammorth
vJass would remove all those ugly SetHandleReal() and SetHandleHandle with 1 sexy SetHandleInteger(YourStruct).

Hahaha!

Uhmm, what is SetHandleInteger? Shouldn't it be SetHandleInt?
11-01-2007, 02:33 PM#10
Ammorth
I guess it should be. Just shows how much CSCache I really use since stacks are superior.
11-01-2007, 08:18 PM#11
jw232
Am I supposed to create a stack? Where should I do that? And should I start one global timer executing the loop function at map initialization?
11-01-2007, 10:25 PM#12
Ammorth
The stack should be created after the struct it is being used for.

For the timer, you can do that for now. Once the spell is working, you can make it pause when the stack has no spells in it.
11-02-2007, 12:54 AM#13
jw232
I mean where should I put set FirstSpellStack=SpellStack.Create(sd)? In Trig_dash_Actions?
11-02-2007, 02:09 AM#14
Ammorth
First off, do not set the FirstSpellStack variable to anything since it is already being manipulated within the stack struct. You can set a variable to it though.
Do not do:set FirstSpellStack = somestruct This is okaysomestruct = FirstSpellStack
You should create a struct every time the spell is casted.

Collapse JASS:

struct spell
    unit caster
    real x
    real y
    real face

    method create takes unit u returns spell
        local spell s = spell.allocate()
        set s.caster = u
    endmethod
endstruct

//! runtextmacro Stack(spell, Spell)

function Spell_cast_actions takes nothing returns nothing
    local unit u = GetSpellAbilityUnit()
    local spell s = spell.create(u)  // our spell struct (not the stack struct) (also sets the caster when created)
    set spell.x = GetUnitX(u)
    set spell.y = GetUnitY(u)
    set spell.face = GetUnitFacing(u)
    call SpellStack.create(s) // creates the stack struct and adds the spell to the stack.
endfunction

Again, I don't mind helping you, but please try to completely create your code in vJass and then post it here. It will be easier for me to address all problems at once than one at a time. Also, it will give you a chance to figure it out yourself, since trial and error is one of the best methods of learning new things, besides guides* and manuals.

*Guides:
- vJass for Spell Makers
- How to Create a New Object Type (vJass)
- How to Properly Inline Text Macros
- Text Macros in vJass
11-02-2007, 02:35 AM#15
jw232
Alright, this is my attempt. I get an error "Unitialized variable "FirstSpellStack" used in function "s__SpellStack_create" " when I cast the spell.
Collapse JASS:
//! textmacro STACK takes TYPE, NAME
globals
    $NAME$Stack First$NAME$Stack
endglobals

    struct $NAME$Stack
        $NAME$Stack next
        $NAME$Stack prev
        $TYPE$ current
        
        static method create takes $TYPE$ cur returns $NAME$Stack
            local $NAME$Stack s = $NAME$Stack.allocate()
            set s.current = cur
            set s.next = First$NAME$Stack
            set First$NAME$Stack.prev = s
            return s
        endmethod
        
        method onDestroy takes nothing returns nothing
            if .prev == null then  // first of stack
                set First$NAME$Stack = .next
                set .next.prev = 0
            else  // middle or at end of stack
                set .next.prev = .prev
                set .prev.next = .next
            endif
            set .next = 0
            set .prev = 0
        endmethod
    endstruct
//! endtextmacro

globals
    timer dashTimer=CreateTimer()
endglobals

struct spelldata
    unit cast
    real distance
    real maxdistance
    real velocity
    real angle
    group done
    group g
    real damageradius=100
    real damage

    static method create takes unit cast, unit targ, real maxdistance, real velocity returns spelldata
        local spelldata w=spelldata.allocate()
        set w.cast=cast
        set w.distance=0
        set w.maxdistance=maxdistance
        set w.velocity=velocity
        set w.angle=GetAngleBetweenPoints(GetUnitX(cast),GetUnitY(cast),GetUnitX(targ),GetUnitY(targ))
        set w.done=CreateGroup()
        set w.g=CreateGroup()


    call BJDebugMsg("created spelldata")
        
        return w
    endmethod
    
    method onDestroy takes nothing returns nothing
        set .cast=null
        call DestroyGroup(.done)
        call DestroyGroup(.g)

    call BJDebugMsg("destroyed spelldata")
    endmethod
endstruct

//! runtextmacro STACK("spelldata", "Spell")

function dashloop takes nothing returns nothing
    local SpellStack ss = FirstSpellStack
    local spelldata sd
    local unit u
    local real x
    local real y

    call BJDebugMsg("dashloop")

    loop
        exitwhen ss == null
    call BJDebugMsg("stack loop")
        set sd = ss.current
        set sd.distance=sd.distance+sd.velocity
        set x=GetUnitX(sd.cast)+Cos(bj_DEGTORAD*sd.angle)*sd.velocity
        set y=GetUnitY(sd.cast)+Sin(bj_DEGTORAD*sd.angle)*sd.velocity
        call SetUnitX(sd.cast,x)
        call SetUnitY(sd.cast,y)
        call SetUnitFacing(sd.cast,sd.angle)
        call GroupEnumUnitsInRange(sd.g,x,y,sd.damageradius,null)
        set sd.g=grouper(sd.cast,sd.g)
        loop
            set u=FirstOfGroup(sd.g)
            exitwhen u==null
            call GroupRemoveUnit(sd.g,u)
            call UnitDamageTargetBJ(sd.cast,u,sd.damage,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL)
        endloop
        if sd.distance>=sd.maxdistance then
            call sd.destroy()
        endif
        
        set u=null
        
        set ss = ss.next
    endloop
endfunction

function Trig_dashstart_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A000'
endfunction

function Trig_dashstart_Actions takes nothing returns nothing
    local unit cast=GetTriggerUnit()
    local unit targ=GetSpellTargetUnit()
    local spelldata sd=spelldata.create(cast,targ,600,40)

    call BJDebugMsg("start")

    set sd.damage=100
    call SpellStack.create(sd)

    //call TimerStart(dashTimer,.03,true,function dashloop)

    set cast=null
    set targ=null
endfunction

function InitTrig_dashstart takes nothing returns nothing
    set gg_trg_dashstart = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_dashstart, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_dashstart, Condition( function Trig_dashstart_Conditions ) )
    call TriggerAddAction( gg_trg_dashstart, function Trig_dashstart_Actions )
endfunction