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!
I am reading tutorials about JASS but it makes me laugh that they don't tell about the spaces..
Thus space affect the trigger?.. i mean look call have 1 space from the left while the next function will have 2 space others are 3.. i really confused.. HOW IS THE SPACE AFFECT THE TRIGGER?.. or WHEN DO I USE A CERTAIN AMOUNT OF SPACE like when i space once?.. or twice? ..
JASS and vJASS are somewhat the same...
JASS is the scripting system of warcraft 3.
vJASS is an extension to JASS.
So infact if you learn vJASS you'll learn JASS too
I suggest you try to do some basic spells in vJASS to learn.
It's easier to understand JASS because you can convert GUI triggers to see how to do many things.
In my own experience i started JASS ( because i didn't know there was vJASS xD ! ) and when i knew about JNGP and an extension for vJASS i learned it.
Library (vJASS) -> All code in a library block are moved to the top of the script. This was created so that code would not have to be placed in the map header in order for the scripts to be used in other triggers.
JASS:
library
function AngleBetweenPoints takes real x1, real y1, real x2, real y2 returns real
return Atan2(y2 - y1, x2 - x1)
endfunction
endlibrary
Scope (vJASS) -> Scopes serve two main purposes. (1) You can declare an initializer, which will be a function that is run on map initialization. Normally in vanilla JASS, you would prefix a function "InitTrig_" for it to be ran on initialization. It usually registers the events/trigger related options. In vJASS, you will usually declare either a scope or a library and make an initializer:
JASS:
scope X initializer Init // run function "Init" on initialization
function SpellCast takes nothing returns boolean
return GetSpellAbilityId() == 'A000'
endfunction
function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function SpellCast))
endfunction
endscope
There is technically another method of making a function run on initialization in vJASS. It has something to do with making it public and naming it "Init", or something similar (it was occasionally used a really long time ago). However, you don't need to know this.
(2) Encapsulation. This means you can make things public or private. private means that you can't use the function outside the scope/library it is in. It will prefix the function with the scope/library name and a random number of underscores to prevent name collisions, misuse, etc. This is especially useful for naming functions. Once upon a time, every function had to be prefixed properly. You couldn't just name something "Init" or "Actions" because it would likely collide with other triggers. That was why there was a standard, known as JESP, for how to properly code spell resources.
However, that is a thing of the past since vJASS. By making a function "private", you can use the same function name in different scopes:
JASS:
scope A
private function Actions takes nothing returns nothing
endfunction
endscope
scope B
private function Actions takes nothing returns nothing
endfunction
endscope
The keyword public means that, in order to call the function, you must call it via SCOPENAME_FunctionName. Example:
JASS:
scope A
private function PrivateFunction takes nothing returns nothing
endfunction
public function PublicFunction takes nothing returns nothing
endfunction
endscope
scope B
private function Test takes nothing returns nothing
call A_PrivateFunction() // error, function is private
call PrivateFunction() // error, no such function (it is private in A)
call A_PublicFunction() // no error! :)
endfunction
endscope
As for things that start off with "function_", that doesn't really exist. However, you may be looking at vanilla JASS resources. Those do not require JASSHelper (integrated in JNGP), and as such they don't have blocks such as "scope" or "library". They are just put as normal functions.
I am reading tutorials about JASS but it makes me laugh that they don't tell about the spaces..
Thus space affect the trigger?.. i mean look call have 1 space from the left while the next function will have 2 space others are 3.. i really confused.. HOW IS THE SPACE AFFECT THE TRIGGER?.. or WHEN DO I USE A CERTAIN AMOUNT OF SPACE like when i space once?.. or twice? ..
Spacing doesn't matter. JASS is not indentation based. This:
JASS:
set garfunkel = GetRandomInt (0, 1 )
Is the same as:
JASS:
set garfunkel = GetRandomInt(0, 1)
It is just a matter of personal preference. You'll develop your own coding style after some time. Just try not to make it too ugly. Proper indentation is always a plus.
GetLocalPlayer () is it the same as GetLocalPlayer ( ) ?.. do i get an error?..
Are there any rules on CAPITALIZATION?.. like the word private... some are Private some are just private.. so when i use a capital letter and when i won't?...
Are there any rules on CAPITALIZATION?.. like the word private... some are Private some are just private.. so when i use a capital letter and when i won't?...
Yes. JASS is case sensitive. Private is not the same as private. Ex:
JASS:
function Test takes nothing returns nothing
endfunction
function Example takes nothing returns nothing
call test() // syntax error, no such function
call Test() // no error
endfunction
Yes. JASS is case sensitive. Private is not the same as private. Ex:
JASS:
function Test takes nothing returns nothing
endfunction
function Example takes nothing returns nothing
call test() // syntax error, no such function
call Test() // no error
endfunction
You can use "private" though. Usually, if you want someone to be able to access your function, don't add the "private" keyword. Just leave it as a regular function and give it a good name.
For all other intents and purposes, you can usually affix "private" to it. There are cases where you might want to use public (avoid name collision, but allow access), but you needn't worry about it too much--especially if you name your functions well.
so in vJASS to create a variable let say an interger you don't need to create Integer in the Variable creator.. right?.. you just type it?..
Also what are those global?.. i found that in most spells before the variables there is a global on the top and below are the variable.. what does global mean?..
You can create globals variables that can be used everywhere in your script and private globals that can be used only on the corresponding library/scope, and public globals that can be used only by timing the scope/library prefix.
JASS:
library A
globals
integer a = 0 //This global can be used anywhere i want
private integer b = 0 //This global can be used only in my library
public integer c = 0 //This global can be used only by typing "A_c"
endglobals
endlibrary
You can also use local variables that can be used only in the function they are declared :
JASS:
function DoSomething takes nothing returns nothing
local unit u = CreateUnit( Player(0), 'hfoo', 0, 0, 0 )
local integer a = 0
//Do some stuff
set u = null
endfunction
( I write set u = null because the unit type is a handle so it's must be nullify otherwise it's a leak. )
Yes. You will use the regular destroy/remove expressions. It depends on what kind of handle you've created, though. If you made a location (in GUI, a "point"), you use call RemoveLocation(<loc>). If you made a timer, then you would use call DestroyTimer(<timer>).
The one thing to note is that if you make a new handle in a local variable, such as:
JASS:
local timer t = CreateTimer()
Then you should null the local variable after you destroy it:
JASS:
local timer t = CreateTimer()
// .. some code later
call DestroyTimer(t)
set t = null
This is to prevent certain memory attached to the handle from staying in memory/remaining unrecycled, most notably the handle ID. You don't need to fully understand this statement I just made, but as a general rule of thumb you should null local handles at the end of a function (by "handles", I mean anything that is not a real, integer, boolean, code, or string variable). There are more rules on it, but you can't really go wrong with just nulling any local handle variable at the end of a function. Here is another example:
JASS:
function Test takes nothing returns nothing
local unit u = GetTriggerUnit()
call SetWidgetLife(u, GetWidgetLife(u) + 100) // heal the unit for 100
set u = null
endfunction
In general, you should null any local handle variable at the end of a function (technically--agents, not handles. but just for ease of memorization, null any handle)
Yes. If you want a local integer variable array then it would look like this:
JASS:
function Example takes nothing returns nothing
local integer array int
set int[0] = 0432 // example
set int[1] = 1234 // example
set int[2] = 5678 // example
endfunction
If you want to make a global:
JASS:
globals
integer array int
endglobals
function Example takes nothing returns nothing
set int[0] = 0432 // example
set int[1] = 1234 // example
set int[2] = 5678 // example
endfunction
Normally you should not use location.
If you use them use :
JASS:
private function Test takes nothing returns nothing
local location l
local group g
local unit u
local effect t
//Do some stuff with them
call RemoveLocation( l )
call DestroyEffect( t )
call DestroyGroup( g )
set l = null
set g = null
set t = null
set u = null
endfunction
But if you want an instant effect use : call DestroyEffect( AddSpecialEffect( model, x, y ) )
or call DestroyEffect( AddSpecialEffectTarget( model, unit, attachment ) )
Ok thanks.
So here's a little list of most used handles and what to do with them :
- unit :
JASS:
local unit u
//Do some stuff
set u = null
- effect :
JASS:
local effect t
//Do some stuff
call DestroyEffect( t )
set t = null
- group
JASS:
local group g = CreateGroup () //To declare a group you must use the native CreateGroup
//Do some stuff
call DestroyGroup( g )
set g = null
- timer
JASS:
local timer t = CreateTimer() //To declare a timer you need to use the native CreateTimer().
//Do some stuff
call DestroyTimer( t )
set t = null
- trigger
JASS:
local trigger t = CreateTrigger() //To declare a trigger you must use the native CreateTrigger()
//Do some stuff
set t = null //Only if you plan to destroy the trigger with the native DestroyTrigger().
EVENT -> CONDITION -> ACTIONS (at least in basics xD).
So to create a trigger in JASS first you will have to create a trigger and to convert it to custom text ^^.
Then you'll see something like this :
JASS:
function Trig_MyTrigger_Actions takes nothing returns nothing
endfunction
//===========================================================================
function InitTrig_MyTrigger takes nothing returns nothing
set gg_trg_MyTrigger = CreateTrigger( )
call TriggerAddAction( gg_trg_MyTrigger, function Trig_MyTrigger_Actions )
endfunction
Ok here we are, there is a init function.
But what is a function. A function is something that can take argument and return one. Infact this init function takes no argument ("nothing") and return nothing
An init function in JASS/vJASS creates the trigger.
Now let's see what's inside this init function : set gg_trg_MyTrigger.
In GUI every trigger got a variable name : gg_trg_TriggerName.
And in every init function they are assigned to a new trigger.
But in JASS we don't want to use that much variables so we'll use a local variable for this trigger.
We'll replace set gg_trg_MyTrigger = CreateTrigger( ) with local trigger MyTrigger = CreateTrigger( ).
Now to add events/conditions/actions.
1.) Events.
To add an event to a trigger we mostly use :
JASS:
local trigger MyTrigger = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( MyTrigger, EVENT_PLAYER_UNIT_SPELL_EFFECT ) //If you want the event "Event - A Unit starts the effect of an ability
If you don't know the events you can add the event in GUI and then convert.
2.) Conditions.
To add a condition to a trigger use :
JASS:
function Condition takes nothing returns nothing
return ( GetSpellAbilityId() == SPELL_ID ) //In the case of checking which spell has been casted.
endfunction
function Init_Trig_MyTrigger takes nothing returns nothing
local trigger MyTrigger = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( MyTrigger, EVENT_PLAYER_UNIT_SPELL_CAST )
call TriggerAddCondition( MyTrigger, Condition( function Condition ) )
endfunction
In this case we created another function.
This one takes nothing but returns something a boolean.
This function check the spell which has been casted and return true if the spell has been casted is the one with the SPELL_ID and else it returns false.
3.) Actions.
To add actions to a trigger :
JASS:
function Actions takes nothing returns nothing
//Do some stuff dude :D
endfunction
function Condition takes nothing returns nothing
return ( GetSpellAbilityId() == SPELL_ID ) //In the case of checking which spell has been casted.
endfunction
function Init_Trig_MyTrigger takes nothing returns nothing
local trigger MyTrigger = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( MyTrigger, EVENT_PLAYER_UNIT_SPELL_CAST )
call TriggerAddCondition( MyTrigger, Condition( function Condition ) )
call TriggerAddActions( MyTrigger, function Actions )
endfunction
We add another function that is the actions of the trigger.
You just have to code inside this now
And now here the finish product with the local trigger leak removed :
JASS:
function Actions takes nothing returns nothing
//Do some stuff dude :D
endfunction
function Condition takes nothing returns nothing
return ( GetSpellAbilityId() == SPELL_ID ) //In the case of checking which spell has been casted.
endfunction
function Init_Trig_MyTrigger takes nothing returns nothing
local trigger MyTrigger = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( MyTrigger, EVENT_PLAYER_UNIT_SPELL_CAST )
call TriggerAddCondition( MyTrigger, Condition( function Condition ) )
call TriggerAddActions( MyTrigger, function Actions )
set MyTrigger = null
endfunction
each time i want to add an event i will put call TriggerRegister..
how about those AnyUnitEvenBJ?.. is that a category?.. Enter's A Region is under that category?.. how about Player - Select Unit?.. i will use AnyPlayerEventBJ i am right?..
each time i want to add a condition i will put call TriggerAddCondition right?.. how about the function Condition?.. do i add like function UnitIsAlive?..
For the event i don't know all the event so for an unique player event just use TriggerRegisterPlayerEvent. Just type it in GUI and convert the most of the time.
For the conditions. The fonction Condition called a function that takes nothing and returns a boolean, this boolean will be the answer do the condition. If it's true then actions will continue if it's false the trigger will not run his actions.
You can put many conditions :
JASS:
function Conditions takes nothing returns boolean
return ( GetSpellAbilityId() == SPELL_ID ) and ( IsUnitAlive( unit ) )
endfunction
I don't made an equation for IsUnitAlive because it already returns a boolean
Actually from what I remember(I'm not at my PC), all the "AnyUnitEventBJ" does is use the "TriggerRegisterPlayerUnitEvent" and loop through all the players(even players won't end up doing anything during any part of the game). So it's better not to use this BJ (like pretty much all others).
But what could be nice is an updated guide to spell making or something like that.
A JASS tutorial could be made, but writing it manually is so last century, and it has been done quite a few times. If I were to write one, I would make the entire thing out of random images and funny pictures. I bet it would work, too.
Actually i read a lot of tutorials about vJASS and JASS but they thought something that i did not understand i mean they not include even the very basic.. but now because of this thread i understand more..
Now may i ask... How does spell arranged?..
Just like this?...
GLOBAL VARIABLES
then
ACTIONS FOR THE SPELLS
then
EVENT OF THE TRIGGER
....
Cause i am really confused where do i put the event.. at the bottom?..
I can copy you one of my spells, i know it isn't very simple but he has everything you may want to know about vJASS :
JASS:
scope Shreding initializer Init
//==========================================================================
//=============================SETUP========================================
//==========================================================================
globals
private constant integer SPELL_ID = 'B000'
private constant real perte = 0.9 //La vie sera multipliée par ce nombre
private constant string model = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl"
endglobals
private function Targ takes unit targ returns boolean
return (GetWidgetLife( targ ) > 0.405 ) and ( IsUnitType( targ, UNIT_TYPE_MAGIC_IMMUNE ) == false ) and ( IsUnitAlly( targ, GetOwningPlayer( GetSpellAbilityUnit() ) ) == false )
endfunction
private function Range takes integer level returns integer
return 400 + 10 * level
endfunction
private function Damage takes integer level returns integer
return 75 * level
endfunction
private function Armor takes integer level returns integer
return 3*level
endfunction
private function MoveSpeed takes integer level returns real
return I2R( 10 + 5 * level )
endfunction
private function AttackSpeed takes integer level returns integer
return 10 + 5 * level
endfunction
//==========================================================================
//=============================END SETUP====================================
//==========================================================================
globals
group g
boolexpr b
endglobals
private function Pick takes nothing returns boolean
return Targ( GetFilterUnit() )
endfunction
private function Cond takes nothing returns boolean
local unit u = GetTriggerUnit()
local integer level
local real x
local real y
local unit f
if ( GetSpellAbilityId() == SPELL_ID ) then
set level = GetUnitAbilityUnit( u, SPELL_ID )
set x = GetUnitX( u )
set y = GetUnitY( u )
call GroupEnumUnitsInRange( g, x, y, Range( level ), b )
loop
set f = FirstOfGroup( g )
exitwhen ( f == null )
call GroupRemoveUnit( g, f )
call UnitDamageTarget( u, f, Damage( level ), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null )
call Debuff.UnitAddDoubleSlowReducArmor( f, 6, MoveSpeed( level ), AttackSpeed( level ), Armor( level ) )
endloop
call SetWidgetLife( u, GetWidgetLife( u ) * perte )
call DestroyEffect( AddSpecialEffect( model, x, y ) )
endif
set u = null
return false
endfunction
//===========================================================================
private function Init takes nothing returns nothing
local trigger Shreding = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( Shreding, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( Shreding, Condition( function Cond ) )
//Set globals
set g = CreateGroup()
set b = Condition( function Pick )
//Preloading effect
call Preload( model )
//Preloading ability
set bj_lastCreatedUnit = CreateUnit( Player(15), dummy, 0, 0, 0 )
call UnitAddAbility( bj_lastCreatedUnit, SPELL_ID )
call KillUnit( bj_lastCreatedUnit )
set Shreding = null
endfunction
endscope
This spell is like a Thunder Clap that reduces armor added to the slows. Beside the caster got his life multiplier by the variable "perte" meaning ~ "lost" in english.
Ok first it's JESP because i made a setup at the begginning, that means i can change the values of the spell without going further in the code.
In this setup there is two parts : the globals and the functions.
The globals are the spell id inside the object editor, a life multiplier factor ( french inside sorry xD ), and the path of the model i will use.
Then i set the area of effect around the caster, the damage the ennemies will take, the armor they will loose, the movement speed they will loose ( in percentage ), the attack speed they will loose ( in percentage ).
It's a bit difficult because i made a custom debuff system that handles those debuff that's why it is a bit disturbing.
Just take a look at the other globals block after the setup, those globals won't be change by the user. They're a part of the code.
Ok now let's take a look about the last part XD !
The Init Part, i create a local trigger var that i use for everything. I add the event, the condition. Oh yes another disturbing thing, I'm trying to not use action block since you can do everything in one single block ( and there is another technical reason i forgot ^^ ).
I set the globals g and b.
I preload the effect and the abilities to prevent lags.
Now let's take a look at the condition function. First i declare some locals, I need the caster, his coordinates, the level of the ability for the unit and a tampon unit for the FirstOfGroup method.
Then i check the Spell_ID of the spell being casted.
Then i set some vars. I pick every units that are in range with the boolexpr condition.
Then i create loop with the FirstOfGroup method to damage everyone and to apply the debuff to everyone.
At the end of the loop I set the life of the caster to his life * the multiplier and at last I create the beautiful effect
At last don't forget to clean leaks
Ok I made this quite fast so if you have any questions go ahead.
Scope uses initializer that means the trigger will init on this function.
To register an event i register it in the Init function. call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
This line means a unit starts the effect of an ability.
Although I wouldn't personally set it up to register it to all players(it runs through to Player(15)), as not only does it use useless processing to register through all the players, but everytime a unit casts a spell, it runs the trigger everytime one does.
There is a better and more highly efficient way of registering a casting unit. For instance, whenever a unit is created it can be registered to the right trigger or you could register it to specific players that have a likelihood or having a unit that would cast the right spell.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.