WHITE LIGHTNING The Key to Professional Games Design Oasis Software The high level graphics development system for the Spectrum 48K CHEAT SHEET & MICRODRIVE MANUAL CONTENTS Creating Large Sprites 1 Moving Sprites 2 Screen Scrolling 2 Simple Putting 2 More Advanced Techniques 4 Collision Detection 7 Sample Source Listings 8 The Lunar Lander Program 9 Table 1 (Lunar Sprites) 20 Diagram 1 (Landsape Sprites) 23 APPENDIX A - THE MICRODRIVE VERSION Introduction 26 Implementing on Microdrive 26 Preparing a Cartridge for Source Code 26 Editing Forth Screens 27 The Edit Buffers 27 Transferring Old Source 27 Bad Sectors 28 Additional Error Messages 28 The Microdrive Sprite Generator 29 Implementing on Microdrive 29 Saving and Loading Sprites 29 Creating Large Sprites 30 Merging Sprites 30 WHITE LIGHTNING CHEAT SHEET This sheet is intended as a supplement to the User Manual and is provided only to "get you started". The best way to use it is to type in the source code at the end of this section and execute the appropriate section as you go. You will need the demostration sprites in memory but sprites 25 to 255 should be deleted to make enough memory available. A word to do this is: : CLRS 256 25 DO I SPN ! TEST IF DSPRITE ENDIF LOOP ; Type this in then type CLRS It is also worth deleting the large sprites 9, 12, 13 and 14 using: 9 SPN ! DSPRITE 12 SPN ! DSPRITE 13 SPN ! DSPRITE 14 SPN ! DSPRITE where means press ENTER. After execution CLRS should be FORGOTTEN using FORGET CLRS Following the example source there is a listing of a complete game. You won't be able to run it without the sprites but we can provide a tape with the source and sprites for one pound seventy five pence if you think it would be helpful. Creating Large Sprites Quite often in games writing large sprites are required which may extend across several screens. The sprite development software can produce sprites with dimensions of up to 15 by 15 characters. Larger sprites need to be constructed in the White Lightning itself. The sample listing for the Lunar Lander contains a routine which we can use as an example. The Sprite Development Program was used to produce 16 sprites, each was 3 high by 8 wide, having sprite numbers 10 to 25. The following routine sets up sprite 128 which is 3 high by 128 wide, and then fills it with the 16 small sprites before deleting them to save memory. SCR#8 0 : MAKE 128 SPN ! 3 HGT ! 128 LEN ! 0 SROW ! 128 SP2 ! ISPRITE 1 16 0 DO I 10 + DUP SPN ! SP1 ! I 8 * SCOL ! GWBLM GWATIM DSPRITE 2 LOOP ; Line 0 just defines the big sprite in memory and sets SROW to 0. Line 1 loops round 16 times with I taking the values 0 to 15. I 10 + calculates the sprite number of the smaller sprite and I 8 * calculates the column in the big sprite that this smaller sprite is to be put into. GWBLM GWATIM moves the pixel data, then the attributes, from the individual small sprites into the big sprite. You won't be able to execute this example without the 16 3 by 8 sprites but it illustrates how big sprites are to be achieved. -- end of page 1 Moving Sprites The chief problem facing the programmer who wants to move sprites around the screen is choosing from the numerous schemes available. We now consider some of these methods, each with its own merits for speed, simplicity, smoothness and storage. We'll begin with the easiest to implement and then work up to some of the more elaborate techniques. Screen Scrolling Where an object is to be moved within a screen window that does not contain any other objects the screen itself scan be scrolled. This is particularly applicable where movement is either horizontal or vertical. Diagonal movement is also possible however. In some cases the object is constrained to move on the screen because parts of the object scrolled off the screen without wrap are lost. To begin with let's consider a very simple example - moving an invader (demo sprite 24) 2 characters high and 2 characters wide left and right under keyboard control. The routine is in three sections: the first sets up the base and initial parameters, the second moves left or right and the third polls the keyboard and controls the movement. : SETUP 0 COL ! 6 ROW ! 24 SPN ! CLS SETAM PUTBLS 2 HGT ! 32 LEN ! ; : LEFT WRL1V : : RIGHT WRR1V ; : KEYS 1 1 KB IF LEFT ENDIF 8 1 KB IF RIGHT ENDIF ; This will move the base by 1 pixel left or right but by changing the words LEFT and RIGHT to be WRR4V and WRL4V or WRR8V and WRL8V movement of 4 or 8 pixels can be achieved. To try these routines we'll need a small test routine. : TESTA ATTOFF SETUP BEGIN KEYS 8 2 KB UNTIL ; TESTA will loop around until SYMBOL SHIFT is pressed. Pressing CAPS SHIFT will move the base left, pressing SPACE will move the base right, and pressing SYMBOL SHIFT will exit the loop. This routine can also be excuted in background using: : TESTB EXX SETUP EXX ' KEYS INT-ON ; To halt, just type INT-OFF. If you've typed in the source at the end of this section, type 6 LOAD to compile and then TESTA or TESTB to execute. Simple Putting Another fairly simple means of moving sprites around the screen is to simply PUT sprites with a blank border around them. Suppose the sprite you want to move is 2 characters high and 2 characters wide. You will need to construct a 4 by 4 sprite so that the 2 by 2 sprite can be contained with a border around the outside. Make sure background is off by typing INT-OFF. Suppose for example, sprite 24 is a 2 by 2 sprite (you can use demo sprite 24 for the purposes of this example). We'll use sprite 62 for the sprite to be moved. To set up sprite 62 use: -- end of page 2 : MAKE 62 SPN ! 4 HGT ! 4 LEN ! TEST 0= IF ISPRITE CLSM ENDIF ; This will create and clear sprite 62. Now use: 1 SCOL ! 1 SROW ! 62 SP2 ! 24 SP1 ! GWBLM Sprite 62 is now set up and ready to use. We now need four words to move the sprite UP, DOWN, LEFT and RIGHT. : UP 7 1 KB IF ROW @ 0 > MINUS ROW +! ENDIF ; : DOWN 8 1 KB IF ROW @ 20 < ROW +! ENDIF ; : LEFT 1 1 KB IF COL @ 0 > MINUS COL +! ENDIF ; : RIGHT 1 2 KB IF COL @ 28 < COL +! ENDIF ; The ENTER and SPACE keys will move the sprite up and down and CAPS SHIFT and Z will move the sprite left and right respectively. The full word to animate the sprite becomes: : TESTC 62 SPN ! 10 COL ! 10 ROW ! BEGIN UP DOWN LEFT RIGHT ADJM PWBLS 6 1 KB UNTIL ; To exit TEST we press the key at 6 1; this is the "P" key. If you're using the source at the end of this section type 7 LOAD MAKE TESTC. The great limitation of this routine, however, is that data already on the screen will be replaced by the sprite being PUT and subsequently lost. Before considering the more sophisticated methods available to us which overcome this limitation let's just consider some simpler methods of circumventing this problem, which will work for similar situations. Supposing the screen holds half a dozen or so fixed objects and we wish to move the invader in the last example through these objects. First of all let's set up a screen with these objects scattered throughout. : SETUP 4 SPN ! 3 2 4 5 6 3 5 12 10 9 12 14 6 0 DO ROW ! COL ! PUTORS LOOP ; Notice that we use the PUTORS word to OR data to the screen; the reason for this will become clear. We'll now redefine UP, DOWN, LEFT and RIGHT so that sprite 62 is only 'PUT' if it is moved. The new code becomes: : KCHK KB DUP ROT OR SWAP ; : UP 7 1 KCHK IF ROW @ 0 > MINUS ROW +! ENDIF ; : DOWN 8 1 KCHK IF ROW @ 20 < ROW +! ENDIF ; : LEFT 1 1 KCHK IF COL @ 0 > MINUS COL +! ENDIF ; : RIGHT 1 2 KCHK IF COL @ 28 < COL +! ENDIF ; The complete word becomes: : TESTD CLS BEGIN COL @ ROW @ SETUP ROW ! COL ! 62 SPN ! @ UP DOWN LEFT RIGHT IF ADJM PWBLS ENDIF 6 1 KB UNTIL ; What is happening is that as soon as the moving sprite is PUT to the screen all screen data is immediately "OR"ed so that if any was blotted out, it is immediately replaced. Type "P" to exit. To use the source code listings type 8 LOAD TESTD. -- end of page 3 More Advanced Techniques Often it is not practical to repeatedly PUT the screen data which accompanies the moving sprite, and more frequently movement is required with a higher resolution than one character. To begin with, let's consider the problem of improving the resolution of the movement. Let's work again with a 2x2 sprite (sprite 24 of the demo sprites will do). Type COLD to clear previous examples. Suppose we wish to move the sprite around the screen with 2 pixel resolution. This means that between 2 successive columns there are 4 intermediate orientations, each successive orientation being 2 pixels right shifted. This means we need 4 sprites in all before the cycle is repeated at the next column position. To begin with let's set up the 4 sprites and number them 100, 101, 102 and 103. To create these 4 sprites, type: : MAKE 2 HGT ! 3 LEN ! 104 100 DO I SPN ! ISPRITE CLSM LOOP ; This will define and clear the 4 sprites and we can now put the character in its various orientations into these sprites. There are two stages to this operation. Firstly sprite 24 needs to be put into sprite 100, then sprites 100 to 103 need to be scrolled and PUT successively to build up the four orientations. : SET1 0 SROW ! 0 SCOL ! 24 SP1 ! 100 SP2 ! GWBLM ; This sets up sprite 100 and the remaining 3 orientations are set up from this sprite using: : SET2 103 100 DO I SP1 ! I 1+ DUP SP2 ! SPN ! COPYM WRR1M WRR1M LOOP ; It's worth putting these sprites on the screen to see what they look like. Assuming you've executed the words MAKE, SET1 and SET2 use: : TESTE CLS 0 COL ! 4 0 DO I 100 + SPN ! I DUP + ROW ! PUTBLS LOOP 8 0 AT ; This will place the 4 orientations, one above the other, so that the resolution of the movement can be seen. To use the source versions type 9 LOAD MAKE SET1 SET2 TESTE This now gives us 2 pixel horizontal resolution so that we now have 128 horizontal plotting positions in the range 0 to 127. We need a simple formula which will calculate the sprite number and the column from the horizontal plotting position. This turns out to be very simple: : HPLOT 4 /MOD COL ! 100 + SPN ! PUTBLS ; So to PUT at X-position 27 (54 pixels from the left-hand column) just use: 27 HPLOT The previous example is useful in that it indicates a way of producing high resolution PUTting but as it stands cannot be used for animation because it does not enable the removal of previously places orientations. Before looking at a scheme for animating these orientations let's generalise this example to cover high resolution vertical movement as well as horizontal movement. -- end of page 4 If we're going to give the same resolution of movement (2 pixels) in the vertical plane, we're going to need 4 vertically shifted orientations for each of the horizontally offset orientations - 16 sprites in all. This time they will need to be 3x3 as opposed to the previously defined 2x3. If you've typed in the last example you'll need to delete the old sprites numbered 100 to 103. If so, type: 100 SPN ! DSPRITE 101 SPN ! DSPRITE 102 SPN ! DSPRITE 103 SPN ! DSPRITE Now type COLD to clear the dictionary. To create the 16 new sprites use: : MAKE 3 HGT ! 3 LEN ! 116 100 DO I SPN ! ISPRITE CLSM SETAM LOOP ; SET1 and SET2 are now used in exactly the same form as in the previous example to set up the first 4 sprites 100 to 103. Each of the horizontally offset orientations needs to be vertically offset by 2 pixels into 4 further orientations. 100 will be offset into 104, 108 and 112; 101 will be offset into 105, 109 and 113; and so on. We'll need a third word SET3 to do this. : SET3 -2 NPX ! 104 100 DO I DUP 12 + SWAP DO I DUP 4 + DUP SP2 ! SPN ! SP1 ! GWBLM SCRM 4 +LOOP LOOP ; Once SET1, SET2 and SET3 have been entered, compiled and executed, the definitions can be forgotten. Since we now have 2 pixel resolution in the horizontal and vertical directions we have 128 horizontal positions and 96 vertical positions. We need a word which can calculate sprite number, column and row from the 2 pixel resolution co-ords X and Y. The following word assumes the vertical then horizontal co-ords have been placed on the stack. : XYPUT 4 /MOD COL ! SWAP 4 /MOD ROW ! DUP + DUP + + 100 + SPN ! ; So to put at X-position 30 (pixel 60) Y-position 17 (pixel 34) use 17 30 XYPUT PUTBLS Note that the 100 + SPN ! at the end of the definition of XYPUT should be amended so that the number is the sprite number of the first of your 16 sprites. To use the source type 9 LOAD 10 LOAD MAKE SET1 SET2 SET3 (ignore MSG# 4s) Let's now deal with the animation of the sprite itself. Perhaps the most powerful method of sprite animation is via the XOR operation. The usefulness of this operation stems from the fact that when an object is XORed with the screen, the screen can be restored simply by repeating the operation. The area of the screen is restored to the same state as it was before the first operation (see page 22 of the White Lightning manual). We can now write a routine which moves a sprite around the screen under keyboard control using a slightly amended form of the word XYPUT. The routine below assumes the 10 sprites in the range 100 to 115, which each have dimensions 3x3, have been set up in the last example. The following variables will be used: -- end of page 5 TSPN Temporary Value for SPN TCOL Temporary Value for COL TROW Temporary Value for ROW XC X co-ordinate YC Y co-ordinate FLAG Collision Flag We'll also use a constant FSPN which holds the number of the first sprite in the series of 16. To adapt these routines for your own use just change FSPN. : PLOAD COL ! ROW ! SPN ! PUTXRS ; : PSET TSPN @ TROW @ TCOL @ ; : PCAL 4 /MOD TCOL ! SWAP 4 /MOD TROW ! DUP + DUP + FSPN + + TSPN ! ; : MOVE YC @ XC @ PCAL PSET PUTXRS PLOAD ; : PLACE YC @ XC @ PCAL PSET PLOAD ; We now need to poll the keyboard. We use the KCHK word again so that the character is not re-PUT unless a key has been pressed. : KCHK KB DUP ROT OR SWAP ; : UP 7 1 KCHK IF YC @ 4 > MINUS YC +! ENDIF ; : DOWN 8 1 KCHK IF YC @ 83 < YC +! ENDIF ; : LEFT 1 1 KCHK IF XC @ 4 > MINUS XC +! ENDIF ; : RIGHT 1 2 KCHK IF XC @ 115 < XC +! ENDIF ; : KREAD 0 UP DOWN LEFT RIGHT ; KREAD will leave 0 on the stack if no key was pressed or 1 if a key was pressed. The complete word becomes: : TESTF 10 XC ! 10 YC ! PLACE BEGIN KREAD IF MOVE ENDIF 6 1 KB UNTIL ; Note that this loop assumes no interference with the values of COL, ROW or SPN between cycles. If you are executing another Forth word, for example a word called TRY, then make sure you temporarily stack COL, ROW and SPN; eg. ... BEGIN KREAD IF MOVE ENDIF COL @ ROW @ SPN @ TRY SPN ! ROW ! COL ! 6 1 KB UNTIL ... This word can be easily executed under an interrupt using: : TESTG KREAD IF MOVE ENDIF ; : TESTH 10 XC ! 10 YC ! EXX PLACE EXX ' TESTG INT-ON BEGIN 6 1 KB UNTIL INT-OFF ; You will notice, however, that if you do execute this routine under the interrupt then the sprite may flicker in passage through certain areas of the screen. This is due to the finite time taken for the dot to scan the screen and can be very annoying. To execute from source type 11 LOAD 12 LOAD then TESTF or TESTH Let's look now at some more powerful techniques which not only help with the flickering but also include collision detection facilities. -- end of page 6 Collision Detection To produce the smoothest movement of all, and include collision detection, a six stage operation is used. The technique utilises two dummy sprites, and all intermediate stages of the operation are carried out in memory. For this example let's number the dummy sprites 116 and 117. To set up the sprites use: : MAKED 3 HGT ! 3 LEN ! 116 SPN ! ISPRITE 5 HGT ! 5 LEN ! 117 SPN ! ISPRITE ; This needs to be typed in and executed before executing the source code or error MSG# 10 will be produced. The six stage procedure is as follows: 1. The last orientation PUT, together with a one character surround, are GOT into the 5x5 dummy sprite. 2. The last orientation is GWXRMed out of the 5x5, restoring the original screen data. 3. The new orientation is COPYMed into the 3x3 dummy. 4. The screen data in the 5x5 is PWNDMed into the 3x3 and SCANM performed on the 3x3 to detect any collision. A flag is set. 5. The new orientation is GWXRMed into the 5x5 dummy. 6. The 5x5 is PUTBLSed onto the screen. The code for this algorithm is the same as the previous example except that the word MOVE needs to be modified. Define sprites as for the previous example and execute MAKED then use: : STEP1 -1 COL +! -1 ROW +! SPN @ 117 SPN ! GETBLS ; : STEP2 1 SCOL ! 1 SROW ! 117 SP2 ! SP1 ! GWXRM ; : STEP3 116 SP2 ! TSPN @ SP1 ! COPYM ; : STEP4 COL @ - SCOL ! ROW @ - SROW ! 117 SP2 ! 116 SP1 ! PWNDM 116 SPN ! SCANM FLAG +! ; : STEP5 SP1 ! GWXRM 117 SPN ! PUTBLS PSET COL ! ROW ! SPN ! FLAG @ IF 100 100 BLEEP 0 FLAG ! ENDIF ; : MOVE YC @ XC @ PCAL PSET STEP1 STEP2 STEP3 STEP4 STEP5 ; To use the source code type COLD to clear all the previous examples then: 11 LOAD FORGET MOVE 14 LOAD 12 LOAD 13 LOAD to compile the words TESTF and TESTH. The Bleep will sound when the sprite collides with any other screen data. -- end of page 7 SCR # 6 0 : SETUP 0 COL ! 6 ROW ! 24 SPN ! CLS PUTBLS 2 HGT ! 32 LOW ! ; 1 : LEFT WRL1V ; : RIGHT WRR1V ; 2 : KEYS 1 1 KB IF LEFT ENDIF 8 1 KB IF RIGHT ENDIF ; 3 : TESTA ATTOFF SETUP BEGIN KEYS 8 2 KB UNTIL ; 4 : TESTB ATTOFF EXX SETUP EXX ' KEYS INT-ON ; 5 6 7 SCR # 7 0 : MAKE 62 SPN ! 4 HGT ! 4 LEN ! TEST 0= IF ISPRITE CLSM ENDIF 1 : 1 SCOL ! 1 SROW ! 62 SP2 ! 24 SP1 ! GWBLM ; 2 : UP 7 1 KB IF ROW @ 0 > MINUS ROW +! ENDIF ; 3 : DOWN 8 1 KB IF ROW @ 20 < ROW +! ENDIF ; 4 : LEFT 1 1 KB IF COL @ 0 > MINUS COL +! ENDIF ; 5 : RIGHT 1 2 KB IF COL @ 28 < COL +! ENDIF ; 6 : TESTC 62 SPN ! 10 COL ! 10 ROW ! CLS BEGIN UP DOWN LEFT RIGHT 7 : ADJM PWBLS 6 1 KB UNTIL ; SCR # 8 0 : SETUP 4 SPN ! 1 3 2 4 5 6 3 5 12 10 9 12 4 6 0 DO ROW ! COL ! 1 : PUTORS LOOP ; : KCHK KB DUPROT OR SWAP ; 2 : UP 7 1 KCHK IF ROW @ 0 > MINUS ROW +! ENDIF ; 3 : DOWN 8 1 KCHK IF ROW @ 20 < ROW +! ENDIF ; 4 : LEFT 1 1 KCHK IF COL @ 0 > MINUS COL +! ENDIF ; 5 : RIGHT 1 2 KCHK IF COL @ 28 < COL +! ENDIF ; 6 : TESTD CLS BEGIN COL @ ROW @ SETUP ROW ! COL ! 62 SPN ! 7 : 0 UP DOWN LEFT RIGHT IF ADJM PWBLS ENDIF 6 1 KB UNTIL ; SCR # 9 0 : MAKE 2 HGT ! 3 LEN ! 104 100 DO I SPN ! ISPRITE CLSM LOOP ; 1 : SET1 0 SROW ! 0 SCOL ! 24 SP1 ! 100 SP2 ! GWBLM ; 2 : SET2 103 100 DO I SP1 ! I1+ DUP SP2 ! SPN ! COPYM WRR1M 3 WRR1M LOOP 4 : TESTE CLS 0 COL ! 4 0 DO I 100 + SPN ! I DUP + ROW ! PUTBLS 5 LOOP 8 0 AT ; 6 : HPLOT 4 /MOD COL ! 100 + SPN ! PUTBLS ; 7 SCR # 10 0 : MAKE 3 HGT ! 3 LEN ! 116 100 DO I SPN ! ISPRITE CLSM SETAM 1 LOOP ; 2 : SET3 -2 NPX ! 104 100 DO I DUP 12 + SWAP DO I DUP 4 + DUP SP2 3 ! SPN ! SP1 ! GWBLM SCRM 4 +LOOP LOOP ; 4 : XYPUT 4 /MOD COL ! SWAP 4 /MOD ROW ! DUP + DUP + + 100 + SPN 5 ! ; 6 7 SCR # 11 0 0 VARIABLE TSPN 0 VARIABLE TCOL 0 VARIABLE TROW 0 VARIABLE XC 1 0 VARIABLE YC 100 CONSTANT FSPN 0 VARIABLE FLAG 2 : PLOAD COL ! ROW ! SPN ! PUTXRS ; 3 : PSET TSPN @ TROW @ TCOL @ ; 4 : PCAL 4 /MOD TCOL ! SWAP 4 /MOD TROW ! DUP + DUP + FSPN + + 5 TSPN ! ; 6 : PLACE YC @ XC @ PCAL PSET PLOAD ; 7 : MOVE YC @ XC @ PCAL PSET PUTXRS PLOAD ; SCR # 12 0 : KCHK KB DUP ROT OR SWAP ; 1 : UP 7 1 KCHK IF YC @ 4 > MINUS YC +! ENDIF ; 2 : DOWN 8 1 KCHK IF YC @ 83 < YC +! ENDIF ; 3 : LEFT 1 1 KCHK IF XC @ 4 > MINUS XC +! ENDIF ; 4 : RIGHT 1 2 KCHK IF XC @ 115 < XC +! ENDIF ; 5 : KREAD 0 UP DOWN LEFT RIGHT ; 6 : TESTF 10 XC ! 10 YC ! PLACE BEGIN KREAD IF MOVE ENDIF 6 1 KB 7 UNTIL ; -- end of page 8 SCR # 13 0 : TESTG KREAD IF MOVE ENDIF ; 1 : TESTH 10 XC ! 10 YC ! EXX PLACE EXX ' TESTG INT-ON BEGIN 6 1 2 KB UNTIL INT-OFF ; 3 4 5 6 7 SCR # 14 0 : STEP1 -1 COL +! -1 ROW +! SPN @ 117 SPN ! GETBLS ; 1 : STEP2 1 SCOL ! 1 SROW ! 117 SP2 ! SP1 ! GWXRM ; 2 : STEP3 116 SP2 ! TSPN SP1 ! COPYM ; 3 : STEP4 COL @ - SCOL ! ROW @ - SROW ! 117 SP2 ! 116 4 SP1 ! PWNDM 116 SPN ! SCANM FLAG +! ; 5 : STEP5 SP1 ! GWXRM 117 SPN ! PUTBLS PSET COL ! ROW ! SPN ! 6 FLAG @ IF 100 100 BLEEP 0 FLAG ! ENDIF ; 7 : MOVE YC @ XC @ PCAL PSET STEP1 STEP2 STEP3 STEP4 STEP5 ; NOTE Make sure that you delete the unwanted sprites 25 to 255, 9, 12, 13 and 14 BEFORE loading source code from tape or the source code will over-run the sprites. LUNAR LANDER Variables PH Horizontal Phase of scrolling landscape SPD Horizontal Velocity of scrolling landscape DOWN Set to 1 if Lander crashes FU Remaining fuel XP Vertical Position of Lander VEL Vertical Velocity of Lander SX Phase of X-velocity dial SY Phase of Y-velocity dial SFU Phase of Fuel Gauge LX Phase of Horizontal Position dial Sprites NUMBERS HEIGHT LENGTH DESCRIPTION 1 1 2 Pointer 3 1 5 Landing Pad 4-9 1 1 Blocks for Landscape Sprites 10-25 3 8 Landscape Sections 26,28,29,31 1 1 Panel Sprites 32,33 1 8 Mini Landscape 43 3 3 Explosion 44 4 5 Landed Lunar Lander 65 3 3 Crashed Lunar Lander 100 6 3 Lunar Lander 101-107 6 3 Lunar Lander orientations (constructed) 128 3 128 Complete Landscape (constructed) -- end of page 9 Sprites 1 to 100 are produced using the sprite generator program. Sprites 101 to 107 are created in the main program using the word SET. Sprite 128 is constructed from sprites 10 to 25 in the main program using the word MAKE. The Lunar Lander This listing is provided as an example of White Lightning programming. In order to run the game you will need to enter the sprites as described in the next section. This is a fairly laborious task so we can offer the fainthearted the sprites and demo on tape for one pount seventy five pence. We recommend, however, that you take the time to build up the sprites yourself as an exercise in self-discipline if nothing else! The program executes one word in foreground and one word in background. The program can be roughly sub-divided the following way: Screens 6,7,8 These set up the screen display, dials, etc. Screens 9,10,11 These three screens form the routine which scrolls the landscape at one of three speeds. This routine is executed in background to give smoother movement. Screens 11,12,13,14,15,16,17 These control the flight of the lander, manipulate the dials, execute the crashes and so on. Screen 18 This executes all the previous definitions in the right order to produce the final game. Let's now look at the program in more detail. SCR # 6 0 : COLOUR 0 ROW ! 16 COL ! 16 LEN ! 23 HGT ! 7 INK 1 BRIGHT 1 SETAV ATTON ; 2 : VTSC COL ! 10 2 DO I ROW ! PUTBLS LOOP ; 3 : SCLE 26 SPN ! 18 VTSC 26 VTSC MIRM 21 VTSC 29 VTSC MIRM ; 4 : VTCL ROW ! COL ! LEN ! HGT ! PAPER SETAV ; 5 : BARS 4 6 1 20 2 VTCL 2 2 1 20 8 VTCL 5 4 1 28 2 VTCL 6 2 3 1 28 7 VTCL 5 1 16 16 14 VTCL ; 7 --> SCR # 7 0 : LND 6 COL ! 17 ROW ! 44 SPN ! PUTBLS BEGIN 7 1 KB UNTIL 1 20 ROW ! 43 SPN ! 7 COL ! 20 0 DO PUTXRS 100 100 BLEEP LOOP 2 44 SPN ! 17 ROW ! 6 COL ! PUTXRS ; 3 : PTST SPN ! COL ! ROW ! PUT BLS ; 4 : BARST 14 23 31 PTST MIRM 14 24 31 PTST MIRM 6 28 28 PTST 5 19 16 32 PTST 19 24 33 PTST ; 6 : LETR 7 INK 0 PAPER 1 18 AT ." FUEL" 0 26 AT ." VERT" 1 26 AT 7 ." VEL" 11 20 AT ." HORZ VEL" ; --> SCR # 8 0 : HRSC 32 16 DO 12 I 29 PTST 17 I 29 PTST LOOP 16 16 1 PTST ; 1 : MARK 152 159 PLOT 7 0 DRAW 216 127 PLOT 7 0 DRAW 128 71 PLOT -- end of page 10 2 0 -7 DRAW ; 3 : PANEL 0 PAPER COLOUR SCLE BARS BARST HRSC LETR MARK ; 4 : MAKE 128 SPN ! 3 HGT ! 128 LEN ! 0 SROW ! 128 SP2 ! ISPRITE 5 16 0 DO I 10 + DUP SPN ! SP1 ! I 8 * SCOL ! GWBLM GWATIM DSPRITE 6 LOOP ; 0 VARIABLE PH 7 256 VARIABLE SPD 0 VARIABLE DOWN 1008 VARIABLE FU --> SCR # 9 0 : S1 1023 AND 8 / SCOL ! 1 LEN ! PUTBLS 16 LEN ! ; 1 : NBR PH @ S1 ; : NBL PH @ 128 + S1 ; 2 : OPEN 0 PAPER 5 INK CLS 0 PH ! EXX 128 SPN ! 16 LEN ! 0 COL ! 3 3 HGT ! 21 ROW ! 0 SCOL ! 0 SROW ! PWBLS PWATTS 2 HGT ! EXX 4 0 PAPER 1008 FU ! 0 BORDER ; : SH8 PH @ DUP 7 AND 0= ; 5 : FUEL -1 FU +! ; : SR SH8 IF NBR ENDIF ; 6 : SL SH8 IF NBL ENDIF ; : SH4 PH @ DUP 3 AND 0= ; 7 : SP SPD +! ; : SS SPD @ ; --> SCR # 10 0 : POLL FU @ IF 8 1 KB IF SS -252 > MINUS SF FUEL ENDIF 1 1 1 KB IF SS 256 < SF FUEL ENDIF ENDIF ; 2 : -P - PH ! POLL ; : +P + PH ! POLL ; 3 : SR1 SR WRR1V 1 -P ; : SL1 SL WRL1V 1 +P ; 4 : SR4 SH4 IF SR WRR4V 4 -P ELSE SR1 ENDIF DROP ; 5 : SL4 SH4 IF SL WRL4V 4 +P ELSE SL1 ENDIF DROP ; 6 : SO DOWN @ IF ELSE SS ABS 256 < IF POLL ENDIF ENDIF ; 7 : SR8 SH8 IF NBR WRR8V 8 -P ELSE SR4 ENDIF DROP ; --> SCR # 11 0 : SL8 SH8 IF NBL WRL8V 8 +P ELSE SL4 ENDIF ; 1 : UR SS 200 > IF SR8 ELSE SR4 ENDIF ; 2 : LR SS 7 > IF SR1 ELSE SO ENDIF ; 3 : RT SS 100 > IF UR ELSE LR ENDIF ; 4 : UL SS -200 < IF SL8 ELSE SL4 ENDIF ; 5 : LL SS -7 < IF SL1 ELSE SO ENDIF ; 6 : LF SS -100 < IF UL ELSE LL ENDIF ; 7 : DEC SS 0< IF LF ELSE RT ENDIF ; SCR # 12 0 : SET -1 NPX ! 3 LEN ! 6 HGT ! 107 100 DO I SP1 ! I 1+ DUP SP2 ! 1 SPN ! ISPRITE COPYM WCRM LOOP ; 2 40 VARIABLE XP 8 VARIABLE VEL 3 : PREP 7 COL ! 0 DOWN ! 40 XP ! ; 4 : TICK VEL @ 255 > IF ELSE 1 VEL +! ENDIF ; 5 : THRUST FU @ IF 7 1 KB IF VEL @ -252 > IF -4 VEL +! FUEL 6 ENDIF ENDIF ENDIF ; 7 --> SCR # 13 0 : MV VEL @ XP @ + DUP 5631 > IF DROP 5631 1 DOWN ! ENDIF DUP 1 XP ! 32 / 8 /MOD 5 - ROW ! 7 AND 100 + SPN ! ROW @ 0< IF ADJM 2 PWBLS ELSE VEL @ 0< IF 1 SROW ! ROW @ 15 > IF 4 HGT ! ELSE 3 5 HGT ! ENDIF ELSE 0 SROW ! 5 HGT ! ENDIF ROW @ DUP SROW @ + 4 ROW ! PWBLS ROW ! ENDIF ; 5 6 7 --> SCR # 14 0 : BANG DOWN @ DUP IF 19 ROW ! 43 SPN ! -5 NPX ! 7 HGT ! 3 LEN ! 1 40 10 DO PUTXRS I 20 + I DO J I BLEEP LOOP PUTXRS 17 ROW ! 2 SCRV 19 ROW ! 5 +LOOP SS ABS 8 < IF 21 ROW ! 45 SPN ! PUTBLS 3 ENDIF ENDIF XP @ 5631 = IF DOWN @ 0= IF 7 COL ! LND 7 COL ! 4 MV 0 VEL ! ENDIF INT-OFF BEGIN 7 1 KB UNTIL 5 ' DEC INT-ON ENDIF ; 6 : OK 0 DOWN ! ; 7 --> SCR # 15 0 : LAND SS ABS 8 < IF VEL @ 32 < IF PH @ 1023 AND 8 / CASE 12 OF -- end of page 11 1 OK ENDOF 13 OF OK ENDOF 30 OF OK ENDOF 31 OF OK ENDOF 58 OF OK 2 ENDOF 59 OF OK ENDOF 91 OF OK ENDOF 92 OF OK ENDOF ENDCASE 3 ENDIF ENDIF BANG ; 4 128 VARIABLE SX 32 VARIABLE SY 63 VARIABEL SFU 5 : XG SX +! 16 COL ! 13 ROW ! 1 HGT ! 16 LEN ! ; 6 : RSET1 7 COL ! 3 LEN ! ; : WLEFT -1 XG WRR1V RSET1 ; 7 : WRIGHT 1 XG WRL1V RSET1 ; --> SCR # 16 0 : XVEL SPD @ 256 + 4 / SX @ - DUP 0< IF WLEFT DROP ELSE 0 > IF 1 WRIGHT ENDIF ENDIF ; 2 : YG DUP MINUS NPX ! SY +! 27 COL ! 2 ROW ! 8 HGT ! 1 LEN ! 3 WCRV RSET1 ; 4 : WUP -1 YG ; : WDOWN 1 YG ; 5 : YVEL VEL @ 256 + 8 / SY @ - DUP 0< IF WUP DROP ELSE 0 > IF 6 WDOWN ENDIF ENDIF ; 7 --> SCR # 17 0 : FVEL FU @ 16 / SFU @ - 0< IF 19 COL ! 2 ROW ! 8 HGT ! 1 LEN ! 1 -1 NPX ! -1 SFU +! WCRV RSET1 ENDIF ; 2 0 VARIABLE LX 3 : MXG 18 ROW ! 16 COL ! 16 LEN ! 1 HGT ! ; 4 : MLEFT MXG WRL1V -1 LX +! ; : MRIGHT MXG WRR1V 1 LX +! ; 5 : MON PH @ 8 / LX @ - DUP 0 > IF DROP MRIGHT ELSE 6 0< IF MLEFT ENDIF ENDIF RSET1 ; 7 --> SCR # 18 0 : OFF PANEL PREP ' DEC INT-ON BEGIN TICK THRUST MV MON XVEL 1 YVEL FVEL MON LAND MON UNTIL INT-OFF ; 2 : TST 256 SPD ! 0 PH ! 1008 FU ! 40 XP ! 8 VEL ! 128 SX ! 3 32 SY ! 63 SFU ! 0 LX ! OPEN OFF ; 4 5 6 7 WORD DESCRIPTIONS COLOUR Sets the attributes in the right-hand half of the screen. VTSC Produces a row of sprites with the current sprite number at the column on the stack. Used to build up the gauges. SCLE Uses VTSC to build up the gauges. VTCL Sets up a specified window with specified attributes. BARS Uses VTCL to set the attributes for the gauges. -- end of page 12 LAND This word controls the landing sequence. The landed sprite is placed on the pad and sits until ENTER is pressed. The explosion is then produced beneath the lander to simulate take-off and the landed lander is then exclusively OR'ed from the pad. PTST General purpose word which sets ROW, COL and SPN from the stack and then performs a PUTBLS. BARST Adds the finishing touches to the gauges by putting sprites 28, 31, 32 and 33 in the appopriate positions. Uses PTST. LETR Places the gauge titles above the gauges. HRSC Puts the horizontal scale on the screen. MARK Draws the indicators used in the gauges. PANEL Execution word to set up the whole right-hand side of the screen; ie. all previous words. MAKE See 'Creating Large Sprites'. S1 Used to calculate the next column in the large landscape sprite, to be put to the screen. LEN is set back to 16 for the next operation. NBR Gets the appropriate column (calculated by S1) when the landscape is moving right. NBL Gets the appropriate column when the landscape is moving left. OPEN Builds up the initial picture for the left of the screen. Notice the use of EXX to set up background variables for execution under interrupt. SH8 PH describes the phase of the landscape with pixel resolution. SH8 checks to see if this phase is a multiple of 8, and if so sets a flag to indicate that a fresh column should be GOT from the landscape sprite. PH is also left on the stack. -- end of page 13 FUEL Decrements the amount of fuel left. SR Checks to see if a character boundary is crossed (see SH8), and if so executes NBR. SL As SR but checks when movement is left. SH4 Checks to see if phase is crossing a half character boundary; scrolling can only increase from one pixel to four pixel movements on such a boundary or scrolling will go out of phase. SF Accelerates horizontal speed (decelerates if negative) by the amount on the stack. SS Puts the current horizontal speed on the stack. POLL Checks to see if there's any fuel left (a non-zero value of FU will act as a true flag), then first checks SPACE to accelerate right if not travelling too fast, then checks CAPS SHIFT to accelerate left if not travelling too fasr. If a key is pressed fuel is decremented. -P Updates phase and does a POLL when moving right. +P Updates phase and does a POLL when moving left. SR1 Moves landscape 1 pixel right and adjusts pointers. SR4 Moves landscape 4 pixels right and adjusts pointers, provided a half character boundary has been reached. If not, a further 1 pixel movement must be made. SL4 As SR4 when moving left. SO When speed is less than 7, keyboard is polled but no scrolling of the screen is executed. The landers is treated as horizontally stationary. -- end of page 14 SR8 Moves landscape 8 pixels (1 character) right and adjusts pointers, provided a full character boundary has been reached. If no, a further 4 pixel scroll is executed. SL8 As SR8 when movement is to the left. UR If speed is greater than 200, try and scroll by 8 pixels right; if not, try and scroll by 4 pixels right. LR If speed is greater than 7, then scroll 1 pixel right, else no scroll. RT If speed is greater than 100 then do a UR, if not do a LR. UL As UR when speed is negative. LL As LR when speed is negative. LF As RT when speed is negative. DEC The execution word which does all the scrolling logic. The words UR to LF are effectively the nodes of a tree which produce one of 7 possible scrolls from -8 pixels to +8 pixels. A detailed understanding of the workings is not necessary as long as you can adapt the routine to serve your needs. SET Creates 7 new orientations of the lander from the original in sprite 100, making 8 in all, each lander being 1 pixel shifted vertically from the one before. This enables single pixel resolution in the lander movement. PREP Used to set up initial values. TICK Increments vertical velocity (acts like gravity) unless terminal velocity has been reached. If you want to make the game more difficult change 1 VEL +! to 2 VEL +! and thus double the planet's gravity. -- end of page 15 THRUST If the lander still has fuel and hasn't reached terminal velocity then increase upward velocity. MV A fairly complicated word which moves the lander vertically. The velocity is added to the position (physicists note that unit time has elapsed, etc). If the lander goes under the base its position is put equal to the base and DOWN is SET to 1. The row and orientation are then calculated. BANG Another fairly involved word which executes a crash if DOWN=1. It checks to see if a safe landing was made and if not decides what sort of crash is required. OK A short word which sets DOWN to 0, indicating a safe landing. LAND If the lander has zero sideways velocity then vertical velocity and horizontal position are checked for a safe landing or a crash. DOWN is set accordingly. XG Updates X-VEL PHASE and sets window for scroll. RSET1 Sets back COL and LEN after XG. XVEL Controlling routine for XVEL gauge. YG Used to adjust Y-VEL gauge. WUP Move up Y-VEL gauge. WDOWN Move down Y-VEL gauge. YVEL Controlling routine for Y-VEL gauge. FVEL Controlling routine for fuel gauge. MXG Used to set up window for small screen movement. -- end of page 16 MLEFT Move pointer left on small screen. MRIGHT Move pointer right on small screen. MON Control routine for scroll screen. OFF Main program loop. TST Final execution word. Initialises paramters and then executes main program loop. THE GAME ITSELF CREATION OF SPRITES Load up the sprite development package and create all the sprites listed in table 1. Once the sprite development package has loaded execute a cold start by pressing the C key and hit Y for yes and then N for the change buffer size prompt. Set the attribute switch to 1 by pressing the A key and then 1. With reference to table 1 set the sprite number to the required value by pressing the S key and then inputting the required value. Input the dimensions (Height and Length) of the sprite by pressing the H or C keys and then inputting the appropriate values. Set the respective Ink, Paper, Flash and Bright values using the X, C, V and B keys. Position the sprite screen X and Y pos cursors to their settings using the SYMBOL SHIFT 5, 6, 7 or 8 keys. Using the direct data input function, key D, input the 8 bytes of data. Move the X and Y pos cursors to the next position and input the data until the sprite is complete on the screen. Set both the X and Y pos cursors to 1 and then GET the sprite into memory by pressing the G key. Clear the sprite screen by pressing SYMBOL SHIFT Q and then create the next sprite. CREATION OF THE 64x3 CHARACTER LANDSCAPE SPRITE This sprite will be made up of 8 8x3 character sprites, which will be joined together into one large sprite in the White Lightning program itself. Set the Ink to 7, the Paper to 0, the Flash to 0 and the Bright to 1, clear the CHR$ square by pressing the Q key. Using the sprites 4,5,6,7,8,9 and the CHR$ square (referred to as 0) build up sprites 10 to 25 as laid out in diagram 1. -- end of page 17 Position the X and Y pos cursors to the appropriate co-ordinates. Input the relevant sprite number and put the sprite to the screen by pressing the D key and then 1. In the case of 0 press J to DUMP the empty CHR$ square to the sprite screen. The landing pad, sprite 3, is also placed in these sprites. Once the 8x3 sprites have been created on the screen, position the X and Y pos cursors to the top left corner of the sprite. Set the sprite number to the appropriate value, set the length to 8 and the height to 3 then press G to get the sprite. Note that the left-hand column of sprite 10 must have a 0 Ink value as well as the right-hand column of sprite 11. Once all the sprites have been created save them off to tape using the SYMBOL SHIFT S key. Load in White Lightning, load in the Lunar sprites. Carefully type in the Lunar Lander program as listed and check your program against the original. It is now best to save your source off to tape. Exit to BASIC using: PROG then save to tape using: SAVE "LUNAR" CODE 52224,6656 Go back into White Lightning using: PRINT USR 24836 Now type 6 LOAD MAKE to create the landscape SET to create the landers To run the program type TST Please note that if there is an error in your source the last few screens can no longer be listed or compiled, since the creation of extra sprites has overwritten the end screens, thus the source would have to be reloaded for editing purposes. PLAYING THE GAME The game itself is more of a simulation than a game. The idea is to land on all four bases without running out of fuel or crashing. The gauges are self-explanatory. The controls are: CAPS SHIFT thrust to the left BREAK SPACE thrust to the right ENTER vertical thrust Once the game is over, hit the ENTER key to escape and then TST for a new game. FUEL If fuel runs out the controls will no longer function. YVEL A safe landing is only made if the VEL gauge registers a velocity in the "safe" region of the centre of the gauge. -- end of page 18 XVEL The horizontal velocity is represented by one of 3 scroll speeds, but safe landings can only be made if the pointer is in the "safe" region in the centre of the gauge. THE SMALL SCREEN This gives a macroscopic view of the 8 screens. The bases are marked. The gauge sometimes cannot keep up with the lander movement, but at scroll speeds of a pixel it will soon "catch up" with the real positions. This is not a "feature" we must admit; keeping up with the gauge slows down the foreground program a lot. -- end of page 19