History of ZZT
The first official release of ZZT occurred in 1990, although most of the
officially released ZZT games would come about in 1991. Four full games were
developed and published in the registered version of ZZT: Town of ZZT,
Caves of ZZT, Dungeons of ZZT, and City of ZZT. The
company Epic Megagames, first known as Potomac Computer Systems,
was a one-man company then, belonging to Tim Sweeney.
The ZZT engine was soon followed by a slightly more advanced engine, Super
ZZT. Three games were officially released for Super ZZT: Proving Grounds,
Lost Forest, and Monster Zoo.
Both of these game engines were packaged with a world editor, which is
largely seen as ZZT's most remarkable feature. However, the original ZZT
engine placed no barriers to editing, while Super ZZT required the use of
a command-line switch /e. Also, ZZT in-game documentation for the editor was
superb, while Super ZZT's documentation was nonexistent.
As far as graphics and sound are concerned, none of the games were "epic."
But as a game creator, ZZT was absolutely "epic." Its success guaranteed that
we would see more from Epic Megagames. We did.
I would like to say that the rest is history...but it wasn't.
MegaZeux
People created their own worlds in ZZT. Oh, did they create more worlds. But
a funny thing happened--people started crashing against the upper limits of
what ZZT could offer as a game creator. For whatever reasons, when you put new
power in the hands of an unskilled but ambitious individual, said individual tends
to think bigger than what the world can support.
In 1994, Alexis Janson created a derivative of ZZT, called MegaZeux. This
game creator gave game developers significantly more capabilities, while
still retaining the same "text mode noir" as ZZT. While it was a decent editor,
MegaZeux didn't maintain any sort of ZZT "standard." This game
creator was designed to exist in its own right, with its own formats and
syntaxes.
Building a Better Engine
Numerous attempts have been made to create an "Open ZZT" engine that can
exist anywhere and everywhere. The most notable attempt to faithfully
reproduce ZZT from the original assembly is Lyon, for the .NET
platform. There are a few others, such as Tyger, Dream ZZT, and Direct ZZT,
although these projects lack completeness.
In terms of web-based ZZT, it appears ZZT Ultra is the first real
attempt to build the game engine in Flash.
The Making of ZZT Ultra
After I had worked on porting the entire Kroz series to Flash (see
Cruz), I started a new job
that kept me extra busy. Eventually, for reasons unknown, my attention
returned to the ZZT scene.
Since revamping old text-mode games seems to be my forte, I decided to
take a crack at making an online version of ZZT. This, like many other
projects, involved design-via-observation. To misquote a famous philosopher,
"I see, therefore, I make."
I am no stranger to reverse engineering. However, I thought that trying
to rebuild ZZT in a perfect rendition of its original behavior was
missing the point. The integrity of the engine is paramount, but the overall
experience cannot be sacrificed.
So, in starting ZZT Ultra, I wanted to up the ante in terms of ambition.
The engine would run any ZZT or Super ZZT game, but it would run it in a
special way--its code base would encapsulate the functionality, while at
the same time, being far more powerful than it needed to be, for the purpose
of extensibility.
Technical Specifications
The original ZZT had very simple requirements. Roughly speaking...
- IBM PC 8086/8088 or better CPU
- MS-DOS 3.0 or better
- 400 KB RAM or higher
- Monochrome or color monitor (for 80x25 or 40x25 text mode)
- IBM PC speaker
- 101/102-key keyboard
- Mouse (optional)
The "new" requirements for ZZT Ultra should work on any modern web browser:
- Adobe Flash 10 or better
- 1.00 GHz processor or better
- 800x600 screen resolution or better
ZZT Ultra is not very ambitious in terms of graphics. This makes sense given
that ZZT was never ambitious graphically, either. The sound is a different
matter--the sound is dynamically generated to ensure accurate reproduction of
PC-speaker-like effects. This is one reason that a fast processor is desired,
because low-latency dynamic sound generation ensures the best reproduction possible.
ZZT Ultra Components
- 80x25 and 40x25 text-mode graphical emulation pane
- Dynamic sound system
- File parsing system (legacy and modern)
- ZZT-OOP compiler
- ZZT-OOP interpreter
- Real-time object manager
- World/Board manager
- Message dispatch system
- Integrated GUI editor
- Integrated world editor
80x25 and 40x25 text-mode graphical emulation pane
For anyone who is familiar with text modes for the IBM PC, very little about
this component is left to the imagination. The IBM PC Code Page 437 character
set is used to draw characters at predefined slots over the entirety of the display.
There are a total of 256 characters in this code page.
The colors are the typical 16-color palette found in the CRT color text modes.
The ZZT game engines used the "blink" configuration setting, and so ZZT Ultra also
implements this setting. Foreground color (what the character is painted) is one of
16 colors, while background color (what the non-character portions of the cells are
painted) is one of 8 colors.
The "blink" flag (the highest-order bit of a color byte) is mostly used to render
water in the game, but some third-party games use the blink flag in other contexts.
Dynamic sound system
The PC speaker was a fairly poor sound-generation device, but the ZZT engines gave
it a good run by allowing individual games to define their own music and sound effect
sequences, which are played through the speaker. ZZT Ultra emulates this functionality.
By keeping samples of PC speaker frequency cycles and percussion effects, ZZT Ultra can
play back any original ZZT sound flawlessly. Some emulators run into problems reproducing
this type of sound because the real-time nature of some of the effects makes it difficult
to accurately time when pulses must be pulled in and out. In particular, the simple "tick"
sound when the player moves has gotten mostly lost in emulated environments, but ZZT Ultra
reproduces it faithfully.
There is a consequence to dynamically generated sound--it requires that the CPU be fast
enough to handle real-time waveform generation without skips or pops. For this reason,
ZZT Ultra can be subject to sound latency with slower computers. Short latency gives best
performance, but might stutter if the CPU is lagging. Long latency, while most compatible,
ends up throwing off the timing of the individual effects when played in rapid succession.
File parsing system (legacy and modern)
Although the integrated editors of ZZT and Super ZZT were very helpful for third parties,
the binary file format itself did not lend itself to more sophisticated types of modding.
ZZT Ultra can load any world file of ZZT or SZT extension provided that it respects the
original engine limitations (i.e. can load in the original program). However, this is only
to support such worlds in ZZT Ultra--there is only limited support for saving in these
formats. ZZT Ultra does not try to "extend" the legacy formats; they are limited to what
they could do with the original editors.
ZZT Ultra readily supports a new "extended ZZT" format, which is far more conducive to
modding outside of ZZT Ultra. The WAD file format, first used by id Software,
is now the preferred choice for world files saved in ZZT Ultra.
ZZT-OOP compiler
Unlike the original ZZT engines, which interpreted every line of ZZT-OOP code by parsing
the text in real-time, ZZT Ultra compiles code first and interprets the compiled bytecode
later. Programmatically, this was a sensible decision that optimizes the overall run
operations.
ZZT-OOP in the original ZZT engines only applied to the OBJECT and SCROLL types. ZZT-OOP
in ZZT Ultra now applies to all object types in the game, as well as serving as a mechanism
for dispatched message handlers. This allows one to effectively rewrite the engine's frontend
operations in ways that were never possible with ZZT and Super ZZT.
Also, ZZT-OOP has many new commands and syntax conventions in ZZT Ultra, which makes the
language far more powerful. However, these new language features do not conflict with the
original ZZT-OOP code from legacy world files. This is because the compiler performs a few
tricks with namespaces when it knows it is compiling code for a legacy world.
ZZT-OOP interpreter
When ZZT-OOP is compiled into bytecode, it is stored internally within ZZT Ultra for fast
run operations until one of the following occurs:
- New world file loaded (previous custom OBJECT and SCROLL code is discarded)
- New OOP types loaded (new world files can modify OOP code for built-in types)
The concept of an "instruction pointer" or IP is used in the interpreter for executing
bytecode. For more information on how the interpreter works, see the details about the
message dispatch system.
Real-time object manager
Most of the animations and real-time events that occur in a ZZT board are implemented
in the form of status elements, which are data structure instances containing location,
cycle, IP, direction, and other miscellaneous pieces of information. These status
elements are tracked independently of the individual grid squares on the board itself.
ZZT Ultra iterates these status element objects at the appropriate rate when the game
is not paused or in some other transition mode. It should be noted that the game speed
setting influences how frequently these objects are updated--the original default of 9.1
Hz was modifiable in the original ZZT engines, and is also modifiable in ZZT Ultra.
The object instances themselves have a critical difference in ZZT Ultra--they support
an unlimited number of additional dictionary-style members instead of the original
"P1,P2,P3" fixed-size configuration. This lets one make significantly more complicated
object behavior than what would have been available to designers in ZZT or Super ZZT,
because any number of new "member" variables can be implemented for objects as appropriate.
World/Board manager
The internal backend storage for ZZT world and board information was relatively
simple in the original ZZT engines, but is much more versatile in ZZT Ultra.
The main difference is that the original fixed-size configuration for world
and board information has been discarded in ZZT Ultra, in favor of dictionary-style
attributes. Such information can now be read and manipulated easily within ZZT-OOP.
By allowing the world and board to be internally modifiable, it is possible for a
third party to program behavior with board sizes, camera control, lighting, and other
attributes that would not have been possible in ZZT or Super ZZT.
The original hard limitations on flags for the world have been erased in ZZT Ultra.
There is now a superset of the original "flag" functionality, called global variables,
which contains all of the original flags. Global variables can be assigned non-boolean
values in ZZT Ultra, which increases the overall versatility of the engine.
Other features that were hard-coded in the original engines are now modifiable.
It is possible in ZZT Ultra to set context-sensitive rectangular regions for a board.
One can also set and use customized masks for area effects (e.g. bomb and torch).
Message dispatch system
ZZT Ultra supports two types of messages used with ZZT-OOP: sent messages and
dispatched messages. A sent message simply modifies the IP of the target
object for when it is executed during the next real-time iteration. This behavior works
the same as it did in ZZT and Super ZZT.
A dispatched message is a new concept to ZZT-OOP. Instead of working with objects
in real-time, a dispatched message is sent to the interpreter to be executed immediately,
and it does not resume after a movement or "idle cycle." This is because a dispatched
message is designed to accomplish a specific function as opposed to execute an
object-oriented action in real-time. Whereas a sent message is associated with an
object instance, a dispatched message is associated with an object type.
Dispatched messages are commonly triggered by ZZT Ultra in response to various events,
like keyboard, timing, world loading, etc. Most of these events are handled by the
main type code (the EMPTY type). It is also possible for an object to dispatch
messages to the "main" type or to other types using the new ZZT-OOP
DISPATCH or
DISPATCHTO commands.
Because so much of ZZT Ultra relies upon dispatched messages, the frontend "core"
code is decidely thinner than it was in ZZT or Super ZZT. Instead, it is expected
that ZZT-OOP code will account for a great deal of the functionality, favoring such
code over hard-coded frontends with limited versatility.
Integrated GUI editor
The original ZZT engines did not let the designer customize the GUI. In ZZT Ultra,
there are several ZZT-OOP commands that set or modify the GUI.
ZZT Ultra comes with a GUI editor, which gives the designer a mechanism for editing
sidebar menus, title screens, and other types of GUIs.
GUI spec files are stored as text files with extension ZZTGUI. The file is
easily-readable JSON, which can be useful if the designer
wants to edit the GUI externally.
ZZT world files created by ZZT Ultra can incorporate GUI spec files, allowing a
world file to define its own frontend.
Integrated world editor
The original ZZT engines had a world editor, and ZZT Ultra has its own world
editor. All the new world-defining features of the engine are supported in this
new editor.
Having the world editor be part of the same application as the game is necessary
due to the nature of ZZT itself. The spirit of ZZT is only realized if third-party
developers are granted control over the features of the engine, as this is something
that has been expected of ZZT from the very beginning.
Fall of ZZT: The Curse of Production Quality
This adventure is going to be an exclusive to ZZT Ultra, making use of
the various enhancements offered by the new engine. It will be a tongue-in-cheek
revue of the early Epic Megagames titles, making light of how advancements in
computer technology have caused game characters from earlier time periods to be
forgotten.
The ZZT Guy plays himself, and meets the other classic, disenfranchised Epic
game characters while pursuing a quest to find out where Epic had gone wrong.
|