Copyright © 2021 by Víctor Parada
This is a little game for the 2021 NOMAM's BASIC 10-liners Contest. This program fits in the PUR-120 category, and it was written using FastBasic 4.5.2 for the 8-bits ATARI XL/XE. Development started on 2021-01-09, and it took 2+5 days. The final version's date is 2020-03-17.
UPDATE: It obtained the 1st place of 26 entries in the category.
Many colorful, swaying and noxious creatures attack you on your interstellar journey. Be faster!
The creatures appear at different distances from both sides of the screen. They move from side to side flapping their wings. Use the paddle controller to move your ship to the desired position. Note that you have a limited range on the bottom area. |
|
Those creature are agressive and they drop bombs as soon as they are on top of you.
Keep moving! If you are fast enough, you could dodge the bombs by jumping through hyperspace. |
|
Shoot them with your laser gun to clear your way. You can accurately shoot one by one or in bulk using continuous shooting by holding the trigger. |
|
More and more of them will arrive, and they will be faster and faster. Farther creatures score more points than closer ones, and there is an additional bonus as they get faster. |
|
You only have one ship. The game is over if a bomb destroys your spaceship. Press the button to try again. |
I started a shooter project for the 2600 console. I was based on Demon Attack and it would include a boss level and some other game variations. However, I wanted to use paddle controller to do something completely different than usual. I wrote a prototype in assembly language, but the movement of the enemies didn't work as expected. The idea was to set a destination for an enemy, make its speed to increase up to a maximum and then to decrease when it is reaching the destination, giving the illusion of inertia.
Demon Attack
My 2600 clone
Trying to find a solution for my problem, I wrote the algorithm in FastBasic for the A8 computer in order to simplify my tests and to find the right rules. Then, I got involved in other NOMAM's contest related projects and left this prototype away, as well as the original 2600 project.
After I complete The Children game, I realized that I didn't have an entry for the PUR-120 category, so I checked my WIPs folder to see if any of the forgotten prototypes could be used for a game with the 120 chars limit, and I thought that this shooter could be the one.
I had used the DLI feature of FastBasic in my prototype, in order to reuse the same P/M player for all the enemies which would be at different heights, just changing the horizontal position of the player when needed. At the same time, I decided to give a different color for each of them. I added a score bar and defined a bitmap for the enemies, giving them personality.
A8 1st prototype in FastBasic
The next step was to add a spaceship and a laser to shoot. In order to check for hits, I needed to read the hardware collision registers, but as all the enemies used the same player, I need to use the height of the laser to compute which of the enemies was hit. I also doubled the width of the enemies to quad size (4 color clocks per pixel), and the width of the spaceship to double size (2 color clocks per pixel).
For the attack of the enemies, I decided to use the same trick I used in Invaders game: used the hardware missiles as a continuos scroll down, and place one bomb (set the required bits) with a given timing, showing up to 4 of them at the same time... one goes from the bottom, another appears on the top. Instead of randomly select which enemy would drop a bomb, I decided to make the first enemy that it is placed over the spaceship to drop a bomb and set a timer for the next bomb to be dropped, which it could be dropped by any of the enemies that meets the condition.
By this time, I had simplified the enemies movement to constant speed from their starting point to the corresponding destination, but the speed would increase on each round, and there would be many waves in a round, with an increasing number of enemies each time. There was also a delay for each of the enemies to enter into the screen.
Instead of fixed heights, I defined fixed zones, and each enemy could use any height from its zone, so it seemed that there were more than 4 enemies when one is killed and another appears immediatelly at another height. Even though the enemies move through all the screen, they looked static, and I add a slightly different bitmap to make an animation with them.
Spaceship, lasers and bombs added, and elements sizes increased.
All the available space was used, and there were some missing features, so I had to simplify some things and remove code that makes no diference in the game play, like changing the calculation of many random values by just constant values.
I sent a copy to DMSC and, as always, he suggested a change in the way I computed the movement of the enemies on every frame to save some bytes. There were some problems in the code, but as the movement changed from constant to deaccelerated (that was half of what I first tried in the 2600) and I liked it, we did a bit of brainstorm to get the right expression, which it could be dependant of the level. I decided to apply the same expresion to the movement of the spaceship, and the result was great.
After some code improvement, I gave this game its new name: "Noxious". The game was ready for the contest.
Get the NOXIOUS.ATR file and set it as drive 1 in a real Atari. Turn the computer on and the game should start after the loading completes. A paddle set in port 1 is required, but only one paddle is used.
A NOTE FOR EMULATOR USERS: This game can be played in emulators like Altirra, with the mouse device set as the Paddle A controller in port 1, but I encourage to try it with a real paddle controller to enjoy the experience.
The abbreviated BASIC code is the following:
The full and expanded BASIC listing is:
|
NOXIOUS (c) 2021 Víctor Parada 2021-03-16 |
graphics 17 |
Sets graphics mode 2 (ANTIC 6) for score and to enable DLI at certain heights |
pmgraphics 2 |
Enables P/M graphics with double scan line resolution |
|
P0=Battleship P1=Laser P2=Enemy Face P3=Enemy Body M0-M3=P4=Bombs |
w=adr("{binary data}")+1 |
Defines lots of data 0-3 (4): Bombs mask (for M0 to M3): 4-7 (4): Player's widths (P0 to P3): 8-16 (8+1): Color palette : 16-23 (5+3): Body, position B 24-28 (5): Body, position A 29-33 (5): Face 34-41 (8): Battleship |
data a() byte = "vit" data b() byte = "oco" data c() byte = ".cl" |
DATA arrays of 4 bytes, needed for DLI, 1 byte each per interrupt A(): body horizontal position of every enemy B(): face horizontal position C(): color |
move w+4,$D008,4 |
Player sizes P0=P3=Quad P2=Double P1=Normal width |
poke 623,17 |
GPRIOR = P0,P1,P2,P3,PF,BAK + Combined Missiles |
move w+8,704,9 |
Players and Missiles colors |
dim e(3) byte,f(3) byte,g(3) byte,i(3) |
E(): precalculated body horizontal position F(): precalculated face horizontal position G(): destination horizontal position I(): vertical position (includes PMBASE and buffers) |
dli set i = a into $D003, b into $D002, c into $D015 |
Configures a DLI |
dli i |
Enables the DLI |
p=pmaddress(2)+21 |
Enemy |
q=p-134 |
Laser: q=pmaddress(1)+17 |
r=p-384 |
Bombs: r=pmaddress(-1)+25 |
do |
MAIN LOOP |
mset r-21,640,0 |
Cleans P/M area |
poke $D01E,0 pause |
Clear collisions |
move w+34,p-172,8 |
Puts the spaceship: pmaddress(0)+113 |
print #6,"{binary data}" |
Prints the title and zero score |
|
Initialize variables |
m=0 |
Laser height (0=no shoot yet) |
u=99 |
Initial horizontal position of the ship |
t=0 |
Score |
k=0 |
Missile selector (last 2 bits) |
j=4 |
Delay for bombs and brightness indicator for new enemies |
n=0 |
Counter for released enemies |
l=5 |
Round number (decreasing down to 1) |
o=15 |
Number of enemies to the next round |
z=dpeek(560)+6 for x=0 to 3 |
Updates the display list to enable 4 interrupts |
poke z+x*3,134 |
(ANTIC mode 6)+(DLI)=6+128=134 |
a(x)=0 b(x)=0 g(x)=0 next x |
Removes the enemies from screen (and buffer) |
poke z,144 |
Turns the first one into a two blank scan lines (16+128=144), shifting the others some scanlines higher |
repeat |
GAME LOOP |
if n>o |
Checks for level change Reached the required enemies? |
o=o*2 |
Duplicates the number of enemies for the next round |
l=l-(l>1) endif |
Goes to next level |
inc j |
Updates delay for next bomb to be dropped |
for x=0 to 3 |
Updates each enemy |
if g(x) |
Is enemy alive? |
move w+16+e(x)&8,i(x),5 |
Updates the body (wings position) |
y=4*l z=e(x)-g(x) |
Computes next horizontal position |
if z |
Not reached the destination yet? |
e(x)=g(x)+z*y/(y+1) else |
Updates the horizontal position |
g(x)=rand(160)+32 endif |
Selects a new destination |
if abs(e(x)-u+8)<9 and j>7 |
Checks if the enemy could drop a bomb. The enemy must be almost over the ship (up to 8 pixels to any side) and the delay from the last dropped bomb has been completed. |
j=0 |
Starts a new delay for the next bomb |
inc k |
Selects next available missile for the bomb |
poke $D004+k&3,e(x)+15 |
Sets the horizontal position of the missile |
y=(i(x)-507)/3*3 |
Computes the vertical position of the bomb. "/3*3" is a simple way to get a multiple of three integer value |
mset y,3, peek(y)!peek(w+k&3) endif |
Updates the missiles area by adding the new bomb to it (without deleting a current one at the same height) |
else |
Creates a new enemy |
inc n |
Updates the count of enemies |
g(x)=rand(128)+48 |
Defines an horizontal destination position |
e(x)=215*n&1 |
Defines an initial horizontal position, it might be at the left or at the right external area of the visible screen |
c(x)=rand(12)*16+36+j&6 |
Sets a color for the enemy's body |
z=p+x*12+128 |
Defines the zone for the enemy |
i(x)=z+rand(7) |
Decides a height inside that zone |
mset z,12,0 mset z-128,12,0 |
Clears the enemy zone, removing the previous one in it |
move w+29,i(x)-128,5 endif |
Reposition the enemy in the current zone (face only, the body is updated on the next frame) |
f(x)=e(x)+8 next x |
Updates enemy's face horizontal position based on its body position |
v=228-paddle(0) |
Reads paddle position |
if v<72 v=72 elif v>168 v=168 endif |
Limits ship's position to a small range: 72-168 |
u=v+(u-v)*3/4 |
Computes next horizontal position on the ship based on its current position and the distance to the destination |
pause poke $D000,u |
Moves battleship |
sound 1 |
Stops the FX for a previously killed enemy |
if peek($D00D)>3 |
Did the laser hit an enemy? |
sound 1,98,12,9 |
Plays a sound |
mset q+m*6,6,0 |
Removes the laser from P/M area |
poke $D01E,0 |
Clears the collisions registers |
x=(m-1)/2 |
Computes which enemy was hit by looking the laser height |
t=t+9-x-l position 12,0 print #6,t;0;0 |
Updates the score. Points depends on the round and the height of the enemy |
e(x)=0 f(x)=0 g(x)=0 |
Removes the enemy from the playfield |
m=0 endif |
Resets the laser height, ready to be fired again |
move adr(e),adr(a),4 move adr(f),adr(b),4 |
Updates enemy's positions |
poke 77,0 |
Disables attract mode |
if m |
Shooting? |
z=q+m*6 |
Computes laser height for P/M |
dec m |
Decreases the height counter |
if m |
Is there more space to travel? |
move z,z-6,12 else |
Scrolls up the laser |
mset z,6,0 endif else |
Removes the laser at the top of the playfield |
if ptrig(0)=0 |
Fired? |
poke $D001,u+4 |
Sets the horizontal position for the laser |
mset q+84,6,8 |
Puts the laser just in front of the ship |
m=14 endif endif |
Sets the height (it decreases as it travels up) |
sound 0,50-m,10,m/2 |
Shooting FX |
-move r-3,r,99 |
Scrolls the bombs down (missiles P/M area) |
until (dpeek($D008)!dpeek($D00A))&$101 |
End of GAME LOOP Game finishes when any of the 4 bombs hit the ship |
sound 0,100,8,8 |
Explosion FX |
move w+29,p-171,5 |
Destroys the ship bitmap (using the face of the enemies' bitmap) |
pause 20 sound |
A small delay to stop the explosion sound FX |
pause 99 |
A delay before allowing the game restart |
while ptrig(0) wend |
Waits for the trigger to restart |
loop |
End of MAIN LOOP |
Return to my 10-liners page.
© 2021 by Víctor Parada - 2021-03-23 (updated: 2021-04-10)