Here are some notes and code snippets for ZZT which may be useful to you, or future me, if I do anything further with the engine.
One of the unique aspects of ZZT is its capacity to be used as a medium for publishing about ZZT itself. Various company magazines, best-practice guides, and reference encyclopedias (collections of cool things you can do) were created throughout the years. However, the drawback of publishing information in this way is that you can't get to it easily without loading the world files in ZZT and actually playing them. The Museum of ZZT File Viewer helps greatly with this visibility problem, but the content is still spread across several publications.
This page will start out as a few random notes, but I hope to eventually map out the encyclopedias and attribute sources where possible.
- 1 ZZT-OOP
- 2 Entities that are visible in the dark
- 3 Linking Boards Together
Set the player's health to a specific value
ZZT infamously only provides addition and subtraction to a handful of pre-defined counters. The Health counter can be directly set to a specific value by abusing the endgame command:
@setHealth #end :touch #endgame #give health 100 #end
Endgame normally sets the player's health to zero and triggers a Game Over state, but if you immediately give the player health afterwards, ZZT will not catch that the game should have ended, and will continue on as if nothing happened.
Uses of the Time counter
The Time counter is normally used on boards where a time limit has been defined. It's always available to mess around with in ZZT-OOP, even when a time limit is not specified on a given board, and it is always set to zero upon entering a board or upon taking built-in damage. Its value will be preserved when restoring from a save file.
The counter can be monitored with an object like this:
@timeCheck #cycle 1 :a #take time 1 timereset #give time 1 /i#a :timereset #give time 1 timereset label tripped. /i #all:timereset #a
This will trigger whenever the player enters the board, similar to SuperZZT's built-in :enter label, or whenever they take damage from built-in entities, such as colliding with a bullet or creature. Caveat: ZZT-OOP #give / #take Health commands do not have this side effect.
Display an external text file
An undocumented menu option (!-FILENAME;) will display the contents of a specified text file. These files may include additional menu links, but the text will not be executed as ZZT-OOP.
@readDoc #end :touch What do you want to read? !-HELLO.TXT;Read HELLO.TXT! !x;Never mind. :x Okay. #end
If the file doesn't exist, nothing happens. Paths may be specified as well. For example, you could display Z:\AUTOEXEC.BAT when running within DOSBox.
This may be useful for conserving limited memory in a large world file, or to allow blocks of text to be viewed on multiple boards without duplicating the text everywhere.
Using objects with tweaked X and Y steps to access additional coordinates
ZZT stats have a couple of internal variables, named X Step and Y Step by the community, that are used to determine the facing or walking direction of an entity. For example, a Transporter pointing to the east would have an X Step of 1 and a Y Step of 0. These numbers are not exposed in the built-in editor, but you can set them with an external editor like KevEdit, anywhere in the range of -128 to 127.
Objects use these variables as part of their #walk / :thud movement system, so when you issue a command like #walk n, it's setting X Step to 0 and Y Step to -1. If X Step or Y Step are set beyond -1 to 1, then the object will skip over entire terrain cells while moving. Be warned: this can allow objects to go out of the boundaries of the screen, which typically results in a runtime error.
These out-of-spec numbers will be preserved when used in reference to flow. For example, when using #walk cw flow on an object moving with an X Step of 1 and a Y Step of 1:
'X=1 Y=1 /i #walk cw flow 'X=-1 Y=1 /i #walk cw flow 'X=-1 Y=-1 /i #walk cw flow 'X=1 Y=-1
If an object with tweaked step values is placed against an obstacle so that it doesn't move, then you can use opp flow, cw flow, and ccw flow to access up to three additional coordinates with the "#if blocked [dir]", "#shoot [dir]" or "#put [dir]" commands:
X Step = 1 Y Step = 1 ..... .1N3. .W@E. .2SX. ..... @ - Our object X - Something solid to prevent the object from moving away N - #if blocked n somelabel S - #if blocked s somelabel E - #if blocked e somelabel W - #if blocked w somelabel 1 - #if blocked opp flow somelabel 2 - #if blocked cw flow somelabel 3 - #if blocked ccw flow somelabel
Here's a less compact example:
X Step = 0 Y Step = 2 ....... ...1... ...N... .2W@E3. ...S... ...X... ....... @ - Our object X - Something solid to prevent the object from moving away N - #if blocked n somelabel S - #if blocked s somelabel E - #if blocked e somelabel W - #if blocked w somelabel 1 - #if blocked opp flow somelabel 2 - #if blocked cw flow somelabel 3 - #if blocked ccw flow somelabel
Here is an example using #put opp flow to conjure a built-in enemy seemingly out of nowhere when the player passes the object:
X Step = -7 Y Step = 0 ...........#....... ........N..#....... .X.....W@E.#...1... ........S..#....... ...........#....... @ - Our object X - Something solid to prevent the object from moving away # - Some incidental walls 1 - "opp flow" -------------------- @someobj #cycle 1 :a /i#if not alligned a #put opp flow lion #end --------------------
Again, tweaked X and Y steps can crash ZZT, and not always consistently so. Take care to safely box in these objects with obstacles that will prevent them from flying off the board, and keep your eyes peeled for unintended side effects, like accidentally #putting objects out-of-bounds because the opp flow coordinate was occupied by a pushable entity.
One more caveat: if you prop an object with tweaked stats against an obstacle like the above examples, be aware that the built-in :thud label is going to trigger constantly. Even if no :thud label is present, ZZT is still going to hunt for one, from the top of your script down to the very bottom. Performance may degrade with many such objects, particularly when running ZZT in DOSBox at 3000 cycles. Here are some workarounds, each with their own potential issues: #lock the objects; place a dummy ":thud" label at the top of the script that just points to an #end command; tell the user to increase their DOSBox cycles.
Entities that are visible in the dark
Dark boards in ZZT mask the entire screen in a grey fog, showing only the player, passages and torches. The player can light torches to view a small radius of the surrounding area. Abuse of these entities can allow you to fill in negative space and create animations that otherwise wouldn't be visible.
Also known as the Kangaroo effect, the namesake of this wiki.
The following entities are visible on dark boards, and can be used to display a limited set of patterns or solid colors on an otherwise grey screen:
- Torches. The safest thing to use.
- Passages, both with stats and statless. This can cause undesired warping to other boards, particularly the title screen with statless passages.
- Player Clones, both with stats and statless.
- Kryptonite Text -- Entity ID 54, which shows up as blinking white text. This is an undefined entity and its presence on a board is capable of crashing ZZT!
Torches and passages cannot be pushed by anything, but player clones and Kryptonite text may be moved by non-player entities such as objects and boulders. Player clones carry some potential side effects, particularly with board edge linking if those clones are up against an edge. (TODO expand on this.) These entities in general can be set as the "under" terrain for entities with stats, so that when the upper object moves, the entity underneath shows through the fog mask.
- Kryptonite Text, presented and coined by Ellypses, 27 Oct 2018, Discord of ZZT
Linking Boards Together
ZZT boards are 60x25, but the internal playfield incorporates a perimeter of "Edge" terrain which is used for determining when the player is attempting to travel to another screen. Edge cells were designed for internal engine use, but it is possible to place them within the visible playfield using STK, an external editor, or even in-game using a bug in ZZT-OOP:
@makeEdgeCell #end :touch #put opp seek
For some reason, putting no 'kind' identifier after the direction causes an edge cell to be made. (TODO attribution.)
If the player touches an edge cell within the visible playfield, it will attempt to transport the player to the linked board in the direction that it was touched. If there is no link, or if the edge on the other side is blocked, then the cell will behave like an obstacle.
Passages transport the player to a destination board which is chosen in the editor. When a player is warped by a passage, ZZT will search for a passage on the destination board with matching foreground and background colors. If a match is found, the player is positioned on top of that passage. If no match is found, then the player remains wherever they are currently located on the destination board. When looking for a matching passage, ZZT starts at the bottom-right, and goes bottom-to-top, right-to-left. Only the first match is used.
Passages made with the internal editor cannot link to the title screen. However, passages made with ZZT-OOP in-game can only link to the title.
Back-To-Back Passage Routing
While passage links normally can't be changed in-game, it is possible to use an intermediate linking board with an array of passages to route the player to different destinations. Additionally, on this linking board, one can use ZZT-OOP to modify which egress passage the player is routed through. Using the title screen as a linking board gives one the ability to place and destroy dynamic passages in-game with ZZT-OOP at will.
- Pepper Bolette by Ellypses uses a static linking board to warp the player to an inventory screen, and back to most boards in the game.