QB CULT MAGAZINE
Issue #7 - December 2000

Script Engines

By 19day <19day@zext.net>

[Editor's note: 19day wanted me to mention that this is an old tutorial. So it's kinda rusty. Nevertheless, it's still a good article.]

19day here, and this is a tutorial that will teach you all about scripting for your RPG.

First things first though, scripting is useful not only for RPG's, but for other types of games as well, I can't think of any off the top of my head, but you'll see.

Anyway, here's the rundown of what scripts are and what they can do: Scripts are like mini-languages that your game engine can interpret and run. Now, in an RPG this is especially useful. Lets say your RPG has lots of NPC's (non player characters) and you want them to say things, but you don't want to flood your source with tons of IF playposx = blah AND playposy = bleh then npc$ = "Hey, I'm Diddddddly" If you had tons of guys in your game, this is a horrible way to do it. Now, if you had a scriping language in your game, you just call apon an external file it sure makes things simpler.

Now, what is the actual language like. Well, it's all up to you. My language, called TLK (talk) is a ascii text file format that has each command on it's own line, and the engine parses each bit of each line. Nekrophidius' Wrath of Sona's EXT (Extended Text) has each command on a line but each parameter is on other lines just after it. I don't happen to like that format, so that's why mine is the way it is.

Another way to have your scripts is to make them like the above, but then make a compiler to convert the scripts to a binary format. This is almost like assembly, where each command is reduced to a byte, and the parameters are usually bytes. This has one one advantage that I can think of, and that's to prevent people from altering your scripts, but to me, since this is all in QB, this is not a concern. so you might as well start with the text based scripts, and then later on you can make a compiler for them.

Basically, what you need for your scripts, first, is a method of the syntax, which you'll want to make constant. You need some way to define a command in the file, I use /, like in IRC, when you don't have any meta commands or comments, there's really no point, but it just makes it look neater. (Nekro uses HTML like tags, < and > surround the command. This has an advantage. He can make his commands as long as he wants, because the names starts at the < and ends at the >, where as mine, I have only 3 characters, which only presents a small problem, that of meaning fulnames, but I think 3 letters is enough, and the number of commands are high, with over 50 characters per position, 50*50*50 is just a lot. But of course not all of the combos will have meaningful names, so you just have to improvise) Now, you need to have some commands to start with, and the command indicator, which in this tutorial, I will make $, so whenever a line begins with a$ that means that it is a command to interpret. Now, we need some simple commands:

$say
prints text on the screen
$col
changes color of text
$end
ends the script

Now, two of these commands need parameters, so $say will have a parameter like text$, so an example of the command will be:

$say Hello there

And $col needs a color parameter, now you would think that if you wanted to change the text color to white (15), you would do this:

$col 15

But then you would be wrong, because remember, this is text, and since the numbers can go as high as three digits, the interpreter would crash (more on that later) so we would need to do this:

$col 015

And $end takes no parameters.

The interpreter is a different matter, now, I'm just going to give you the code, which is pretty simple, but I'll tell you what it all means after.

SUB talk (talkfile$)
  OPEN talkfile$ for input as #1
  talklines% = 0do
  LINE INPUT crap$
  talklines% = talklines% + 1
  LOOP UNTIL EOF(1)close #1
  'the above just figures out the number of lines in the file
  OPEN talkfile$ for input as #1
  FOR i% = 1 TO talklines%
    LINE INPUT hold$ 'take in a whole command
    IF MID$(hold$, 1, 1) = "$" THEN 'make sure it is a command      
      IF MID$(hold$, 1, 4) = "$say" THEN
        PRINT MID$(hold$, 6)
      END IF
      IF MID$(hold$, 1, 4) =  "$col" THEN
        COLOR VAL(MID$(hold$, 6, 3))
      END IF
      IF MID$(hold$, 1, 4) = "$end" THEN
        GOTO quittalk
      END IF
    END IF
  NEXT i%
  quittalk:
END SUB

Pretty simple, eh? Well, it's good to get the basics out of the way. What it does is figure out the number of lines in the file, and then it goes and reads in each line and depending on what the first 4 letters are (commands start with a $ anyway) it decides if there is anything else to parse and then does what it's supposed to do. Remember that undefined length things, like text for the $say command, is a difficult matter, because we can't know how long the parameter is.

Now, since it's on it's own, we can just do MID$(hold$, 6) because without the last parameter in MID$ it just takes it until the end of the line. But if we have a few paraters that are unknown, like speaker and text, we need to make it one parameter with some sort of divider. In my /YRN command takes 1 parameter, which is the text for the yes and the text for the no (just in case it is a 2 choice question, but not a yes or no one) but since the lengths of the two are unknown, I just say whatever-whatever, so in the command, if it were a yes or no question, I would say.../YRN Yes-No and the if statement in the interpreter would then parse out the whole big parameter, by searching for the '-' and then having the YES text from the '-' to the beginning of the parameter and the NO text from the '-' to the end of the parameter.

Events:

The next thing you're going to have to learn is event handling, it's the sort of thing that allows NPC's to say "Go kill that evil wolf in the forest" and then, when you do kill it, they say "Thank you so much" Basically, events are like triggers that make things happen, like when you talk to a guy, a door opens and stuff like that. this is another thing that would make hardcoding it a big mess, so, what we need is an event holder, which, in an ideal world, would be an array of the boolean type, but since there is no boolean in QB, and we don't want to have to screw around with bits, we'll use STRING * 1 to make bytes. This also has it's use, becuase instead of using lots of events to record repeated NPC actions, you can just change the number in one event, so basically you can have 1 even per each unique NPC.So here's are event array:

DIM SHARED events(100) AS STRING * 1  '101 events

And now, we can make commands to set and do things with this events, here are the new commands:

$set ### ###
$ife ### ### $cmd

So, $set takes 2, 3 digit numbers, the first one is the event holder number, and the second is the new value that that event holder will hold. Example:

$set 001 100 'sets holder 1 to 100

And $ife takes 3 parameters, the event holder to check, the value tocheck for and the command to do if it's true. Example:

$ife 001 100 /say it's right

The IF statements in the interpreter would be this...

    IF MID$(hold$, 1, 4) = "$set" THEN
      events(VAL(MID$(hold$, 6, 3))) = CHR$(VAL(MID$(hold$, 10, 3)))
    END IF   
    IF MID$(hold$, 1, 4) = "$ife" THEN
      IF events(VAL(MID$(hold$, 6, 3))) = CHR$(VAL(MID$(hold$, 10, 3))) THEN
        hold$ = MID$(hold$, 14)
        GOTO top 'top would be a line label right after the line inputline
      END IF
    END IF

IF Structures

This is a interesting thing to do, I'm not going to give you real code for a proper if structure, becuase I haven't actually made my script engine be able to do it, I use lots of GOTO's and SUB's in my scripts :) Basically, you'll just a need to keep a stack to record the beginning and endings of each and just skip the bits that you should.

My newer way, consists of using /+### commands one right after the other, but they only do it like AND, let's say I wanted to have my NPC say "You're a very mean man!" if event 1 was 1, 2 was 10 and 3 was 0. Well, one way I could do it is this...

/+001 01 /+002 10 /003 00 /say You're a very mean man!

But if I wanted it do do more than just the one command, I could use a subroutine...

/+001 01 /+002 10 /+003 00 /cal npc1.. other commands.
/sub npc1
/col 050
/say You're a very mean man!
/ret

There is a flaw with my SUB methods though, and that is that I have no stack, that means I can't call a sub within a sub, now, I could rig that up easily, but I just haven't come across a situation that I need that for, so I just haven't. Another problem with my IF structures, is that I wanted something to be this AND (this OR this), I would need 2 commands... (lets say, in Qb,it would be EVENT 1 = 1 AND (EVENT 2 = 2 OR EVENT 2 = 3)

/+001 01 /+002 02 /do whatever
/+001 01 /+002 03 /do whatever

Really doesn't mean much, but it just makes the files a little bigger and harder to understand. So that's it really.

Legend = * not yet implemented
         # a number parameter, number of # signs indicates number of digits.
         $ a string

My set: (If you need idea's, here's my complete set of tags and commands)

/SAY text$          'prints text
/COL #              'new color number
/YRN ans1-ans2      'Asks for an answer, left returns variable SEL = 1, right
                    ' to 0
/SLP                'wait for a key
/LOC x y            'Locate for new printing position
/ON0 ##             'if sel = 0 then goto line number ##
/ON1 ##             'if sel = 1 then goto line number ##
/3D0                'shuts 3d text off
/3D1                'turns 3d text on
/NEW                'loads a new TLK file and runs it
/END                'end the talk
/+### ## /cmd       'if event ### equal to ## then do /cmd (see farbelow)
/(### ### ## ##     'if events ### through ### are = to ## goto line ##
/=### ##            'event ## is now equal to ## (in that ## order)
                    ' ++ or -- add or subtract from current value
/giv ##             'Give item number ## to player *
/L##                'Line lable ##, for use with goto's
/IMG file$  *       'instead of talk, show image. (would have to be 1sttag)
/TIL ### ### ###    'put a tile at x coord ###, y coord ###, use tilenum ###
/IFC ### ### ##     'if player coords at x ###, y ### then goto linenumber
                    ' ##
/OBJ ### ### ##     'if the object currently talking is at ### ### thengoto
                    ' ##
/SND file$          'plays the Voc file$
/WLK string$        'string$ is a series of U, D, L and R's that movethe dot.
/JMP ##             'immediately jump to line ##
/MAP ### ### ###    'actually change the tile in ### ### position totile ###
                    ' if ### is pmx and ### is pmy then it will useplayers XY
                    ' position, can also be in p+# or p-# to add orsubtract
                    ' a number from player position to find pos
/IFT ### ### ### ## 'if the tile at ### ### is = ### jump to line ##
                    ' if ### is pmx and ### is pmy then it will useplayers XY
                    ' position, can also be in p+# or p-# to add orsubtract
                    ' a number from player position to find pos
/BRK             *  'A tag that tells the talk reader whether or not to
                    ' allow the player to escape from the dialogue.
/DEL $ ######       'A delay tag, if $ is V, then it waits for the
                    ' vertical retrace and it's end ##### number oftimes,
                    ' if it's S then the program SLEEP's for #####seconds,
                    ' and if its D then the program does a FOR NEXT loop
                    ' from 1 to ##### (##### doesn't need 0's like00001)
/FD+ ## ## ## ### ### ### ###  'a really complex but useful command, ascreen
                               'fader. The ## ## ## are the R G and Bvalues
                               'to fade to, the ### is the number offrames,
                               'almost a delay, while the ### is theamount
                               'for a partial fade, which is alwaysdependant
                               'on the first ###, where half of thefirst ###
                               'is a 50% fade, and the ### and the last###
                               'is the low and high color numbers tofade
                               'from and to. Usually 000 and 255
/FD- ## ## ## ### ### ### ###  'Fades back from /FD+, like a fade infrom a
                               'fade out
/MLD ### ### map$              'loads a new map when the TLK isfinished.
/SUB name$                     'at the bottom of the script, one may use
                               ' subroutines that act like GOSUB's, and
                               ' the /SUB command is the declaration,start.
/CAL name$                     'Calls the subroutine from the mainscript.
/RET                           'Marks the end of a SUB, and retunrsscript
                               ' execution to the line from which it was
                               ' called.
/WRP ### ### ### ### # *       'Warps the players from coords ###,### to
                               ' coords ###,### usign effect number #

Well, I hope some of this helps you.

19day