How does plotscripting work?
Or, The Magic Behind the Most Convoluted System in Existance!
The Stack[edit]
The main driving force behind plotscripting is this magical entitity known as the stack. It does not like to be refered to by name, instead prefering to do its work behind the scenes.
The stack is like a stack of dishes at a chinese-buffet restaraunt, where they're in that spring-loaded container, and you can only take the top plate, not any plate in the middle. The stack has two operations that can happen to it: Push and Pop. Pushing something onto the stack is like putting a plate on the stack of plates. Poping it is like taking the top plate off and using it (or, throwing it in the garbage).
So, what does this have to do with plotscripting? Well, when a script is triggered, it's pushed onto the stack, where it can run. Only the top-most script can run at any time. When it completes, it's popped off the stack, so whatever's beneath it can do its stuff (or, if there's nothing, it does nothing).
The Script[edit]
So, we've looked at how scripts are stored in memory, and how the engine can find them. But, how do they actually run?
Well, the interpreter starts at the beginning, and works its way from there. Different types of commands have different numbers, and the interpreter uses those numbers to figure out what type of data follows the number. However, that's beyond the scope of this FAQ ;)
Anyway, the interpreter runs until something puts it into a wait state. The wait state tells the interpreter that it's done, and should pass control to the rest of the engine. The only thing that can put the interpreter into a wait state is a waiting command, such as wait, wait for hero or wait for camera. This means that the following code will freeze the engine, as the interpreter never goes into a waiting state:
while(true) do ( )
Also note that this code will not freeze the engine, as the interpreter will almost continuously be put into a waiting state:
while(true) do (wait(1))
In fact, if that script it running, it's mostly harmless, except for taking up a bit of stack and heap space. Of course, the script its in will never end, so you should only use an infinite loop if you really know what you're doing.
Now, let's say you have a script like this:
show text box(1) wait for text box wait(1000) show text box(2) wait for text box
As you can see, between the two text boxes, you have quite a bit of time to do whatever you want: Talk to NPCs, fight battles (which, as far as the script can tell, only take one tick), do whatever. Some of these things might even trigger another script! In this case, the script is loaded into the heap, and pushed onto the stack like anything else.
From the interpreter's point of view, all that happens is that another script is called. It might be a function in a script, or it might be from a seprate event. It doesn't know, and it doesn't care. It just runs that script until it finishes, at which point it's popped from the stack, and the previous script keeps running, right where it left off. Note that if it had 500 ticks left to wait when the other script happened, it will still have 500 ticks to wait, regardless of how many passed in the other script.
The End[edit]
Well, that's all for now. If you want to know more about how scripts themselves are compiled, check out the (very much incomplete) HSX docs. They'll tell you all about how they're compiled, and how the interpreter runs it.