Information Topics


General Information

ZZT World Support

Reference

Miscellaneous

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:

-1X=0Y=0(idle)
0X=1Y=0(east)
1X=1Y=1(southeast)
2X=0Y=1(south)
3X=-1Y=1(southwest)
4X=-1Y=0(west)
5X=-1Y=-1(northwest)
6X=0Y=-1(north)
7X=1Y=-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 ModeScanline CountCharacter Height Row CountShrink Behavior
0200825None
020014254/7 Lines Removed
020016251/2 Lines Removed
1350843None
13501425None
135016257/8 Lines Removed
2400850None
24001428None
24001625None

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:

-1Idle
0East
1South
2West
3North

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:

BLACK0
DARKBLUE1
DARKGREEN2
DARKCYAN3
DARKRED4
DARKPURPLE5
BROWN6
GREY7
DARKGREY8
BLUE9
GREEN10
CYAN11
RED12
PURPLE13
YELLOW14
WHITE15

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:

TYPEThe object's type ID.
CYCLEThe object's cycle (1-255).
XThe object's 1-based X-coordinate in the board.
YThe object's 1-based Y-coordinate in the board.
STEPXThe object's X component of the step direction.
STEPYThe object's Y component of the step direction.
UNDERIDThe type ID under the object.
UNDERCOLORThe color attribute under the object.
IPThe object's instruction pointer.
FLAGSThe object's flags.
DELAYThe 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:

P1Parameter 1 value.
P2Parameter 2 value.
P3Parameter 3 value.
FOLLOWERCentipede follower ID.
LEADERCentipede leader ID.
ONAMEOBJECT name (from @).
CODEIDCustom compiled code ID.
CHARCustom character code.

There are other keywords that are not member variables technically, but they are treated as member-based property extensions of the object:

COLORThis reads and writes the color attribute from the grid. Setting this normally does not change the background color bits.
COLORALLOnly write access supported. This writes all eight bits of a color attribute to the grid.
DIRThis 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:

TYPEThe numerical mapping for the type.
COLORThe color attribute at the type's grid square.
COLORALLWhen used to set the type's grid square, this sets all eight bits of the attribute.
DIRThe object's equivalent numeric directional constant, as it would evaluate to STEPX and STEPY.
CYCLEThe object's cycle (1-255).
XThe object's 1-based X-coordinate in the board.
YThe object's 1-based Y-coordinate in the board.
STEPXThe object's X component of the step direction.
STEPYThe object's Y component of the step direction.
UNDERIDThe type ID under the object.
UNDERCOLORThe color attribute under the object.
CHARCustom character code.
FOLLOWERCentipede follower ID.
LEADERCentipede leader ID.
P1Parameter 1 value.
INTELLIGENCEAlias for P1.
SENSITIVITYAlias for P1.
PHASEAlias for P1.
P2Parameter 2 value.
PERIODAlias for P2.
RESTINGTIMEAlias for P2.
DEVIANCEAlias for P2.
RATEAlias for P2.
P3Parameter 3 value.
DESTINATIONAlias for P3.
ONAMEName of an object (ONAME member).
BINDAlias 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...

EMPTY0
BOARDEDGE1
MESSENGER2
MONITOR3
PLAYER4
AMMO5
TORCH6
GEM7
KEY8
DOOR9
SCROLL10
PASSAGE11
DUPLICATOR12
BOMB13
ENERGIZER14
STAR15 *
CLOCKWISE16
COUNTER17
BULLET18 **
WATER19 ***
LAVA19 ***
FOREST20
SOLID21
NORMAL22
BREAKABLE23
BOULDER24
SLIDERNS25
SLIDEREW26
FAKE27
INVISIBLE28
BLINKWALL29
TRANSPORTER30
LINE31
RICOCHET32
_BEAMHORIZ33 ****
BEAR34
RUFFIAN35
OBJECT36
SLIME37
SHARK38
SPINNINGGUN39
PUSHER40
LION41
TIGER42
_BEAMVERT43 ****
HEAD44
SEGMENT45
FLOOR47
WATERN48
WATERS49
WATERW50
WATERE51
ROTON59
DRAGONPUP60
PAIRER61
SPIDER62
WEB63
STONE64
_TEXTBLUE73 *****
_TEXTGREEN74 *****
_TEXTCYAN75 *****
_TEXTRED76 *****
_TEXTPURPLE77 *****
_TEXTBROWN78 *****
_TEXTWHITE79 *****

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


BOARDNAMEThe board name. This title is set in the ZZT editor.
SIZEXThe 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.
SIZEYThe 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.
MAXPLAYERSHOTSThe maximum number of shots the player can have on the screen at once.
CURPLAYERSHOTSThe current number of shots the player has on the screen.
ISDARKEquals 0 if board is lit and 1 if board is dark.
EXITNORTHBoard number of the board linked in the north direction. Zero indicates no linkage.
EXITSOUTHBoard number of the board linked in the south direction. Zero indicates no linkage.
EXITWESTBoard number of the board linked in the west direction. Zero indicates no linkage.
EXITEASTBoard number of the board linked in the east direction. Zero indicates no linkage.
RESTARTONZAPEquals 0 for normal damage profile and 1 for "Re-enter when zapped" damage profile.
MESSAGESaved toast message string. This is maintained for compatibility with ZZT world files; Super ZZT and ZZT Ultra do not use this property.
PLAYERENTERXSaved player X position. This is used with "Re-enter when zapped".
PLAYERENTERYSaved player Y position. This is used with "Re-enter when zapped".
CAMERAXCamera X position (left edge). This is used for boards that scroll (all Super ZZT boards and possibly some ZZT Ultra boards).
CAMERAYCamera Y position (top edge). This is used for boards that scroll (all Super ZZT boards and possibly some ZZT Ultra boards).
TIMELIMITNumber of seconds in board's time limit. If this is zero, there is no time limit.
PLAYERCOUNTUsually equals 1. This quantity can grow to a larger value if PLAYER clones are placed in the board.
FROMPASSAGEHACKEquals 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


WORLDTYPEThe world type code. -1=ZZT, -2=Super ZZT, -3=ZZT Ultra.
WORLDNAMEThe world name.
THISGUIThe name of the current GUI shown.
LOCKEDEquals 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.
NUMBOARDSNumber of boards in ZZT world.
NUMBASECODEBLOCKSNumber of code blocks for types alone, before custom code is compiled.
NUMCLASSICFLAGSNumber of global variables set using the boolean mechanism. This limits the number of global flags set for the world when compared against CLASSICFLAGLIMIT.
LASTCLASSICFLAGName of last global variable set using the boolean mechanism. This flag would be overwritten if the maximum number of global flags has been reached.
CODEDELIMITERString 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".
BOARDCurrently active board number.
AMMO(Inventory)
GEMS(Inventory)
HEALTH(Inventory)
TORCHES(Inventory)
SCORE(Inventory)
TIME(Inventory)
Z(Inventory)
TORCHCYCLESNumber of cycles remaining on active torch. Equals zero if no torch is active.
ENERGIZERCYCLESNumber of cycles remaining on active energizer. Equals zero if no energizer is active.
KEYnnThe "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.
ISSAVEGAMEWhen 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.
HIGHESTOBJPTRWhen a WAD file is created, this property is set to the "next" unique identifier for when a new status element is created.
EVERPLAYEDThis 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.

GEMHEALTHHow much health is gained by picking up a single gem. Set to 1 for ZZT and 10 for Super ZZT.
MAXSTATELEMCOUNTMaximum 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.
CLASSICFLAGLIMITOriginal 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).
NOPUTBOTTOMROWThis 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.
BECOMESAMECOLORThis 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.
LIBERALCOLORCHANGEThis 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).
ZSTONELABELThis 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".
FREESCROLLINGDefault 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.
LEGACYCAMERADefault 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).
SENDALLENTERDefault 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.
GAMESPEEDThe 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.
  
FASTESTFPSThis 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.
LEGACYTICKDefault 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.
OBJMAGICNUMBERDefault is 32. This controls the maximum number of 'own-code' legacy commands per turn, such as for OBJECT and SCROLL types.
SOUNDOFFDefault is 0, indicating that sound should be played in ZZT Ultra. If this is 1, no sound is played.
MASTERVOLUMEDefault 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.
IMMEDIATESCROLLDefault 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.
ORIGINALSCROLLDefault 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.
OVERLAYSCROLLDefault 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.
OLDTORCHBARDefault 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.
HIGHSCOREACTIVEDefault is 1, indicating that high scores are recorded and shown. If this is 0, high scores are not shown or posted for any world.
HIGHSCOREMINDefault is 100. This tells ZZT Ultra to only show a high score prompt when the user scores at least this high.
HIGHSCOREPROMPTDefault 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).
PLAYERDAMAGEHow much damage the player takes from a typical hit. Default is 10.
KEYLIMITHow 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.
KEYSBLOCKPLAYERSet 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.
POINTBLANKFIRINGSet 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.
BLINKWALLBUMPSet 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.
TELOBJECTSet 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).
DETECTSCRIPTDEADLOCKSet 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).
PLAYRETENTIONSet 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).
PLAYREVERBSet 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).
PLAYSYNCSet 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.
PLAYERRUNDELAYSet 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.
PLAYERFIREDELAYSet 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.
BLACKKEYGEMSSet 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.
BLACKDOORGEMSSet 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).
ALLCOLORKEYSSet 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.
PLAYERCHARNORMPlayer's normal character. Default is 2.
PLAYERCOLORNORMPlayer's normal color. Default is 31.
PLAYERCHARHURTPlayer's "hurt" character. Default is 1.
PLAYERCOLORHURTPlayer's "hurt" color. Default is 127.
PAUSEANIMATEDDefault is 1. Set to 0 to inhibit the flashing player pause animation.
MOUSEBEHAVIORSet 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.
  
MOUSEEDGENAVSet 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).
MOUSEEDGEPOINTERSet 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.
BOARDEDGETRANSSet 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.
OBJECTDIEEMPTYSet 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.
REENTRYMOVESTYPESet 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.
SCORELIMITSet to 2000000000 by default. The original score limit was 32767.
AUTOSAVESECSSet 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.
BOARDCHANGESAVESECSSet 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.
REENTRYZAPSAVESECSSet to 10 by default. Controls the number of seconds between autosaves triggered by zapped re-entry.
MAXSAVESTATESSet 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.
SITELOADCHOICEWhen #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_AUTORUNZIPSet 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_EXTRAFILTERSet by deployed configuration; defaults to "" if not set. This indicates a filter pattern (*.zip, *.wad, *.zzt, etc.) to apply when #LOADWORLD 0 is invoked.
SCANLINESSee the #SCANLINES command.
BIT7ATTRSee the #BIT7ATTR command.
SCRCOLBORDERSee the #SCROLLCOLOR command.
SCRCOLSHADOWSee the #SCROLLCOLOR command.
SCRCOLBGSee the #SCROLLCOLOR command.
SCRCOLTEXTSee the #SCROLLCOLOR command.
SCRCOLCENTERTEXTSee the #SCROLLCOLOR command.
SCRCOLBUTTONSee the #SCROLLCOLOR command.
SCRCOLARROWSee the #SCROLLCOLOR command.
CONFIGTYPEIndicates 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.
WATERSOUNDDefaults to 1. Set to zero to turn off water/lava contact sound effect.
WATERMSGDefaults to 1. Set to zero to turn off water/lava contact message.
INVISIBLESOUNDDefaults to 1. Set to zero to turn off invisible wall contact sound effect.
INVISIBLEMSGDefaults to 1. Set to zero to turn off invisible wall contact message.
FORESTSOUNDDefaults to 1. Set to zero to turn off forest contact sound effect.
FORESTMSGDefaults to 1. Set to zero to turn off forest contact message.
FAKEMSGDefaults to 1. Set to zero to turn off fake wall contact message.
DUPSOUNDDISTDefaults 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.
ALLOWINGAMERESTOREDefaults to 1. Set to zero to turn off in-game savegame/save state restoration.
ALLOWINGAMECONSOLEDefaults to 1. Set to zero to turn off in-game console availability.
ALLOWINGAMECHEATDefaults to 1. Set to zero to turn off in-game cheat availability.
FASTCHEATFLAGDefaults 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).
INVENTORYFLAGDefaults 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".

EXTRAINVTIMEOUTWhen 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.
ZZTGAMEGUIDefaults 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.
SZTGAMEGUIDefaults 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.
BQUESTHACKThis 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.
VERSIONThe 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.

XRest
0Tick
1Tweet
2Cowbell
4Hi snare
5High woodblock
6Low snare
7Low tom
8Low woodblock
9Bass drum

The following codes set the duration for subsequent notes. The duration starts at thirty-second note speed (T).

WWhole note
HHalf note
QQuarter note
IEighth note
SSixteenth note
TThirty-second note

The following codes are duration modifiers.

.Multiplies previously set duration by 1.5.
3Divides 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.

ZnnThe 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.
PnnThe 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.
JThis 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.
OnnThis 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.
VnnThe 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.
UnnnThe 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


NameDescriptionSequence
PLAYERMOVEPlayer movement ticksZ00P01:@V40K0:0: T0
FORESTPlayer forest-clearing sound (ZZT)Z00P02:@V40K0:0: TA
FORESTSZT0Player forest-clearing sound (SZT)Z00P03:@V40K0:0: T+F
FORESTSZT1Player forest-clearing sound (SZT)Z00P03:@V40K0:0: T+C
FORESTSZT2Player forest-clearing sound (SZT)Z00P03:@V40K0:0: T+G
FORESTSZT3Player forest-clearing sound (SZT)Z00P03:@V40K0:0: T++C
FORESTSZT4Player forest-clearing sound (SZT)Z00P03:@V40K0:0: T+F#
FORESTSZT5Player forest-clearing sound (SZT)Z00P03:@V40K0:0: T+C#
FORESTSZT6Player forest-clearing sound (SZT)Z00P03:@V40K0:0: T+G#
FORESTSZT7Player forest-clearing sound (SZT)Z00P03:@V40K0:0: T++C#
COLLECTGEMPlayer gem collectionZ00P04:@V40K0:0: T+C-GEC
COLLECTAMMOPlayer ammo collectionZ00P05:@V40K0:0: TCC#D
COLLECTTORCHPlayer torch collectionZ00P06:@V40K0:0: TCASE
PUSHERPlayer push attempt, or pusher movementZ00P08:@V40K0:0: T--F
BREAKABLEHITBreakable wall or bullet destroyed by bulletZ00P09:@V40K0:0: -TC
ALREADYHAVEKEYAttempt to collect key when already in inventoryZ00P10:@V40K0:0: SC-C
READSCROLLPlayer scroll collectionZ00P11:@V40K0:0: TC-C+D-D+E-E+F-F+G-G
COLLECTKEYPlayer key collectionZ00P12:@V40K0:0: +TCEGCEGCEGS+C
OPENDOORPlayer opens doorZ00P13:@V40K0:0: TCGBCGBI+C
DOORLOCKEDPlayer unable to open locked doorZ00P14:@V40K0:0: --TGC
INVISIBLEWALLPlayer hits invisible wallZ00P15:@V40K0:0: T--DC
WATERBLOCKPlayer hits water or lavaZ00P16:@V40K0:0: T+C+C
DUPLICATEDuplicator successfully duplicatesZ00P20:@V40K0:0: SCDEFG
DUPFAILDuplicator unsuccessfulZ00P21:@V40K0:0: --TG#F#
BOMBTICK1Bomb tick countdown (low)Z00P22:@V40K0:0: T8
BOMBTICK2Bomb tick countdown (high)Z00P23:@V40K0:0: T5
BOMBACTIVATEBomb activationZ00P24:@V40K0:0: TCF+CF+C
BOMBEXPLODEBomb explosionZ00P25:@V40K0:0: T+++C-C-C-C-C-C
TORCHOUTTorch runs outZ00P26:@V40K0:0: TC-C-C
PLAYERSHOOTPlayer shoots bulletZ00P31:@V40K0:0: T+C-C-C
OBJECTSHOOTOBJECT shoots bulletZ00P32:@V40K0:0: TC-F#
RICOCHETBullet hits ricochetZ00P33:@V40K0:0: T9
ENEMYDIEEnemy diesZ00P34:@V40K0:0: TC--C++++C--C
PLAYERHURTPlayer damagedZ00P35:@V40K0:0: T--C+C-C+D#
TIMELOWTimed board is running out of timeZ00P42:@V40K0:0: I.+CFC-F+CFQ.C
ENERGIZERPlayer energizer collectionZ00P43:@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
ENERGIZERENDEnergizer effect about to wear offZ00P44:@V40K0:0: S.-C-A#GF#FD#C
TRANSPORTERTransporter effectZ00P46:@V40K0:0: TC+D-E+F#-G#+A#C+D
PASSAGEMOVEPlayer moves through passageZ00P47:@V40K0:0: TCEGC#FG#DF#AD#GA#EG#+C
OOPERRORZZT-OOP compiler or run-time errorZ00P48:@V40K0:0: Q.++C
DOSERRORError related to file systemZ00P49:@V40K0:0: --S22I1S44I1S00
GAMEOVERGame over due to player running out of healthZ00P50:@V40K0:0: S.-CD#G+C-GA#+DGFG#+CF---HC

Return to Table of Contents



This page is Copyright © 2016 Christopher Allen.