• 🏆 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!

[JASS] My First Jass Spell

Status
Not open for further replies.
Level 10
Joined
Sep 3, 2009
Messages
458
Well This is My first Jass Spell. Can anyone comment on how I did? It's Pretty Simple.

Ice Nova

Creates nova of ice at a target location damaging enemy units.

Code:

JASS:
function Trig_Explode_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A00C' 
endfunction

function Trig_Explode_Actions takes nothing returns nothing
//================================================
//Variable Setup
//================================================
local unit u = GetTriggerUnit()
local location Temploc = GetSpellTargetLoc()
local real x = GetLocationX( Temploc )
local real y = GetLocationY( Temploc )
local group enemies = CreateGroup()
local unit target
local effect nova = AddSpecialEffect( "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl", x, y )
call DestroyEffect( nova )
call GroupEnumUnitsInRangeOfLoc(enemies, Temploc, 300.0, null)

loop
  set target = FirstOfGroup( enemies )
  exitwhen target == null
  if IsUnitEnemy( target, GetOwningPlayer(u)) then
     call UnitDamageTarget( u, target, 250, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
  endif
  call RemoveLocation( Temploc )
  call GroupRemoveUnit(enemies, target)
endloop


endfunction

//===========================================================================
function InitTrig_Explode takes nothing returns nothing
    set gg_trg_Explode = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Explode, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Explode, Condition( function Trig_Explode_Conditions ) )
    call TriggerAddAction( gg_trg_Explode, function Trig_Explode_Actions )
endfunction
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Okay the first thing I notice is that you're not nulling your local handle variables.

JASS:
local unit u
local location Temploc
local group enemies
local unit target
local effect nova

These all have to be set to null at the end of the function. You can save a couple of steps, however, instead of declaring the effect variable you can pass AddSpecialEffect directly into the DestroyEffect function. This will also save you from having to null the variable later.

You can eliminate the location handle as well, by simply using real coordinates. I see you already have them declared, but let me modify their definition slightly:

JASS:
local real x = GetSpellTargetX()
local real y = GetSpellTargetY()

Now you don't need to gather the coordinates of the location, you can reference them directly through the event response parameters. This will also allow you to remove the RemoveLocation[/lass] (by the way you don't need to remove the location each iteration of the loop, it should be outside; and since you don't reference it anywhere within the loop you could even remove it before-hand). You will have to modify your call to [icode=jass]GroupEnumUnitsInRangeOfLoc but there is an easier method anyways:

JASS:
call GroupEnumUnitsInRange(enemies, x, y, 300.0, null)

If you know programming languages then you will probably want to move on to vJass which is a far more dynamic approach to JASS coding. I only have a link to JassHelper in my signature (you can find Jass NewGen Pack on the same site) but if you want you can also go to Google.com and search "wc3c.net JNGP" and I think the very first link is the right one.

Eh, I had to double-check to see which link it was so here I just copy and pasted:

http://www.wc3c.net/showthread.php?t=90999
 
Level 10
Joined
Sep 3, 2009
Messages
458
Okay the first thing I notice is that you're not nulling your local handle variables.

JASS:
local unit u
local location Temploc
local group enemies
local unit target
local effect nova

These all have to be set to null at the end of the function. You can save a couple of steps, however, instead of declaring the effect variable you can pass AddSpecialEffect directly into the DestroyEffect function. This will also save you from having to null the variable later.

You can eliminate the location handle as well, by simply using real coordinates. I see you already have them declared, but let me modify their definition slightly:

JASS:
local real x = GetSpellTargetX()
local real y = GetSpellTargetY()

Now you don't need to gather the coordinates of the location, you can reference them directly through the event response parameters. This will also allow you to remove the RemoveLocation[/lass] (by the way you don't need to remove the location each iteration of the loop, it should be outside; and since you don't reference it anywhere within the loop you could even remove it before-hand). You will have to modify your call to [icode=jass]GroupEnumUnitsInRangeOfLoc but there is an easier method anyways:

JASS:
call GroupEnumUnitsInRange(enemies, x, y, 300.0, null)

If you know programming languages then you will probably want to move on to vJass which is a far more dynamic approach to JASS coding. I only have a link to JassHelper in my signature (you can find Jass NewGen Pack on the same site) but if you want you can also go to Google.com and search "wc3c.net JNGP" and I think the very first link is the right one.

Eh, I had to double-check to see which link it was so here I just copy and pasted:

http://www.wc3c.net/showthread.php?t=90999

Wow thanks for the trouble dude highly appreciate it

OK!

1. I need to null my variables
2. How do I pass the special effect thingy
3. So I can just set
JASS:
local real x = GetSpellTargetX()
whitout putting the location parameter? And also do Is till need to use the location var Temploc?
4.I dont understand the "reference them directly" part can you explain a bit more.
5. Yeah I know a bit about C++

Thanks in Advance!
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
1. I need to null my variables

Yes.

2. How do I pass the special effect thingy

JASS:
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl", x, y ))

3. So I can just set
Jass:
local real x = GetSpellTargetX()
whitout putting the location parameter? And also do Is till need to use the location var Temploc?

That's correct. See on the site how it's highlighted in purple? It is a valid native that was added in one of the more recent patches.

4.I dont understand the "reference them directly" part can you explain a bit more.

I mean you have access to GetSpellTargetX/GetSpellTargetY - you don't need to define a location as GetSpellTargetLoc() in order to pull the coordinates out; this completely saves you from having to declare the location (it used to be necessary that you draw the coordinates from the location).

5. Yeah I know a bit about C++

If you want to check the link I provided there is more information on in there; it expands on the JASS syntax by implementing parallel arrays to associate data together (as a class would be associated data in C++). The JassHelper link in my signature directs to the JassHelper manual (JassHelper is a parser that reads vJass into JASS format).
 
Level 10
Joined
Sep 3, 2009
Messages
458
JASS:
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl", x, y ))

This is pretty neat


That's correct. See on the site how it's highlighted in purple? It is a valid native that was added in one of the more recent patches.
cool didn't see this is the function list

I mean you have access to GetSpellTargetX/GetSpellTargetY - you don't need to define a location as GetSpellTargetLoc() in order to pull the coordinates out; this completely saves you from having to declare the location (it used to be necessary that you draw the coordinates from the location).

This is pretty neat too. Didn't see this on the functions list. And yeah I'm using NewGen but this is the first time I made something in Jass



If you want to check the link I provided there is more information on in there; it expands on the JASS syntax by implementing parallel arrays to associate data together (as a class would be associated data in C++). The JassHelper link in my signature directs to the JassHelper manual (JassHelper is a parser that reads vJass into JASS format)

Uhm I'm confused on vJass. I know that it is like a better way of coding but it will be translated to Jass. So does that mean I need to have a program to write vJass I'm confused... truly
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
No, if you're using the Jass NewGen Pack then it already uses the JassHelper compiler, however you may need to update it. The process here is relatively simple, just download the updated files and copy them to the folder where those files are already (I believe the download is very straight forward, you should be able to figure it out).

It allows you to not only organize your code way better using libraries and scopes, but the modularity that is granted is the real selling point; you can declare your own initializers in structs (which are like classes), scopes, and libraries - if you are experienced with organizing code it makes a very big difference.

Another bonus is the ability to declare global variables (and constants) - this proves useful when you want to be able to easily update your code. Let's take your above code for example, you could instead define the string path as a constant:

JASS:
globals
    constant string ICE_NOVA_EFFECT_PATH = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
endglobals

Now your code can consist of variables, leaving the hard-coded values in a separate location where they can easily be navigated and modified without danger.

JASS:
call DestroyEffect(AddSpecialEffect(ICE_NOVA_EFFECT_PATH, x, y))

This keeps your code tidy, and if you ever happen to use this constant more than once in your code it will save you the trouble of having to track down each instance of it when you can simply change the configured value declared in the globals block.

Think of a global variable as a static variable. If you start learning structs you'll understand this syntax even more-so as they are used in struct syntax:

JASS:
struct DataClass
    public unit u = null
    public real data

    static method create takes unit u, real data returns thistype
        local thistype d = allocate()
        set d.u = u
        set d.data = data
        return d
    endmethod
endstruct

Completely purposeless example, but I suggest you look into vJass if this interests you.
 
Level 10
Joined
Sep 3, 2009
Messages
458
No, if you're using the Jass NewGen Pack then it already uses the JassHelper compiler, however you may need to update it. The process here is relatively simple, just download the updated files and copy them to the folder where those files are already (I believe the download is very straight forward, you should be able to figure it out).

It allows you to not only organize your code way better using libraries and scopes, but the modularity that is granted is the real selling point; you can declare your own initializers in structs (which are like classes), scopes, and libraries - if you are experienced with organizing code it makes a very big difference.

Another bonus is the ability to declare global variables (and constants) - this proves useful when you want to be able to easily update your code. Let's take your above code for example, you could instead define the string path as a constant:

JASS:
globals
    constant string ICE_NOVA_EFFECT_PATH = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
endglobals

Now your code can consist of variables, leaving the hard-coded values in a separate location where they can easily be navigated and modified without danger.

JASS:
call DestroyEffect(AddSpecialEffect(ICE_NOVA_EFFECT_PATH, x, y))

This keeps your code tidy, and if you ever happen to use this constant more than once in your code it will save you the trouble of having to track down each instance of it when you can simply change the configured value declared in the globals block.

Think of a global variable as a static variable. If you start learning structs you'll understand this syntax even more-so as they are used in struct syntax:

JASS:
struct DataClass
    public unit u = null
    public real data

    static method create takes unit u, real data returns thistype
        local thistype d = allocate()
        set d.u = u
        set d.data = data
        return d
    endmethod
endstruct

Completely purposeless example, but I suggest you look into vJass if this interests you.

wow cool. So I guess i need to start doing vJass now eh? Thanks for the help dude! +rep!
 
Level 10
Joined
Sep 3, 2009
Messages
458
not necessarily now, but you might want to learn it as soon as possible, it works great... ^_^

anyway this nova looks cool, though the function names are still GUIsh... ^_^ ... :goblin_good_job:

wew thanks. hey i forgot to post the updated code here it is.

JASS:
function Trig_Ice_Nova_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A00C' 
endfunction

function Trig_Ice_Nova_Actions takes nothing returns nothing
//================================================
//Variable Setup
//================================================
local unit u = GetTriggerUnit()
local real x = GetSpellTargetX()
local real y = GetSpellTargetY()
local group enemies = CreateGroup()
local unit target 
call DestroyEffect( AddSpecialEffect( "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl", x, y ) )
call GroupEnumUnitsInRange(enemies, x, y, 200.0, null)

loop
  set target = FirstOfGroup( enemies )
  exitwhen target == null
  if IsUnitEnemy( target, GetOwningPlayer(u)) then
     call UnitDamageTarget( u, target, 150, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
  endif
  call GroupRemoveUnit(enemies, target)
endloop
//================================================
//Null Variables
//================================================
set u = null
set enemies = null
set target = null 


endfunction

//===========================================================================
function InitTrig_Ice_Nova takes nothing returns nothing
    set gg_trg_Ice_Nova = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Ice_Nova, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Ice_Nova, Condition( function Trig_Ice_Nova_Conditions ) )
    call TriggerAddAction( gg_trg_Ice_Nova, function Trig_Ice_Nova_Actions )
endfunction

I guess for now I'll be going on the grueling task of converting all my spells in Jass XD.

And GUIsh? what do you mean?
 
one more leak, the unit group, destroy it after the loop...

if you use vJASS, you can avoid creating/destroying it with each run coz you can just use a global group

JASS:
globals
      private group TEMP_GROUP = CreateGroup()
      //then just use this on the groupenums, no need for the local group anymore
endglobals

with GUIsh I mean this, its just a matter of preference though...
JASS:
function Trig_Ice_Nova_Actions

//you can change it
example
function NovaAct...
 
Level 10
Joined
Sep 3, 2009
Messages
458
one more leak, the unit group, destroy it after the loop...

wew ima fix that

if you use vJASS, you can avoid creating/destroying it with each run coz you can just use a global group

JASS:
globals
      private group TEMP_GROUP = CreateGroup()
      //then just use this on the groupenums, no need for the local group anymore
endglobals

wow that's pretty neat so that does that mean that I can just use the group whenever I want without removing it? Also wuts the private thingy there?


with GUIsh I mean this, its just a matter of preference though...
JASS:
function Trig_Ice_Nova_Actions

//you can change it
example
function NovaAct...

wait so you mean you can change those?
 
Yes just make sure you dont destroy the global group... and just use it for GroupEnums... ^_^...

about private, its a keyword that makes the variable private, meaning you can only use it on the scope/library that you made it (at least easily coz you still can use it on other triggers, because private only adds the library/scope name to the name of the variable)... using private, you can have multiple variables of the same name in different scopes/libraries...



Yes you can change them, except the Init_Trig unless you use vJASS (scopes or libraries)... but if you change them be sure to set them at the init trig... like this

example:
JASS:
//if you change the actions name into this
function Nova_Actions takes nothing returns nothing

//be sure to edit this
call TriggerAddAction( gg_trg_Ice_Nova, function Trig_Ice_Nova_Actions )

//into this

call TriggerAddAction( gg_trg_Ice_Nova, function Nova_Actions )
 
Level 10
Joined
Sep 3, 2009
Messages
458
This should be better eh?

JASS:
function Ice_Nova_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A00C' 
endfunction

function Ice_Nova_Actions takes nothing returns nothing
//================================================
//Variable Setup
//================================================
local unit u = GetTriggerUnit()
local real x = GetSpellTargetX()
local real y = GetSpellTargetY()
local group enemies = CreateGroup()
local unit target 
call DestroyEffect( AddSpecialEffect( "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl", x, y ) )
call GroupEnumUnitsInRange(enemies, x, y, 200.0, null)

loop
  set target = FirstOfGroup( enemies )
  exitwhen target == null
  if IsUnitEnemy( target, GetOwningPlayer(u)) then
     call UnitDamageTarget( u, target, 150, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
  endif
  call GroupRemoveUnit(enemies, target)
endloop
call DestroyGroup (enemies)
//================================================
//Null Variables
//================================================
set u = null
set enemies = null
set target = null 


endfunction

//===========================================================================
function InitTrig_Ice_Nova takes nothing returns nothing
    set gg_trg_Ice_Nova = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Ice_Nova, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Ice_Nova, Condition( function Ice_Nova_Conditions ) )
    call TriggerAddAction( gg_trg_Ice_Nova, function Ice_Nova_Actions )
endfunction
 
I told you, its just preference... anyway its easier to read those function names now... ^_^

heres and example of using privates...


JASS:
library LIBA

     globals
            integer i = 0
     endglobals
endlibrary

JASS:
library LIBB

     globals
            integer i = 0
     endglobals
endlibrary

now this wont compile, because you have 2 global variables with the same name





JASS:
library LIBA

     globals
            private integer i = 0
     endglobals
endlibrary

JASS:
library LIBB

     globals
            private integer i = 0
     endglobals
endlibrary

now this will compile even if the 2 variables are named i, because they are both private
 
Level 10
Joined
Sep 3, 2009
Messages
458
I told you, its just preference... anyway its easier to read those function names now... ^_^

heres and example of using privates...


JASS:
library LIBA

     globals
            integer i = 0
     endglobals
endlibrary

JASS:
library LIBB

     globals
            integer i = 0
     endglobals
endlibrary

now this wont compile, because you have 2 global variables with the same name





JASS:
library LIBA

     globals
            private integer i = 0
     endglobals
endlibrary

JASS:
library LIBB

     globals
            private integer i = 0
     endglobals
endlibrary

now this will compile even if the 2 variables are named i, because they are both private

Wow awesome. I think I'll be checking vjass out later, do you know any tuts?

Hey I have a question on the trigger editor, the first icon on the left (the one like a map icon where it has the name of the map) I can do jass there right? cause there's a field where i can put it
 
yup, but I dont use it, if you use newgen, you can just use libraries instead of writing jass functions there...

I wont also prefer to use that place, coz its easier to manage libraries... ^_^

for vJASS tuts, I've read the vJASS OOP tutorial in the tut section, though that one is specifically for structs...\

Here's a link to Vexorian's vJASS manual

vJASS manual
 
Level 10
Joined
Sep 3, 2009
Messages
458
yup, but I dont use it, if you use newgen, you can just use libraries instead of writing jass functions there...

I wont also prefer to use that place, coz its easier to manage libraries... ^_^

for vJASS tuts, I've read the vJASS OOP tutorial in the tut section, though that one is specifically for structs...

lol fair enough XD

vJass OOP? ima look at that thanks for yer help too man! +rep
 
Note: I'll comment the lines so you can understand easily
-This is not an efficient system (I like to do it with struct but that might be a bit advanced for you)
-Needs Jass helper because of the globals... sorry...
-this one does not take into consideration map bounds

JASS:
//vJASS codes need to be inside a scope or a library
scope Dash initializer init
//initializer is a function that is run on map init
//scopes need an initializer, for libraries, that is optional
    globals
         //8190 is the number of members initialized
         //To save the unit to dash
         private unit array CASTER [8190]
         //a timer for a periodic action
         private timer DASH_TIMER = CreateTimer()
         //timer interval         
         private constant real TICK = .03
         //the max distance of the dash
         //since this is an example, it wont support multi levels
         private constant real MAX_DISTANCE = 600.00
         //distance moved per TICK
         private real DIST_PT = 5.00
         //to keep track of how far the unit already moved
         private real array DIST [8190]
         //For indexing
         private integer INDEX = 0
         //distance to be travelled by each unit depending on angle
         private real array DX [8190]
         private real array DY [8190]
         //constant means the value cannot be changed in-game
         private constant integer DASH_ID = 'A000'
        //the raw code of the Dash spell
    endglobals
    
    private function DashCheck takes nothing returns boolean
        //this checks whether the spell caster is dash
        return GetSpellAbilityId() == DASH_ID
    endfunction
    
    private function DashLoop takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i > INDEX
            //if unit havent travelled max distance yet, we make him dash
            if DIST[i] < MAX_DISTANCE then
                //moves the unit
                call SetUnitPosition(CASTER[i], GetUnitX(CASTER[i]) + DX[i], GetUnitY(CASTER[i]) + DY[i])
                set DIST[i] = DIST[i] + DIST_PT
            else
                //To recycle indexes
                set DIST[i] = DIST[INDEX]
                set CASTER[i] = CASTER[INDEX]
                set INDEX = INDEX - 1
                set i = i - 1
                if INDEX == 0 then
                    //if no instances is running, we pause timer
                    call PauseTimer(DASH_TIMER)
                endif
            endif
            set i = i + 1
        endloop

    endfunction
    
    private function DashAct takes nothing returns nothing
        //This gets the angle between the target point and the caster coz GetUnitFacing is not accurate
        //and only returns some angles
        local real angle = Atan2(GetSpellTargetY() - GetUnitY(GetTriggerUnit()), GetSpellTargetX() - GetUnitX(GetTriggerUnit()))
        //here we increase index
        set INDEX = INDEX + 1
        //Sets the data needed for the dash
        set CASTER[INDEX] = GetTriggerUnit()
        set DIST[INDEX] = 0.00
        //This calculates the distance to be travelled (the change in X,Y position)
        //based on the angle of the dash
        set DX[INDEX] = DIST_PT*Cos(angle)
        set DY[INDEX] = DIST_PT*Sin(angle)
        //turns on the periodic loop if this is the only instance
        if INDEX == 1 then
            call TimerStart(DASH_TIMER, TICK, true, function DashLoop)
        endif
    endfunction 
    
    private function init takes nothing returns nothing
        //we set up the trigger here
        local trigger t = CreateTrigger()
        local integer i = 0
        loop
           exitwhen i > 15
           call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
           set i = i + 1
        endloop
        call TriggerAddAction(t, function DashAct)
        call TriggerAddCondition(t, Condition(function DashCheck))
     endfunction
endscope
 
Level 10
Joined
Sep 3, 2009
Messages
458
Note: I'll comment the lines so you can understand easily
-This is not an efficient system (I like to do it with struct but that might be a bit advanced for you)
-Needs Jass helper because of the globals... sorry...
-this one does not take into consideration map bounds

JASS:
//vJASS codes need to be inside a scope or a library
scope Dash initializer init
//initializer is a function that is run on map init
//scopes need an initializer, for libraries, that is optional
    globals
         //8190 is the number of members initialized
         //To save the unit to dash
         private unit array CASTER [8190]
         //a timer for a periodic action
         private timer DASH_TIMER = CreateTimer()
         //timer interval         
         private constant real TICK = .03
         //the max distance of the dash
         //since this is an example, it wont support multi levels
         private constant real MAX_DISTANCE = 600.00
         //distance moved per TICK
         private real DIST_PT = 5.00
         //to keep track of how far the unit already moved
         private real array DIST [8190]
         //For indexing
         private integer INDEX = 0
         //distance to be travelled by each unit depending on angle
         private real array DX [8190]
         private real array DY [8190]
         //constant means the value cannot be changed in-game
         private constant integer DASH_ID = 'A000'
        //the raw code of the Dash spell
    endglobals
    
    private function DashCheck takes nothing returns boolean
        //this checks whether the spell caster is dash
        return GetSpellAbilityId() == DASH_ID
    endfunction
    
    private function DashLoop takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i > INDEX
            //if unit havent travelled max distance yet, we make him dash
            if DIST[i] < MAX_DISTANCE then
                //moves the unit
                call SetUnitPosition(CASTER[i], GetUnitX(CASTER[i]) + DX[i], GetUnitY(CASTER[i]) + DY[i])
                set DIST[i] = DIST[i] + DIST_PT
            else
                //To recycle indexes
                set DIST[i] = DIST[INDEX]
                set CASTER[i] = CASTER[INDEX]
                set INDEX = INDEX - 1
                set i = i - 1
                if INDEX == 0 then
                    //if no instances is running, we pause timer
                    call PauseTimer(DASH_TIMER)
                endif
            endif
            set i = i + 1
        endloop

    endfunction
    
    private function DashAct takes nothing returns nothing
        //This gets the angle between the target point and the caster coz GetUnitFacing is not accurate
        //and only returns some angles
        local real angle = Atan2(GetSpellTargetY() - GetUnitY(GetTriggerUnit()), GetSpellTargetX() - GetUnitX(GetTriggerUnit()))
        //here we increase index
        set INDEX = INDEX + 1
        //Sets the data needed for the dash
        set CASTER[INDEX] = GetTriggerUnit()
        set DIST[INDEX] = 0.00
        //This calculates the distance to be travelled (the change in X,Y position)
        //based on the angle of the dash
        set DX[INDEX] = DIST_PT*Cos(angle)
        set DY[INDEX] = DIST_PT*Sin(angle)
        //turns on the periodic loop if this is the only instance
        if INDEX == 1 then
            call TimerStart(DASH_TIMER, TICK, true, function DashLoop)
        endif
    endfunction 
    
    private function init takes nothing returns nothing
        //we set up the trigger here
        local trigger t = CreateTrigger()
        local integer i = 0
        loop
           exitwhen i > 15
           call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
           set i = i + 1
        endloop
        call TriggerAddAction(t, function DashAct)
        call TriggerAddCondition(t, Condition(function DashCheck))
     endfunction
endscope

wow this is pretty complicated for a dash O. O

although I know a bit of how to move a unit now, the indexing here is pretty confusing
 
^_^

just take your time and learn... the indexing there is a recyling index... and that dash is MUI...

though its not that efficient yet, there are still a lot of things to add there, like checking if the unit will go beyond the map bounds, etc...

it might really look complicated right now, but once you understand the concepts, you will see that its pretty easy... ^_^

EDIT: oh, on the DashLoop, local integer i = 1
 
Level 10
Joined
Sep 3, 2009
Messages
458
Dude here's mah Ice Nova with globals

JASS:
//================================================
//Globals
//================================================
globals
  constant string ICE_NOVA_SFX = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
  constant group ICE_NOVA_GROUP = CreateGroup() 
endglobals
//================================================  
function Ice_Nova_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A00C' 
endfunction

function Ice_Nova_Actions takes nothing returns nothing
//================================================
//Variable Setup
//================================================
local unit u = GetTriggerUnit()
local real x = GetSpellTargetX()
local real y = GetSpellTargetY()
local unit target 
call DestroyEffect( AddSpecialEffect( ICE_NOVA_SFX, x, y ) )
call GroupEnumUnitsInRange(ICE_NOVA_GROUP, x, y, 200.0, null)

loop
  set target = FirstOfGroup( ICE_NOVA_GROUP )
  exitwhen target == null
  if IsUnitEnemy( target, GetOwningPlayer(u)) then
     call UnitDamageTarget( u, target, 150, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
  endif
  call GroupRemoveUnit(ICE_NOVA_GROUP, target)
endloop
//================================================
//Null Variables
//================================================
set u = null
set target = null 


endfunction

//===========================================================================
function InitTrig_Ice_Nova_V2 takes nothing returns nothing
    set gg_trg_Ice_Nova_V2 = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Ice_Nova_V2, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Ice_Nova_V2, Condition( function Ice_Nova_Conditions ) )
    call TriggerAddAction( gg_trg_Ice_Nova_V2, function Ice_Nova_Actions )
endfunction

do I still need to destroy the group?
 
nope, it would bug if you destroy the group... ^_^

anyway I suggest making them private... and dont put constant on the group, it may cause bugs...

only use constant in constant reals, integers, strings,

you can also make all the other locals into globals, though not much difference in performance I think... ^_^

JASS:
globals
   private unit target = null
endglobals
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
I laughed when I saw the constant group haha. Sorry I went to sleep last night after my last post here but I can see Adiktuz helped you out.

Anyways, another way to approach this Ice Nova spell would be to use structs. The effects of the spell could be encapsulated into a struct - this would allow you to more dynamically be able to use this spell. Let's say you want to have another spell on the same hero that uses Ice Nova's effect when he is attacked 10% of the time; in this case you would want to be able to create the Ice Nova separately from the Ice Nova spell.

This is how I would have laid out the spell in vJass, however the spell really is too simple for this to save much trouble at all. :smile:

JASS:
library IceNovaSpell initializer init
    globals
        constant real       ICE_NOVA_AREA       = 300
        constant real       ICE_NOVA_DAMAGE     = 250
    
        constant integer    ICE_NOVA_ABIL_ID    = 'A00C'
        constant string     ICE_NOVA_FX_PATH    = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
    endglobals

    struct IceNova
        private unit caster = null
        private integer level
    
        private static group enumGroup = CreateGroup()
        private static thistype temp
        
        private static method enumGroupFilter takes nothing returns boolean
            if IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(temp.caster)) then
                call UnitDamageTarget(temp.caster, GetFilterUnit(), ICE_NOVA_DAMAGE, false, false, /*
                    */ ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
            endif
            return false
        endmethod
        static method create takes unit caster, real x, real y, integer level returns thistype
            local thistype nova = allocate()
            
            set nova.caster = caster
            set nova.level  = level
            
            set temp = nova
            call DestroyEffect(AddSpecialEffect(ICE_NOVA_FX_PATH, x, y))
            call GroupEnumUnitsInRange(enumGroup, x, y, ICE_NOVA_AREA, Filter(function thistype.enumGroupFilter))
            
            call nova.destroy() 
            return 0
        endmethod
    endstruct
    
    //==============================================================================================================
    private function cond takes nothing returns boolean
        return GetSpellAbilityId() == ICE_NOVA_ABIL_ID
    endfunction
    private function actn takes nothing returns nothing
    
        call IceNova.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY(), 0)
        
    endfunction
    public function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Filter(function cond))
        call TriggerAddAction(t, function actn)
    endfunction
endlibrary

It's all about interface; if this is how you have your spell setup then it becomes extremely simple to actually create the nova (as you can see in the actn function near the bottom). You could probably move all of the code directly into the IceNova struct, which would really organize things to where you want them when it comes time to re-use the code. It's just preference though.
 
Level 10
Joined
Sep 3, 2009
Messages
458
I laughed when I saw the constant group haha. Sorry I went to sleep last night after my last post here but I can see Adiktuz helped you out.

>.> <,< :D

Anyways, another way to approach this Ice Nova spell would be to use structs. The effects of the spell could be encapsulated into a struct - this would allow you to more dynamically be able to use this spell. Let's say you want to have another spell on the same hero that uses Ice Nova's effect when he is attacked 10% of the time; in this case you would want to be able to create the Ice Nova separately from the Ice Nova spell.

This is how I would have laid out the spell in vJass, however the spell really is too simple for this to save much trouble at all. :smile:

JASS:
library IceNovaSpell initializer init
    globals
        constant real       ICE_NOVA_AREA       = 300
        constant real       ICE_NOVA_DAMAGE     = 250
    
        constant integer    ICE_NOVA_ABIL_ID    = 'A00C'
        constant string     ICE_NOVA_FX_PATH    = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
    endglobals

    struct IceNova
        private unit caster = null
        private integer level
    
        private static group enumGroup = CreateGroup()
        private static thistype temp
        
        private static method enumGroupFilter takes nothing returns boolean
            if IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(temp.caster)) then
                call UnitDamageTarget(temp.caster, GetFilterUnit(), ICE_NOVA_DAMAGE, false, false, /*
                    */ ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
            endif
            return false
        endmethod
        static method create takes unit caster, real x, real y, integer level returns thistype
            local thistype nova = allocate()
            
            set nova.caster = caster
            set nova.level  = level
            
            set temp = nova
            call DestroyEffect(AddSpecialEffect(ICE_NOVA_FX_PATH, x, y))
            call GroupEnumUnitsInRange(enumGroup, x, y, ICE_NOVA_AREA, Filter(function thistype.enumGroupFilter))
            
            call nova.destroy() 
            return 0
        endmethod
    endstruct
    
    //==============================================================================================================
    private function cond takes nothing returns boolean
        return GetSpellAbilityId() == ICE_NOVA_ABIL_ID
    endfunction
    private function actn takes nothing returns nothing
    
        call IceNova.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY(), 0)
        
    endfunction
    public function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Filter(function cond))
        call TriggerAddAction(t, function actn)
    endfunction
endlibrary

It's all about interface; if this is how you have your spell setup then it becomes extremely simple to actually create the nova (as you can see in the actn function near the bottom). You could probably move all of the code directly into the IceNova struct, which would really organize things to where you want them when it comes time to re-use the code. It's just preference though.

Well I think I can understand some things now. But I have a few questions

1. Is there a difference between a private put in a globals block and a private that is declared anywhere else?

2. What's a private static? whats a method?

3. What does GetFilterUnit() do?

4. Why are there like for example, "nova.something". Wuts the "." for. Ehm confused
 
a private in a globals block is a global

in Berb's code, he made the other variables into struct members...

static means that variable will be shared by the whole struct, if there is no static there, the variable will be created for each struct instance... (structs is like a huge collection of arrays...)

example:

JASS:
globals
       group array G
endglobals

this is the same as

struct A
       group G
endstruct

while:


globals
       group G
endglobals

is the same as

struct B
       static group G
endstruct


methods are functions but must be declared inside structs... and they can be static or instanced...

GetFilterUnit() returns the unit currently being checked in the condition...

call GroupEnumUnitsInRange(enumGroup, x, y, ICE_NOVA_AREA, Filter(function thistype.enumGroupFilter))

meaning the units that will be checked if they will be included in the group above...

"nova.something
-> the . is used to get a member of the nova struct... example: to get the caster unit -> nova.caster

@Berb - cant this line

local thistype nova = allocate()

be just set into

set temp = allocate()?
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Perhaps I posted a little too much information; what I did was make a really simple struct with only two members and one static member (global). The entire script is within a library which allows it to be ordered amongst other libraries. The idea of the private keyword is (as far as I know) the exact same as in C++; anything declared as private cannot be referenced outside the containing scope.

A scope refers to:
  • Libraries (library)
  • Scopes (scope)
  • Interfaces (interface)
  • Structs (struct)
  • Modules (module)

That's all I can think of at the moment, you definitely don't need to know how to use all of these right away - it is a gradual process.

There are only two scope modifiers that you need to concern yourself with, those being private (which doesn't allow outside reference) and public. The other option here is that you do not explicitly define a modifier, which will declare the function/variable/etc. as it is defined. I'll give an example:

JASS:
scope SomeScope
    // This function will be referenced as "SomeScope_SomeFunction" when referenced outside of the scope.
    // Inside the scope it should be referenced as "SomeFunction".
    public function SomeFunction takes nothing returns nothing
    endfunction

    // This function will be referenced as "SomeOtherFunction" when referenced outside of the scope.
    // Inside the scope it will also be referenced as "SomeOtherFunction".
    function SomeOtherFunction takes nothing returns nothing
    endfunction

    // This function can only be referenced inside the scope, and it will be referenced as "YourFunction".
    private function YourFunction takes nothing returns nothing
    endfunction
endscope

If there is a function (no modifier) defined somewhere in the code that matches the name of a public function and you are trying to reference that function from within the public scope then the public function will be referenced, rather than the static function.

I'll give you an example:

JASS:
scope OtherScope
    function YourFunction takes nothing returns nothing
        call BJDebugMsg("function YourFunction is executed.")
    endfunction
endscope
scope SomeScope initializer YourOtherFunction
    public function YourFunction takes nothing returns nothing
        call BJDebugMsg("function SomeScope_YourFunction is executed.")
    endfunction
    public function YourOtherFunction takes nothing returns nothing
        call YourFunction()
    endfunction
endscope

The line call YourFunction() will execute the method above it, or in other words "function SomeScope_YourFunction" will be displayed on the screen at initialization.

@Berb - cant this line

local thistype nova = allocate()

be just set into

set temp = allocate()?

Yea. It can.
 
Level 10
Joined
Sep 3, 2009
Messages
458
a private in a globals block is a global

in Berb's code, he made the other variables into struct members...

static means that variable will be shared by the whole struct, if there is no static there, the variable will be created for each struct instance... (structs is like a huge collection of arrays...)

example:

JASS:
globals
       group array G
endglobals

this is the same as

struct A
       group G
endstruct

while:


globals
       group G
endglobals

is the same as

struct B
       static group G
endstruct


methods are functions but must be declared inside structs... and they can be static or instanced...

GetFilterUnit() returns the unit currently being checked in the condition...

call GroupEnumUnitsInRange(enumGroup, x, y, ICE_NOVA_AREA, Filter(function thistype.enumGroupFilter))

meaning the units that will be checked if they will be included in the group above...

"nova.something
-> the . is used to get a member of the nova struct... example: to get the caster unit -> nova.caster

@Berb - cant this line

local thistype nova = allocate()

be just set into

set temp = allocate()?

Perhaps I posted a little too much information; what I did was make a really simple struct with only two members and one static member (global). The entire script is within a library which allows it to be ordered amongst other libraries. The idea of the private keyword is (as far as I know) the exact same as in C++; anything declared as private cannot be referenced outside the containing scope.

A scope refers to:
  • Libraries (library)
  • Scopes (scope)
  • Interfaces (interface)
  • Structs (struct)
  • Modules (module)

That's all I can think of at the moment, you definitely don't need to know how to use all of these right away - it is a gradual process.

There are only two scope modifiers that you need to concern yourself with, those being private (which doesn't allow outside reference) and public. The other option here is that you do not explicitly define a modifier, which will declare the function/variable/etc. as it is defined. I'll give an example:

JASS:
scope SomeScope
    // This function will be referenced as "SomeScope_SomeFunction" when referenced outside of the scope.
    // Inside the scope it should be referenced as "SomeFunction".
    public function SomeFunction takes nothing returns nothing
    endfunction

    // This function will be referenced as "SomeOtherFunction" when referenced outside of the scope.
    // Inside the scope it will also be referenced as "SomeOtherFunction".
    function SomeOtherFunction takes nothing returns nothing
    endfunction

    // This function can only be referenced inside the scope, and it will be referenced as "YourFunction".
    private function YourFunction takes nothing returns nothing
    endfunction
endscope

If there is a function (no modifier) defined somewhere in the code that matches the name of a public function and you are trying to reference that function from within the public scope then the public function will be referenced, rather than the static function.

I'll give you an example:

JASS:
scope OtherScope
    function YourFunction takes nothing returns nothing
        call BJDebugMsg("function YourFunction is executed.")
    endfunction
endscope
scope SomeScope initializer YourOtherFunction
    public function YourFunction takes nothing returns nothing
        call BJDebugMsg("function SomeScope_YourFunction is executed.")
    endfunction
    public function YourOtherFunction takes nothing returns nothing
        call YourFunction()
    endfunction
endscope

The line call YourFunction() will execute the method above it, or in other words "function SomeScope_YourFunction" will be displayed on the screen at initialization.



Yea. It can.

I'm overwhelmed :vw_wtf: lemme see if I can make something using what you guys just said, wait.
 
Level 10
Joined
Sep 3, 2009
Messages
458
.allocate, allocates a struct instance, its like giving you an index to use in array variables...

so it's like instead doing something like struct[9] it just allocates data for it? something like that ok. So I need to do allocate()

thistype returns whatever that struct's name is... its usually used when the struct name is long... ^_^

so it's like putting the struct in a variable, where in you can use that variable to refer to the struct something like that. so wait If you already did:

JASS:
private static thistype temp

why do you need to do:

JASS:
local thistype nova = allocate()

and why did he set temp = nova?

and also wuts nova.destroy() ?

thanks for bearing with me by the way >.>
 
Level 10
Joined
Sep 3, 2009
Messages
458
about temp and nova, well he just separated them but as he said you can just do it like this:

set nova = .allocate()

.destroy calls the method onDestroy (you dont really need to create one unless ur doing more complex codes especially libraries)

basically it destroys the instance...

I think it was set temp = allocate()

oh so he just separated it but it's just the same thing, which is the struct name.

hmmm also in a method do I need to do return 0?

oh and another

JASS:
call GroupEnumUnitsInRange(enumGroup, x, y, ICE_NOVA_AREA, Filter(function thistype.enumGroupFilter))

On the "Filter(funtion thistype.enumGroupFilter))"

wuts the "Filter" for can I just do function thistype.....

and also is the thistype. part the struct?
 
oh right set temp = .allocate()

^_^

you dont need to return 0 in a method, you need to return what it returns

if it returns nothing you dont need to return anything...


Filter() is like Condition() they convert the function inside the () into boolexpr which is needed for the grouploop...

as I said thistype returns the name of the struct
thistype.enumGroupFilter
is the same as

yourstructname.enumGroupFilter

and enumGroupFilter is a static method which returns a boolean...
 
Level 10
Joined
Sep 3, 2009
Messages
458
oh right set temp = .allocate()

^_^

you dont need to return 0 in a method, you need to return what it returns

if it returns nothing you dont need to return anything...


Filter() is like Condition() they convert the function inside the () into boolexpr which is needed for the grouploop...

as I said thistype returns the name of the struct
thistype.enumGroupFilter
is the same as

yourstructname.enumGroupFilter

and enumGroupFilter is a static method which returns a boolean...

uhm so why did he put return 0?

I get filter now it's pretty cool
 
because of this: returns thistype

but normally what you return is the value of thistype, in that case temp...

but its okay to use 0 there, because the struct usage was instant... but in non-instant cases, you must return temp


btw, I'm making a Channeled/OverTime Nova System which I might upload a bit later... ^_^
 
Level 10
Joined
Sep 3, 2009
Messages
458
because of this: returns thistype

but normally what you return is the value of thistype, in that case temp...

but its okay to use 0 there, because the struct usage was instant... but in non-instant cases, you must return temp


btw, I'm making a Channeled/OverTime Nova System which I might upload a bit later... ^_^

hmmmm ok

wow I'll sure check it out when you upload it
 
Level 10
Joined
Sep 3, 2009
Messages
458
Hey Guys! I have a question

JASS:
public function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Filter(function cond))
        call TriggerAddAction(t, function actn)
    endfunction

JASS:
library IceNovaSpell initializer init
    globals
        constant real       ICE_NOVA_AREA       = 150
        constant real       ICE_NOVA_DAMAGE     = 250
    
        constant integer    ICE_NOVA_ABIL_ID    = 'A00C'
        constant string     ICE_NOVA_FX_PATH    = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
    endglobals

Sorry I posted in the visitor messages I was pretty impatient >.>.<.<
 
Level 10
Joined
Sep 3, 2009
Messages
458
on 1) IDK to Berb... most people make that function private

2) yes...

1. Hmmm well public means that the function can be used outside the scope right? The scope being the library? Am I right?

2. Wow so I need to make a function called that too eh?
Oh if I make the function init private, I can make another function called init on different libraries right?
 
1. Hmmm well public means that the function can be used outside the scope right? The scope being the library? Am I right?

2. Wow so I need to make a function called that too eh?
Oh if I make the function init private, I can make another function called init on different libraries right?

you can still call functions that are not public outside the library as long as its not private, I think public is useful for function inside a scope...

2)yup, especially on scopes but for libraries, an initializer is optional...

when I say scope I mean this:

JASS:
scope
endscope
 
Level 10
Joined
Sep 3, 2009
Messages
458
Hey I made something with vjass. It's pretty much like the NovaSpell but I did everything I learned from you guys

Attack System

Well basically I'm converting my GUI attack system to vJass. Although I have only done the basic stuff. I do apoligize for using locations, can't figure out how to polarproject x and y coordinates.

JASS:
library AttackSystem initializer init
    globals
        constant real ATTACK_AREA = 300
        constant real ATTACK_DAMAGE = 100
        constant real ATTACK_THRUST = 100
        
        constant string ATTACK_SFX = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
        
        constant string ATTACK_ORDER_ID = "holdposition"
    endglobals
    
    struct Attack
        private unit caster = null
        private location CasterLoc = null
        private location DamageLoc = null
        
        private static group enumGroup = CreateGroup()
        private static thistype temp
        
        private static method enumGroupFilter takes nothing returns boolean
            if IsUnitEnemy(GetFilterUnit(), GetOwningPlayer( temp.caster )) then
                call UnitDamageTarget( temp.caster, GetFilterUnit(), ATTACK_DAMAGE, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
            endif
            return false
        endmethod
        static method cast takes unit caster returns thistype
            local thistype temp = allocate()
            
            set temp.caster = caster
            set temp.CasterLoc = GetUnitLoc( caster )
            set temp.DamageLoc = PolarProjectionBJ( temp.CasterLoc, ATTACK_THRUST, GetUnitFacing( caster ) )
            
            call DestroyEffect(AddSpecialEffectLoc( ATTACK_SFX, temp.DamageLoc ))
            call GroupEnumUnitsInRangeOfLoc( enumGroup, temp.DamageLoc, ATTACK_AREA, Filter( function thistype.enumGroupFilter ) )
        
            call temp.destroy()
            return 0
        endmethod
    endstruct
    
    //==============================================================================================================
    private function condition takes nothing returns boolean
        return ( GetIssuedOrderIdBJ() == String2OrderIdBJ(ATTACK_ORDER_ID) )
    endfunction
    
    private function action takes nothing returns nothing
        
        call Attack.cast(GetTriggerUnit())
        
    endfunction
    private function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_ORDER)
        call TriggerAddCondition(t, Filter(function condition))
        call TriggerAddAction(t, function action)
    endfunction
endlibrary
 
you can use the cast function as the action of the trigger rather than using another function to do Attack.create

call TriggerAddAction(t, function action)

you can do

call TriggerAddAction(t, function Attack.cast)

JASS:
static method cast takes nothing returns thistype
            local thistype temp = allocate()
            
            set temp.caster = GetTriggerUnit()

and the locations leak... remove them before the .destroy

for the polar project of x/y

projected x = x + Distance*Cos(angle in radians)
projected y = y + Distance*Sin(angle in radians)

distance is the distance between the original point and the projection point...
 
Level 10
Joined
Sep 3, 2009
Messages
458
you can use the cast function as the action of the trigger rather than using another function to do Attack.create

call TriggerAddAction(t, function action)

you can do

call TriggerAddAction(t, function Attack.cast)

JASS:
static method cast takes nothing returns thistype
            local thistype temp = allocate()
            
            set temp.caster = GetTriggerUnit()

and the locations leak... remove them before the .destroy

for the polar project of x/y

projected x = x + Distance*Cos(angle in radians)
projected y = y + Distance*Sin(angle in radians)

distance is the distance between the original point and the projection point...

wow cool. I'll be converting the locs into coordinates in a bit just one question, is angle in radians the angle?
 
Status
Not open for further replies.
Top