Written by Peter Kelly (Last updated: 27 January 1998).
Around 1990--91 there was a project in Russia similar to ours. Pretty much all of the file formats were figured out and documented, and a suite of programs which would allow you create and edit AGI games was created. This was called AGDS (Adventure Game Design System).
Of course this was in the days before the internet took off, so no one put up a website about it, which is why we assumed we were the first ones to do this. But thanks to Igor Nesterov who supplied the package and performed the original translation of the documentation, we can now benefit from this project. Vassili Bykov has since provided an improved translation.
I have recently found out that the author of the package was Alex Simkin (with help from Serge Lapin). Alex tells me that some of the tools were originally written to solve a puzzle in Leisure Suit Larry 1. A friend from Elias (a publising group) asked him to make a compiler so he could edit the logics and translate the games into Russian. The AGDS package was the result. I would like to thank Alex and Serge for all their work on AGDS, which has helped us a lot, particularly in the area of logic programming. I would also like to thank Konstantin Mironovich for putting me in touch with Alex after seeing one of the AGI websites.
Written by A. V. Horev, Chuvashia State University and translated by Vassili Bykov.
Translator's note: The original uses terms such as AGDS interpreter, AGDS programming, AGDS language. In the translation I am replacing AGDS in these with AGI, leaving AGDS only to refer to the set of resource creation and manipulation utilities this document originally accompanied. I believe this is only fair.
In the ``classic'' AGI, words of command names are separated with
dots, while in this text they are separated with underscores
(new.room.v
vs. new_room_v
) - the consequence of using MASM as
the assembler for logic code in AGDS. I leave the names with underscores,
but keep this in mind.
``... how did it come to pass?'' (from a song)
I want to tell you how we made this program using the AGDS package we bought because, in my option, just reading the package's manual is not enough to create your own programs. In this story I will try to point out to you the causes of difficulties we had, tell how we made that program, how we used the utilities from the package, and how, in my opinion, you have to approach writing such programs. But in order to understand this story you have to have to at least skim through the manual.
So, you bought the package and read the manual. Probably, you don't have a feeling you can start coding right away. I didn't -- however the manual you receive with the package now has been updated based on my experience of using the product. Anyway, now is the time for you to read on.
It is possible that, having read the manual, you ask yourself a question, ``Can I master the package in a short enough time to create my own program?'' This is why a bit about myself. I have eleven years of programming experience, however my experience has never had anything to do with game or education programs development -- most probably, just like yours. I have always been wondering how programs like these are created, and I suspected that they require specialized tools and experience in that kind of programming. Probably, the developers are not quite regular programmers. This is all true. But I should say that in order to create a fairly simple educational program, like Thunderstorm, it is enough to have experience with at least one high-level language, a personal computer, AGDS package, and enough time and desire to do that. But you will learn the package fairly quickly only of you have good programming skills and some experience of using IBM PC.
I should say this project popped as if out of nowhere and we had a fairly short time to finish it: about 2 months, a month of that to make the program itself. At that time we had no idea how we shall approach that task, nor did we have the tools to do that. All we had was IBM PC XT and the rumours of a unique AGDS package. That is, we had to start from scratch and, honestly, it was pretty hard.
In a hurry I went to Moscow, checked out the product, found it suitable though the real test would be at home. Bought. Came back. Printed out the manual. Read. Complete mess in the head. No idea what to do. More news: the demo does not link on an XT. How can I start working with this knowledge, how can I explain the artist how to draw everything so it is then easier to program? And how to program is not clear either. What to do? Tried to play a few Sierra games to get used to them. But all the games are controlled using key phrases that, naturally, I don't know. (He probably means the input in English and he doesn't speak English. --VB) No time to guess. Tried to print out words.tok using WL utility. Didn't help much because the dictionary stores separate words while the game requires complete sentences which are in some way analyzed by the interpreter and, depending on the result, the game develops. Now this is interesting! This means I can write an educational program and communicate with it using sentences. (As it appeared later, only English).
Then I found that in In Search of a Lost Planet (by the way, it begins
with a key phrase `start game') you can load and turn on the debugger
using Alt+D
and Scroll Lock
. This opens a text window with the
commands. Later came understanding that to the left of a command is
the number of logic it is from, while to the right and below, the
result. Noticed that if nothing happens in the game (no input using
error keys or key phrases), the logic numbers and commands are the
same, and are mostly tests. So it is like something runs in a cycle.
Besides, if the debugger was turned on while the character on the
screen was moving, every cycle a new phase of the character's movement
was drawn on the screen (for example, the leg was going up). Picked up
the manual, read Interpreter Work Cycle. All became a bit more clear.
As it appears, each cycle the interpreter redraws the character if a
movement command was issued (this is easier to see than to understand
from explanations, try to do that yourself). So the character is a
controlled object (Ego, as it is called in AGI language), and has a
VIEW resource associated with it, which is its picture on the screen.
And, as the movement direction changes, the interpreter automatically
chooses the proper animation (loop of cels in the VIEW resource) to
draw the character.
I disassembled the source code of the logics seen in the debugger using SM. Printed them out. As it appears, the programs are much larger than I thought because what I saw in the debugger were only the commands required at that moment according to the tests in the program. And then I started to figure out how to write programs, specifically educational programs, for the interpreter, because there were no examples at all.
The title intentionally talks about an educational program because I think we shall use AGDS to create educational programs on various topics. Learning while playing is the best kind of learning. I think you will agree that a good educational program should be like a game.
When you bought AGDS you have also received a Thunderstorm educational program as an example. Not only the program itself, but also the commented source code of logics of that program. I think the program is far from being perfect but it may be a good example to you in learning AGI language. Though the program is fairly simple and does not use all commands of the language, I hope it can give you enough information to later easily read the source of Sierra games' logics, which are the true examples of AGI programming.
So let us begin with the fundamental issues of AGI programming, since programming is the most complex part of game creation.
Programming for AGI is fairly unusual because of the specifics of how the interpreter works:
assignn 10, 2
means store the value 2 into the variable
number 10; assignv 10, 2
means store the value of variable 2 into
the variable 10; set 10
mean set flag 10 to 1. Most often you will
have to memorize what each command does and the type of its
operands.new_room_v
used to
completely change the program behaviour.I want to warn you that most probably you will not be able to use the debugger on IBM PC XTs because DUU and VM utilities (of the versions we have) have problems including log.dbg into data volumes on IBM PC XTs. Because Thunderstorm was created on an XT, I wrote it ``blindly'' because I could not (and still cannot) use the debugger. For that reason there is no debugger in the program - this may complicate your study of Thunderstorm.
By the time I had to start coding I had an approximate design of what we wanted to make but no idea of how to approach it.
Was that the right design? Could it actually be used to create a program? The design, and how detailed it is, determines a lot, in particular the efficiency of the programmer's work. The design I got was written in general, not considering the interpreter's capabilities. I'll be straight and tell you that I used it only to keep to the required topic and to not "lie" in the accompanying text, so there is no point in including the design here. You will get an idea of the plot when we start looking at the source code.
But here I want to tell you what criteria the design should satisfy.
First of all about the designer's personality and his role in the project. The designer should be able to create designs, be a good psychologist, and have an idea of the interpreter's capabilities to not come up with an unrealistic design. (Which must be impossible at the first try.)
The designer has the key role in the project -- assuming the team includes at least three members: the designer, the artist, and the programmer. The designer, keeping to the design he created, should facilitate the collaboration between the artist and the programmer.
The artist's and the programmer's work can be separated in time: the artist should follow the design first, creating VIEW and PICTURE resources for the programmer to bring together according to the same design. In the course of work it is possible that some corrections to the already existing art will be needed. This is why, once the design is created, communication between the artist and the programmer is very important, especially because there should be some equilibrium between the artist's and the programmer's effort, especially dealing with VIEW resources. The increase in the required artist's effort almost always decreases the programmer's, and the other way around.
Here is an example. There is a place in Thunderstorm where the ionisation process is explained. It shows how a moving electron collides with an atom and kicks out another electron from it. It is possible for the artist to create three separate VIEW resources: atom, electron, and the electron that gets kicked out. Then the programmer has to describe these three objects separately, associate them with the VIEW resources that will be their on-screen images, and code the whole ionisation process: set the electron's motion, determine the moment when it touches the atom, describe how the kicked out electron moves. This is extremely tedious and may dramatically increase the size of the program, or require subprograms. The alternative is to make it simpler by giving more work to the artist: draw the whole process in a single loop of a VIEW resource, the way it is done in Thunderstorm. In this case all the programmer has to do is describe one object, associate it with the view resource and play back the animation. Remember that with this approach you cannot arbitrarily increase the animation length: VIM editor and the interpreter itself have their limitations, besides, a large VIEW resource takes a significant part of the interpreter's internal memory. Even if does not overflow, redrawing large images may significantly slow down the program. Don't make animations longer than 25 average (50x50) cels.
We have distracted from the design a bit. The design should completely and in details describe the plot. Using the interpreter's terminology, the following should be included:
Probably you can extend this list yourself. How detailed the design should be is subjective, but you have to be specific.
Therefore, an advice: The design should be created by someone familiar with the interpreter capabilities. Otherwise you will have to change the design as you go, or the programmer will have to improvise, which can lead to conflicts.
So let's assume the design is ready, the art is drawn, and we can
Each room in the game has its program (LOGIC resource, or simply "logic"), the backdrop, and the objects some of which can be controlled by the interpreter according to the directions in the program, and one of which may be controlled by the user using the arrow keys (object 0 called Ego).
Interpreter Work Cycle explains that at any moment in the interpreter
memory (approximately two hundred 256-byte pages) the LOGIC resource 0
is loaded which is continuously executed and is, in fact, the
algorithm of the program work. Also the memory contains other
explicitly loaded LOGIC, VIEW, and SOUND resources. PICTURE resource
should not be in memory because once the backdrop is displayed we can
unload it from memory using discard_pic
command.
Starting to code, remember that the program controlled by the
interpreter executes in cycles (this is unusual and takes some time to
get used to). This means that when call
or call_v
command is
executed in logic 0, control is passed to the logic which is the operand
of the command. That logic may similarly pass control to yet another logic,
etc. The called logic's commands will be executed once (unless it
contains inner cycles not associated with controlling objects - for
example, incrementing a variable in a cycle until it reaches a certain
value, or looping waiting for a key to be pressed, etc.) and, when
return command is encountered, or by default when the last operator of
the logic is reached, control returns to the command that follows the
call
command of the caller logic. Its commands will also execute and
return
command will return control to its caller, etc. until control
returns to logic 0. return
in logic 0 returns control to the
interpreter which executes the rest of the Interpreter Work Cycle (see
block diagram), for example redraws or moves controlled objects
according to their settings.
Important consequence: You should never perform any cyclic activities related to drawing objects on the screen yourself. The interpreter will do this for you. But you have to provide it with the proper information.
For example, suppose you want to play the animation associated with
the object 1 once. To do that it is enough to issue end_of_loop 1, 121
command, where 121 is the identifier of the flag set to 1 once the
animation reaches the last frame. Therefore, you have to return
control to the interpreter on every subsequent iteration of the
interpreter cycle until this flag is set. The code looks like this:
if_ not_ isset 121 else_ A end_of_loop 1, 121 return A: ..............
(This is correct assuming that issuing end_of_loop
to a running
animation has no effect whatsoever (in particular does not affect the
value of the counter of interpreter cycles left until the next cel
change) -- because this is exactly what happens here. --VB)
The same applies to playing sounds, moving objects and, in general, all commands setting a flag once their action completes.
Now it gets a bit more clear and we can start working on logic 0. (It
is a good idea to read further keeping handy the source code of
Thunderstorm's log0.asm
, log01.asm
and log05.asm
.
Logic 0 (unlike all other logics) always stays in the interpreter's
memory and determines all the interpreter's actions relevant to the
overall game control. This is why I decided it has to store the
description of actions common to all logics of the future program (how
many logics there will be I knew only approximately). These actions, I
decided, would first of all include the tests if the keys I have
reserved for certain actions (help, turning sound on or off,
restarting the program, etc.) are pressed (see log0.asm
). Besides, it
should include one-time actions that show the program title screen and
perform initial initializations. (Initialization code is in logic 1,
log01.asm
).
Initialization code is executed only once and contains the commands that change the cursor shape, built-in debugger parameters, character colour, maximum score, and assign certain numeric codes to the keys I have chosen (set_key command), to be used later by the controller command to determine the state (pressed/released) of these keys.
Because logic 0 is so important, here is its block diagram:
+-----------+ +--------------+ +---------+
| v17 = 0?| | | handle | | |
|---------|Y+--->|interp. error +--->| quit |
| N | | | call_ 97 | | |
+-----------+ +--------------+ +---------+
v
+-----------+
| v0 = 0 ?| |
|---------|N+--->-----------------------+
| Y | | |
+-----------+ |
v |
+-----------+ +----------------+ |
| f6 ->1 ?| | | set script | |
|---------|Y+--->| table | |
| N | | | size | |
+-----------+ +-------+--------+ |
v | |
+-----------+ | |
| call_ 01 | | |
|initialize |<-----------+ |
| program | |
+-----------+ |
v |
+-----------+ +---------------+ |
| f6 ->1 ?| | | new_room 5 -| |
|---------|Y+--->| start | |
| N | | | session | |
+-----------+ +---------------+ |
v |
+-----------+ |
| new_room 2| |
| title | |
| screen | |
+-----------+ |
_---------------------------------+
v
+-----------+ +--------------+
|F1 press | | | display |
|---------|Y+---->| message 6 |
| N | | | (help) |
+-----------+ +--------------+
v
+-----------+ +--------------+
|F2 press | | | toggle |
|---------|Y+---->| flag 9 |
| N | | |(sound on/off)|
+-----------+ +--------------+
v
+-----------+ +--------------+
|F10 press| | | change |
|---------|Y+---->| speed - |
| N | | | (v10) |
+-----------+ +--------------+
v
+-----------+ +--------------+
|F9 press | | | |
|---------|Y+---->| restart |
| N | | |(restart_game)|
+-----------+ +--------------+
v
+-----------+ +--------------+
| Alt+z ?| | | |
|---------|Y+---->| quit |
| N | | | |
+-----------+ +--------------+
v
+-----------+ +--------------+
| Alt+i ?| | | show |
|---------|Y+---->| credits |
| N | | | info |
+-----------+ +--------------+
v
+-----------+ +--------------+ -
| v0 = 2 ?| | | | |call
|---------|N+---->| call_v 0 |- - - - |the current room's
| Y | | | | |logic !
+-----------+ +--------------+ -
v ^ +---------------------+
+-----------+ +--------------+ |- display greeting, |
| f54->1 ?| | | N | | | switch to the |
|---------|N+---->|------------|Y+--->| text mode |
| Y | | |hit any key?| | | ; |
+-----------+ +--------------+ |- f54 -> 1; |
| |- return |
+----------------<-+ +---------------------+
v |
+--------------+ +----------------------+
|hit any key?| | |wait for any keypress |
|------------|N+-->|(initial greeting is |
| Y | | |on the screen now) |
+--------------+ +----------------------+
v
+--------------+
| return to |
| graphics mode|
|and execute |
| new_room 5, |
| i.e.begin |
| game session |
+--------------+
Note that logic 0 always begins with a check if variable 17 is 0.
Non-zero value means the interpreter has found an error. Error handler
is in log97.asm
(you can use this logic unchanged in all your
programs).
If there is no error, let's check the current room number in the
variable 0 (further in the text we shall refer to variables as `v
'
followed by the variable number, for example v0
means variable 0). We
start in the room 0 because all variables are 0 when the interpreter
starts. Initialization logic executes in room 1 (see log01.asm
),
called using call 01
. After that we check if we have just started the
program or the program has been restarted with restart_game
. This is
to avoid showing the title on restart. So on the first execution flag
6 (f6
) is reset (equals 0) and new_room 2
command executes
meaning we move to the room number 2.
Second room is the Thunderstorm title screen. The corresponding logic
is logic 2 (same as the room number, have a look into log02.asm
- when
we are finished with logic 0 we shall come back to it).
new_room
is a complex and powerful command, it executes a lot of
actions. For now, it is important to understand the following:
new_room n
stores the command operand in v0
, i.e. the
value of n, 2 in our case.f5
-> 1)!After new_room 2
log0.asm
checks if any reserved keys have been
pressed, using controller command. Then we check if we are in the
second room (title screen). If yes we check if any key is pressed
using test_key. If not, the title screen logic is called. As soon as
any key is pressed, the screen is switched to the text mode and the
greeting text is displayed. This also sets flag 54. Next time logic 0
executes the invitation is not displayed (because we check f54
) and we
simply wait for any key to be pressed in a wait loop. During that
cycle control is not returned to the interpreter. When a key is
pressed the screen switches to the graphics mode and new_room 5
command is issued. In this room the game (education) session begins.
Thus logic 0 should contain:
v17
) and a call to the error handler.call_v 0
command that calls the logic corresponding to the last
room set using new_room v
, which has to be executed each
interpreter cycle.Let us return to logic 2 (see log02.asm
) which describes the actions
in the second room. I shall not describe the details of how the
rainbow is moved across the large letters of the program title, it is
enough to say that the letters have priority 4 while the outer
areas have priority 7. The rainbow, stored in a VIEW resource 21, is
assigned priority 5 in the program. This creates an illusion of the
rainbow moving only in the inner area of the letters. You will
understand how to move the rainbow when you study the source code in
log02.asm
.
It is more important to study the overall structure of logic 2 because it is characteristic to the interpreter programs and is common to all logics we discuss below.
Note that the logic begins with a check for the flag 5 (f5
) state.
This flag is reserved by the interpreter. This does not mean you
cannot change its value (you can change any flags and values, reserved
or not; the question is what will happen). The flag is called reserved
because under certain circumstances the interpreter may change the
value of the flag or the variable, or read their (set by you) values
and react in a certain way. You can see the list of all the reserved
flags and variables in the AGDS manual.
Flag 5 is set when the new room is run for the first time, i.e. this
is the first entry into the room's logic. You probably remember that
after a new_room command the interpreter sets f5
and loads the room
logic. After that control is passed to logic 0 which, in turn, calls
the room logic using call_v 0
(see Interpreter Work Cycle).
Any room logic usually includes a part which we can call the
initialization code. This part describes actions that have to be
executed only once on the first call. If you consider that the
interpreter automatically resets f5
on the next cycle, we have an
ideal tool for selecting initialization code. This code usually loads
resources used in the logic. It can load other logic resources used as
subroutines of this logic. If these ``subroutine logics'' also include
initialization code, these logics have to be called after loading from
the initialization code of the room. The example is logic 20 (blinking
stars) which is loaded and called from the initialization code of
logic 2.
Also note how this part loads and draws the scenery (letters of the
title as holes in a screen with the rainbow band moving behind them).
The command sequence load_pic n
, draw_pic n
, discard_pic n
,
... show_pic
is important! Any other sequence can lead to crash
because the loaded resource takes a lot of memory.
Initialization code is finished using return which returns control to
logic 0 and then to the interpreter. The interpreter will reset f5
before the next cycle, and that cycle will execute the bodies of all
the called logics.
I shall not consider the rainbow band motion algorithm (the body of
logic 2) because it is described in the comments in the source code.
Just note that each new action (the band moving up, the band moving
down, company title appearing, and the message ``educational program'')
increments the variable 201 (v201
). At the end of each action a flag
associated with that action is set. After all actions have completed,
new_room 2
command is issued and the title is repeated, unless
interrupted by any key press (which is checked in logic 0).
Before we continue our discussion of logics, a few words about the program structure.
Perhaps I won't say anything new, but the following conditions are important when you design an educational program.
First condition is satisfied naturally if you make the program linear so one can go into a new room only after all the actions in the previous one are complete. In each room the following actions are performed:
There are two possibilities to display the explanation: displaying the
text in a text mode; an example is the Thunderstorm greeting, see
log0.asm
, or displaying the text in a graphics mode using print
command. This opens a text window in the centre of the screen, resized
to fit the text to display. The interpreter provides two modes of
opening the window: it may close automatically after some period of
time, or close when the user presses the Enter key. If the text does
not fit on the screen, it is accompanied by a warning message.
Thunderstorm usually uses the second option (text windows).
To check the student's understanding, a question is asked and three numbered choices are displayed in the text window, which closes after the student types the choice number he chooses in the input line and presses Enter key. This approach I consider the most appropriate. If the answer is correct (indicated by a sound signal and increasing the score by two points), the room is changed to the next one where the teaching continues. If the answer is incorrect (also indicated by a sound signal and a corresponding message) the lesson is repeated and the score is decreased by one point.
The logic that starts teaching is logic 5. Its commented source code
is in log05.asm
, this is why I will give here only a short
description.
This logic, according to the scenario, explains how a cloud is formed.
Initialization code loads only the resources that are required for
displaying the picture that starts the lesson. Because the commentary
text has to be printed sequentially, one of the variables (v130
)
tracks this sequence. When v130
changes, print_v 130
command
prints a new message. v130
is incremented when the ENTER
key
is pressed, confirming that the text has been read and the animation
illustrating the explanation has been viewed. This way the student is
in control of the teaching speed.
Because the program is intended to be executed cyclically, tests of
v130
value are all over the place. They begin with EQn
labels
where n is the value of v130
at which the corresponding
command block is to be executed. These blocks load the resources
required to illustrate the recently printed text and run the required
animations.
Note that in order to repeatedly play an animation associated with an
object (for example, a shining sun -- object 1), it is not necessary to
play its animation using end_of_loop
command and then, when the
animation end flag is set, issue the command again. It is enough to
turn on cyclic animation using start_cycling
and display the object on
the screen using draw command. The interpreter will take care of the
rest if you have not forgotten to include the object into the list of
objects the interpreter controls using the command animate_obj
.
The lesson is finished by printing a question and three possible
answers (MENU
label) using print command with the number of the
message containing the question as the operand. If the correct answer
is selected, teaching continues in the room number 7. Before entering
it all used variables and flags are reset to zero unless they are used
by other logics.
The actions in room 7, where the processes inside a cloud are
illustrated, are described in the LOGIC resource 7 (see source code in
log07.asm
. Logic 7 is loaded in memory automatically when this room is
set as the current using new_room 7
command issued from logic 5. Logic
7 also has a message selection variable (v130
), the value of which is
incremented each time a message is issued. When the logic is invoked
in a cycle, only the block for which the variable value and the block
number are the same executes. Tests are performed by the commands
labelled EQn
where n is the block number.
This logic manages text windows differently. A window goes away automatically after a time period set in variable 21. For example, the following shows message 5 in a window visible for 20 seconds
reset 15 ........... assignn 21, 40 print 5
Resetting flag 15 means the window should close after a time delay
specified by the second operand of the assign command, in half second
intervals. However, the window can be closed before the time interval
expires using ENTER
key.
Room 7 shows the structure of the cloud and how three raindrops are
formed. Let is consider how this is done in greater detail. The cloud
itself is drawn as a backdrop (PICTURE resource 7). How a drop is
formed is in VIEW resource 14. The drops are described as objects
controlled by the interpreter with IDs 1, 2, and 3. These objects are
moved from the top side of the cloud downwards to the specified point
using move_obj
command. When the destination point is reached the flag
specified in move_obj
is set. The moment the motion begins, playback
of the VIEW resource associated with the object using set_view command
is started with end_of_loop
command. The time delay between the frames
(set with cycle_time
), step size -- number of pixels the object moves
each interpreter cycle (set with step_size
) (I suspect they may
actually mean ``each step'' --VB) and the time delay between the steps
(set with step_time
) were determined by trial and error so that
animation finishes by the time the object reaches the destination.
The drops moving down create air flow (objects 4 and 5 with cycles 1
and 2 of the VIEW resource 22 associated with them playing
repeatedly). After all three drops have formed we show how they move
out of the cloud (it starts raining). This is accompanied by cold air
flows (object 6, 7 and 8 -- left, centre. and right arrows) directed
towards the ground. After all drops fall down we move to the room 9
using new_room 9
.
This command destroys all loaded logics and resources in the
interpreter memory used in room 7 (except logic 0) and loads logic 9
(see its source in log09.asm
) controlling the animation of
thunderstorm and the situation after it finishes.
While you study that logic's source note the following:
The rain is displayed as eight drops (objects 8 to 15), the algorithm
of their motion is in logic 10 (source file log10.asm
). The drops are
moved across the screen randomly, and in every starting point for
every drop the playback of the associated animation is started. Study
the source of this logic referring to its commends.
There is no ready-made sound resource with the sound of rain drops. The sound is generated programmatically from the sound which accompanies the rain drops falling down in logic 7 (SOUND resource 12 -- two high-pitched beeps with a pause in between). The trick is to not let the sound play until completion and restart it on every cycle, ignoring the flag associated with that sound.
The type of a lightning (between the clouds or between the cloud and the ground) is chosen randomly. Y coordinate of the lightning between the clouds, and both coordinates and the priority (relative to the lake shore in PICTURE resource 9) of the lightning striking the ground, are also random within a certain range. This creates an effect of the lightning striking close to us or far away, as well as moving vertically and horizontally.
There is no prefabricated SOUND resource for thunder as well. I chose
the sound with the lowest pitch and stop the playback from the program
before it terminates normally. Bellowing thunder effect is created by
allowing the sound to play for a random period of time. The sound is
also accompanied by the screen shaking using shake_screen
command.
To create a delay between the events (lightning, then thunder) we analyse variable 153 which is incremented on each iteration cycle. When the thunder sound finishes playing it is reset to zero and everything starts over.
Thunderstorm can be stopped at any time (after at least three
lightning strikes counted in variable 154) by pressing ENTER
key.
After that PICTURE resource 10 is loaded in memory using variable 30
-- the same scenery but after the storm, with the sun, rainbow, and
seagulls. Speaking of bird watching, logic 112 controlling the gulls
is not loaded from the initialisation code of logic 9. But logic 112
has its own initialisation code which can be executed only if flag 5
is set. We do just that and immediately reset it after the first call
to logic 112.
After you have seen enough rainbow and seagulls press ENTER
and try
answering the question. After a correct answer the room is changed to
11 using new_room 11
command, where we tell a bit about electricity,
lightning and repeat Franklin's experiment.
The source code is in log11.asm
and is, as usual, extensively
commented. There are no new tricks in that logic. Pay your attention
to the new test commands that check whether the user-controlled object
0 (Ego, a cloud) is in a certain area of the screen. Ego is controlled
using arrow keys. You don't have to program them. To start moving the
object in a certain direction press the corresponding arrow key once.
Holding the key down will only slow down the object 0. Object 0 is
always controlled with these keys, however the program can still
control it using the interpreter commands.
Reading the logic source note that after all six messages are
displayed, flag 230 is set. This prevents showing these messages again
and the tests starting with label EQ7
will execute.
While Franklin's experiment is performed, and electric charges collect
on various objects (the tree, the ground, and the water), the cloud is
allowed to move only within a rectangular area set using block
command, and the area is positioned in such a way that the cloud
cannot move vertically. The cloud location is checked using
center_position
command (the result is true if the object base's
center is within the block area). When the test conditions are true
input line 23 shows a message, for example, ``The kite is in the cloud''
indicating to the user that the condition set by center_position
command is true.
When we illustrate how electric charges collect on an object under a passing cloud, rectangular area set in center_position test is shrunk to a vertical line segment. This is why, as the cloud moves, the condition tested by this command is true only when the object base centre is on the line. If the area were wider all actions selected by the condition would execute on each interpreter cycle while the base centre of Ego (the cloud) is in the area. This would cause flickering of, for example, the message in the line 23 ``The cloud is over the ground''. It would flicker as it is displayed on each iteration of the cycle. SOUND 15 would also be restarted on each cycle.
This is why when you display messages using display command, keep in
mind that the command will display the message on each cycle. This
will cause the message to flicker unless you guard against multiple
execution with extra tests. If you use print command, the text in the
message window would not flicker, of course, but any animation stops
until the window is closed by pressing ENTER
key.
Execution of logic 11 finishes by asking the student a question. A
correct answer moves us to a new room with new_room 13
command.
Room 13 illustrates ionisation process in the cloud according to the
program in logic 13 (see the source code in log13.asm
). This logic is
mostly built just like those we have already discussed, but the
following may be interesting to you:
We have already discussed the necessity of balancing the artist's and programmer's work: the more effort the artist puts in PICTURE and VIEW resources, the easier it may be to the programmer to write the program animating these resources. If the programmer can draw, or the artist can program, this is the best case: they have no turf to divide. If everybody can do only his own part of work, probably it is the designer's responsibility to choose the best method of implementing the design and make the artist's and the programmer's work more efficient.
This is enough to conclude the topic of dividing responsibilities in the team. Above, when we discussed the design, I gave an example of animating ionisation process and mentioned that we chose to implement it as a single animation rather than programmatically moving pictures of separate objects. This is more economical. But if you simply played this animation after some introductory text, without accompanying it with sound, it would be too dull. But how can you create a sound track for a fairly large animation when you cannot find a SOUND resource to accompany it? I did the following. Picked short sounds best, in my opinion, representing various stages of the animation (an electron hitting an atom, photon emission, electron emission, and capture of the electron by another atom). After that I had to programmatically synchronise each stage with its sound. Among the interpreter's commands there is a current_cel command which comes in handy. This command stores the number of the cel currently displayed on the screen in a variable of your choice. Comparing the number with the number of animation cel at which the sound should begin playing, I solved the problem. Just remember that the sounds should be sufficiently short for each of them to play to completion before another sound is due playing -- because the flag indicating the end of playback is not checked in this logic. This is why computers displaying animations faster than the one this program was created on may occasionally ``swallow'' sounds.
This logic uses three animations illustrating a collision of an
electron with an atom at a certain speed, then another collision at
double that speed leading to electron emission from the atom (and
turns the latter into a positive ion), and capturing of the electron
by another atom which turns into a negative ion. The observer can view
each process in a slow motion which is accomplished by increasing the
time delay between frames (in interpreter cycles) using cycle_time
command. To illustrate all three stages of ionisation the three parts
of the animation play repeatedly, changing each other without any
accompanying text. After each playback of all three animations, a
short delay is implemented as cycles, during which two variables, 240
and 241, are incremented. This is because the interpreter allows no
other means of delaying for a specified period of time. How the delay
is implemented you can understand yourself, just keep in mind that
control does not return to the interpreter so any animation stops (for
example, the stars on the background stop blinking).
The display of the three ionisation stages can be interrupted at any
moment by pressing the ENTER
key, which moves us into a new room with
a new_room 15
command. The new room illustrates how a lightning leader
is formed and the measures of lightning protection.
Events taking place in room 15 are coded in logic 15 (see the source in log15.asm. This logic follows the programming approach we have already discussed, so I shall explain just a few details.
This is how the destruction of an unprotected house by a lightning strike was implemented. Two VIEW resources of the same size were created, showing a house being destroyed and a fire. The picture of an undamaged house initially displayed is cel 0 or view 16. Then, when the centre of the base of the cloud controlled from the keyboard is right over the house, animation of the lightning hitting from the cloud is started. The cloud motion is stopped by the program at this moment. When the lightning touches the roof of the house (cel 8 of the lightning animation), cel 0 is changed to cel 1 showing a damaged roof. This illustrates how the instantaneous the damage is. After that, starting with the first cel, the animation associated with the house is played with the given delay between the cels. Event before that, the animation of flames was started, programmatically positioned ``behind'' the house and playing in a loop. While the house was still intact the flames were hidden behind it. This explains how the flame takes over the house as the house animation makes larger and larger area of its view transparent.
Note another issue related to illustrating lightning protection, when
the cloud passes over a lightning pole and discharges through it. This
moment is chosen by the result of center_position
test command for the
cloud (object 0), using a rectangular line small enough to be a
vertical line fragment. This is why the actions associated with the
lightning strike are executed only once. That vertical line passes
through the tip of the lightning pole. Because the moment for the
lightning strike is chosen when the cloud base centre crosses this
line, we have to calculate the coordinates of the point where the
lightning should be positioned for its end to hit the tip of the
lightning pole. This is accomplished by setting an offset between the
base points of the cloud and the lightning (v66
). Besides, the
direction of the cloud motion (left or right) relative to the
lightning pole is important. This direction is retrieved using get_dir
command and is later used to programmatically move the cloud one pixel
each step after the strike (during the strike the cloud is stopped).
If we didn't do that after the strike, the condition would be true
again on the next interpreter cycle and the lightning would strike
again and again.
Note that in this logic two different VIEW resources are used as animations of the lightning: first view 7, the lightning striking the house, then view 34, the lightning striking the lightning pole. Of the five animation loops of the latter view only loop 2 is is used which shows the vertical lightning.
In this logic the program is completed by a summary and congratulations.
Now a few words about the principle of distributing resources across
the volumes I used. File vol.0
holds the LOGIC resources, vol.1
the PICTURE resources, view.2
the VIEW resources and vol.3
the
SOUND resources.
Finally, note that as you study the sources you will probably notice many pieces that could be coded more efficiently. In particular, many logics could be made smaller by introducing subroutines (especially logic 13 with lots of repeated chunks), etc. I intentionally leave the source as it is because I think this form is better suited to studying the AGI language.
Remember that:
new_room
and new_room_v
.number_of_loops
command. Use
this command when you set the object cycle number using
set_loop_v
.log.dbg
is loaded in
memory, the debugger can be turned on using the Scroll Lock
key. It
can help you trace the program logic.call
command is loaded temporarily and
removed after the call. If it is called often, this wastes time.
Load often called logics explicitly using load_logic
command.ts.exe
from
Norton Utilities for that. (This is what is actually called
``grepping the source'' and grep(1)
is tool to use. --VB)Remember that:
When you use PM:
add_to_pic
command. But
be careful about that. (Why? --VB)W
key.
You have to know everything! Your design is the key to success! Read carefully the manual and this text. Play Sierra games to understand what the interpreter can do. You are the key person!
This is mostly about quirks I ran into working with them on IBM XT. This applies only to the utility versions I have.
Incorrectly includes the debugger table in the volumes. In the result the debugger does not work.
Does not link volumes. I linked everything using DUU.
Extracting lists of objects from Sierra games, you have to pick the translation key for each particular game for the list to restore correctly. Experiment.
No problems found.
How to insert backward references using the decimal offset I explained in the package's manual. There is nothing else to say.
This is about all I wanted to say. Now, if you haven't done so yet, try running Thunderstorm after building it using bld.bat batch file. The program asks five simple questions, the correct sequence of answers is 2, 3, 2, 1, 3.
Of course, the Thunderstorm does not use all of the interpreter's capabilities. My goal was to explain the most important aspects of its operation. I hope my story will help you start creating your own programs as quickly as possible. I wish you to not repeat my mistakes (unlikely though it is) and I apologise for being subjective on occasions and, possibly, repeating myself.
On behalf of our group (L.E. Kalihman, D.V. Spasibenko and myself) I wish you success in creating your own programs. Everything depends on your will and skills!
By receiving Thunderstorm educational program you acknowledge my exclusive rights to all source code of its logics (except logics 10 and 112). Any modification of the source code with the purpose other than study, as well as distribution of such source, or the text of accompanying documents without purchasing AGDS package is considered a copyright infringement. Thunderstorm Copyright (C) Intep Corp, Cheboksary City. All rights reserved. Thunderstorm can be purchased with the source code from Intep Corp, Cheboksary City, or from Elias Corp, Moscow, as a demo code of AGDS package.
(Hmm, and what about logics 10 and 112 based on KQ3 code, as well as AGI itself, reverse-engineered and resold? What about Sierra's rights? Funny how people talk about copyright and try to sell something built on someone else's reverse-engineered program. Apparently the whole passage cannot be taken too close to heart. --VB)
August 1991 Cheboksary City
Besides AGDS (which was the first non-Sierra AGI interpreter), a number of alternative interpreters have been brought to light in the last few years. None of them is fully functional at the time of this writing, although good progress is being made to have full compatibility with the original Sierra interpreter. Some of the new interpreters are portable and open the possibility of playing the Sierra AGI classics natively in platforms where the original AGI interpreter has never been ported, such as UNIX and BeOS. The new possibilities include four voice PCjr or sixteen-voice, digitally sampled IIgs sound replaying in the IBM-PC.
MEKA stands for ``Möller, Ewing and Kelly Adventure'' and has been developed by AGI hackers Joakim Möller, Lance Ewing and Peter Kelly using DJGPP and the Allegro library to run under MS-DOS. It has support for both v2 and v3 games, and some routines from MEKA have been reused in Sarien, specifcally the LZW decompression routines and modified picture decoding routines.
JAGI (1998) is a free AGI interpreter for Linux written in C by Jens Christian Restemeier. JAGI uses GGI for graphics output and OSS for sound. The latest available version is 0.1, distributed under the GNU GPL. JAGI supports only v2 games.
Formerly known as Yggdrasil, Sarien (1999) is a portable AGI interpreter developed by Stuart George and distributed under the GNU GPL. It runs in the MS-DOS and Linux platforms and is capable of playing v2 and v3 AGI and AGDS games. (If you like acronyms, Sarien can be interpreted as a ``Sierra AGI Resource Interpreter ENgine''.)
Developed by XoXus, the Linux AGI Interpreter, or LAGII (1999), runs in the Linux console using SVGAlib. Currently it supports v2 games. Fully expanded, LAGII stands for ``Linux Adventure Game Interpreter Interpreter''. It is distributed under the GNU GPL.