![]() |
#1 | |
Free Software Terrorist
Technical Director
|
![]() Note: the only sites allowed to host this tutorial are wc3campaigns,wc3jass and thehelper, any other site doing so is stealing it
-- Introduction to JASS -- This is supposed to be a brief introduction to JASS and programming, you might or might not need GUI (triggers) knowledge, the point of it is to make you ready to start reading other tutorials, open source JASS scripts and even the manual at http://jass.sourceforge.net/doc/ so you start the learning curve. If you can already handle JASS this is not the tutorial for you. Also if you already know a programming language, the manual at http://jass.sourceforge.net/doc/ should be enough for you. My target is to make a practical JASS course, since there are already plenty of theorical tutorias. I will purposely skip some stuff, remember that this is just to start in JASS, and after that you will have to see Open source JASS scripts and other Tutorials. The only thing I will require for you is that you should already know how to extract files from a MPQ , search for that in the forums or google. But I don't want to ellaborate on that. The first step is to open World Editor, create a new map and then open the trigger editor, select the trigger 'Map initialization' and you will most probably see this: Trigger: ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Now we will have to set the battle field ready for battle, change the Event to A Player skips a cinematic, remove the actions and replace them with a text message action. Rename the trigger to JASS test. Trigger: Time to get started, go to the edit menu and Convert It to custom text. press OK. ![]() function Trig_JASS_test_Actions takes nothing returns nothing call DisplayTextToForce( GetPlayersAll(), "TRIGSTR_004" ) endfunction //=========================================================================== function InitTrig_JASS_test takes nothing returns nothing set gg_trg_JASS_test = CreateTrigger( ) call TriggerRegisterPlayerEventEndCinematic( gg_trg_JASS_test, Player(0) ) call TriggerAddAction( gg_trg_JASS_test, function Trig_JASS_test_Actions ) endfunction The first obstacle, what happened to the "This is a message" text? TRIGSTR_004 , reffers to a string in war3map.wts (the map strings) you are able to edit that file with the World Editor's File\Export/Import Strings... commands. But for practical reasons we will forget about that and just use the text we like. ![]() function Trig_JASS_test_Actions takes nothing returns nothing call DisplayTextToForce( GetPlayersAll(), "This is a message" ) endfunction //=========================================================================== function InitTrig_JASS_test takes nothing returns nothing set gg_trg_JASS_test = CreateTrigger( ) call TriggerRegisterPlayerEventEndCinematic( gg_trg_JASS_test, Player(0) ) call TriggerAddAction( gg_trg_JASS_test, function Trig_JASS_test_Actions ) endfunction Here we will see 2 functions , Trig_JASS_test_Actions and InitTrig_JASS_test. Each "Trigger" in the Trigger editor comes with a InitTrig_TRIGGERNAME (Spaces are replaced with _) function that is always called at the initialization of the map. In this case we are talking about the InitTrig_JASS_test function. In the contents of that function we will see these lines: ![]() set gg_trg_JASS_test = CreateTrigger( ) call TriggerRegisterPlayerEventEndCinematic( gg_trg_JASS_test, Player(0) ) call TriggerAddAction( gg_trg_JASS_test, function Trig_JASS_test_Actions ) For now we will say they are the Setup of a trigger that is saved under the gg_trg_JASS_test variable. A line to highlight is ![]() call TriggerAddAction( gg_trg_JASS_test, function Trig_JASS_test_Actions ) It says that the action function for the gg_trg_JASS_test trigger is Trig_JASS_test_Actions. So for now the only thing we will do is editing the contents of that function , keep the other function with no changes. We are now ready to start the course! 1. First Saving. Save the map, it shouldn't give you errors unless you made a major mistake when replacing the contents of the message, double check it. Most likely the message MUST be between quotes "" . If it doesn't give you errors, Test the map using the Test Map button. Now in game press Escape, it should show the "This is a message" text. This is the way we will test JASS for now. 2. Variables Tests The syntax for making a local variable inside a function is the following: local <VARIABLE TYPE> <VARIABLE NAME> Optionally you can initiate the local variable with a value local <VARIABLE TYPE> <VARIABLE NAME> = value Variable names should not start with a number, and may only have alpha numeric characters and _ Strings The first type we will use is string , a string is just text between "". The syntax does not care about what you use inside the string. We will call the variable "a" . It is time to say that JASS is case sensitive, so typing A is not the same as typing a , you can't use LOCAL nor Local for the declaration of the variable, you must use local. ![]() function Trig_JASS_test_Actions takes nothing returns nothing local string a="This is a message" call DisplayTextToForce( GetPlayersAll(), a ) endfunction In the example we just moved the message to the value of the variable named "a", then we used the variable in the Function that shows the message, we didn't put the variable name inside "". Test the map, it should work the same way it worked before. You can have multiple variables inside a function, but always make sure the declaration of the variables is before any other line. ![]() function Trig_JASS_test_Actions takes nothing returns nothing local string a="This is a message" local string b="This is other message" call DisplayTextToForce( GetPlayersAll(), a ) endfunction What's the point of having 2 variables if we don't use the second one? Let us copy the Message function call to create another one. Then replace a with b ![]() function Trig_JASS_test_Actions takes nothing returns nothing local string a="This is a message" local string b="This is other message" call DisplayTextToForce( GetPlayersAll(), a ) call DisplayTextToForce( GetPlayersAll(), b ) endfunction When you test the map it will show both messages. Changing the contents of a variable To change the value of a variable. We use a set statement. set <variable name> = ![]() function Trig_JASS_test_Actions takes nothing returns nothing local string a="This is a message" local string b="This is other message" call DisplayTextToForce( GetPlayersAll(), a ) set a="This is a new value!" call DisplayTextToForce( GetPlayersAll(), a ) endfunction Test the map, the result will be This is a message This is a new value! Concatenation When using String variables you will have to use concatenation. Concatenation is an operation that puts strings together. Concatenation happens when you use the + operator between string values. ![]() function Trig_JASS_test_Actions takes nothing returns nothing local string a="hell" local string b="o" call DisplayTextToForce( GetPlayersAll(), a+b ) endfunction The result will show the "hello" message. You can use concatenation to unify a simple string value with a variable ![]() function Trig_JASS_test_Actions takes nothing returns nothing local string a="hell" call DisplayTextToForce( GetPlayersAll(), a+"o" ) endfunction The result is hello as well. You can concatenate multiple strings. ![]() function Trig_JASS_test_Actions takes nothing returns nothing local string a="AA" call DisplayTextToForce( GetPlayersAll(), "The value of a is "+a+" so live with it!" ) endfunction The result is The value of a is AA so live with it! Null strings A null strings might be null or "" . But a string variable can't be set to null , instead you use "" to reset the string to empty Arrays An array is basically a group of variables, that share the same name, but have different indexes. To declare arrays we use: local (ARRAY_TYPE) array (ARRAY_NAME) You can't initialize an array when declaring it. The way to use or change the value of an array is different, instead of just using the name of the variable, you have to use the name of the variable + [index] Indexes of arrays might go from 0 to 8191. ![]() function Trig_JASS_test_Actions takes nothing returns nothing local string array a set a[0]=" zero " set a[1]=" one " call DisplayTextToForce( GetPlayersAll(), "Concatenation with values of arrays: "+a[0]+a[1] ) endfunction The result in game is: "Concatenation with values of arrays: zero one " We will go back into arrays later. Important note about variables Variables must have a value before you access them. Try this function: ![]() function Trig_JASS_test_Actions takes nothing returns nothing local string a call DisplayTextToForce( GetPlayersAll(), "The value of a is "+a+" so live with it!" ) endfunction When you go to the game and press escape, nothing will happen, trying to access the a variable will just halt the trigger. Integers Now we will test integer variables, Integers are just numbers without a fractionary part that might be negative. I2S This function will convert an integer into a string, usage is I2S(integer) ![]() function Trig_JASS_test_Actions takes nothing returns nothing local integer a=34 call DisplayTextToForce( GetPlayersAll(), "Value of a is "+I2S(a) ) endfunction The result : Value of a is 34 Integer operators + : addition * : Product / : division - : substraction As a standard, computer languages and math in general consider product and division before addition and substraction. (): You can use parenthesis to determine which operation goes first. Operators can be used between variables, values (constants), and/or functions. ![]() function Trig_JASS_test_Actions takes nothing returns nothing local integer a=1 local integer b=2 local integer c=3 local integer d=4 local integer r set r= a+b+c*d call DisplayTextToForce( GetPlayersAll(), "1. r= "+I2S(r) ) set r= (a+b+c)*d call DisplayTextToForce( GetPlayersAll(), "2. r= "+I2S(r) ) set r= a+(b-c) call DisplayTextToForce( GetPlayersAll(), "3. r= "+I2S(r) ) set r= (a+b+c+d)/5 call DisplayTextToForce( GetPlayersAll(), "4. r= "+I2S(r) ) endfunction Result: 1. r= 15 2. r= 24 3. r= 0 4. r= 2 Explanation: 1+2+3*4 = 1+2+12 = 15 (1+2+3)*4 = 6*4 = 24 1+(2-3) = 1 + (-1) = 0 (1+2+3+4) / 5 = 10 / 5 = 2 Division between integers will not round the values. ![]() function Trig_JASS_test_Actions takes nothing returns nothing local integer r = 28 / 5 call DisplayTextToForce( GetPlayersAll(), "r= "+I2S(r) ) endfunction Result: r= 5 28 / 5 equals to 5.6 , if it rounded the result would be 6 instead of 5 Back to the past Concanenation between integers works if you first convert them to strings. ![]() function Trig_JASS_test_Actions takes nothing returns nothing local integer a = 2 local integer b = 3 call DisplayTextToForce( GetPlayersAll(), I2S(a)+I2S(b) ) endfunction The result will be 23. If you want the result to be an integer, you will have to convert the string back to an integer. For this we use S2I(string) . ![]() function Trig_JASS_test_Actions takes nothing returns nothing local integer a = 2 local integer b = 3 local string st= I2S(a)+I2S(b) local integer new = S2I(st) set a= new-1 call DisplayTextToForce( GetPlayersAll(), I2S(a) ) endfunction The result is 22 Arrays Integer arrays, or arrays of any type work the same local (ARRAY_TYPE) array (ARRAY_NAME) The index of an array between [] (remember?) is an integer, but make sure you don't use negatives there. ![]() function Trig_JASS_test_Actions takes nothing returns nothing local integer a = 2 local integer array b set b[a] = 5 set b[a+1] = 7 set b[a+2] = b[a+1]-b[a] set b[1356] = b[a+2] + 335 call DisplayTextToForce( GetPlayersAll(), I2S(b[a]) ) call DisplayTextToForce( GetPlayersAll(), I2S(b[a+1]) ) call DisplayTextToForce( GetPlayersAll(), I2S(b[a+2]) ) call DisplayTextToForce( GetPlayersAll(), I2S(b[1356]) ) endfunction The result is: 5 7 2 337 Reals Reals are numbers, unlike integers they allow(have) fractionary-decimal parts. Operators and rules for reals are mostly the same as for integers. But division will not round them. We use R2S(real) to convert a real into a string Integers can be used as reals unless it is a return value (see the functions section) ![]() function Trig_JASS_test_Actions takes nothing returns nothing local real r = 28 / 5 call DisplayTextToForce( GetPlayersAll(), "r= "+R2S(r) ) endfunction Oops, we have 5.000 as result, what happened? 28 and 5 are integers, and the game reads them an integers, so / between 28 and 5 is an integer division, after that the result (5) is converted to real and it becomes 5.0 Changing some stuff: ![]() function Trig_JASS_test_Actions takes nothing returns nothing local real r = 28.0 / 5.0 call DisplayTextToForce( GetPlayersAll(), "r= "+R2S(r) ) endfunction The result is 5.600 Note that even if the results are shown with 3 decimal digits, internally the game considers much more digits, it is R2S() which makes the string to only show 3 decimals. Again: ![]() function Trig_JASS_test_Actions takes nothing returns nothing local real r1 = 28.0 / 5 local real r2 = 28 / 5.0 call DisplayTextToForce( GetPlayersAll(), "r1= "+R2S(r1) ) call DisplayTextToForce( GetPlayersAll(), "r2= "+R2S(r2) ) endfunction Result: r1= 5.600 r2= 5.600 For the operation to be done in the "real world", only 1 of the values operated has to be real. While integers may be used as reals, reals can't be used as integers. To use a real as an integer we use the R2I(real) function. ![]() function Trig_JASS_test_Actions takes nothing returns nothing local real a = 28.0 / 5.0 local integer r =R2I(a) call DisplayTextToForce( GetPlayersAll(), "r= "+I2S(r) ) endfunction The result is: r= 5 R2I does not round. You can combine integer operations and real operations ![]() function Trig_JASS_test_Actions takes nothing returns nothing local integer a=5 local integer b=7 local real c = (a*b) / 34.55 set a = R2I(c) + 5 call DisplayTextToForce( GetPlayersAll(), "a= "+I2S(a) ) endfunction The result is going to be 6. Boolean Booleans are either true or false values. There are functions that return boolean values, and there are also comparissions that return boolean values. There is no B2S function that translates a boolean into a string, you can always make one yourself though. But for this course, I will just talk about booleans, then advance into the statements section where we will use booleans. Comparisions == equal to != not equal to They are used with any type, even between booleans. If 2 values match == will return true, and != false, if they don't match == will be false and != true . < Less than <= Less than or equal >= Greater than or equal > Greater than These comparisions will only work between integers and reals. Operators not: The boolean becomes its opposite not true will return false not(true) will return false not false will return true not true will return false and: Both values are true true and true returns true false and false returns false false and true returns false true and false returns false or: Any of the values is true true or true returns true false or false returns false false or true returns true true or false returns true We will say more about boolean values and variables later. Other types There are other types, integer,real, string and boolean are just the native types. The other types are handle and its descendants, handles are objects like unit, trigger, multiboard. Declaring and setting variables of these types works the same way as for the other types, And you can use == and != comparisions for them. For more information about how to use them you'd have to extract scripts\common.j and scripts\blizzard.j from war3patch.mpq , and browse those files with a text editor looking for the type declarations and functions usage. Or you can use the api browser at http://jass.sourceforge.net/doc/ (see the end of the function tests section for more information) Global Variables We used local variables on the tests, but the Global variables also exist. A Global variable is not specific for the function and can be used on every function. To declare a global variable you would have to go the map script, and find the globals declaration section like: ![]() globals ... <VARIABLE TYPE> <VARIABLE NAME> [= VALUE] <ARRAY TYPE> array <ARRAY NAME> constant <Constant type> <Constant name> = <value> ... endglobals But , there is no (easy) way to edit the map script and keeping the triggers. The globals endglobals would only be used if you are making a map script from scratch, or if it is common.j or blizzard.j But we are interested into using global variables in JASS scripts inside just the trigger editor. There are many ways of making global variables. Variable Editor Inside the trigger editor go to the Edit\Variables... menu command. In the variable editor go to Edit\New variable . For this test use glob as name and integer as type. Press ok. Global variables declared with the variable editor get the udg_ preffix automatically. (udg stands for USER DEFINED GLOBAL) ![]() function Trig_JASS_test_Actions takes nothing returns nothing set udg_glob = 34 call DisplayTextToForce( GetPlayersAll(), "a= "+I2S(udg_glob) ) endfunction The result will be 34. You can also have global arrays. Go back to the variable editor, double click the glob variable, this time mark the array checkbox. The size thing for the array, is actually the maximum index that will be automatically initialized with the initial value you set at map initialization. Press OK. A message saying that you used glob without it being an array will appear, this time press ok. Don't forget to edit the script ![]() function Trig_JASS_test_Actions takes nothing returns nothing set udg_glob[7] = 34 call DisplayTextToForce( GetPlayersAll(), "a= "+I2S(udg_glob[7]) ) endfunction The result would be 34. Global variables and arrays work the same as locals, but they are global so they work on every function. Other ways to create variables, are the trigger editor, Each "trigger" in the trigger editor automatically creates a trigger global variable gg_trg_TRIGGERNAME (with spaces replaced with _ ) Also Rects(Regions) and sounds created with the editor, get gg_rct_RECTNAME and gg_snd_SOUNDNAME . You can make world editor save Preplaced units, destructables and items in global variables. For this you have to go to the trigger editor and find an action/event/condition that uses an unit. For this example, the Specific unit event - A unit dies. First place a sorceress in the terrain. Edit No unit select variable and click the select unit button. You are back to the world editor window and you can click the sorceress. Save the map, and now convert the new trigger to custom text. ![]() function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing endfunction //=========================================================================== function InitTrig_Untitled_Trigger_001 takes nothing returns nothing set gg_trg_Untitled_Trigger_001 = CreateTrigger( ) call TriggerRegisterUnitEvent( gg_trg_Untitled_Trigger_001, gg_unit_hsor_0002, EVENT_UNIT_DEATH ) call TriggerAddAction( gg_trg_Untitled_Trigger_001, function Trig_Untitled_Trigger_001_Actions ) endfunction Note the gg_unit_hsor_0002 variable used in the event registration, this is our new unit variable that when the map starts points to the preplaced sorceress (0002 might be another number in your case) We will use GetUnitName(unit) to know the name of an unit as a string ![]() function Trig_JASS_test_Actions takes nothing returns nothing call DisplayTextToForce( GetPlayersAll(), GetUnitName(gg_unit_hsor_0002) ) endfunction No chance to fail, the result will be Sorceress. 2. Function Tests You already have an idea of what a function is. ![]() function Trig_JASS_test_Actions takes nothing returns nothing call DisplayTextToForce( GetPlayersAll(), "This is a message" ) endfunction Is a function. A function is a group of actions and statements that can be called when needed. It might or might not need arguments, And it may return a value. When we say: ![]() function Trig_JASS_test_Actions takes nothing returns nothing We are talking about a function that has/receives no arguments ("takes nothing") and does not return a value ("returns nothing"). That's what the syntax word "nothing" means. Arguments A function might take only 1 argument. When a function just needs one argument we use: ![]() function Trig_JASS_test_Actions takes <ARGUMENT TYPE> <ARGUMENT NAME> returns nothing But if it takes more than 1 argument we use a comma to separate them: ![]() function Trig_JASS_test_Actions takes <ARGUMENT1 TYPE> <ARGUMENT1 NAME>, <ARGUMENT2 TYPE> <ARGUMENT2 NAME> returns nothing function Trig_JASS_test_Actions takes <ARGUMENT1 TYPE> <ARGUMENT1 NAME>, <ARGUMENT2 TYPE> <ARGUMENT2 NAME>, <ARGUMENT3 TYPE> <ARGUMENT3 NAME> returns nothing The arguments are separated by commas (,) . until the word "returns" which means the end of the argument list. Inside the function, you can use its arguments as if they were local variables. Functions may or may not have local variables declared inside. Calling a function For calling functions we use: call <Function Name>(Argument1,Argument2,...,ArgumentN) In cases where the function takes no arguments, it is just: call <Function Name>() Functions can call other functions, AS LONG AS the other functions are written ABOVE the function in the map script. If a function is above other function in the same 'trigger', the other function may call it. But there is no safe way to make sure the contents of a custom trigger will be added to the map script before the contents of another trigger. The Frozen Throne editor, comes with a "Custom Script section" feature, When you are at the trigger editor, click on the map's name, and you will be able to see the Custom Script section, that's a place for functions. The contents of the Custom Script section are added to the map script before the contents of every trigger. An example wouldn't hurt: ![]() function Msg takes string s returns nothing call DisplayTextToForce( GetPlayersAll(), s ) endfunction function ShowMessage takes nothing returns nothing call Msg("This is a message!") endfunction function ShowSum takes integer a, integer b returns nothing local integer r=a+b call Msg("r= "+I2S(r)) endfunction function Trig_JASS_test_Actions takes nothing returns nothing call ShowMessage() call ShowSum(33,45) endfunction The result will be: This is a message! r = 78 -- Note for example that the Msg function does the job of showin messages to all the players without you having to write that everytime. That's the reason functions exist, to save you time, And this is the biggest advantage JASS has over GUI. Functions are your friends, remember. Whenever you feel like using the same group of lines over and over and again and again, make those lines into a function. Functions with return values Functions may have arguments, and return values. ![]() function <Function Name> takes <argument list> returns <Return type> I explained argument list above, Return Type is nothing when you don't need it to return any value, or it might be a type when it does need a return value. Keep in mind that if a function has a return value, it has to return something. How do you say what value a function returns? Returning values When a function is declared to return a value, you must use the return statement: return(<value>) or return <value> But this return stament MUST be the last line of the function. Making a function return a value When you want to get a value from a function, you can use: <Function Name>(Argument1,Argument2,...,ArgumentN) When you need a value of the type returned by the function. (if the function takes nothing, use <Function Name>() One note: Functions that have return values can also be with call <Function Name>([arguments]) , but you won't know the returned value. An example once again: ![]() function Msg takes string s returns nothing call DisplayTextToForce( GetPlayersAll(), s ) endfunction function SumAsString takes integer a, integer b returns string call Msg("SumAsString called") return I2S(a+b) endfunction function Triple takes integer n returns integer return(3 * n) endfunction function Trig_JASS_test_Actions takes nothing returns nothing call Msg("The triple of 3 is "+I2S(Triple(3))) call Msg("The sum of 156 and 768 is "+SumAsString(156,768)) call SumAsString(3,7) endfunction The result will be: The triple of 3 is 9 SumAsString called The sum of 156 and 768 is 924 SumAsString called Note that SumAsString is called before Msg is called, The arguments are evaluated before calling Msg. Warning Return values of "returns real" functions must be real, if you use an integer it will give you compile errors. Back into global variables This test will show how a global variable works, First of all create 2 global integer variables: r1 and r2 with the variable editor (Look the variables tests section) ![]() function Msg takes string s returns nothing call DisplayTextToForce( GetPlayersAll(), s ) endfunction function AddSubstract takes integer a, integer b returns nothing set udg_r1=a+b set udg_r2=a-b endfunction function Trig_JASS_test_Actions takes nothing returns nothing local integer a=14 local integer b=56 call AddSubstract(a,b) call Msg("a= "+I2S(a)) call Msg("b= "+I2S(b)) call Msg("The Addition is "+I2S(udg_r1)) call Msg("The Substraction is "+I2S(udg_r2)) endfunction The result: a= 14 b= 56 The Addition is 70 The Substraction is -42 Note that we are using udg_r1 and udg_r2 on both functions, and that they actually work as if they were 2 return values of the function Non user declared functions There are 2 files in war3patch.mpq : scripts\common.j and scripts\blizzard.j extract them with a mpq editor and open both of them with a Text Editor like notepad. You will see a lot of stuff, they both have global declarations, but common.j also has type declarations. In these you can find all the non native types. But the important part is for now is the functions. Scroll down in both files until you get to the functions. The other way would be using the api browser at http://jass.sourceforge.net/doc/ or any other tool that browses the functions (There are plenty of JASS editors out there) common.j is the most important file because it has the native functions, Functions that are inside the game, probably coded in C++, you can't see their contents with just a text editor as you can see the contents of blizzard.j functions. common.j functions are the ones that allow you to do stuff in warcraft III , blizzard.j functions just call the native functions from common.j to assist you a little more. The blizzard.j functions work the same way as your functions, blizzard.j is used by the game before your map's script so you can call them as you call any function. common.j functions are natives and are declared like functions, they are only different in their declaration because they have the name native instead of function. But when calling them you use the same rules as when calling any function. Some of the things we have used are common.j natives: ![]() native R2I takes real r returns integer native I2S takes integer i returns string native R2S takes real r returns string native S2I takes string s returns integer And some are blizzard.j functions: ![]() //=========================================================================== function DisplayTextToForce takes force toForce, string message returns nothing if (IsPlayerInForce(GetLocalPlayer(), toForce)) then // Use only local code (no net traffic) within this block to avoid desyncs. call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, message) endif endfunction Every text after // is a comment. We note that DisplayTextToForce uses DisplayTextToPlayer, and we only needed to show a text for a player, Player 1. Should replace the Msg function to make it call DisplayTextToPlayer ![]() native DisplayTextToPlayer takes player toPlayer, real x, real y, string message returns nothing We need a player argument, and to figure out what x and y mean. There is no problem about them though, because DisplayTextToForce used 0 for x and y. But what to use for player? How do we make it work for Player 1? At this time we have no idea, so we will figure out that the easy way. We make a new trigger temporarily and try to find an action that uses player, then choose Player 1 (red) Trigger: Convert to Custom Text: ![]() function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing call ClearSelectionForPlayer( Player(0) ) endfunction //=========================================================================== function InitTrig_Untitled_Trigger_001 takes nothing returns nothing set gg_trg_Untitled_Trigger_001 = CreateTrigger( ) call TriggerAddAction( gg_trg_Untitled_Trigger_001, function Trig_Untitled_Trigger_001_Actions ) endfunction Player 1 is Player(0) !, Since the 0 is between () we would think that it is a function. Player is actually a native: ![]() constant native Player takes integer number returns player Note that the argument number is 0 for Player 1 , this is something to remember, in JASS Player 1 is Player(0) , Player 2 is Player(1) , Player 12 is Player(11) . Now let's change the Msg function ![]() function Msg takes string s returns nothing call DisplayTextToPlayer(Player(0),0,0, s ) endfunction function Trig_JASS_test_Actions takes nothing returns nothing call Msg("This is a message, again") endfunction The result is : This is a message, again You are now ready to make your own functions, and to call functions you made, functions in blizzard.j or natives from common.j . But you aren't ready for programming in JASS yet, 1 more sections is remaining. 3. Statement Tests Programming in JASS wouldn't be programming without Conditional structures and Loops. if then ... elseif ... else ... If we want an action to be done, only if a condition is true, we use an if statement: ![]() if <boolean> then ...actions... endif When the boolean value is true, the actions are executed. ![]() function Trig_JASS_test_Actions takes nothing returns nothing if true then call DisplayTextToForce( GetPlayersAll(), "Is true" ) endif endfunction The result will be Is true. But: ![]() function Trig_JASS_test_Actions takes nothing returns nothing if false then call DisplayTextToForce( GetPlayersAll(), "Is true" ) endif endfunction From now we will use this function: ![]() native GetRandomInt takes integer lowBound, integer highBound returns integer I decided that the best way to show how statements work in JASS is by having fun using problems from the first lessons of an Algorithm Class. Given the numbers a and b between 1 and 10. Show the major number. If they are equal say so ![]() function Msg takes string s returns nothing call DisplayTextToPlayer(Player(0),0,0, s ) endfunction function Trig_JASS_test_Actions takes nothing returns nothing local integer a=GetRandomInt(1,10) local integer b=GetRandomInt(1,10) call Msg("a="+I2S(a)) call Msg("b="+I2S(b)) if (a>b) then call Msg(I2S(b)) endif if b>a then call Msg(I2S(b)) endif if (a==b) then call Msg("they are the same number") endif endfunction NOTE: If the game is choosing the same numbers every time, you have to go to File\prefferences\Test map and disable the Fixed Random Seed option. Note that a>b is between () and b>a isn't, it doesn't matter if you use () or not, unless you want something to be evaluated before, but using the comparision inside () seems easier to read and In theory should make the compiler parse it faster. In game press Escape. The result is going to be different every time. For example: a=4 b=6 6 or a=7 b=5 7 or maybe: a=3 b=3 they are the same number. else There is another way to use if and it is when you combine it with an else statement: ![]() if <boolean> then ..then actions.. else ..else actions.. endif The else actions will be executed when the boolean is false. If the boolean is true, then the 'then actions' are executed. Given a number from 1 to 100 determine if it is a multiple of 2 ![]() function Msg takes string s returns nothing call DisplayTextToPlayer(Player(0),0,0, s ) endfunction function Trig_JASS_test_Actions takes nothing returns nothing local integer a=GetRandomInt(1,100) local integer div=(a / 2) call Msg("a="+I2S(a)) if (div*2==a) then call Msg(I2S(a)+" is a multiple of 2") else call Msg(I2S(a)+" isn't a multiple of 2") endif endfunction Explanation, a / 2 is integer division between a and 2 , if you multiply the result by 2 a number will appear, if it is the same as a then a was a multiple. 48/2=24 ; 24*2=48 9/2=4 ; 4*2=8 elseif There is also chance for having to use elseif inside an if statement ![]() if <boolean0> then ..actions.. elseif <boolean1> then ..actions.. elseif <boolean2> then ..actions.. ··· else ..actions.. endif An if may have as many elseif as you want, I think there is a limit but it was something like 26. It may also not have any elseif statements. The else statement is optional. Because of logical reasons, an if block is unable to have more than 1 else statement. When first boolean is not true, will evaluate the second boolean, if it is not true it will evaluate the third and so and so until the else statement or the endif statement. Whenever it finds a true value it will execute the actions and forget the rest of the contents of that if statement. Given a number from 0 to 10 , display its literal name. ![]() function Msg takes string s returns nothing call DisplayTextToPlayer(Player(0),0,0, s ) endfunction function Trig_JASS_test_Actions takes nothing returns nothing local integer a=GetRandomInt(1,100) call Msg("a="+I2S(a)) if (a==0) then call Msg("zero") elseif (a==1) then call Msg("one") elseif (a==2) then call Msg("two") elseif (a==3) then call Msg("three") elseif (a==4) then call Msg("four") elseif (a==5) then call Msg("five") elseif (a==6) then call Msg("six") elseif (a==7) then call Msg("seveb") elseif (a==8) then call Msg("eight") elseif (a==9) then call Msg("nine") else call Msg("ten") endif endfunction The result is something like: a=1 one or a=7 seven * Challenge 1: Given a, b and c, numbers from 1 to 10 , display the maximum loops What if you have to repeat something? and you don't have time to type all that stuff, or you don't know how many times you have to repeat it? JASS only has one loop statement: ![]() loop ..actions.. exitwhen <boolean1> ..actions.. exitwhen <boolean2> ..actions.. exitwhen <boolean3> ... endloop Every action inside loop and endloop will be repeated until any of the exitwhen statements gets a true value. loop ... endloop statments may have as many exitwhen statements as you want, they even might have no exitwhen statement at all (This should be combined with waits, else the game will freeze for some time and halt the process) And the exitwhen statement might be anywhere as long as it is inside the loop.. endloop block. Counters A counter is a number that has an initial value and controls a loop unti the number becomes higher or lower than another value, allowing you to control the number of times you will repeat something easily. Show "Hello World!" as many times as the given integer number from 1 to 5 indicates: ![]() function Msg takes string s returns nothing call DisplayTextToPlayer(Player(0),0,0, s ) endfunction function Trig_JASS_test_Actions takes nothing returns nothing local integer a=GetRandomInt(1,5) local integer i call Msg("a= "+I2S(a)) set i=1 loop exitwhen (i>a) call Msg("Hello World!") set i=i+1 endloop endfunction The result is like: a= 4 Hello World! Hello World! Hello World! Hello World! The integer variable i is the counter this time, the loop will be repeated until i is greater than a , and i is increased inside the loop. Waiting for condition For this test, we will use a wait native: ![]() native TriggerSleepAction takes real timeout returns nothing It interrupts the execution of a trigger until the timeout time ends. Also we will use this native: ![]() constant native IsUnitInRange takes unit whichUnit, unit otherUnit, real distance returns boolean Which will return true if the 2 units are at least as close as the distance argument says. Place 1 footman and a knight in the map. Make both preplaced units global variables (like the time we made a sorceress a unit variable, remember?) Make sure the knight and the footman are far enough away from each other. ![]() function Msg takes string s returns nothing call DisplayTextToPlayer(Player(0),0,0, s ) endfunction function Trig_JASS_test_Actions takes nothing returns nothing loop exitwhen IsUnitInRange(gg_unit_hkni_0003,gg_unit_hfoo_0002, 300 ) call TriggerSleepAction(1) endloop call Msg("The knight and footman are in range") endfunction Test the map. Press Escape. Move the footman close to the knight. Once the footman is close enough, It will say: The knight and footman are in range. Press Escape Again, It will instantly say that they are in range. Move the footman away from the knight, this time press escape 3 times, and order it to move close to the knight again, when it gets to the position, 3 messages saying that the knight and footman are in range will be shown. triggers may have multiple instances and they are threaded so this isn't weird. Other articles should elaborate more about this. Nested Statements You can have loop statements inside ifs, if statements inside loops , if statements inside other ifs and loop statements inside other loops. exitwhen inside an if statement which is inside a loop block is possible. exitwhen inside a loop that is inside another loop, will only exit the nested loop. Back to the past : return Whenever you need to exit a function, you can use the return statement , if the function had a return value, remember that the return statement should return something. BUT the function must have a return value at the end of the function too. Q: Is there an integer number between 1 and 5 that matches the statement: x! = a ? (when a is an integer number between 1 and 120) (In case there isn't one, don't show any message) Math note: x! is the Factorial of x which equals 1 * 2 * 3 * ... * x ![]() function Msg takes string s returns nothing call DisplayTextToPlayer(Player(0),0,0, s ) endfunction function Factorial takes integer x returns integer local integer i=1 local integer r=1 loop exitwhen (i>x) set r=r*i set i=i+1 endloop return r endfunction function Trig_JASS_test_Actions takes nothing returns nothing local integer x=1 local integer a=GetRandomInt(1,120) call Msg("a= "+I2S(a)) loop exitwhen (Factorial(x) ==a) if (x>5) then return endif set x=x+1 endloop call Msg(I2S(a)+" equals to "+I2S(x)+"!") endfunction Funny example, when you are in game, press esc and esc, until it finds a number that is a factorial of 1, 2 ,3 ,4 or 5. It should eventually find one. The only five numbers that would have a result are: 1 , 2, 6, 24 and 120. The example is good because it shows a return statement inside an if statement that is inside a loop statement. But a better way of solving the problem would be: ![]() function Msg takes string s returns nothing call DisplayTextToPlayer(Player(0),0,0, s ) endfunction function Trig_JASS_test_Actions takes nothing returns nothing local integer x=1 local integer a=GetRandomInt(1,120) local integer r=1 call Msg("a= "+I2S(a)) loop set r=r*x exitwhen (r>=a) or (x>5) set x=x+1 endloop if (r==a) then call Msg(I2S(a)+" equals to "+I2S(x)+"!") endif endfunction Recursion I said something about functions that call themselves. An example would be, again the Factorial : Given a number a between 1 and 10 , calculate its Factorial WITHOUT USING LOOP STATEMENTS ! The correct definition of n! is : 0! = 0 1! = 1 n! = n(n-1)! ![]() function Msg takes string s returns nothing call DisplayTextToPlayer(Player(0),0,0, s ) endfunction function Factorial takes integer x returns integer if (x<=1) then return 1 endif return x*Factorial(x-1) endfunction function Trig_JASS_test_Actions takes nothing returns nothing local integer x=GetRandomInt(0,10) call Msg("x= "+I2S(x)) call Msg(I2S(x)+"! equals to "+I2S( Factorial(x) )) endfunction This is, indeed, another way to make loops, but it depends on how the problem you'd want to solve is solved. Most of the times a loop is better but sometimes recursion is a great idea. ----- * Challenge 2: What is the integer part of the square root of a given integer number from 1 to 10000? (Do not use the square root native) * Challenge 3: Is a given number from 1 to 10000 prime? (prime numbers are only multiples of themselves and 1) * Challenge 4: Show the first n (n is a number from 5 to 30) numbers of the fibonacci sequence : (n==8) 0, 1, 1, 2, 3, 5, 8 , 13 * You don't have to make the challenges, but if you make the challenges yourself you will have some good practice. Inside this hidden block you can find the solutions to the challenges:
The end You are now ready to browse open source scripts, read more tutorials and the documentation at http://jass.sourceforge.net/doc/ I also made another tutorial that will tell you how to apply this knowledge in practical JASS : Triggers in JASS If you have any question, please make it at forums, PMing me wouldn't work too well because I am off line most of the time, if you make your question at forums, more people can help and learn from your question. This tutorial should only be present at the sites where I (vexorian) personally submitted it. |
|
![]() |
![]() |
Sponsored Links - Login to hide this ad! |
|
![]() |
#2 |
Free Software Terrorist
Technical Director
|
![]() Fixed a missing line in one of the examples. (thanks Anitarf)
__________________ |
![]() |
![]() |
![]() |
#3 | |
Taur is... MY SISTER!!!
Respected User
|
![]() Very good tutorial. Too bad I didn't have this when I was learning JASS. There are some small grammar errors, but thats ok.
__________________There is one very minor error, and it isn't even that big of a deal, but Quote:
|
|
![]() |
![]() |
![]() |
#4 |
User
Join Date: Mar 2003
Posts: 226
![]() |
![]() what makes you think it starts at 1?
|
![]() |
![]() |
![]() |
#5 |
User
Respected User
Join Date: Jul 2005
Posts: 593
![]() |
![]() wow. very nice tut. but it's so long i can't read it all.
__________________ |
![]() |
![]() |
![]() |
#6 |
Procrastination Incarnate
Development Director
|
![]() As far as I remember, the fibonacci sequence is only defined that each next number is a sum of two prequels, and the first two numbers are defined as you please. There are therefore infinite fibonacci sequences, but the most "common" is indeed 1,1,2,3,5... but I think you can easily have 1,3,4,7,11,18...
__________________Anyway, great tutorial, Lord Vexorian. |
![]() |
![]() |
![]() |
#7 |
User
Join Date: Mar 2003
Posts: 226
![]() |
![]() you can't just use any starting numbers, the initial ones are defined.
according to http://mathworld.wolfram.com/FibonacciNumber.html the 0th fibonacci number is 0 not 1. most people skip it because they start counting at 1, but c'mon we're programmers here we count from 0. |
![]() |
![]() |
![]() |
#8 | |
Free Software Terrorist
Technical Director
|
![]() Quote:
Some people say it should be 1,1 other peple say that it should be 0,1 . It doesn't really matter too much. |
|
![]() |
![]() |
![]() |
#9 |
User
Join Date: Jan 2004
Posts: 158
![]() |
![]() Woah, I' think I've actually learnt something from this
![]() Thanks man! ^_^ |
![]() |
![]() |
![]() |
#10 |
Posts: n/a
|
![]() Big thanks.. this was highly informative and made Jass look like another common programming language.. not that mumbo jumbo mysterious demonic letters i see whenever i try to convert any of my triggers to custom text. Really helped me alot. You deserve more rep but I can't give anymore.
Also, shows just how powerful the WE is... |
![]() |
![]() |
#11 |
User
Join Date: Aug 2003
Posts: 80
![]() |
![]() I know this tutorial has been up a while, I momentarily come on and off to check and to read and clearify things when I get rusty with triggers, but I also noticed 1 thing and I think it should be included in this tutorial. When converting to jass it is important that you know if your trigger is "Initially Turned ON" or not [this can be spotted by checking the checkmark beside "Initially on" which is located above the "Comment" section], because after you convert to jass I highly doubt there is a way to change the trigger to enable/disable, Of course you can always undo and everything, But if your trigger is not running Try to check to see if the Paper icon outline is faded gray or not, and you should always make a back up gui incase of this or other errors. Otherwise you'd have to include another trigger just to turn that trigger on or off again.
__________________Enable, and Initially On are both different. Enable means Letting the trigger to be used in the game, and disabling it will take it out of the game (Means it can never be used wether turned on or off) Initially On means the trigger is turned on from the start, Unchecking this will allow the trigger to be turned off from the start. Thx for this tutorial I learned much out of it!. |
![]() |
![]() |
![]() |
#12 |
Free Software Terrorist
Technical Director
|
![]() I updated my JASS tutorial to use my JASS tags...
I will soon reupload the solutions. Alert: The only sites allowed to have this tutorial are Wc3campaigns, Wc3jass.com and thehelper.net , any other site holding is actually stealing this tutorial |
![]() |
![]() |
![]() |
#13 |
( ~)>
Respected User
Join Date: Feb 2005
Posts: 959
![]() ![]() ![]() |
![]() Hopefully the triggers will be using the [trigger] tag .....
__________________ |
![]() |
![]() |
![]() |
#14 |
.
Respected User
|
![]() http://www.wc3modforge.com/forum/showthread.php?t=10177
__________________Here's a thief. And Vex, you know what I think of this tutorial, posted it before, simply wonderful. |
![]() |
![]() |
![]() |
#15 |
User
Join Date: Dec 2005
Posts: 2
![]() |
![]() set udg_Level17Units[1]='H00B'
set udg_Level17Units[2]='u002' set udg_Level17Units[3]='h00C' set udg_Level17Units[4]='n008' set udg_Level17Units[5]='n00B' set udg_Level17Units[6]='h00D' set udg_Level17Units[7]='o000' set udg_Level17Units[8]='e000' set udg_Level17Units[9]='n009' set udg_Level17Units[10]='H00E' set udg_Level17Units[11]='h00F' set udg_Level17Units[12]='n00A' set udg_Level17Units[13]='h00G' set udg_Level17Units[14]='u003' set udg_Level17Units[15]='u004' can anyone tell me where to get a list of these unit id codes of length 4? thanks so much. |
![]() |
![]() |
![]() |
Thread Tools | Search this Thread |
|
|