Post by DragonComplex on Aug 12, 2009 2:39:56 GMT -5
What is Jass
The JASS scripting language provides an extensive API that gives programmers control over nearly every aspect of the game.
It can, for example, manipulate the terrain. It has a syntax similar to Turing and Delphi, although, unlike those languages, it is case sensitive, the JASS syntax checker tends to crash and is somewhat slow.
This is what a JASS trigger would look like:
Unit will die on Region and Revive timer will start!
Revive timer finished and revive unit with checking unit
Before moving on there are a few things you must know:
* Global Declarations
* Types
* Functions
* Statements
* Expressions
* Miscellaneous
* Type Casting
* Library Functions
Before we get into anything else i want to take some time i talk about a few simple things that you will come across with Jass.
* The List is not complete there are more but I'm just going to talk about the simple ones *
Global Declarations
We define a JASS program or script to be a series of *.ai and *.j files that make up either a runnable map trigger script or an AI script.
* A map script is responsible for initializing callbacks or triggers that are executed when certain events occur within a game. In addition, the map script generally initializes the map by creating the initial units and setting the initial properties of the map. The files that compose a map script are common.j, Blizzard.j, and war3map.j (loaded in that order).
* An AI script is responsible for the continuous execution of building, training, and attacking commands by a single computer player within a game. The files that compose an AI script are common.j, common.ai, and a user-defined AI script (e.g., human.ai, elf.ai, etc.).
common.j, Blizzard.j, and common.ai are located in the Scripts/ directory in the MPQ, but they can be overridded by placing them within a *.w3m. We call the declarations in these files library functions (and variables) since they are loaded with every map/AI script respectively. See Library Functions for more information.
war3map.j is located in each *.w3m map file. It is automatically generated by the World Editor each time you save your map (so if you edit it directly, it will be overwritten when you edit your map again in the World Editor). However, you can edit segments of it directly in the World Editor by creating a trigger and using the "Convert to Custom Text" option in the "Edit" menu.
Types
JASS has a type system that helps to ensure correctness of assignments and expressions (i.e., that you don't assign strings to integer variables or try to add two units together). There are several native types which are predefined, but users can also declare subtypes of the basic native types. An array of any type can also be declared.
Native Types
integer - integer variables can hold the range of integral numbers ranging from -2147483647 to 2147483647.
real - real variables can hold rational numbers (i.e., floating-point or fractional numbers). One assumes the values are 32-bit and conform to the IEEE Standard 754 Floating-Point standard (someone want to verify this?).
boolean - boolean variables can take on the values true or false. Boolean values are returned by boolean operations and predicates to "if" statements must be boolean types.
string - strings variables hold a series of characters. The value of a string variable may be null in which case the variable refers to nothing and it is illegal to use it (except to assign a value to it).
handle - a handle variable is basically a "pointer". It refers to some data structure that is internal to Warcraft III which you can not manipulate directly. All user-defined types inherit from handle. For example, a unit is a subtype of handle that points to some unit data-structure in the game. Any handle variable can be assigned the value null, which means it refers to nothing.
Functions
A function is declared in the following manner:
See Global Declarations for more information. Remember that you can not call functions you have not yet declared (no forward references), but you can call a function within itself (i.e., recursion is legal).
If a function declaration is prefixed with constant, like the following:
then you can not call non-constant functions within the function body. (Nevertheless, note that you can still use the set statement in the body to alter function arguments, so it is not really "constant" in the typical sense; it is more of a hint to the programmer than anything else)
Statements
Each function body contains a series of statements; one statement per line. This section describes what each statement does:
he set statement assigns a new value to a variable. In case (1), the variable is of a basic type (not an array). The expression must evaluate to a value of a type that conforms to the variable's declared type.
In case (2), the variable is an array. index may be any arbitrary expression that evaluates to an integer value, which will the the index into the array to reference. The expression must evaluate to a value of type that conforms to the array's basic type. E.G., if the variable was declared as a unit array then the expression must conform to the type unit:
The call statement is used to call a function (native or user-defined). arguments is a comma separated list of expressions which are passed to the function as its respective parameters. If the function is declared to take nothing as parameters, then the argument list is empty (e.g., call func_name()). The types of each argument expression must conform to the corresponding parameter type in the function's declaration. For example, a function with the prototype:
The return value of the function is discarded.
If Then Else
An if-then-else statement is a conditional branch that spans multiple lines. In each condition block (begun with an if and ended with a endif), each predicate expression is evaluated in turn (first the one in the if, then the one in the next elseif, etc.). Each must return a boolean value. The first predicate which evaluates to true causes its associated statements to be executed (the statements in the immediately proceeding sub-block). The remainder of the statements in the other sub-blocks are ignored.
Expressions
An expression is a segment of code that evaluates to a value (which has a type). Most statements require the use of expressions and variable declarations can be initialized to the value of an expression. Expressions can be arbitrarily nested within other expressions. This section describes the available expressions in JASS.
These expressions perform addition, subtraction, multiplication, and division on their two operands, and negation and identity on a single operand respectively. Multiplication and division have higher precedence than addition and subtraction; each pair has equal precedence, and they are evaluated left to right. Thus:
Is equivalent to:
Operands must either be integers or reals. If any operand is real, then the result is real, otherwise the result is an integer.
Comparison Operators
These expressions evaluate equality, inequality, greater than or equal to, less than or equal to, strictly greater than, and strictly less than, respectively.
Miscellaneous
ASS Comments are identical to C++ and Java style line comments. Any text following a // is ignored until the end of the line. For example,
White Space
Except for newlines, all whitespace is ignored. In other words, the level of indentation of declarations and statements and number of spaces/tabs between words is irrelevant. In addition, extra newlines are allowed anywhere (i.e., you can have empty blank lines).
However, each function signature and statement must span only one line. There is no way to continue a statement on a second line. This may have been violated in some places in this manual to make code more readable, but should be maintained if you want a correct script.
Type Casting
Type casting is possible in JASS by making use of what is called the return bug.
The return bug
Warcraft 3's JASS compiler and the world editor's parser allow return values different from the last return value of a function to be of any type no matter the return type it is supposed to return.
example of the return bug is the following:
As you should note, the return value of the function is integer, last return statement returns 0, but actually, the only return value the function will consider is return h , it will return the handle argument it just took.
This function will take a handle argument and return an integer that is the pointer to that handle.
This function will do the opposite it will take an integer and return the handle that is at the memory position the integer is pointing. It is also possible to type cast a handle value into a value of a handle descendant type:
Note that in this case, an extra return statement is not required.
As for the handle type, you can retrieve a handle descendant type value from an integer.
You can also type cast between different handle derived types, even if they don't share the same descendace tree.
There is no logic for doing this unless you want to save a value of certain handle derived type in a variable of another handle derived type. Doing this for the arguments of native functions is the same as giving them null values.
Type casting also works between native types, with some considerations.
If you are familiar with JAVA, you would assume this opperation will trunc the real, but that's not the case. The return value is different from the argument, and you can retrieve the real back with the inverse of this function, but it will crash if you give it a wron value.
Will return the pointer to the given string. Be careful with this operation, it seems that strings are recycled when they are no longer used by variables, and it won't consider typecasted integers. The inverse function will crash if you give it a wrong value.
Type casting between boolean and integer, will represent 1 for true and 0 for false,
Also, consider that the function that performs the type casting does not have to follow the patern of the functions stated here to work. The only thing required is the return value bug abuse.
For example:
Also take advantage of the return bug. As a matter of fact GetDyingDestructable is a function from blizzard.j.
Library Functions
Many native functions and constants are declared in common.j, common.ai, and Blizzard.j. This section will descibe several functions that are of note. Obviously, this section is incomplete; if you have written or have found a more thorough description of the native API, I would be happy to link it here. See the API Browser section for a listing of the API.
Important Note: Others have observed that the following functions in common.j do not function correctly when used in AI scripts:
* native functions that return string.
* native functions that take callbacks (code, trigger, boolexpr, etc.) as arguments such as triggers and enumerators (ForGroup, etc.).
* ExecuteFunc.
These functions work normally in map scripts. Also be aware that native functions declared in common.ai are obviously not available to map scripts.
There are 3 common.ai functions that work for map scripts, once you manually add them to common.j , those functions are GetUnitGoldCost , GetUnitWoodCost and GetUnitBuildTime
The current list of topics are:
* Threads
* Triggers
* Inter-Script Communication
* Enumerations
* Filters
Threads
Usually only a single thread of execution is started for the AI. New threads can be started in an AI script with the function declared in common.ai:
Map scripts, can simply use ExecuteFunc
This is it so far I'll have more soon!
The JASS scripting language provides an extensive API that gives programmers control over nearly every aspect of the game.
It can, for example, manipulate the terrain. It has a syntax similar to Turing and Delphi, although, unlike those languages, it is case sensitive, the JASS syntax checker tends to crash and is somewhat slow.
This is what a JASS trigger would look like:
Unit will die on Region and Revive timer will start!
[size=4]function Trig_Trigger_Conditions takes nothing returns boolean
if ( not ( GetUnitTypeId(GetEnteringUnit()) == 'hfoo' ) ) then
return false
endif
if ( not ( GetOwningPlayer(GetEnteringUnit()) == Player(0) ) ) then
return false
endif
return true
endfunction
function Trig_Trigger_Actions takes nothing returns nothing
call KillUnit( GetEnteringUnit() )
call CreateTimerDialogBJ( udg_DeathTimer, "TRIGSTR_001" )
call StartTimerBJ( udg_DeathTimer, false, 30 )
endfunction
//===========================================================================
function InitTrig_Trigger takes nothing returns nothing
set gg_trg_Trigger = CreateTrigger( )
call TriggerRegisterEnterRectSimple( gg_trg_Trigger, gg_rct_Region_Death )
call TriggerAddCondition( gg_trg_Trigger, Condition( function Trig_Trigger_Conditions ) )
call TriggerAddAction( gg_trg_Trigger, function Trig_Trigger_Actions )
endfunction[/size]
Revive timer finished and revive unit with checking unit
[size=4]function Trig_Revive_Conditions takes nothing returns boolean
if ( not ( CountLivingPlayerUnitsOfTypeId('hfoo', Player(0)) == 0 ) ) then
return false
endif
return true
endfunction
function Trig_Revive_Actions takes nothing returns nothing
call CreateNUnitsAtLoc( 1, 'hfoo', Player(0), GetRectCenter(GetPlayableMapRect()), bj_E )
endfunction
//===========================================================================
function InitTrig_Revive takes nothing returns nothing
set gg_trg_Revive = CreateTrigger( )
call TriggerRegisterTimerExpireEventBJ( gg_trg_Revive, udg_DeathTimer )
call TriggerAddCondition( gg_trg_Revive, Condition( function Trig_Revive_Conditions ) )
call TriggerAddAction( gg_trg_Revive, function Trig_Revive_Actions )
endfunction[/size]
Before moving on there are a few things you must know:
* Global Declarations
* Types
* Functions
* Statements
* Expressions
* Miscellaneous
* Type Casting
* Library Functions
Before we get into anything else i want to take some time i talk about a few simple things that you will come across with Jass.
* The List is not complete there are more but I'm just going to talk about the simple ones *
Global Declarations
We define a JASS program or script to be a series of *.ai and *.j files that make up either a runnable map trigger script or an AI script.
* A map script is responsible for initializing callbacks or triggers that are executed when certain events occur within a game. In addition, the map script generally initializes the map by creating the initial units and setting the initial properties of the map. The files that compose a map script are common.j, Blizzard.j, and war3map.j (loaded in that order).
* An AI script is responsible for the continuous execution of building, training, and attacking commands by a single computer player within a game. The files that compose an AI script are common.j, common.ai, and a user-defined AI script (e.g., human.ai, elf.ai, etc.).
common.j, Blizzard.j, and common.ai are located in the Scripts/ directory in the MPQ, but they can be overridded by placing them within a *.w3m. We call the declarations in these files library functions (and variables) since they are loaded with every map/AI script respectively. See Library Functions for more information.
war3map.j is located in each *.w3m map file. It is automatically generated by the World Editor each time you save your map (so if you edit it directly, it will be overwritten when you edit your map again in the World Editor). However, you can edit segments of it directly in the World Editor by creating a trigger and using the "Convert to Custom Text" option in the "Edit" menu.
Types
JASS has a type system that helps to ensure correctness of assignments and expressions (i.e., that you don't assign strings to integer variables or try to add two units together). There are several native types which are predefined, but users can also declare subtypes of the basic native types. An array of any type can also be declared.
Native Types
integer - integer variables can hold the range of integral numbers ranging from -2147483647 to 2147483647.
real - real variables can hold rational numbers (i.e., floating-point or fractional numbers). One assumes the values are 32-bit and conform to the IEEE Standard 754 Floating-Point standard (someone want to verify this?).
boolean - boolean variables can take on the values true or false. Boolean values are returned by boolean operations and predicates to "if" statements must be boolean types.
string - strings variables hold a series of characters. The value of a string variable may be null in which case the variable refers to nothing and it is illegal to use it (except to assign a value to it).
handle - a handle variable is basically a "pointer". It refers to some data structure that is internal to Warcraft III which you can not manipulate directly. All user-defined types inherit from handle. For example, a unit is a subtype of handle that points to some unit data-structure in the game. Any handle variable can be assigned the value null, which means it refers to nothing.
Functions
A function is declared in the following manner:
function func_name takes param_list returns return_type
[size=4] variable_declaration
variable_declaration
...
statement
statement
statement
...[/size]
endfunction
See Global Declarations for more information. Remember that you can not call functions you have not yet declared (no forward references), but you can call a function within itself (i.e., recursion is legal).
If a function declaration is prefixed with constant, like the following:
[size=4]constant function const_func takes integer a returns nothing
...
endfunction[/size]
then you can not call non-constant functions within the function body. (Nevertheless, note that you can still use the set statement in the body to alter function arguments, so it is not really "constant" in the typical sense; it is more of a hint to the programmer than anything else)
Statements
Each function body contains a series of statements; one statement per line. This section describes what each statement does:
[size=4]set variable = expression // (1)
set array_variable[index] = expression // (2)[/size]
he set statement assigns a new value to a variable. In case (1), the variable is of a basic type (not an array). The expression must evaluate to a value of a type that conforms to the variable's declared type.
In case (2), the variable is an array. index may be any arbitrary expression that evaluates to an integer value, which will the the index into the array to reference. The expression must evaluate to a value of type that conforms to the array's basic type. E.G., if the variable was declared as a unit array then the expression must conform to the type unit:
[size=4]call func_name(arguments)[/size]
The call statement is used to call a function (native or user-defined). arguments is a comma separated list of expressions which are passed to the function as its respective parameters. If the function is declared to take nothing as parameters, then the argument list is empty (e.g., call func_name()). The types of each argument expression must conform to the corresponding parameter type in the function's declaration. For example, a function with the prototype:
[size=4]function MakeUnitsDance takes region where, integer num,
string nameOfDance returns nothing[/size]
[size=4]call MakeUnitsDance(GetTriggeringRegion(), numGrunts*2, "salsa")
// Or
call MakeUnitsDance(myRegion, 100, MyChooseDanceFunction())
// Etc.[/size]
The return value of the function is discarded.
If Then Else
[size=4]if predicate then
statement
statement
...
elseif predicate then
statement
statement
...
elseif predicate then
...
else
statement
statement
...
endif[/size]
An if-then-else statement is a conditional branch that spans multiple lines. In each condition block (begun with an if and ended with a endif), each predicate expression is evaluated in turn (first the one in the if, then the one in the next elseif, etc.). Each must return a boolean value. The first predicate which evaluates to true causes its associated statements to be executed (the statements in the immediately proceeding sub-block). The remainder of the statements in the other sub-blocks are ignored.
Expressions
An expression is a segment of code that evaluates to a value (which has a type). Most statements require the use of expressions and variable declarations can be initialized to the value of an expression. Expressions can be arbitrarily nested within other expressions. This section describes the available expressions in JASS.
[size=4]expression + expression
expression - expression
expression * expression
expression / expression
- expression
+ expression[/size]
These expressions perform addition, subtraction, multiplication, and division on their two operands, and negation and identity on a single operand respectively. Multiplication and division have higher precedence than addition and subtraction; each pair has equal precedence, and they are evaluated left to right. Thus:
[size=4]a + b * c - d / e[/size]
Is equivalent to:
[size=4]( a + (b * c) ) - (d / e)[/size]
Operands must either be integers or reals. If any operand is real, then the result is real, otherwise the result is an integer.
Comparison Operators
[size=4]expression == expression
expression != expression
expression >= expression
expression <= expression
expression > expression
expression < expression[/size]
These expressions evaluate equality, inequality, greater than or equal to, less than or equal to, strictly greater than, and strictly less than, respectively.
Miscellaneous
ASS Comments are identical to C++ and Java style line comments. Any text following a // is ignored until the end of the line. For example,
[size=4]function my_function takes nothing returns nothing // This is a comment
local integer foo // This is a comment
// This is a comment
set foo = 10 // This is a comment
endfunction[/size]
White Space
Except for newlines, all whitespace is ignored. In other words, the level of indentation of declarations and statements and number of spaces/tabs between words is irrelevant. In addition, extra newlines are allowed anywhere (i.e., you can have empty blank lines).
However, each function signature and statement must span only one line. There is no way to continue a statement on a second line. This may have been violated in some places in this manual to make code more readable, but should be maintained if you want a correct script.
Type Casting
Type casting is possible in JASS by making use of what is called the return bug.
The return bug
Warcraft 3's JASS compiler and the world editor's parser allow return values different from the last return value of a function to be of any type no matter the return type it is supposed to return.
example of the return bug is the following:
[size=4]function H2I takes handle h returns integer
return h
return 0
endfunction[/size]
As you should note, the return value of the function is integer, last return statement returns 0, but actually, the only return value the function will consider is return h , it will return the handle argument it just took.
This function will take a handle argument and return an integer that is the pointer to that handle.
[size=4]function I2H takes integer i returns handle
return i
return null
endfunction[/size]
This function will do the opposite it will take an integer and return the handle that is at the memory position the integer is pointing. It is also possible to type cast a handle value into a value of a handle descendant type:
[size=4]function Handle2Unit takes handle h returns unit
return h
endfunction[/size]
Note that in this case, an extra return statement is not required.
As for the handle type, you can retrieve a handle descendant type value from an integer.
[size=4]function I2Item takes integer i returns item
return i
return null
endfunction[/size]
You can also type cast between different handle derived types, even if they don't share the same descendace tree.
[size=4]function Unit2Item takes unit u returns item
return u
return null
endfunction[/size]
There is no logic for doing this unless you want to save a value of certain handle derived type in a variable of another handle derived type. Doing this for the arguments of native functions is the same as giving them null values.
Type casting also works between native types, with some considerations.
[size=4]function Real2Int takes real r returns integer
return r
return 0
endfunction[/size]
If you are familiar with JAVA, you would assume this opperation will trunc the real, but that's not the case. The return value is different from the argument, and you can retrieve the real back with the inverse of this function, but it will crash if you give it a wron value.
[size=4]function String2Int takes string s returns integer
return s
return 0
endfunction[/size]
Will return the pointer to the given string. Be careful with this operation, it seems that strings are recycled when they are no longer used by variables, and it won't consider typecasted integers. The inverse function will crash if you give it a wrong value.
Type casting between boolean and integer, will represent 1 for true and 0 for false,
Also, consider that the function that performs the type casting does not have to follow the patern of the functions stated here to work. The only thing required is the return value bug abuse.
For example:
[size=4]function GetArrayMemberAsUnit takes integer i returns unit
return udg_IntegerArray[i]
return null
endfunction
function GetDyingDestructable takes nothing returns destructable
return GetTriggerWidget()
endfunction[/size]
Also take advantage of the return bug. As a matter of fact GetDyingDestructable is a function from blizzard.j.
Library Functions
Many native functions and constants are declared in common.j, common.ai, and Blizzard.j. This section will descibe several functions that are of note. Obviously, this section is incomplete; if you have written or have found a more thorough description of the native API, I would be happy to link it here. See the API Browser section for a listing of the API.
Important Note: Others have observed that the following functions in common.j do not function correctly when used in AI scripts:
* native functions that return string.
* native functions that take callbacks (code, trigger, boolexpr, etc.) as arguments such as triggers and enumerators (ForGroup, etc.).
* ExecuteFunc.
These functions work normally in map scripts. Also be aware that native functions declared in common.ai are obviously not available to map scripts.
There are 3 common.ai functions that work for map scripts, once you manually add them to common.j , those functions are GetUnitGoldCost , GetUnitWoodCost and GetUnitBuildTime
The current list of topics are:
* Threads
* Triggers
* Inter-Script Communication
* Enumerations
* Filters
Threads
Usually only a single thread of execution is started for the AI. New threads can be started in an AI script with the function declared in common.ai:
Map scripts, can simply use ExecuteFunc
This is it so far I'll have more soon!