Exult Developer Interview: Reinventing a Classic

Developer Interview: Jeff Freedman and Ryan Nunn on Exult

O'Reilly Network: What's one interesting example of
usecode's intrinsic functions that the Exult team figured out, and how did you
do it?

Jeff Freedman: One of the most challenging was
path_run_usecode (whose name, of course, we didn't know). Certain
animation sequences in the original, like walking to a bucket and picking it
up, weren't happening in Exult, and this was the "unknown" intrinsic common to
those situations. It's taken a lot of trial and error, but we eventually
figured out that when this function is called, the NPC [non-player character]
should be scheduled to walk to the given location, and, when he gets there, the
specified usecode function should be called with that final parameter as the
event ID.

Ryan Nunn: The path_run_usecode usecode
intrinsics are reasonably interesting. To figure them out is trial and error
and looking over lots of disassembled usecode. We pretty much just have to
watch the usecode executing to see what it's trying to do. Then all that can be
done is to take a guess. Sometimes these guesses are correct; other times they
are not. There are numerous cases where we can get an intrinsic working, but
then there will be one or two cases where things just don't work. It's annoying,
and many times we had to implement workarounds for the cases that just didn't
work.

ORN: So what are your impressions of the usecode language
that Origin developed for the Ultima games?

JF: It does the job nicely, and was a really smart way for
them to control the game. Also, designing their virtual machine to be purely
stack-based makes it easy to write a compiler that targets it, as we've done in
ExultStudio.

RN: It's a fairly functional language, but it does have
some flaws. In particular, in order to execute timed events, another language
had to be used. So there were actually two languages.

Another is that the usecode must finish executing before the game can go on.
It would have been more flexible if functions could be suspended, and resumed
later.

ORN: It was basically a hit-or-miss method when it came to
figuring out the way game stat data is interpreted by the Ultima VII engine.
Describe the steps involved that the Exult team devised in order to decipher
the stat data.

JF: The general statistics weren't that hard, since they're
shown in the "stat" displays in the original. What we've guessed at is the way
they're interpreted during combat. And it's probable that we are not doing
things the same way as the original. In particular, I don't think we're using
dexterity to determine speed, and I'm pretty sure the original did.

RN: The simplest way is to use the cheat menu system in the
original game and toggle flags/change data and then compare savegames before
and after the changes were made. Lucky for us, most of the data had already
been figured out by others, but there was still a fair amount that we needed to
figure out ourselves.

ORN: Exult uses the ScummVM project's FM synthesizer. Why
was this particular sound code used? And was there much difficulty involved in
integrating it with Exult?

RN: The FMOPL FM Synth (which I did grab from ScummVM) is
an optional method to play music with. By default, we use the standard Windows
MIDIOut on Windows, Timidity on Linux, and QuickTime on MacOS. I used the FM
Synth code from ScummVM because porting it wasn't going to be difficult. The
idea of why I did it was to allow PDA devices, such as the Zaurus (which doesn't
have any native method of MIDI playback) a method of playing music without
requiring an excessive amount of storage space.

ORN: How similar are the graphic scalers used in Exult to
those in Snes9x?

JF: They're essentially the same, since both were done by
Derek Liauw. I'm always amazed at how good they look, even compared to the
scaling done by image-editing programs.

RN: I would say they are "exactly" the same, but that isn't
really true. We do use the same scalers as Snes9x, but the code of our scalers
is slightly modified to perform clipping (the original code by Derek didn't
have any clipping), and are also in C++ templates so they can easily work in
both 16-BPP and 32-BPP display modes. Other than that, they are the same.

ORN: Any plans to add additional graphics and sound
improvements to the original Ultima VII titles? Or, through ExultStudio, how
extensively will the user be able to modify the games' graphics and sound?

JF: ExultStudio is useful for changing graphics, and makes
it easy to import PNG images into Ultima VII, or to export
Ultima VII images as PNGs. You can also bring up dialogs to set
weapon, armor, and monster attributes. Theoretically, you could create an
original game with our tools.

RN: The idea is for everything to be changeable. As it
currently is, the sound effects are WAV files and music can be supplied as both
MIDs and OGGs. Graphics need to be in the Ultima VII format, but we supply
plug-ins for The GIMP and Photoshop, as well as a conversion tool.

ORN: Any plans to create versions of Exult that support the
other Ultima titles (i.e. Ultima VIII, IX)? How similar are the various Ultima
engines, or are they vastly different?

RN: There are no plans for Exult to support any other
Ultima titles. All of the engines are just far too different. Ultima VII and
Ultima VIII have a number of similarities, but there are just too many
differences. The Ultima VIII usecode format is so vastly different from Ultima
VII that supporting it would be as much work as writing a new game engine.

However, a few people from the Exult team (including myself) are working on
Pentagram, a re-creation of the
Ultima VIII engine. There is going to be very little code reuse from Exult.

ORN: Any advice to those looking into reverse-engineering a
game engine -- either technical or legal advice?

RN: Find out as much about the original game as possible.
Knowing cheats (especially anything that cause minor state changes, such as
toggling flags), bugs, and other engine limitations can be a great help in
decoding file formats and understanding the internal structure of the original
engine.

Try modifying the [game's] original data files and observing the result to
the game. This can be a good way for decoding file formats, especially opcodes
in compiled scripts. If the files have checksums, though, this may not
work.

When examining various files, look for patterns in the individual files and
between files. Arrays are often very obvious to notice.

There may be similarities between the file formats of a game and other games
made by the same company. For instance, the Flex file formats used in Ultima
VII, Ultima VIII, and Ultima IX are all fairly similar. If another game by the
same company has been reverse-engineered, it may have code that might be useful
to you. Alternatively, if you reverse engineer one game, it may be "trivial" to
add support for another game.

Last and not least, always -- and I repeat, always -- use platform and
byte-order independent methods to read and write files.

JF: So far, we haven't had any legal trouble. I'd like to
think this is due to our emphasis that users have to own a legal copy of Ultima
VII in order to use Exult. So we're helping to sell copies of the game. But
it's just as likely that we're merely lucky.