There aren't a lot
of tutorials out there about scripting, and most of those just show the frame
of mind, rather than actual commands, when describing the process. I'm going
to try to be different. I'm going to explain how to make a scripting engine,
and how you can invoke scripts in your game BUILD-Engine style, storing the
locations and type of invokation using the NPC routines in your game. For
the actual scripting system, I'll give code examples, but some of it will
be in pseudocode.
Engine was written by Ken Silverman in a galaxy far, far away. It also happened
to be originally written in QB. But anyone who made build maps knows it for
it's scripting. This isn't actual bonified Build style scripting, because
few engines made in QB use sectors. However, BUILD fans will remember that
all those impressive scripts were created by a mere invisible sprite with
a hitag and lowtag, sitting and waiting to be triggered. This is how we're
doing things in this article. Being able to bind scripts to objects makes
all the work way easier for the programmer and the map creator, because
it's a simple mechanism to implement for the programmer, and a simple concept
to understand for a non-programmer doing map work.
Section 1: Commands from a file
For the longest time, I contemplated how to easily insert events into my
game engine (which was fairly mature at the time, so a complete rewrite was
out of the question). I didn't want to hard code them, since that would severely
drive up the size of my source and executable, and make my code unbearable
to work with, so I started brainstorming. My first idea was a seperate BASIC
file which would be included and called upon whenever there was scripting
to be done. This idea was thrown out, since the extra work wouldn't make
the game any easier to work on. Then, I started thinking of a scripting engine.
My first idea was to have everything in the file holding the scripts, be
it locations, or other data -- all scripts would be stationary. This was
a bad idea, I had decided, since I wanted moving NPCs, but I wasn't going
to be able to bind scripts to NPCs, and this approach would mean a lot of
hard drive activity, since the file would constantly be opened and checked
for scripts every time the Player moved or pressed space. Heres my final
-All scripts are in one file
-Every script has a header and a footer
-The header is numbered to allow for fast seeking
-If a script is to be run, an extra variable for each object on the screen
is checked, and then the file is checked for that script number
-Extra sprites were to be created, both would be invisible, one to signify
when a player walked over a location to run the script, and the other to
allow a script to be run when a player hits without a visible object.
The code to read raw commands was easy. Heres an example that could easily
be converted to use in a program.
'This is here because any given SLEEP sucks, and if you press space to enter
'the space is still in the buffer, so this gives you a chance to move your
hand off the key.
FOR a = 1 TO 100
clearthebuffer$ = INKEY$
fileno = FREEFILE
FILE$ = "script.scr"
OPEN FILE$ FOR INPUT AS #fileno
'Finds the record first.
'searches for "entry 1" or whatever.
WHILE com$ <> "entry" + STR$(scriptno%)
INPUT #1, com$
IF EOF(fileno) THEN CLOSE : END 'If it can't find the script, it exits.
WHILE com$ <> "end" 'This is where it searches for the end of the entry.
INPUT #fileno, com$ 'a check for "entry" and "EOF" will work too.
'I check the first 5 letters of any command.
SELECT CASE LCASE$(LTRIM$(RTRIM$(LEFT$(com$, 5)))) 'Five letters of every
command are read. The rest are just for
CASE "sleep" 'just sleep. 'user ease. The LTRIM and RTRIM let the scripting
SLEEP 'tabs in the file to follow the same spacing rules programmers use.
CASE "line1" 'line1 text string
line1$ = MID$(com$, 6)
CASE "line2" 'line2 text string
line2$ = MID$(com$, 6)
CASE "line3" 'line3 text string
line3$ = MID$(com$, 6)
CASE "line4" 'line4 text string
line4$ = MID$(com$, 6)
CASE "dialo" 'dialogbox 0 or 1
'Will fill the top of the screen with color X.
lin% = VAL(MID$(com$, 10))
line (0, 0)-(319, 90), lin%, BF
CASE "showt" 'showtext 0 or 1
IF VAL(RIGHT$(com$, 1)) = 1 THEN
line (0, 0)-(319, 90), 1, BF
line (0, 0)-(319, 90), 0, BF
otimer = TIMER
lin = VAL(MID$(com$, 6))
WHILE TIMER < otimer + lin:WEND
this information, a state-of-the-art tile engine, some nice art, dialog that
doesn't make your audience want to wretch, a story which involves more than
a dragon with a perchant for coming back every 1000 years to get it's ass
whomped, you too can make an RPG for the ages! Look at the screenshot above.
I have no idea what game that came from. I was just looking for a screenshot
from an RPG. There you are.
As always, remember that Ass is Ass. It's ass in QB, it's ass in C, it's
ass in Pascal(though in C, it's probably crashy ass, where in QB it's probably
just ass). It takes balls of steel to stay away from that which could be
called "ass", Remember that through hard work, tons of beta testing,
and the balls to admit that something you wanted to do sucks ass, you too
can make a game which isn't ass.
a random picture of some anonymous chick. Who is she? I have no idea. It
certainly breaks up the monotony of this long article though, doesn't it?
Lets all give it up for google and anonymous chicks!!!(yay!)
I'll dissect the routine for you:
-Opens file and searches for string
-Checks first five letters of every line for a command.
-if the letters spell sleep, if it reads that, it just runs the command.
-for line1,2,3,4, it changes the string variable to that exists after the
first 5 lines.
-Dialogbox will draw that infamous blue box at the top of the screen. Be
afraid. Be very afraid.
-Showtext writes the text in line1,2,3,4 if the param is 1, otherwise it
redraws the blue box.
-Pause will just wait for X seconds.
An entry in Script1.SCR would look something like this:
line1 This is a test
line2 of the emergency broadcast system.
line3 if this was more than a test
line4 You'd be in a bunker, not reading this tutorial.
This section should be shorter, since the concept is so simple.
All you need to do here, is check the variables for all the objects you have
and if you have an invisible sprite for placing both types of script (one
for activated by space, and another for scripts objects activated by walking
over it). All objects can have scripts bound the them if they are activated
by pressing space, but the clear one does come in handy.
I'm only going to cover the easiest way to do it, using a small piece of
'this is just a description of the structure. I can't remember how to do
this in QB since most of the structs I've been doing lately have been in
C(my latest engine in QB uses a 2d array to simulate a struct, and there
are too many refrences to it to change that now...), so I may not get this
X as integer
Y as integer
sprite as integer
scriptbinding as integer
dim objs(1 to 100) as objects
'just a regular script check when we hit spacebar.
'since this is just a generic routine, direction checking isn't done.
'Therefore, you must be standing directly over the sprite to activate it.
if inkey$ = " " then'just spacebar checking.
for a = 1 to 100 'This would be in the player movement routine.
if objs(a).sprite > 0 OR objs(a).sprite = -2 then 'Checks for the transparant
if objs(a).X = myX% and objs(a).Y = myY% then 'object placement sprite
if objs(a).scriptbinding <> 0 then
'It checks through the array to see if you are on any of them.
'Checks for scripts using that sprite we defined as 'walkoverable' and runs
the correct script
for a = 1 to 100
if objs(a).sprite = -1 then 'This number is just any number we chose to represent
'detection sprites. You touch em, they go off.
if objs(a).X = myX% and objs(a).Y = myY% then
if objs(a).scriptbinding <> 0 then
'Same here. It just checks the entire array for a match.
This code should be almost self-explainitory. Most of it is just the collision
detection any game engine should already have. Since the script bindings
are held in the objects memory structures, it's a simple matter to work with
them. The only difference between the two algorithms above is why the scripts
are invoked (one is invoked because the user presses space, the other is
invoked when the user walks over an object set to the -1 sprite).
That's it for this tutorial. For my next game engine, I'm going to try to
implement storage, decisions, mathematics, and loops in the scripting engine.
If I achieve those goals. I'll write a tutorial on it. It would be easy to
use named scripts (instead of script X%, it would be script text$, making
it easier to work with the large file in huge projects). To implement that
with the code I provided would be rather easy, just change the data types
in the struct and on the script sub for the script, and remove the STR$()
in the script sub, but keep in mind that working with strings is generally
slower than working with integers.
Update: It turns out that there is one restriction on this code -- if you
call the scripting routines using certain variables, you cannot change those
variables using their names. EG. if you call a sub foo(x), and you call it
foo bar, you won't be able to make any changes to foo, you'll have to change
x. reply if that doesn't make sense, but I believe the feature is due to
QBs recursion support...
-SJ Zero doesn't recycle his old articles often, but
with chicks, explosions and other cool shit, this time it's ok.
can write to QB Accelerator at email@example.com