Bomber

Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Game

Bomber is a horizontally-scrolling arcade game in which the player pilots an aircraft across a cityscape, dropping bombs onto buildings. The aircraft moves row by row down the screen while the player presses “Z” to release a single bomb per pass; a one-time altitude boost with “K” adds a tactical element. Custom UDGs are defined in the subroutine at line 1500 to render the plane, bomb, explosion, and building-top graphics. Three separate high-score tables are maintained across three difficulty levels, each accepting up to 13 characters of player name via INPUT. The city skyline is procedurally generated using randomized building heights biased by the chosen difficulty offset variable L.


Program Structure

The program is organized into a main loop and several distinct subroutines accessed via GO SUB:

  • Lines 5–170: Initialization, title screen, intro music, and the core gameplay loop.
  • Lines 1000–1020: City generation subroutine — draws randomized buildings and ground.
  • Lines 1100–1189: High-score and controls display screen.
  • Lines 1200–1250: Initializes the three-slot high-score name array Q$ and zeroes scores.
  • Lines 1500–2040: UDG (User Defined Graphic) definition block, loading pixel data for all custom characters.
  • Lines 3500–3570: Level selection screen (three difficulty tiers).
  • Lines 4010–4499: Crash/death handler — records high scores, offers replay.
  • Lines 4500–4560: Mission complete handler — records high scores, offers replay.

Initialization and Variable Layout

Line 11 sets up most game state in a single long LET chain: plane row H, bomb height BH, bomb trigger flag t, bomb column w, score SC, three high-score accumulators SCH1/SCH2/SCH3, level offset L, difficulty value D, lives indicator LI, loop variable j, and level number LE. Line 5 uses POKE 23658,8 to enable CAPS LOCK, ensuring uppercase key reads work reliably without holding CAPS SHIFT.

UDG Definitions (Lines 1500–2040)

Six UDGs are defined by poking pixel data into the UDG area using POKE USR "X"+Z,A loops with inline DATA and RESTORE:

UDGPurpose
ABuilding block (solid rectangle)
BAirplane body
CAirplane nose/tail
DBomb/explosion marker
eLeft building-top slope
fRight building-top slope
GRandomized texture block (each row is the same random byte)

Note that line 1545 uses RESTORE 1550 but is immediately followed by the FOR loop for UDG “e” at line 1550, so the extra RESTORE on line 1545 is redundant. The UDG “G” definition at line 1580 reads a single random value D from a DATA D statement, but since D was computed once before the loop, all eight rows of the UDG receive the same byte — producing a uniform random texture per game session.

Core Gameplay Loop (Lines 110–170)

The main loop iterates column Q from 0 to 31, scrolling the plane across each row. Key mechanics within the loop:

  • Plane display: Line 120 prints the plane UDGs (\b\c) at position (H,Q) with a brief BEEP simulating engine noise.
  • Bomb drop: Line 140 checks for “Z” keypress; line 141 advances the bomb vertically each column iteration by incrementing BH.
  • Bomb hit detection: Line 142 uses ATTR to check whether the cell below the bomb has attribute value 1 (INK 1, PAPER 0 — building top) or 40 (PAPER 5, INK 0 — building body, since 40 = 5×8+0). A hit triggers a flash explosion animation, resets the bomb state, and awards 5 points.
  • Bomb ground impact: Line 144 catches the bomb reaching row 20 without hitting a building.
  • Collision detection: Line 145 checks ATTR(H, Q+2) for building attributes ahead of the plane; a collision triggers the scrolling destruction animation and branches to line 4000.
  • Altitude boost: Line 149 allows a single upward move with “K” when J=0 and H>3, setting J=1 to prevent reuse.
  • Mission complete: Line 148 checks for the specific coordinate H=20, Q=15 as a win condition, branching to line 4500.

After each full column sweep, line 160 increments H, moving the plane one row lower, and the loop restarts from column 0.

City Generation (Lines 1000–1020)

Buildings are drawn in pairs of columns stepping by 2 from column 2 to 31. Building height is randomized as INT(RND*8+10+L), where L is the difficulty offset (-3 for hard, 2 for medium, 4 for easy), biasing buildings taller at harder difficulties. Each building cell prints UDG \a (solid block) in INK 0 on PAPER 5; the building top row prints UDGs \e\f (slope characters) in INK 1.

Difficulty and High-Score System

Line 3500 presents three difficulty choices. The selected level sets L (city height bias) and LE (level index 1–3). Three independent high scores (SCH1, SCH2, SCH3) and player name slots (Q$(1), Q$(2), Q$(3)) are maintained for each level. At game-over or mission complete, lines 4041–4043 (or 4522–4526) compare the current score against the stored high score for the active level and prompt for a name if a new record is set. The name array is dimensioned at line 1200 with DIM Q$(Z,13) inside a loop, which reinitializes the array each pass — only the final iteration (Z=3) survives, correctly creating a 3×13 string array.

Notable Techniques and Anomalies

  • The intro music loop (lines 40–70) uses RESTORE 60 and reads DATA from line 60 twice via nested NEXT N/NEXT I — note that line 60 is actually a NEXT I statement, not a DATA line; the DATA is at line 70. RESTORE 60 will therefore position the data pointer before line 70’s DATA, which works correctly since RESTORE seeks to the nearest DATA at or after the given line.
  • The crash animation at line 145 uses OVER 1 with TAB 31 inside a nested loop over all rows and all INK colors, producing a full-screen XOR stripe effect.
  • The high-score display subroutine (lines 1100–1189) uses a character-by-character typing effect: it prints each letter of a string individually with a short BEEP, then re-enters the loop with a new string via GO TO 1110, exploiting LEN A$ checks to sequence through multiple text sections.
  • Line 142 prints a “blank” at AT bh-1,w after the explosion, which may erase part of the plane graphic if the bomb detonates very close to the aircraft row.
  • The win condition at line 148 (H=20 AND Q=15) is a fixed coordinate trigger rather than a completion-of-all-buildings check, meaning the game ends at a specific point in the plane’s descent regardless of how many buildings remain.
  • “COMPLEATED” (line 4510) and “CONTROLES” (line 1175) are spelling errors in the string literals.

Image Gallery

Source Code

    5 POKE 23658,8
   11 LET H=3: LET BH=h: LET t=0: LET w=0: LET sc=0: LET SCH1=0: LET SCH2=0: LET SCH3=0: LET L=0: LET D=INT (RND*8+1): LET LI=3: LET j=0: LET LE=0
   12 GO SUB 1200
   15 GO SUB 1500: GO SUB 3500: GO SUB 1100
   20 BORDER 0: PAPER 0: INK 5: CLS 
   21 FOR a=0 TO 20: BEEP .005,a: PRINT INK 2;AT a,0;"█";AT a,31;"█": NEXT a: FOR a=31 TO 0 STEP -1: BEEP .005,a: PRINT INK 2;AT 0,a;"█";AT 21,a;"█": NEXT a
   30 PRINT AT 10,10;"CITY LANDER"
   40 FOR N=1 TO 2: RESTORE 60
   50 FOR I=1 TO 8: READ H,J: BEEP H,J
   60 NEXT I: NEXT N
   70 DATA .1,11,.1,11,.8,16,.05,11,.05,16,.05,11,.05,16,1,20
   80 CLS : REM CITY
   90 BORDER 0: INK 2: PAPER 0: CLS : GO SUB 1000
  100 LET H=2: LET J=0: LET BH=H: LET SC=0: LET T=0: LET W=0
  101 PLOT 0,160: DRAW 255,0: PRINT INK 6;AT 0,23;"SCORE ";SC;AT 0,2;"HIGH-SCORE "
  102 IF L=-3 THEN PRINT AT 0,15;SCH1
  103 IF L=2 THEN PRINT AT 0,15;SCH2
  104 IF L=4 THEN PRINT AT 0,15;SCH3
  110 FOR q=0 TO 31
  120 PRINT INK 6;AT H,q;"\b\c": BEEP .0008,(H+6)*2
  140 IF INKEY$="z" OR INKEY$="Z" AND w=0 THEN LET t=1: LET w=q
  141 IF t=1 THEN PRINT INK 7;AT bh,w;"\d": BEEP .0008,50: LET BH=bh+1
  142 IF ATTR (bh,w)=1 OR ATTR (bh,w)=40 THEN PRINT PAPER 2; INK 6; FLASH 1;AT bh,w-1;"\g\g";AT bh+1,w-1;"\g\g": PRINT INK 0; PAPER 0;AT bh-1,w;" ": FLASH 0: PRINT INK 2;AT 21,w-1;"██": BEEP .01,0: LET bh=h: LET t=0: LET w=0: LET SC=SC+5
  143 PRINT INK 6;AT 0,29;SC
  144 IF bh>20 THEN PRINT INK 0;AT bh-1,w;"█": BEEP .01,0: LET bh=h: LET t=0: LET w=0
  145 IF ATTR (H,Q+2)=40 OR ATTR (h,q+2)=1 THEN FOR i=1 TO 7: INK i: FOR j=0 TO 21: BEEP .01,j: PRINT AT j,0; OVER 1;TAB 31: NEXT j: NEXT i: CLS : GO TO 4000
  146 PRINT AT H,Q;"  ": PRINT AT BH-1,W;" ": PRINT AT BH,W;" "
  147 IF ATTR (bh+2,w)=2 THEN PRINT INK 0;AT bh,w;"█"
  148 IF H=20 AND Q=15 THEN GO TO 4500
  149 IF INKEY$="K" AND J=0 AND H>3 THEN LET h=h-1: PRINT AT h,q;"  ": LET j=j+1
  150 NEXT Q
  160 LET H=H+1
  170 GO TO 110
  999 STOP 
 1000 REM CITY
 1010 FOR N=2 TO 31 STEP 2: LET s=INT (RND*8+10+L): FOR Z=20 TO s STEP -1: PRINT INK 0; PAPER 5;AT Z,N;"\a";AT Z,N+1;"\a": PRINT INK 1;AT s-1,n;"\e\f": NEXT Z: NEXT N: FOR z=0 TO 31: PRINT INK 2;AT 21,z;"█": NEXT z
 1015 INK 7
 1020 RETURN 
 1100 PAPER 0: INK 4: BORDER 0: CLS : LET D=2: LET AL=6: LET A$="TODAYS GREATESTS"
 1110 FOR A=1 TO LEN A$
 1120 PRINT AT D,AL+A;A$(A TO A)
 1125 BEEP .01,30
 1130 NEXT A
 1135 IF LEN A$=10 THEN GO TO 1150
 1136 IF LEN A$=11 THEN GO TO 1160
 1137 IF LEN A$=12 THEN GO TO 1170
 1140 LET D=5: LET AL=0: LET A$="LEVEL(1)..": GO TO 1110
 1150 LET D=7: LET AL=0: LET A$="LEVEL(2)...": GO TO 1110
 1160 LET D=9: LET AL=0: LET A$="LEVEL(3)....": GO TO 1110
 1170 PRINT AT 5,11;Q$(1);" ";SCH1
 1172 PRINT AT 7,12;Q$(2);" ";SCH2
 1173 PRINT AT 9,13;Q$(3);" ";SCH3
 1174 PLOT 0,75: DRAW 255,0
 1175 LET D=13: LET AL=3: LET A$="CONTROLES:-"
 1176 FOR A=1 TO LEN A$
 1177 PRINT AT D,AL+A;A$(A TO A)
 1178 BEEP .01,30
 1179 NEXT A
 1180 IF LEN A$=9 THEN GO TO 1183
 1181 IF LEN A$=18 THEN GO TO 1188
 1182 LET D=15: LET AL=7: LET A$="Z....FIRE": GO TO 1176
 1183 LET D=17: LET AL=7: LET A$="K....UP(ONCE ONLY)": GO TO 1176
 1188 PRINT FLASH 1; INK 6;AT 20,9;"PRESS ANY KEY"
 1189 PAUSE 0: RETURN 
 1200 FOR Z=1 TO 3: DIM Q$(Z,13): NEXT Z
 1210 LET Q$(1)=" ___________"
 1220 LET Q$(2)=" ___________"
 1230 LET Q$(3)=" ___________"
 1240 LET SCH1=0: LET SCH2=0: LET SCH3=0
 1250 RETURN 
 1500 RESTORE 1500: FOR Z=0 TO 7: READ A: POKE USR "A"+Z,A: NEXT Z: DATA 0,126,126,126,126,126,126,0
 1510 RESTORE 1510: FOR Z=0 TO 7: READ A: POKE USR "B"+Z,A: NEXT Z: DATA 128+64+32+1+2+4,128+64+7,255,255-128,63,3,3,0
 1520 RESTORE 1520: FOR Z=0 TO 7: READ A: POKE USR "C"+Z,A: NEXT Z: DATA 0,128,152,254,255,254,128,0
 1530 RESTORE 1530: FOR Z=0 TO 7: READ A: POKE USR "D"+Z,A: NEXT Z: DATA 195,126,126,90,126,126,60,24
 1545 RESTORE 1550
 1550 FOR z=0 TO 7: READ a: POKE USR "e"+z,a: NEXT z: DATA 1,3,7,15,31,63,127,255
 1560 FOR z=0 TO 7: READ a: POKE USR "f"+z,a: NEXT z: DATA 128,192,224,240,248,252,254,255
 1570 RESTORE 1580
 1580 FOR z=0 TO 7: LET D=INT (RND*255+1): READ a: POKE USR "G"+z,a: NEXT z: DATA D,D,D,D,D,D,D,D
 2040 RETURN 
 3500 PAPER 0: INK 6: BORDER 0: CLS 
 3510 PRINT FLASH 1;AT 19,10;"LEVEL (1/3)": FLASH 0
 3515 INK 2: FOR A=0 TO 31: BEEP .005,a: PRINT AT 0,A;"█";AT 21,A;"█": NEXT A: FOR A=21 TO 1 STEP -1: BEEP .005,a: PRINT AT A,0;"█";AT A,31;"█": NEXT A
 3520 INK 6: PRINT AT 4,2;"3......(EASY)";AT 6,2;"2......(QUITE HARD)";AT 8,2;"1......(VARY HARD)"
 3525 FOR A=7 TO 12: PRINT PAPER 2; INK 0; PAPER 5;AT A,27;"\a\a": NEXT A: PRINT INK 1;AT 6,27;"\e\f"; INK 0; PAPER 5;AT 12,25;"\a\a";AT 11,25;"\a\a"; PAPER 0; INK 1;AT 10,25;"\e\f"
 3530 IF INKEY$>"3" OR INKEY$<"1" THEN GO TO 3530
 3540 PAUSE 0: IF INKEY$="1" THEN LET L=-3: LET LE=1
 3550 IF INKEY$="2" THEN LET L=2: LET LE=2
 3560 IF INKEY$="3" THEN LET L=4: LET LE=3
 3570 RETURN 
 4010 PRINT FLASH 1;AT 10,3;"YOU NUT YOU COST US A PLANE"
 4020 FOR Z=0 TO 50: BEEP .01,INT (RND*30)-20: NEXT Z
 4041 IF LE=1 AND SC>=SCH1 THEN LET SCH1=SC: PRINT AT 15,1;"HIGH SCORE INPUT NAME.MAX 13": INPUT Q$(1)
 4042 IF LE=2 AND SC>=SCH2 THEN LET SCH2=SC: PRINT AT 15,1;"HIGH SCORE INPUT NAME.MAX 13": INPUT Q$(2)
 4043 IF LE=3 AND SC>=SCH3 THEN LET SCH3=SC: PRINT AT 15,1;"HIGH SCORE INPUT NAME,MAX 13": INPUT Q$(3)
 4045 PRINT FLASH 1;AT 20,8;"ANOTHER GO (Y/N) ?"
 4050 PAUSE 0
 4060 IF INKEY$="Y" OR INKEY$="y" THEN GO TO 15
 4499 STOP 
 4500 BORDER 0: PAPER 1: INK 7: CLS 
 4510 PRINT FLASH 1;AT 10,7;"WELL DONE YOU HAVE";AT 11,4;"COMPLEATED YOUR MISSION"
 4520 FOR A=0 TO 60: BEEP .01,RND*30: NEXT A
 4522 IF LE=1 AND SC>=SCH1 THEN LET SCH1=SC: PRINT AT 15,1;"HIGH SCORE INPUT NAME.MAX 13": INPUT Q$(1)
 4524 IF LE=2 AND SC>=SCH2 THEN LET SCH2=SC: PRINT AT 15,1;"HIGH SCORE INPUT NAME.MAX 13": INPUT Q$(2)
 4526 IF LE=3 AND SC>=SCH3 THEN LET SCH3=SC: PRINT AT 15,1;"HIGH SCORE INPUT NAME.MAX 13": INPUT Q$(3)
 4530 PRINT PAPER 1; FLASH 1;AT 20,8;"ANOTHER GO (Y/N)"
 4540 PAUSE 0
 4550 IF INKEY$="Y" OR INKEY$="y" THEN GO TO 15
 4560 STOP 

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.