• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[JASS] Learning JASS. Last Question

Status
Not open for further replies.
Level 16
Joined
Mar 3, 2006
Messages
1,564

JASS:
scope SpellTemplate initializer Init
 
private keyword Data
 
globals
    private constant real TIMER_INTERVAL = 0.035
    private constant integer ABIL_ID = 'A000' //change this to the rawcode of your spell
    //Other constant globals here...
 
 
    private Data array data
    private integer index=0
    private timer tim
 
    //Other changeable globals here...
 
endglobals
 
private function Execute takes nothing returns nothing
    local integer i=0
    local Data d
    //Other local variables here...
 
 
    loop
        exitwhen i>=index
 
        set d=data[i]
 
        //Code to run each timer execution here...
 
 
        set i=i+1
    endloop
    //Any leak cleaning code here...
 
endfunction
 
private struct Data
    //Declare struct members here...
    unit caster
 
    integer ID
 
    static method create takes unit caster returns Data //make sure you change this based on what data you need
        local Data d = Data.allocate()
 
        //Other create code here...
 
 
        set data[index]=d
        set d.ID=index
        if index==0 then
            call TimerStart(tim, TIMER_INTERVAL, true, function Execute)
        endif
        set index=index+1
 
        return d
    endmethod
 
    method onDestroy takes nothing returns nothing
        set index=index-1
        set data[this.ID]=data[index]
        set data[this.ID].ID=this.ID
 
        if index==0 then
            call PauseTimer(tim)
        endif
        //Other destroy code here (such as leak cleaning, removing units etc)...
 
    endmethod
 
endstruct
 
private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == ABIL_ID
endfunction
 
private function Actions takes nothing returns nothing
    call Data.create(GetTriggerUnit()) //make sure you change this based on what data your spell needs
endfunction
 
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddAction(t, function Actions)
    call TriggerAddCondition(t, Condition(function Conditions))
 
    set tim = CreateTimer()
    //Other initialisation things here...
 
endfunction
 
endscope


1) In the function Execute, do I add the action of moving the missile unit in that function ?

2) What is the purpose of using the integer ID ?

3) Why do I have to use the 2 integers i and index ?

4) The function
JASS:
call TimerStart(tim, TIMER_INTERVAL, true, function Execute)
this only starts the timer but where is the event that registers that the timer has expired ?
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
Seriously, some of your questions were decent, but 90% of them are just complete nonsense. Do you even try to look at the code and understand what it does?
You won't learn much if every time you see something you don't get right that second, you post "why that" and "what that".
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
Seriously, some of your questions were decent, but 90% of them are just complete nonsense. Do you even try to look at the code and understand what it does?
You won't learn much if every time you see something you don't get right that second, you post "why that" and "what that".

Well, I may be because of me misunderstanding the codes and their uses properly.

Also I don't know if the method I used before to understand a code is right or not ? But what I do to understand any code is track the variables, like in loops I put the index with a certain value and use it to track the rest of the functions but in this codes I can't track them; thats why I find it difficult to understand why we used a certain variables in our codes.

I hope you understand what I mean :ned:
 
Level 8
Joined
Aug 4, 2006
Messages
357
Starquizer said:
1) In the function Execute, do I add the action of moving the missile unit in that function ?
Yes. If you want to move missile units gradually, put the move actions inside the loop so that each time Execute is called, it will move each of the missile units a little bit towards their destinations.

Starquizer said:
2) What is the purpose of using the integer ID ?
Each instance of Data is assigned a place in the private Data array data. The onDestroy method needs to know where an instance was located in the array in order to destroy it. If we didn't allow each instance to know its place in the array, we would have to loop through possibly the entire array until we found the index of the currect instance.

Starquizer said:
3) Why do I have to use the 2 integers i and index ?
The global integer index is always equal to the number of instances of the struct. When we create a new instance of Data, we set data[index] = the instance we just created, and we increase the index by 1. This effectively adds the new instance to the end of the array, and updates the size. When we destroy an instance, we replace it with the instance that was at the end of the array, and decrease the index by 1. The local integer i is a temporary integer commonly used in loops. It allows us to perform actions on each of the elements in an array by storing the index of the element we are working with. After each loop, we increase i by 1 so that we can work with the next element. It does not change the global integer "index" in any way and does not affect the size of the array.

Starquizer said:
4) The function
JASS:
call TimerStart(tim, TIMER_INTERVAL, true, function Execute)
this only starts the timer but where is the event that registers that the timer has expired ?
This starts tim as a repeating timer that expires every TIMER_INTERVAL seconds, and runs the function Execute whenever it expires. Basically, tim is set to execute the function Execute every 0.035 seconds.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564

JASS:
scope SpellTemplate initializer Init
 
private keyword Data
 
globals
    private constant real TIMER_INTERVAL = 0.035
    private constant integer ABIL_ID = 'A000' //change this to the rawcode of your spell
    //Other constant globals here...
 
 
    private Data array data
    private integer index=0
    private timer tim
 
    //Other changeable globals here...
 
endglobals
 
private function Execute takes nothing returns nothing
    local integer i=0
    local Data d
    //Other local variables here...
 
 
    loop
        exitwhen i>=index
 
        set d=data[i]
 
        //Code to run each timer execution here...
 
 
        set i=i+1
    endloop
    //Any leak cleaning code here...
 
endfunction
 
private struct Data
    //Declare struct members here...
    unit caster
 
    integer ID
 
    static method create takes unit caster returns Data //make sure you change this based on what data you need
        local Data d = Data.allocate()
 
        //Other create code here...
 
 
        set data[index]=d
        set d.ID=index
        if index==0 then
            call TimerStart(tim, TIMER_INTERVAL, true, function Execute)
        endif
        set index=index+1
 
        return d
    endmethod
 
    method onDestroy takes nothing returns nothing
        set index=index-1
        set data[this.ID]=data[index]
        set data[this.ID].ID=this.ID
 
        if index==0 then
            call PauseTimer(tim)
        endif
        //Other destroy code here (such as leak cleaning, removing units etc)...
 
    endmethod
 
endstruct
 
private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == ABIL_ID
endfunction
 
private function Actions takes nothing returns nothing
    call Data.create(GetTriggerUnit()) //make sure you change this based on what data your spell needs
endfunction
 
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddAction(t, function Actions)
    call TriggerAddCondition(t, Condition(function Conditions))
 
    set tim = CreateTimer()
    //Other initialisation things here...
 
endfunction
 
endscope


1) When will the method onDestroy be called ?

2) Can the variable d.ID be used outside the Data type struct ?

3) If the spell is casted more than one time at a time, won't there be an overlap in the timer tim ?
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
Globals:
TIMER_INTERVAL = 0.035 (real)
ABIL_ID = 'A000' (integer)
data[] (Data)
index = 0 (integer)
tim (timer)
----------------------------------------

A unit cast the spell.

Events are registered and conditions are checked.

the function Actions will be executed and the timer will be created, Data.create will be called.

Data.create(GetTriggerUnit())
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
data[0] = d
d.ID = 0
If index == 0 (TRUE) then
TimerStart(tim, 0.035, true, function Execute)
endif
index = 1
return d :)confused: what will be the return value of this ?)

When the timer expires:

function Execute()
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
i = 0 (local integer)
d (local Data)
loop [exitwhen: 0 >= 1 (FALSE)]
d = data[0]
// Adding the action of moving the missile unit here
i = 1
--- <loop run again> -----------------------------
[exitwhen: 1 >= 1 (TRUE)]
endloop


When onDestroy is called:

onDestroy()
¯¯¯¯¯¯¯¯¯¯
index = 0
data[0] = data[0]
data[0].ID = 0
If index == 0 (TRUE) then
PauseTimer(tim)
endif

That is as far as I get and I don't know even if this is right or wrong and I couldn't track how the function goes when more than one cast the spell.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
When the timer expires:

function Execute()
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
i = 0 (local integer)
d (local Data)
loop [exitwhen: 0 >= 1 (FALSE)]
d = data[0]
// Adding the action of moving the missile unit here
i = 1
--- <loop run again> -----------------------------
[exitwhen: 1 >= 1 (TRUE)]
endloop

That was the first expire.

> 2nd Expire: (index = 1 if onDestroy hasn't called yet)
i = 0
d
loop [exitwhen: 0 >= 1 (FALSE)]
d = data[0]
// Move the missile unit another small distance towards destination
i = 1
endloop


Is all that I said true or non-sense ?
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
1) I need a dummy unit which will be the missile but I don't know where to put its variable, do I create it in the globals or in the struct members ?

2) Do I need variable for the target point so I could move the dummy unit and do I also put it in the struct ?

3) Where is the function that tells that the dummy unit has reached its final destination so that onDestroy be called ?

4) What else do I need in the script; what I was given is just a template ?


JASS:
scope SpellTemplate initializer Init
    private keyword Data
 
    globals
        private constant real TIMER_INTERVAL = 0.035
        private constant integer ABIL_ID = 'A000' //change this to the rawcode of your spell
        //Other constant globals here...
        private Data array data
        private integer index = 0
        private timer tim
        // Other changable globals here...
    endglobals
 
 
    private function Execute takes nothing returns nothing
        local integer i = 0
        local Data d
        // Other local variables here...
        loop
            exitwhen i >= index
            set d = data[i]
            //Code to run each timer execution here...
            set i = i + 1
        endloop
        //Any leak cleaning code here...
    endfunction
 
 
    private struct Data
        //Declare struct members here...
        unit caster
        integer ID
 
        static method create takes unit caster returns Data //make sure you change this based on what data you need
            local Data d = Data.allocate()
            //Other create code here...
            set data[index] = d
            set d.ID = index
            if index == 0 then
                call TimerStart(tim, TIMER_INTERVAL, true, function Execute)
            endif
            set index = index + 1
            return d
        endmethod
 
        method onDestroy takes nothing returns nothing
            set index = index-1
            set data[this.ID] = data[index]
            set data[this.ID].ID = this.ID
            if index==0 then
                call PauseTimer(tim)
            endif
            //Other destroy code here (such as leak cleaning, removing units etc)...
        endmethod
 
    endstruct
 
 
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == ABIL_ID
    endfunction
 
 
    private function Actions takes nothing returns nothing
        call Data.create(GetTriggerUnit()) //make sure you change this based on what data your spell needs
    endfunction
 
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddAction(t, function Actions)
        call TriggerAddCondition(t, Condition(function Conditions))
        set tim = CreateTimer()
        //Other initialisation things here...
    endfunction
 
endscope
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
I am not accusing you of anything, I simply said it uses the same technique and he should read what I posted there as it explains why and what.

It's an indexing system. The best kind of MUI. Of course it uses the same technique. But yeah, I guess your post might help.


I am just getting help from the topic and don't want it to cause any conflicts between anyone.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
I made somethings but its completely mess.


JASS:
scope SpellTemplate initializer Init
    private keyword Data
 
    globals
        private constant real TIMER_INTERVAL = 0.035
        private constant integer ABIL_ID = 'A000' //change this to the rawcode of your spell
        private constant integer UNIT_ID = 'e000' // rawcode of the dummy (Missile) unit
        //Other constant globals here...
        private Data array data
        private integer index = 0
        private timer tim
        // Other changable globals here...
    endglobals
 
 
    private function Execute takes nothing returns nothing
        local integer i = 0
        local Data d
        local location CP // Cast Point
        // Other local variables here...
        loop
            exitwhen i >= index
            set d = data[i]
            set CP = d.CastPoint[i]
            call setUnitPositionLoc(d.missile, CP)
            set i = i + 1
        endloop
        //Any leak cleaning code here...
    endfunction
 
 
    private struct Data
        //Declare struct members here...
        unit caster
        unit array missile[1000]
        integer ID
        location array CastPoint[1000]
 
        static method create takes unit caster returns Data //make sure you change this based on what data you need
            local Data d = Data.allocate()
            //Other create code here...
            set data[index] = d
            set d.ID = index
            set d.CastPoint[index] = GetSpellTargetLoc()
            set d.missile[index] = CreateUnitAtLoc(GetOwningPlayer(d.caster), UNIT_ID, d.missile[index], 0)
            if index == 0 then
                call TimerStart(tim, TIMER_INTERVAL, true, function Execute)
            endif
            set index = index + 1
            return d
        endmethod
 
        method onDestroy takes nothing returns nothing
            set index = index-1
            set data[this.ID] = data[index]
            set data[this.ID].ID = this.ID
            if index==0 then
                call PauseTimer(tim)
            endif
            //Other destroy code here (such as leak cleaning, removing units etc)...
        endmethod
 
    endstruct
 
 
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == ABIL_ID
    endfunction
 
 
    private function Actions takes nothing returns nothing
        call Data.create(GetTriggerUnit()) //make sure you change this based on what data your spell needs
    endfunction
 
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddAction(t, function Actions)
        call TriggerAddCondition(t, Condition(function Conditions))
        set tim = CreateTimer()
        //Other initializations things here...
    endfunction
 
endscope



JASS:
scope SpellTemplate initializer Init
    private keyword Data
 
    globals
        private constant real TIMER_INTERVAL = 0.035
        private constant integer ABIL_ID = 'A000' //change this to the rawcode of your spell
        //Other constant globals here...
        private Data array data
        private integer index = 0
        private timer tim
        // Other changable globals here...
    endglobals
 
 
    private function Execute takes nothing returns nothing
        local integer i = 0
        local Data d
        // Other local variables here...
        loop
            exitwhen i >= index
            set d = data[i]
            set i = i + 1
        endloop
        //Any leak cleaning code here...
    endfunction
 
 
    private struct Data
        //Declare struct members here...
        unit caster
        integer ID
 
        static method create takes unit caster returns Data //make sure you change this based on what data you need
            local Data d = Data.allocate()
            //Other create code here...
            set data[index] = d
            set d.ID = index
            if index == 0 then
                call TimerStart(tim, TIMER_INTERVAL, true, function Execute)
            endif
            set index = index + 1
            return d
        endmethod
 
        method onDestroy takes nothing returns nothing
            set index = index-1
            set data[this.ID] = data[index]
            set data[this.ID].ID = this.ID
            if index==0 then
                call PauseTimer(tim)
            endif
            //Other destroy code here (such as leak cleaning, removing units etc)...
        endmethod
 
    endstruct
 
 
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == ABIL_ID
    endfunction
 
 
    private function Actions takes nothing returns nothing
        call Data.create(GetTriggerUnit()) //make sure you change this based on what data your spell needs
    endfunction
 
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddAction(t, function Actions)
        call TriggerAddCondition(t, Condition(function Conditions))
        set tim = CreateTimer()
        //Other initializations things here...
    endfunction
 
endscope



I got 2 errors; the first one
JASS:
call setUnitPositionLoc(s__SpellTemplate___Data_missile[d] , CP)
Undeclared function

second one
JASS:
set s___SpellTemplate___Data_missile[s__SpellTemplate___Data_missile[d]+SpellTemplate___index]
=CreateUnitAtLoc(GetOwningPlayer(s__SpellTemplate___Data_caster[d]) ,
 SpellTemplate___UNIT_ID , s___SpellTemplate___Data_missile[s__SpellTemplate___Data_missile[d]+SpellTemplate___index] , 0)
Cannot convert Unit to Location

<< ADDED >>

OK, corrected the mistakes and errors but does the variables are created right; I mean there are variables that are of array type are they right to be an array ?


JASS:
scope SpellTemplate initializer Init
    private keyword Data
 
    globals
        private constant real TIMER_INTERVAL = 0.035
        private constant integer ABIL_ID = 'A000' //change this to the rawcode of your spell
        private constant integer UNIT_ID = 'e000' // rawcode of the dummy (Missile) unit
        //Other constant globals here...
        private Data array data
        private integer index = 0
        private timer tim
        // Other changable globals here...
    endglobals
 
 
    private function Execute takes nothing returns nothing
        local integer i = 0
        local Data d
        local location CP // Cast Point
        // Other local variables here...
        loop
            exitwhen i >= index
            set d = data[i]
            set CP = d.CastPoint[i]
            call SetUnitPositionLoc(d.missile[i], CP)
            set i = i + 1
        endloop
        //Any leak cleaning code here...
    endfunction
 
 
    private struct Data
        //Declare struct members here...
        unit caster
        unit array missile[1000]
        integer ID
        location array CastPoint[1000]
 
        static method create takes unit caster returns Data //make sure you change this based on what data you need
            local Data d = Data.allocate()
            //Other create code here...
            set data[index] = d
            set d.ID = index
            set d.CastPoint[index] = GetSpellTargetLoc()
            set d.missile[index] = CreateUnitAtLoc(GetOwningPlayer(d.caster), UNIT_ID, GetUnitLoc(d.caster), 0)
            if index == 0 then
                call TimerStart(tim, TIMER_INTERVAL, true, function Execute)
            endif
            set index = index + 1
            return d
        endmethod
 
        method onDestroy takes nothing returns nothing
            set index = index-1
            set data[this.ID] = data[index]
            set data[this.ID].ID = this.ID
            if index==0 then
                call PauseTimer(tim)
            endif
            //Other destroy code here (such as leak cleaning, removing units etc)...
        endmethod
 
    endstruct
 
 
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == ABIL_ID
    endfunction
 
 
    private function Actions takes nothing returns nothing
        call Data.create(GetTriggerUnit()) //make sure you change this based on what data your spell needs
    endfunction
 
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddAction(t, function Actions)
        call TriggerAddCondition(t, Condition(function Conditions))
        set tim = CreateTimer()
        //Other initializations things here...
    endfunction
 
endscope
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
Why do you make the unit and location struct members arrays??? Just put them in the struct as normal variables. The only thing you need an array if is the actual struct.

Because they will be used outside the struct and if they are not indexed I won't be able to use them.


JASS:
scope SpellTemplate initializer Init
    private keyword Data
 
    globals
        private constant real TIMER_INTERVAL = 0.035
        private constant real DIST = 32 // value of the distance cut every timer interval high value means high speed
        private constant integer ABIL_ID = 'A000' //change this to the rawcode of your spell
        private constant integer UNIT_ID = 'e000' // rawcode of the dummy (Missile) unit
 
        //Other constant globals here...
        private Data array data
        private integer index = 0
        private timer tim
        // Other changable globals here...
    endglobals
 
 
    private function Execute takes nothing returns nothing
        local integer i = 0
        local Data d
        local location array step
        // Other local variables here...
        loop
            exitwhen i >= index
            set d = data[i]
            set step[i] = PolarProjectionBJ(GetUnitLoc(d.missile[i]), DIST, AngleBetweenPoints(GetUnitLoc(d.caster), d.CastPoint[i]))
            call SetUnitPositionLoc(d.missile[i], step[i])
            set i = i + 1
        endloop
        //Any leak cleaning code here...
    endfunction
 
 
    private struct Data
        //Declare struct members here...
        unit caster
        unit array missile[1000]
        integer ID
        location array CastPoint[1000]
 
        static method create takes unit caster returns Data //make sure you change this based on what data you need
            local Data d = Data.allocate()
            //Other create code here...
            set data[index] = d
            set d.ID = index
            set d.CastPoint[index] = GetSpellTargetLoc()
            set d.missile[index] = CreateUnitAtLoc(GetOwningPlayer(d.caster), UNIT_ID, GetUnitLoc(d.caster), 0)
            if index == 0 then
                call TimerStart(tim, TIMER_INTERVAL, true, function Execute)
            endif
            set index = index + 1
            return d
        endmethod
 
        method onDestroy takes nothing returns nothing
            set index = index-1
            set data[this.ID] = data[index]
            set data[this.ID].ID = this.ID
            if index==0 then
                call PauseTimer(tim)
            endif
            //Other destroy code here (such as leak cleaning, removing units etc)...
        endmethod
 
    endstruct
 
 
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == ABIL_ID
    endfunction
 
 
    private function Actions takes nothing returns nothing
        call Data.create(GetTriggerUnit()) //make sure you change this based on what data your spell needs
    endfunction
 
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddAction(t, function Actions)
        call TriggerAddCondition(t, Condition(function Conditions))
        set tim = CreateTimer()
        //Other initializations things here...
    endfunction
 
endscope


A mess, isn't it ? :grin:
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
The struct itself is indexed. Variables inside it therefore are indexed automatically.

OK, got it just use normal variables inside the struct.

--------------------------------------------------

If you read the script in the previous topic I used some BJs because I cant find the natives of the functions I used. Also is the script right I mean calclating the new destination must be inside the loop ?
 
Yes, you must calculate the new destination in the loop, although you can use another function for it if that function is called from within the loop. PolarProjectionBJ is useful if you don't know trigonometry, but quite slow since it has to convert between locations and x/y. Look at how the function does it internally (ctrl-click on it in NewGen)
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
Yes, you must calculate the new destination in the loop, although you can use another function for it if that function is called from within the loop. PolarProjectionBJ is useful if you don't know trigonometry, but quite slow since it has to convert between locations and x/y. Look at how the function does it internally (ctrl-click on it in NewGen)

I am getting confused I can make the location in native function and with the least variables.

JASS:
function PolarProjectionBJ takes location source, real dist, real angle returns location
    local real x = GetLocationX(source) + dist * Cos(angle * bj_DEGTORAD)
    local real y = GetLocationY(source) + dist * Sin(angle * bj_DEGTORAD)
    return Location(x, y)
endfunction

that is a BJ function which uses 2 real; that means that I have to create 2 variables for X and Y coordinates in the struct.

I could use a little help here.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
This isn't working, not even the missile unit created.


JASS:
scope SpellTemplate initializer Init
    private keyword Data
 
    globals
        private constant real TIMER_INTERVAL = 0.035
        private constant real DIST = 32 // value of the distance cut every timer interval high value means high speed
        private constant integer ABIL_ID = 'A000' //change this to the rawcode of your spell
        private constant integer UNIT_ID = 'e000' // rawcode of the dummy (Missile) unit
 
        //Other constant globals here...
        private Data array data
        private integer index = 0
        private timer tim
        // Other changable globals here...
    endglobals
 
 
    private function Execute takes nothing returns nothing
        local integer i = 0
        local Data d
        local location array step
        // Other local variables here...
        loop
            exitwhen i >= index
            set d = data[i]
            set step[i] = PolarProjectionBJ(GetUnitLoc(d.missile), DIST, AngleBetweenPoints(GetUnitLoc(d.caster), d.CastPoint))
            call SetUnitPositionLoc(d.missile, step[i])
            set i = i + 1
        endloop
        //Any leak cleaning code here...
    endfunction
 
 
    private struct Data
        //Declare struct members here...
        unit caster
        unit missile
        integer ID
        location CastPoint
 
        static method create takes unit caster returns Data //make sure you change this based on what data you need
            local Data d = Data.allocate()
            //Other create code here...
            set data[index] = d
            set d.ID = index
            set d.CastPoint = GetSpellTargetLoc()
            set d.missile = CreateUnitAtLoc(GetOwningPlayer(d.caster), UNIT_ID, GetUnitLoc(d.caster), 0)
            if index == 0 then
                call TimerStart(tim, TIMER_INTERVAL, true, function Execute)
            endif
            set index = index + 1
            return d
        endmethod
 
        method onDestroy takes nothing returns nothing
            set index = index-1
            set data[this.ID] = data[index]
            set data[this.ID].ID = this.ID
            if index==0 then
                call PauseTimer(tim)
            endif
            //Other destroy code here (such as leak cleaning, removing units etc)...
        endmethod
 
    endstruct
 
 
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == ABIL_ID
    endfunction
 
 
    private function Actions takes nothing returns nothing
        call Data.create(GetTriggerUnit()) //make sure you change this based on what data your spell needs
    endfunction
 
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CAST)
        call TriggerAddAction(t, function Actions)
        call TriggerAddCondition(t, Condition(function Conditions))
        set tim = CreateTimer()
        //Other initializations things here...
    endfunction
 
endscope

There are no errors in the script but I don't know why it doesn't work ?
 
Last edited:
Level 8
Joined
Aug 4, 2006
Messages
357
You need to learn to Ctrl-click stuff in JNGP. Here's AngleBetweenPoints:
JASS:
function AngleBetweenPoints takes location locA, location locB returns real
    return bj_RADTODEG * Atan2(GetLocationY(locB) - GetLocationY(locA), GetLocationX(locB) - GetLocationX(locA))
endfunction
It's pretty messy looking. Until you get a greater understanding of JASS and trigonometry, I would not worry about replacing AngleBetweenPoints with natives or replacing PolarProjectionBJ with natives.

Ok the main problem with your script is that you never set d.caster to anything. This makes it so you can't create the unit. Here I fixed it for you:

JASS:
scope SpellTemplate initializer Init
    private keyword Data
 
    globals
        private constant real TIMER_INTERVAL = 0.035
        private constant real DIST = 32 // value of the distance cut every timer interval high value means high speed
        private constant integer ABIL_ID = 'A000' //change this to the rawcode of your spell
        private constant integer UNIT_ID = 'e000' // rawcode of the dummy (Missile) unit
 
        //Other constant globals here...
        private Data array data
        private integer index = 0
        private timer tim
        // Other changable globals here...
    endglobals
 
 
    private function Execute takes nothing returns nothing
        local integer i = 0
        local Data d
        local location missilePoint
        local location stepPoint
        // Other local variables here...
        loop
            exitwhen i >= index
            set d = data[i]
            set missilePoint = GetUnitLoc(d.missile)
            if DistanceBetweenPoints(missilePoint, d.CastPoint) < 10 then
                call d.destroy()
            else
                set stepPoint = PolarProjectionBJ(missilePoint, DIST, AngleBetweenPoints(missilePoint, d.CastPoint))
                call SetUnitPositionLoc(d.missile, stepPoint)
                call RemoveLocation(stepPoint)
            endif
            call RemoveLocation(missilePoint)
            set i = i + 1
        endloop
        //Any leak cleaning code here...
    endfunction
 
 
    private struct Data
        //Declare struct members here...
        unit caster
        unit missile
        integer ID
        location CastPoint
 
        static method create takes unit caster, loc target returns Data //make sure you change this based on what data you need
            local Data d = Data.allocate()
            //Other create code here...
            set data[index] = d
            set d.ID = index
            set d.CastPoint = target
            set d.caster = caster
            set d.missile = CreateUnit(GetOwningPlayer(d.caster), UNIT_ID, GetUnitX(d.caster), GetUnitY(d.caster), 0)
            if index == 0 then
                call TimerStart(tim, TIMER_INTERVAL, true, function Execute)
            endif
            set index = index + 1
            return d
        endmethod
 
        method onDestroy takes nothing returns nothing
            set index = index-1
            set data[this.ID] = data[index]
            set data[this.ID].ID = this.ID
            if index==0 then
                call PauseTimer(tim)
            endif
            //Other destroy code here (such as leak cleaning, removing units etc)...
            call KillUnit(this.missile)
        endmethod
 
    endstruct
 
 
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == ABIL_ID
    endfunction
 
 
    private function Actions takes nothing returns nothing
        call Data.create(GetTriggerUnit(), GetSpellTargetLoc()) //make sure you change this based on what data your spell needs
    endfunction
 
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CAST)
        call TriggerAddAction(t, function Actions)
        call TriggerAddCondition(t, Condition(function Conditions))
        set tim = CreateTimer()
        //Other initializations things here...
    endfunction
 
endscope
Here's the changes I made:
1. created a local location variable missilePoint so GetUnitLoc(d.missile) will not leak.
2. changed local location array step -> local location stepPoint. There is no point in having a location array, especially since it is local and will only last till the end of one Execution call. stepPoint finds the next place to move a unit and sets its position to that place. Then stepPoint is destroyed.
3. the data for a missile will now be destroyed once it gets to its destination (within 10 of its destination).
4. set d.caster = caster //sets the caster
5. set d.CastPoint = target //sets the cast point
6. the missile is killed when the data is destroyed.

Edit: fixed the things that EOW mentions in the post below.
 
Last edited:
Level 16
Joined
Mar 3, 2006
Messages
1,564

JASS:
scope SpellTemplate initializer Init
    private keyword Data
 
    globals
        private constant real TIMER_INTERVAL = 0.035
        private constant real DIST = 32 // value of the distance cut every timer interval high value means high speed
        private constant integer ABIL_ID = 'A000' //change this to the rawcode of your spell
        private constant integer UNIT_ID = 'e000' // rawcode of the dummy (Missile) unit
 
        //Other constant globals here...
        private Data array data
        private integer index = 0
        private timer tim
        // Other changable globals here...
    endglobals
 
 
    private function Execute takes nothing returns nothing
        local integer i = 0
        local Data d
        local location missilePoint
        local location stepPoint
        // Other local variables here...
        loop
            exitwhen i >= index
            set d = data[i]
            set missilePoint = GetUnitLoc(d.missile)
            if DistanceBetweenPoints(missilePoint, d.CastPoint) < 10 then
                call d.destroy()
            else
                set stepPoint = PolarProjectionBJ(missilePoint, DIST, AngleBetweenPoints(GetUnitLoc(d.caster), d.CastPoint))
                call SetUnitPositionLoc(d.missile, stepPoint)
                call RemoveLocation(missilePoint)
            endif
            call RemoveLocation(missilePoint)
            set i = i + 1
        endloop
        //Any leak cleaning code here...
    endfunction
 
 
    private struct Data
        //Declare struct members here...
        unit caster
        unit missile
        integer ID
        location CastPoint
 
        static method create takes unit caster, location target returns Data //make sure you change this based on what data you need
            local Data d = Data.allocate()
            //Other create code here...
            set data[index] = d
            set d.ID = index
            set d.CastPoint = target
            set d.caster = caster
            set d.missile = CreateUnit(GetOwningPlayer(d.caster), UNIT_ID, GetUnitX(d.caster), GetUnitY(d.caster), 0)
            if index == 0 then
                call TimerStart(tim, TIMER_INTERVAL, true, function Execute)
            endif
            set index = index + 1
            return d
        endmethod
 
        method onDestroy takes nothing returns nothing
            set index = index-1
            set data[this.ID] = data[index]
            set data[this.ID].ID = this.ID
            if index==0 then
                call PauseTimer(tim)
            endif
            //Other destroy code here (such as leak cleaning, removing units etc)...
            call KillUnit(this.missile)
        endmethod
 
    endstruct
 
 
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == ABIL_ID
    endfunction
 
 
    private function Actions takes nothing returns nothing
        call Data.create(GetTriggerUnit(), GetSpellTargetLoc()) //make sure you change this based on what data your spell needs
    endfunction
 
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CAST)
        call TriggerAddAction(t, function Actions)
        call TriggerAddCondition(t, Condition(function Conditions))
        set tim = CreateTimer()
        //Other initializations things here...
    endfunction
 
endscope


Something wrong with the script; not a syntax error but something causing the missile to go unstopped and it can't be removed and I got a lot of message saying |double free of location in "SpellTemplate__Execute"|
 
Level 8
Joined
Aug 4, 2006
Messages
357
You're removing the wrong location in the else block. This causes you to try to remove the same location twice, hence the error message "double free of location". Copy/paste this in place of your else...endif :
JASS:
            else
                set stepPoint = PolarProjectionBJ(missilePoint, DIST, AngleBetweenPoints(missilePoint, d.CastPoint))
                call SetUnitPositionLoc(d.missile, stepPoint)
                call RemoveLocation(stepPoint)
            endif
I also removed a leak in AngleBetweenPoints(...) by replacing GetUnitLoc(d.missile) with the variable missilePoint. I should have spotted that earlier.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
You're removing the wrong location in the else block. This causes you to try to remove the same location twice, hence the error message "double free of location". Copy/paste this in place of your else...endif :
JASS:
            else
                set stepPoint = PolarProjectionBJ(missilePoint, DIST, AngleBetweenPoints(missilePoint, d.CastPoint))
                call SetUnitPositionLoc(d.missile, stepPoint)
                call RemoveLocation(stepPoint)
            endif
I also removed a leak in AngleBetweenPoints(...) by replacing GetUnitLoc(d.missile) with the variable missilePoint. I should have spotted that earlier.

It worked well but with a small minor bug: At some angles the missile unit is duplicated and not removed; this happened mostly when I cast the spell near the caster.
 
Level 8
Joined
Aug 4, 2006
Messages
357
Try increasing the destroy distance to 25 instead of 10.

I don't think this is related to the bug, but you should do it anyway. Make sure the dummy has the Locust ability, and try adding this: call SetUnitPosition(d.missile, GetUnitX(d.caster), GetUnitY(d.caster)) after set d.missile = CreateUnit(...). When wc3 creates one unit on top of another, it will automatically displace the created unit even if the created unit has locust. SetUnitPosition will make sure the missile starts exactly on top of the caster and won't displace the missile since it checks for locust.
 
Level 8
Joined
Aug 4, 2006
Messages
357
What would happen if we did Data.create() inside the create() method? Well, you'd actually get a compile error. Suppose the error did not appear, though. What would happen if we created a new Data object? The create method would keep calling itself until your wc3 crashes! Thus, this is not a good idea.

We use Data.allocate() to simply get a new Data object.
We use Data.create(...) to get a new Data object (using allocate) and initialize its properties.

You are very welcome :D

P.S. I am going to sleep. It's 2:10 am over here... I'll let someone else help you.
 
Status
Not open for further replies.
Top