Copyright © 2020 by Víctor Parada
This is a little game for the 2020 NOMAM's BASIC 10-liners Contest. This program fits in the EXTREM-256 category, and it was written using FastBasic 4.3 for the 8-bits ATARI XL/XE. Development started on 2019-04-24, and it took 4+10 days. The final version's date is 2020-03-20.
UPDATE: It obtained the 8th place of 25 entries in the category.
Chase and shoot the enemy ships through the tunnel without hitting the walls.
Press START key to enter the tunnel. | |
Fly through the tunnel without hitting the walls. Move the joystick in the same direction than the tunnel moves. | |
Your shield protects you when you crashes into a wall, but it will loose it strength and it won't recover. | |
When you fly in the right direction, you will approach to an enemy ship, but it will escape if you don't follow it. | |
You can fire a short-range laser ball when the ship is close enougth to destroy it. If it is not so close, you will miss the shot and you will have to try again. | |
When you hit a shoot to to a ship, you will destroy it and score. Then, you will have to follow the next ship. | |
The game is over when the shield is completely destroyed. Press START key to begin a new chase. |
While I was reading some of the Atari history, something called my attention and it was the first game that Atari licensed to another manufacturer: Tunnel Hunt. It was licensed in 1982 to Centuri. I had never hear about that arcade game, so I did a quick search and found that the game was developed by Owen Rubin at Atari in 1978, but it started as Tube Chase with color vector graphics and then turned into raster graphics, first with ellipses and finally with sqares. Before licensing it to Centuri, Exidy also tried it with the name Vertigo.
Tunnel Hunt flyer
Descriptive flyer
Gameplay
I view some online videos and also tried it in MAME, so I thought that it could simplified to be programmed as a tenliner. In a couple of days (not full days, just a couple of hours on those days), I could write some prototypes in DMSC's FastBasic for the tunnel, with a nice travel effect using graphics mode 2 (ANTIC mode 7), with four color registers for the tunnel (including background) and one color for the text. My idea was to program a little game only with some of the elements of the original game: following the tunnel and shooting enemies.
1st prototype
Prototype at the end of the 2nd day
I forgot this project for a couple of weeks, but when I resumed it, I thought that I could add another color to the tunnel effect asigning the register reserved for the text. Then I had to modify the parameters for routine that generated each of the 9 screens into a cache. As the text should have every time a different color than the background, it would be readable. But I had to change from graphics mode 2 to one, in order to reduce the distance between squares borders. I had to draw the new screens in a spreadsheet in order to find the best parameters for the routine.
Tunnel in graphics mode 1 using 5 color registers
After a redesign
I was thinking about how to add the next elements of the game, but then I thought that it could be great to have a way to split the screen colors in orde to have a non flashing text window for the score and other counters. As the standard way to get that is by using Display Lists Interrupts (DLI), I asked DMSC if DLIs were in his roadmap. He replied that he would like it, so we talk about it for some months, trying to find the best way to do it in a flexible and practical way for many purposes. In the meantime, I forgot this project again for about 9 months, and resumed it when FastBasic 4.3 with DLI support was released.
It was time to add the enemy ship and the gunsight. I tried to improve the perspective, so I tried to adjust some of the elements of the game and adjust lots of parameters, bitmaps, etc. Also, the shield counter was implemented, starting from 99 instead of 999 as it was planned (internally, it starts from 9999 but only 2 chars are displayed only when they change for speed reasons). This took some short periods of time during many days, and I didn't add a DLI yet!!!
Adding P/M for enemies and calibrate positions
Adjusting bitmaps
One special item it made me to think about was the fact that in most of the pilot games you have to move the joystick to the top when you want to go down, but while I played it, I realized that the joystick needs to be moved in the same direction from where the tunnel advances. I finally decided to preserve the original game's feature.
With the enemy ship OK, the next step was to add the fireball and the explosion effect. I was running out of space just because I did not optimize the resources, so I rewrote some portions of the code to see how much free space I had to continue with the adition of more features. I had enough space to add a title screen or to add a color table to make the tunnel look like the original game, without using just a counter which jumps from a light color to a dark one like in the classic rainbow effect. I decided to add a title screen similar to the original one and to see if it fits in the available space. I packed data in a simple Run-length Encoding (RLE), and the unpack routine was also short, so I still had space for the color table. Instead of insert a static table as binary data, I tried a routine to create that table, and it was too short that I was surprised. I also used the new color table for the title screen, as in the arcade.
New title screen
No big color & brights changes in the tunnel
With the DLI enabled and the bogus title out of the score bar, I had some extra space to turn the shield counter into a bar like in the original game using the same chars designed for the title screen. Another idea was to include a laser temp meter, but I prefered to control the trigger, in order to dissallow continuous fire. There was more available space to code, but I used some of it optimizing the code to speed up things instead of to save space. With the contest's deadline being reached in few hours, I decided to stop improving the game, to pack it and to send it.
Technical info about the released version:
Get the TUNNEL.ATR file and set it as drive 1 in a real Atari (or emulator). Turn the computer on and the game should start after the loading completes. A joystick in port 1 is required.
The abbreviated BASIC code is the following:
The full and expanded BASIC listing is:
|
Memory usage: $8000-$81FF: Charset $8200-$93FF: Tunnel screens $9400-$97FF: P/M (double line res) $9800-$99FF: Title screen $9A00- : Colors sequence $9F00 : DLIST $A000- : P/M bitmaps |
|
Tunnel screens are in this order: 0 1 2 3 4 5 6 7 8 |
|
Players: P0: 53256 53248 $9600 704 Gun sight P1: 53257 53249 $9680 705 Laser balls P2: 53258 53250 $9700 706 Enemy P3: 53259 53251 $9780 707 Explosion Width Hpos Buff Col |
graphics 0 |
Graphics mode 0 used for text window |
e=adr("{binary data}")+1 |
|
t=adr("{binary data}")+1 |
Title Screen (packed) |
dim sc(8) byte, cx(2) byte, cy(2) word, ex(11) byte, ey(11) word, jx(15) byte, jy(15) byte, di(3) byte, wi(3) byte, dd(3) byte |
Game arrays |
move e,adr(sc),97 |
Initialize all the arrays |
mset $9400,$1400,0 |
Clean P/M area and bitmaps area |
poke 752,1 |
Disables cursor |
poke 82,0 |
Removes left margin |
dli set in = $C8 into $D01A, $E into $D016 |
Sets DLI for panel color |
move e+97,$9F00,34 dpoke 560,$9F00 |
New DLIST |
move $E000,$8000,512 mset $8008,8,$0F mset $8010,8,$F0 mset $8018,8,$FF poke 756,$80 |
CHARSET |
poke 54279,$94 |
PMBASE |
poke 623,1 |
GPRIOR=1: P0-P3 over playfield |
poke 559,42 |
SDMCTL=2+32+8: Double line resolution players only (no missiles) in standard playfield |
poke 53277,2 |
GRACTL=2: Players only, no missiles |
move e+198,53256,4 |
Sets players width: P0&P1=single P2&P3=quad |
move e+140,$A072,8 |
Gun sight |
move e+148,$A16E,16 move e+164,$A272,7 move e+171,$A373,5 move e+176,$A46E,16 move e+192,$A572,6 |
Bitmaps for enemy |
x=e+202 y=x+4 z=y+4 w=z+4 v=w+12 |
Draw all 9 screens |
m=$8200 |
Each screen uses 512 bytes starting at $8200 |
for i=0 to 8 |
For each screen |
for j=0 to 3 |
Draw four blocks, each one over the previous |
a=peek(v+(i/3)*4+j) b=peek(w+(i mod 3)*4+j) c=peek(z+j) d=peek(x+j) |
Gets coordinates, size and color |
for k=0 to peek(y+j) mset m+(a+k)*20+b, d, c next k next j |
Draw the block, one line at a time |
m=m+512 next i |
Point to the next screen buffer |
m=$9840 |
Unpack title screen |
for i=0 to 53 d=peek(t+i) a=d/64 b=d&63 mset m,b,a m=m+b next i |
This routine unpacks data using a simple RLE algorithm. Each byte has 2 bits representing 2 horizontal blocks (1 char) on screen, and 6 bits with a repetition count. |
cc=$9A00 for i=1 to 15 for j=0 to 7 mset cc+(i-1)*15+j,15-2*j,i*16+j*2 next j next i |
Build color sequence table |
position 24,0 print "PRESS START" |
Start screen, just once |
dli in |
Activates DLI |
do |
|
mset $9400,$400,0 |
Clean P/M area |
move e+131,704,9 |
Sets default colors |
poke $9F05,$98 |
Displays title screen |
repeat i=(i+1)*(i<210) j=peek(cc+i) pause 2 poke 708,j until peek(53279)=6 |
Waits for START key |
poke $9F05,sc(4) |
|
position 21,0 print "HITS:0 SHIELD:99" |
|
sound 2,255,10,1 sound 3,254,10,1 |
Tunnel sound |
x=1 y=1 |
Tunnel position |
z=4 |
Tunnel absolute position |
c=0 |
Tunnel previous absoulte position |
u=1 v=1 |
User direction |
j=0 |
Joystick position |
d=0 |
Joystick previous position |
p=32 |
Horizontal movement of enemy |
q=1 |
Current horizontal direction of enemy |
w=399 h=3 |
Enemy distance |
b=0 |
Enemy previous distance |
f=20 |
Frecuency of turns |
g=f |
Frecuency counter |
i=0 |
Time counter |
m=0 mm=1 |
Phaser counter |
o=0 |
Shoot |
oo=0 |
Trigger control |
n=0 |
Destroyed enemies counter |
s=9999 |
Shield strength |
t=9900 |
Shield limit before printing (saves CPU) |
ss=0 |
Delay before counting wall hits |
ci=0 |
Color index |
repeat |
GAME LOOP |
poke 77,0 |
Disables attract mode |
p=p+q if p=0 or p=63 or rand(70)=0 q=-q endif i=(i+1)&15 if i mod 4=0 |
Adjust horizontal position of enemy (oscillation) |
g=g-(g>3) |
Increase frecuency of tunnel changes |
if rand(g)=0 |
Tunnel changed? |
a=rand(3)-1 |
Selects a value between -1 and 1 |
if rand(2) |
Decides if changes horizontally or vertically |
a=a+x |
Horizontally |
if a>=0 and a<3 x=a else x=1 endif else |
Keeps new position unless it is out of bounds, in which case, return to center |
a=a+y |
Verically |
if a>=0 and a<3 y=a else y=1 endif endif |
Keeps new position unless it is out of bounds, in which case, return to center |
z=y*3+x |
Computes absolute position |
g=f endif |
Resets frecuency |
ci=(ci+1)*(ci<210) pause 0 move cc+210-ci,708,5 poke $9F05,sc(z) endif |
Updates frame and color |
if h<>b or c<>z move ey(y+di(h)),$9700,$80 poke 53258,wi(h) b=h endif |
Enemy distance changed or tunnel changed perspective |
d=j j=stick(0) |
Reads joystick |
if c<>z or d<>j u=jx(j) v=jy(j) poke 53248,cx(u) move cy(v),$9600,$80 c=z endif |
Tunnel changed perspective or player has moved the joystick |
r=ex(x+di(h))+p/dd(h) poke 53250,r |
Moves enemy horizontally |
if m |
Shooting |
if m=2 |
The laser ball reached the gun sight? |
poke 53249,255 |
Removes the laser ball |
if w<75 |
Close enough to the enemy? |
sound 1,10,8,10 |
Start explosion sound |
move ey(y)+$300,$9780,$80 poke 53251,r |
Puts the explosion in the same place where the enemy ship is |
poke 53250,255 w=399 h=3 o=40 |
Next enemy is far away |
n=n+(n<999) position 26,0 print n |
Increase the score |
poke 706,rand(14)*16+$1A |
Selects a new color for the next enemy ship |
f=f-(f>8) endif |
Increases tunnel changes frecuency |
mm=-mm |
Changes the side for the next laser ball |
oo=0 else |
Clears trigger register to "not pressed" |
move cy(v)+$500-m,$9680,$80 poke 53249,cx(u)-m*mm |
Update laser ball position |
sound 1,80-2*m,12,m/6 poke 705,2*m endif |
Fire FX |
m=m-(m>0)*2 else |
Next step of the shooting |
oo=oo+strig(0) |
Trigger has been released? |
m=40*(1-strig(0))*(o=0)*(oo>0) endif |
Shoot only if there is no current shoot and trigger was previously released |
if o |
Hit |
poke 707,o/3+16*rand(16) sound 1,100,4,o/4 o=o-(o>0) else |
Explosion FX |
poke 53251,255 endif |
Remove explosion from screen |
a=abs(u-x)+abs(v-y) |
Hits on walls |
if a if ss=0 |
Not aligned? |
sound 0,92,8,2*((a>1)+(i<8 and a=1)>0) |
Scratch sound |
s=s-a |
Decrease shield strength |
if s<t position 37,0 t=t-100*(t>0) print t/100;" "; endif |
Time to print damage? |
w=w+a*(w<395) else ss=ss-(ss>0) endif else |
Enemy escapes |
sound 0 |
Stops scratch sound |
w=w-(w>30) |
Reaching enemy, but not too much |
ss=40 endif h=w/100 |
Reset delay before scratchs |
until t=0 |
|
sound |
Shuts up! |
position 30,0 print "game over" |
End of game... |
loop |
Return to my 10-liners page.
© 2020 by Víctor Parada - 2020-03-20 (updated: 2020-08-15)