Copyright © 2022 by Víctor Parada
This is a little game for the 2022 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 2019-10-03, and it took 1+2+5 days. The final version's date is 2022-03-25.
UPDATE: It obtained the 8th place of 38 entries in the category.
You are a bubble in a spiky world, but other bubbles want to chase you. Guide them to the green spikes.
You are the orange bubble at the right. Two purple bubbles will try to chase you. | |
Use the joystick to move the bubble between the spikes. No diagonal moves are allowed. Everything kills you, do not touch anything! | |
The purple bubbles know that the blue spikes kill them, so they don't move through them, but they can do diagonals. | |
Guide the other bubbles to the green spikes, because they are not aware about their power. | |
When all the other bubbles are blown up, a new spiky field will appear, this time with more mad bubbles. | |
You win when you complete 5 rounds, and a time-based score will be displayed. If you get zero points, it took you too much time to complete all rounds. | |
If you die, the game is over and you will get zero points. Press the joystick button to try again. |
When I was starting in tenliners, some of the previous tenliners caught my eye. One of them was Cemetry Chase by Yoda Zhang, where an evil ghost tried to catch yow while you try to collect gold. Some years later, it came into my mind the idea of a game with the same dynamics, but with many enemies behaving like a swarm of bees, where you must hide behind walls and let them crash into them, as in Berzerk but without shooting.
I wrote a proof of concept of that game in Atari BASIC, trying to fit the PUR-80 category. The result was a working prototype, but the game dynamics was a bit confusing and, after all, it was not as fun and interesting as I thought, so I discarded it.
Cemetry Chase
Prototype with moving enemies
Later, I saw another game with the same dynamics: a port of Zombies from the VIC-20. I found it more confusing than mine, and you could hide behind a protected spot and all the enemies would eventually die trying to reach you. Frustrating and, again, not much fun.
Some weeks ago, an entry for 2022 contest catched my attention: Zombie Apocalypse Simulator by Craig Miller. I was shocked, because it combined the same dynamic of the following enemies with a pushing objects mission. I found it great, because Craig was also able to fit the same PUR-80 category I was trying.
VIC Zombies port
Zombie Apocalypse Simulator tenliner
While commenting about it in the AtariAge forum, I searched for my prototype and shared a video of it, where a couple of comments made me reconsider it as a project. I sat down and reviewed the code trying to remember what was done: it was a mess! Thankfully it was simple to understand and it took me a couple of hours to make it fit into 10 lines. An interesting fact was that the prototype didn't use any of the memory management instructions (PEEK and POKE) as it is usual in my games, just plain BASIC (COLOR, PLOT, DRAWTO and LOCATE statements).
I faced some problems: it was as boring as always and there was not enough coding space to improve the playability. It was time to begin with the optimization of the algorithm in order to find out how much space was available to improve the graphics, to add sounds, and, the most important, to make it fun and challenging.
I took one of the highlighted features that it was recognized from my video: walls dissapear when enemies hit them, so you are not able to find a spot where to hide and let the enemies to die in a row. To enhance this idea, I added a different type of walls. It could act as a target where you must guide the enemies, and there would be just one for each enemy.
Even when the game logic used only 4 lines, plus 1 for the flow control and 1 for the game over, the initialization of every round required a lot of space: 4 lines. An extra line was required for the DIM of the required arrays. Total: 11 lines. Some more optimizations and simplification could be done to save space, but every time I did it, I wanted to make a shift to improve playability. After some juggling, I had an interesting game concept. I could add nice sounds, but I had to manage the game graphics using the standard charset and default colors. Yes, I decided to sacrifice the bitmaps in order to have a challenging game, and to keep it clean of PEEKs and POKEs (and I also omitted the POKE to disable the ATRACT mode!).
The game was challenging, but it was because the player moved too quick and it was easy to crash. I decided to add more walls in the playfield and to reduce the speed of the player to the half, increasing the playability. More walls means that the player has to be more careful when he moves, but it also means that the enemies can get stuck behind them, and the player has to go for them. I also reduced the playfield size, leaving an empty line to display the final score or the game over message in it, which were previously written over the bottom border.
Prototype with the new rules
Final prototype
While I was writing this page, I decided to change the name of the game and the characters I used for the playfield, to create a new theme. This project started as "Leave me alone!" and finished as "Spiky World". I also swapped the sides of the enemies and traps to make the game more challenging.
1st version of the game
I didn't submit the game to the contest because I was not satisfied. I didn't want the game to use ATASCII fonts, I wanted bitmaps. After two weeks and with a fresh mind, I added a simple routine to set up a modified charset and to measure how much space it would require: about 130 bytes or a line and a half. That was the number of bytes I had to remove from the current version to be able to use the new fonts. In that version, 4 lines were for the round setup, 5 lines for the game logic and 1 line for the game over routine, so I tried to compress each of those sections at least on half a line.
The problem was that many lines were the destination of a GOTO, so those cannot be joined or a destination could not be in the middle of a line. I had to rewite the game routine to adjust it to less lines. I also had to rewrite the round setup. At the end, the game over message and sound FX had to be removed to keep it shorter than half a line and combined with the game routine, and the round setup had to be simplified to be merged with the charset setup.
When I could fit the whole game in 10 lines, I tried some different bitmaps to make it attactive, update this page and submit the game to the contest just before the deadline!!!
1st prototype with bitmaps
Get the SPIKY.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 joystick in port 1 is also required.
The abbreviated BASIC code is the following:
The full and expanded BASIC listing is:
|
Spiky World (c) 2022 Victor Parada 2022-03-25 |
|
Screen element codes for COLOR and LOCATE - 32: Blank - 33: Player - 34: Died player - 3: Trap - 129: Enemy - 163: Wall |
0 |
LINE 0: MAIN/ROUND LOOP (Level initialization on the firts run) |
graphics 17 |
20x24 4-color text mode |
poke 756,144 |
Enables custom charset |
h=33 |
|
trap 2 |
Forces the skip of some instructions following a DIM when running for 2nd time |
dim x(9),y(9),z(9) |
Allocates arrays on the first run - X(): Horizontal position of every enemy - Y(): Vertical position of every enemy - Z(): Availability of every enemy |
a=adr("{binary data}") |
|
n=1 |
Starts round 1 with 2 enemies |
t=5E3 |
Sets initial value for countdown timer (score) at 5000 |
g=32 |
|
for b=0 to 79 poke 36992+b,peek(57472+b) poke 36872+b,peek(a+b) next b |
Copies the digit bitmaps and the sprite bitmaps to the cutom charset |
2 |
LINE 2: Initialization (executed once per run) |
a=17 |
Initial horizontal position for the player |
u=163 v=u color u |
Initializes spikes color to draw the playfield |
plot 0,0 drawto 0,22 drawto 19,22 drawto 19,0 drawto 0,0 |
Draws first wall |
l=4 |
Forces to run the following code through the whole playfield drawing spikes |
for k=0 to 1 |
When K=0 - Puts spikes in the playfield - Initializes Z() as "not available" for all enemies When K=1 - Puts enemies in the right side of the playfield and mark them as "active" - Puts traps in the left side of the playfield |
for i=0 to l |
Puts spikes in the whole plaifield (K=0 and L=4) or enemies and traps (K=1 and L=round number) |
b=11 |
Initial vertical position for the player |
for j=0 to 1 |
Two enemies in the same column, one in the top half and one in the bottom half |
e=i+j*5 |
Arrays' index |
d=int(rnd(0)*9)+2+10*j |
Selects a random vertical position in the top half (J=0) or bottom half (J=1) |
c=9+i+i-4*k |
Sets the horizontal position in the right half of the playfield every second column |
x(e)=c y(e)=d z(e)=k |
Saves the position of the enemy (valid when K=1) |
color u plot c,d |
Draws an enemy in the selected position (K=1) or spike (K=0) |
color v plot c-8+5*k,22-d |
When K=1, draws a trap in the left side of the playfield, vertically mirroring the enemies, otherwise puts a spike |
next j |
End of top/bottom loop |
next i |
End of left to right loop |
l=n-1 |
Sets the limit of enemies to the round number for K=1 |
u=129 v=3 |
Sets enemy and trap elements for K=1 |
next k |
End of spikes/enemies+trap loop |
color h plot a,b |
Draws the player at a fixed position at the right |
f=1 |
Flag to say that there are enemies in the screen |
m=n+n |
Counter for remaining enemies/traps in the round (two times the round number) |
5 |
LINE 5: GAME LOOP |
t=t-(t>0) |
Decreases the timer (until zero) |
j=stick(0)*p |
Reads the joystick every two loops (to slow down the player) |
c=a+(j=7)-(j=11) d=b+(j=13)-(j=14) |
Compute next player position, diagonals are not allowed |
locate c,d,l |
Checks for the contents of the new player position |
e=l=h |
Stayed? |
color g+e |
Prepares to remove the player if it moved |
6 |
LINE 6: Move the player |
k=l=g or e |
Keep playing if moved to a space or stayed |
plot a,b |
Removes the player if it moved |
color 34-k plot c,d |
Puts the player in the new/same position |
a=c b=d |
Updates player position |
i=(i+1)*(i<9) |
Iterates the enemies counter |
u=x(i) v=y(i) |
Gets the position of current enemy |
goto 9-z(i)*k*2 |
If the current enemy is not available or the player didn't do a good move, skips the whole routine to move the enemy. |
7 |
LINE 7: Enemy calculations |
c=u+sgn(a-u) d=v+sgn(b-v) |
Computes next position for current enemy, moving towards the player, including diagonals |
locate c,d,l |
Checks for the contents of the enemy new position |
e=l<99 |
Not blocked by a spike or another enemy |
r=l<>3 |
Avoided a trap? |
sound 0,80+i,8+r+r,4*e |
Plays a sound when moving, makes a noise if fell into a trap |
goto 9-e |
Skips the move of the current enemy if it is blocked my |
8 |
LINE 8: Move an enemy |
k=l<>h |
(not) Hit the player? |
color g plot u,v |
Removes the enemy from the old position |
color 34*r+g*k+63*r*k plot c,d |
Draws the enemy at the new position or remove a trap |
z(i)=r |
Updates availability of the enemy. |
m=m-1+r |
|
x(i)=c y(i)=d |
Updates the position of the enemy |
f=m>0 |
Repeat loop if there are enemies |
9 |
LINE 9: Flow controlL |
sound 0,0,0,0 |
Stops sounds |
n=n+1-f |
Increase the next round number |
p=1-p |
Delay flag for the player. Without this, the movement is too sensitive |
on n<6 and k goto 5*f |
Are there more rounds and still alive? |
position 8,23 print #6;t*k |
Displays the score only if all the rounds were completed, zero when lost or run out of time |
on strig(0) goto 9 |
Remains in the game over routine until the button is pressed |
run |
Restarts |
Return to my 10-liners page.
© 2022 by Víctor Parada - 2022-03-01 (updated: 2022-04-09)