Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
67 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Contents
 Script Anatomy
 Making IInstruction
 do stuff

 The Script Editor

 Printable version
 Discuss this article
 in the forums


Making IInstruction do stuff

As I said before, IInstruction doesn't do stuff. So why this section?

For an example, I want the most basic instruction supported: End Script. Upon executing this command, the script will cease execution. End Script needs no data.

I will therefore make a class called CEndScript. It will implement IInstruction. Therefore, you will have to implement all the functionality / state of IInstruction in CEndScript. PrevInstruction, NextInstruction, etc will always remain references to IInstruction classes, because this is how we keep all the instructions compatable with each other.

Now, when loading the scripts, how do we differentiate an End Script from another command? Well, how does a CPU recgonise what to do? We use an OpCode. I find that if you use a 32-bit opcode, you'll give your scripting language a maximum library of 4.2 billion different types of instructions. I tend to use the OpCode 0 to mean a read error. If ever the script parser reads the opcode 0 from a file, abort, and crash Windows while you're at it! So I'll use the OpCode = 1 to signify an End Script.

CScript_Open( filename )
  Declare OpCode as 32bit Integer
  Declare TempIns as IInstruction
  Open the file.
  While Not Eof
    Read OpCode from file
    '// When OpCode = 1, create CEndScript
    Set Temp = New (whichever class signified by the opcode)
    Temp.Load( file_handle )
    Append Temp to be the last element in the linked list.
  EndWhile
  Close file
End Method

This is *very* simplified. No support for child objects.

There are ways to make this simple to code, but I'll let you work this out. The important thing to note here is that we're loading the *whole* script in at once - it's not streamed from the disk as it's executing. You might wish to stream it, but that's up to you to implement.

Child Instructions

First, make sure you understand the previous section. This one extends on it.

In the last section I introduced a linear script format, in the form of a linked list. Now, you're more than likely going to have to make an If type instruction. I have Simple If, Compound If, and a Switch (Select Case in VB) which implement all I'll ever want when it comes to decisions. You might decide to be clever and inplement proper looping as well. I'm implementing a Pre-test, Post-test and a For loop in my language. How can we implement this easilly in our language though?

Let me write some Visual Basic code here:

Sub DoIt()
  Debug.Print "Doing it!"
  If Game.Flags(456) < 344 Then
    Debug.Print "We had best make the flag up to 344!"
    For Counter = Game.Flags(456) To 344
        Game.Flags(456) = Game.Flags(456) + 1
    Next Counter
  Else
    Debug.Print "All is well."
  End If
End Sub

I'll not guarantee that'll work, since I just wrote it, but you'll see several layers of indenting. If you know anything about programming, you'll be indenting your code - it's much easiser to read. And it also makes it much easier to work out a way to implement something similar in our language. Could we not say that the incrementation of Game.Flags(456) is a child instruction of the For loop? And what about that If statement. I'd like to say that it's got 2 children - the True and the False case. A Switch could have any number of children instructions.

Let's add 3 extra members to IInstruction:

  • NumChildren As 32bit Integer (The number of child branches)
  • ParentInstruction As IInstruction (The parent instruction of this instruction)
  • Child() As IInstruction (An array of child instructions - NumChildren tells how many there are.)

First, let's get this Child() bit straight. If the above source code were my language, then the If instruction would have 2 children. One would be Debug.Print "We had....." and the other would be Debug.Print "All...". What about the For instruction you say? Well, it's the NextInstruction of Child(0).. In more of a programming sense: CIf.Child(0).NextInstruction.

About the ParentInstruction: I find it beneficial to be able to move around my data structures easilly. PrevInstruction takes you to the previously executed instruction (which is mainly for the editor's use), and ParentInstruction takes you back up the tree. It gives a really fast way to escape from the "nesting" of our instructions. It also allows for a instruction called CToStartOfLoop - this is like a continue in c? Trust me, it's very little extra effort, and it makes a lot easier later on. And the ParentInstruction will be null or Nothing (in VB) if the instruction is in the highest level of the script (i.e. anything in line with CScript.FirstInstruction), but you already knew that.

I'll leave it up to you to implement the decision and looping specifics. My language doesn't have ending instructions for these types of instructions (i.e. no End If, End Switch, End While, Next Counter, etc). Whenever an instruction's NextInstruction is Nothing, it's eary to use the ParentInstruction to go back to the start of the loop, if necessary (I also have a ParentsChild integer in IInstruction to tell the instruction which child it belongs in.. It saves fumbling around).

How can this be loaded from a file? Recursion is always an option for this. VB people: beware of stack overflows! Another part of my game engine *used to* use recursion, and after loading about 3,500 maps, it'd die. I'm told on good authority that this is less of a problem in C++, but I dare say if you tried to load a script with 60,000 instructions in it, the stack would overflow. With that in consideration, you could run the risk of having some smart-arse trying to make your program crash that way, or you could use a single method to load it using a few loops, a user-implemented stack, temporary object references, and a few other tricks. I use that way, but not because I'm concerned about stack overflows (seriously, who in their right mind would want to have to add 4,000 instructions using RPG Studio??), but because I find the code runs faster than a similar recursive algorithm. This might not be the case in C++. Recursion is a lot easier than the alternative though. And chances are, there are many other ways of doing this. I'm not teaching you how to program, so you can work this one out yourself :-)

Instruction data / parameters

Ok, you've implemented CMessageBox. It will show a message box in-game, and it will display some text on it. Where does the text come from?

CMessageBox has a variable implemented in it called Msg. It's a String. In the Load method of CMessageBox, all you have to do is load the string from the file handle.

Read the linked source code to Script_Teleport.txt. You should see how I implemented all this stuff.. It's a bit messy, but I'm still developing it. I might take this oppotunity to elaborate on a possible file format:

  1. Load OpCode (create appropriate object and execute the load method)
  2. Load each data item in sequence (I like to store the length of a string before storing the string. It tells me when I'm loading how long the string buffer should be)
  3. For instructions with a variable child count, load the child count. (Switches come to mind here)
  4. For each child instruction, load a boolean to see if that child is null / Empty. Remember, just because an Instruction can have a child instruction, it doesn't mean there has to be one.
  5. Load each child instruction (recursion is handy here)
  6. Load a boolean to see if there is a NextInstruction (False means Null, and that is the end of the branch in the tree)

Ok, that was simple :-)



Next : The Script Editor