Thursday, February 21, 2019

reactorX - My 2019 BASIC 10 Liner

I've always been attracted to the strange arcade games. At the arcade in 1984, you might have found me wasting my quarters on Zaxxon. I didn't have to wait in line because no one else wanted to play it. But those isometric graphics lured me in. Gottlieb made some off beat games: Q-Bert, Mad Planets and Reactor. The sound on those machines is impressive and was authored by David Thiel.

I decided to create this year's 10 liner based on Reactor. I call it Reactor-X. It has three moving sprites plus the player's ship. Use your ship to bump the particles into the outer wall before the reactor core swells and goes into meltdown. Grab it over on my GitHub.
reactorX - 2019 BASIC 10 Liner Contest
The fast moving particles with complete multi-body collisions seen in this play-through were made possible using a new integer BASIC for the Atari called FastBasic. (I used Version 4.0.)

Instructions:
1. The opening screen shows the outer wall. Press the FIRE button to start.
2. You control the white +-shaped ship with your joystick. The three rogue atomic particles start on the other side of the core.
3. The particles do a random-walk while seeking out your ship. The bounce off of each other when they collide.
4. Gain points by hitting the particles with your ship.
5. The reactor core grows over time pushing your ship and the particles towards the deadly wall.
6. Use your ship to bump the particles towards the outer wall.
7. Be careful because the particles can bump you towards the wall, too! You have 5 lives.
8. Bump the particle into the wall to destroy it.
9. Two win, bump all three particles into the wall before the core swells too big!
Here's the compressed 10-line code:

Let's dig into the code:
data D() WORD = -1,1,2,2,1,-1,-2,-2,-1,1,2 Array to store info to draw an octagon. The drawing routine is at the bottom of the listing.
data x() WORD = 0,10,0,-10,0
data y() WORD = -20,20,20,20,0
Arrays to store X and Y positions of sprites with initial positions. Fifth index is the origin so I can reuse physics code.
dim alive(4), colsnX(4), colsnY(4) Arrays that store alive/dead state of PM's and changes in position due to collisions.
numkilled = 0
numlives = 5
score=0
rate = 15
counter = rate-1
Initialize some counters.
graphics 7
poke 752,1
Use 4 color 160x80 mode
Turn off cursor in text window
PM=$A000 mset $a000,1024,0
poke 54279,PM/256
poke 53277,3
poke 559,46
Clear out some RAM
 Tell ANTIC where PM RAM is' Enable PM display
Enable PM DMA with 2-line res
dpoke 704,$440F
dpoke 706,$4444
dpoke 709,$404F
Set colors for players 0 and 1,
players 2 and 3, and
the text window
x0=80
y0=40
color 1
radius = 19
exec octagon
Store the coordinates for the center of the screen.
Pick color 1, default is orange
Set the radius to draw the large octagon. Call the routine at the end of the listing.
?"Push FIRE" WHILE STRIG(0):WEND CLS Wait for player to start the game.
WHILE numkilled < 3 AND numlives> 0 This begins the main game loop
  alive(0) = 0 Reset the player is still alive flag.
  inc counter
  setcolor 1,counter,15
  if counter MOD rate = 0
    color 2
    radius = counter/rate
    exec octagon
  endif
Take care of the background graphics. Increment a counter to rotate the colors using the SETCOLOR command. Draw a larger octagon to enlarge the core every RATE game loop cycles. The EXEC OCTAGON jumps to the drawing routine at the end of the listing.
  poke 53248,x0+44+X(0)
  move adr(" -stuff-")+1, PM+$200 + y0 + 9 + Y(0), 17
Move player location. Poke the X coordinate and MOVE memory for the Y coordinate. The sprite is defined by the string, which starts at one byte higher than the address in FastBasic.
  for i=1 to 3
    move adr(" ball " ) + 1, PM + $200 + i*128 + y0 + 9 + Y(i), 14
    poke 53248+i,(x0+43+X(i))*(alive(i)=0)
  next i
Loop to move each  particle location. If particle is dead, keep it off the screen.
  poke 53278,1
  PAUSE 1
Clear collision register and paint the screen once to record collisions.
  for i=0 to 3 Loop through all the PM's
    colsnX(i) = 0
    colsnY(i) = 0
Initialize the collision accumulators...
    PiPF = peek(53252+i) Check the player-player collision register.
    if PiPF&1 = 1
      alive(i) = 1
Someone hit the wall! Set the alive/dead flag.
      if i = 0
        X(i) = 0
        Y(i) = -2*radius
        dec numlives
        mset PM+$200,128,0
        exit
It was the player's ship. Reset it's position and take away a life. Exit the collision detection loop.
      else
        X(i) = -43-x0
        inc numkilled
      endif
It was a particle. Kill it and remove it from the screen.
    elif PiPF&2 = 2
      freq = 80
      j=4
      exec bounce
    endif
Something hit the core. Make it bounce and get ready to play a tone.
    PiPj = peek(53260+i) Now check for player collisions. The 'i' value is subject player and 'j' value is player that was hit.
    if PiPj > 0 Act if there was a collision.
      if PiPj&1 = 1
        freq = 40
        j = 0
        score=score+5
The ship made a hit. Get some points and get ready to play a high-pitched tone.
      elif PiPj&2 = 2
        j = 1
      elif PiPj&4 = 4
        j = 2
      elif PiPj&8 = 8
        j = 3
      endif
Check for collisions with the particles.
      exec bounce Compute bounce position change.
    endif
  next i
Close the loop.
  if freq>0
    sound 0,freq,10,15
  else
    sound
  endif
  freq = 0
Play a tone or stop the tone, if needed.
  for i=1 to 3
    X(i)=X(i) - SGN((X(i)-X(0))/10) + colsnX(i) + RAND(3)-1 -(X(i)/33)
    Y(i)=Y(i) - SGN((Y(i)-Y(0))/10) + colsnY(i) + RAND(3)-1 -(Y(i)/33)
  next i
Update particle locations: attract to player, add in the bounce, do a little random walk, and stay away from the walls.
  P.657,2
  ? score, numlives;
Update the scoreboard. The POKE command positions the cursor before printing to the screen.
  S=STICK(0)
  LR=(S&4=4)-(S&8=8)
  UD=(S&1=1)-(S&2=2)
  Y(0) = Y(0) + colsnY(0) + UD+UD
  X(0) = X(0) + colsnX(0) + LR+LR
Get joystick input and move the player. Also add in the player bounce move.
WEND End of game loop.
SOUND Game over. Stop the sound.
if numkilled=3 and alive(0)=0 
  ?;"You Win!";
else
  ?;"/MELTDOWN\";
endif
Figure out the result.
do
loop
Wait forever.
proc bounce
  colsnX(i) = colsnX(i) + 2*SGN(X(i)-X(j))
  colsnY(i) = colsnY(i) + 2*SGN(Y(i)-Y(j))
endproc
Procedure to add in bounce motion. The motion is towards the ith particle and away from the jth particle. When j=4, the motion is away from the origin.
proc octagon
  plot x0-radius, y0 + 2*radius
  for p = 1 to 8
    drawto x0 + D(p)*radius, y0 + D(p+2)*radius
  next p
endproc
Procedure to draw an octagon. Loop through the vertices and draw lines. Scale the size by 'radius.'