REM >.!RunImage REM Taito Space Invaders (Circa 1978) Clone REM Keys... REM Z -- Move Left. REM X -- Move Right. REM Enter -- Fire. REM ************************************************************* REM Initialisation Part 1... (Files & Environment.) REM ************************************************************* REM Setup error trapping. ON ERROR REPORT:PRINT" at line ";ERL:END REM Setup our variables. bail%=FALSE :REM TRUE to quit game. scores_save%=TRUE :REM TRUE to save HiScores. DIM scores_name$(10),scores_value%(10) :REM HiScore table. REM Check we have all our files. File HiTable is a special case, REM as we can carry on without it. If it doesn't exist then we try REM to create it with FNcreate_hitable. If that returns FALSE REM (We can't create the file for some reason.) then we change the REM value of a global variable which we check before we try to REM save our Hiscore Table. In the event that we can't create the REM file, scores will be lost when we exit the game. REM Locate files... (Mission critical...) file_err%=0 IF FNfile(".Resources.Gfx0")<>1 THEN file_err%+=1 IF FNfile(".Resources.Gfx1")<>1 THEN file_err%+=1 IF file_err% THEN ERROR 255,"File Missing!!" REM Load files... (Mission critical...) OSCLI"SLoad .Resources.Gfx0" :REM Sprites. OSCLI"SMerge .Resources.Gfx1" :REM Charset. REM HiScore Table... IF FNfile(".Resources.HiTable")<>1 THEN IF NOT FNcreate_hitable THEN scores_save%=FALSE ELSE PROCload_hitable ENDIF REM ************************************************************* REM Initialisation Part 2... (Object offsets.) REM ************************************************************* REM Objects... Each object (Base, saucer, missile etc.) is REM given a block of memory 52 bytes long to hold all relevant REM data for that object. This, apart from being quicker, is far REM easier to code. These are tokens to address offsets in each REM of those blocks of memory. (See tutorial for how this REM works...) Contents for offsets 32-48 (flags1% - flags5%) will REM be different, depending on the nature of the object. I'll REM define LOCAL variables with more meaningful names and point REM them to these in the object's handler routines. REM Object offsets... xpos%=0:ypos%=4:xsize%=8:ysize%=12 :REM Position & Size. dir%=16:speed%=20 :REM Movement. active%=24:beenhit%=28 :REM Object Status. flags1%=32:flags2%=36:flags3%=40 :REM Misc flags. flags4%=44:flags5%=48 REM Define flags usage for screen and rail objects. x_eig%=flags1%:y_eig%=flags2% :REM Offsets in screen obj. setmode%=flags3% toprail%=flags1%:baserail%=flags2% :REM Offsets in rail obj. REM ************************************************************* REM Initialisation Part 3... (Global objects and variables.) REM ************************************************************* REM Reserve memory for global object blocks... DIM screen%52,rail%52 :REM Global objects. REM Get the video display into a VGA 256 colour mode. This is REM also the same MODE as our sprites. Call to PROCget_screeninfo REM calls the OS to get some values and sets up our screen object REM for us. MODE28:OFF :REM 1280*1024 (256 cols.) PROCget_screeninfo :REM Setup screen object. REM Define property values for other global objects. rail%!xsize%=FNconv_units(6,1) :REM Rail properties. rail%!ysize%=FNconv_units(6,2) rail%!toprail%=screen%!ysize%-50 rail%!baserail%=30 REM ************************************************************* REM Main Loop... (Title>Game>Title.) REM ************************************************************* REPEAT CLS:bail%=FNtitle IF NOT bail% THEN CLS:PROCmain UNTIL bail% CLS:PRINT"GoodBye!":END REM ************************************************************* REM Title Loop... (Display our title sequence.) REM ************************************************************* REM Animate in our initial screen layout and then perform a cycle REM between Score Advance & HiScore tables. Do a keyboard scan REM periodically and check for -17 (Q) and -99 (Space Bar) and REM return from function accordingly... Object hwind% points to REM the area of the screen where either the HiScore or Score REM Advance table gets placed in attract mode. This is the area REM below "Invaders" and above "PRESS SPACE TO PLAY" text. Objects REM inv1% - inv3% are identical copies of each other, except for REM xpos & ypos values. DEFFNtitle LOCAL i%,space%,invt%,hwind%,str%,string$,x%,saucer%,inv1%,inv2% LOCAL inv3%,t%,y%,s$ DIM space%52,invt%52,hwind%52 :REM Title sequence objects. DIM saucer%52,inv1%52 REM Initialise LOCAL (title sequence) variables. space%!xsize%=FNconv_units(100,1) :REM Space text properties. space%!ysize%=FNconv_units(40,2) space%!xpos%=FNcentre_sprite(space%,screen%) space%!ypos%=800 invt%!xsize%=FNconv_units(150,1) :REM Invaders text properties. invt%!ysize%=FNconv_units(40,2) invt%!xpos%=FNcentre_sprite(invt%,screen%) invt%!ypos%=space%!ypos%-invt%!ysize% hwind%!xsize%=530:hwind%!ysize%=500:REM Window properties. hwind%!xpos%=FNcentre_sprite(hwind%,screen%) hwind%!ypos%=200 saucer%!xsize%=FNconv_units(40,1) :REM Saucer properties. saucer%!ysize%=FNconv_units(20,2) saucer%!xpos%=hwind%!xpos%+100 saucer%!ypos%=(hwind%!ypos%+hwind%!ysize%)-160 inv1%!xsize%=FNconv_units(20,1) :REM Common invader props. inv1%!ysize%=FNconv_units(20,2) inv1%!xpos%=hwind%!xpos%+100+FNcentre_sprite(inv1%,saucer%) inv2%=FNcopy_object(inv1%) :REM Create two copies. inv3%=FNcopy_object(inv1%) inv1%!ypos%=hwind%!ypos%+100 :REM Unique invader props. inv2%!ypos%=inv1%!ypos%+inv1%!ysize%+40 inv3%!ypos%=inv2%!ypos%+inv2%!ysize%+40 REM Animate the initial screen title "Space Invaders" & "PRESS REM SPACE TO PLAY" messages. PROCdraw_rail(rail%!toprail%):PROCdraw_rail(rail%!baserail%) FOR i%=0-space%!xsize% TO space%!xpos% STEP 5 PROCplot("space",i%,space%!ypos%) NEXT i% FOR i%=screen%!xsize% TO invt%!xpos% STEP -5 PROCplot("invaders",i%,invt%!ypos%) NEXT i% string$="PRESS SPACE TO PLAY" str%=LEN(string$)*FNconv_units(8,1) PROCprint(FNcentre_text(str%,screen%!xsize%),170,string$,10) string$="OR Q TO QUIT" str%=LEN(string$)*FNconv_units(8,1) PROCprint(FNcentre_text(str%,screen%!xsize%),145,string$,10) REM Cycle the remainder of the attract mode. WHILE NOT INKEY(-99) AND NOT INKEY(-17) PROCwipe_sprite(hwind%) REM Display Score Advance table... string$="SCORE ADVANCE" str%=LEN(string$)*FNconv_units(8,1) x%=hwind%!xpos%+FNcentre_text(str%,hwind%!xsize%) PROCprint(x%,(hwind%!ypos%+hwind%!ysize%)-20,string$,0) string$="TABLE" str%=LEN(string$)*FNconv_units(8,1) x%=hwind%!xpos%+FNcentre_text(str%,hwind%!xsize%) PROCprint(x%,(hwind%!ypos%+hwind%!ysize%)-45,string$,0) x%=saucer%!xpos%+saucer%!xsize%+10 PROCplot("saucer_0",saucer%!xpos%,saucer%!ypos%) PROCprint(x%,saucer%!ypos%+10,"= 500 POINTS !",0) PROCplot("inv_3",inv3%!xpos%,inv3%!ypos%) PROCprint(x%,inv3%!ypos%+20, "= 100 POINTS !",0) PROCplot("inv_2",inv2%!xpos%,inv2%!ypos%) PROCprint(x%,inv2%!ypos%+20, "= 75 POINTS !",0) PROCplot("inv_1",inv1%!xpos%,inv1%!ypos%) PROCprint(x%,inv1%!ypos%+20, "= 50 POINTS !",0) REM Wait for 1000 counts or until SPACE or Q pressed... t%=TIME+1000 REPEAT:UNTIL TIME=t% OR INKEY(-17) OR INKEY(-99) IF INKEY(-17) THEN =TRUE :REM Get outta here (Q)... IF INKEY(-99) THEN =FALSE :REM Play a game... REM Display HiScore table... REM First line... (This is conditional on scores_save%) PROCwipe_sprite(hwind%) string$="TODAY'S" IF scores_save% THEN string$="ALL TIME" str%=LEN(string$)*FNconv_units(8,1) x%=hwind%!xpos%+FNcentre_text(str%,hwind%!xsize%) PROCprint(x%,(hwind%!ypos%+hwind%!ysize%)-20,string$,0) REM Second line... string$="HI SCORES" str%=LEN(string$)*FNconv_units(8,1) x%=hwind%!xpos%+FNcentre_text(str%,hwind%!xsize%) PROCprint(x%,(hwind%!ypos%+hwind%!ysize%)-45,string$,0) REM Loop through the HiScore table arrays displaying names and REM scores... y%=(hwind%!ypos%+hwind%!ysize%)-100 FOR i%=1 TO 10 IF i%<10 THEN string$=" "+STR$(i%)+". " ELSE string$=STR$(i%)+". " ENDIF s$=FNstr_pad(FNencode_string(scores_name$(i%)),8," ") string$+=FNstr_upper(s$):string$+=" ... " string$+=FNstr_rightalign(STR$(scores_value%(i%)),6) str%=LEN(string$)*FNconv_units(8,1) x%=hwind%!xpos%+FNcentre_text(str%,hwind%!xsize%) PROCprint(x%,y%-(i%*FNconv_units(15,2)),string$,0) NEXT i% REM Wait for 1000 counts or until SPACE or Q pressed... t%=TIME+1000 REPEAT:UNTIL TIME=t% OR INKEY(-17) OR INKEY(-99) IF INKEY(-17) THEN =TRUE :REM Get outta here (Q)... IF INKEY(-99) THEN =FALSE :REM Play a game... ENDWHILE IF INKEY(-17) THEN =TRUE :REM Get outta here (Q)... =FALSE REM ************************************************************* REM Game Loop... (Play one instance of the game.) REM ************************************************************* REM Loop through one instance of the game. This basically means... REM Setup in-game stuff. -> Play until out of bases. -> Manage REM hiscore entry. -> Return. DEFPROCmain LOCAL base%,shell%,saucer%,evnt%,lives%,str%,string$,x%,y% REM Reserve memory for object blocks & setup pointer tokens. REM (Local objects.) DIM base%52,shell%52,saucer%52 lives%=flags4% :REM Base tokens. REM Initialise LOCAL (in-game) variables. base%!xsize%=FNconv_units(30,1) :REM Base properties. base%!ysize%=FNconv_units(15,2) base%!xpos%=FNcentre_sprite(base%,screen%) base%!ypos%=50:base%!speed%=6:base%!lives%=3 base%!dir%=base%!speed% shell%!xsize%=FNconv_units(5,1) :REM Shell properties. shell%!ysize%=FNconv_units(20,2) shell%!active%=FALSE:shell%!speed%=10 saucer%!xsize%=FNconv_units(40,1) :REM Saucer properties. saucer%!ysize%=FNconv_units(20,2) saucer%!ypos%=rail%!toprail%-(10+saucer%!ysize%) saucer%!speed%=5:saucer%!active%=FALSE PROCinitialise_display :REM Initial layout. REM Our event generator... REPEAT REM Keyboard poll... evnt%=0 IF INKEY(-74) THEN PROCevnt_kb_basefire(base%!xpos%,base%!ypos%) IF INKEY(-98) THEN PROCevnt_kb_baseleft:evnt%=1 IF INKEY(-67) THEN PROCevnt_kb_baseright:evnt%=2 IF INKEY(-82) THEN PROCevnt_kb_screendump IF INKEY(-17) THEN base%!lives%=0 REM Update screen... REM This is our equivalent of an arcade game's VBlank interrupt. REM We've polled the input device (our keyboard) and we're REM idling whilst processing events (evnt%=0). Either way REM something's going on, usually AI/Collision detection REM related. REM Process evnt_kb event updates. (Keyboard.) IF evnt%>0 THEN REM Update base position. (Only left & right.) IF evnt%<=2 THEN base%!xpos%+=base%!dir% PROCplot("base",base%!xpos%,base%!ypos%) ENDIF ENDIF REM Process evnt_fr events. (Frame Refresh.) PROCevnt_fr_basefire PROCevnt_fr_saucer UNTIL base%!lives%=0 string$="GAME OVER!!!":str%=LEN(string$)*FNconv_units(8,1) y%=screen%!ysize%/2 PROCprint(FNcentre_text(str%,screen%!xsize%),y%,string$,10) PROCwait(1000) ENDPROC REM ************************************************************* REM Proceedures (Keyboard events.) REM ************************************************************* REM Move base left. If we're already at left edge then bail... DEFPROCevnt_kb_baseleft REM Collision detection. (Base & Playfield.) IF base%!xpos%<=0 THEN base%!xpos%=0:ENDPROC base%!dir%=0-base%!speed% :REM Change direction. ENDPROC REM Move base right. If we're already at the right edge, bail... DEFPROCevnt_kb_baseright REM Collision detection. (Base & Playfield.) IF base%!xpos%>=screen%!xsize%-base%!xsize% THEN base%!xpos%=screen%!xsize%-base%!xsize%:ENDPROC ELSE base%!dir%=0+base%!speed% :REM Change direction. ENDIF ENDPROC REM Fire a shell. If we're already firing a shell bail. (We can REM only fire one shell at a time or else the animation gets REM confused.) shell_fired% is a global variable that is set REM boolean TRUE when player fires a shell and is cleared when REM either the missile scrolls off the top of the screen or REM hits an invader. DEFPROCevnt_kb_basefire(x%,y%) IF shell%!active% THEN ENDPROC :REM Bail if active. shell%!active%=TRUE :REM Set active. shell%!xpos%=x%+(base%!xsize%/2) :REM Initial position. shell%!ypos%=y%+base%!ysize% ENDPROC REM Debug only... Dump a screen to disc as ".Dump" DEFPROCevnt_kb_screendump ENDPROC REPEAT:UNTIL NOT INKEY(-82) OSCLI"ScreenSave .Dump" ENDPROC REM ************************************************************* REM Proceedures (VBlank/Frame Events) REM ************************************************************* REM Continue animation of a player's shell... If one hasn't been REM created then we bail back to event loop. DEFPROCevnt_fr_basefire LOCAL out%,sh_cpy%:out%=flags1% IF NOT shell%!active% THEN ENDPROC :REM Nothing to do. REM shell%!out% is set TRUE if the current shell is out of play. shell%!out%=FALSE IF shell%!ypos%>=rail%!toprail%-shell%!ysize% THEN shell%!active%=FALSE:shell%!out%=TRUE :REM Hit top. ENDIF REM Decide what to plot. Wipe sprite if we're out of play. IF shell%!out% THEN sh_cpy%=FNcopy_object(shell%) :REM Clear shell. sh_cpy%!ypos%-=2:PROCwipe_sprite(sh_cpy%) ELSE PROCplot("shell",shell%!xpos%,shell%!ypos%) :REM Display shell. ENDIF shell%!ypos%+=shell%!speed% :REM Advance frame. ENDPROC REM Process saucer animation & generation events. A saucer can be REM randomely activated n% of the time, but not if one is REM currently on the screen. When activated, there is a 50% chance REM of it starting on the left hand side of the screen, likewise REM we have a 50% chance of it starting on the right. DEFPROCevnt_fr_saucer LOCAL hit%,timer%,spr% hit%=flags1%:timer%=flags2%:spr%=flags3% :REM Object tokens. IF NOT saucer%!active% THEN IF FNpct(3) AND FNpct(1) THEN saucer%!active%=TRUE:saucer%!beenhit%=FALSE:saucer%!spr%=0 REM Decide which side of the screen to start from. IF FNpct(50) THEN saucer%!dir%=0-saucer%!speed% :REM Right edge. (50%) saucer%!xpos%=screen%!xsize% ELSE saucer%!dir%=0+saucer%!speed% :REM Left edge. (50%) saucer%!xpos%=0-saucer%!xsize% ENDIF ENDIF ELSE saucer%!spr%+=1:saucer%!timer%+=1 REM Has saucer gone off screen?? IF saucer%!xpos%<=0-saucer%!xsize%ANDSGN(saucer%!dir%)=-1 THEN saucer%!hit%=FALSE:saucer%!active%=FALSE:saucer%!timer%=0 ENDPROC ENDIF IF saucer%!xpos%>screen%!xsize%ANDSGN(saucer%!dir%)=1 THEN saucer%!hit%=FALSE:saucer%!active%=FALSE:saucer%!timer%=0 ENDPROC ENDIF REM Has saucer been hit by player shot?? IF NOT saucer%!beenhit% THEN IF FNhit(shell%,saucer%) THEN PROCwipe_sprite(shell%) PROCplot("saucer_2",saucer%!xpos%,saucer%!ypos%) saucer%!hit%=saucer%!timer%:saucer%!beenhit%=TRUE shell%!active%=FALSE ENDIF ELSE IF FNhit(shell%,saucer%) THEN PROCwipe_sprite(shell%):shell%!active%=FALSE ENDIF ENDIF REM Animate saucer... IF NOT saucer%!beenhit% THEN IF saucer%!spr%>=100 THEN saucer%!spr%=0 IF saucer%!spr%<50 THEN PROCplot("saucer_0",saucer%!xpos%,saucer%!ypos%) ELSE PROCplot("saucer_1",saucer%!xpos%,saucer%!ypos%) ENDIF saucer%!xpos%+=saucer%!dir% ELSE REM Animate/Remove explosion... IF saucer%!timer%>=saucer%!hit%+200 THEN PROCwipe_sprite(saucer%) saucer%!active%=FALSE:saucer%!hit%=FALSE saucer%!timer%=0 ELSE IF saucer%!timer%>=saucer%!hit%+175 THEN PROCplot("saucer_5",saucer%!xpos%,saucer%!ypos%) ELSE IF saucer%!timer%>=saucer%!hit%+150 THEN PROCplot("saucer_4",saucer%!xpos%,saucer%!ypos%) ELSE IF saucer%!timer%>=saucer%!hit%+100 THEN PROCplot("saucer_3",saucer%!xpos%,saucer%!ypos%) ENDIF ENDIF ENDIF ENDIF ENDIF ENDIF ENDPROC REM ************************************************************* REM Functions & Proceedures. (HiScore Table/HiScore Entry.) REM ************************************************************* REM Create a clean HiScore table. Return TRUE if we can, else REM return FALSE. Either way, we fill our two arrays with default REM values. DEFFNcreate_hitable LOCAL i%,file_hdl%,n$ FOR i%=1 TO 10 IF i% MOD 2=0 THEN n$="Software" ELSE n$="DynaByte" scores_name$(i%)=FNencode_string(n$) scores_value%(i%)=(11-i%)*1000 NEXT i% file_hdl%=OPENOUT".Resources.HiTable" IF file_hdl%=0 THEN =FALSE FOR i%=1 TO 10 PRINT#file_hdl%,scores_name$(i%),scores_value%(i%) NEXT i% CLOSE#file_hdl% =TRUE REM Load our HiTable from disc. The names are stored in RAM in REM encoded form and are only decoded for display. DEFPROCload_hitable LOCAL i%,file_hdl% file_hdl%=OPENIN".Resources.HiTable" FOR i%=1 TO 10 INPUT#file_hdl%,scores_name$(i%),scores_value%(i%) NEXT i% CLOSE#file_hdl% ENDPROC REM Save our HiTable back to disc. The names are stored in RAM in REM encoded form and are only decoded for display. DEFPROCsave_hitable LOCAL i%,file_hdl% file_hdl%=OPENOUT".Resources.HiTable" FOR i%=1 TO 10 PRINT#file_hdl%,scores_name$(i%),scores_value%(i%) NEXT i% CLOSE#file_hdl% ENDPROC REM ************************************************************* REM Misc. Functions & Proceedures (Game Specific) REM ************************************************************* REM Currently this only displays our base. I'll add the initial REM positions of the invaders et al over time. Hence the reason REM for this routine. DEFPROCinitialise_display PROCdraw_rail(rail%!toprail%):PROCdraw_rail(rail%!baserail%) PROCplot("base",base%!xpos%,base%!ypos%) :REM Display our base. ENDPROC REM Repeat a 6*6 image across the screen to produce a "rail" like REM effect at position y%. DEFPROCdraw_rail(ypos%) LOCAL i%,reps% reps%=screen%!xsize%/rail%!xsize% FOR i%=1 TO reps%+1 PROCplot("rail",(i%-1)*rail%!xsize%,ypos%) NEXT i% ENDPROC REM Get various information about screen resolution. This includes REM viewing area and conversion factors. We cannot rely on fixed REM values because they change from mode to mode. So grab the REM current values from the OS and bung 'em into some variables. DEFPROCget_screeninfo LOCAL blk% DIM blk% 20 :REM Reserve space for parameter block. REM Setup parameter block... blk%!0=4 :REM XEigFactor (Convert.) blk%!4=5 :REM YEigFactor (Convert.) blk%!8=11 :REM XWindLimit (Width.) blk%!12=12 :REM YWindLimit (Height.) blk%!16=-1 :REM Termination byte. SYS "OS_ReadVduVariables", blk%, blk%:REM Call OS. REM Setup our screen% object... screen%!x_eig%=blk%!0 :REM X & Y EigFactor. screen%!y_eig%=blk%!4 screen%!xsize%=(blk%!8)+1< OS Units) REM 2 = Height (Pixels > OS Units) REM 3 = Width (OS Units > Pixels) REM 4 = Height (OS Units > Pixels) REM Returns REM conv% = Converted value. DEFFNconv_units(size%,op%) LOCAL conv%,mode% REM Check to see if the screen mode has changed. If so, then REM call PROCget_screeninfo to update the values. This is REM probably overkill and can be omitted, but still good practice REM for debugging. mode%=MODE IF mode%<>screen%!setmode% THEN PROCget_screeninfo REM Do the conversion. CASE op% OF WHEN 1 : conv%=size%<>screen%!x_eig% WHEN 4 : conv%=size%>>screen%!y_eig% OTHERWISE ERROR 255,"Unknown FNconv_units() operation : "+STR$(op%) ENDCASE =conv% REM Display a string character by character with a delay between REM each one. Each character is indexed to the 8*8 character set REM sprites using ASCII codes. DEFPROCprint(x%,y%,string$,char_delay%) LOCAL i%,chsize% chsize%=FNconv_units(8,1) :REM Convert 8*8px to OS Units. FOR i%=1 TO LEN(string$) PROCplot(STR$(ASC(MID$(string$,i%,1))),x%+((i%-1)*chsize%),y%) IF char_delay%>0 THEN PROCwait(char_delay%) NEXT i% ENDPROC REM Collision detection. Takes two objects and returns TRUE if REM one is inside or has hit the other, else returns FALSE. Vars REM tx_ext% & ty_ext% hold the two far edges of the sprite. We REM calculate these by adding the target object's size to it's REM position each time we're called. We then use these values to REM perform a bounds check, one for x (ix% is TRUE if in.) and REM one for y (iy% is TRUE if in.). Finally... We return an REM evaluation of ix% AND iy%. DEFFNhit(obj%,targ%) LOCAL tx_ext%,ty_ext%,ix%,iy% ix%=FALSE:iy%=FALSE IF obj%!active% AND targ%!active% THEN tx_ext%=targ%!xpos%+targ%!xsize% :REM Right hand edge. ty_ext%=targ%!ypos%+targ%!ysize% :REM Top edge. IF obj%!xpos%>=targ%!xpos% AND obj%!xpos%<=tx_ext% THEN ix%=TRUE IF obj%!ypos%>=targ%!ypos% AND obj%!ypos%<=ty_ext% THEN iy%=TRUE ENDIF =ix% AND iy% REM Create a copy of an object and return it's address. DEFFNcopy_object(obj%) LOCAL newobj% DIM newobj%52 newobj%!xpos%=obj%!xpos% :REM Position. newobj%!ypos%=obj%!ypos% newobj%!xsize%=obj%!xsize% :REM Size. newobj%!ysize%=obj%!ysize% newobj%!dir%=obj%!dir% :REM Movement. newobj%!speed%=obj%!speed% newobj%!active%=obj%!active% :REM Status. newobj%!beenhit%=obj%!beenhit% newobj%!flags1%=obj%!flags1% :REM Misc Flags. newobj%!flags2%=obj%!flags2%:newobj%!flags3%=obj%!flags3% newobj%!flags4%=obj%!flags4%:newobj%!flags5%=obj%!flags5% =newobj% REM Clear a sprite from the screen. (New method.) REM Before we created an identical bitmap in the sprite file REM and displayed this over our image. This should be faster, and REM result in a smaller sprite file. DEFPROCwipe_sprite(obj%) GCOL0,0 TINT0 RECTANGLE FILLobj%!xpos%,obj%!ypos%,obj%!xsize%,obj%!ysize% ENDPROC REM Centre text horizontally... REM Where string_length% is the length of the string in multiples REM of OS Units, where each multiple is the width of a character. REM Var screen% is the screen width. REM width... DEFFNcentre_text(string_length%,screen%) =(screen%/2)-(string_length%/2) REM Centre sprite horizontally... REM Takes the two "object" blocks as parameters and extracts the REM required values from them. Then returns the centre position. DEFFNcentre_sprite(object%,relative%) =(relative%!xsize%/2)-(object%!xsize%/2) REM ************************************************************* REM Functions & Proceedures (Generic & Portable Code) REM ************************************************************* REM Display a given image. REM sprite$ = Name of image to display. REM x% = X Co-ordinate. REM y% = Y Co-ordinate. DEFPROCplot(sprite$,x%,y%) OSCLI"SChoose "+sprite$ PLOT &ED,x%,y% ENDPROC REM Returns TRUE n% percent of the time. DEFFNpct(n%) LOCAL rnd% rnd%=RND(100) =rnd%<=n% REM Check for the prescence of a filing system object at a given REM location. I say object, as this can check for the existance of REM directories as well as files. REM Parameter block... REM f$ = Filespec to check for in current directory. REM ...On Exit. REM f% = 0 Object not found. REM = 1 Object is a file. REM = 2 Object is a directory. DEFFNfile(f$) LOCAL f% SYS"XOS_File",17,f$ TO f% =f% REM Encode or decode a string using a simple XOR algo... This REM prevents hackers from peeking at and modifying strings in REM files. Strings are in reverse order in BASIC datafiles, but REM are still in plaintext format. REM string$ = String to mash up. REM Returns REM mashed$ = Mashed up string. DEFFNencode_string(string$) LOCAL i%,mashed$ FOR i%=1 TO LEN(string$) mashed$+=CHR$(ASC(MID$(string$,i%,1)) EOR 131) NEXT i% =mashed$ REM Introduce a delay of n% hundredths of a second. DEFPROCwait(n%) LOCAL t% t%=TIME+n% REPEAT:UNTIL TIME>=t% ENDPROC REM Convert supplied string to uppercase. DEFFNstr_upper(in$) LOCAL i%,out$,c% out$="" FOR i%=1 TO LEN(in$) c%=ASC(MID$(in$,i%,1)) IF c%>=97 AND c%<=122 THEN out$=out$+CHR$(c%-32) ELSE out$=out$+CHR$(c%) ENDIF NEXT i% =out$ REM Pad a string to len% characters by adding multiple copies of REM char$ to the end of it. If longer than len% characters, then REM chop it to len% characters. DEFFNstr_pad(string$,len%,char$) LOCAL diff%,out$ diff%=len%-LEN(string$) :REM How many to add. IF diff%<=0 THEN out$=LEFT$(string$,len%) ELSE out$=string$+STRING$(diff%,char$) ENDIF =out$ REM Fix a string's length to len% characters and return it right REM aligned... DEFFNstr_rightalign(string$,len%) LOCAL out$ out$=STRING$(len%," ") RIGHT$(out$,LEN(string$))=string$ =out$