This article is designed to accompany the ZZT world Examplia. Together, the world and article intend to provide easy reference material and examples for basic ZZT-OOP functionality. Examplia consists of a single playable board which contains objects that demonstrate every command, condition, and label ZZT-OOP offers.
ZZT's freely editable worlds have meant that for years the typical approach to learning ZZT-OOP was by example, studying objects in other worlds to figure out how they work and applying that knowledge towards new creations. Examplia specifically tries to put all the basics in one place rather than having to scour individual worlds to find a working example. While Examplia is a comprehensive demonstration world for code, only a few objects interact with other objects. Newcomers don't need to be overwhelmed by complex interactions between objects, or worry about creating sophisticated engines. The world is meant to demonstrate basic principles with this article providing commentary on some behaviors that may not be so obvious solely from looking at code.
While this article does not get into the more advanced capabilities of ZZT-OOP or showcase any engines that radically alter gameplay, it should serve as a solid foundation of basic concepts and some design principles to consider when creating ZZT Worlds.
Welcome to Examplia!
Trademarked Soda Machines
Upon stepping into Examplia, the player is corralled into a small area blocked off by sliders. It's a bit of an extreme way to force the player to read the opening sign.
There are also a few decorative objects in the area: vending machines, a garbage can, and a recycling bin. The vending machines are a good place to start as they do very little, just displaying a message when the player touches them.
@Cola Machine #end :touch A trademarked cola vending machine. #end
Let's walk through this first object one line at a time.
@Cola Machine - This gives the object a name. It is necessary to name objects in order for them to receive events sent from other objects that don't make use of the generic OTHERS or ALL identifiers. The name will also be shown when displaying a multi-line message that appears in a message window. Neither of these are relevant to the cola machine here. Even so, it can still be useful to name objects as the name will also show up in various places in third-party editors and the Museum's file viewer, making it far easier to find a specific object.
#end - All objects will execute their code automatically. This command stops code execution until the object receives an event from the player or other object. Without it, the object would continue on and end up displaying its message before the player touched it.
:touch - This is a label defined by ZZT, and by far the most common label of all. Whenever a player touches an object it will go to its :touch label if it has one. Labels themselves are ignored during code execution and can be placed anywhere you like.
#end - Halts code execution. There's no more code after the :touch label here, so like the name, it's technically redundant. Stray #end commands are harmless, though they will count against the amount of memory a board has available to it.
And that's all there is to it! A lot of fun of ZZT comes from trying to decipher its visuals. Would be interpreted as a soda machine before touching it? Is its design made clear once the object has been described? I bet you can guess what trademarked cola I had in mind here despite the graphic being "right pointing triangle". (This brilliant use of ASCII comes from Deep December.
The second vending machine design is much the same, though it demonstrates message windows instead.
@Vending #end :touch A vending machine full of overpriced snacks.
Everything is basically identical as far as commands go. The extra #end has been removed to demonstrate that objects will simply stop when they reach the end of their code. You'll likely find lots of extra #end commands in code as it's an easy habit to develop and objects with multiple events do need #end to stop one event finishing unintentionally causing the next one to start.
A vending machine full of overpriced snacks. - For this vending machine, the descriptive text is too long to fit on a single line. Instead it will be displayed in a large scrollable message window. In a medium as visually limited as ZZT, you might think that longer descriptions help build a more defined world, and you'd be correct. However, that doesn't mean that they're ideal in every situation. Consider a row of books the player must search through. If each book displays its title when touched in a single line message, the player can rapidly move from book to book until they find the correct one.
If every book results in a message window opening, the player now has to stop and close the window before moving on to the next book. For light decoration, it might be wise to try to fit your descriptions on a single line. Though at the same time, the limited size of these messages can sometimes make doing so a challenge! This object's message would fit on a single line if we removed "overpriced" from the description. It's up to the author to decide whether or not the more detailed text is worth it. Here, by having the player describe the contents as overpriced, it makes more sense for the object to not offer further interactivity like making a purchase.
Where there are plastic bottles and bags, there's bound to be a nearby receptacle to dispose of the empty containers. In fact, there are quite a number of them throughout Examplia. Please don't litter.
@Garbage #end :touch #zap touch A garbage can. #end :touch #restore touch In French, it's called a "poubelle". #end
Our garbage cans don't need much for flavor and another simple one-liner will do. This particular garbage can is going to be important in demonstrating a few things though. What if you want an object to have more than one message it can display? Or more generically, an object that behaves differently when touched a second (or additional) times.
#zap touch - The first of two new commands, allows us to do just that. When the command is executed after first touching the object, ZZT will scan the object's code starting from the very top (not where it currently is in its code execution). When it finds a :touch label, it turns the line into a comment by changing the colon to an apostrophe resulting in the code being changed to read 'touch instead. Once a match has been found, the command stops, allowing you to zap labels one-by-one in a linear fashion.
Afterwards, the next time the player touches the garbage can, when ZZT scans the code for a :touch label, it will find the second one! Here a second message is presented instead (owing this time to Invasion ZZT Revision for the French lesson).
The counterpart to #zap is #restore which can be used to reverse the effect so that the third time the garbage can is touched it will once more display the original message. It's very very very important to note however that #zap and #restore aren't quite one to one opposites of each other. While #zap will comment out the first matching label, #restore will continue to scan the object and restore every instance of the label, not just one.
From a design perspective, it's important that players are aware that touching objects multiple times is to be expected or not. For our garbage can, the original message offers nothing to the player to guide them to continue interacting with the object. In fact, because the player can see several more of these objects on the board, it's more likely they'll think they're all identical to this one and they may not even bother to touch the others!
Here the "perk" is just another line of text. Imagine though if every garbage can displayed this text, but one of them had a hidden key inside required to progress through the game. You'd be setting players up for a lengthy search where the garbage cans wouldn't be checked until they had gotten pretty desperate!
The welcome sign makes a modest attempt at guiding the player towards touching things multiple times by outright saying Some objects may be worth interacting with repeatedly. Even so, players will be more inclined to speak with NPCs or re-examine the unusual. Mundane objects are far more likely to be treated as one-and-done with regards to touching for information. Sometimes a decorative object is just a decorative object.
@Sign #end :touch #change white sliderns empty #change white sliderew red empty [...]
#change sliderns empty / #change white sliderew red empty - In order to set the player loose, the barriers need to be removed. There are plenty of ways to keep a player in a certain area until they're ready to move onward of course. This one is a straightforward one that makes it clear to the player that the obstacles are no longer an issue as they can see they are no longer there.
These commands transform the first element into the second element. The #change command will replace all matching instances of one element with the other. Additionally, a color can also be provided to the source element to limit the match to a specific color, and/or to the replacement element to provide a specific color for the replacement rather than using the current color.
Some elements have specific rules about their colors. With this object, the "red empty" is actually red although the renderer will always draw empties as black on black. As for the white east/west sliders, this allows east/west sliders of other colors to be used on the board safely. Check out the bon fire on the beach and you'll see one tile of firewood is actually an east west slider that survives the #change command due to its color not matching.
ZZT has some very strict rules about colors, only seven can be referenced in code as ZZT's default editor limits the colors that can be used to these same seven. They are the bright colors: blue, green, cyan, red, purple, yellow, and white. In the past toolkits were used to copy specially colored elements onto new boards. Today third party editors are used instead which provide access to any color combination ZZT can handle. ZZT-OOP however, is stuck with the default seven which can be a bummer for more animated visual effects produced through code.
The code presented here isn't the object's complete code. I'm opting to use [...] as an indicator for omitted ZZT-OOP.
With the paths no longer obstructed, the player may now head pretty much anywhere they like. The boardwalk here is a good a place to start as any.
The boardwalk also has its share of garbage cans and recycle bins. It's easy to simply place multiple copies of objects like this all over the board wherever they're needed, however with each object comes increased memory usage. Luckily, ZZT has a helpful command to share code and reduce the amount of memory an object requires.
#bind garbage - Instead of having copy after copy of identical garbage can, ZZT offers us the #bind command which will cause an object to discard its own code and instead point to a specified object's code. Every other recycle bin and garbage can on the board instead runs this command.
This has a number of advantages. Not only for memory usage, but also now by changing the original objects' code, all the bound objects will be updated as well. Bound objects may use another object's code, but they maintain their other properties. You can have bound objects running at a different cycle (speed) (if set via a third-party editor in advance), or simply change their color/character. The darker garbage can and recycle bins in the alley towards the top right are using #bind just like others.
Alas, #bind has a significant caveat that has been the bane of many a ZZTer over the years: Code is pointed to, not copied, and because commands like #zap and #restore modify that code, it means that the code is modified for all the bound objects as well. You can see this in action with our garbage can's alternating messages. The first garbage can touched will display the "garbage can" message. Touching a different can afterwards will display the second "poubelle" message instead as the initial :touch label has been zapped.
For these garbage cans, that's no big deal. For objects like the recycle bin that don't use #zap, there's no trouble either. Where it gets messy is when programming something like custom enemies the player has to fight off in numbers. These objects often require a non-insignificant amount of memory, and if they require multiple shots to defeat, #bind stops being an option, requiring the more costly copying of the code instead.
These days, modern forks of ZZT v3.2 increase the memory limits of boards from 20,000 to 32,767 (the maximum that it can increase to without breaking compatibility with the v3.2 world format) making it less of an issue. Though if your creation requires this, make sure the player is made aware as ZZT v3.2 is still seen as the standard!
Jogger in Red
Endlessly jogging along the boardwalk is a jogger in red. This object demonstrates ZZT's #walk command that can make it easy to create an endless loop of movement.
@Jogger #walk e #end :touch JOGGER: Can't talk. Jogging endlessly. #end :thud #walk opp flow #end
#walk e - This time we break from the convention seen in every other object so far. This time, we want our object to begin executing code on its own. Objects can have a direction stored for walking which can be set with #walk. Here the jogger is told to begin walking east. It will then reach the #end command, but despite no longer executing code, it will continue to move! Once a direction has been set via #walk the object will endlessly attempt to move in that direction until it is stopped with #walk i (with "i" meaning "idle").
The neat thing about #walk is that walking objects can easily be interrupted without breaking their movement pattern. If the player talks to the object by touching them, they continue to walk. Were this object hard-coded to go east a dozen times, and then go west a dozen times then there'd be no easy way to determine where they were when the were interrupted by being touched.
:thud - Another built-in label that relates to walking. When a walking object is obstructed by something, they will be sent a thud event and execute the code that follows.
#walk opp flow - With :thud, it's not uncommon to use this as an opportunity to change course. Flow is used to reference the direction an object is currently walking, so east in this example. Opp is used to take the opposite of the following direction. Here, this means to begin walking opposite of east, and so the object turns around and begins to walk west. Eventually they'll bump into a wall on the west, be sent to :thud again, and change direction to move east again. A convenient little loop!
Kids Asking Questions
@Child #end :touch CHILD: Gem for your thoughts? !1;"ZZT-OOP better not be difficult." !2;"Please do not turn conversations into !2;"a financial matter." #end
Messages don't have to be one-sided! It's also possible to allow the player to make selections with hyperlinks. The child in purple standing outside the bait shop will demonstrate.
!1;"ZZT-OOP better not be difficult." - Lines of code that start with an exclamation point are treated as hyperlinks. They are then followed by a label name, then a semicolon to delineate the label from the text, and finally a string of text to display.
In ZZT, hyperlinks hide everything up to and including the semicolon. Instead, a is shown to indicate that the line may be selected. The object's name is also temporarily replaced with a message telling the playing they can select this option. If the player presses ENTER with such a line highlighted (between the red "«" and "»" characters) the object will then jump to that label in its code.
If the player chooses not to decide, the window will close and the object will continue executing code from where it was, in this case reaching #end and stopping.
The two hyperlinks here zap the :touch label preventing the player from repeating the conversation as the child runs out of money.
@Child [...] :1 #zap touch #give gems 1 CHILD: It's so easy, achild could do it. #end :2 #zap touch #give gems 1 CHILD: I guess that counts. #end :touch CHILD: Sorry, I'm flat broke now. I'm a kid after all. #end
#give gems 1 - This command will give the player a single gem! Any of the counters on the sidebar may be referenced: health, ammo, torches, gems, score, and even time (though its behavior is somewhat unusual). Keys however, are a no-go. The #give command can only adjust numeric values.
When making hyperlinks with short labels like this one, it's important to not use too long of a string as it's possible to make the indented text run out of bounds of the scroll causing minor visual glitches. They're harmless, but ugly and easily averted by just having the text continue on additional lines with hyperlinks to the same label
Jerry's Bait Shop (You know the place)
With a shiny gem in hand, the player needs to find something to spend it on! I feel like I could write an entire article on the philosophy of video game shops, but lord knows I don't have the time. Instead, let's see what Jerry's got for sale.
@herring #end :touch A red herring.
@herring #cycle 1 #end :touch A green herring.
Two fish on display are virtually identical, except one has modified its speed with #cycle. Everything ZZT runs on cycles with #cycle 1 being the fastest, #cycle 3 being the default for objects, and #cycle 255 being an absurd maximum. (It's rare to go beyond cycle ten or so.)
ZZT counts elapsed cycles and if current count is divisible by an object's cycle it will get to act. You can compare the two herring and see a small difference in how long until the message shows up on screen.
At times, it may actually be the same! If you just happen to touch the cycle three herring on a cycle count divisible by three, it will display immediately. For objects where timing isn't critical, the default is fine as the pause is only really there if you go looking for it, but for moving or responsiveness between objects it's very important to consider the object's cycle.
Third party editors will allow you to pre-set the cycle allowing you to avoid using the command entirely unless you want to specifically speed up or slow down an object during gameplay.
The two objects on the beach up above that dance back and forth are doing so at cycle one and cycle three. With movement, the difference is far more pronounced.
@Jerry #end :touch #zap touch JERRY: Howdy there! Name's Jerry. I sell worms, plastic minnows, and such. :touch #if rod return JERRY: Now then. What would you like? !1;Rent a fishing rod. !1;(3 gems + 2 gem refundable deposit) !2;"Just browsing." #end [...]
Jerry here is going to really up the ante a bit, combining much of what's been covered so far with a new techniques and commands as well.
:touch - This isn't anything new by now, but what is different is that there's a second instance of the label here in the middle of the first :touch! Take a moment to think of what's happening here and I'm sure it will become clear.
Figured it out? What we have here is introductory dialog that displays only the first time the player meets Jerry. The second label is ignored (and we'll get to that #if command in a moment) and the text continues. The first time the player speaks with Jerry they'll be introduced and the regular text and options for what to purchase will be displayed within the same window.
@Jerry [...] :1 #take gems 5 tp #set rod JERRY: Here you are! Hope you catch somethin'. #end :tp JERRY: Sorry buddy, I can't let you have that for free! #end
#take gems 5 tp - Jerry's running a business here and as you'd likely have guessed, uses the opposite of #give, #take to decrease the player's gem count. First let's cover the scenario where the player does indeed have the give gems to make the purchase. When this is the case, the #take succeeds and code execution continues.
#set rod - Where it reaches this new command #set. This command is used to set a flag with the given name. Flags are either set or not, making them your basic Boolean value, just with a customizable name. Only ten flags can be set at once, so flag management is essential for more complex ZZT worlds. (We'll learn how to remove a flag later on.) Jerry gives the player the item and some words of encouragement, ending the transaction.
Of course, the player might try to pull a fast one on ole' Jerry and try to get a fishing rod without the gems. Jerry's too smart for that though. The #take command accepts an optional label to jump to or command to execute for this exact situation. If the player doesn't have enough gems, none will be taken at all. Instead, Jerry will jump to :tp. This label can be whatever you like. ZZTers learning from other ZZTers means some custom label names will show up in a number of worlds. In this case, the label is short for "too poor". Reaching that label results in Jerry displaying a different message about the lack of funds.
#if rod return - Back in the earlier example we skipped over this line whose purpose is probably far more clear now. When the player speaks with Jerry with the flag rod having been #set, the #if command here will then evaluate to true and then jump the following label or command.
@Jerry [...] :touch #if rod return [...] #end :return JERRY: All done fishing? !3;"Sure am!" !2;"Not yet." #end :3 #clear rod #give gems 2 JERRY: Here's the deposit back. Hope to see you gen'. #end
Jerry, you're loaded with code buddy. With the flag set Jerry instead asks the player if they're ready to return the rod and get their deposit back. (This deposit thing is a bit of a contrivance for example's sake. Most games wouldn't do something this elaborate for the sake of two gems.) Another pair of hyperlinks are displayed, yet only one label is actually new. Jerry's response is generic enough when the player says they're not going to rent a rod that it can work just as well as a response to not being ready to return the rod. It's an easy way to save some code if the feedback is still applicable.
#clear rod - Here's our counterpart to setting the flag. #clear will unset/clear it. In Examplia, very few flags are actually used so it would be just fine to let the player keep the rod for the entire game and not mess around with all this, but in larger sized worlds it's much more important to manage your flag count and clear flags when you can. Setting an 11th flag results in the 10th flag being replaced, something that can lead to an unwinnable game! Clearing a flag that hasn't been set however, is harmless and does nothing.
Also take note that the object named "Jerry" is not the yellow smiley face! That object has no code at all as the player never actually interacts with it directly. Instead "Jerry" is a brown object adjacent to the smiley representing a counter. The other two tiles that look identical are actually vertical blink wall rays to save on objects, though this means that only one of the three tiles that make up the counter can be interacted with. Consider how the placement of the yellow object guides the player specifically to the center tile. Some may prefer to make all three tiles an object and either #bind two of them to the original, or have them all send a message to that smiley face and have them actually handle the store's functions.
Next to the bait shop is this wonderful burger shaped dining establishment. It's another store with a different item for sale.
@Burger #end :touch EMPLOYEE (monotonously): welcome to $ENERGIZ▐▀▐▀▌ $ ▐▄▐█ $B U R G▐▄▐ ▌ [...] !1;"One energizer burger please." (-5 ♦) !2;"Yeah can I get uh....." #end :2 EMPLOYEE: Take your time. #end :1 #take gems 5 tp EMPLOYEE: Order up. #put s energizer #end :tp *SIGH*
I made it this far without showing off the last way to display text? Dang.
$B U R G▐▄▐ ▌ - ASCII art aside, the $ command is ZZT's only real method of changing how text is displayed. Instead of yellow, the text will be colored white. In addition the text will also be centered in the message window.
White text like this is often used for headings or to indicate when it's the player speaking rather than whomever they're talking to. (Though this isn't something to rely on being the case. Some authors may do the opposite, or simply convey the player's dialog in another manner.) In this particular example, the white text isn't actually centered! There's no magic here, all it takes to get around the centering is to pad any such lines with spaces. This will allow you the ability to use the alternate colored text while still being able to draw with that text and keep everything aligned.
#put s energizer - The rest of the shop is really no different than Jerry's. If the gems can't be taken the transaction ends. If they can, this time rather than setting the flag or increasing a counter with #give (this could be a health restoring burger after all), an energizer is placed to the south. #Put allows elements to be placed on the board. Like with #change from earlier, an optional color can be specified as well. Though in this case, energizers cannot be anything other than their unique dark purple color.
That energizer will come in handy as we move on to...
The Star Chamber of Death
Every ZZT game needs one of these. (That's sarcasm. No ZZT game has ever needed one of these, though that never stopped anyone.) A small blue building in the corner has a... gun thing that is endlessly throwing stars. A transporter keeps the stars from getting out, and a dude in green keeps the player from risking their life for a purple key contained inside.
Let's see what Green Dude is up to as they have a few fairly rare commands and labels within their code plus some far more common ones that need to be introduced here as well.
@Green Dude #end :touch GREEN DUDE: Oh no! I dropped my keys in the star chamber of death! Drat! #if energized helpme GREEN DUDE: I'd ask you to help, but you are clearly not invincible #end :helpme #lock GREEN DUDE: Oh hey, could you help me get my keys out of there? The stars won't hurt you while you're energized. /n/n/n/e #zap touch #unlock #end [...]
Phew! Okay this really is quite a bit to take in all at once. The #if command makes a reappearance, but in a somewhat different form. #If can do more than just check for flags being set. ZZT offers a handful of conditions that can be tested as well. Like with flags, if the condition is true, the code will jump to the label or run the command, and just move on to the next line of code if the condition is false.
The one here is energized which is true when the player is currently invincible due to having picked up an energizer element. You know, the very thing that the burger place sells. In this example, the conversation is different depending on whether or not the player is currently energized.
If the player is invincible, before the rest of the dialog appears, a new command shows up: #lock. After executing this command, the object discards any external events from being handled. Here it's used before a series of hard-coded movement commands.
/n/n/n/e Movement commands can be written with a dedicated command #go, but it's far more common to use the slash character as a form of shorthand as this also allows multiple directions to be specified on a single line. Green Dude, seeing the player is invincible takes a few steps away from the transporter to the star chamber. It's important to lock the object before it begins to move like this as otherwise the player could touch the object again in the middle of its movement causing it to display its text again, and then re-running the movement commands from the start.
If an object attempts to move with #go or its / shorthand but can't, it will not continue code execution. Sometimes this is desirable. In this case, the directions wind up placing the Green Dude smack in the middle of the jogger's path. If the timing was such that the jogger blocked Green Dude, the continued attempts at moving would ensure that eventually the object would stop in a knowable position.
Other times, this can cause problems. Unexpected obstructions may result in an object never finishing its movement commands and just being stuck. In fact, with the lock in place here, it could really cause trouble as there's no way to interact with the object at all until it finishes moving and reaches #unlock which allows external events to propagate once more.
One thing to note is how the text is drawn in the message window for this object. Once ZZT begins parsing lines of text, it will keep doing so even as other commands are executed to perform things like jumps to labels. A single message window will contain the "Oh no!" and "Oh hey" lines despite them being several lines apart in the actual code. However, the #if here also results in a blank line being added to the text!
The specifics of this aren't the most important for a beginner, but what is important of being aware that text parsing can continue past certain commands. This can be a huge problem if added blank lines or just unexpected continuation of text results in flashing one line messages turning into obstructive pop-ups. Many a poorly made ZZT action game suffers from this with message windows consisting of "Ouch!" and a blank line being displayed every time an object is shot, ruining the pacing of a shooting sequence.
@Green Dude [...] :touch #if not any purple key phew GREEN DUDE: Please? Can I get my keys back? #end :phew #lock GREEN DUDE: Oh! You found it! I'll just take that key away from you... /i ... /i/i/i/i GREEN DUDE: What do you mean "only counters like health and ammo can be given to and taken from!"?!
Time for another unique condition! #If any can be used to check if an element can be found on the board. Not is also introduced which can be used to negate a condition. ZZT-OOP doesn't offer a way to check if the player currently has any keys, but since the only key on this board is a purple one, just seeing if it's no longer there is enough to deduce that the player has therefore picked it up. Obviously, this technique won't work if there are extra purple keys on the board, or if there's the possibility of the player already having a purple key when they get here, so it's not a one size fits all solution.
By now you know probably have seen enough to be aware that the color parameter is optional and that a true condition will jump to a label or execute a command, with a false condition causing code to continue to the next line.
In that :phew label, the object locks itself again, this time with no intention of unlocking. The object no longer needs to respond or do anything, so it's perfectly okay to just ditch it, although the game may feel a little more polished if instead :touch was zapped yet again and some final dialog added. Here, the Green Dude just doesn't want to interact with the player anymore. Sometimes that's the way things are.
There's a new direction introduced here as well: /i which is used to make an object idle in place. You might argue that the object is blocked in a neutral direction like this by itself, but ZZT is smart enough to realize the importance of doing nothing and the command works just fine. (Perhaps this wasn't always the case during ZZT's development, as in addition to /i and #go i, ZZT-OOP has an outright #idle command as well. All of these behave the same way.)
Here the idles are used to stagger dialog resulting in a message window, followed by a flashing ellipses, and then after letting it linger for a moment, another message window which brings up my next bit of information for me. Just as keys can't be detected, they also can't be manipulated. The only thing that will remove a key from the player's inventory is opening a door of the matching color, so Green Dude is going to be walking home (and perhaps the player just acquired a sweet ride).
This is probably a good time to bring up the all important question with no perfect answer of: how many idles should I use for text? That answer depends on a number of things. Objects have a cycle that determines how often they get to act and that includes how fast they get through a series of idles. There's definitely no one-size-fits-all answer here. A short message can be read faster than a full sentence the correct count may be shorter or longer depending on what's being written out.
Many games have cinematic sequences for the player to watch, often with dialog presented in flashing text that changes automatically. Here finding a sweet spot is much more difficult. The ellipses obviously doesn't take long to read. Complex dialog will have a higher minimum, and a lot of players find lengthy text presented in this form to be tedious. The sentences rarely flow naturally as messages can't hold longer sentences, and are often made even shorter by prefixing the text with who is supposed to be speaking. Too many idles will make the player restless. Too few, and the text will fly by before it can really be read. These days, despite the obstruction of message windows covering up most of a scene, they tend to be preferred as they let the reader process information at their own pace. Think twice before relying too heavily on flashing text for extensive conversation, but remember, this information is presented as ZZT community wisdom and not a hard and fast rule that can never be broken.
@Green Dude [...] :energize /i/i GREEN DUDE: Hey! Player! Over here! #end
Green Dude has one last trick up their sleeve. Shortly after collecting an energizer they'll call out to the player thanks to another built-in label. Objects will go to their :energize label whenever an energizer is picked up. For Green Dude, the small delay is just because both the energizer and Green Dude want to produce a flashing message and ZZT can only prioritize one. This provides a little breathing room.
@Stars #throwstar e /i/i/i #restart #end :touch Some horrible star-throwing device. #restart :bombed The strange device is damaged! #cycle 10 #restart
ANYWAY. Green Dude just stole the show for a bit, but there's also this dark green star throwing object inside the chamber that spawns an endless onslaught of stars. I'm trying to be pedantic and use "throw" as the verb as the command to produce this indestructible homing projectile that sours many games is #throwstar. Go ahead and watch one of the thrown stars this object produces and see how long until it disappears on its own and you'll learn why these things have a lousy reputation.
Let your energizer run out and enter the chamber when not invincible (if you even can!) to really understand their enduring lack of popularity.
Of course, like everything else in ZZT, ZZTers over the years have come with creative uses for stars and ways to mitigate their potency a bit, but in their unadulterated form they quickly get extremely out of hand.
#Restart is the new command here. This command is basically a special label jump that always jumps to the very beginning of an object's code. It's the easiest way to make a loop.
Something to consider here is that touching the object will interrupt the sequence of idles, and since there's no locking involved, that means that by standing to the right of the object and holding left, the player will just endlessly be hurt by stars until they die with the pause being ignored entirely.
And, while you probably won't be able to tell due to how quickly the chamber fills with stars, this is a good place to mention that picking up an energizer will reverse ZZT's idea of "towards the player". The stars will actually try to run away from you. You can see this a bit better with the caged animals in the zoo on the bottom right quadrant of the board. They'll also instinctively try to escape from an invincible player. This applies to objects that try to /seek the player as well! So be careful when mixing energizers and objects as the behavior often isn't desirable there.
There's also a new built-in label to cover here. :Bombed will unsurprisingly be activated if an object is caught in a bomb's explosion. Here the device gets damages and so the stars will flow significantly more slowly.
By now, nearly everything in this little area has been seen, though the left ATM has one new command and demonstrates an interesting property of it!
@ATM #end :touch An ATM machine. !1;Try to make a withdrawal. #end :1 :1 #zap 1 #put s purple gem #put s purple gem #put s purple gem #put s purple gem #put s purple gem Lucky break! #end :1 Looks like it's finally out of gems.
None of this is actually new here, but the use multiple #put commands demonstrates something that might be unexpected.
What's unusual here, is that the object will keep executing code after a #put, so upon making a withdrawal all five gems appear at once. Clever programmers will use this to push the player away by putting something and then using #change to change it into empty tiles. This only works if the element can be pushed in the first place. This behavior wouldn't work properly if we were putting down walls. If the element currently occupying the destination tile can't be pushed, then it is overwritten. By not specifying a color with #put, the color of the replaced tile will be used instead (unless ZZT has a default color for the element).
When you pick up the gems, the boardwalk gets ruined! ZZT only keeps track of what's beneath an element when it has a stat. This allows objects like the jogger to run around without messing up our woodwork, but the gems do not have stats and simply turn into empties when collected. (Objects and other statted elements will also take on the background color of the tile they are standing on, which is why the jogger gains and loses a brown background as they move between dark yellow on black fake walls and black on dark yellow fake walls.)
#Put has a genuine bug associated with it that can cause problems if you're not aware of it. ZZT will prevent you from putting something out of bounds, however the code that does this is slightly off and as such, the bottom row of the playable area of any board is considered off limits. Modern forks and source ports offer the ability to fix this bug, but if you're targeting vanilla ZZT v3.2 it's something to keep in mind. This bug applies regardless of the direction. An object currently in the bottom row will be unable to #put w blue key successfully even though the bottom border isn't relevant there.
There's still one last person on the boardwalk that's yet to be covered, but they put even Green Dude to shame as far as how much there is to cover there. For now, let's travel to the north eastern alley proving I did not have a solid plan of how to arrange this board to best present concept from the most basic to the most complex!