Field Data Types
- Data Types should not be confused with Archetypes. An archetype is a descriptor for a class, which is a collection of different fields.
- When a field is created via the CLI, it must be declared as to what type of data it will contain.
- Variables also have datatypes. There are two ways to declare a variable in HeroScript
- Immediately declare its type
- Declare it with the VAR keyword, in which case its type will not be assigned until the first time that it is set to a particular value, and then that type will become its permanent datatype.
TODO: Add description of the FSGUID and RawData data types.
|NONE||--||For more information, see the NONE section below.|
|ARRAY||ARRAY||Maintains a fixed length group of values of a given type. This is like LIST but much more efficient -- it specifies exactly how many elements it has to be ahead of time (and this cannot change). The default values will be the default for whatever type is contained within the array. |
|--||ASSOCIATION||A complex datatype which can only be used to declare variables, and not field types. An association has three sub data elements, which are accessed as though they were subfields: |
|BOOLEAN||BOOLEAN||Logical field which can be set to "true" or "false". The default is false.|
|CUBIC||CUBIC||Maintains a fixed sized three-dimensional group of a given type. This is like a TABLE, but with three dimensions. The default value will be the default for whatever type is contained within the group. |
|ENUM||ENUM||A field of this type will contain a value that references one constant on an enumerated list of constants (called, interestingly enough, the "enumerator list"). The default value is the first value of the enumeration, if it has values. Otherwise it is an invalid value. Please see the section on Enumeration for more info.|
|FLOAT||FLOAT||This is a standard 32-bit floating point value, such as 0.0, 2.6, 3.2037. Leading zeroes are not required. The default value is 0.0|
|GUICONTROL||--||For defining GUIXML elements.|
||This is a field which contains a class (and thus that class's fields). In this way it is possible to have any arbitrary complex nested structure in various classes. For more information, please see the section below on subclasses. Default is the default of all the individual subfields|
|ID||ID||This field stores a numerical identifier which corresponds to some sort of definition, node or prototype, etc. Although it is an integer value, it can be a very large one (UINT - unsigned 64-bit integer) so it is necessary to use fields/variables of type ID for all Global Unique Identifiers (GUID), as an integer will not do. The default value is 0.|
|INTEGER||INTEGER||A signed 64-bit integer which can be positive or negative. The default value is 0.|
||Please see the section on List Information. Defaults to an empty list.|
|LOOKUPLIST||LOOKUPLIST||A key/pair list. Please see the section on List Information. Defaults to an empty list.|
|NODEREF||NODEREF||Declares a pointer to a node. Note that if a class is not specified, a WHERE will be needed if it is desired to access fields on the node. Defaults to no reference (NONE)|
||Declares a noderef of a specific class, which makes it easier to access its fields. Important: A NODEREF type is a pointer to a node in the GOM, so setting a variable of type NODEREF is just changing what it points to - not creating a node or copying its values. Defaults to an invalid reference (NONE)|
|SCRIPTREF||SCRIPTREF||References a script. It can be set to a script name such as |
|STRING||STRING||An unlimited-length string value, such as "this is a string" or "27" or "Michelangelo". To determine its current length via HeroScript, check <string>.length. Defaults to an empty string ("")|
|TABLE||TABLE||Maintains a fixed sized two-dimensional group of a given type. Defaults to the default value of whatever type is contained within the table. |
|Date Time||Date Time||A type used to display or track the current date/time|
|Time Interval||TimeInterval||A literal time interval is of the form HH:MM:SS.SSS where HH is the hours, MM the minutes, and SS.SSS is the seconds (the fractional part is optional).|
|TIMER||TIMER||A collection of different types. Please see the TIMER section for more information.|
|VECTOR3||VECTOR3||Has fields: x, y, and z. Can be added/subtracted to other vectors, or multiplied/divided by a float. Operations affect all fields. Default is (0,0,0). |
In HeroEngine, each datatype is a "Union" type. The normal scalar types all use the same amount of memory. Complex types use more. If given a choice between using a simple data type or a complex one, it is more efficient to use a simple type.
Note: To convert an integer to a float, simply set it.
f as float f = 1 f = 0.3 f = .2 // Note that a leading zero is not required
There are many other options for Auto Conversion.
The NONE Data Type
- The variables can be cleared by setting them to NONE
- They can be checked in an expression using the equals/not-equals operators
- A noderef will be equal to NONE if:
- It is null; or
- The noderef is invalid; or
- The node that it referred to is no longer loaded in the game.
- A scriptref will be equal to NONE if:
- It is null; or
- The script reference is invalid.
- A noderef will be equal to NONE if:
Adding classes to nodes
There are different ways of either defining the class that a node is, or of adding additional fields to that node.
- When it is created, a node is defined as a particular class, such as with
CreateNodeFromClass(). This is its "base class".
- If a node's class is later modified, to inherit from another class, or fields are added to that class, then all existing nodes of that class will inherit the new fields.
- An existing node can have another class attached to it. This is called "glomming", and can be achieved with the
glomClass()function in HeroScript, or commands such as CNAC (Create Node Add Class) via the CLI.
- A node can have fields of type
class, which allow for it have subfields. These do not change or add to the classes of the node, but do allow for access to other fields via that node (see "subfields" below for more information).
It is also worth noting that while adding a class to a node will increase the number of fields available to that node, it will not add field *data* to that node. Adding the class is simply adding more locations in which data can later be stored.
If multiple classes are added to a node, and any of the classes have the same field names, then the node will only have *one* instance of that field. If it has never had the field before, the field is added with a NULL value. If it already had the field, and a new class is added which has the same field name, there is no change to that field's data on the existing node. The only difference will be that the node will then gain access to the other fields on that class which it didn't have yet.
Subfields -- Creating fields which are classes
Sometimes it may be desired to have a node which is of 1 or more classes, which also has access to other classes and fields, without being of those classes. Or, it might be desired to have a complex nesting structure where one node has many different possible types of data that it needs to keep separate, such as to have many different
damage fields which are capable of storing different data even though they're on the same node. The way this is accomplished is via subfields -- adding fields to the node which are of type
class, and thereby allowing access to those classes' fields, without affecting the data in the node's other fields.
In this way you could have any arbitrary complex nested structure in a class. For example, create a field definition:
: cfd "field_which_is_class", class "that_class_name"
Define a class which uses that field
: ccd <classholder>, asset; "field_which_is_class"
Add that created class to a node.
: mnac <node id>; <classholder>
And then it is possible to modify the node's subfields which were based on that class:
: MN <node id>; field_which_is_class.field_in_that_class_name=<value>
For a more specific example, there might be a noderef which is a pointer to a node of a nine-headed hydra. Each head can be in a different location, doing something different, and taking different amounts of damage. There are of course many different ways to accomplish this, but for the purpose of this example, one way might be create a class
hydra and as part of that class, create a field which was an array of class
hydrahead class would have its own fields such as
location. In this way, each of the nine heads would have its own field data.
: cfd heads, array 9 of class hydrahead // Create the "heads" field definition : mcdaf hydra, heads // Modify the "hydra" class to add the "heads" array : cnfc hydra // Create node ID #2, from class "hydra"
Then to access the subfields, the dot (.) connector would be used:
: mn 2; heads.damage=3 : mn 2; heads.damage=27 : mn 2; heads.location=(1, 3, 5)
This technique could be further extended, with subfields being defined to be of type class and having their own subfields, ad infinitum:
: mn 342; orc.arm.left.hand.damage=.5
Create a variable of type VECTOR3 and set its fields
// the old way v as vector3 v.x = 1.0 v.y = 2.0 v.z = 3.0 printV3(v) // or as of version 1.23 var v2 = (1.0, 2.0, 3.0)
would produce an output of:
(1.0, 2.0, 3.0)
printV3(v * v.y)
would result in:
(2.0, 4.0, 6.0)
(v*v) would result in an error. Vectors can only be multiplied or divided by floats, and can only be added or subtracted by other vectors.
ll as lookupList by string of integer ll["test"] = 3 if (ll has "test") // list uses remove <list> at <offset> remove ll entry "test" // or: remove "test" from ll .
l2 as lookuplist indexed by enum Tolkien of string l2[frodo] = "hmm" l2[bilbo] = "dork" remove frodo from l2 if (l2 has frodo) println("frodo="+l2[frodo]) .
Example field dataypes
- list of class foo
- lookup list of list of class fruit