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.