Noxious

Copyright © 2021 by Víctor Parada

The NOXIOUS

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.


Description

Many colorful, swaying and noxious creatures attack you on your interstellar journey. Be faster!


Instructions

NOXIOUS start 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.
NOXIOUS bombs 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.
NOXIOUS shoot 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.
NOXIOUS score 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.
NOXIOUS end You only have one ship. The game is over if a bomb destroys your spaceship.
Press the button to try again.

Development of the game

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

Demon Attack

Inertia

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.

Prototype 1

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.

Prototype 2

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.


Download and try

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 code

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)