ZZT-OOP Reference
The extended ZZT-OOP in ZZT Ultra is designed on the assumption that the original
syntax of the language is inviolate. Therefore, one should never be required,
in theory, to rewrite old code to work in ZZT Ultra.
All additions and enhancements to ZZT Ultra work to the benefit of both new users
and seasoned users of the language. The language should have the same general look
and overall functionality in ZZT Ultra as it did for OBJECT types in ZZT or Super ZZT.
Changes to syntax are designed to be inclusive to older conventions while providing
more capabilities.
The language, as before, is case-insensitive. Thus all commands, variables,
etc. have the same behavior if written in upper case, lower case, or some combination
of the two.
Old Commands
Text and Labels
@Name
@This sets the object's name. ZZT only used this as a marker for the
#SEND Name:Label and #BIND commands. ZZT Ultra sets the ONAME member variable
for the object, which is used in various #SEND and #DISPATCH commands.
:Label
This defines a label that serves as a destination for a sent or dispatched message.
ZZT Ultra lets the designer pick alphanumeric characters or underscores for the
label. It also permits dollar signs, which the original ZZT did not allow. This is
due to the need for messages to be sent or dispatched that cannot be confused with
the namespace established by original ZZT-OOP code. Many global variables, labels,
etc. in ZZT Ultra will have dollar signs to prevent any possibility of namespace
pollution with pre-existing ZZT worlds.
'Comment
A comment line does nothing; it is ignored by ZZT and ZZT Ultra.
Comment lines can serve an auxilliary purpose as "zapped" labels.
Text
Text shows up at the GUI label locations TOAST1 and TOAST2 (if defined) for text messages
that fit within 1-2 lines. If only TOAST1 is defined, a message is flashed only if it
is a single line in length.
Any text that is larger than what could be flashed at the TOAST labels is displayed
as a large "scroll" interface, which the user can navigate with arrows. The user must
close the scroll manually. During the time the scroll is open, object action is suspended.
When a large "scroll" interface is about to be displayed, ZZT Ultra sets the $SCROLLMSG
global variable to 1, indicating a scroll is about to be opened. This can be useful for
objects that might want to behave differently in preparation for such an implicit pause.
ZZT Ultra resets $SCROLLMSG to 0 after the scroll is closed.
$Text
This variation on text shows text within a scroll interface as centered instead of
left-justified, and white instead of yellow. Note that the dollar sign is actually
shown if there is only enough text to display a toast message.
!Label;Text
This acts as a button link within a scroll interface. The object is sent the message
if the user selects the link.
ZZT Ultra uses button links in other contexts, such as savegame restore, file selection,
and HLP file navigation. The exact behavior will depend on the context in which the scroll
interface is displayed:
- If link appears from an object's code during its tick iteration, the link
navigates to the message label within the same object, as if by #SEND.
- If link appears from a dispatched message routine, the link navigates to
the message label within the main type code via another dispatched message.
- If link appears from a HLP file, the link navigates to the same file.
!-Filename;Text
This acts as a "file button" link within a scroll interface (a HLP file). The HLP file
should open in the scroll interface if the user selects the link, provided that the file is
within a valid security sandbox.
The security sandbox is determined by the origin of the game file. If the game file
originated from a deployed (site) configuration, the link will work if Filename is
also present in the configuration. If the game file was part of a loaded ZIP archive,
the link will work if Filename is present within the same ZIP archive. If the game file
is a WAD, the link will work if Filename is embedded within the WAD. In all other contexts,
the button link will not work.
Movement
#GO Direction [Count]
/Direction [Count]
#IDLE
Basic object movement in a direction. The typical syntax only takes a direction,
but it has been noted that Super ZZT also permits the specification of a numeric count
after the direction. ZZT Ultra expands this count into multiple movement commands as
part of compilation (it is uncertain exactly how the Super ZZT engine handled this
internally).
Each time an object moves, it uses up a turn.
The object will attempt to push if it can.
If the object is unable to move (pushing included), it will block indefinitely at
the movement command until (a) the path becomes clear again, or (b) it is sent a message
to do something else.
The "/" syntax lets the user place multiple movement commands on a single line.
This allows one to "economize" complex movement sequences on relatively few lines.
The use of #IDLE is equivalent to /i, which uses up a turn without actually moving.
Curiously, the command #GO will block indefinitely when used with an idle direction,
which does not happen with /i.
#TRY Direction [Command]
?Direction [Count]
Attempted object movement in a direction. The typical syntax takes a direction and
an optional command if movement is not possible. It has been noted that Super ZZT
also permits the specification of a numeric count after the direction for the "?"
syntax. ZZT Ultra expands this count into multiple movement commands as part of
compilation (it is uncertain exactly how the Super ZZT engine handled this internally).
Each time an object moves, it uses up a turn. This turn is NOT used up if the alternate
command is executed instead of the attempted move.
The object will attempt to push if it can.
If the object is unable to move (pushing included), it will execute the command
(usually a jump label) if one is provided. If no command is provided, the object uses
up the turn and does not move.
The "?" syntax lets the user place multiple movement commands on a single line.
This allows one to "economize" complex movement sequences on relatively few lines.
ZZT-OOP allows multiple #TRY commands to be chained together on a single line, which
has the effect of creating multi-conditional movement attempts. When such a "chained"
configuration is present, the remainder of the line is ignored the moment a single
#TRY direction has successful movement.
Curiously, an idle direction always counts as a failed movement attempt. The
alternate command is ALWAYS executed (if one exists) if the move direction is idle.
#WALK Direction
In ZZT, this command sets the step direction for the OBJECT type. When the step
direction is anything other than idle, the object will move in the step direction until
it hits something. Note that this movement operation is independent of the normal
object's turn, which means the object could quite possibly get two movement turns if
it moves on its normal cycle and also moves on its "walk" turn.
If an OBJECT is unable to move in the step direction due to being blocked, it is
sent the THUD message if defined. For a ZZT world, an object will never push or squash
anything from movement as a result of walking--it stops if blocked by anything, including
pushables. For a Super ZZT world, on the other hand, #WALK-based movement is more synonymous
with #TRY, allowing push operations and stopping if pushing is impossible.
ZZT Ultra does not hard-code a type's walk behavior. Instead, it dispatches the
$WALKBEHAVIOR message to the code after the normal cycle for the object is completed,
if $WALKBEHAVIOR is defined. ZZT Ultra implements $WALKBEHAVIOR for the
OBJECT type as the movement behavior mentioned in the above paragraphs. There is
the possibility of using $WALKBEHAVIOR to perform actions other than simple movement.
This command is useful in ZZT Ultra even if $WALKBEHAVIOR is not defined, because
the step direction can be used in many diverse ways (e.g. firing, river movement).
Program Flow Control
#END
The function of #END depends on whether the interpreter is handling a normal object
cycle or a dispatched message.
- For a normal object cycle, the object goes into idle mode and stops
executing commands.
- For a dispatched message, the stack is popped and control is restored
to the end of the last dispatch command. If this was the last dispatched
message in the stack, the dispatch operation is finished.
#RESTART
Jump back to the program's start.
The actual "start" of the program is defined by the object type. Most of the time,
#RESTART leads to the actual first line of code. However, SCROLL and OBJECT
types define the #RESTART location to be the first line of the custom code portion.
#ENDGAME
This dispatches the message $ENDGAME to the main type code. The original behavior
would have invariably put the game into "game over" mode, but ZZT Ultra lets this message
be handled in different ways.
#DIE
The object dies and disappears from the grid. If the object had been sitting on top of a
special type/color combination, that type/color combination is restored in ZZT Ultra.
There is one exception to the restored type/color combination: if the type is OBJECT,
and the OBJECTDIEEMPTY property is 1. OBJECTDIEEMPTY equals 1 by default,
which causes #DIE for the OBJECT type to leave behind a WHITE EMPTY.
Object and Terrain Creation
#BECOME [Color] Type
The object "dies" and becomes a different type. Optionally, a color qualifier can be
used to create a type of a specific color.
The original behavior for this command kept most of the original internal member
variables. ZZT Ultra attempts to do the same, but there is now the possibility of
initializing the member variables of the new type within this command using type kwargs.
If the color of the object is black (0) at the time of the #BECOME statement, its
color is changed to white (15).
Note that ZZT and Super ZZT have separate color replacement policies for #BECOME. The
"ZZT" policy is to keep the UNDERCOLOR of the object if no color qualifier was
present for the type, and if the target type was identical to UNDERID. The
"Super ZZT" policy is to use the object's own color instead. This is controlled via
the BECOMESAMECOLOR property.
#PUT Direction [Color] Type
This spawns a new type, just next to the object, in the direction specified. Optionally,
a color qualifier can be used to create a type of a specific color.
The #PUT command usually overwrites the adjacent square with the type, but it
makes an exception if the adjacent square is pushable. The adjacent square, if
pushable, is pushed out of the way first before the new type is placed.
ZZT Ultra lets you initialize member variables of the new type using type kwargs.
If the color of the destination square is black (0) at the time of the #PUT statement,
its color is changed to white (15) just before the #PUT operation.
There is a world property, NOPUTBOTTOMROW, which controls whether #PUT considers
the bottom row of the board to be a valid destination. By default, NOPUTBOTTOMROW is
set to 1 for ZZT and 0 for Super ZZT.
ZZT Ultra offers special "pseudo-directions" for this command, called OVER and
UNDER. When these directions are used instead of an ordinary direction, an object
is placed over or under the placer. The object that ends up "underneath" the other
remains in an inactive, ghosted state until the object on top moves to a different
square.
#CHANGE [Color1] Type1 [Color2] Type2
Color1: Source color (optional)
Type1: Source type
Color2: Destination color (optional)
Type2: Destination type
This changes everything in the board matching the first type to the second type. Color
qualifiers are optional for both source and destination types.
ZZT Ultra lets you initialize member variables of the new type using type kwargs in the
destination type. Additionally, type kwargs can also be used in the source type to act as
a "change filter."
It is not possible to change the PLAYER type to anything else.
A change operation that transforms a non-status element into a status element will retain
the non-status element underneath the new status element only if it is nonblocking (i.e. a
"floor" type). If the non-status element is blocking, it is replaced by EMPTY before placing
the new status element.
Object Member Update
#CHAR Expression
This sets the character for the object. Note that this only works for objects if the
type has HASOWNCHAR set for the type.
During a $CUSTOMDRAW dispatched message, the #CHAR command has an alternate function:
it identifies the character used to render the type when it must be displayed. It is not
necessary to have HASOWNCHAR set for the type if a $CUSTOMDRAW handler is present.
However, if HASOWNCHAR is set and the $CUSTOMDRAW routine does not set a character,
the CHAR member variable is picked as the character to display by default.
If one wishes to set the CHAR member variable without updating the screen immediately,
one could write #SET .CHAR Expression instead of #CHAR Expression.
The original behavior for this command only let the user pick a constant number (0-255);
ZZT Ultra expands the functionality to allow for any expression.
#CYCLE Expression
This modifies the cycle delay for the object, which can be set from 1 to 255, with 1
being the fastest.
The original behavior for this command only let the user pick a constant number (1-255);
ZZT Ultra expands the functionality to allow for any expression.
Value Assignment
#SET LValue [Expression]
This command sets a global or member variable. The original behavior for this command
only allowed for setting global flags to boolean values (true), without the opportunity to
set the variable to an expression value.
To understand how this command sets variables in ZZT Ultra, see
Variables and LValues.
If the old #SET syntax is used (with no Expression present), ZZT Ultra sets a global
variable with the original ZZT or Super ZZT limits in mind. The property NUMCLASSICFLAGS
is checked against CLASSICFLAGLIMIT to see if the last-set global variable should be
overwritten. While such hard limits on flags were generally considered a bad thing in the
original engines, some worlds nonetheless rely upon flags being overwritten in this manner.
#CLEAR LValue
This command deletes a global or member variable. The original behavior for this command
only allowed for clearing global flags, which effectively set them to false. For ZZT Ultra,
one can remove almost any type of variable, global or member.
Attempting to #CLEAR a global variable reduces the property NUMCLASSICFLAGS by 1.
This is done to simulate the hard limits on flags in the original engines.
Messages
#LOCK
This "locks" an object against externally sent messages. This locking includes messages
sent as a result of built-in stimuli, such as TOUCH, SHOT, BOMBED,
THUD, and ENERGIZE.
Note that the lock state never "locks the object out of its own messages." If an object
sends itself a message using an ordinary #SEND or a #SEND ALL, the message is always
sent.
The lock state does not prevent dispatched messages from reaching an object type.
#UNLOCK
This "unlocks" an object, allowing externally sent messages.
#SEND Label
#Label
This command sends a message "to itself." The object's IP immediately jumps to the new
label if it exists. If the label does not exist, the IP skips the command.
#SEND Name:Label
#Name:Label
This command sends a message to one or more objects identified by @Name. This
can include the object sending the message. If the object does not have the label, the IP
skips the command after having sent the message elsewhere if necessary.
In addition to a name, the value of Name can also resemble one of these stock strings:
- ALL: Send message to all objects in the board, including the sender.
- OTHERS: Send message to all objects in the board, but excluding the sender.
- SELF: Send message to self only (same as ordinary #SEND).
#BIND Name
This serves as a code economizer. If an object has a specific name, "binding" this
object will effectively cause the object to behave according to the "bound" code instead
of its own. Usually, a bound object will only have a single line of code, which is the
#BIND command.
ZZT Ultra simply swaps out the object's existing code block pointer with the new block,
restarting IP in the process. If an ONAME member had been set, it is removed.
#ZAP Label
#ZAP Name:Label
This command replaces the first recognized instance of the label in the code with a
comment. This has the effect of causing a message that would normally hit this label to
reach the next label by the same name within the same code. Repeated "zaps" replace
downstream labels with comments as appropriate.
The Name:Label syntax applies the zap to object code identified by object name.
Like the original ZZT, ZZT Ultra replaces the label for every object that happens
to use the code block, including objects running the code after having executed a #BIND.
This is something that should be kept in mind when using #ZAP with bound objects.
The fact that ZZT Ultra object code can create and use an unlimited number of
dictionary-style member variables largely eliminates the need for new code to make use
of #ZAP to alter program behavior. #ZAP can still be useful, but member variables are now
a much more versatile option.
#RESTORE Label
#RESTORE Name:Label
This command works in the opposite way as #ZAP: the first recognized code comment
matching the label name is transformed back into a label. Repeated "zaps" replace
downstream comments with labels as appropriate.
The Name:Label syntax applies the restore to object code identified by object name.
Restoration of labels does not follow a last-in-first-out order--it follows
first-in-first-out-order under most circumstances. This means that restoring previous
iterative zap behavior with multiple zaps is only effective if every label is restored,
then zapped N number of times.
HOWEVER, it has been experimentally discovered that the original label search
algorithms used by ZZT are quite convoluted. The presence of numbers or other
special characters in the label can significantly alter the pattern-matching behavior.
See ZZT Objects and Behaviors for more
information.
Inventory
#GIVE Inventory Expression
This increases a specific inventory quantity by an integer amount. The original
syntax only allowed constant numbers for the quantity; ZZT Ultra takes any expression.
#TAKE Inventory Expression [Command]
This decreases a specific inventory quantity by an integer amount. The original syntax
only allowed constant numbers for the quantity; ZZT Ultra takes any expression.
If the player's inventory is not high enough to allow a successful #TAKE, the Command is
executed (usually a jump label) if it is present.
Projectile Launch
#SHOOT [SILENT] Direction
This shoots an enemy-owned BULLET in the specified direction. ZZT Ultra lets the
user insert the SILENT keyword before the direction to prevent the
OBJECT-shooting sound from playing.
Each time an object shoots, it takes up a turn.
This command hard-codes certain point-blank behaviors. PLAYER is dispatched RECVHURT,
and BREAKABLE is destroyed. OBJECT types are sent the SHOT label only if the
POINTBLANKFIRING property is set.
#THROWSTAR [SILENT] Direction
This shoots a STAR in the specified direction. ZZT Ultra lets the user insert
the SILENT keyword before the direction to prevent the OBJECT-shooting sound from
playing. This is not actually meaningful except to maintain consistency with syntax for
#SHOOT, because the original behavior did not generate a sound when a STAR was
launched.
Each time an object throws a star, it takes up a turn.
This command hard-codes certain point-blank behaviors. PLAYER is dispatched RECVHURT,
and BREAKABLE is destroyed.
Miscellaneous
#IF [NOT] Condition [THEN] Command
This statement tests for a variety of criteria. If the condition evaluates to nonzero
(or true), the command is executed. If the condition is not zero (or false), the command
is ignored. See Conditions for more information about conditional
expressions.
#PLAY String
The sound effect/music playback command takes an encoded string representing notes
and/or percussive sound effects. See Sound Playback for more
information about sound playback.
New Commands
New ZZT-OOP commands extend the functionality of the language. These commands
are not available to worlds originating from ZZT or Super ZZT; the compiler
effectively ignores them from the namespace in such cases. Only when processing
a ZZT Ultra-originated world file or object type definition will these commands
become available.
Calculation
#CHAR4DIR Direction Expression1 Expression2 Expression3 Expression4
This command sets the object's character to one of the numbers (0-255) calculated
from the four expressions, based on the preceding direction. The direction determines
which expression becomes the character:
- East: Expression1
- South: Expression2
- West: Expression3
- North: Expression4
- Idle: Undefined
Like #CHAR, this command has a secondary function during $CUSTOMDRAW.
#OFFSETBYDIR CoordPair LValue1 LValue2
CoordPair: Step coordinates (relative or polar)
LValue1: Provides starting X; Receives Result X
LValue2: Provides starting Y; Receives Result Y
This command offsets X and Y variables by relative coordinates (rectangular or polar).
The X and Y expressions must be LValues.
Use this command to calculate what coordinates would be after taking a "step."
#DIR2UVECT8 Expression LValue1 LValue2
Expression: Direction of resolution 8
LValue1: Receives X step
LValue2: Receives Y step
This command sets X and Y variables to step values represented by an 8-directional
number from the preceding expression. The X and Y expressions must be LValues.
An 8-directional number evaluates as follows:
-1 | X=0 | Y=0 | (idle) |
0 | X=1 | Y=0 | (east) |
1 | X=1 | Y=1 | (southeast) |
2 | X=0 | Y=1 | (south) |
3 | X=-1 | Y=1 | (southwest) |
4 | X=-1 | Y=0 | (west) |
5 | X=-1 | Y=-1 | (northwest) |
6 | X=0 | Y=-1 | (north) |
7 | X=1 | Y=-1 | (northeast) |
The resulting "unit vector" can be used in directional evaluation. Note, however,
that ZZT's standard movement and firing commands only allow for cartesian movement as
opposed to diagonal movement. Of course, ZZT Ultra is not as limited in how movement
and evaluation can occur, given its more powerful command architecture.
#ATAN2 CoordPair Expression LValue
CoordPair: Step coordinates (relative or polar)
Expression: Resolution for direction
LValue: Receives direction
This command reads a fine-grain arctangent based upon relative coordinates. The
coordinate pair represents a delta for which an accurate direction should be calculated.
The expression is the resolution for the resulting calculation.
Common "resolution" values are 256, 4, and 8. The resolution cannot exceed 256.
The result of #ATAN2 will always fall within the range of [0, resolution-1]. This means
that if a resolution of 4 is picked, the numbers returned will be one of 0, 1, 2, or 3,
which match the mathematical basis for legacy cartesian directions.
#RANDOM LValue Expression1 Expression2
LValue: Receives random number
Expression1: Range minimum
Expression2: Range maximum
This command sets an LValue to an integer ranging from a low number to a high number,
inclusive.
Change Appearance
#COLOR Expression
This command sets the object's color attribute to the expression, which must evaluate
to a number between 0 and 255. Color attribute breakdown is the same as typical CRT text
mode:
- Bits 0-3: Foreground color
- Bits 4-6: Background color
- Bit 7: Blink flag
Color is usually only meaningful for bits 0-3 when setting color for objects, because
the background color is nearly always taken from the terrain underneath an object, rather
than the object itself.
During a $CUSTOMDRAW dispatched message, the #COLOR command has an alternate function:
it identifies the color used to render the type when it must be displayed. In this context,
all bits of the color are meaningful, including background color and blink.
It is borderline criminal that ZZT-OOP did not originally support this command. Anyway,
here it is.
#COLORALL Expression
This command is similar to #COLOR. The only difference is that #COLORALL sets all 8 bits
of the color, regardless of the color of the type underneath. This can be useful for types
with the FULLCOLOR attribute set.
#DRAWCHAR CoordPair Expression1 Expression2
CoordPair: Coordinates
Expression1: Character code or string
Expression2: Color attribute
This command draws a character (first expression) and color (second expression) to the grid
at the specified coordinates. This is only a momentary drawing operation--types and colors in
the board are not affected.
The first expression can be either a character code (number) or a string.
#ERASECHAR CoordPair
This command redraws a character at the specified coordinates, showing the permanent type at
the grid square. This operation is generally used to erase decorative content drawn via
#DRAWCHAR.
#GHOST ObjectPointer Expression
ObjectPointer: Object pointer expression to "ghost"
Expression: Ghosted status
The original ZZT engines required that a status element be linked to the board grid itself.
ZZT Ultra expects the same, for the most part. However, it is possible to temporarily "ghost"
a status element, allowing it to remain within the board metadata without actually showing up
visibly. The #GHOST command sets or clears the "ghost" status of an object. The expression
determines status: 0=clear, 1=set.
When a visible status element is ghosted, it disappears from the grid. When this happens,
other objects can move to the vacated spot without interaction. All movement commands applied
to ghosted status element succeed; there is no grid interaction.
When a ghosted status element is "un-ghosted," it reappears in the grid. When this happens,
any object that had existed at the same spot is destroyed. Note that color information is not
retained during ghosted status; one should invoke #COLOR or #COLORALL after an "un-ghosting"
operation.
Program Flow Control
#EXTRATURNS Expression
This command tweaks how many turns an object can take during the same iteration. It only
affects the object being executed currently, and only for this iteration.
By default, each object gets exactly one turn, which is used up when it moves, shoots, goes
idle, or dies. Less-deterministic events can also end turns early, such as the display of a
scroll interface or a change in paused status.
An example of how #EXTRATURNS could be used:
#EXTRATURNS 3
#SHOOT E
#SHOOT W
#SHOOT N
#SHOOT S
The above code fires bullets in all directions on a single iteration. Only the last
#SHOOT command will end the iteration for the object.
#DISPATCH Label
This command dispatches a message to the label found in the main type code, if such a
label exists. Execution continues immediately afterwards in the object that dispatched
the message.
#DISPATCHTO ObjectPointer:Label
ObjectPointer: Object pointer expression (the "target")
Label: Target label
This command dispatches a message to the label found in object code associated with
the object pointer. See Variables for more information about object
pointers. Nothing happens if the object pointer is invalid or the label is missing in
the target code.
Execution continues immediately afterwards in the object that dispatched the message.
#SENDTO ObjectPointer:Label
ObjectPointer: Object pointer expression (the "target")
Label: Target label
This command sends a message to the label found in object code associated with the object
pointer. See Variables for more information about object pointers.
Nothing happens if the object pointer is invalid or the label is missing in the target code.
Like the #SEND command, #SENDTO respects the lock status of the object.
#DONEDISPATCH
This command serves a special function for messages dispatched to objects. Normally,
the internal processing state of the interpreter is discarded immediately after a
dispatched message ends, returning control to the previously executing code in the interpreter.
The IP of the object as it executes on its normal real-time iteration is retained despite any
dispatched messages that might have passed over the same code.
#DONEDISPATCH alters the processing state such that the IP is remembered by the object after
the message ends. The command sets the number of turns remaining to 1, and when the last turn
is used up later, the object's next iteration will proceed at that location instead of the
original IP.
#SWITCHTYPE CoordPair Type1 Label1 Type2 Label2 Type3 Label3 ...
CoordPair: Evaluation coordinates
TypeN: Nth type
LabelN: Jump label (if TypeN matches)
This command acts as a "switch statement" for type matching. The type at the coordinate
pair is compared with the following types. For the first type that matches, control jumps
to the label immediately following the type.
If no type matches, the statement is ignored.
Given the length of some of these statements, it is beneficial to use one or more
backslashes as line-continuation characters.
#SWITCHVALUE Expression ExpressionValue1 Label1 ExpressionValue2 Label2 ...
Expression: Evaluation expression
ExpressionValueN: Nth expression constant
LabelN: Jump label (if ExpressionValueN matches)
This command acts as a "switch statement" for value matching. The expression
is compared with the following expression values, which can only be numeric or string
constants (not parenthetical expressions). For the first value that matches, control
jumps to the label immediately following the value.
If no value matches, the statement is ignored.
Given the length of some of these statements, it is beneficial to use one or more
backslashes as line-continuation characters.
#SETPLAYER ObjectPointer
This command identifies which object in the board is the "player" object. In nearly all
circumstances, this is the one and only PLAYER type in the board. ZZT Ultra allows one
to set the player to any object in the board.
Various commands within ZZT-OOP implicitly refer to the player's position and other
information when conducting calculations. #SETPLAYER is one way that ZZT Ultra code can set
momentary, unconventional target locations, or perhaps select other playable characters.
#PAUSE
This sets the game state to paused. During a paused state, objects do not iterate in
real-time, but dispatched messages are still processed.
While the state is paused, the main type code receives the dispatched message $PAUSED
for every frame of action. Note that these messages are handled very frequently, often much
more frequently than what the game speed would process for real-time object iterations. The
main reason for this is to allow the underlying code to rapidly respond to any action that
requires immediate attention while paused, such as time-sensitive events.
#UNPAUSE
This sets the game state to normal, allowing objects to iterate in real-time. The main
type code stops receiving $PAUSED dispatched messages.
Advanced Movement
#SETPOS ObjectPointer [OVER/UNDER] CoordPair
ObjectPointer: Object pointer expression (the "source")
CoordPair: Destination coordinates
This command sets an existing object's position to a different location within the board,
identified by the coordinate pair.
If there is an object at the destination, it is destroyed to make way for the one moved in
its place (unless the source and destination are the same, in which case, nothing happens).
If the object pointer is invalid, nothing is moved to the destination. Note that any object
at the destination is still destroyed. Thus "#SETPOS 0 CoordPair" can be used to remotely
kill any existing status element.
This command can be used to move the object currently being executed. Unlike the standard
move commands, though, the object will not use up a turn in the process. In this way, an
object can move in more complicated ways than were possible in ZZT or Super ZZT.
ZZT Ultra offers special "pseudo-directions" for this command, called OVER and
UNDER, which can optionally precede the coordinate pair. If these directions are
used, the object is moved over or under an object at the destination instead of
destroying it. The object that ends up "underneath" the other remains in an inactive,
ghosted state until the object on top moves to a different square.
#FORCEGO Direction
This is a "nuclear" version of #GO, which unequivocally destroys any object in its path
when moving. Movement will also occur even if the destination square is impassible or
unpushable. If the square is pushable, the object moves on top of it instead of pushing it.
There is a context when #FORCEGO will not work--if the movement would place the object off
the board. If this happens, no move occurs and the next command is executed.
This command always uses up a turn.
#PUSHATPOS CoordPair Direction
CoordPair: Coordinates of push application
Direction: Push direction
This command executes a single "push" operation at the coordinates in a specific direction,
if a push is possible.
Note that nothing needs to exist behind the actual "push" coordinates--it is entirely
possible to execute a push at a "phantom" location.
#SMOOTHTEST ObjectPointer Expression1 Expression2 Expression3
ObjectPointer: Object pointer expression (the "source")
Expression1: Magnitude of step (256 -> 1 square)
Expression2: Direction of step (resolution of 256)
Expression3: Single-axis limit (256 -> 1 square)
This command prepares a "smooth" trajectory towards a destination. The origin is the object
at the specified pointer, and the destination is indicated by a magnitude (the first
expression) and a direction (the second expression). The actual step taken along this vector
is limited to have a single-axis maximum (the third expression).
The values of the magnitude and limit are somewhat oversized. Most of the time,
a unit of 1 in ZZT signifies a single grid square's length. But the magnitude and limit in this
command are treated as if there are 256 units per single grid square length. Thus a value
of 256 indicates 1 square ahead, 512 indicates 2 squares ahead, and 384 indicates 1.5 squares
ahead.
The direction has a resolution of 256, unlike the typical legacy cartesian direction resolution
of 4. The #ATAN2 command can help generate directions at this resolution.
Once the vector has been calculated, the results are placed in four global variables:
- $DESTX: Destination whole X-coordinate of object if move is taken.
- $DESTY: Destination whole Y-coordinate of object if move is taken.
- $FRACX: Fractional X-coordinate of object if move is taken.
- $FRACY: Fractional Y-coordinate of object if move is taken.
#SMOOTHMOVE ObjectPointer
ObjectPointer: Object pointer expression (the "source")
This command executes a move operation on an object in a similar fashion to #SETPOS. The
movement coordinates used as the destination are $DESTX, $DESTY, $FRACX, and $FRACY, as
generated by the last call to #SMOOTHTEST.
One would use #SMOOTHTEST and #SMOOTHMOVE to have an object move in a "clean" linear vector,
possibly along a diagonal path or non-integer step. During individual move operations, the
object being moved tracks its own "fractional" coordinates internally with the members .FX
and .FY.
If it seems like black magic for a gridded ZZT object firmly rooted in integer cell
alignment to be able to move in a partial fashion, keep in mind that the fractional components
of the movement constitute an abstraction only. Unless #SMOOTHMOVE or other manual calculations
are used to keep track of fractional components of movement, any such abstractions can simply be
ignored when convenient. All objects MUST exist within a unique integer X and Y pair,
but fractional components have no representation visibly, and have no meaning in collision
detection.
Group Movement
#GROUPSETPOS CoordPair ArrayVarName
CoordPair: Step coordinates (relative or polar)
ArrayVarName: Array of object pointers
This command executes a special "group move" operation on multiple objects at the same time.
The objects are tracked via an array of object pointers. The coordinate pair, indicating the
destination of the move, is relative to the object being iterated (which should, in theory, be
included within the array identified by ArrayVarName). Thus the object invoking #GROUPSETPOS
acts as an "anchor point" for the entire group.
Group movement in this context behaves similar to #SETPOS in that it overwrites anything
at the destination.
The organization of individual objects within the group does not need to have a specific order.
It is not even necessary to have the objects be packed together without gaps, although this is
generally a good idea. As long as an object has its pointer represented in the array, group
movement will move the object by the appropriate step.
One can add or remove group members at any time by expanding or shrinking the size of the
array. One can remove group members by simply killing them, or by setting the object pointer
location within the array to 0 (this does not kill the object; it merely "frees" it from the
group).
#GROUPSETPOS does not take up a turn. It should be noted that all objects part of the group
will continue to execute their own turns, independent of any movement that might have occurred
that had moved the entire group. The individual objects can move, fire, or perform other actions
on their own while being simultaneously moved around by proxy.
#GROUPGO CoordPair ArrayVarName
CoordPair: Step coordinates (relative or polar)
ArrayVarName: Array of object pointers
This command executes a special "group move" operation on multiple objects at the same time.
There are several differences between this command and #GROUPSETPOS...
- The move operation takes up a turn.
- Single-step cartesian movement prompts ZZT Ultra to perform a "rim test." The "rim" is the
block of leaders-first objects in the group that will contact a potentially pushable edge. If
every part of the "rim" destination can be pushed, all is pushed, and the move succeeds.
- Diagonal movement and longer-than-1-step movement patterns do not perform a "rim test."
Instead, every destination point is checked. The move will only succeed if every destination
point is not blocked; pushability is not evaluated.
- If unable to push or move, the turn ends and IP remains at the #GROUPGO statement until a
movement can be taken. In this way, the command resembles #GO.
Unlike #GROUPSETPOS, #GROUPGO is not designed to overwrite blocking terrain or objects.
One should think of it as the "larger" version of #GO.
#GROUPTRY CoordPair ArrayVarName [Command]
CoordPair: Step coordinates (relative or polar)
ArrayVarName: Array of object pointers
Command: Alternate command (optional)
This command executes a special "group move" operation on multiple objects at the same time.
There are several differences between this command and #GROUPSETPOS...
- The move operation takes up a turn if move is successful.
- Single-step cartesian movement prompts ZZT Ultra to perform a "rim test." The "rim" is the
block of leaders-first objects in the group that will contact a potentially pushable edge. If
every part of the "rim" destination can be pushed, all is pushed, and the move succeeds.
- Diagonal movement and longer-than-1-step movement patterns do not perform a "rim test."
Instead, every destination point is checked. The move will only succeed if every destination
point is not blocked; pushability is not evaluated.
- If unable to push or move, the alternate command is executed, and a turn is not taken up.
In this way, the command resembles #TRY.
Unlike #GROUPSETPOS, #GROUPTRY is not designed to overwrite blocking terrain or objects.
One should think of it as the "larger" version of #TRY.
#GROUPTRYNOPUSH CoordPair ArrayVarName [Command]
CoordPair: Step coordinates (relative or polar)
ArrayVarName: Array of object pointers
Command: Alternate command (optional)
This command executes a special "group move" operation on multiple objects at the same time.
There are several differences between this command and #GROUPSETPOS...
- The move operation takes up a turn if move is successful.
- No "rim test" is performed; every destination point is checked. The move will only
succeed if every destination point is not blocked; pushability is not evaluated.
- If unable to move, the alternate command is executed, and a turn is not taken up.
In this way, the command resembles #TRY without the possibility of pushing.
#GROUPTRYNOPUSH is the least "invasive" type of group movement.
Input
#PLAYERINPUT LValue1 LValue2
LValue1: Receives movement direction
LValue2: Receives shoot direction
This command reads the movement and shoot directions, respectively. See
Directions for more information. If the player did not move or
shoot since the last poll, the direction is set to -1 (idle).
After a single call to #PLAYERINPUT, both movement and shoot directions return -1 on
subsequent calls until the player initiates more movement and/or shooting operations.
#READKEY LValue Expression
LValue: Receives key-down status
Expression: Key code or character string
This command reads the key-down status of a keyboard key. Note that this is not the
same as the buffered key input typically expected of typewriter-style input--the LValue
simply receives an indicator representing the press status (down or up) of the key. A
nonzero value indicates a pressed status; a zero value indicates a released status.
If Expression is an AS3 key code, the key-down
status is read regardless of shift status. If Expression is a character string,
the shift status is implied based upon the character (i.e. "c" is not the same as "C").
#READMOUSE
This command reads mouse status into the global variables $MOUSEX,
$MOUSEY, and $LMB. The equivalent mouse cursor grid coordinates are
reported in $MOUSEX and $MOUSEY. The value of $LMB is 1 if the
left mouse button is depressed, and 0 if it is released.
Grid Polling
#TYPEAT LValue CoordPair
LValue: Receives type code
CoordPair: Queried coordinates
This command sets an LValue to the type code at the specified coordinates.
#COLORAT LValue CoordPair
LValue: Receives color attribute
CoordPair: Queried coordinates
This command sets an LValue to the color attribute at the specified coordinates.
Note that this includes foreground, background, and blink attributes. See the #COLOR
command for more information about the bit breakdown of the color attribute.
#LITAT LValue CoordPair
LValue: Receives lit status
CoordPair: Queried coordinates
This command sets an LValue to the lit status at the specified coordinates. This is
only valid when the board is dark. A value of 1 indicates a lit square, while a value
of 0 indicates an unlit square.
#OBJAT LValue CoordPair
LValue: Receives object pointer
CoordPair: Queried coordinates
This command sets an LValue to an object pointer, representing the object at the
specified coordinates. If no object exists at the coordinates, LValue is set to -1.
If an object does exist, LValue is set to a nonnegative integer, which can be used as
an object pointer in other commands.
Object pointers are valid for as long as the object exists in the board. Object
pointers cannot be referenced in other boards. When an object dies, pointers to it
are no longer considered valid.
See Variables for more information about object pointers.
Regions
#SETREGION Expression CoordPair1 CoordPair2
Expression: Expression representing region name
CoordPair1: Bounding coordinates corner 1
CoordPair2: Bounding coordinates corner 2
This sets a named rectangular region for the board, which covers the inclusive range
between the two coordinate pairs.
#CLEARREGION Expression
Expression: Expression representing region name
This clears a named rectangular region for the board. Nothing happens if the region
does not exist.
Object and Terrain Creation
#CLONE CoordPair
This command saves a snapshot of the square located at the coordinates. This "clone"
can be used in placement commands later on. Duplication operations make use of #CLONE to
create copies next to a duplicator.
#SPAWN [OVER/UNDER] CoordPair [Color] Type
CoordPair: Creation coordinates
Color: Created type's color (optional)
Type: Created type
This spawns a new type at the coordinates. ZZT Ultra lets you initialize member
variables of the new type using type kwargs.
Unlike #PUT, no provision for automatic pushing is made for #SPAWN. If there is an
object already at the coordinates, it is destroyed to make way for the spawned type.
ZZT Ultra offers special "pseudo-directions" for this command, called OVER and
UNDER, which can optionally precede the coordinate pair. If these directions are
used, an object is positioned over or under an object at the destination instead of
destroying it. The object that ends up "underneath" the other remains in an inactive,
ghosted state until the object on top moves to a different square.
#SPAWNGHOST LValue CoordPair [Color] Type
LValue: Receives object pointer
CoordPair: Creation coordinates
Color: Created type's color (optional)
Type: Created type
This spawns a new type at the coordinates with ghosted status. It only makes sense to
create a type this way if it is represented by a status element. Because the object is
not tracked in the grid after creation, the LValue is used to refer to the ghosted status
element in future commands.
While the usage of the command can vary, one helpful use is projectile creation. A
ghosted status element does not occupy "space" within the grid, but it can still move and
run code that interacts with the grid indirectly.
A ghosted status element can only be "shown" using manual drawing commands such as
#DRAWCHAR.
#CHANGEREGION Expression [Color1] Type1 [Color2] Type2
Expression: Expression representing region name
Color1: Source color (optional)
Type1: Source type
Color2: Destination color (optional)
Type2: Destination type
This changes everything within the named region in a similar way as #CHANGE. Specifying
the built-in range ALL works the same as #CHANGE, covering the entirety of the board.
#KILLPOS ObjectPointer CoordPair
CoordPair: Destination coordinates
This command kills an existing object at the specified position. Its UNDERID and
UNDERCOLOR are left behind.
Nothing happens if no object is at the coordinates. Ghosted status elements are not
killed even if they are at the coordinates.
Properties and Config Variables
#GETPROPERTY DynamicText LValue
DynamicText: Property name (can embed $ to evaluate globals)
LValue: Receives property value
This command sets an LValue to a board or world property name. If the board has a
property by this name, it always has precedence over a world property if a world property
also exists.
The PropertyName can be dynamically put together like dynamic text. For example, the name
KEY$NUM will concatenate "KEY" and the value of NUM, such that if NUM is 5, the property
evaluated will be "KEY5".
#SETPROPERTY DynamicText Expression
DynamicText: Property name (can embed $ to evaluate globals)
Expression: Property value to assign
This command sets a board or world property to an expression's value. If the board has
a property by this name, it always has precedence over a world property when setting
properties.
PropertyName can be dynamically put together (see #GETPROPERTY).
ZZT Ultra reacts to #SETPROPERTY by dispatching $ONPROPERTY to the main type code.
The global variable $PROP will be set to the property name, allowing the handler to
determine which property is set.
Everything from scrolling to inventory updates to game speed changes can be handled as a
result of calling #SETPROPERTY, allowing a designer to exercise a great deal of control over
the frontend and game objects.
To learn more about board and world properties, see
Board/World Properties.
#SETCONFIGVAR Expression1 Expression2 Expression3
Expression1: Config hive name
Expression2: Config variable name to set
Expression3: Value to assign
This command sets a long-term configuration variable. Configuration variables are
remembered after ZZT Ultra is exited. The first expression is the config hive (a string),
while the second expression is the config variable name (a string). The relationship
within long-term configuration storage is always dictionary-style, as in hive -> var1,
hive -> var2, hive -> var3, etc. The third expression is the value to store, which can
be a string or an integer.
Configuration variables, as implemented in Flash, are saved as shared objects.
The configuration of ZZT Ultra when it is run initially is composed of four hives,
representing the configuration containers for the options accessible from the ZZT Ultra
main menu. These hives are "CFGMODERN", representing the "modern" property configuration,
"CFGCLASSIC", representing the "classic" property configurations, "CFGZZTSPEC", representing
ZZT-specific properties, and "CFGSZTSPEC", representing Super ZZT-specific properties.
The options manager remembers the options in these containers between sessions of ZZT Ultra.
#GETCONFIGVAR Expression1 Expression2 LValue
Expression1: Config hive name
Expression2: Config variable name to get
LValue: Receives value
This command retrieves a long-term configuration variable. The first expression is
the config hive (a string), while the second expression is the config variable name (a
string). The LValue receives the variable value.
If either the config hive or the config variable is not defined, the LValue receives
0 (an integer).
#DELCONFIGVAR Expression1 Expression2
Expression1: Config hive name
Expression2: Config variable name to delete
This command removes a long-term configuration variable. The first expression is
the config hive (a string), while the second expression is the config variable name (a
string). Nothing happens if either the config hive or config variable does not exist.
#DELCONFIGHIVE Expression
Expression: Config hive name to delete
This command removes a long-term configuration variable. The expression is
the config hive (a string). Nothing happens if the config hive does not exist.
One should be careful about deleting a config hive--all config variables within that
hive are permanently lost after this command is invoked.
#GETTYPEINFO Type Expression1 LValue
#SETTYPEINFO Type Expression1 Expression2
Type: Type to query or modify
Expression1: Type property name string
Expression2: Value to assign to type property
LValue: Receives type property value
These commands get or set existing type property information. Most type information
(for built-in types as well as custom types) is accessible through these commands. For
information on type properties, see Type Definitions.
It is rare that type information will need to be modified midway through a world file's
run, although there might be contexts when it could be useful. Nearly any type property is
readable using #GETTYPEINFO, but there are limits on what kind of type info can be set using
#SETTYPEINFO. The following list covers ZZT Ultra's behavior when a type property is modified.
- NUMBER: Read-only (integer).
- NAME: Read-only (string).
- CYCLE: Read/write (integer).
- STEPX: Read/write (integer).
- STEPY: Read/write (integer).
- CHAR: Read/write (integer). Setting this to a string will use the first character's code.
- COLOR: Read/write (integer).
- NOSTAT: Read-only (integer).
- BLOCKOBJECT: Read/write (integer).
- BLOCKPLAYER: Read/write (integer).
- ALWAYSLIT: Read/write (integer).
- DOMINANTCOLOR: Read/write (integer).
- FULLCOLOR: Read/write (integer).
- TEXTDRAW: Read/write (integer).
- CUSTOMDRAW: Read-only (integer).
- HASOWNCHAR: Read-only (integer).
- HASOWNCODE: Read-only (integer).
- CUSTOMSTART: Read-only (integer).
- PUSHABLE: Read/write (integer).
- SQUASHABLE: Read/write (integer).
- (all others): Extra properties defined in a type's definition will be
returned per their original declaration. Attempting to read from a property that
was never defined before will return zero. Attempting to write a property that
was never defined before will add the extra property to the type definition, which
will be picked up for future-created objects of that type (if NOSTAT=0).
Properties, if modified, do not take immediate visual effect across the board. This is
because the property information is only referenced casually by the engine itself. For
example, changing the shown character of a LION will not modify LION instances on the screen,
but the difference will be picked up when each LION moves (or the entire screen is otherwise
updated).
Text Processing
#DYNTEXT DynamicText
DynamicText: Text (can embed $ to evaluate globals, members, or properties)
This command acts as text line that can be placed in a scroll interface. The idea behind
#DYNTEXT is to automatically replace inscribed variable names with their corresponding values.
For example...
#DYNTEXT I have $GOLD pieces of eight.
Assuming the global variable GOLD is set to 153, this evaluates to "I have 153 pieces
of eight."
Any number of variables, preceded by a $, can be included on the same line. If you want
to show this line centered, the first character, if it is a $, counts as a center identifier
instead of a variable marker.
#DYNLINK Label;DynamicText
Label: Link destination label
DynamicText: Text (can embed $ to evaluate globals, members, or properties)
The only difference between this command and #DYNTEXT is that the dynamically-generated
text functions as a link instead of a normal line.
#DYNTEXTVAR LValue;DynamicText
LValue: Receives text result
DynamicText: Text (can embed $ to evaluate globals, members, or properties)
This command creates dynamic text, but instead of sending it to a scroll interface, it
writes it to the provided LValue.
#SCROLLSTR Expression1 Expression2 DynamicText
Expression1: Length of marquee
Expression2: Advancement scalar
DynamicText: Text (can embed $ to evaluate globals, members, or properties)
This command creates and advances a scrolling marquee within a toast message label.
It is relatively straightforward to queue text into a marquee and advance it either
left or right at the desired rate.
The length of the marquee in characters is set with the first expression. If this
number is zero, the previous length from an earlier command is assumed.
The advancement scalar controls what the command does with the text provided:
- = 0: Text is queued in marquee string; no scrolling occurs.
- > 0: Queued marquee string is moved right by this character count.
- < 0: Queued marquee string is moved left by this character count.
When the advancement scalar is zero, the previously queued text is erased if any
had existed already. Setting Expression2=0 with no text at all is a useful way of
"clearing" the marquee text.
When the advancement scalar is nonzero, text is funneled into the output on
the opposite side of the vector (i.e. positive adds to left, while negative adds
to right). It is not necessary to add additional text while advancing the marquee;
the already-queued text is often all that is necessary to show.
#SCROLLCOLOR Expression1 Expression2 Expression3 Expression4 Expression5 Expression6 Expression7
Expression1: Border color
Expression2: Drop-shadow color
Expression3: Background color
Expression4: Main text color
Expression5: Center text color
Expression6: Button color
Expression7: Arrow color
This command changes the scroll interface color scheme. The seven colors are
updated the next time a scroll interface is opened.
This command also sets the following configuration properties:
SCRCOLBORDER, SCRCOLSHADOW, SCRCOLBG, SCRCOLTEXT,
SCRCOLCENTERTEXT, SCRCOLBUTTON, and SCRCOLARROW.
This example restores the scroll interface colors to defaults:
#SCROLLCOLOR 15 0 1 14 15 13 12
#TEXTTOGUI DynamicText
DynamicText: GUI label (can embed $ to evaluate globals)
This command re-routes all subsequent text from the toast labels or scroll interfaces.
Instead of the text being displayed at those locations, the text is drawn as multi-line output
using the provided GUI label as a starting point. The GUI label DynamicText defines
the starting line and width (from max character length). As many lines below this starting
line will be used to display the text as required.
When #TEXTTOGUI is used, text will not cause implicit pauses because a scroll
interface will not be displayed. To get back to the toast labels and scroll interfaces, set
DynamicText to NONE.
#TEXTTOGRID Expression Type
Expression1: Expression representing region name
Type: Expression representing text type
This command re-routes all subsequent text from the toast labels or scroll interfaces. Instead
of the text being displayed there, the text is physically set as types and colors within the
gridded board data, using the specified region as the bounding box and a text type name (e.g.
_TEXTBLUE) as the type and color to place.
When #TEXTTOGRID is used, text will not cause implicit pauses because a scroll
interface will not be displayed. To get back to the toast labels and scroll interfaces, set
the region name to NONE.
#DUMPSE Expression
This is a debugging command, which dumps to a scroll interface information about the
status element matching a unique ID. A unique ID is the integer stored when an object
pointer is established.
#DUMPSEAT CoordPair
This is a debugging command, which dumps to a scroll interface information about the
status element at a coordinate pair. This can be useful when run on the extended cheat
line, because any grid square can be "inspected" at run-time.
#SUBSTR LValue Expression1 Expression2 Expression3
LValue: Receives substring
Expression1: Source string
Expression2: Starting position
Expression3: Substring length
This command assigns LValue to a substring. The source string is the first expression,
with the second expression identifying the zero-based starting position within this string,
and the third expression identifying the length of the substring.
If the first expression is not a string type, it is converted to a string automatically.
#INT LValue Expression
LValue: Receives typecast integer
Expression: Source string
This command assigns LValue to an integer version of an expression. This is a useful
way to convert a possible string representation of a number to an integer.
If a string cannot be converted to an integer, it is converted to zero.
Arrays
#SETARRAY ArrayVarName Expression
ArrayVarName: Receives array
Expression: Starting size
This command assigns ArrayVarName to be an array type. Arrays are stored as global
variables and can hold a sequence of values. An array normally acts as a stack within
the confines of ZZT-OOP. The expression identifies the initial size of the array (an
index count).
#PUSHARRAY ArrayVarName Expression
ArrayVarName: Array to extend
Expression: Value to push
This command pushes an expression to the end of the array. This increases the
overall item count of the array by one.
#POPARRAY ArrayVarName LValue
ArrayVarName: Array to contract
LValue: Receives popped value
This command pops an expression from the end of the array, storing the result in
LValue. This decreases the overall item count of the array by one. Nothing will
happen if the array's item count was already at zero.
#LEN LValue ArrayOrStringVarName
ArrayOrStringVarName: Array or string variable whose size to get
LValue: Receives length value
This command sets the LValue to the number of items in an array. It can also be used
with a string variable to obtain the number of characters in the string.
Viewport Update
#LIGHTEN CoordPair
This sets the lit status for the coordinates to 1. This does not update the display of
the square immediately. Lit status has no meaning for rooms that are not dark.
#DARKEN CoordPair
This sets the lit status for the coordinates to 0. This does not update the display of
the square immediately. Lit status has no meaning for rooms that are not dark.
#UPDATELIT
After a series of #LIGHTEN and #DARKEN commands, #UPDATELIT updates the display of all
squares cached from those commands. This is normally used when the player moves with a
torch active in a dark room.
#SUSPENDDISPLAY Expression
This suspends or restores updates to the viewport. A value of 1 for the expression
suspends the viewport; a value of 0 restores updates. All actions that would normally
update the viewport will not occur if updates are suspended.
#CAMERAFOCUS CoordPair
This modifies the board properties CAMERAX and CAMERAY in such a fashion
that the coordinate pair will be placed at the center of the viewport. Because
CAMERAX and CAMERAY refer to the upper-left corner of the displayed part
of the grid, they will be clipped automatically against the boundaries of the grid if
necessary.
Nothing is updated as part of this command. To update the viewport after a camera
re-focus, invoke #UPDATEVIEWPORT or a related command.
#UPDATEVIEWPORT
This updates the entire viewport as defined by the current GUI.
If objects are still active in real-time, they will incrementally update the GUI even
without such a full-viewport update.
#ERASEVIEWPORT
This erases the entire viewport, writing black squares.
#DISSOLVEVIEWPORT Color
This "dissolve-updates" the entire viewport in one of two ways, depending on the color:
- If Color == -1, the viewport actual contents are dissolved "in."
- If Color != -1, the viewport is dissolved "out," writing colored squares.
#SCROLLTOVISUALS Expression Direction
Expression: Milliseconds for transition
Direction: Scroll direction
This "scroll-updates" the entire viewport using a special board-scrolling effect
(one could call it a "Zelda-style" scrolling effect).
The direction indicates the virtual scroll movement direction, which must be any
direction other than idle. The expression is the amount of time, in milliseconds,
that the scroll operation should take to complete.
A scroll update yields a reasonable result after the board has been changed, but
not yet updated. As the scroll effect happens, incremental portions of the new board
are gradually funnelled into the viewport, with old board portions removed.
It is very important to reconcile the destination board's properties and grid
appearance prior to invoking this command. For example, the camera board properties
must be set appropriately (for Super ZZT), and torch masks must be activated at the
correct location (for dark rooms in ZZT). Failure to ensure a decent appearance of
the destination will result in choppy or misplaced screen updates.
GUI Update
#USEGUI DynamicText
DynamicText: GUI name (can embed $ to evaluate globals)
This swaps the current GUI with the specified one. This is a fairly sweeping change
in terms of user interface, because key mappings, viewport dimensions and label locations
are all different with one GUI and another.
#DRAWGUICHAR Expression1 Expression2 Expression3 Expression4
Expression1: Column
Expression2: Row
Expression3: Character code or string
Expression4: Color attribute
This command draws a character at the column and row (first two expressions) relative
to the GUI position on the page. The command works similar to #SETGUILABEL, but this command
identifies the target location by coordinates instead of GUI label name.
The third expression can be either a character code (number) or a string. The fourth
expression is the character color.
#ERASEGUICHAR Expression1 Expression2
Expression1: Column
Expression2: Row
This command redraws the GUI character at the column and row (two expressions) relative
to the GUI position on the page. This operation is generally used to erase decorative content
drawn via #DRAWGUICHAR.
#SETGUILABEL DynamicText Expression [Color]
DynamicText: GUI label (can embed $ to evaluate globals)
Expression: Expression to write
Color: Color attribute (optional)
This writes the expression to a GUI label. Optionally, one can specify the label color.
If no color is specified, the label's default color is used.
#MODGUILABEL DynamicText Expression1 Expression2 Expression3 Expression4 Expression5
DynamicText: GUI label (can embed $ to evaluate globals)
Expression1: Column
Expression2: Row
Expression3: Maximum label length
Expression4: Default color attribute
Expression5: Label justification flag
This modifies a GUI label of the active GUI, changing its position, maximum character length,
default color, and justification. The first two expressions represent the column and row of
the GUI label. The third and fourth expressions represent the maximum length and default color,
respectively. The last expression equals 0 if the label should be left-justified, and 1 if the
label should be right-justified.
With this command, any existing GUI label can be changed, and new GUI labels can be set. Of
course, it is usually more straightforward to set the GUI labels in the GUI editor and keep the
positions constant. Use #MODGUILABEL to perform small changes to a GUI layout that would not be
addressed as easily from the GUI editor.
The act of changing a GUI label configuration does not move or redraw any information shown
at the GUI label. The GUI must be redrawn separately for such an update to work properly.
#CONFMESSAGE GuiLabelName Expression Label1 Label2
GuiLabelName: GUI label
Expression: Expression to write
Label1: "Yes" main type code label
Label2: "No" main type code label
This displays a confirmation message (the expression, which usually evaluates to a string)
at the GUI label. The game state pauses until the user enters "Y" for yes or "N" for no.
If the user picks yes, the first label is dispatched to the main type code. If the user
picks no, the second label is dispatched to the main type code.
#TEXTENTRY GuiLabelName Expression1 Expression2 Expression3 Label1 Label2
GuiLabelName: GUI label
Expression1: Initial text
Expression2: Maximum character count
Expression3: Color attribute
Label1: "Accept" Main type code label
Label2: "Reject" Main type code label
This displays a text-entry interface. The GuiLabelName is the location where the
interface will be shown. The first expression represents the initial text to place
in the entry box, the second expression identifies the maximum character count, and the
third expression sets the color used in the entry box.
If the user successfully enters a quantity in the box and presses Enter, the first
label is dispatched to the main type code and the global variable $TEXTRESULT is
set to the user-entered text. If the user presses Escape, the second label is dispatched
to the main type code.
#DRAWPEN GuiLabelName Expression1 Expression2 Expression3 Expression4 Expression5
GuiLabelName: GUI label
Expression1: Range start
Expression2: Range end
Expression3: Pen measurement
Expression4: Pen character code
Expression5: Pen color attribute
This displays a "pen" at a GUI label. A "pen" is a single character that identifies a
measurement along a gauge, such as speed control or editor attributes.
The first two expressions identify the range of the "gauge" along the GUI label. If the
first expression is less than the second expression, the gauge increases from left to right.
If the second expression is less than the first, the gauge increases from right to left.
The third expression identifies the actual pen measurement, which is rendered relative
to the range. If the measurement is above or below the range, the pen is squashed against
the corresponding boundary (like it would have had it been a real needle on a real gauge).
The fourth and fifth expressions identify the character code and color used to draw the pen
itself, respectively. Specifying -1 for the color uses the color of the GUI label.
#SELECTPEN GuiLabelName Expression1 Expression2 Expression3 Expression4 Expression5 Label
GuiLabelName: GUI label
Expression1: Range start
Expression2: Range end
Expression3: Start pen measurement
Expression4: Pen character code
Expression5: Pen color attribute
Label: "Finished" main type code label
This brings up a pen-selection interface, which lets the user adjust a global variable with
the arrow keys. The game pauses until the user presses Enter or Escape. When control resumes
to the normal, the global variable $PENRESULT receives the new selected value, and the
label is dispatched to the main type code.
The syntax is the same as #DRAWPEN with the exception of the dispatched label on selection
completion.
#DRAWBAR GuiLabelName Expression1 Expression2 Expression3 Expression4
GuiLabelName: GUI label
Expression1: Range start
Expression2: Range end
Expression3: Bar measurement
Expression4: Bar color attribute
This displays a horizontal "bar" at a GUI label. A "bar" is used to represent the player's
life in Super ZZT.
The first two expressions identify the range of the "bar" along the GUI label. If the first
expression is less than the second expression, the bar increases from left to right. If the
second expression is less than the first, the bar increases from right to left.
The third expression identifies the actual bar measurement, which is rendered relative to
the range. If the measurement is above or below the range, the bar is drawn empty (if too low)
or full (if too high).
The fourth expression identifies the color used to draw the bar. Specifying -1 for the
color uses the color of the GUI label.
Palette
#BIT7ATTR Expression
Change the meaning of bit 7 of the color attribute. By default, bit 7 of the color
attribute indicates the blink flag. Use this command to switch the meaning between blink
flag and high-intensity background colors.
A value of 1 for the expression allows background colors to range between BLACK and GREY,
or 0 to 7. A value of 0 for the expression allows background colors to range between BLACK
and WHITE, or 0 to 15, which is the same range for foreground colors.
This command implicitly sets the BIT7ATTR world property to the same value.
#PALETTECOLOR Expression1 Expression2 Expression3 Expression4
Expression1: Color register (0-15)
Expression2: Red scalar (0-255)
Expression3: Green scalar (0-255)
Expression4: Blue scalar (0-255)
This command changes a single palette color register, as indicated by the first
expression. The new color of this register will be the RGB color as indicated by the second,
third, and fourth expressions.
The concept of palette color registers is derived from the original VGA hardware in
palettized video modes. In principle, any of the named colors (BLACK to WHITE) can have
their color values and hues tweaked. When a color register is modified, every instance of
that color is modified, no matter where it is on the page, or whether it is used in a
foreground or background capacity within a character cell.
#PALETTEBLOCK Expression1 Expression2 Expression3 MaskOrLumpOrArray
Expression1: Start color register (0-15)
Expression2: Number of color registers to update
Expression3: Scalar extent (1-255)
MaskOrLumpOrArray: Palette storage location
This command changes multiple palette color registers, as indicated by the first
and second expressions. This is a useful command for modifying some or all colors in
the palette at the same time.
The storage location of the palette color RGB entries can be a mask, a WAD lump, or an
array. If MaskOrLumpOrArray is a string, the palette is pulled from a mask if such a mask
by the string name is present, or a WAD lump by this name if no mask is present. If
MaskOrLumpOrArray is a global variable, it is assumed to be an array with the RGB entries.
Masks and global variables assume array-like layout of entries; WAD lumps assume a binary
representation with 3 bytes per palette RGB entry (red, then green, then blue).
The string "NONE" is special: this represents the default palette.
The third expression represents the extent assumed for the RGB entries. Common values
include 255 and 63. The value 255 is a very common extent for RGB scalars, but the
original VGA hardware only supported 6-bit palette registers. Thus, it is common for a
WAD lump representing a legacy palette to require an extent of 63, while most other types
of palette representation require an extent of 255.
#FADETOCOLOR Expression1 Expression2 Expression3 Expression4
Expression1: Milliseconds for transition
Expression2: Red scalar (0-255)
Expression3: Green scalar (0-255)
Expression4: Blue scalar (0-255)
This command performs a fade-to-solid-color transition. All palette color registers
are faded to the specified RGB color over the period indicated by the milliseconds count.
After this command is complete, nothing will be distinguishable on the screen for lack
of any unique colors. One must invoke other palette commands to transition away from a
uniform color.
#FADETOBLOCK Expression1 Expression2 Expression3 MaskOrLumpOrArray
Expression1: Milliseconds for transition
Expression2: Start color register (0-15)
Expression3: Number of color registers to update
Expression4: Scalar extent (1-255)
MaskOrLumpOrArray: Palette storage location
This command performs a fade-to-palette transition. The specified range of color
registers is faded to the stored palette range over the period indicated by the
milliseconds count.
See #PALETTEBLOCK for storage location specifics. The only difference between this
command and #PALETTEBLOCK is that #FADETOBLOCK fades in the change, while #PALETTEBLOCK
performs the change immediately.
Character Sets
#SCANLINES Expression
Change the number of scanlines used to display the text characters. The expression can
be 0 (CGA, 200 scanlines), 1 (EGA, 350 scanlines), or 2 (VGA, 400 scanlines). This
command implicitly sets the SCANLINES world property to the same value.
Setting a scanline mode will reset the font to the default for that scanline mode,
which is 8 scanlines per character for CGA, 14 scanlines per character for EGA, and 16
scanlines per character for VGA. By default, the scanline mode is 2 (VGA), which shows
the most well-defined characters.
One can use the scanline mode in conjunction with the font height set in the #CHARSELECT
command to set a variety of different fonts and row counts.
#CHARSELECT MaskOrLumpOrArray Expression1 Expression2 Expression3 Expression4 Expression5
MaskOrLumpOrArray: Character set storage location
Expression1: Character cell X-size
Expression2: Character cell Y-size
Expression3: Cells across count
Expression4: Cells down count
Expression5: Starting character to update
Change some or all of the characters of the font. This command has its roots in the
low-level text-mode services provided by EGA and VGA hardware, known as the
character-generator interface.
The character cell sizes are defined in the first two expressions. The only accepted
value for cell X-size is 8. The cell Y-size can be 8, 14, or 16.
The "cells across" and "cells down" counts identify the character data layout within
the provided storage (MaskOrLumpOrArray). Most character data patterns assume a cells-across
count of 1 and a cells-down count as the number of characters to update. This is because
text-mode character metadata, generally speaking, will tend to stack the patterns "vertically"
as opposed to horizontally. Note that this also applies to the formats saved by the character
editor in ZZT Ultra; the cells-across count would only be 1 for ZZT Ultra's own generated
content.
The fifth expression identifies the first character index to update within the range of
0-255. This is usually zero if the entire character set is being updated. If only a partial
set of characters needs to be updated, this number can be any starting point.
The storage location of the character data can be a mask, a WAD lump, or an
array. If MaskOrLumpOrArray is a string, the character data is pulled from a mask if such
a mask by the string name is present, or a WAD lump by this name if no mask is present. If
MaskOrLumpOrArray is a global variable, it is assumed to be an array.
Masks and global variables assume array-like layout of ones and zeroes; WAD lumps assume
a binary representation with each byte representing bit fields for a single character
scanline.
The string "NONE" is special: this represents the default character set.
One should take care to match up the scanline mode and character set height correctly.
Usually, the display maintains 25 rows if the character height of the stored set matches up
to the scanline mode. Of course, it is possible to intentionally mismatch the scanline mode
and character set height to produce different types of displays. The following table
describes how each mode and character set height affect the display:
Scanline Mode | Scanline Count | Character Height |
Row Count | Shrink Behavior |
0 | 200 | 8 | 25 | None |
0 | 200 | 14 | 25 | 4/7 Lines Removed |
0 | 200 | 16 | 25 | 1/2 Lines Removed |
1 | 350 | 8 | 43 | None |
1 | 350 | 14 | 25 | None |
1 | 350 | 16 | 25 | 7/8 Lines Removed |
2 | 400 | 8 | 50 | None |
2 | 400 | 14 | 28 | None |
2 | 400 | 16 | 25 | None |
It is usually not a good idea to rely upon shrink behavior, because of the low-quality
results. It is better to create a character set that appropriately matches the scanline
mode.
Taking proper advantage of a display with greater than 25 rows requires that every part
of the world be tailored to such a size. This includes GUIs, viewports, and board sizes.
Since most text-mode displays are tailored to 25 rows, it is usually more convenient to
rely upon a 25-row display.
FOR-style Loops
#FOREACH LValue Expression
LValue: Receives object pointer
Expression: Expression representing region name
This is a "region-iterator" loop command for locating objects. Positions in the
named region (or ALL to represent every position in the board) are cycled,
with LValue receiving an object pointer to an object on this iteration.
Objects are located using a "left-to-right, then down" algorithm. Non-stat
types are ignored. After the last object is found within the region, control jumps
to the statement beyond the #FORNEXT statement.
If there are no objects found within the region, the loop is never entered;
control moves to beyond the #FORNEXT statement immediately.
#FORREGION LValue1 LValue2 Expression
LValue1: Receives X value
LValue2: Receives Y value
Expression: Expression representing region name
This is a "region-iterator" loop command for locating coordinates. Positions in
the named region (or ALL to represent every position in the board) are cycled,
with the two LValue quantities receiving the 1-based X and Y coordinates for this
iteration.
Coordinates are located using a "left-to-right, then down" algorithm. All types,
stat or not, are found. After the last object is found within the region, control
jumps to the statement beyond the #FORNEXT statement.
If the region is empty, the loop is never entered; control moves to beyond
the #FORNEXT statement immediately.
#FORMASK LValue1 LValue2 CoordPair MaskName
LValue1: Receives X value
LValue2: Receives Y value
CoordPair: Coordinates denoting center of mask
MaskName: Mask name string
This is a "mask-iterator" loop command for locating spaces within a mask.
Positions in the "masked-in" location are cycled, with the two LValue quantities
receiving the 1-based X and Y coordinates for this iteration. The mask is centered
about CoordPair.
Positions are located using a "left-to-right, then down" algorithm. All
positions, regardless of type, are evaluated. A "true" mask condition will have
coordinates appear as an iteration in the loop; a "false" mask condition will not
have these coordinates appear. After the last position
is found, control jumps to the statement beyond the #FORNEXT statement.
For sections of the mask that are clipped against the sides of the board,
these are not evaluated and will not appear as an iteration in the loop.
If the mask would not yield even a single pair of coordinates, the loop is never
entered; control moves to beyond the #FORNEXT statement immediately.
#FORNEXT
This command serves as the end-boundary of a #FOREACH, #FORREGION, or #FORMASK
loop.
You cannot nest these loops; ZZT Ultra supports only one #FOREACH, #FORREGION,
or #FORMASK iterator at a time.
Board and World
#CHANGEBOARD Expression
This changes the board to a new board represented by a board number. The expression
must evaluate to an integer between 0 and the highest board number in the world.
When the board is changed, nothing about the display is updated--the display must be
updated separately using #DISSOLVEVIEWPORT, #SCROLLTOVISUALS, etc.
If this command is invoked from an object as opposed to the main type code, care must
be exercised when referring to anything that impacts the board. The moment #CHANGEBOARD
is completed, all commands afterwards use the new board as the frame of reference instead
of the old board where the object resides. This means that things like movement and
checking old board content are out of the question for the remainder of the iteration.
It is possible for objects to resourcefully change to a new board, extract information
from it, and then immediately change back to the old board to make informed decisions.
This is actually necessary when performing walking transitions between linked boards.
When the player "jumps" from the side of one board to the opposite side of the other,
the program needs to know what the destination square is going to be before committing
to movement (and board change).
#SAVEBOARD Expression
This saves an instance of the board to the temporary save timeline. The expression
evaluates to a code representing the classification for this saved instance: 0=manual
save, 1=board-change save, 2=zap save, 3=auto save. The only difference between
these codes is how they are displayed in the board-restore interface; these codes
otherwise have no meaning.
There is also a special "wipe saves" code: -1 will erase all existing save instances
and reset the timeline to ground zero. This also has the effect of resetting the world
properties to the ground zero state. One would conduct this operation when starting
or restarting a world from the beginning.
Various configuration operations can alter how frequently board instances are
saved. In theory, only file-based saves are absolutely necessary, since that was
the only type of save feature that ZZT and Super ZZT originally supported.
ZZT Ultra does not allow instances to be captured for the title screen. Attempts to
save when the title screen is the current board will be ignored. This is because the
state of the title screen (i.e. the original board at ground zero) needs to be kept
unique and apart from the rest of the timeline.
#SAVEWORLD Expression
This brings up an interface for saving a .WAD file of the current world. If the
expression evaluates to 0, the board is saved as a basic archive. If the expression
evaluates to 1, the world is saved as a savegame. Like the original .SAV files, the
fundamental structure of both formats is the same; there are only a few subtle
differences in world properties.
#LOADWORLD Expression
This brings up an interface for loading a world file. The expression's value determines
the filter: 1=featured, 0=site, -1=.ZZT, -2=.SZT, -3=.WAD, -4=.ZIP.
The value of 0 brings up the general interface for loading content from the deployed (site)
configuration, which can include a mixture of these formats, depending on what is available
in the configuration. The SITELOADCHOICE property further qualifies what is shown in
the scroll.
The value of 1 invokes a special "featured world file" that had been last loaded or
otherwise selected as part of configuration. This is either the value of DEP_STARTUPFILE
(if a startup file had been set) or whichever world file had been last loaded from #LOADWORLD.
No interface is shown in this context; the world is loaded immediately.
Note that the act of loading external world files does not change which world is featured;
the featured world can only be one that is visible within the deployed (site) configuration.
If the value of the expression is a string, it is assumed to be the path (or just the
filename) of a file in the deployed (site) configuration. No interface is shown in this
context; the world is loaded immediately. Nothing happens if the specified file is not mapped
in the deployed (site) configuration, even if it exists at the path.
If the world is successfully loaded, the $ONWORLDLOAD message is dispatched to
the main type code.
#RESTOREGAME Expression
This brings up an interface for loading a savegame. The expression's value determines
what type of the interface to show: 0=Both Board and World Restore, 1=Board Restore Only,
2=World Restore Only.
If a world is restored, the $ONRESTOREGAME message is dispatched to the main type
code. If a board is restored (no change in world), the $ONRESTORESTATE
message is dispatched to the main type code.
High Scores
#POSTHS Expression1 Expression2 Expression3 Expression4
Expression1: Comma-delimited high score line
Expression2: High score filename
Expression3: Sort key column index
Expression4: Sort order code
This command posts a high score to a file, and also retrieves the updated high score
list that results from the update in a specific sorted order.
When a high score line is posted, it is comma-separated (#DYNTEXTVAR is the best way
to create a comma-separated high score line). The original ZZT storage format of
SCORE,NAME is supported by default for ZZT and SZT world files, but it is possible to
post any number of custom fields to a high score file.
The retrieved list is sorted based on the sort key column index and the sort order
code. The sort key column in the default implementation is 2 (the SCORE), and the sort
order code is -1 (reverse order). The column can be set to any integer within the range
of valid columns, and the sort order code can be set to 1 (forward), -1 (reverse), or
0 (unsorted).
If the post operation succeeds, the main type code is dispatched $ONPOSTHS. If
the post operation fails, the main type code is dispatched $ONFAILPOSTHS.
Important: The user can do certain things in ZZT Ultra that will disable
high score posting implicitly. The default handler for the cheat entry box sets
HIGHSCOREACTIVE to zero immediately, which can only be rescinded by reloading a world
file and starting the game over from the beginning.
The user can also disable high scores on a more fundamental level if more egregious
cheating is attempted. If the user actively executes any console action, or
attempts to manipulate SCORE in the configuration, ZZT Ultra internally disables
high score functionality for the remainder of the session (even reloading a world will
not rescind this).
#GETHS Expression1 Expression2 Expression3
Expression1: High score filename
Expression2: Sort key column index
Expression3: Sort order code
This command fetches the high score list from a file. The fundamental difference
between this command and #POSTHS is that this command only retrieves the list--it does
not attempt to post a score.
If the get operation succeeds, the main type code is dispatched $ONGETHS. If
the get operation fails, the main type code is dispatched $ONFAILGETHS.
#GETHSENTRY LValue Expression1 Expression2
LValue: High score filename
Expression1: Row index
Expression2: Column index
After a successful call to #GETHS or #POSTHS, the individual high score table values
can be read into LValue with this command. Any row or column index of the retrieved
high score list can be read. If the row or column index exceeds the boundaries of the
list, LValue is set to -1. In this way, the table can be sized iteratively.
If high scores did not load correctly, or else the file is empty or nonexistent, the
first row will be empty (-1 returned for all columns).
High score files always reserve the first two columns for a primary key value
(column 0) and a timestamp (column 1). All other columns, starting with column 2,
resemble the comma-separated line posted to the file from #POSTHS statements. Thus
the default file format would have four columns (PRIMARYKEY,TIMESTAMP,SCORE,NAME).
Miscellaneous
#PLAYSOUND DynamicText
DynamicText: SoundFX name (can embed $ to evaluate globals)
This command plays a stored sound effect. Sound effects can be stored by name in the
world file. See Sound Playback for more information.
ZZT Ultra also supports many default sound effects, which map to the original
hard-coded sound effects found in ZZT and Super ZZT.
#GETSOUND LValue Expression
LValue: Receives playing status
Expression: Sound channel number
This command sets LValue to 0 if the specified sound channel is not playing, and 1 if
the specified sound channel is playing. The expression must evaluate to a channel number
between 0 and 15.
#STOPSOUND Expression1 Expression2
Expression1: Start of sound channel range
Expression2: End of sound channel range
This command stops playing the range of sound channels between the lower and higher
channel numbers, respectively. Nothing happens if sound channels are stopped that were
not playing to begin with.
#MASTERVOLUME Expression1 Expression2 Expression3
Expression1: Start of sound channel range
Expression2: End of sound channel range
Expression3: Channel volume
This command sets the volume level of the channel range to the specified channel
volume, which is a number between 0 and 50. See the "Vnn" code for the #PLAY statement
syntax for more information about volume levels.
The volume of the current note as played by the channel is independent of the master
volume level set by this command. The level in this command further attenuates the
volume of the note played per the #PLAY statement sequence itself.
#EXECCOMMAND Expression
Expression: Command string
This command dynamically compiles and executes a single line of ZZT-OOP code
stored in the expression (a string variable). This is mostly used to run user-entered
cheat commands, but it can be used in other contexts.
When ZZT Ultra executes a command using #EXECCOMMAND, it creates a temporary type
and code block to encapsulate the run environment, compiles the code, runs the code,
and then removes the temporary type and code block. This makes #EXECCOMMAND only
useful for "bird's eye" operations as opposed to object-specific or type-specific
operations.
Conditions
A condition, or conditional expression, is found in #IF commands.
GlobalVar
This yields true if the global variable exists and is nonzero, or false if the global
variable does not exist or is zero.
Before ZZT Ultra, only world flags could be checked this way--global variables as a
larger concept were not implemented.
ANY [Color] Type
This examines the entire board for the presence of a type with an optional color
qualifier. If one or more match is found, yields true. If zero matches found, yields
false.
ALLIGNED [Direction]
ALIGNED [Direction]
This examines the object's position relative to the player, and yields true if there
is a cartesian-direction alignment of the player in that direction. Setting direction
to IDLE or I, or omitting a direction, checks all four directions.
The original ZZT-OOP only supported the misspelled ALLIGNED statement, and no
direction was permitted (it could only check all directions). ZZT Ultra makes the
condition slightly more powerful, and it gives grammar fiends some peace of mind
with a much-needed alternate spelling.
CONTACT [Direction]
This examines the object's position relative to the player, and yields true if the
player is point-blank in that direction. Setting direction to IDLE or I,
or omitting a direction, checks all four directions.
With the original ZZT-OOP, CONTACT did not support a direction (it could only check
all directions).
BLOCKED Direction
This examines a square adjacent to the object's position to see if it is blocking.
If the square is blocking, yields true. If nonblocking, yields false.
Pushable squares are nearly always considered blocking squares--the ability of the
object to possibly push the square is not taken into consideration by BLOCKED.
ENERGIZED
This yields true if the player is energized, or false if the player is not energized.
In ZZT Ultra, this simply evaluates to whether or not the ENERGIZERCYCLES property
is greater than zero.
BLOCKEDAT CoordPair
ZZT Ultra only: This yields true if a type at the coordinates is blocking.
Like BLOCKED, pushability is not taken into consideration.
TYPEIS CoordPair [Color] Type
ZZT Ultra only: This yields true if a type matching the specification is
located at the specified coordinates.
ANYTO Direction [Color] Type
ZZT Ultra only: This yields true if a type matching the specification is
located point-blank from the object in the specified direction.
ANYIN Expression [Color] Type
ZZT Ultra only: This yields true if a type matching the specification is located
anywhere within the expression representing a named region.
SELFIN Expression
ZZT Ultra only: This yields true if the object itself is located anywhere within
the expression representing a named region.
CANPUSH CoordPair Direction
ZZT Ultra only: This yields true if a push operation at the coordinate pair
would succeed in the specified direction.
A push operation is considered viable regardless of whether or not one of the squares
in the way of the push operation would be squashed as a result of the push.
CANPUSH only yields false when the push would fail completely, resulting in
nothing moved at all.
SAFEPUSH CoordPair Direction
ZZT Ultra only: This yields true if a push operation at the coordinate pair
would succeed in the specified direction, WITHOUT squashing anything. This is the only
fundamental difference between this condition and CANPUSH.
SAFEPUSH1 CoordPair Direction
ZZT Ultra only: This yields true if a push operation at the coordinate pair
would succeed in the specified direction, and allowing squashing as long as type to be
squashed is not located at the immediate coordinate pair.
The tendency of pushers to "save the one in front of me and no other" is bizarre
to say the least. It is nevertheless important to be able to reproduce this behavior.
Many ZZT and Super ZZT worlds require that the square at point-blank range not be
squashed, even if it would be squashed in other contexts.
HASMESSAGE ObjectPointer Label
ZZT Ultra only: This yields true if the object code associated with the
object pointer has a working, unzapped label, and false if no such label exists.
If HASMESSAGE is true, it means #SENDTO or #DISPATCHTO would definitely work.
If the object pointer is invalid, false is returned.
VALID ObjectPointer
ZZT Ultra only: This yields true if the object pointer is valid, and false if
not. This is useful when checking if an object tracked with a pointer unexpectedly dies.
TEST Expression
ZZT Ultra only: This is the rawest form of condition, taking an expression that
evaluates to true if the expression is nonzero, and false if zero.
Directions
In ZZT-OOP, a direction identifies a cartesian direction (north, south, east, or west)
that can be used for moving, shooting, or pointing.
In ZZT Ultra, there is a direct mathematical relationship between the named directions
and the numbers that represent them:
-1 | Idle |
0 | East |
1 | South |
2 | West |
3 | North |
When calculating directions, it is often useful to apply an "AND" operation of the
result and the number 3, which has the effect of clipping an "over-rotated" or
"under-rotated" direction to the range 0-3.
EAST
E
This direction faces horizontal +1.
SOUTH
S
This direction faces vertical +1.
WEST
W
This direction faces horizontal -1.
NORTH
N
This direction faces vertical -1.
IDLE
I
This direction is neutral. When used in a movement command, it simply means the
object gives up a turn without moving.
SEEK
This evaluates to a direction that faces towards the player. Note that if ENERGIZED
is set, this behavior is inverted--SEEK faces away from the player.
FLOW
This evaluates to the object's step direction. WALK sets the step
direction.
RNDNS
Randomly evaluate to a north or south direction.
RNDNE
Randomly evaluate to north or east direction.
RND
Randomly evaluate to north, south, east, or west. For ZZT, horizontal directions
are twice as likely to be picked as vertical directions. For Super ZZT, all directions
are equally likely to be picked.
CW Direction
This prefix "turns" the following direction clockwise by 90 degrees.
CCW Direction
This prefix "turns" the following direction counter-clockwise by 90 degrees.
RNDP Direction
This "perpendicular" prefix turns the following direction either clockwise or
counter-clockwise by 90 degrees, picking the turn direction randomly.
OPP Direction
This prefix "flips" the following direction by 180 degrees.
RNDSQ
ZZT Ultra only: This is similar to RND, but it always picks directions
with equal probability.
TOWARDS CoordPair
ZZT Ultra only: This is similar to SEEK, but any target can be selected
based on the coordinates.
MAJOR Direction
ZZT Ultra only: This prefix tweaks the result of SEEK or TOWARDS,
picking the "longer" of the differences in X and Y between the object and the target, and
using the "longer" length's axis for movement.
If the object is cartesian-aligned with the target, MAJOR does not change the
direction.
MINOR Direction
ZZT Ultra only: This prefix tweaks the result of SEEK or TOWARDS,
picking the "shorter" of the differences in X and Y between the object and the target,
and using the "shorter" length's axis for movement.
If the object is cartesian-aligned with the target, MINOR will never point towards
the target, because the minor difference would be zero.
Expression
ZZT Ultra only: Where a direction is expected, it is also possible to use
a parenthetical expression in place of a keyword-oriented clause. Parentheses must
be used; no singular values.
Inventory
Inventory is used in #GIVE and #TAKE commands. In ZZT Ultra, inventory
quantities exist as world properties by the same name as the inventory name, and can
also be read and written with #GETPROPERTY and #SETPROPERTY.
AMMO
TORCHES
GEMS
HEALTH
SCORE
Z
These quantities are standard inventory items displayed in the GUI, and represented
as integers. ZZT Ultra assumes they can range anywhere within the +/- 2-billion-plus
range expected of a 32-bit integer. The original limits on inventory were much tighter,
usually in the +/- 32767 range.
TIME
This quantity is integral like the other inventory items, but it counts up,
towards the time limit, instead of down, as the GUI behavior would have you
believe. This means increasing TIME has the effect of shortening the time before
damage is taken for a timed board, and decreasing TIME has the effect of
lengthening the time.
Color KEY
ZZT Ultra only: This describes a series of "key" quantities, which are integral,
but usually do not take values other than 0 or 1. A key of a particular color (0-15) is
represented as the equivalent world property KEYnn. For example, BLUE KEY
evaluates to KEY9.
ZZT Ultra lets configuration permit more than one key per color in inventory; the
original ZZT behavior only let the player carry a single key at one time (and would
even block the player from moving across keys of the same type already in inventory).
(Anything else)
ZZT Ultra only: Any other word that does not represent an inventory property
already in use will create a new property, which functionally works the same as any other
type of inventory quantity. For example, #GIVE COINS 1 creates the COINS inventory
property for future use in #GIVE, #TAKE, and other commands.
Colors
The original ZZT-OOP supported the following colors:
BLUE
GREEN
CYAN
RED
PURPLE
YELLOW
WHITE
ZZT Ultra supports all 16 of the standard foreground colors in CRT text mode.
There are numbers associated with each of the 16 colors:
BLACK | 0 |
DARKBLUE | 1 |
DARKGREEN | 2 |
DARKCYAN | 3 |
DARKRED | 4 |
DARKPURPLE | 5 |
BROWN | 6 |
GREY | 7 |
DARKGREY | 8 |
BLUE | 9 |
GREEN | 10 |
CYAN | 11 |
RED | 12 |
PURPLE | 13 |
YELLOW | 14 |
WHITE | 15 |
Additionally, ZZT Ultra also supports numerical expressions where a color would
be expected. This allows code to "calculate" a color attribute as needed.
Expressions
Something that ZZT-OOP did not originally support was a system of expressions for
calculating quantities and assigning variable values. This imposed severe restrictions
on what a designer could do with ZZT-OOP. In ZZT Ultra, expressions are supported,
allowing a designer much more flexibility.
An expression conforms to one of two syntaxes: bare operand or parenthetical
sequence.
Bare Operand
A bare operand is composed of just a single quantity to be read from or written to,
and can be one of the following:
Integer
An integer constant. Can begin with a hyphen to indicate negative sign. All other
parts of the integer must be composed of decimal digits 0-9.
"String"
A string constant, enclosed in double quotes.
GlobalVar
A global variable name, composed of alphanumeric characters (or underscores).
~Property
A property name (world or board), composed of alphanumeric characters (or
underscores). The first character is a tilde, denoting a property.
.MemberVar
A member variable name, composed of alphanumeric characters (or underscores). The
first character is a period, indicating member scope.
SELF
An object pointer to the object being iterated.
TypeName
A well-known type name, such as BLINKWALL, evaluates to an integer constant.
Color
A color name, such as GREEN, evaluates to an integer constant.
Direction
A direction evaluates to an integer between -1 and 3.
Parenthetical Sequence
A parenthetical sequence is composed of a sequence of bare operands, enclosed in
parentheses, and linked together via operators. This is an example of a
parenthetical sequence:
(.P1 - 3 * 5)
The way this should be read is: "Read .P1, subtract 3, multiply by 5."
Order of operations is always left-to-right within the sequence. There are
no priority rules for these sequences.
The following operators are defined for parenthetical sequences:
+ | Add two numbers. |
- | Subtract two numbers. |
* | Multiply two numbers. |
/ | Divide two numbers (integer division). |
= | Return 1 if first value equal to second. |
!= | Return 1 if first value not equal to second. |
> | Return 1 if first number greater than second. |
< | Return 1 if first number less than second. |
>= | Return 1 if first number greater than or equal to second. |
<= | Return 1 if first number less than or equal to second. |
& | Bitwise AND two numbers. |
| | Bitwise OR two numbers. |
^ | Bitwise XOR two numbers. |
. | Indirection (ObjectPointer.Member). |
[ index ] | Array dereference. The zero-based index dereferences
an array created with #SETARRAY. |
LValues
In this documentation, the term LValue refers to an expression that
could serve as the left-hand side of an assignment statement, and thus can be
written to. This includes the following types of expressions:
GlobalVar
.MemberVar
~Property
(ObjectPointer.Member)
Attempting to write to a non-LValue causes an error.
Variables
Three types of variables can be used to store data in ZZT-OOP expressions:
global variables, properties, and member variables. All types of variables can
store either an integer quantity or a string quantity, with integer quantities
being the most common type.
A global variable, composed of alphanumeric characters (and underscores), is
stored as a dictionary entry in the world file. Note that global variables are
NOT the same as world or board properties.
Board and world properties have special functions within ZZT Ultra, so they
are held as a separate category. A property name is composed of alphanumeric
characters (and underscores). Note that setting a property with #SET will have
the same effect of dispatching $ONPROPERTY as with #SETPROPERTY.
These properties are discussed in Board/World Properties.
There is no hard limit to the number of global variables that ZZT Ultra can
retain. ZZT world files could only retain a maximum of 10 world flags, and Super
ZZT world files could only retain a maximum of 16 world flags.
A member variable, composed of alphanumeric characters (and underscores), is
usually stored as a dictionary entry in an object instance in a board. Most of
the time, member variables are accessed via the .MemberVar bare-operand syntax.
It is also possible to use the indirection operator in an expression to fetch a
member variable of an object identified by an object pointer.
An object pointer is usually retrieved from the #OBJAT command. The SELF
keyword also identifies the object being iterated, which is the equivalent of
calling #OBJAT var +0,+0. Note that SELF has no meaning when a dispatched
message is being handled by the main type code or some other type that lacks
representation as an object instance. Attempting to refer to SELF or member
variables in such contexts may have undefined results.
Some member variables are permanent and cannot be removed by the #CLEAR command:
TYPE | The object's type ID. |
CYCLE | The object's cycle (1-255). |
X | The object's 1-based X-coordinate in the board. |
Y | The object's 1-based Y-coordinate in the board. |
STEPX | The object's X component of the step direction. |
STEPY | The object's Y component of the step direction. |
UNDERID | The type ID under the object. |
UNDERCOLOR | The color attribute under the object. |
IP | The object's instruction pointer. |
FLAGS | The object's flags. |
DELAY | The object's clock cycle delay before the next iteration. |
Other member variables may or may not exist in an object, as their existence is
usually type-sensitive. Common examples of such variables:
P1 | Parameter 1 value. |
P2 | Parameter 2 value. |
P3 | Parameter 3 value. |
FOLLOWER | Centipede follower ID. |
LEADER | Centipede leader ID. |
ONAME | OBJECT name (from @). |
CODEID | Custom compiled code ID. |
CHAR | Custom character code. |
There are other keywords that are not member variables technically, but they
are treated as member-based property extensions of the object:
COLOR | This reads and writes the color attribute from the grid.
Setting this normally does not change the background color bits. |
COLORALL | Only write access supported. This writes all
eight bits of a color attribute to the grid. |
DIR | This reads or writes the two step direction quantities
STEPX and STEPY, translating to or from the numeric directional constants. |
It should be noted that an object's TYPE is not the same as a well-known
numerical mapping, but rather ZZT Ultra's internal assignment for type look-up
information. To compare against type names, one should use the ZZT-OOP commands
dedicated for comparing and locating types, such as #TYPEAT, TYPEIS,
and ANYTO.
Coordinate Pairs
The CoordPair syntax refers to a grid square on the board. There are three
different ways to specify such a square: absolute coordinates, relative coordinates,
and polar coordinates.
- Expression, Expression - Absolute coordinates
- +/-Expression, +/-Expression - Relative coordinates
- [Expression, Expression] - Polar coordinates
To specify absolute grid coordinates, simply provide two expressions separated by a
comma. The first is the 1-based X-coordinate, and the second is the 1-based Y-coordinate.
Relative coordinates are distinguished from absolute coordinates via a sign before each
expression. The absolute coordinates are calculated by adding the values of X and Y of the
expressions to the object's own X and Y. For example, these coordinates would be offset by
3 west and 2 south from the object being iterated:
-3, +2
Polar coordinates are also relative, but instead of rectangular offsets, the two
expressions form a magnitude and a direction. For example, these coordinates would be
offset by 5 north of the object being iterated:
[5, 3]
Any of these three syntaxes are valid for coordinate pairs, but #OFFSETBYDIR and
#ATAN2 are generally not very useful with the absolute syntax.
Types
ZZT and Super ZZT defined unique names for all the types in the game. ZZT-OOP
refers to these types using such names. In ZZT Ultra, the names are also interpreted,
but the exact numerical mapping of the type names is different.
ZZT Ultra also supports special keywords for types in some contexts:
CLONE
This represents the type copied from the last #CLONE command.
ALL
This represents all types. Obviously, this cannot be placed on the grid; placement
commands cause an error if ALL is used. One uses ALL as a generic
"catch-all" filter, like in a #CHANGE command.
Expression
It is possible to use an expression to directly specify the numerical mapping of a
type if it is known. If this is done, though, it comes at the expense of the ability
to provide any kwargs.
When one specifies a well-known type name, it can come with optional "kwargs," or
keyword arguments, which can further filter the type (when used as a condition) or set
member variables in the new type (when used as placement).
A type without kwargs looks like this:
RUFFIAN
A type with kwargs looks like this:
RUFFIAN;INTELLIGENCE=5;RESTINGTIME=3
The kwargs for the RUFFIAN type refer to a RUFFIAN object with
intelligence=4 and resting time=2 (the parameters are zero-based in reality but
1-based in the ZZT editor).
Color qualifiers are possible as a prefix for a type, but it is equally possible
to specify colors using kwargs:
SOLID;COLOR=DARKRED
ZZT Ultra supports the following kwargs:
TYPE | The numerical mapping for the type. |
COLOR | The color attribute at the type's grid square. |
COLORALL | When used to set the type's grid square, this sets
all eight bits of the attribute. |
DIR | The object's equivalent numeric directional constant,
as it would evaluate to STEPX and STEPY. |
CYCLE | The object's cycle (1-255). |
X | The object's 1-based X-coordinate in the board. |
Y | The object's 1-based Y-coordinate in the board. |
STEPX | The object's X component of the step direction. |
STEPY | The object's Y component of the step direction. |
UNDERID | The type ID under the object. |
UNDERCOLOR | The color attribute under the object. |
CHAR | Custom character code. |
FOLLOWER | Centipede follower ID. |
LEADER | Centipede leader ID. |
P1 | Parameter 1 value. |
INTELLIGENCE | Alias for P1. |
SENSITIVITY | Alias for P1. |
PHASE | Alias for P1. |
P2 | Parameter 2 value. |
PERIOD | Alias for P2. |
RESTINGTIME | Alias for P2. |
DEVIANCE | Alias for P2. |
RATE | Alias for P2. |
P3 | Parameter 3 value. |
DESTINATION | Alias for P3. |
ONAME | Name of an object (ONAME member). |
BIND | Alias for ONAME. |
Note that most of the kwargs only apply to types represented by objects. For types
that are not represented by objects (terrains such as solid, floor, water, etc.) only
the COLOR and COLORALL kwargs are valid.
The ONAME or BIND kwarg has a special meaning when used in placement
and change commands. For example, when used like this:
#PUT E GREEN OBJECT;ONAME="CUSTOMBULLET"
An OBJECT type is created with its code immediately bound to the OBJECT on the same
board named CUSTOMBULLET, if such an object exists on the board. In a similar fashion,
ONAME can be used as a change filter as a way of capturing only those OBJECTs on
the board that have a specific name or are bound to that name.
The well-known type names evaluate to numbers designed to be harmonized across the
ZZT and Super ZZT mappings. Those already familiar with these mappings would know that
there is some overlap that would create aliasing problems had one mapping or the other
become the "dominant" one. ZZT Ultra juggles some of these mappings around when it
loads a board, such that the mappings will be internally consistent no matter what type
of world file is loaded.
ZZT Ultra's internal numerical type mappings are...
EMPTY | 0 |
BOARDEDGE | 1 |
MESSENGER | 2 |
MONITOR | 3 |
PLAYER | 4 |
AMMO | 5 |
TORCH | 6 |
GEM | 7 |
KEY | 8 |
DOOR | 9 |
SCROLL | 10 |
PASSAGE | 11 |
DUPLICATOR | 12 |
BOMB | 13 |
ENERGIZER | 14 |
STAR | 15 * |
CLOCKWISE | 16 |
COUNTER | 17 |
BULLET | 18 ** |
WATER | 19 *** |
LAVA | 19 *** |
FOREST | 20 |
SOLID | 21 |
NORMAL | 22 |
BREAKABLE | 23 |
BOULDER | 24 |
SLIDERNS | 25 |
SLIDEREW | 26 |
FAKE | 27 |
INVISIBLE | 28 |
BLINKWALL | 29 |
TRANSPORTER | 30 |
LINE | 31 |
RICOCHET | 32 |
_BEAMHORIZ | 33 **** |
BEAR | 34 |
RUFFIAN | 35 |
OBJECT | 36 |
SLIME | 37 |
SHARK | 38 |
SPINNINGGUN | 39 |
PUSHER | 40 |
LION | 41 |
TIGER | 42 |
_BEAMVERT | 43 **** |
HEAD | 44 |
SEGMENT | 45 |
FLOOR | 47 |
WATERN | 48 |
WATERS | 49 |
WATERW | 50 |
WATERE | 51 |
ROTON | 59 |
DRAGONPUP | 60 |
PAIRER | 61 |
SPIDER | 62 |
WEB | 63 |
STONE | 64 |
_TEXTBLUE | 73 ***** |
_TEXTGREEN | 74 ***** |
_TEXTCYAN | 75 ***** |
_TEXTRED | 76 ***** |
_TEXTPURPLE | 77 ***** |
_TEXTBROWN | 78 ***** |
_TEXTWHITE | 79 ***** |
Special Notes:
* | When Super ZZT worlds are loaded, type 72 is converted to 15 automatically. |
** | When Super ZZT worlds are loaded, type 69 is converted to 18 automatically. |
*** | The functionality is the same in ZZT and Super ZZT, but the appearance and message displayed on contact are different. ZZT Ultra only changes details about this type when the world file changes. |
**** | Super ZZT worlds represent blink wall beam types as 70 and 71; they are converted to 33 and 43 automatically. |
***** | When ZZT worlds are loaded, text types are converted from 47-53 to 73-79. This runs opposite most other type harmonization decisions, because it favors the mappings from Super ZZT instead of ZZT. |
If an entirely new type is defined in ZZT Ultra, its name must correspond to one
of the numbers not mapped in the above list in order for its name to be considered
well-known for ZZT-OOP. This is not difficult, as most of the number slots were never
used in the original board RLE format.
Board/World Properties
World and board properties are read and written using #GETPROPERTY and #SETPROPERTY.
When #SETPROPERTY is called, $ONPROPERTY is dispatched to the main type code with
$PROP set to the property name.
Board Properties
BOARDNAME | The board name. This title is set in the ZZT editor. |
SIZEX | The board X-size, usually 60 for ZZT and 96 for Super ZZT.
ZZT Ultra allows the board X-size to be just about any positive number, and boards need
not have the same uniform size over the entire world. |
SIZEY | The board Y-size, usually 25 for ZZT and 80 for Super ZZT.
ZZT Ultra allows the board Y-size to be just about any positive number, and boards need
not have the same uniform size over the entire world. |
MAXPLAYERSHOTS | The maximum number of shots the player can have on
the screen at once. |
CURPLAYERSHOTS | The current number of shots the player has on the
screen. |
ISDARK | Equals 0 if board is lit and 1 if board is dark. |
EXITNORTH | Board number of the board linked in the north direction.
Zero indicates no linkage. |
EXITSOUTH | Board number of the board linked in the south direction.
Zero indicates no linkage. |
EXITWEST | Board number of the board linked in the west direction.
Zero indicates no linkage. |
EXITEAST | Board number of the board linked in the east direction.
Zero indicates no linkage. |
RESTARTONZAP | Equals 0 for normal damage profile and 1 for "Re-enter
when zapped" damage profile. |
MESSAGE | Saved toast message string. This is maintained for
compatibility with ZZT world files; Super ZZT and ZZT Ultra do not use this property. |
PLAYERENTERX | Saved player X position. This is used with "Re-enter
when zapped". |
PLAYERENTERY | Saved player Y position. This is used with "Re-enter
when zapped". |
CAMERAX | Camera X position (left edge). This is used for boards
that scroll (all Super ZZT boards and possibly some ZZT Ultra boards). |
CAMERAY | Camera Y position (top edge). This is used for boards
that scroll (all Super ZZT boards and possibly some ZZT Ultra boards). |
TIMELIMIT | Number of seconds in board's time limit. If this
is zero, there is no time limit. |
PLAYERCOUNT | Usually equals 1. This quantity can grow to a larger
value if PLAYER clones are placed in the board. |
FROMPASSAGEHACK | Equals 0 if last board navigation to this board was
ordinary navigation. Equals 1 if navigation is the result of DUPLICATOR passage navigation
hack. |
World Properties
WORLDTYPE | The world type code. -1=ZZT, -2=Super ZZT, -3=ZZT Ultra. |
WORLDNAME | The world name. |
THISGUI | The name of the current GUI shown. |
LOCKED | Equals 0 if unlocked and 1 if locked. This is maintained
for compatibility with ZZT world files; it does not actually "lock" the world in
ZZT Ultra. |
NUMBOARDS | Number of boards in ZZT world. |
NUMBASECODEBLOCKS | Number of code blocks for types alone, before
custom code is compiled. |
NUMCLASSICFLAGS | Number of global variables set using the boolean
mechanism. This limits the number of global flags set for the world when compared
against CLASSICFLAGLIMIT. |
LASTCLASSICFLAG | Name of last global variable set using the boolean
mechanism. This flag would be overwritten if the maximum number of global flags has been
reached. |
CODEDELIMITER | String representing the delimiter used as a ZZT-OOP
line separator. ZZT and Super ZZT worlds set this to "\r". Worlds built explicitly
for ZZT Ultra set this to "\n". |
BOARD | Currently active board number. |
AMMO | (Inventory) |
GEMS | (Inventory) |
HEALTH | (Inventory) |
TORCHES | (Inventory) |
SCORE | (Inventory) |
TIME | (Inventory) |
Z | (Inventory) |
TORCHCYCLES | Number of cycles remaining on active torch.
Equals zero if no torch is active. |
ENERGIZERCYCLES | Number of cycles remaining on active energizer.
Equals zero if no energizer is active. |
KEYnn | The "key" properties represent that a key of color code nn
is present in inventory. The count is usually somewhere between 0 and 1, but it can be
larger in ZZT Ultra. |
ISSAVEGAME | When a WAD file is created, this property is set. The
value equals 0 for an ordinary WAD file and 1 for a savegame WAD file. |
HIGHESTOBJPTR | When a WAD file is created, this property is set to
the "next" unique identifier for when a new status element is created. |
EVERPLAYED | This is set to 0 when a world is originally loaded.
When the game is launched "for real" (i.e. a world is "played" in a context outside of
the title screen), this is set to 1. |
"Configuration" World Properties
Some world properties are used in a "configuration" capacity. These properties are
not part of any one world per se, but rather assigned to the world properties as a means
of changing the overall game behavior.
GEMHEALTH | How much health is gained by picking up a single gem.
Set to 1 for ZZT and 10 for Super ZZT. |
MAXSTATELEMCOUNT | Maximum number of status element objects permitted
per board. Set to 151 for ZZT and 129 for Super ZZT. While this could be overridden and
increased, there are notable consequences that can come from letting a board fill up too
much. For example, "The Three Lakes" in Town of ZZT ends up being a lot more
challenging. |
CLASSICFLAGLIMIT | Original global flag count limitations are
reproduced with this property. Limits are 10 (for ZZT) and 16 (for Super ZZT). Worlds
created specifically for ZZT Ultra should have a much higher limit (i.e. 1000000). |
NOPUTBOTTOMROW | This controls whether the bottom row of
the grid will be clipped against #PUT. ZZT sets this to 1, while Super ZZT sets this to 0. |
BECOMESAMECOLOR | This controls which color #BECOME leaves
behind if the type used in #BECOME matches UNDERID. ZZT sets this to 0, which makes the type use
UNDERCOLOR. Super ZZT sets this to 1, which makes the type use the actual color of the OBJECT. |
LIBERALCOLORCHANGE | This controls how #CHANGE statements handle
color qualifiers in the source type. ZZT sets this to 0, which means a color qualifier must match
all 16 bits of the foreground color. Super ZZT sets this to 1, which only distinguishes the first
3 bits of the foreground color (light and dark colors are considered equivalent). |
ZSTONELABEL | This text label is used in Super ZZT to identify
the text to show for the "Z" counter. The default text is "Stone". The act of setting a flag
starting with the letter "Z" when a Super ZZT world is loaded will change this text to the flag
text, minus the letter "Z". |
FREESCROLLING | Default is 0 for ZZT and WAD, and 1 for Super ZZT.
A value of 1 causes the player to be constantly re-centered within the viewport after each move.
This generally only needs to be 1 when the viewport is smaller than the board, so that scrolling is
required. |
LEGACYCAMERA | Default is 0 for ZZT and WAD, and 1 for Super ZZT.
A value of 1 honors the unusual Super ZZT loose centering range of (Left=10, Right=11, Top=9,
Bottom=7). |
SENDALLENTER | Default is 0 for ZZT and WAD, and 1 for Super ZZT.
A value of 1 causes #SEND ALL:ENTER to be executed whenever the board is changed. |
GAMESPEED | The game speed setting. Default is 4.
The amount of time per game tick iteration is controlled by this quantity. The following
frame counts are set for game speeds:
Speed Frames Virtual Hz
0 1 30 *
1 1.25 24
2 1.648351648 18.2
3 2.197802198 13.65
4 3.2967032967 9.1
5 3.5964 8.341675
6 3.95603526 7.58335
7 4.39558829 6.825025
8 4.945054945 6.066667
The original ZZT only implemented four distinct speeds despite having
nine selections. These would have been 0, 2, 4, and 8, with other selections
aliased as either some multiple of the 18.2-Hz rate or unlimited frame rate.
* Also see FASTESTFPS.
|
| |
FASTESTFPS | This modifies the "Virtual Hz"
for the fastest speed setting. This is 30 by default, but it can be set to any
rate between 1 and 10000. It is not reasonable to expect a completely unthrottled
frame rate for the fastest setting (which the original ZZT had), but it is possible
to make the fastest game speed operate at a rate above what can be displayed by
the application. |
LEGACYTICK | Default is 0 for WAD files and 1 for ZZT
and SZT files. A value of 0 indicates that cycle delay counters exist uniquely for each
individual object in the board. A value of 1 implements a legacy "master tick modulo"
ranging from 1 to 420 and ticking upwards, which iterates objects only if their order
within the status element buffer for the board matches the master tick value, after both
integers had been modded by the CYCLE value. The master tick value itself lacks
determinism as implemented in the original engines, but its is nonetheless necessary for
some ZZT worlds that rely upon object iteration in a specific order. |
OBJMAGICNUMBER | Default is 32. This controls the
maximum number of 'own-code' legacy commands per turn, such as for OBJECT and SCROLL
types. |
SOUNDOFF | Default is 0, indicating that sound
should be played in ZZT Ultra. If this is 1, no sound is played. |
MASTERVOLUME | Default is 50, indicating that
sound channels are all at maximum volume (non-attenuated) by default. By setting
this to a number less than 50, one can attenuate all channels. See the #PLAY
statement "Vnn" code for information about volume levels. |
IMMEDIATESCROLL | Default is 0, indicating that a ZZT
scroll interface should "scroll" open and shut as it normally would have done in the
classic engines. If this is 1, the scroll is displayed immediately without a delay.
The editor sets IMMEDIATESCROLL to 1 for editing convenience. |
ORIGINALSCROLL | Default is 0, indicating that the
scroll interfaces should expand in both horizontal and vertical directions. If this is
1, the scroll expands only in the vertical direction, which was the original ZZT
behavior. This property has no effect if IMMEDIATESCROLL is 1. |
OVERLAYSCROLL | Default is 1, indicating that the
modern "overlay" scroll interface should be shown, which is less obtrusive and has a
drop-shadow. Set to 0 to display the original "opaque" scroll. |
OLDTORCHBAR | Default is 0, indicating that the
new incremental torch progress bar is shown. Set to 1 to display the older torch
progress bar, which is only four units long with less incremental indication. |
HIGHSCOREACTIVE | Default is 1, indicating that high
scores are recorded and shown. If this is 0, high scores are not shown or posted for
any world. |
HIGHSCOREMIN | Default is 100. This tells ZZT Ultra
to only show a high score prompt when the user scores at least this high. |
HIGHSCOREPROMPT | Default is 1. This tells ZZT Ultra
what would prompt for a high score: 0=Never, 1=Any time the game ends, 2=Only when the
global variable $PLAYERMODE=2 (after #ENDGAME or player has lost all health). |
PLAYERDAMAGE | How much damage the player takes from
a typical hit. Default is 10. |
KEYLIMIT | How many keys of a typical color the player
can carry at once. Default is 1. Increasing this alleviates one of the annoyances of
ZZT, which is the need for forced backtracking if the player fails to unlock a door and
winds up with more than one key of the same color. However, some puzzles are explicitly
designed to force the player to backtrack, so overriding this setting might make some
challenges easier than they are intended to be. |
KEYSBLOCKPLAYER | Set to 1 if keys block the player
from moving over them if inventory is full for that key's color. Default is 1. If set
to zero, failure to pick up the key will still let the player pass over the key. This
alleviates another problem with key pickup limits: the player's progress can be stopped by
uncollectable keys. |
POINTBLANKFIRING | Set to 1 if player and object shots can
send SHOT messages to targets when #SHOOT is called at point-blank range. Even though
1 is the default, the original behavior for ZZT would have had this set to zero (no point-blank
firing). Note that this setting does not affect some intrinsic tests for point-blank shots,
such as the player standing just next to a SPINNINGGUN, or the player shooting a lion
point-blank. |
BLINKWALLBUMP | Set to 0 by default, which bumps the
player from a blink wall beam strike in a "smart" way, never causing instant death. If set
to 1, the original ZZT behavior is used, which bumps the player if bumping is possible, and
kills the player if bumping is not possible. |
TELOBJECT | Set to 0 by default. This forces the commands
/, ?, #GO, and #TRY to prevent push interactions between OBJECT types and TRANSPORTER types.
The $PUSHBEHAVIOR dispatched messages would normally trigger push-related calculation
logic if an OBJECT tries to move into a TRANSPORTER, which throws off push calculation logic
significantly. If TELOBJECT is set to 1, the OBJECT type would interface with TRANSPORTER
types (and create problems). |
DETECTSCRIPTDEADLOCK | Set to 1 by default. This catches
when a script appears to be going on for too long, and stops execution in such a case.
Setting this to 0 allows scripts to be run indefinitely (potentially with deadlock condition
if a script is poorly written, such as lack of /I in an infinite loop). |
PLAYRETENTION | Set to 1 by default. This prepends the
note configuration "Z01@" to all #PLAY commands, which gives the #PLAY command a
unique channel apart from built-in sound effects, as well as an automatic octave
and duration reset. Setting this to 0 makes the #PLAY commands processed totally raw,
which means playback that is manifested on the same channel as effects (the original
behavior). |
PLAYREVERB | Set to 1 by default. This prepends the
note configuration "K40:0.3:" to all #PLAY commands, which gives the #PLAY command a
bit of reverb. Setting this to 0 will not give reverb to #PLAY commands (the original
behavior). |
PLAYSYNC | Set to 1 by default. This applies a special
"code-towing-forward" behavior to an OBJECT that is playing music with #PLAY commands
interspersed with /i statements to simulate delays and looping. Whenever the channel-1
playback queue becomes empty, PLAYSYNC, if set to 1, will advance the IP of the OBJECT that
played the last note in order to locate more notes to play immediately. This has the effect
of erasing momentary playback delays resulting from non-synchronized idle cycles.
Play synchronization will only happen for #PLAY statements that use channel 1, and will only
tow an object's IP forward if at least one whole note worth of note content had been played. |
PLAYERRUNDELAY | Set to 8 by default, which indicates the
time delay before held-key rapid run is logged. This setting simulates a typematic delay
regardless of what the typematic delay is set to on the hosting machine. |
PLAYERFIREDELAY | Set to 8 by default, which indicates the
time delay before held-key rapid fire is logged. This setting simulates a typematic delay
regardless of what the typematic delay is set to on the hosting machine. |
BLACKKEYGEMS | Set to 0 by default. This treats a black
key as just one more key color. Setting this to 1 re-instates a strange quirk of ZZT that
gives 256 gems to the player when a black key is picked up. |
BLACKDOORGEMS | Set to 0 by default. This treats a black
door as just one more door color. Setting this to 1 re-instates a strange quirk of ZZT that
takes 256 gems away when a black door is opened (the door cannot be unlocked if the
player has fewer than 256 gems). |
ALLCOLORKEYS | Set to 0 by default. Setting this to 1
treats key colors between 0 (BLACK) and 7 (GREY) as their own unique KEYnn properties when a
key of the corresponding color is touched. The default behavior aliases 0-7 color attributes
for keys as 8-15 color attributes, which is necessary for a few ZZT and Super ZZT games.
Note that WAD files ignore ALLCOLORKEYS--all key colors are considered unique for WADs. |
PLAYERCHARNORM | Player's normal character. Default is 2. |
PLAYERCOLORNORM | Player's normal color. Default is 31. |
PLAYERCHARHURT | Player's "hurt" character. Default is 1. |
PLAYERCOLORHURT | Player's "hurt" color. Default is 127. |
PAUSEANIMATED | Default is 1. Set to 0 to inhibit the
flashing player pause animation. |
MOUSEBEHAVIOR | Set to 3 by default, which allows the
mouse to be used to identify move and shoot locations instead of moving the player via
analog motion check. Other possible values for MOUSEBEHAVIOR:
Value Behavior
0 No click-to-move; custom behavior
1 Click-to-move, with minor-nav vector picked before major-nav vector
2 Click-to-move, with major-nav vector picked before minor-nav vector
3 Click-to-move, with jagged vector picked
This property only governs the behavior of the mouse within the game. The mouse will
always function in a menu-and-scroll capacity within the GUI itself.
A value of 0 for MOUSEBEHAVIOR causes mouse clicks to dispatch the $ONMOUSE
message to the main type code, where #READMOUSE can be used to poll the mouse position.
|
| |
MOUSEEDGENAV | Set to 1 by default. Click-to-move
mouse action when the fringes of the board are clicked can be tweaked to control how
much of the cell counts towards adjacent board navigation or just simple navigation to
the cell itself, without moving to the adjacent board. 0=No board navigation;
1=Board navigation at 50%, and 2=Board navigation at 100% (for the entire cell). |
MOUSEEDGEPOINTER | Set to 1 by default. Click-to-move
mouse action is revealed when the mouse pointer hovers over the fringes of the board, via
a flashing arrow. If the property is set to zero, the arrow does not appear, although the
action (governed by MOUSEEDGENAV) still applies upon a click. |
BOARDEDGETRANS | Set to 1 by default. When a BOARDEDGE
type is touched by the player, the transition effect will be either a dissolve or a scroll
effect depending on this property. 0=Scroll effect always; 1=Scroll effect when player at
edge of board only; 2=Dissolve effect only. |
OBJECTDIEEMPTY | Set to 1 by default. When an OBJECT
dies with #DIE, it leaves behind a WHITE EMPTY. If the property is set to 0, an OBJECT
leaves behind its UNDERID like any other status element. |
REENTRYMOVESTYPE | Set to 0 by default. This retains the
type and color underneath the player when the player enters a board from a different location
from which it was left. Set to 1 to restore the original ZZT behavior, which had buggy
"fake pickup" action when re-entering a board from a passage. |
SCORELIMIT | Set to 2000000000 by default. The original
score limit was 32767. |
AUTOSAVESECS | Set to 60 by default. Controls the number
of seconds between time-based autosaves. An autosave does not occur when the game is paused,
or if the player remains motionless. |
BOARDCHANGESAVESECS | Set to 20 by default. Controls the
number of seconds between board-change-based autosaves. It does not make sense to log
separate save instances too often if navigation would take the player between different boards
in a rapid succession. |
REENTRYZAPSAVESECS | Set to 10 by default. Controls the
number of seconds between autosaves triggered by zapped re-entry. |
MAXSAVESTATES | Set to 30 by default. Controls how many
save instances are remembered at one time. If this limit is reached, the oldest instances
are discarded to make room for the newest ones. |
SITELOADCHOICE | When #LOADWORLD 0 is invoked, this property
controls what appears in the scroll:
Value Behavior
0 Local files of same type of world only
1 Choice of local files of any type of world; no site options
2 Only site configuration; no local load options
3 The default; shows site configuration AND local load options
|
DEP_AUTORUNZIP | Set by deployed configuration; defaults to
zero if not set. This indicates the behavior when a ZIP archive containing only one game file
is loaded: 0=browse contents; 1=load game file. |
DEP_EXTRAFILTER | Set by deployed configuration; defaults to
"" if not set. This indicates a filter pattern (*.zip, *.wad, *.zzt, etc.) to apply when
#LOADWORLD 0 is invoked. |
SCANLINES | See the #SCANLINES command. |
BIT7ATTR | See the #BIT7ATTR command. |
SCRCOLBORDER | See the #SCROLLCOLOR command. |
SCRCOLSHADOW | See the #SCROLLCOLOR command. |
SCRCOLBG | See the #SCROLLCOLOR command. |
SCRCOLTEXT | See the #SCROLLCOLOR command. |
SCRCOLCENTERTEXT | See the #SCROLLCOLOR command. |
SCRCOLBUTTON | See the #SCROLLCOLOR command. |
SCRCOLARROW | See the #SCROLLCOLOR command. |
CONFIGTYPE | Indicates the configuration style picked:
0=Modern, 1=Classic. In principle, either of these styles can be customized to have any
combination of properties, but it can be helpful to detect which style is active. |
WATERSOUND | Defaults to 1. Set to zero to turn off
water/lava contact sound effect. |
WATERMSG | Defaults to 1. Set to zero to turn off
water/lava contact message. |
INVISIBLESOUND | Defaults to 1. Set to zero to turn off
invisible wall contact sound effect. |
INVISIBLEMSG | Defaults to 1. Set to zero to turn off
invisible wall contact message. |
FORESTSOUND | Defaults to 1. Set to zero to turn off
forest contact sound effect. |
FORESTMSG | Defaults to 1. Set to zero to turn off
forest contact message. |
FAKEMSG | Defaults to 1. Set to zero to turn off
fake wall contact message. |
DUPSOUNDDIST | Defaults to 1000. This controls the
maximum number of squares away from the player from which a DUPLICATOR's sound can be heard.
Setting this number to something small, like 8, will cause the duplicator sound to play
only when the player is within 8 squares (horizontally or vertically). A value of zero will
completely silence duplication noises. |
ALLOWINGAMERESTORE | Defaults to 1. Set to zero to
turn off in-game savegame/save state restoration. |
ALLOWINGAMECONSOLE | Defaults to 1. Set to zero to
turn off in-game console availability. |
ALLOWINGAMECHEAT | Defaults to 1. Set to zero to
turn off in-game cheat availability. |
FASTCHEATFLAG | Defaults to 0. Set to 1 to treat
simple text in the cheat box as if it had been a flag-set cheat (i.e. +text). |
INVENTORYFLAG | Defaults to "I". ZZT Ultra lets users
activate a common "inventory" status action in ways that previously required using the
cheat box to set a flag. The way this works is that the "I" key sets or clears whatever
flag is in INVENTORYFLAG. A flag can be cleared instead of set by preceding it with a
hyphen. Some examples:
Evil Sorceror's Party works if INVENTORYFLAG is set to "I".
Pepper Bolette works works if INVENTORYFLAG is set to "-M".
|
EXTRAINVTIMEOUT | When an extra inventory item is
changed using #GIVE or #TAKE, a label temporarily indicates what is updated in the game GUI.
This label is shown for the number of ticks set by EXTRAINVTIMEOUT. Setting this number to
zero prevents a temporary label from being shown. |
ZZTGAMEGUI | Defaults to "ZZTGAME". Set to the string
that represents the main game GUI of a ZZT game. The "classic" configuration alters this
to "CLASSICZZTGAME" to better represent the older interface. |
SZTGAMEGUI | Defaults to "SZTGAME". Set to the string
that represents the main game GUI of a Super ZZT game. The "classic" configuration alters
this to "CLASSICSZTGAME" to better represent the older interface. |
BQUESTHACK | This identifies that a Banana Quest world is
being played. The default is 0; a value of 1 indicates that namespace replacement and a
variety of other engine modifications must take place as part of loading and running the world.
|
VERSION | The property contains text representing the
current version of ZZT Ultra. This is not a configuration setting in the strictest
sense, but it can be useful for a program to query. |
ZZT-OOP code can define any number of additional world properties.
Sound Playback
Sound in ZZT Ultra is generated by #PLAY strings, which are strings containing
note- and effect-encodings. Some of these strings are stored in a world file (or
as defaults by ZZT Ultra) for quick playback with #PLAYSOUND. Other strings are invoked
from #PLAY commands, which are usually stored within custom object code itself.
Click here to learn about the built-in sound effects.
ZZT Ultra supports all the old ZZT and Super ZZT #PLAY string syntax, and it adds
several new codes of its own.
Old codes
The following codes play a note at the current duration and octave.
C | (Self-explanatory) |
D | (Self-explanatory) |
E | (Self-explanatory) |
F | (Self-explanatory) |
G | (Self-explanatory) |
A | (Self-explanatory) |
B | (Self-explanatory) |
If a note code has a suffix of #, it moves up by a half step (a sharp).
If a note code has a suffix of !, it moves down by a half step (a flat).
The following codes play a rest or percussion sound at the current duration.
Realistically, identifying these sounds as such takes more than a little imagination.
Such is the nature of ZZT, of course. The names come directly from the original in-game
ZZT-OOP documentation.
X | Rest |
0 | Tick |
1 | Tweet |
2 | Cowbell |
4 | Hi snare |
5 | High woodblock |
6 | Low snare |
7 | Low tom |
8 | Low woodblock |
9 | Bass drum |
The following codes set the duration for subsequent notes. The duration
starts at thirty-second note speed (T).
W | Whole note |
H | Half note |
Q | Quarter note |
I | Eighth note |
S | Sixteenth note |
T | Thirty-second note |
The following codes are duration modifiers.
. | Multiplies previously set duration by 1.5. |
3 | Divides previously set duration by 3. |
The following codes manipulate octaves. The octave starts at 4, and
can be dropped as low as 2 or raised as high as 7.
- | Move octave down for subsequent notes. |
+ | Move octave up for subsequent notes. |
New codes
ZZT Ultra enhances the playback possibilities with new codes.
Znn | The two-digit number identifies the channel
on which the subsequent notes will be played. The number ranges from 00 to 15.
The original PC speaker did not support multiple channels on a typical IBM PC.
A handful of programs in the 1980s supported the Tandy 3-tone, but ZZT did not
take advantage of this functionality. Multiple channels are offered by ZZT Ultra
as a matter of convenience. |
Pnn | The two-digit number identifies a priority
effect, which usually appears as the first code on a line after the channel is set.
When a #PLAY string must be queued for a particular channel, the following rules
apply:
- If the priority of the new queued content is GREATER than the priority
of the old queued content, the new queue replaces the old queue.
- If the priority of the new queued content is LESSER than the priority
of the old queued content, the new queue is discarded.
- If the priority of the new queued content is EQUAL to the priority
of the old queued content, the new queue is appended to the end of the old queue.
|
Pnn: | This is a variation on the priority effect,
which defines "self-overridden mode." This has the effect of replacing the old queue
with the new queue even if the priority of old and new queues are equal. This
is normally used with same-channel sound effects that must be reset due to
"rapid-fire" effects, such as bullet-firing, etc. |
@ | This resets the octave to 4 and the duration
to T. |
J | This represents a 64th-note duration. The
original ZZT sound system might have had trouble accounting for note durations this
short, but ZZT Ultra can handle them. |
Onn | This changes the octave to a specific number.
The octave can range between 0 and 7. Even though the + and - codes are still limited
to the range of 2 to 7, this code lets a composer reach two additional low octaves. |
Vnn | The two-digit number identifies volume level
for the current channel. Max volume level is 50, half of maximum is 45, quarter of
maximum is 40, etc. Volume level of 00 is totally silent. |
Unnn | The three-digit number identifies the tempo
(quarter notes per second) for ALL channels. The default tempo (for the original ZZT)
is the somewhat unusual 960/7 = 137.142857143 quarter notes per second. |
Unnn: | This is a variation on the tempo modifier,
which affects only the current channel. |
Knn:nnnnn: | The first two-digit number identifies an
echo attenuation factor (same attenuation effect levels as V setting), while the second
number (which can be a floating-point number of any length) identifies the echo delay
length in seconds. Setting the first number to 00 cancels the echo effect for the
channel. |
%nnnnn:nnnnn:nnnnn:nnnnn: | This identifies a
frequency sweep. There are four floating-point values: start frequency, end frequency,
frequency increment per duration, and length of single duration in seconds. The original
ZZT did not support this type of sound-generation logic, but ZZT Ultra facilitates this
mechanism on the basis that other programs that supported the PC speaker did so. |
Rname: | This identifies a "repeat" or "reload" effect
name (the same type of name that would be played from #PLAYSOUND). When this code is
encountered, the queue is immediately appended with the effect mentioned. For this
reason, it is normally appropriate to place this effect near the end of the effect's
queue.
One can use the "repeat" effect to loop background music.
|
All other unmapped characters in a PLAY string are ignored.
Masks
The original ZZT defined two types of masks (both were the same size and shape):
one for bomb blasts, and one for torch lighting masks. These are represented in ZZT
Ultra as the masks named BOMB and TORCH respectively. Super ZZT had a
slightly modified bomb mask (more square aspect). ZZT Ultra supports this modified
mask as SZTBOMB.
A custom mask can be stored in a world file. To retrieve the mask contents, one
uses the #FORMASK command, which iterates over coordinates covered by the "1" slots
of the mask, while ignoring the "0" slots. Mask portions that are clipped against
the sides of the board are not iterated in #FORMASK.
What the bomb/torch mask looks like as defined for ZZT:
"000111111111000",
"001111111111100",
"011111111111110",
"011111111111110",
"111111111111111",
"011111111111110",
"011111111111110",
"001111111111100",
"000111111111000"
What the bomb mask looks like as defined for Super ZZT:
"000011111110000",
"001111111111100",
"011111111111110",
"011111111111110",
"111111111111111",
"111111111111111",
"111111111111111",
"111111111111111",
"111111111111111",
"111111111111111",
"111111111111111",
"011111111111110",
"011111111111110",
"001111111111100",
"000011111110000"
Type Definitions in ZZT Ultra
In order to properly emulate the ZZT and Super ZZT game environments,
ZZT Ultra supports definitions for the built-in types. The default file,
zzt_objs.txt, contains the default definitions, and is loaded by
ZZT Ultra automatically when it initializes. A world file can provide
extensions or overrides for these type definitions.
The type definitions are encoded as a JSON dictionary, with the individual
definitions keyed to the type names, and the values equalling a dictionary with
object properties. Some properties are required, while others are optional.
The following covers the meaning of the object properties.
NUMBER
The type index associated with the type. For the most part, these are the
same as the numbers used to represent the types in RLE data within the ZZT and
Super ZZT world files, although ZZT Ultra attempts to harmonize the numbers after
loading world files to prevent conflicts between ZZT and Super ZZT
(see Types).
When a world file is loaded in ZZT Ultra, the RLE type information must match a
type's NUMBER. If the type does not match any definition's NUMBER,
the EMPTY type (0) is placed in the grid instead.
CHAR
The default character used to display the type.
COLOR
The default color attribute used to display the type.
NOSTAT
Equals 1 if the type is not designed to be associated with a status element
object, and 0 if a status element object should be generated whenever the type
is placed.
ALWAYSLIT
Default is 0, which means the type is shown according to standard lighting effects.
If this is set to 1, the type is "always lit," meaning, it is always drawn as lit even
when the room is dark and the square is not lit. The original ZZT behavior set
ALWAYSLIT for only three types: PLAYER, PASSAGE, and TORCH.
DOMINANTCOLOR
Default is 0, which allows the type to accept any color customization. If this
is set to 1, the placement-oriented commands (#PUT, #CHANGE, #SPAWN, etc.) will not
allow color customization. Dominant colors are used for types like ammo, forest,
and lions. Note that it is still possible to customize the color for such a type
by using a modified color in the board data.
TEXTDRAW
Default is 0, which means the type is drawn with normal character and color rules.
If this is set to 1, the meaning of type and color are flipped, which makes the 0-255
color attribute represent a character, and the type represent a predefined color for
the text.
CUSTOMDRAW
Default is 0, which means drawing uses normal character/color rules when drawing
unless TEXTDRAW is defined. If CUSTOMDRAW is set to 1, the
$CUSTOMDRAW message is dispatched to the type code with X and Y member variables
set to the location of the type that is to be drawn. This routine must set the
character and color.
CUSTOMDRAW is used in a variety of types, but $CUSTOMDRAW routines
are the most complex with types such as LINE or WEB, whose appearances
are highly dependent on the surrounding type presence.
BLOCKOBJECT
Default is 0, which means standard movement from an object will overrun the type.
If set to 1, standard movement will be blocked under most circumstances.
BLOCKPLAYER
Default is 0. This is usually the same as BLOCKOBJECT. It is only used to
evaluate click-to-move navigation by the PLAYER. For example, an ENERGIZER
would block most objects, but click-to-move would consider it to be nonblocking because
it can be collected.
PUSHABLE
Default is 0, which means the type cannot be pushed. Other values include 1 and 2.
If set to 1, the type can be pushed around freely. If set to 2, the type can be pushed
only under special circumstances. For PUSHABLE=2, the $PUSHBEHAVIOR
message is dispatched to the type code to determine the conditions for whether or not
the type can be pushed.
SQUASHABLE
Default is 0, which means the type cannot be "squashed" when a push operation
occurs and there is no clearance for moving it. Setting this to 1 will allow push
operations to "squash" the type if clearance is otherwise exhausted.
The following properties are only used when NOSTAT is equal to 0.
CYCLE
The default cycle associated with the object.
STEPX
The default X-step associated with the object.
STEPY
The default Y-step associated with the object.
FULLCOLOR
Default is 0, which limits the object's color to only bits 0-3 and 7 when it moves.
If this is 1, movement will take all 8 bits of color with it. Only PASSAGE
and PLAYER types need to retain all color bits.
HASOWNCHAR
Default is 0, which means standard code is used to determine character to draw.
If this is 1, the character to draw is determined by the CHAR member variable
set for the object.
HASOWNCODE
Default is 0, which means the object's code is self-contained within the current
definition. If this is 1, ZZT Ultra will expect to have the type definition's own
code extended by custom ZZT-OOP code. OBJECT and SCROLL types have
HASOWNCODE=1.
ZZT Ultra treats objects with custom code as a "hybrid" of the code portion for
the type itself and the custom code portion. The code for these two portions are
"stacked back-to-back" during compilation, such that the type's portion is at the
beginning and the custom portion is at the end. This means the code for the type
must be organized in such a way that the custom code portion is handled reasonably
as a result of dispatched and sent messages.
The SCROLL type offers an example of how to visualize this:
'----------------------
'START BUILT-IN PORTION
'----------------------
:SLOOP
/i
#IF TEST(.COLOR & 15 = 15) CWRAP
#COLOR (.COLOR+1)
#SLOOP
:CWRAP
#COLOR 9
#SLOOP
:$DISPSCROLL
#DONEDISPATCH
'----------------------
'END BUILT-IN PORTION
'----------------------
'----------------------
'START CUSTOM PORTION
'----------------------
Hello, I'm a lumberjack.
And I'm okay.
'----------------------
'END CUSTOM PORTION
'----------------------
CUSTOMSTART
Default is 0, which means the object's #RESTART location
is located at the genuine beginning of the built-in portion of the code. If this is
1, the end of the built-in portion (and the start of the custom portion) is where
#RESTART will go, instead. OBJECT and SCROLL types have
CUSTOMSTART=1.
Extra values
All other object properties set defaults for the object at the time it is created.
For example, setting the P1 property guarantees that the object will have
P1 set to a default when it is created.
Dispatched Message Handling
When a message is dispatched, ZZT-OOP code runs until it returns. If a message
is dispatched using the #DISPATCH or #DISPATCHTO commands, ZZT Ultra employs a stack,
which keeps track of the last object and IP of the calling location.
A dispatched message begins at a label. It ends when #END is encountered, the
underlying object dies, or if turns are exhausted.
Dispatched messages can go to any type. However, if a dispatched message is sent
to a type that does not have a status element object associated with the type
(i.e. NOSTAT=1), there are certain actions that the ZZT-OOP code must not
perform, such as attempting to move, or set member variables. This is because the
member scope is transitory--.X, .Y, .TYPE, .CHAR, and .COLOR might
evaluate properly, but nothing else will. Attempting to "abuse" the empty object
template used for no-stat types can cause problems.
The following dispatched messages are sent by ZZT Ultra:
$WALKBEHAVIOR
After an object's real-time iteration is over, this is dispatched to the type if
the message exists. The object is always guaranteed to have full representation of
member scope because $WALKBEHAVIOR is only sent while processing real-time
iterations for existing objects.
The FLOW direction represents the object's step direction. ZZT-OOP code
can also make use of the individual X and Y components of the step direction by
referring to .STEPX and .STEPY.
The OBJECT type uses $WALKBEHAVIOR to take an additional non-pushing
move. The $WALKBEHAVIOR routine, as defined for the OBJECT type, sends
the THUD message. #DONEDISPATCH is used to ensure that THUD is
processed as a "sent" message instead of a dispatched message.
$PUSHBEHAVIOR
This message is dispatched to a type with PUSHABLE=2 to find out about
whether or not it can be pushed. At the time the message is processed, ZZT Ultra
sets global variable $PUSHDIR to the push direction attempted. If the push is
allowable, the routine must set the global variable $PUSH to 1. If the push
is not possible, the routine must leave $PUSH at 0 (the default value). It
is also possible to force the push operation to squash the current location by setting
$PUSH to 2.
The SLIDERNS and SLIDEREW types have very simple $PUSHBEHAVIOR
routines because they need only accept two push directions and reject the other two.
The TRANSPORTER type, though, is much more involved.
If the push behavior requires "moving" the push evaluation location to a new
position, the routine must set $PUSH to 3. For this code, the routine must
also set two additional global variables: $PUSHDESTX and $PUSHDESTY.
These global variables default to the current evaluation position when
$PUSHBEHAVIOR is sent out, but it can be moved to a different location.
A transporter moves these coordinates by one step (if push clearance exists just
beyond transporter) or to the next opposing-direction transporter (if no push
clearance exists just beyond transporter).
Theoretically, it is possible in ZZT Ultra to have push behavior trigger a
variety of actions and teleport effects that the original ZZT engine never would
have permitted.
$CUSTOMDRAW
This message is dispatched to a type with the CUSTOMDRAW property when it must
be drawn to the grid. The .X, .Y, .CHAR, and .COLOR member variables
are all set appropriately when the message is received.
The value of .CHAR is either the default character code for the type (if not
an object or HASOWNCHAR is not set) or the actual value of the .CHAR
member (if it is an object and HASOWNCHAR is set). This character is updated
with the #CHAR or #CHAR4DIR command.
The value of .COLOR is always the color attribute stored for the grid
square, which can range from 0 to 255. This color is updated with the #COLOR
command. Note that it is not absolutely necessary to set this in $CUSTOMDRAW
if only the character will be customized.
$ENDGAME
This message is dispatched to the main type code (the EMPTY type) when
#ENDGAME is executed.
$PAUSED
This message is dispatched to the main type code (the EMPTY type) for
every frame of action during a paused game state.
$SECONDINTERVAL
This message is dispatched to the main type code (the EMPTY type) after
every second of action during an unpaused game state.
$ONPROPERTY
This message is dispatched to the main type code (the EMPTY type) when
#SETPROPERTY is called. The property name is stored in the global variable
$PROP when the message is received.
$ONMOUSE
This message is dispatched to the main type code (the EMPTY type) when
the mouse is clicked within the viewport and the MOUSEBEHAVIOR property is set to
0.
$ONWORLDLOAD
This message is dispatched to the main type code (the EMPTY type) when
a world is loaded in ZZT Ultra, whether it is from a ZZT file, a SZT file, or a
WAD file. Savegame restore, however, does not dispatch the message.
World-loading behavior was hard-coded in ZZT and Super ZZT to display either
a title screen or an intro screen. ZZT starts its world immediately in an unpaused
state, showing the world's title screen board. Super ZZT starts in a paused state
and shows an intro GUI, with the title screen only shown when the user explicitly
requests it or starts a new game.
In ZZT Ultra, the initial behavior on world load can be set to do just about
anything, such as allowing for special configuration, providing an extended
introduction, or even throwing the player in the fray immediately.
$ONRESTOREGAME
This message is dispatched to the main type code (the EMPTY type) when
a savegame is restored (from a restored world file). This message is mostly used
to redraw the GUI and re-establish a paused state.
$ONRESTORESTATE
This message is dispatched to the main type code (the EMPTY type) when
a board save instance is restored. This message is mostly used to redraw the GUI
and re-establish a paused state.
$ONPOSTHS
This message is dispatched to the main type code (the EMPTY type) when
a high score post operation succeeds.
$ONFAILPOSTHS
This message is dispatched to the main type code (the EMPTY type) when
a high score post operation fails.
$ONGETHS
This message is dispatched to the main type code (the EMPTY type) when
a high score get operation succeeds.
$ONFAILGETHS
This message is dispatched to the main type code (the EMPTY type) when
a high score get operation fails.
Built-in Sound Effects
Name | Description | Sequence |
PLAYERMOVE | Player movement ticks | Z00P01:@V40K0:0: T0 |
FOREST | Player forest-clearing sound (ZZT) | Z00P02:@V40K0:0: TA |
FORESTSZT0 | Player forest-clearing sound (SZT) | Z00P03:@V40K0:0: T+F |
FORESTSZT1 | Player forest-clearing sound (SZT) | Z00P03:@V40K0:0: T+C |
FORESTSZT2 | Player forest-clearing sound (SZT) | Z00P03:@V40K0:0: T+G |
FORESTSZT3 | Player forest-clearing sound (SZT) | Z00P03:@V40K0:0: T++C |
FORESTSZT4 | Player forest-clearing sound (SZT) | Z00P03:@V40K0:0: T+F# |
FORESTSZT5 | Player forest-clearing sound (SZT) | Z00P03:@V40K0:0: T+C# |
FORESTSZT6 | Player forest-clearing sound (SZT) | Z00P03:@V40K0:0: T+G# |
FORESTSZT7 | Player forest-clearing sound (SZT) | Z00P03:@V40K0:0: T++C# |
COLLECTGEM | Player gem collection | Z00P04:@V40K0:0: T+C-GEC |
COLLECTAMMO | Player ammo collection | Z00P05:@V40K0:0: TCC#D |
COLLECTTORCH | Player torch collection | Z00P06:@V40K0:0: TCASE |
PUSHER | Player push attempt, or pusher movement | Z00P08:@V40K0:0: T--F |
BREAKABLEHIT | Breakable wall or bullet destroyed by bullet | Z00P09:@V40K0:0: -TC |
ALREADYHAVEKEY | Attempt to collect key when already in inventory | Z00P10:@V40K0:0: SC-C |
READSCROLL | Player scroll collection | Z00P11:@V40K0:0: TC-C+D-D+E-E+F-F+G-G |
COLLECTKEY | Player key collection | Z00P12:@V40K0:0: +TCEGCEGCEGS+C |
OPENDOOR | Player opens door | Z00P13:@V40K0:0: TCGBCGBI+C |
DOORLOCKED | Player unable to open locked door | Z00P14:@V40K0:0: --TGC |
INVISIBLEWALL | Player hits invisible wall | Z00P15:@V40K0:0: T--DC |
WATERBLOCK | Player hits water or lava | Z00P16:@V40K0:0: T+C+C |
DUPLICATE | Duplicator successfully duplicates | Z00P20:@V40K0:0: SCDEFG |
DUPFAIL | Duplicator unsuccessful | Z00P21:@V40K0:0: --TG#F# |
BOMBTICK1 | Bomb tick countdown (low) | Z00P22:@V40K0:0: T8 |
BOMBTICK2 | Bomb tick countdown (high) | Z00P23:@V40K0:0: T5 |
BOMBACTIVATE | Bomb activation | Z00P24:@V40K0:0: TCF+CF+C |
BOMBEXPLODE | Bomb explosion | Z00P25:@V40K0:0: T+++C-C-C-C-C-C |
TORCHOUT | Torch runs out | Z00P26:@V40K0:0: TC-C-C |
PLAYERSHOOT | Player shoots bullet | Z00P31:@V40K0:0: T+C-C-C |
OBJECTSHOOT | OBJECT shoots bullet | Z00P32:@V40K0:0: TC-F# |
RICOCHET | Bullet hits ricochet | Z00P33:@V40K0:0: T9 |
ENEMYDIE | Enemy dies | Z00P34:@V40K0:0: TC--C++++C--C |
PLAYERHURT | Player damaged | Z00P35:@V40K0:0: T--C+C-C+D# |
TIMELOW | Timed board is running out of time | Z00P42:@V40K0:0: I.+CFC-F+CFQ.C |
ENERGIZER | Player energizer collection | Z00P43:@V40K0:0: S.-CD#EF+F-FD#C+C-CD#E+F-FD#C
+C-CD#E+F-FD#C+C-CD#E+F-FD#C
+C-CD#E+F-FD#C+C-CD#E+F-FD#C
+C-CD#E+F-FD#C+C-CD#E+F-FD#C |
ENERGIZEREND | Energizer effect about to wear off | Z00P44:@V40K0:0: S.-C-A#GF#FD#C |
TRANSPORTER | Transporter effect | Z00P46:@V40K0:0: TC+D-E+F#-G#+A#C+D |
PASSAGEMOVE | Player moves through passage | Z00P47:@V40K0:0: TCEGC#FG#DF#AD#GA#EG#+C |
OOPERROR | ZZT-OOP compiler or run-time error | Z00P48:@V40K0:0: Q.++C |
DOSERROR | Error related to file system | Z00P49:@V40K0:0: --S22I1S44I1S00 |
GAMEOVER | Game over due to player running out of health | Z00P50:@V40K0:0: S.-CD#G+C-GA#+DGFG#+CF---HC |
|