Expressions and Data References

Overview
Expressions in HeroScript are similar to most other programming languages. They can be anything from a simple number to a complex mathematical formula involving calls to other functions. They are only valid as parts of statements.
There are also various different kinds of data references in HeroScript. For example, how to access the data in a timer field, a subfield, and an array, all warrant some additional explanation, which is included on this page.
Literals
These are the ways of referencing different types of data.
 integer: 123
 float: 3.14, 0.23, .7
 time interval: interval: 0:30:00.000
 string: "hello"
 vector3: (1.0, 0.0, 0.0) ** as of HeroEngine 1.23
String tokens
There are also various string tokens such as the following:

$$
: Inserts a "$" character in a string 
$Q
: Inserts a doublequote " in a string 
$R
: Inserts a return character <cr> in a string 
$T
: Inserts a tab character in a string
System Variables
Please see the section on System Variables.
Node/Class Field References
Given that myVar is a variable of a type with a class that has field1, and field1 is of a class type that has field2, subfields are accessed as follows:
myVar.field1.field2
Timer field references
Given that n is a reference to a node, and that node has a class with the field exampleTimer of any timer type, the following fields may be retrieved:
n.exampleTimer.firstEnabled  Date Time 
n.exampleTimer.lastEnabled  Date Time 
n.exampleTimer.lastFired  Date Time 
n.exampleTimer.fireCount  Integer 
n.exampleTimer.enabled  Boolean 
n.exampleTimer.elapsed  Time Interval 
n.exampleTimer.remaining  Time Interval 
WallClockTimers have a fireTime field which can be read or set. It is a combination DateTime such as SYSTEM.TIME.NOW
. Other timers have an interval field which can be read or set to a TimeInterval.
The remaining field can be set to a Time Interval but only applies until the timer fires (does not change interval).
List References
Given that myVar is a variable of a list type, the following gives the first element of the list:
myvar[1]
Operators
Operator  Name  Operand data type(s) 

+  add  integer, float, string 
  subtract  integer, float 
*  multiply  integer, float 
/  divide  integer, float 
+=  add and assign  integer, float, string 
=  subtract and assign  integer, float 
*=  multiply and assign  integer, float 
/=  divide and assign  integer, float 
not  logical inverse  boolean 
and  logical and  boolean 
or  logical or  boolean 
is exactly  test class on node  node  produces boolean 
is kindof  test class on node  node  produces boolean 
= or ==  equal  integer, float, string, node, class, boolean  produces boolean 
>  greater than  integer, float, string  produces boolean 
>=  greater than or equal  integer, float, string  produces boolean 
<  less than  integer, float, string  produces boolean 
<=  less than or equal  integer, float, string  produces boolean 
!= or <>  not equal  produces boolean 
mod  produce remainder  modulus operator: returns the integer remainder 
&  ^ ~ << >>  bitwise operations  Same as C. See http://en.wikipedia.org/wiki/Bitwise_operation 
Operator Precedence
Expressions are processed from left to right, except in specific precedence cases.
Level  Operator  Name 

highest  .  dot 
IS KINDOF IS EXACTLY 

not ~  
boolean "not" bitwise "not" unary negation  
mod * / 
mod multiplication division  
+  
addition subtraction  
<< >> 
bit shift left bit shift right  
has < > <= >= 
has less than greater than less than or equals greater than or equals  
== != 
equals not equal to  
&  bitwise "and"  
^  bitwise "exclusive or"  
  bitwise "or"  
and  boolean "and"  
or  boolean "or"  
lowest  = += = *= /=  assignment 
It is worth noting that in some cases, evaluation will stop before the entire expression is processed. For example, if there are multiple AND operators in an expression, which would all need to be TRUE simultaneously, then if any single one of them results as FALSE, the Here's Journey engine won't bother evaluating the others.
For example, suppose that trying to check the value of a field on a noderef will result in a script error if the NodeRef is None. So it is desirable to check that the node exists:
function demo (n as noderef of class x) if n != None then if n.name = "Bob" kill(n) . . .
A faster way to do this would be:
if n != None and n.name = "Bob" kill(n) .
So, even though checking n.name
would generate an error if n
was None, the engine won't get that far. However, writing the above statement as:
if n.name="Bob" and n!=None
then it *would* generate an error, since the engine would be evaluating the n.name
part first.
The same principle (processing stops when it becomes unnecessary) is true for the OR operator, except that it is reversed. For example, if there are multiple expressions being compared with OR, then the first one that returns a value of TRUE means that the rest can be skipped.
Examples
if i mod 7 . if x == 2 . if y <= 27 . if n is kindof ExampleClassName .
println("runLimit = "+SYSTEM.EXEC.RUNLIMIT.timeString) runLimit = 0:0:0.5