Coordinator

Copyright © 2023 by Víctor Parada

Coordinator

This is a little game for the 2023 NOMAM's BASIC 10-liners Contest. This program fits in the "stock" PUR-80 category, and it was written in Atari BASIC for the 8-bits ATARI XL/XE. Development started on 2023-02-08, and it took 2+2 days. The final version's date is 2023-03-16.

UPDATE: It obtained the 8th place of 37 entries in the category.


Description

Help the little elf to find the eleven potions needed to freeze the lava and escape alive.


Instructions

COORD start Lava is surrounding the elf and you must help him to survive. He starts in the center of the area.
COORD move Use both paddles to guide the elf to the shining potion. One paddle makes the elf to move upwards or downwards, the other paddle makes him to move to the left or to the right. The elf moves one step at a time, so don't turn the knobs too much because the elf might fall directly into the lava.
COORD lava Every time the elf gets a potion, the floor will break a little and more lava will appear.
COORD fail The elf has limited time to get the potion. He fails if he runs out of time or if he touch the lava during the journey. Every time he fails, the journey starts again automatically.
COORD win When the elf gets all the potions, the lava will cool down and be harmless. Then, the journey is complete. Press any button to start the journey again.
COORD both This game could be played by one player using both paddles at the same time, or by two players, each of them with one paddle.

Development of the game

Some weeks ago, I had to renew my driving license. It is required to approve reflex, coordination, vision and hearing tests. The coordination test requires you to move a needle through a path over a 2D surface using a kind of big scissiors using both hands and, to increase difficulty, you have to complete it in a given amount of seconds. It is not a secret that I like paddle games, but using two paddles at the same time is not usual and I thought about a game with the same dynamics using both of them: one for the horizontal movement and the other for the vertical one. Instead of a single path, the game should provide increasing difficulty with more complex paths as the levels are completed.

drivetest

Coordination tests for drivers.

Technically, paddles provide a value that could be read by the program that represents an angle of the knob, within a range between 0 and 228. Using that, I wrote a simple proof of concept for this game in Atari BASIC, trying to fit the PUR-80 category. The first prototype had no path but an open field with a starting point and a destination. I decided to use graphics mode 5+16 (ANTIC mode 10) for the playfield and double scan lines resolution for the player and objects. That provides a resolution of 80x48 in 3 colors plus the background for the playfield and P/M with square pixels. To manage the P/M in Atari BASIC, I used the string manipulation trick I developed some years ago. To display timers and score I turned one line at the top into an ANTIC mode 6 text line.

proto1

Proof of concept.

But I realized that at the start of the game, the paddles could be in any position and not pointing to the starting spot. Also, if the knob is quickly moved, two consecutive reads of the paddle register could give very different values, and the player is not moved but teletransported to the new position, avoiding traps and walls. Those two issues made me rethink about how to move the player: instead of moving the player itself, you move a pointer that is followed by the player, one pixel at a time.

For simplicity, I didn't add the pointer using another P/M graphics (and to save space in the source code), and it was fun to see that if the sprite was placed in the middle of the screen, it automatically moves to that invisible point smoothly in the Altirra emulator. That was enough to force the player to move that invisible pointer to the start point.

At that moment, I had redefined the game, and instead of to follow a path, the sprite must avoid walls. The walls would appear one by one each time the sprite reach the destination. So the starting spot was turned in the initial destination as the starting point was the middle of the screen, and the first and empty playfield turned into stage 0 with a special treatment in order to allow the player to get used to the paddles' sensitivity. The destination spot is moved between two different locations in the playfield on each new stage, and the player must go to one side and then return using a longer path and few more time.

In order to draw the walls, I thought about using DATA statements spammed in the code to fill empty spaces, but all that coordinates would use too much space and I had to pack it somehow. A small unpacking subroutine over a binary string of data was enough. 10 levels required 30 bytes plus 12 bytes to draw the surrounding walls for the initial stage. Two of the bytes are the initial coordinate and the destination coordinate of a wall, each one composed has two nibbles which represent few positions in a 7x5 matrix. The third byte represents the delay used to decrease the timer during the stage.

proto2

Testing the full path.

For speed reasons and the difficulty on reducing the number of lines from 11 to 10 even when there was many free space at the end of the lines, I had to remove the score and the game over message, so the text line previously inserted at the top was removed. Doing that allowed me to add a tick sound for the timer, a color rotation for the target and a loop to wait for any trigger to restart the game after it was completed.

When the game was almost ready, I tried it in a real 800XL using real paddles instead of the use of Altirra with a mouse emulating both paddles. The problem was that my paddles were very jittery, and the sprite was very unstable. Anyway, it was fun to see how the controls worked. The game happened to be very difficult, and I needed a lot of practice to fine tune timer parameters (the 3rd data byte) for each stage and complete the challenge.

proto3

Ready for final testing on real machines.

Testing in my 800XL using jittery paddles.


Download and try

Get the COORD.ATR file and set it as drive 1 in a real Atari (or emulator). Turn on the computer and the game should start after loading. Don't press OPTION key as internal BASIC is required. A set of paddles in port 1 is also required.

A note for Altirra users: This game might not work as expected in emulators because the paddle emulation is done by the mouse device in its default mode, which it's inverted on one of the axis. To be able to play this game in Altirra, you have to add a custom input map called "Paddle A+B" with the following controllers in it:


The code

The abbreviated BASIC code is the following:

The full and expanded BASIC listing is:


Coordinator
(c) 2023 Víctor Parada
0
LINE 0: INITIALIZATION
dim b$(512),f$(32768-adr(b$)),c$(128),d$(128)
Reserves memory for game elements data, forcing PMBASE to be at page 128 ($80)
b$="{binary data}"
b$(512)=b$
b$(2)=b$
c$=b$
d$=b$
Clears P/M area
d$(99)="{binary data}"
Target bitmap
a=adr("{binary data}")
Wall coordinates (From and To) and Time for each stage.
Stage 0 includes playfield limits
graphics 21
Set screen to graphics mode 5 (ANTIC 10/$A) without text window
poke 54279,128
PMBASE
poke 559,46
SDMCTL=2+32+8+4: Double line resolution P/M in standard playfield
poke 53277,3
GRACTL=2+1: Enable players and missiles
poke 704,15
Color of the player
u=53252
PF Collision vector for P0
v=53260
Pn Collision vector for P0
r=0
Initialize round
s=0
Set target side
l=0
Data pointer
h=4
Initial number of walls to draw (playfield borders)
3
LINE 3: NEXT STAGE
for k=1 to h
Draws stage wall(s)
  gosub 9
  plot i,j
Gets initial coordinates
  gosub 9
  drawto i,j
Gets destination coordinates
  gosub 9
next k
Gets delay for timer
h=1
Forces only one new wall for every next stage
4
LINE 4: INITIAL STAGE LOOP
poke 53249,56+137*s
color 2
plot 0,0
drawto 79,0
Draws the timer bar at the top of the screen
q=0
Initializes the global counter
p=79
Sets the timer
if r=0 then
c$=b$
x=132
y=64
Resets player position
5
LINE 5: GAME LOOP
poke 53278,0
Clears collisions
q=(q+1)*(q<n)
o=q=0
p=p-o
Computes game counters and flags
sound 0,20+p,10,o*4
Plays a tick for the timer
x=x+sgn(240-paddle(0)-x)
y=y+sgn(paddle(1)-26-y)
poke 53248,x
c$(y)="{binary data}"
Moves player
color 3
Color of spent time
plot p,0
Removes a block of time
poke 705,q*8+50
Rotates the target color
7
LINE 7: GAME OVER CHECK
sound 0,0,0,0
Shuts off the tick of the timer
if peek(u)>0 or p=0 then
Check for a hit into a wall
on r=0 goto 4
Repeats if initial stage
for k=0 to 160
  sound 0,k,8,8-k/20
next k
Crash FX on upper stages
run
Restarts
8
LINE 8: TARGET CHECK AND WIN
on peek(v)=0 goto 5
Reached to the destination?
r=r+1
Next stage
s=1-s
Changes the side of the target
on r<11 goto 3
End of the game?
d$=b$
Game Over.
Removes the taget
poke 708,8
Changes PF color
on stick(0)=15 goto 8
Waits for a trigger...
run
and restarts
9
LINE 9: GET DATA SUBROUTINE
n=peek(a+l)
m=int(n/16)
i=m*13.2
j=(n-m*16)*11.3+2
Gets next byte of data
N=byte value
M=high nibble
I=Horizontal coordinate in the playfield
J=Vertical coordinate in the playfield
l=l+1
Updates data pointer
color 1
Sets drawing color to PF1
poke 77,0
return
Disables attract mode

Return to my 10-liners page.

© 2023 by Víctor Parada - 2023-02-18 (updated: 2023-04-03)