Stellar Fighter is a space combat game in which the player pilots a fighter craft across a cockpit-display screen, maneuvering to intercept and destroy waves of alien ships using phasor fire. The game uses User Defined Graphics (UDGs) to render several distinct alien sprite designs, swapping between them by POKEing bitmap data into UDG memory as the score advances. A machine-code-free but UDG-heavy approach drives the visual variety: five separate enemy sprite sets (each three characters wide) are loaded at runtime via RESTORE/READ/POKE loops into UDGs \\a–\\u. Game mechanics track phasor energy (PHE), hull damage (DAM%), alien craft destroyed (ACD), and docking count, with difficulty scaling upward after each completed wave by reducing the enemy fire-rate thresholds.
Program Structure
The program is organized into well-separated functional blocks:
- Lines 1–19: Initialization — intro screen, title music (BEEP sequence via DATA at line 12), lift-off animation, and UDG setup via
GO SUB 8000. - Lines 20–26: Cockpit HUD drawing using PLOT/DRAW graphics and PRINT for instrument labels.
- Lines 590–655: Main game loop — enemy movement, player input polling, display of enemy sprite.
- Lines 700–840: Enemy attack resolution, damage accumulation, explosion animation, and end-condition checks.
- Lines 6000–6143: Laser firing routine — draws laser beams with looped PLOT/DRAW, checks hit via
ATTR, calls explosion subroutine, increments score. - Lines 7000–7045: Explosion animation subroutine using expanding UDG characters in all eight directions.
- Lines 8000–8052: UDG definition routines — five enemy sprite sets loaded from DATA.
- Lines 8500–8650: Docking sequence — animated docking bar, energy/damage reset after three docks.
- Lines 9300–9430: Player-destroyed sequence with “Heaven” screen, music, and replay prompt.
- Lines 9500–9530: Intro radio-message routine, printed character by character with BEEP.
- Lines 9600–9640: Mission-complete handler — resets stats, increases difficulty, loops to next wave.
- Lines 9990–9998: Abort and SAVE/CLEAR.
UDG Sprite System
Five distinct enemy sprite designs are defined across UDGs \\a–\\u (characters 144–164). Each design occupies four UDG characters arranged as a 1×3 display string plus a three-character wide “damaged” variant. Loading is done by FOR F / FOR G / POKE USR CHR$ (144+f)+g, a loops reading from DATA statements, with RESTORE pointing to the correct DATA block before each load.
| Subroutine | UDGs used | A$ (normal) | C$ (damaged) | Trigger score |
|---|---|---|---|---|
| 8000 | \\a–\\d (144–147) | " \\a " | "\\b\\c\\d" | Start |
| 8015 | \\f–\\i (149–152) | " \\f " | "\\g\\h\\i" | SC=RA (random 4–11) |
| 8025 | \\k–\\m (153–155) | " ,J, " | "\\k\\l\\m" | SC=RAN (random 13–25) |
| 8035 | \\n–\\q (157–160) | " \\n " | "\\o\\p\\q" | SC=GOG (random 27–52) |
| 8045 | \\r–\\u (161–164) | " \\r " | "\\s\\t\\u" | Docking |
Note that the subroutine at line 8035 has a bug: the inner POKE loop reads a without a preceding READ a statement, so it will POKE whatever value a holds from the last assignment rather than the DATA values. The corresponding DATA at lines 8037–8040 is never consumed by this routine, meaning the fourth enemy sprite set will display incorrectly or be identical to the previous UDG state.
A similar bug exists at line 8045: POKE USR CHR$ (161+f)+g,a again lacks READ a, so DATA lines 8047–8051 are also never read by their intended loop.
Main Game Loop Logic
The main loop runs from approximately line 620 to 1000. Enemy position is stored in D (row) and A (column). Lines 620–630 apply random drift to both axes. Lines 641–644 read INKEY$ for the five control keys (5/6/7/8/1) with boundary checks. The counter C is incremented each iteration; it is compared to thresholds FO, FOR, and FORT to determine which sprite variant to display and when the enemy fires back. This creates a timed attack cycle without any explicit timing loop.DA
| Variable | Initial value | Meaning |
|---|---|---|
FO | 10 | Threshold for damaged sprite display |
FOR | 20 | Threshold for second damaged sprite |
FORT | 30 | Threshold for enemy fire |
RF | 0 | Enemy fire-event counter |
| Random damage per hit (15–44) |
After each completed wave of 43 kills (SC=43, line 6136), the thresholds FO, FOR, FORT are each decremented by 5 at line 9605, making enemies attack faster. A floor check at line 9611 prevents them from dropping below 3/2/4 respectively.
Hit Detection
Hit detection uses ATTR (10,16) at line 6130 to check whether the screen cell at the center crosshair position has the enemy’s ink color (6 = yellow). This is a compact and idiomatic attribute-based collision method that avoids any coordinate arithmetic. If the attribute matches, the explosion subroutine at line 7000 is called.
Laser and Explosion Effects
The laser is drawn in a loop (lines 6105–6125) that plots two converging lines from the cockpit sides toward the center, then redraws them in INK 0 to erase. The explosion routine at line 7000 prints the UDG \\e (char 148) at the hit point and then in expanding rings of eight positions using AT 10±P, 15±P patterns, cycling through decreasing INK values for a flash effect. The COX counter repeats the flash twice before returning.
Docking Sequence
When the player presses “1”, line 8500 checks the color of the cockpit bay area using ATTR (20,17). If the bay is showing the correct color (5 = cyan), docking proceeds. After four dock events (DOCK=4), an animated docking bar crawls across row 10 using a CP pointer incremented with a PAUSE delay. Energy and damage are reset at line 8650 via GO SUB 8001 then GO TO 598. Only three docks are permitted per mission (counter checked at line 8503).
Notable BASIC Idioms and Techniques
- The intro radio message at line 9500 prints each character individually with a BEEP to simulate a teletype/radio effect:
FOR B=1 TO LEN I$: BEEP .05,3: PRINT I$(B);: NEXT B. RESTORE 6at line 11 re-reads the program’s own DATA from line 12 for the title music — though the DATA is actually at line 12, not line 6. This points to a non-existent line, so RESTORE will find the first DATA statement it can reach, which is line 12. This is intentional and works correctly.- The “Heaven” end screen at line 9412 draws a pixel-art figure using block-graphic escapes (
\\ \\etc.) combined with OVER 1 printing at line 9412 for overlay effects. - Line 8001 is referenced as a subroutine label via
GO SUB 8001(lines 8610, 9640) but is not a subroutine — it contains only LET statements and falls through. This functions as an inline reset ofA$andC$to the default enemy type, relying on the calling code to subsequently GO TO an appropriate line rather than RETURN. - The variable name
FOR(line 591) shadows the BASIC keywordFOR. This is valid in Sinclair BASIC since variable names can match keywords when used in an assignment context, but it is unusual and could cause confusion.
Anomalies and Bugs
- Lines 8035 and 8045 are missing
READ ainside their POKE loops (see UDG section above), leaving those two enemy sprite sets non-functional. LET B$is never defined anywhere in the program, so line 651 (PRINT AT D,A;B$) will cause a variable-not-found error if reached. TheFORthreshold comparison logic at line 649 may prevent this in practice ifCalways exceedsFORbefore reaching line 651, but the variable is nonetheless undefined.
Source Code
1 REM FROM LIST LIBRARY TAPE V1.7
5 BORDER 0:PAPER 6:INK 0:CLS :GO SUB 9500:GO TO 7
7 PAPER 7:INK 1:BORDER 1:CLS :PRINT AT 3,8;"STELLAR FIGHTER":PRINT AT 4,8;"\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''":REM STOP
8 PRINT INK 2; AT 7,8;"COCKPIT CONTROLS"; INK 0; AT 9,11;"5=LEFT"; AT 11,11;"8=RIGHT"; AT 13,11;"6=DIVE"; AT 15,11;"7=CLIMB"
9 PRINT INK 0:PRINT AT 17,11;"1=DOCK":PRINT AT 19,11;"0=FIRE"
11 RESTORE 6:FOR N=1 TO 19:READ C,D:BEEP C,D:NEXT N:PAUSE 500
12 DATA .15,7,.15,7,.15,7,.9,0,.9,7,.15,5,.15,4,.15,2,.9,12,.45,7,.15,5,.15,4,.15,2,.9,12,.45,7,.15,5,.15,4,.15,5,1,2
13 BORDER 0:PAPER 1:INK 7:CLS
14 PRINT AT 5,14;"WE"; AT 7,13;"HAVE"; AT 9,11;"LIFT OFF"
15 FOR G=1 TO 20:FOR F=0 TO 7:BORDER F:BEEP .02,F:NEXT F:NEXT G
18 GO SUB 8000
19 INK 5:PAPER 0:BORDER 0:CLS
20 PLOT 0,40:DRAW 70,0:DRAW 20,-20:DRAW 75,0:DRAW 20,20:DRAW 70,0
21 PLOT 2,38:DRAW 68,0:DRAW 20,-20:DRAW 75,0:DRAW 20,20:DRAW 68,0
22 PLOT 2,38:DRAW 0,-36:DRAW 251,0:DRAW 0,36:PLOT 90,18:DRAW 0,-16:PLOT 165,18:DRAW 0,-16
25 PRINT INK 7:PRINT AT 18,23;"ACD":PRINT AT 20,23;"CI":PRINT AT 18,1;"DAM%"
26 PRINT INK 7:PRINT AT 20,1;"PHE":PRINT AT 20,12;"CODE"
590 LET DOCK=0:LET GOG= INT (RND*26)+27:LET RAN= INT (RND*13)+13:LET RF=0:LET C=0:LET SC=0:LET RA= INT (RND*8)+4:LET ACD=0
591 LET FO=10:LET FOR=20:LET FORT=30
598 LET PH=1000:LET DAM=0
599 PRINT INK 5:PRINT AT 20,17;"\ \ "
600 LET DA= INT (RND*30)+15
610 LET D= INT (RND*14):LET A= INT (RND*27)
620 LET R1= INT (RND+.5):IF R1=0 AND D>4 THEN LET D=D-1:IF R1=1 AND D<17 THEN LET D=D+1
630 LET R2= INT (RND+.5):IF R2=0 AND A>2 THEN LET A=A-1:IF R2=1 AND A<16 THEN LET A=A+1
641 IF INKEY$="8" AND A<17 THEN LET A=A+3
642 IF INKEY$="7" AND D>3 THEN LET D=D-3
643 IF INKEY$="6" AND D<13 THEN LET D=D+3
644 IF INKEY$="5" AND A>2 THEN LET A=A-3
645 IF INKEY$="1" THEN GO TO 8500
647 IF INKEY$=" " THEN GO TO 9995
649 LET C=C+1:IF C>FO THEN GO TO 652:IF C >=FOR THEN GO TO 651
650 PRINT INK 6; AT D,A;A$:GO TO 654
651 PRINT AT D,A;B$:GO TO 655
652 PRINT INK 6; AT D,A;C$
655 IF INKEY$="0" THEN LET PH=PH-10:PRINT INK 7; AT 20,5;PH:GO TO 6000
700 FOR B=-20 TO -15:BEEP .005,B:NEXT B
800 LET RF=RF+1
801 PRINT INK 6; AT 20,26;C$
805 PRINT INK 7:PRINT AT 9,16;"!":PRINT AT 11,16;"!":PRINT AT 10,15;"-":PRINT AT 10,17;"-"
810 IF RF<FORT THEN GO TO 900
812 FOR X=4 TO 0 STEP -4
813 PLOT A*8+10,175-D*8:DRAW INK X;40,-36
814 FOR I=0 TO 10:BEEP .05,3:NEXT I:BEEP .05,10:BEEP .05,12
815 NEXT X
816 FOR X=7 TO 0 STEP -1:BEEP .005, INT (RND*10):BORDER X:NEXT X
819 LET RF=0:LET C=0:LET DAM=DAM
820 LET DAM=DAM+DA:PRINT INK 7:PRINT AT 18,6;DAM
825 IF DAM>60 AND DAM<100 THEN GO TO 9300
830 IF DAM >=99 THEN GO TO 9400
840 PRINT AT D,A;" ":GO TO 600
900 PRINT AT D,A;" "
1000 GO TO 620
5000 REM FIRING LASER
6000 REM SHOOT
6001 IF PH<100 AND PH>0 THEN PRINT AT 20,7;" "
6010 IF PH <=-100 THEN GO TO 9300
6105 FOR N=3 TO 0 STEP -3
6110 PLOT INK 0;7,48:DRAW INK N;119,41
6120 PLOT INK 0;248,48:DRAW INK N;-110,40
6125 NEXT N
6127 FOR Q=1 TO 10:BEEP .01,1+F:BEEP .01,10:NEXT Q
6130 IF ATTR (10,16)=6 THEN GO TO 6134
6133 GO TO 656
6134 GO SUB 7000
6135 LET ACD=ACD+1:LET SC=SC+1:LET RF=0:PRINT INK 7:PRINT AT 18,27;ACD
6136 IF SC=43 THEN GO TO 9600
6139 LET C=0:IF SC=RA THEN GO SUB 8015
6140 IF SC=RAN THEN GO SUB 8025
6141 IF SC=GOG THEN GO SUB 8035
6143 GO TO 600
7000 LET Z$="\e"
7010 FOR N=6 TO 0 STEP -6
7011 LET COX=0
7015 PRINT INK N; BRIGHT 1; AT 10,15;Z$
7020 FOR P=1 TO 4
7030 PRINT INK N; AT 10+P,15+P;Z$; AT 10-P,15-P;Z$; AT 10+P,15-P;Z$; AT 10-P,15+P;Z$; AT 10,15+P;Z$; AT 10,15-P;Z$; AT 10+P,15;Z$; AT 10-P,15;Z$
7035 BEEP .0025, INT (RND*20)
7040 NEXT P:NEXT N
7041 LET COX=COX+1
7042 IF COX=2 THEN GO TO 7045
7043 FOR N=3 TO 0 STEP -3
7044 GO TO 7015
7045 BRIGHT 0:RETURN
8000 FOR F=0 TO 4:FOR G=0 TO 7:READ A:POKE USR CHR$ (144+f)+g,a:NEXT G:NEXT F
8001 LET A$= " \a ":LET C$="\b\c\d"
8005 DATA 0,0,36,195,255,219,0,0
8006 DATA 96,241,250,252,255,255,241,96
8007 DATA 129,24,60,126,255,255,255,90
8008 DATA 6,143,95,63,255,255,143,6
8009 DATA BIN 01000000,0, BIN 01100010, BIN 00100001,0, BIN 00011000,1, BIN 10000101
8010 RETURN
8015 RESTORE 8020:FOR F=0 TO 3:FOR G=0 TO 7:READ a:POKE USR CHR$ (149+f)+g,a:NEXT G:NEXT F
8016 LET A$=" \f ":LET C$="\g\h\i"
8020 DATA 0,129,66,60,60,66,129,0
8021 DATA 192,48,12,3,3,12,48,192
8022 DATA 0,126,195,231,255,126,0,0
8023 DATA 3,12,48,192,192,48,12,3
8024 RETURN
8025 RESTORE 8027:FOR F=0 TO 3:FOR G=0 TO 7:READ A:POKE USR CHR$ (153+f)+g,a:NEXT G:NEXT F
8026 LET A$=" ,J, ":LET C$="\k\l\m"
8027 DATA 0,0,126,219,255,126,0,0
8028 DATA 3,15,62,252,63,15,3,0
8029 DATA 255,255,126,60,255,255,255,165
8030 DATA 192,240,124,63,252,240,192,0
8031 RETURN
8035 RESTORE 8037:FOR F=0 TO 3:FOR G=0 TO 7:POKE USR CHR$ (157+f)+g,a:NEXT G:NEXT F
8036 LET A$=" \n ":LET C$="\o\p\q"
8037 DATA 0,66,153,255,153,66,0
8038 DATA 96,64,193,255,255,193,64,96
8039 DATA 0,255,231,219,219,231,255,0
8040 DATA 6,2,131,255,255,131,2,6
8041 RETURN
8045 RESTORE 8047:FOR F=0 TO 3:FOR G=0 TO 7:POKE USR CHR$ (161+f)+g,a:NEXT G:NEXT F
8046 LET A$=" \r ":LET C$="\s\t\u"
8047 DATA 0,0,219,255,255,219,0,0
8048 DATA 0,112,250,142,255,254,250,112
8050 DATA 0,24,60,126,195,126,60,24
8051 DATA 0,14,95,113,255,127,95,14
8052 RETURN
8500 IF ATTR (20,17)=5 THEN GO TO 610:GO TO 8501
8501 GO SUB 8045:GO TO 8502
8502 LET DOCK=DOCK+1
8503 IF DOCK <>4 THEN GO SUB 8035:GO TO 610
8506 PRINT INK 6; FLASH 1; AT 20,12;"DOCKING"
8507 LET CP=1
8508 PRINT BRIGHT 1:INK 4:PRINT AT 10,CP;C$
8509 LET CP=CP+1
8510 IF CP=16 THEN GO TO 8590
8511 PAUSE 25
8512 PRINT AT 10,CP-1;" "
8513 GO TO 8508
8591 FOR N=7 TO 0 STEP -7
8592 PLOT INK 0;248,48:DRAW INK N;119,40
8594 BEEP 1,-10:BEEP 1,10:BEEP 1,-10
8595 NEXT N
8599 PRINT AT 10,14;"( )"
8600 PRINT INK 5; AT 20,17;"\ \ "; INK 7; AT 20,12;"CODE "
8605 PRINT AT 18,6;" "
8606 PRINT AT 20,5;" "
8610 GO SUB 8001
8650 GO TO 598
9300 PRINT INK 2:PRINT AT 20,17;"\ \ "
9350 GO TO 700
9400 PRINT AT D,A;" ":BORDER 0
9401 FOR G=1 TO 3:FOR F=0 TO 7:BORDER F:BEEP .05,F+G-1:NEXT F:NEXT G
9405 BORDER 0:PAPER 7:INK 1:CLS
9410 PRINT AT 7,10;"S O T H I S"; AT 9,10;"M U S T B E"; AT 11,10;"H E A V E N"
9411 PRINT TAB 10;"\''\''\''\''\''\''\''\''\''\''\''"
9412 PRINT INK 0; AT 15,15;"\ \ "; AT 16,15;"\ \ "; AT 17,13;"\ \ \ \ \ \ "; AT 18,15;"\ \ "; AT 19,15;"\ \ "; AT 20,15;"\ \ "; AT 21,14;"\.'\ \ \'.":PRINT OVER 1; AT 21,14;\{20}\{0}"\.: \:."
9420 RESTORE 9425:FOR N=0 TO 35:READ C,D:BEEP C,D:NEXT N
9425 DATA 1,0,1,2,.5,3,.5,2,1,0,1,0,1,2,.5,3,.5,2,1,0,1,3,1,5,2,7,1,3,1,5,2,7,.75,7,.25,8,.5,7,.5,5,.5,3,.5,2,1,0,.75,7,.25,8,.5,7,.5,5,.5,3,.5,2,1,0,1,0,1,-5,2,0,1,0,1,-5,2,0
9426 CLS :PRINT "O.K.TRAINEE DO YOU WANT ANOTHER TRY AT STAYING ALIVE??? PUSH 'N' TO STOP PROGRAM OR 'Y' TO RUN AGAIN"
9427 PAUSE 0:IF INKEY$="N" THEN GO TO 9998
9428 PRINT FLASH 1:PRINT AT 16,11;"BRAVE LAD"
9429 FOR F=1 TO 30:LET RS= INT (RND*50):BEEP .05,RS:NEXT F
9430 PAUSE 100:CLS :RUN
9500 LET I$="THIS IS BASE CALLING FIGHTER.... YOUR MISSION IS TO ESCORT A CONVOY OF CRAFT ACROSS THE GALAXY. IT IS UNLIKELY YOU WILL SURVIVE THOUGH. YOU MAY DOCK FOR REPAIRS ONLY 3 TIMES. "
9505 LET I$=I$+" READ -OUTS *DAM%=DAMAGE *PHE =PHASOR ENERGY *ACD =ALIEN CRAFT DESTROYED *CI =CRAFT IDENTITY THIS IS BASE----OVER AND OUT"
9510 PRINT " RADIO MESSAGE "
9511 PRINT " ++++++++++++++ "
9520 FOR B=1 TO LEN I$:BEEP .005,3:PRINT I$(B);:NEXT B
9530 PAUSE 0:RETURN
9600 PRINT AT D,A;" "
9601 PRINT FLASH 1:PRINT AT 0,0;" MISSION COMPLETE ":PAUSE 300
9605 LET SC=0:LET DAM=0:LET PH=1000:LET DOCK=0:LET FO=FO-5:LET FOR=FOR-5:LET FORT=FORT-5
9610 PAUSE 100:PRINT FLASH 0; AT 0,0;" "
9611 IF FOR<2 THEN LET FOR=2:IF FO<3 THEN LET FO=3:IF FORT<4 THEN LET FORT=4
9615 PRINT AT 0,0;" RETURN HOME TO FLY AGAIN"
9616 PAUSE 200:PRINT AT 0,0;"(32*SP)"
9620 PRINT AT 18,6;" ":PRINT AT 20,5;" "
9630 FOR F=1 TO 30:BEEP .05,20:BEEP .05,0:NEXT F
9640 GO SUB 8001:GO TO 598
9990 PRINT FLASH 1:PRINT AT 0,0;" MISSION ABORTED ":PAUSE 50
9995 PAUSE 50:RUN
9997 CLEAR :SAVE "STELRF.B1" LINE 1
9998 CLEAR
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
