By extended events, I mean anything that takes place over the span of multiple iterations of the main loop. This is a main difference between DOS and Windows programming. In DOS games, you probably wrote your fade so that the whole thing took place in one iteration of the main loop. That is, you probably called a function called FadeOut() or something similar, which displayed 30 or so frames before returning. You know by now that Windows doesn't like you doing things like that, so you need to write effects that know how to update themselves each frame. Figuring out exactly how to do this can be a point of some confusion to new Windows programmers, so once again, I'm going to explain how I've done it in my game, to use it as an example. Hopefully it will give you some ideas as to how to pull off the same kinds of things.
First let's figure out exactly what's going to need to run over a number of frames. The first and most obvious category is graphical effects. This includes fades, animations, particle system displays, etc. Up next is text effects, which can include fading text onto or off of the screen, or simply displaying text over a number of frames. Third, we have things like waiting for a key before going any further. It's so simple you may not have thought of it, but you can't just sit in an empty loop until the user presses a key! You have to keep going! Finally, perhaps most importantly, are scripts. Most scripts will not execute in a single frame. You don't want them to, because they may be controlling things that show up on the screen. Terran's script for the opening scene, for instance, takes about six and a half minutes to run. At thirty frames per second, that's about 11,700 frames.
All right, then... so how do we get a script to run over multiple frames? It's actually quite simple. When I wrote up the set of commands supported by my scripting engine, I separated them into two groups: terminating and non-terminating. The former contains commands which, once executed, cause the script to stop running until the next frame. The latter contains commands which can be executed all in the same frame; that is, they don't terminate the script for that frame. Thus, in any given iteration of the main loop, a script may execute any number of non-terminating commands, but only one terminating command.
Before going any further, let me show you the simple data structures I use to hold my scripts:
int nScripts[MAX_LINES][MAX_PARAMETERS + 1][MAX_SCRIPTS]; int nNextLine[MAX_SCRIPTS];
There are several constants used here that are #defined elsewhere in the program. MAX_LINES is the maximum number of lines that a script may contain. MAX_PARAMETERS is the maximum number of parameters that a script command can take. I've added one to it in the array declaration because there needs to be a column to hold the command itself, in addition to its parameters. MAX_SCRIPTS is the maximum number of scripts that can be executing simultaneously. There are implementations that use less memory than this, such as allocating memory as more scripts are needed at once, and using a variable-size memory location for each command, but it's really not necessary since this represents only a small chunk of memory, unless you want to run fifty scripts at a time or something insane like that. In truth, you'll probably never need more than three. I have MAX_SCRIPTS set to five just in case.
The nNextLine array holds the index of the next line to be executed for each script. When a script is loaded, its corresponding entry in nNextLine is set to 0 to indicate the first line should be executed. If an entry in nNextLine is -1, this means no script is loaded in that slot. The entries in nNextLine are used to easily implement if statements, loops, and all sorts of other goodies.
Whenever the scripting engine is called to run anything that's currently loaded, it checks the script slots one at a time. If a script is loaded in that slot, and no effects are linked to it (more on this in a sec), then it executes commands from that script until a terminating command is encountered. That command is executed, and then the engine leaves the script and goes on to the next one. Pretty simple, hey?
So now you're probably wondering: if Terran's opening scene script takes 11,700 frames to execute, does that mean that there are 11,700 terminating commands in it!? Of course not! If you write a script that ridiculously long, I expect no less than a cure for cancer to come out of it. The idea behind the long execution time is that the script can be suspended. For example, take a look at this pseudo-script-code:
DoSomeAnimation ShowText 12, 133
Suppose this corresponds to "Do some random animation, then display twelve lines of text, starting with line #133." Well, the animation is going to take time to finish! If you want to display the text after the animation is complete, you can't just execute the animation in one frame, and display the text in the next. This is where the idea of effects comes into play.