9.3 The Making of Thunderstorm Educational Program
"... how did it come to pass?"
(from a song)
A. V. Horev
Chuvashia State University
Retrived from the Internet Archive
Translated from Russian and HTMLized by Vassili Bykov.
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.
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.
How We Started
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.
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.
What Do You Have to Know
about the Interpreter
to Write a Simple Educational Program?
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:
- The cyclic nature of the interpreter operation completely defines the style of AGI programming. The style is rather unusual and requires understanding and getting used to
it. We shall discuss, using the source of of Thunderstorm as an example, how to write programs executing in that cyclic manner.
- A common set of variables, flags, and strings for all the subroutines loaded in the interpreter memory, which means any variable or flag is accessible from any part of the
program (this includes the reserved variables and flags).
- Data elements of any type (variable, flag, resource) are identified with a unique ID number (0..255, string variable - 0..11). At first this seems inconvenient, but later
you get used to it.
- Command name determines the data type it operates on, the nature of its operation, and the result. For example, consider assignment commands:
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.
- Small internal interpreter memory occasionally overflows. This is annoying because there may be lots of free RAM at the same time.
- There are powerful commands
new_room
and new_room_v
used to completely change the program behaviour.
- There are many test commands which allow not only to check the state of variables and flags, but also the position of objects one relative to another, or relative to some
special lines and areas drawn on the background (for example, control barriers or water surface). The result of tests determines the progress of the game according to the design.
- The ability to fairly easily animate objects on the screen. Note that all the images programmatically associated with objects the interpreter or the user can control, have
to be first created using the VIM utility, as well as the background created using PM utility. That is, creating an animation is a two-stage process: drawing a frame-by-frame
animated sequence of the object (a human figure walking but not moving) and programmatic movement of the object for which the animation is played (this creates a walking figure).
We shall later discuss the interaction between the designer, the artist and the programmer.
- The ability to use a built-in debugger, invoked using Scroll Lock if the debugger table log.dbg is loaded into the interpreter memory.
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.
How I Coded 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.
I Had the Design
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:
- All rooms and conditions when the rooms are changed, i.e. when the background art and the controlling programs change.
- All actions taking place in each room and conditions determining when each particular action takes place.
- All objects in the room, and whether an object is a part of the scenery (PICTURE resource) or a separate thing (VIEW resource) controllable from the program; this is important
to the artist.
- Position, at least approximate, of all objects created as VIEW resources; this is important to the programmer.
- All items the player can get as the result of certain actions, and those actions themselves.
- Approximate priorities of the objects, both parts of scenery and VIEW resources, which creates an illusion of depth; this is important to the artist and the programmer.
- Interactions between the objects: for example, whether the object treats other objects as obstacles, whether it can cross the horizon or leave a certain area, etc.; this
is important to the programmer.
- Conditions when the on-screen image of an object may change. For example, when a walking person crosses a brook, the images should change to the image knee-deep in water,
etc.
- All messages and dialogue that occur in each room under certain conditions, and those conditions.
- Game vocabulary including the words the game understand.
- Sentences used to control the objects and reactions to them, etc.
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 #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:
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:
- A test for an error (using v17) and a call to the error handler.
- A call to the initialization logic.
- Actions common to all logics.
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 area--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 LOGIC resources, vol.1 - PICTURE resources, view.2 - VIEW resources and vol.3 - 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:
- The interpreter works in a cycle described in The Interpreter Work Cycle.
- There are reserved variables and flags.
- Logic 0 is loaded in memory at all times and controls your program in general, while all other logics control specific rooms you get into as the game unfolds.
- Your program is executed by the interpreter in a loop, so do not introduce any cycles that would move objects, change their views, or play sounds yourself. The interpreter
itself will any such action and will notify you of completion by setting a flag associated with it. The interpreter checks the conditions you specify using test commands and
executes only the actions with true conditions; without the tests, all the commands would execute on each cycle. This might slow down the program or break it at all.
In general, once you have started an action, let it run to completion by returning control to the interpreter until the flag signalling completion is set - unless you after
after some special effects.
- Remember the effect of
new_room
and new_room_v
.
- Interpreter's memory is limited! Do not load many resources simultaneously, especially if they are not used at the same time. Load them as they are needed and keep an eye
(during the debugging) on the amount of free memory, displaying variable 8 in line 23 or 24 using
display
. If free memory is low, if possible, manipulate resources
loading them and unloading, or change the program structure to introduce more rooms. Make (together with the artist) VIEW resources smaller and use shorter sounds. Remember
that the interpreter loads a SOUND resource only once even if there are many commands to load it within the current room. Be careful when you discard resources. For example, discard_view
command discards all resources loaded after the one being discarded.
- Include the error handler in your program (logic 97 in the example) and do not forget to check if variable 17 is 0.
- You can accidentally associate an inexistent VIEW resource with an object as its picture. The interpreter will fail without signalling an error. You can avoid this situation
if you check the number of loops in the resource with
number_of_loops
command. Use this command when you set the object cycle number using set_loop_v
.
- Large VIEW resources not only take more memory but are also slower to display. This may even lead to their distortion (gaps in the picture). Keep cels as small as possible,
best of all - only large enough to fit the object.
- Don't overload the program with controlled objects, especially if they are large. The more objects there are, the slower they draw. The interpreter can control at most
15 objects at any time.
- About the built-in debugger. If its table log.dbg is loaded in memory, the debugger can be turned on using Scroll Lock key. It can help you trace the
program logic.
- The logic called with
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.
- All variables and flags are accessible from any logic.
- Before you change rooms clear all variables and flags you work with. This will save you some nasty surprises. You may select a fixed group of variables and flags and use
it as work variables everywhere. Cleanup procedure can then be a subroutine called before entering any new room.
- There is no tool in the package (maybe you can create such yourself using the interpreter as the foundation) to combine PICTURE and VIEW resources on the screen to see
them against each other and arrange objects in the best way. This should be done by trial and error, adjusting the program and interacting with the artist.
- The interpreter cannot process images (VIEW resources): it is impossible to resize them, rotate, etc. All that should be painted by the artist.
- The parser works only with Roman letters and is biased towards English, i.e. the input can include only English words unless you do something special, like replace regular
ASCII table with Cyrillic but even that will unlikely give the desired result as the interpreter ignores certain codes, so you will not be able to use them. [I should say this
is overly pessimistic as I saw myself KQ3 hacked by someone to be in Russian: it printed all messages in Russian and understood Russian commands! --VB]
- Messages in Russian are printed fine, except you should use a capital Roman A instead of a capital Cyrillic A. [The shape of the letter "A" is the same in Cyrillic but
it is represented with a different byte value. --VB]
- Distribute resources over volumes so that their size is about the same. This will speed up the access.
- No explanations are worth as much as you own experience and studying the source of Sierra games. You can extract their logics using VM utility and disassemble the source
using SM utility. You may have to search for text in many files, I recommend 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:
Using VIM:
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.
- DUU
- Incorrectly includes the debugger table in the volumes. In the result the debugger does not work.
- VM
- Does not link volumes. I linked everything using DUU.
- OM
- 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.
- WL and WM
- No problems found.
- SM
- 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
You can help keep The Sierra Help Pages and its affiliates alive by helping to defray some of the costs of hosting this site.
If it has been of help to you, please consider contributing to help keep it online.
Thank you.
Top
The Sierra Help Pages | Sierra Game Help
| Walkthroughs | Hints, Tips & Spoilers
| Utilities | Links
| SHP Forums | Search
© 2013 to present The Sierra Help Pages. All rights reserved. All Sierra games, artwork and music © Sierra.