The Helper offers free technical support for computer games,
computer hardware, and other computer software. Staffed
by volunteer tech support workers, The Helper is highly
regarded as a premium destination for those who are
experiencing technical problems with their computers.
vJass - I: Uncomplicating the Complicated
By Romek - The First in the Series
Introduction:
This tutorial is best viewed on Thehelper forums. If you are viewing this tutorial from the World-Editor-Tutorials website, click here.
This tutorial is the first in a series of vJass tutorials which I will be writing. The tutorials are ultimately aimed at wannabe spell and system makers, though anyone can use them. This first tutorial will go over the very basics of vJass, and will not be covering resource making.
I know, another Jass tutorial (). Though I have to start my series from something, don't I? And maybe this one will cover things that other's didn't.
This tutorial should cover all the basics of vJass, including functions, variables, arguments, scopes, etc. We'll also cover removing BJs*, which is an essential skill for any coder. I suggest trying these out as you go along, making code and seeing if it compiles, and possibly testing it.
I also suggest taking breaks throughout this tutorial, as it can be a lot for one go. If you have any questions or suggestions, be sure to post them here!
* - BJs are basically useless functions. Will be explained later in more detail.
Starting Jass:
Create a trigger object, go to Edit -> Convert to Custom Text, and delete everything that's there. This is the place where you'll be vJassing.
An important rule I think I should mention here, before anything else, is that Jass is CaSe SeNsItIvE!. So make sure you keep that in mind!
Triggers in Jass
While using GUI, you have learned that a trigger is something that contains events, conditions and actions, and you can browse triggers by looking at the little tree view on the left side of the editor. This is actually a false idea of what triggers actually are. Triggers, are actually just another variable type, just like effects or unit groups. The things you see on the left side will be referred to as 'Trigger Objects' throughout this tutorial, to not get the two ideas mixed up.
Instead of Events, Conditions and Actions, Jass actually uses functions and initializers.
Initializers are functions that are called when the map is loading. This is where triggers are created, and events, conditions and actions are added to them. It's very possible to have a trigger object without a single trigger, as they are most certainly not a necessity.
Functions and Comments:
Jass is coded entirely using functions. Everything you see and do is made through functions. To declare a function, use the following syntax:
Jass:
functionNAMEtakesnothingreturnsnothing// Things inside the functionendfunction
NAME is obviously the name of the function. Functions can be called anything you want them to, as long as it doesn't start with a number (or underscore), and contains only letters, numbers or underscores (_).
Here are some examples of Valid function names:
Init (Only letters)
Hello_World (Underscores are allowed)
B4N4N4PI3 (Numbers are allowed if they're not the first character)
Whilst the following are Invalid:
3Hello (Numbers first are disallowed)
Find% (Uses non alphanumeric characters)
If functions aren't initializers, they must be called using the following syntax:
Jass:
callNAME()
NAME is the name of the function. You cannot call a function that's below the place you're calling from in Jass.
Jass:
functionAtakesnothingreturnsnothingcallB()// Will give an error. B is below A.endfunctionfunctionBtakesnothingreturnsnothingcallA()// Will make 'function A' run.endfunction
Notice the comment in the middle of the function. Everything written after a double slash (//) will be ignored by the parser, so you can type anything you want there to make your code look cleaner, or to leave notes for yourself. I strongly suggest you use comments as much as possible, as it certainly helps people understand your code. Also, if you've left some code for a month or so, and come back to it. You'll have a difficult time grasping what everything does if you don't comment.
A good rule to remember while commenting things is that you should always write why you are doing something, and not what you are doing.
You should already know what a Variable is from your GUI experience. However, there are two types of variable in Jass. The type of variable you used in GUI is called a Global. This is because it can be accessed from anywhere in the script (or scope), and can be overwritten from anywhere. To declare a global in vJass, use the following syntax:
The globals commented with 1 and 2 are the basic syntax for globals, and will not compile (save) as they are now. 3 and 4 are both examples, which will compile without errors. TYPE can be any variable type, for example: unit, integer and effect.
Some notable types are integer, real, boolean and string. An integer is a whole number; a real is a number with a decimal point; a boolean is either true or false; and a string is a set of characters, or a message.
What's also notable is that integers can be in hexadecimal and octal. Here are some example usages of these common types:
Jass:
localbooleanb=truelocalrealr=0.47249localintegeri1=90429// Decimallocalintegeri2=0xF0// Hexidecimal. Prefixed with 0xlocalintegeri2=045// Octal. Prefixed with a 0.localstringmessage="Hello, World!"// Strings must be within quotation (") marks.
NAME is the name of the global, which is used within code to access the value of the global. Globals can also be initialized, which basically means giving them an initial value. VALUE is the initial value of the global. If this is not specified, it'll be 'blank'.
Number 3 is an integer (whole number) variable named 'Five'. Number 4 is an integer variable named 'Four', with an initial value of 4. You can also make globals constant by adding the constant keyword before the type. Constants have to be initialized, and cannot be changed at all throughout the code. They're also very fast. An example of a constant would be Pi.
The other type of variable you will encounter is called a local. These are unavailable in GUI, and can be difficult to understand if you haven't used them before. Local variables, as the name implies, are only accessible from the function in which they were declared (Yes, they are declared in functions). However, when a single function is run multiple times, the locals within it are all allocated to different memory 'slots', meaning they cannot be overwritten by other functions. So, to sum up, local variables are function-specific variables, that cannot be accessed or overwritten - the direct opposite of globals.
Locals are not related to other variables outside of the function in any way, and as a result, 2 locals may share the same name (as long as they are in different functions). Because of this, locals tend to use very short, common names, such as single letters. To declare a local, use the following syntax:
Local variables commented with 1 and 2 are just examples of the basic syntax. A 'local' prefix is required when declaring local variables. TYPE is the type of the variable, as with globals. NAME is the name of the variable, which is used to access it throughout the function. You can initialize locals in exactly the same way as you can with globals. 3 and 4 are examples of local variables.
Note that locals must be declared at the top of a function - On the first lines, before anything else.
To set variables to values, use the following syntax:
Jass:
setNAME=VALUEseti=5
The first, as always is the basic syntax, while the second is an example. NAME is the name of the variable, whilst VALUE is what you want to set the variable to.
Integers and Reals can be added, subtracted, multiplied and divided in Jass (more operations are available via functions).
Jass:
localintegeri=2seti=i*3// Triples i - Becomes 6.seti=i+10// Adds 10 to i. Becomes 12.seti=i-5// Subtracts 5 from i. Becomes -3.seti=i/2// Divides i by 2. Becomes 1.seti=i+5*2// * and / come before + and -. So this becomes i + (5 * 2), which is 12.seti=i*i// This squares i
What's also noteworthy is that integers are always rounded down if they were to have decimal points (which they can't). Decimals are also always rounded down, though by adding 0.5 to the result, you can round the number correctly.
Jass:
localrealrlocalintegeriseti=1/2// becomes 0. 0.5 is rounded down.setr=1./2.// becomes 0.5. Notice the decimal point after the numbers. This means there is nothing after the decimal point.seti=99/100// This also becomes 0. As 0.99 is still below 1.setr=99./100.// This becomes 0.99.
Integers and reals can usually be used with eachother, though when they cannot, I2R and R2I can be used to convert an integer to a real, or a real to an integer. This is especially useful when you want to round an integer division.
Jass:
localreali=99/100// Although this variable is a real, 99/100 is an integer division, and will return 0. 99./100. should be used instead.localintegeri=R2I(99./100.+0.5)// Will make i equal 1. As adding the 0.5 made the number round correctly. Also, we converted as 0.5 cannot be added to integers.
Strings can be concatenated using '+'. For example:
Booleans can be used with 'not', 'and' or 'or'. And requires that all the booleans involved equal true, or it'll become false. Or requires that at least one must be true. Not simply reverses the booleans value.
For example:
As before, 1 is the basic example of syntax. It's the same as with ordinary variables, except the 'array' keyword after the type. 2 is an example of an array of integers.
Arrays cannot be initialized.
Arrays are used in a very similar way to other variables, except that the index is put between brackets ([]). They are also set in this way. The index must be an integer.
Array indices start at 0, not 1. What's very important is that the indices only (safely) go up to 8191. Anything above will simply not work.
Other variable types include: code, unit, trigger, etc. There is a vast amount of different types, which can be used to store many things in the game.
If Statements:
If Statements are used to skip certain parts of code, or only run parts of code when some conditions are met. if marks the beginning of the if block. Once 'if' is in place, a boolean follows, which is then followed by a 'then'. To end the if, use the keyword 'endif'. Everything after 'then', but before 'endif' will be executed only if the condition is true.
Jass:
if<BOOLEAN/CONDITION>then// Do somethingendif
Although booleans must be true or false, this does not mean that the raw value must be used when comparing. All variables can be compared to eachother by using '==' and '!='.
'==' means "Is equal to", whilst '!=' means "Is not equal to". Here are some examples, with what boolean value they would give.
Jass:
if5==5then// trueif4==5then// falseif4!=5then// trueif3==5-2then// trueifsomeBoolean==falsethen// true if someBoolean is false.ifsomeBoolean!=truethen// true is someBoolean is falseif"hello"=="hello"then// trueif"hello"=="hel"+"lo"then// trueif5==4+1andsomeBoolean==falsethen// 'and' and 'or' can also be used.
Reals and Integers can be compared to eachother in other ways.
For example, to set an integer to 5 if it's over 5, you'd use the following:
Jass:
ifSomeInt>5thensetSomeInt=5endif
If SomeInt is 5 or below, nothing will happen, and the entire if block will be skipped.
However, we can also make use of else. Else is another keyword that is put into an if block. If the condition is false, then the 'else' functions will be called.
Jass:
if<BOOLEAN/CONDITION>then// Do stuff if it's trueelse// Do stuff if it's falseendif
If the conditions are true, then do what comes after the 'then', otherwise, do what's in the 'else'. Here's an example:
Jass:
ifsomeInteger>5thensetsomeInteger=someInteger-1// If it's greater than 5, decrease it by 1.elsesetsomeInteger=someInteger+2// Otherwise, increase it by 2.endif
The last keyword related to if statements is elseif. You can probably tell that this is in fact a combination of else and if. If the original boolean is false, then the elseif will be checked. If that's true, then the functions after that will be executed, and the if will end.
In fact, elseif is basically a neater way of doing the following:
Jass:
if<SomeBoolean>then// Some Actionselseif<SomeOtherBoolean>then// Some Other Actionsendifendif
Though it's part of the original if-block, so another 'endif' isn't required:
Jass:
if<SomeBoolean>then// Some Actionselseif<SomeOtherBoolean>then// Some Other Actionsendif
Else can also be added to the end of the if block. However, all elseifs must go before the else.
An if statement can have as many elseifs as you want, though they'll be checked in order. So if the first one is true, all the rest of the elseifs, and the else will be skipped. The else functions will be called if all the elseifs are false.
Here's a full example:
Loops allow similar code to be executed multiple times without having to copy the code over. Loops are usually used with an integer which counts the amount of times it has looped, and it stops looping when the counter reaches a certain amount.
Loops are used by putting the keyword loop before the code to be looped, and [/b]endloop[/b] after it.
Jass:
loop// Code to be executed multiple timesendloop
exitwhen is used to stop the looping.
Jass:
exitwhen<BOOLEAN/CONDITION>
It must be within a loop, and any amount of them can be used per loop. The looping will stop once any of the booleans are true, and it will stop at the position of the exitwhen.
Local integers are often used to stop a loop after it's looped a certain amount of times:
Jass:
localintegeri=0loopexitwheni==3// Code to be runseti=i+1endloop
Everytime the loop runs, it will increase the integer 'i', by 1. And exitwhen i == 3 will be true after the loop has been run 3 times. Counters are almost always used, as when a loop doesn't end, it will create a lot of lag, and stop the current function that's running. However, it is possible to have an endless loop with waits, which will work fine, though timers are much better in that case.
Loops also work very well with arrays, as the integer variable can be used to set the arrays. For example:
When making them. Functions can in fact take variables and return them. Taking and returning values can be a difficult concept to grasp, however, I find that it helps to think of functions as factories. A function can take values, just as a factory needs resources. A function can then return a value once it's done something, just as a factory produces products.
The basic syntax for making a function take or return values is the following:
TYPE_A, TYPE_B, and TYPE are all variable types, such as integers, booleans, units or players.
NAME_A and NAME_B are the names of the variables the function takes. These are treated like locals, and they are referenced by the name. The variable the function returns doesn't need a name, as that is defined in another function, where the value is actually used.
Here's an example:
Functions can take as many arguments as you want, but they can only return one value.
Functions can also take nothing and return a value, or take arguments and return nothing.
To make a function return a value, use the keyword return followed by the value that should be returned. For example:
The function returns an integer, and as '5' is an integer, this is valid. 'return true', however would not compile.
To utilize both takes and returns, you can make a function like the following:
Notice that the function takes an integer called 'i', so 'i' is used to reference that throughout the function.
Here's an example which utilizes two arguments:
So, now that you know how these work within the actual function, you may be wondering how to utilize these.
If you remember, when calling a function, you did the following:
Jass:
callFuncName()
There are 2 empty parenthesis there, this is because FuncName doesn't take any arguments.
When a function takes arguments, you put them within the parenthesis, and separate them with a comma (,) if there are more than one:
In these cases, 'a' would be usable in function B, C, and D with the value of 1. b would usable with the value of 2, and c with the value of 3.
To use the returned value, simply set a variable to the function. That may not make much sense, so here's an example:
Jass:
functionReturn5takesnothingreturnsintegerreturn5endfunction// Within a function:localintegeri=Return5()
Arguments can also be used:
Jass:
functionMultiplytakesintegera,integerbreturnsintegerreturna*bendfunction// Within a functionlocalintegerabc=Multiply(2,5)
The value of the variables will be the value that the function returned. If a function returns a value, the value doesn't have to be used. So:
Jass:
callMultiply(2,5)
Is also valid, but is useless, as it does nothing in this case.
Natives and BJs:
Natives and BJs are functions which are included in Jass. BJ stands for Blizzard Jass (not Blow Jass or Blizzard Job - As many people think). Natives are the functions which Jass is made up of. By default, they appear as Purple in the trigger editor. BJs, on the other hand, appear Red. BJs are just functions like ones you can make yourself. They use locals, loops, ifs, natives, etc. Natives cannot be modified, and are the heart of Jass.
Natives and BJs are just like functions you declare, so they can take and return values. These include functions such as R2I, KillUnit and GetTriggerUnit.
You can access the function list by clicking on Function List. You can then search for functions. This will be extremely useful for you until you learn how functions are named. You'll probably never learn the names of every function, though most functions are named in a very similar way. For example, many functions which get something related to a unit start with "GetUnit".
A very useful tip when searching for things is that you can use "%" in-between words to search for functions containing both of them, but not in that order.
For example, if you were looking for a function which sets a units movement speed, you could search for: "Unit%Speed". This'll return every function related to units and speed, including functions which set and get movement speed and turn speed.
Converting GUI actions to Jass may help you look for function names too, though beware of the BJs! Actually, they can be very easily removed. I'll go over that now.
So, you want to attach an effect to a unit, but you can't find the function. So you make a GUI trigger like the following:
Trigger:
Test
Events
Conditions
Actions
Special Effect - Create a special effect attached to the origin of (Triggering unit) using Abilities\Spells\Other\TalkToMe\TalkToMe.mdl
And then go to Edit -> Convert to Custom Text.
You'll end up with something like this:
This should appear red in your editor, which tells you that it's a BJ.
Hold CTRL, and left click on the function to bring up the function list with the clicked function.
You'll see that AddSpecialEffectTargetUnitBJ is actually made up of:
Most bj_ globals are used in GUI for things like "Last Created Effect". Though in Jass, this is useless, as we can set the effect we want to a local. So we simply remove the bj_ globals and are left with:
This another common feature of BJs. It is done to make GUI more readable (apparently).
If you were using the BJ in your code to begin with, be careful of the order of the arguments, though this is usually an easy fix:
Hopefully you can see how the order changed from the BJ to the Native. The best way to do so is to follow the names of the arguments, as the colours mark out.
Instead of using bj_lastCreatedEffect, as you would in GUI. (Last Created Special Effect), you can simply use a local:
Most BJs just call natives with reversed arguments, which is utterly useless and inefficient (Such as that Special Effect function). This is why most BJs are looked down upon. There are however, some useful BJs, such as BJDebugMsg or ModuloInteger.
In fact, I strongly suggest you use BJDebugMsg when testing things, or when debugging code. It's a very simple function which displays a string, though it's quick and easy to use.
Scopes, Initializers and Encapsulation:
Initializers are functions which are called when the game is loading. Old Jass initializers would be called InitTrig_TriggerObjectName. Though with vJass, we'll take the neater approach.
Scopes allow you to easily declare initializers with desired function names, as well as use encapsulation (Limiting access to the contents of the scope from things outside of it). To declare a scope, use the following syntax:
Jass:
scopeNAMEinitializerFUNC// Contents of the scopeendscope
NAME is the name of the scope. This must be unique. As with function and variable names, scope names cannot contain non-alphanumeric characters, and cannot start with numbers. However, unlike with functions and variables, they cannot contain underscores. FUNC is the name of a function within the scope. This function will be run at map initialization.
Jass:
scopeTestinitializerInitfunctionInittakesnothingreturnsnothing// This function will be run when the game loadsendfunctionendscope
Note that initializers are infact optional, and you can use a scope solely for encapsulation.
So, what is this encapsulation I've been droning on about? You can add a private or public prefix to functions, globals, and everything else in the scope to limit it's accessibility from outside the scope.
Adding a private keyword will make a function or global inaccessible from anywhere outside the scope, whilst adding a public prefix will mean a SCOPENAME_ prefix will need to be added to access the variable or function. For example:
Jass:
scopeTestglobalsprivateintegerPrivpublicintegerPubendglobalsprivatefunctionPrivFunctakesnothingreturnsnothingendfunctionpublicfunctionPubFunctakesnothingreturnsnothingendfunctionendscopescopeAnotherScopefunctionSomeFunctiontakesnothingreturnsnothingsetPriv=5// Error - It's privatesetPub=5// Error - It's publicsetTest_Priv=5// Error (private functions are accessible ONLY within the scope)setTest_Pub=5// This is allowed. 'Test' is the scope that 'Pub' is withincallPrivFunc()// Error - PrivatecallPubFunc()// Error - PubliccallTest_PubFunc()// Works - It has the 'Test_' prefix.endfunctionendscope
Private and Public global/function names must be unique to the scope they are in. It's generally good practice to make all your globals and functions private unless they're needed outside the scope (Which is nearly never in a spells case). Because of this, it's possible to give functions and globals short, descriptive names such as "Init", "Actions" or "Group".
Knowing how to make initializers, and how to display messages with BJDebugMsg should be all you need to start experimenting with Jass yourself, and get some results.
Remember I2S and R2S are used to convert integer and reals to strings, so they can be used with BJDebugMsg.
Two very useful functions you may want to use are:
These are used to get a random number between numbers a and b, and can be very useful if you're learning some stuff, and would like to experiment.
For example, to check whether or not a number is greater than 5, and display the result, you could do the following:
Great tutorial. Easy to understand just don't say bj's are basically useless because some aren't.
That is excactly what basically covers, while the majority of BJ's functions are useless some aren't.
Back on track;
So for a tutorial that covers vJass this really doesn't cover that much? I think you would be better of linking to a basic jass tutorial, and then focus completely on vJass. However combining the basics of Jass and vJass as one entity is a really great idea if it's done properly.
The last bit deserves an example of the difference between regular Jass trigger interface and vJass efficient trigger interface.
> So for a tutorial that covers vJass this really doesn't cover that much?
Hmm, you think?
I didn't want to get into structs and stuff, as this is just the very, very basics. Structs and such will be in the next one.
> However combining the basics of Jass and vJass as one entity is a really great idea if it's done properly.
And would you say this is done properly?
> The last bit deserves an example of the difference between regular Jass trigger interface and vJass efficient trigger interface.
I think most of the parts need more examples actually.
I'll add that later, as well as another little section at the end.
Quick glance through the post (too lazy to look at it in detail right now )
Quote:
If a variable that has no value is used, it will stop the crash the current thread. Simply initialize your variables before you use them.
It will stop the crash the current thread? Yes, that makes perfect sense I'm assuming the first 'the' is meant to be 'and'?
Also, given the fact that you've only covered scopes (and it's not a particularly long section anyway), perhaps that could be removed from this tutorial and saved for a future one (I assume there's going to be a future one because of the I in the title? ), and just stick to standard JASS for this one?
Quote:
It's generally good practice to make all your globals and functions private unless they're needed outside the scope (Which is never in a spells case).
Never say never There are reasons why one would leave members of a scope non-private (e.g. for interaction between multiple spells) - not really important, but just for the sake of being more correct ^.^
And it's exceptionally long for a single post - you couldn't have separated it into multiple posts?
Nice job Romek, I just scim read it, but it seems pretty useful
I never learnt vJASS, just because I couldn't be bothered to learn it, but if I get time I might properly read through this tutorial and actually learn it.
> It will stop the crash the current thread? Yes, that makes perfect sense I'm assuming the first 'the' is meant to be 'and'?
I blame uBerpLayer for not spotting that.
> Never say never
I'll stick a "nearly" into that sentence then. =|
> Nice job Romek, I just scim read it, but it seems pretty useful
Thanks
> I never learnt vJASS, just because I couldn't be bothered to learn it, but if I get time I might properly read through this tutorial and actually learn it.
Great!
> A: If a variable that has no value is used, it will stop the crash the current thread. Simply initialize your variables before you use them. Stops the crash of the current thread?
> Initializers are functions which are called when the game is loading. Are executed.
> However combining the basics of Jass and vJass as one entity is a really great idea if it's done properly.
And would you say this is done properly?
I would. The key here is to express yourself in short and precise sentences. Once sentences get too long the reader may quickly lose sense of structure and get confused. Also if you want readers to read and learn something that is completely new to them, again short and precise is the key. Too many informations apparently overflows the brain. You're handling both nicely.
As for the information you're providing I can't really find much to put a finger on. One however; are you sure that BJ stands for Blizzard Jass and not Blizzard.j
Updated with another paragraph or so to the end.
I think it's got all the detail it needs now.
> I would.
=)
> One however; are you sure that BJ stands for Blizzard Jass and not Blizzard.j
I've heard that it's Blizzard Jass more than blizzard.j.
And regardless, a *.j extension is used for Jass code anyway, so both could be valid.
Wow Romek, what a wonderful tutorial! Really helps people learn JASS good and faster then from Vexorian's turorial, although his turorial is still more detailed, but your is all the info people need in shortest as possible. I never tried vJASS, but I will start when you post the next part
Once again, great job!