Scripting and You
-----------------
Michael Dowling (typosoft@hotmail.com)
www.geocities.com/typosoft/
May 30, 2002
I've seen a lot of scripting tutorials on the net, but I wanted to make my own.
I hope this one doesn't suck, but this is how I handle my scripting, and I think
I've done a fairly good job with Ped Xing's Quest.
First off we need to explore the concept of triggers.
triggers are variables that hold game data that your scripting language can use.
So to start your program off, do this
DIM SHARED Trig(-20 TO 256)
That looks kinda odd, huh? Well the reason it goes into the negatives is because
those variables will be changed up a lot, like random numbers, loop variables,
and game variables that communicate with the script (number of enemies on the
screen, amount of money the player has, etc...). Now let's discuss the concept
of scripting.
When you play your favorite RPG on the old SNES, you'll notice that they have
text boxes, towns, forests, and events that happen. The one thing that ties all
those things together is a scripting engine. A scripting engine is when you make
a file that has a series of commands in it that are interpreted by your program.
In this tutorial we're using QBasic. So a scripting language opens a text file,
reads it, scanning for commands and acting on those commands.
Let's start working on some code...
=-scripting.bas-=
DEFINT A-Z
'Script engine
'Michael Dowling (typosoft@hotmail.com)
'www.geocities.com/typosoft/
'
'This is an example of a scripting engine. It is exact code taken
'from Ped Xing's Quest scripting engine.
DECLARE SUB Script (File$)
DIM SHARED Trig(-20 TO 255)
SUB Script (File$)
Newt:
REDIM IFs(1 TO 20) 'This will hold the IF result variables
LineNumber = 0 'Line number of the script file
AtIf = 0 'Which if of the IFs() you are in
IF LTRIM$(RTRIM$(File$)) = "" THEN GOTO SkipScript
File01% = FREEFILE
OPEN TheFile$ FOR INPUT AS File01%
DO UNTIL EOF(File01%)
INPUT #File01%, TheLine$
LineNumber = LineNumber + 1
INPUT #File01%, T$
T$ = LTRIM$(RTRIM$(UCASE$(T$)))
'The most important and hardest scripting command there is...IF!
IF T$ = "IF" THEN
AtIf = AtIf + 1 'Know that you are at a certain if number in the script
INPUT #File01%, T$, N$, Eq 'Inputs the number to compare then the number to compare it to then how it should relate to it
IF INSTR(T$, "/TRIG") THEN 'This takes the T$ and sees if you are using a number stored in a TRIG and converts it to QB code
VV = VAL(MID$(T$, 6, 3))
T$ = STR$(Trig(VV))
END IF
IF INSTR(N$, "/TRIG") THEN 'This does the same the above one does, but it
VV = VAL(MID$(N$, 6, 3)) 'converts the number you are comparing to, so you could compare two trigs.
N$ = STR$(Trig(VV))
END IF
V = VAL(T$): N = VAL(N$) 'convert the values of the strings to values of integers
IF AtIf = 0 THEN AtIf = 1 'make sure not to error out
IFs(AtIf) = 0 'True = -1 and False = 0
IF Eq = 0 AND V <> N THEN IFs(AtIf) = -1 'if Eq = 0 then it's saying IF the first number doesn't equal the second number, then the outcome is true
IF Eq THEN
IF Eq = -1 AND V = N THEN IFs(AtIf) = -1 'if Eq = -1 then it's saying IF the first number equals the second number, then the outcome is true
IF Eq = -999 AND V < N THEN IFs(AtIf) = -1 'if Eq = -999 then IF the first number is less than the second number, then the outcome is true
IF Eq = -99 AND V >= N THEN IFs(AtIf) = -1 'if Eq = -999 then IF the first number is greater than the second number, then the outcome is true
END IF
IF IFs(AtIf) = 0 THEN 'If the IF conditional turns out false, then skip the inside of the IF/END IF conditional block
AI = AtIf
'This also has to check for IFs inside of the first IF/END IF block
'to keep things in orde. It works the same way QB does.
DO UNTIL EOF(File01%)
INPUT #File01%, TP$
IF LTRIM$(RTRIM$(TP$)) = "IF" THEN AI = AI + 1
IF LTRIM$(RTRIM$(TP$)) = "END IF" THEN AI = AI - 1: IF AI = AtIf - 1 THEN EXIT DO
LOOP
AtIf = AtIf - 1
END IF
TT = 0: T$ = ""
END IF
'Exits the IF and backtracks throught the AtIf list
IF T$ = "END IF" THEN IF AtIf THEN IFs(AtIf) = 0: AtIf = AtIf - 1
'Goes to a label in the script file. similar to GOTO in QB
IF T$ = "GOTOL" THEN
AtDo = 0: AtLoop = 0
INPUT #File01%, LL$
CLOSE #File01%
OPEN TheFile$ FOR INPUT AS #File01%
DO
IF EOF(File01%) THEN InScript = 0: EXIT SUB
LINE INPUT #File01%, TBB$
LOOP UNTIL UCASE$(LTRIM$(RTRIM$(TBB$))) = LTRIM$(RTRIM$(UCASE$(LL$))) + ":"
END IF
'Handles the triggers. You can set Trig(T) with TRIG or add X
'value with TRIG+
IF T$ = "TRIG" THEN INPUT #File01%, T, E: Trig(T) = E
IF T$ = "TRIG+" THEN INPUT #File01%, T, E: Trig(T) = Trig(T) + E
'Random number generator that puts a random number in Trig(TT)
IF T$ = "RAND" THEN
INPUT #File01%, TT, lowerbound, upperbound
Trig(TT) = INT((upperbound - lowerbound + 1) * RND + lowerbound)
END IF
'Print the value of Trig(T) on the screen
IF T$ = "PRINTT" THEN INPUT #File01%, T, S: PRINT Trig(T): SLEEP S
'Scripting commands that call individual functions of the game are
'very useful. They can be used inside of a 'loop'. There is an example
'later in the tutorial.
IF T$ = "HVAR" THEN HandleVariables
IF T$ = "HTRIGGERS" THEN CALL HandleTrigs
IF T$ = "HPLAYERS" THEN CALL HandlePlayers
IF T$ = "HPROJ" THEN CALL HandleProjectiles
'Here is something that is pretty complicated, but at the same time not.
'This can take the number that the trigger holds and place it into a
'variable that is inside of something like a player or a trigger. Since
'you can't specify directly from the script you will have to use a
'command similar to this as a middle man. See at the bottom of the code
'for how it takes the scripting commands and inputs and places them into
'the players and triggers or vice versa.
IF T$ = "FILLTRIG" THEN
INPUT #File01%, Family, Branch, Ite, Tri
GOSUB SetFamilyVar
Trig(Tri) = FamBran
END IF
IF T$ = "USETRIG" THEN
INPUT #File01%, Family, Branch, Ite, Tri
GOSUB UseFamilyVar
END IF
LOOP
CLOSE #File01%
SkipScript:
ERASE Tem, IFs, DOs, PedSpells
EXIT SUB
'This is that middleman thing I wrote about earlier in the code
'probably isn't useful to you other than the concept.
SetFamilyVar:
SELECT CASE Family
CASE 0: FamBran = CurrentPlayer
CASE 1: 'Player
IF Branch = 1 THEN FamBran = Player(Ite).x
IF Branch = 2 THEN FamBran = Player(Ite).y
IF Branch = 3 THEN FamBran = Player(Ite).Tile
IF Branch = 4 THEN FamBran = Player(Ite).HP
IF Branch = 5 THEN FamBran = Player(Ite).Chi
IF Branch = 6 THEN FamBran = Player(Ite).Keys
IF Branch = 7 THEN FamBran = Player(Ite).Death
IF Branch = 8 THEN FamBran = Player(Ite).Alive
IF Branch = 9 THEN FamBran = Player(Ite).GoodBad
IF Branch = 10 THEN FamBran = Player(Ite).AI
IF Branch = 11 THEN FamBran = Player(Ite).Coins
IF Branch = 12 THEN FamBran = Player(Ite).FindOpposite
IF Branch = 13 THEN FamBran = Player(Ite).Dir
IF Branch = 14 THEN FamBran = Player(Ite).Frame
CASE 2: 'Trigger
IF Branch = 1 THEN FamBran = Trigger(Ite).x
IF Branch = 2 THEN FamBran = Trigger(Ite).y
IF Branch = 3 THEN FamBran = Trigger(Ite).Tile
IF Branch = 4 THEN FamBran = Trigger(Ite).Taken
IF Branch = 5 THEN FamBran = Trigger(Ite).IndexNum
CASE 3: 'WorldMap
IF Branch = 1 THEN FamBran = VAL(MID$(LTRIM$(RTRIM$(WorldMap.MapFile)), 37, 39))
END SELECT
RETURN
UseFamilyVar:
SELECT CASE Family
CASE 1: 'Player
IF Branch = 1 THEN Player(Ite).x = Trig(Tri)
IF Branch = 2 THEN Player(Ite).y = Trig(Tri)
IF Branch = 3 THEN Player(Ite).Tile = Trig(Tri)
IF Branch = 4 THEN Player(Ite).HP = Trig(Tri)
IF Branch = 5 THEN Player(Ite).Chi = Trig(Tri)
IF Branch = 6 THEN Player(Ite).Keys = Trig(Tri)
IF Branch = 7 THEN Player(Ite).Death = Trig(Tri)
IF Branch = 8 THEN Player(Ite).Alive = Trig(Tri)
IF Branch = 9 THEN Player(Ite).GoodBad = Trig(Tri)
IF Branch = 10 THEN Player(Ite).AI = Trig(Tri)
IF Branch = 11 THEN Player(Ite).Coins = Trig(Tri)
IF Branch = 12 THEN Player(Ite).FindOpposite = Trig(Tri)
IF Branch = 13 THEN Player(Ite).Dir = Trig(Tri)
IF Branch = 14 THEN Player(Ite).Frame = Trig(Tri)
CASE 2: 'Trigger
IF Branch = 1 THEN Trigger(Ite).x = Trig(Tri)
IF Branch = 2 THEN Trigger(Ite).y = Trig(Tri)
IF Branch = 3 THEN Trigger(Ite).Tile = Trig(Tri)
IF Branch = 4 THEN Trigger(Ite).Taken = Trig(Tri)
IF Branch = 5 THEN Trigger(Ite).IndexNum = Trig(Tri)
END SELECT
RETURN
END SUB
-=end code=-
Well, there is an example, and it seems complicated, but it isn't. This is
a pretty advanced script engine, and is just for inspiration and "oh, that's
how you do a scripting engine" type things.
How to have your game communicate with the scripting engine
------------------------------------------------------------
If you want the scripting engine to know that there are 80 coins in the player's
pocket, then you will have to go through a middle man...the trigger. You will
need a sub that fills trigs with up to the frame information like money, hp,
enemies, etc...
Example of scripting code
-------------------------
-=script.dat=-
TopOfFile:
TRIG+,1,1
IF,/TRIG001,25,1
GOTOL,QuitIt
END IF
PRINTT,1,1
GOTOL,TopOfFile
QuitIt:
EXIT FILE
-=end of file=-
what that does is loop because it is going through all that code then hitting
the line that says GOTOL,TopOfFile. That makes it loop until that IF becomes
true. So when the TRIG+ cause TRIG001 to reach 25, the IF conditional will
be true and exit the loop by GOTOL,QuitIt
Well, that's about all I feel like writing. I think it is a good start.