Patrol

Copyright © 2020 by Víctor Parada

Patrol

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.4 for the 8-bits ATARI XL/XE. Development started on 2020-03-30, and it took 5+4+4 days. The final version's date is 2020-08-16.

UPDATE: It obtained the 2nd place of NNN entries in the category.


Description

Drive to POINT Z, jumping over craters and other obstacles, shooting to destroy the big ones and UFOs.


Instructions

PATROL start Your mission is to arrive at POINT Z in your buggy, avoiding all the obstacles in your way.
PATROL accelerate You can accelerate to the right and brake to go back to the left. Note that you will win more points when you are in the middle of the screen, and less points when you are back near the border.
PATROL jump You can jump over craters, small rocks and mines, but you cannot go forward or backward while jumping.
PATROL shoot You can't jump over big rocks, but you can destroy them with your short range missiles. You cannot fire missiles while jumping.
PATROL mix There are combination of obstacles, and you must do both jumping and firing in coordination.
PATROL ufo An UFO will throw bombs to you. You can avoid them or destroy the UFO by shooting it with your vertical cannon.
PATROL crash If you crash into any obstacle, your buggy is destroyed and you have to start the stage again from the last checkpoint.
PATROL checkpoint When you arrive to a checkpoint, you will get an extra buggy. You can have 5 buggies at most.
PATROL difficulty Every stage is a bit more difficult than the previous. The first one has about 8 obstacles, but the last might have more than 20 in the same distance, so there is less space between obstacles!!!
PATROL gameover The game is over when all your buggies crashed or when you reach POINT Z. Press the trigger to start again.

Development of the game

In the 2020 NOMAM thread at AtariAge forum, I published a parallax demo with multi fine scrolling using FastBasic 4.3 and its brand new feature: DLI. This was in response to another post about complexities of horizontal fine scrolling in BASIC. I tried to make it look like Moon Patrol, so I added a static lunar buggy, but I got excited and added a simple animation of the wheels.

PATROL demo

Horizontal muti fine scrolling demo

Demo in action

To build that demo, I used a custom display list with some blank zones (only BAK color register is in use) and some ANTIC mode 4 and 5 lines for the scrolling stripes. Each stripe was 1K long, that is about 25 screens of random obstacles, but for the mountains and hills there were repeating patterns, so the corresponding pointers could be reset back properly and smoothly. While the colors were managed easily by the DLI, I quickly found that glitches appeared when I set the horizontal fine scroll bit to some of the lines. It seemed to me that there is less CPU cycles for DLI when combining horizontal fine scroll bit with DLI enable bit in the same DL instruction, and a more complex DLI routine should be used. To bypass this problem, I redesigned the DL and inserted some bogus ANTIC mode 14 (graphics mode 15) lines with static data and the DLI enable bit, but without the fine scroll bit, leaving the fine scrolling lines without a DLI. This forced me to not split colors in the bottom stripe, one set of colors for the surface and another for the craters, because I wanted to add the plants and get a colorful playfield.

PATROL glitch

1st prototype with glitches

PATROL no glitch

Glitches removed.

For the buggy I used simple bitmaps on player/missile graphics in single line resolution, using P0 for the wheels and cannons, and P1 for the body. The bitmaps were designed using a spreadsheet (to get the data byte values immediately) and I tried a simple 3-steps animation for the wheels.

While I was writing the post, I thought that it could be converted to a simple tenliner game, so I added the ability to move forward and backward and to jump using the joystick. I also modified the buggy bitmap and incuded a "boing" sound for the jumps.

The next day, a collision detection was included to know when the buggy crashed or fell into a crater. This was somehow tricky in order to simplify the algorithms. I changed the use of each PF color register in this way: PF0 for the irregularities of the ground and the body of the rocks, PF1 for the shadows of the rocks and craters, PF2 as a second color I later used for mines, and PF3 for an invisible object to be placed just over the craters. Even when I lost one color by defining PF3 with the same value of BAK, there was a simple routine to detect both a crash into an obstacle and a fall into a crater by checking the P0PF collision register but discarding collisions with PF0 (ground). The score was the same for all the obstacles that were jumped over (double for big rocks because the have double width), but I added a multiplier based on the buggy position in the playfield: 10 points for objects jumped at the left of the playfield, 30 points at the center and 20 for some range in the middle. I included special bitmaps for the destroyed buggy along with the explosion sound FX.

PATROL prototype 1

1st prototype with user control of the buggy

PATROL prototype 2

A buggy explosion

Even when I used too many coding space with data to initialize the graphics, I had enough space to include more features. From the demo, the terrain has always been generated randomly, but I wanted to generate it with some patterns between checkpoints, introducing different type of obstacles as the game advances, like in the arcade. That should also include an extra line at the bottom of the playfield to scroll with the letters. Another feature I wanted to include was shooting to the obstacles and to some UFOs as well as other kind of enemies as obstacles, like tanks or ships. Also, I wanted to replicate the scoreboard of the arcade, including the high score and the timeline with the course percentage.

I started with the horizontal cannon. I used a quadruple width missile M0 (8 color clocks wide) by 1 scan line. As M0 has the same color of P0, it was a black missile. Detecting a collision was simple, but it took me too much time to find a way to detect which one was the obstacle that it was hit and where it was in the scrolling ground stripe to remove it. After many tries, I decided to discard the collision register and scan the ground while the missile was moving and remove the missile when a rock was detected. Even when the big rocks could be jumped over and get score for that like any other obstacle, I assigned more than the double of the score if it was destroyed. This worked very well, except for the fact that a lag was introduced. The ground stripe no longer moved in a constant speed: it was slower than normal whenshooting, and faster than normal when jumping. Shooting while jumping was not allowed, but it was possible to jump just after firing, and the result was a strange acceleration during the play. I also had to backup the ground stripes in order to restore them to repeat the stage after a crash.

PATROL prototype 3

Adding a front missile and detecting collisions

PATROL prototype 4

Checking for objects where the missile is

By that time, the abbreviated code was using more than the available coding space for an EXTREM-256 tenliner, so it was time to optimize the code. After compressing data and reuse bytes, I could measure how much space was now available, and I had to decide which was the next feature to include that it could fit in that space. I thought that the vertical shooting should not use too much space, as it could be added to the existing firing routine, so I designed a couple of UFO types to be displayed using P/M graphics. Brainstorming this game in early stages of development, I decided to use another stripe of ANTIC 4 with different groups of UFOs, but by this development time, I had a few bytes available, and it was not enough if that stripe would use at least the same than any other horizontal fine scrolling stripe.

I selected M2 for the vertical shoot and P3 for the UFO, and at the same time, I changed the horizontal missile from M0 (black) to M3 (light blue). As soon as I tried to add one of the new UFO bitmap to the code, I found that I had no space for that. Instead, I just copied the bitmap from one of the ATASCII graphical characters, and the diamond was selected. Displaying it in single width and single line resolution result in a ship very similar to one of the arcade UFOs. But I also had to remove some of the existing features to complete the UFO routine and its bomb, which was implemented with P2 without a nice shape but a single block. While testing, I found a special situation where the bomb was dislayed as a line because a continuous drop.

PATROL prototype 5

Adding the UFO and the vertical shoot

PATROL prototype 6

"Line" bomb

The removed features were the high score and the buggy explosion, which was replaced by a just a partial wheels change on P0. I also reduced the stage length to the half to make shorter stages. I wrote a script to count the times a variable was used in the source code, and reassigned the one-letter variable names to the most used variables. I think I recovered more than 50 bytes of source code space by doing this a couple of times. I could manage to fit the full code in exactly 10 lines of BASIC program, leaving about a total of 12 bytes of free space at the end of few lines.

The game was limited to display just 26 stages, each of them trying to reach the corresponding checkpoint from A to Z, and the increasing difficulty was given by shorter plain segments between obstacles. Once the player reached that stage, it kept playing up to 99990 points, which it was the maximum displayable score I defined once to fit the scoreboard (and because FastBasic could only manage integer numbers up to 32767, so I limited it to 9999 with a static 0 in the scoreboard).

But I was not happy with the result. Even when the UFO could be killed, there was no explosion sound, no points were added to the score and the "line" bomb was was still present. Also, the gameplay was not that hard, but there was no way to get extra buggies, and it was hard to reach many checkpoints by simple mistakes. While writing comments into the code, I decided to hack the game to test which could the highest score be, an it was a few more than 99990 point, so I removed the score limit and forced a game over when POINT Z is reached. That gave me the coding space to rewrite the UFO and its bomb routine to include the explosion sound and bonus score.

PATROL prototype 7

Simplified scoreboard and a buggy crash

The latest changes where to recover a buggy at the end of every stage, to limit the height of the jump, disabling the posibility of jumping over the big rocks, forcing to shoot to them in coordination to jump over small rocks and craters in front or behind them, and an adjust in the stage generator, to force a constant area without obstacles at the beginning, but making harder to shot the UFO at the stage start. I couldn't fix the line missiles bug because it would require an extra flag variable and too much coding space to code the logics, so I just converted it into a rush of missiles. These simple changes made me to reorganize the code to fit the maximum line length and keep only 10 lines of code.

PATROL prototype 8

Rush of bombs

PATROL prototype 9

Unable to jump over big rocks

3 months after I finished the game, a new beta of Fastbasic was announced, and it included 2 improvements that I was expecting for: function name abbreviation and being able to omit function parentheses in certain conditions. Also, there was a change in the abbreviations of the "IF-THEN-ELSE-ENDIF" structure that it could almost save 2 bytes on every use of the "IF" statement without and "ELSE", or save just 1 if it is used. With all this changes, this game could be shrinked from 2551 bytes to 2445 bytes, saving 106 bytes for extra code.

As the max number of bytes could be 2550 bytes (plus 10 for the new-line characters), I had 115 chars to improve the game. I fixed the rush of bombs issue and allowed the UFO to appear more than once in every stage. I also speeded up a litle the mountains and hills parallax to make the game feel more fluid, which allowed me to optimize a couple of conditions and save more bytes. I splitted the end of game message in two, showing either "game over" only when no more buggies are left to continue, or "completed" when you arrive at POINT Z.

To make the game more challenging, I shortened even more the length of a stage and assigned the obstacles in a way that during the first 11 stages one or two harder obstacles are introduced every time, including some new ones, and during the remaining stages it is more evident that there is less space between obstacles to shoot or jump, and to avoid the UFO's bomb.

The final version of Patrol has a size of 2532 bytes (plus 10 EOLs), that is 18 bytes less than the 2550 maximum for 10 lines of Fastbasic code. 10 lines of source code seem to be few space for a game, but this one has 240 statemens which include more than 350 bytes of binary data for sprites, display list, playfield, color palette, etc.

PATROL prototype 8

Final version


Download and try

Get the PATROL.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 code

The abbreviated BASIC code is the following:

The full and expanded BASIC listing is:


(Moon) Patrol
(c) 2020 Víctor Parada
2020-08-16

$A150-$BC20 cleaned area by GR.8
$B000 FastBasic PMBASE when in GR.0
$BC40 GR.0 screen data area dpeek(88)

$A000-$A040 DL
$A100-$A12F Filler $AA
$A130-$A15F Filler $55
$A180-$A183 Buffer Multi HSCROL
$A190-$A193 Buffer HPOS P0-P3
$A194-$A197 Buffer HPOS M0-M3
$A200-$A2FF Parallax far mountains
$A300-$A3FF Parallax middle hills
$A400-$A5FF Parallax ground
$A600-$A7FF Parallax underground
$A800-$A9FF Active parallax ground
$AA00-$ABFF Active parallax underground
$B000-$B1FF Charset (1st half)
$B200-$B3FF Charset (2nd half, with modified 3rd quarter)
$B300-$B3FF P/M M0-M3
$B400-$B4FF P/M P0
$B500-$B5FF P/M P1
$B600-$B6FF P/M P2
$B700-$B7FF P/M P3
graphics 8
Cleans 8K at top of RAM
graphics 0
Sets text mode (replaced by custom DL)
poke 82,1
Sets left margin
poke 752,1
Disables cursor
poke 756,$B0
Points to new charset (same as PMBASE)
d=adr("{binary data}")+1
Data
- Custom DL (45)
- Charset (121)
data cb() byte = 0,$96,$B8,$FA
data c2() byte = $8E,$CE,66,$F4
data c1() byte = $96,$B8,$F4,$B8
data hs() byte = 0,0,0,0
hh=adr(hs)
DLI vectors (BAK,PF1,PF2,HSCROL)
w=adr("{binary data}")+1
More data
- Mountains (8+8)
- Hills (23+1)
- Buggy bitmaps (53+1)
- Color palette (9)
- Crash bitmap (8)
- Obstacles (68)
s0=d+15
s1=d+23
s2=d+34
s3=d+37
Memory address of pointers to each stripe: Mountains, Hills, Ground, Underground
move d,$A000,50
Installs DL
dli set dl = hs into $D404, cb into $D01A,
             c1 into $D017, c2 into $D018
Display List Interrupt for fine scrolling and color palette
dli dl
Enables DLI
mset $A100,48,$AA
mset $A130,48,85
Fills the fillers
move $E000,$B000,512
move d+45,$B20F,121
CHARSET
Copies the first half to be used by scoreboard and defines bitmaps for some of the second half
move w,$A200,16
move $A200,$A210,$F0
Builds the mountain pattern
move w+8,$A300,24
move $A300,$A318,$E0
Builds the hill pattern (reusing some of the mountains)
pmgraphics 1
Initializes P/M graphics in single line resolution
dpoke $D008,257
P0 & P1 in double width
poke $D00C,192
M3 in quadruple width and M2 single width
data yy() byte = "{binary data}"
Sequence for jump (reverse order)
(string length is automatically ignored by routine)
move w+84,704,9
Changes color palette
dpoke 560,$A000
Enables custom DL
do
MAIN LOOP
  v=5
Lives
  k=0
Killed flag (forces a stage creation)
  r=0
Stage counter
  t=0
Wheels timer
  s=0
  n=0
Score
  print "{binary data}"
Displays a clean scoreboard
  repeat
Game loop
    mset $A190,8,0
Removes all P/M from screen (in the buffer)
    mset $B300,$500,0
Removes P/M
    l=$A200
    m=$A300
    p=$A800
    q=$AA00
Resets block scrolling pointers (corresponding to S0, S1, S2 and S3 addresses in DL)
    dpoke s2,p
    dpoke s3,q
    move d,$A000,50
Scrolls back to the left of the ground stripes
    poke 77,0
Disables attract mode
    pause 0
    poke $D01E,0
Resets collision registers
    x=72
    y=0
Coordinates of the buggy
    j=0
Resets the jumping flag
    i=0
Resets the game timer
    if k=0
Sets up a new stage?
      mset p,512,0
      mset q,512,80
Cleans the terrain
      b=-r
      for a=10 to 511
First delay long enough to start with a clean terrain in the first screen
        if b>33-r+rand(4) and a<325
Distance between obstacles depends on the stage (plus a small random delta to avoid patterns)
          c=rand(17)
Selects an obstacle
          if r<10
Increasing difficulty during the first stages
            c=4-r/2+rand(3+r/2+r)
          endif
Introducing new obstacles each stage
          move w+101+c*4,p+a,4
Puts the obstacle in the ground stripe
          if c<5
Crater?
            move w+169+c*4,q+a,4
          endif
First 5 obstacles require the crater part in the underground
          b=0
Restets the distance counter
          a=a+3
        else
Skips the obstacle in the loop to avoid it being overwritten by ground
          poke p+a,64+rand(3)
Puts a random ground bitmap when there is no obstacle
          inc b
        endif
      next
Increase the distance counter
      move p,$A400,$400
Backups ground for the stage
    else
Repeating the stage...
      move $A400,p,$400
    endif
Restores ground backup
    move $E300,$B742,7
Resets UFO
    u1=1
Marks UFO as alive
    u3=1
Enables bomb
    poke $B3B9,192
Shooting
Draws an horizontal missile (M3)
    f=0
Fire counter
    z=0
Horizontal position of laser
    o=0
Explosion counter
    u=0
Hit-a-rock flag, avoids hitting two rocks with the same shot
    nf=0
Trigger release flag
    e=0
    h=0
    g=0
Fine scrolling counters
    k=0
Resets the killed flag
    repeat
Stage loop
      if i&3=2
Scrolls the mountains
        inc e
        if e=4
Fine scroll
          inc l
          e=0
Block scroll
          if l=$A210
            l=$A200
          endif
        endif
Scroll back?
        dpoke s0,l
Block scroll in DL
        poke $A180,8-e
      endif
Fine scroll for DLI
      if i&1
Scrolls the hills
        inc h
        if h=4
Fine scroll
          inc m
          h=0
Block scroll
          if m=$A318
            m=$A300
          endif
        endif
Scroll back?
        dpoke s1,m
Block scroll in DL
        poke $A181,8-h
      endif
Fine scroll for DLI
      i=(i+1)&$FF
Next step
      if u1*peek($D00A)
Is UFO still alive?
        o=9
UFO was hit, sound FX please
        n=n+25
Adds many points to the score
        u1=0
      endif
Disables the UFO for a moment
      u2=i>=x and u3
      if u2
Drop a Missile?
        u3=0
Disables continuous drop
        poke $A192,i
      endif
Locks horizontal position for the missile
      poke $A193,u1*i
Locks the UFO out of screen if it was killed, or advance one color clock
      if not i
Start new UFO sequence?
        poke $D01E,0
Clear collision register
        u1=1
Enable UFO
        u3=1
      endif
Enable bombs
      if g
        inc p
        inc q
Block scroll every 2 steps (2 color clocks)
        dpoke s2,p
        dpoke s3,q
      endif
Block scroll in DL
      g=2-g
Scrolls de ground
      poke $A182,8-g
      poke $A183,8-g
Fine scroll for DLI
      nf=nf+strig(0)
Confirms trigger release
      t=(t+1)*(t<2)
Next wheels turn
      if not j
        st=stick(0)
Not jumping?
        if st&1=0
Jump?
          j=20
        else
Triggers the jumping
          x=x-(st&4=0)&(x>50)+(st&8=0)&(x<120)
Move left or right?
          bb=x/40
        endif
Sets bonus by position
        if not f+strig(0) and nf
Shoot?
          poke $A196,x+4
Sets horizontal position of vertical missile (M2)
          nf=0
Disables automatic next shoot
          f=7
Starts shooting sequence
          u=0
Resets hit-a-rock flag
          z=x+8+g
        endif
Sets the position of the horizontal shoot
      else
Jumping...
          n=n+(peek(p+x/4-10)>67)*bb*(g>0)
      endif
Adds points while jumping over an obstacle
      poke $A190,x
      poke $A191,x
Updates horizontal position of the buggy
      if j
Jumping?
        y=yy(j)
Next height
        sound 1,100-3*y,10,3*(y>0)
Jumping sound
        dec j
Decrease jumping step
      elif n
        s=s+n
        n=0
Points to add to score?
        nt$=str$(s)
Converts number to digits
        position 25-len(nt$),0
        print nt$
      endif
Align score right (besides a static "0")
      pause 0
      move d,$A000,50
      move $A180,hh,4
      move $A190,$D000,8
Refresh the screen
      move $B348,$B338,112
Scrolls the vertical missile up
      mset $B64C,3,u2*u1
Draws a missile if it was just dropped
      u2=0
Resets dropping flag
      if f
Firing?
        dec f
Decreases firing counter
        a=u=0
Still not hitting a rock?
        mset $B3A4,4,32*(f=6)
Puts a vertical shoot in the first step
        sound 2,9+f,10,f*a
Firing sound FX, shuts up automatically
        z=z+6
Next step of shoots
        poke $A197,z*a*(f>0)
      endif
Sets position of the horizontal shoot
      move w+31,$B5B3-y,15
      move w+44+13*t,$B4B3-y,15
Updates the buggy
      -move $B648,$B64C,117
Scrolls down the missile
      if peek($D004)>1 or peek($D00E)
Crashed?
        move w+93,$B4B8-y,8
Displays crash bitmap
        k=1
Sets killed flag
        n=0
      endif
Discards new points for score
      a=p+(z+g)/4-10
Shot hit a rock?
Computes the absolute position of the shoot on the ground
      b=peek(a)=73
      c=peek(a+1)=73
Is there a big rock?
      if f*(b+c) and not u
Found a rock?
        dpoke a+c,65
Removes the rock
        u=1
Sets hit-a-rock flag
        o=9
Enables explosion sound
        n=n+3*bb
      endif
Scores for the hit
      c2(2)=i
Changes color of mines
      if o
Explosion sound effect
        dec o
        sound 3,80-o*5,0,o
      endif
Plays a step of the FX on each loop
    until p>$A950+u1*30 or k
End of stage?
    sound
Shuts up!
    v=v-k
Lost a life?
    b=k=0
    if b
Survived the stage?
      inc r
Increase stage counter
      poke $BC65,r+$A0
Updates the POINT stage
      v=v+(v<5)
    endif
Recovers a life (5 max)
    poke $BC5E,v+$D0
Prints current lives
    for a=0 to 31 step 1+k
      sound 1,200-a*b*6,8+2*b,7-a/4
      pause 0
    next
Plays a tune (buggy explosion or end of stage)
  until r=26 or not v
Game is over when there are no more lives or all stages were completed.
  if v
Displays end of game message
    print "{binary data}"
  else
Completed game
    print "{binary data}"
  endif
Game over
  while strig(0)
  wend
Wait for trigger to restart
loop

Return to my 10-liners page.

© 2020 by Víctor Parada - 2020-04-09 (updated: 2021-04-10)