Intruder Alert!

Copyright © 2025 by Víctor Parada

Pac-10Liner

This is a little game for the 2025 NOMAM's BASIC 10-liners Contest. This program fits in the EXTREM-256 category, and it was written using FastBasic 4.7 for the 8-bits ATARI XL/XE computers line. Development started on 2024-10-31, and it took 3+5+3 days. The final version's date is 2025-03-26.


Description

You are an intruder humanoid that must escape from the maze destroying all the guardian robots.


Instructions

INTRUDER start Press the button to start.
INTRUDER move Use the joystick to move around the maze and reach any of the room exits. There are robots trying to chase and destroy you.
INTRUDER fire Use the trigger to shoot to any robot in your way out. You will score 10 points for each destroyed robot. You can perform only one shot at a time. Beware! Robots can dodge shots towards their legs.
INTRUDER kill If there are no more robots in a room when you leave it, you will receive a bonus of 100 points. But more robots will chase you in the next room.
INTRUDER boss If you stay in a room for too much time, the evil smiling boss will came to chase you. He can't be killed.
INTRUDER shock Don't touch the electrified walls, or you will die just like as a robot chased you.
INTRUDER end You have three lives. The game is over when you loose all of them. Press the trigger to play again.

Development of the game

In the search for classic games that had not been ported to a tenliner, I found Berzerk. I thought that it could be really simple to code it and gave it a try. My idea was to use both a modified character set and player/missile sprites for static and moving robots respectively.

My first attempt was to use the sprites from the Atari 2600 version of the game, because the robots could fit ANTIC 7 chars (GR.2) and it could very easy to animate their tracking eye. But I realize there was a problem: it could be hard to draw the maze using that screen resolution, because I should have to manage all 15 posible combinations of wall junctions for random mazes.

Original Berzerk

Original Berzerk arcade game.

2600 port

Port of Berzerk for Atari 2600 console.

Trying to solve that, I changed the graphics mode to ANTIC 4 (GR.12), using the whole character as a block for the walls. PLOT and DRAWTO could be used to draw the maze. But, each robot required a tile of 4 chars, and each scan line should be doubled, using more bytes for the redefined charset in the source and wasting some valuable bytes for coding. The good thing is that the tiles for the robots can be placed half the way, giving a less grid-like look of the playfield.

In three days I had a playable prototype of the game in FastBasic, with some animations for the player and the robots. I could replicate the maze generation algorithm, but I did not use a seed for random numbers. Instead, I used the random number generator from the POKEY chip through the RAND() function.

INTRUDER prototype

Test using tiles in ANTIC 4 mode.

INTRUDER prototype

Robots move smoothly towards the player.

Then, the headache started: I faced a bug while testing, but I couldn't find where in the code it was, because it appeared few times during a lot of hours playing and tracing and debugging. After a couple of days I got frustrated and suspended the project. A couple of weeks later, with a fresh mind, I gave it a new try, added more tracing code but, again, I couldn't find what was going on and dropped the project.

INTRUDER prototype

Tracing to find the bug.

Some months later, it was opened the call for the 2025 tenliners contest and I had no fresh ideas. I reviewed some of the many dropped prototype games I've written through the years and this came in front of them. I played it a few times, removed many of the trace code and sat down to think. I was sure there was a border condition that triggered the bug, and it could be related to the hardware registers for collision detection and when the events happens during the screen refresh. If this was true, I could probably be detecting a bit late a collision of the bullet with a robot that it is no longer there. To solve this without adding some extra PAUSE statements to force a screen refresh with the cost of a slower game, I might change the whole game logic.

With less than 3 weeks for the deadline, I decided to rewrite the game logic. It was required to add many new variables to precompute and hold temporary data in order to perform many actions quickly and get the full screen refreshed in just one frame. This was good and bad at the same time: The bug disappeared, but the cost was less coding space because most of the required extra variables were of two letters. I did some statistics of variable usage and reassigned them in order to keep the most used ones as a single letter variable. Anyway, the coding space was almost exhausted and many of the pending features should be discarded and/or some existing features removed to make room for the most important ones.

INTRUDER prototype

New code and new color palette.

I added some sound FX, defined the final color palette, restricted and/or released game dynamics and made them all fit the 10 lines of code. After some days, I was satisfied with the result, even when I couldn't include some of the features I wanted to. The game was hard enough in order to make it even harder!

While cleanning and commenting the code for this page, I decided to change the name of the game. Instead of "Berzerk", during a night call with friends from the retro community, I told them that the game I was working on would be called "Krezreb", and it remained in the source code until it was finished. But one of the features I couldn't include was a background sound routine to mimic the "Intruder alert, intruder alert! The humanoid must not escape!" digitized speechs from the original game. Then I thought about a new game name based on those speechs, and some options were "Intruder" and "Intruder humanoid", but "Intruder alert!" was selected.


Download and try

Get the INTRUDER.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:


Intruder Alert! - KREZREB - BERZERK
(c) 2024-2025 Victor Parada G.
o=adr("{binary data}")+14
Binary data
Outlined player (13 bytes)
Frozen player (right) (13-1 bytes)
Walking player (right) (13-1 bytes)
Frozen player (left) (13-1 bytes)
Walking player (left) (13-1 bytes)
Walking robot bitmap (19 bytes)
Color palette (9-2 bytes)
Otto bitmap (16 bytes)
Gates locations (4 bytes)
Border walls locations (12 bytes)
Maze walls locations (8 bytes)
Otto jumping height (10 bytes)
Animation of standby robots eyes (40 bytes)
Horizontal coordinates of entry points (4 bytes)
Vertical coordinates of entry points (4 bytes)
Robot bitmaps (32 bytes)
Playfield blocks (16 bytes)
graphics 28
Graphics mode 12+16 (40x24 in ANTIC mode 4)
pmgraphics 2
Double line resolution P/M graphics
move o+67,704,9
Set color palette
dpoke dpeek(560)+27,7
Enable score line by changing bottom two lines by one ANTIC 7 line and a blank scan line
move $E000,$8000,$400
move o+172,$8008,48
poke 756,$80
Redefine charset
dim r(9),p(800),oh(9) byte,re(19),
  wx(3) byte,wy(3) byte
R(): Position of each robot
P(): Lookup table; tells which robot is at a given screen position (-1=none)
OH(): Otto bouncing height
RE(): Sequence of bitmaps for the animation of the robot's eyes
WX() and WY(): Coordinates of the 4 entry points of the playfield for the Player and Otto
move o+114,adr(oh),58
Populate array tables
s=dpeek(88)+42
Address of Playfield
o1=o+74
Pointer to Otto's bitmap
position 3,22
print #6,"INTRUDER ALERT"
Print title
data _d()=0,7,0,-7
Drawing direction for walls
X=Up,Right,Down,Left Y=Left,Down,Right,Up
do
Main loop
  while strig(0)
  wend
Wait for the trigger to start the game
  w=3
Initial position of the player
  m=1
Number of robots
  li=3
Number of lives
  sc=0
Score
  position 3,22
  print #6,3 rtab(17) sc
Print initial lives and score
  repeat
Game loop
    poke 77,0
Disable ATRACT mode
    sound
Turn off every sound
    mset pmadr(-1),$280,0
Clear all P/M bitmaps
    mset dpeek(88),880,0
Clear the playfield
    mset adr(p),1602,$FF
Clear the robots lookup table
    t=0
    for q=0 to 3
Draw the opened and closed paths
      color 37+129*(w<>q)
      exec _m peek(o+90+q)
    next q
Use a different color for closed gate
    color 37
    for n=0 to 19
      sound 0,n,2,n/2
Draw the walls, first 12 segments are surrounding walls (fixed), last 8 are center ones (random direction)
      if n>11 then t=rand(4)
Select a random direction
      exec _m peek(o+94+n)
    next n
    sound
Draw a wall segment
    n=0
    while n<m
Add M robots to the playfield
      c=rand(15)
Select a zone
      if (c-(c>7)) mod 3<2
Check that it is not one of the gate zones
        c=40*(rand(5)+c/5*7)+rand(5)+c mod 5*7
Select a random place inside the selected zone
        if dpeek(c+s)+dpeek(c+s+40)=0
Check that there is space for a robot tile
          exec _r c,1
Add a robot
          r(n)=c
Save position of the robot
          p(c)=n
Add the robot to the lookup table
          inc n
        endif
      endif
    wend
Increment the number of robots
    t=0
Global timer (0-9)
    g=0
Moving robot steps counter
    x=wx(w)
id of the robot that is moving
Horizontal coord of player
    y=wy(w)
Vertical coord of player
    u=0
Horizontal coord of moving robot
    v=0
Vertical coord of moving robot
    h=0
Robot's moving axis (0=horizontal 1=vertical)
    i=0
Robot's moving direction (0=no robot is moving)
    f=0
Fire flag
    dx=1-2*(w=1)
Horizontal speed of player when moving
    dy=0
Vertical speed of player when moving
    gx=0
Horizontal movement of the shot
    gy=0
Vertical movement of the shot
    d=0
Horizontal position of the shot
    e=0
Vertical position of the shot
    l=0
Current/next action of the moving robot
    oc=500
Back-counter for Otto to appear
    k=0
Shooting sound FX counter
    z=0
Robot explosion FX counter
    repeat
Round loop
      t=(t+1) mod 10
Update global timer
      dpoke $800A,re(t)
      dpoke $801A,re(t+10)
Rotate robot's eyes
      j=stick(0)
Move the Player?
      if j<15
If the joystick was moved...
        sound 0,j,8,2
Walking sound
        dx=(j&8=0)-(j&4=0)
        dy=(j&2=0)-(j&1=0)
        x=x+dx
        y=y+dy
Compute new deltas and player coordinates
        sound 0
      endif
Walking sound (step 2)
      if oc
Evil Otto
        dec oc
Update coundown
        if oc=0
Release Otto?
          ox=wx(w)
          oy=wy(w)
        endif
Set staring point of Otto
      elif t&1
Otto is active, but moves every two frames
        ox=ox+sgn(x-ox)
        oy=oy+sgn(y-oy)
Compute new psosition towards the palyer
        sound 2,200,12,9-t
      endif
Boing sound FX
      if g
        dec g
        if h
Is there a walking robot?
          v=v+i
        else
Vertical
          u=u+i
        endif
Horizontal
        if g=0
Reached the robot its destination?
          p(r(q))=-1
Remove the robot from the old location pointer
          r(q)=r(q)+i+39*i*h
          p(r(q))=q
Set Tile at new position
          if rand(3) then exec _w
Continue moving?
          if g=0 then l=3
        endif
No direction? Remove the P/M of the robot on the next frame
      elif l=0 and n
Select a robot to move
        q=rand(n)
Select a robot
        u=r(q) mod 40*4+56
        v=r(q)/40*4+20
Compute P/M coordinates of that robot
        exec _w
Select a direction to move the robot
        if g then l=1
      endif
If the selected direction is free, enable the movement
      if z
        dec z
        sound 1,200-4*z,6,z
      endif
Perform explosion sound FX
      if f
Shooting?
        sound 1,40-3*k,6,k
        k=k-(k>0)
Shooting sound FX might not last the whole path of the bullet
        d=d+gx
        e=e+gy
Update position of the shot
      else
The shot is not active
        if strig(0)=0
Is the trigger pressed?
          gx=2*dx
          gy=2*dy
Same direction than the player but a double speed
          d=(x+7-6*(dx<0))&$FE
          e=(y+5)&$FE
Fire in even position
          if gx
Bitmap of the projectile
            fz=3
          else
Horizontal Bitmap (also valid on diagonals)
            fz=$202
          endif
Verical bitmap
          k=9
Enable shooting sound FX
          f=1
        endif
      endif
Shooting is now active

Precompute element positions
      p1=o+12*(j<15)*(t&1)+24*(dx<0)
      p2=pmadr(0)+y-1
Player
      o2=pmadr(2)+oy-oh(t)
Otto
      b1=pmadr(-1)+e
      b2=b1-gy
Missile
      r2=pmadr(1)+v-1
      r3=o+48+9*t&1
Robot
      pause
Wait for the next frame
      poke $D01E,0
Reset hit registers

Move elements in screen
      pmhpos 0,x
      move p1,p2,13
Draw player
      if f
        pmhpos 4,d
        dpoke b2,0
        dpoke b1,fz
      endif
Draw the missile

Draw a walking robot
      if l=1
        exec _b
        exec _r r(q),0
        l=2
Convert tile into P/M
      elif l=2
        exec _b
Move P/M robot
      elif l=3
        exec _r r(q),1
        mset r2,10,0
        l=0
      endif
Convert P/M robot back to a tile
      if oc=0
        move o1,o2,16
        pmhpos 2,ox
      endif
Draw Evil Otto
      pause
Wait for the frame to be displayed
      fk=peek($D000)+peek($D008)&2*8
      if fk
Did our shot hit something?
        sound 1
Shut up the shooting FX
        dpoke b1,0
Remove shot from screen
        if fk&$10
Hit a walking robot?
          l=0
Disable robot animation
          g=0
No robots are walking now
          mset r2,10,0
Remove bitmap of the robot
          c=q
Last walking robot
        elif fk&4
Hit a standby robot?
          a=(d-56)/4+(e-20)/4*40
Find contact byte of the tile
          b=a-peek(adr("{binary data}")+peek(s+a))
Adjust to upper left byte
          c=p(b)
Identify the robot number
          exec _r r(c),0
          p(b)=-1
        endif
Remove robot
        if fk&$14
          p(r(c))=-1
Hit a robot?
          if n>1 and c<n-1
            r(c)=r(n-1)
            p(r(c))=c
If the removed robot is not the last in the list, move the last one to fill the gap
            if q=n-1 then q=c
          endif
If the moving robot was the last of the list, update its position in the list
          dec n
One robot less
          exec _s 1
Increase the score
          z=12
Enable explosions sound FX
          if n=0 then oc=oc/4
        endif
If there are no more robots, force Otto to appear earlier
        f=0
      endif
Disable shot
      ss=peek($D004)+peek($D00C)*16
    until ss
Check for player collision
    if ss&$F7
Killed?
      for a=0 to 14
        sound 0,9,2,a
        move o-13*a&1,pmadr(0)+y-1,
          13
        pause 
        sound
        pause
      next a
Animate the electric shock of the player
      mset pmadr(0)+y,13,0
Remove the player bitmap from screen
      dec li
Decrease lives
      position 3,22
      print #6,li
Print new number of lives
    else
Got a pathway
      w=(x>wx(1))*3+(y<wy(0))*2+(x<wx(3))
Select the next entry point
      m=m+(m<10)
Increase the number of robots for the next room
      if n=0 then exec _s 10
    endif
Bonus for killing all the robots in the current room
  until li=0
Game ends when there are no more lives
loop
Restart game
proc _s a
  sc=sc+a
  position 5,22
  print #6,rtab(16) sc
endproc
Increase and print the score. There previously is a fixed trailing zero on screen.
proc _m c
Draws a wall segment
C bits:
- 7: unused (0)
- 6-5: direction
- 4-0: 24 spots of the playfield (6x4)
  a=c&31 mod 6*7+2
Horizontal coord of starting point
  b=c&31/6*7
Vertical coord of starting point
  c=c/32+t
  plot a,b
  drawto a+_d(c),b+_d(3-c)
endproc
Direction to draw a segment
proc _w
Check if current robot can move towards the player
  a=x-u+1
  b=y-v-2
Compute deltas to the player
  if rand(5)=0 or rand(3) and abs(a)>abs(b)
Select (most of the time) the axis where the robot is far from the player
    h=0
    i=sgn(a)
    a=s+r(q)+i+i*(i>0)
Horizontal axis
    if peek(a)!peek(a+40)=0 then g=4
  else
Nothing is blocking, prepare to move
    h=1
    i=sgn(b)
Vertical axis
    if dpeek(s+r(q)+40*(i+i*(i>0)))=0 then g=4
  endif
endproc
Nothing is blocking, prepare to move
proc _r a b
  dpoke s+a,b*$301
  dpoke s+a+40,b*$402
endproc
Add or remove a robot tile
proc _b
  pmhpos 1,u
  move r3,r2,10
endproc
Draws a moving robot

Return to my 10-liners page.

© 2025 by Víctor Parada - 2025-03-26 (updated: 2025-03-30)