Robbo

Play Robbo online without sound effects

Story

When I was 3 years old my parents bought a personal computer Atari 130 XE. Games were stored on a magnetic tapes, and it was quite a challenge to load them. There was an extension cartridge covered with a duct tape, which had a button that should be pressed while the game is loading. And the loading process took some time. Minutes. Probability of loading was ~50%. Anyway it was a magic machine where I learned how to write programs on the Basic programming language.

Robbo was my favorite game. It was interactive, colorful, and all resources were precisely measured. If you have 10 bullets — you should spend them wisely. Some parts of game were so dynamic that sometimes I fell off the chair while an enemy was chasing me.

This game was written by a polish developer Janus Pelc, and released in 1989 year. I could not understand how such a game was made and tried to code something similar. In my first attempts I've just waited for user input and draw a new game state after it. It was obvious that this could not be a right way, because it would take too many combinations and transitions between them. But I could not make anything more complex because everything that I knew were: PRINT, INPUT, IF, GOTO, and poke for changing color. And it was not modern if, that IF could only jump to some other numbered line.

«Upgrade» magazine

Next big iteration happened in my ninth grade of school. There was a weekly magazine that wrote about computer's hardware and software called Upgrade. One day they've decided to make a monthly release with a compact-disk! And they've asked readers to send them some software. I've already partially developed a new implementation using Visual Basic on my AMD-K6 II 500 PC, so I've painted a bunch of sprites in their firm-style and send it. And they've actually released it on a CD! How happy I was. It was before the era of CD-R\W drives. After few months my hard drive crashed, and all the code was gone. But I have some printed parts of it which I've debugged on the paper a lot. Algorithm of NPC moving along the wall by right-hand rule took literally 8 A4 pages. It was time of Dial-up internet.

I've downloaded that old sprites from the web.archive project, so you can see what it looked like. You can see a flying Mustdie and the Black Support

In the 11 grade I've made a new implementation. Code became much more simple — I've generalized a lot of behaviors.

Normal implementation

During all that years I've played my favorite childhood game using the Atari800Win emulator. Eight years have passed, I started to work as a programmer, learned patterns, fell in love with functional programming style, and with js. It took two evenings, the code became shorter. Then refactoring took few months (I've decided to publish the code on GitHub, and to write an article about it on a public resource habrahabr).

GnuRobbo Some time before the last reimplementation I've found an OpenSource project GnuRobbo. They've implemented a clone of Robbo using C++ language. It is even available in the Android Store. I've never looked in their source code, thinking that it would be a kind of cheating. But one day I've tried to look how they've implemented bomb explosions and found out that there are a lot of tiny places that works differently from the original implementation. I've made too much frame-by-frame screenshots of the game in the emulator and tried to implement everything as close to the original as it was possible.

Every game with some world is building around the infinite loop where we collect the player's input and draw the current world state. I tried to update only the changed parts of the screen.

While decomposing all objects behaviours I understood that they share a lot of common patterns. For example: bomb, store, some guns, starship can be pushed by a player if there is a space on the other side.

What's inside?

Game code is written with MVC pattern in the mind. But I've decided to not overcomplify, and moved model into the controller. So main modules are: Controller, View, SpriteManager, KeyboardHandler, and a unique file for each game object type. Each GameObject is a small controller that is capable only of managing it own state.

View is initializing game UI, canvases, tracks sprites update, scrolls the game field.

SpriteManager hold logics of drawing an object at exact position.

Controller holds the game map and methods for operating with it:

From the beginning I understood that iterating over all cells and check if the object is alive was not an optimal way, so I've implemented a list of Active Objects. In each world physics step only their actions are processed. This cause some additional logics for removing and adding objects to this list.

At this point everything looked smooth until I start to compare everything with the original game frame by frame. There were a lot of small nuances that I did not mention before: for example, if Robbo is moving a stone to the left and there is a up-down bird on the way — he would die when the bird would move down, but would not if he moves stone to the right. I would show:

Fixed this by sorting action objects in the original order.

Example of a game object Door:

( function( R ) {
  'use strict';
  R.objects.Door = {
    // you can explode the door with a bomb
    explodable: true,
    // eat() would be called on interaction
    eatable: true,
    eat: function( eater ) {
      // check the amount of keys
      if( eater.keys > 0 ) {
        // subtract one key (and update UI)
        eater.set( 'keys', eater.keys - 1 );
        // destroy the door
        this.game.setCell( this, 'Empty' );
        // play door open sounf
        this.game.playSound( 'door_default' )
      }
      
// if this function returns false - `eater`
// would not be switched with the door
      
// engine would think that attempt to eat failed
// in case of a door - it destroys itself
// we just open the door,
// and do not actually `eat` it

      return false;
    }
  };
} )( window.R );

This was easy to implement a lot of simple objects, but bomb, explosion, bullet, lava, and teleport caused some struggles.

Teleportation

Teleports can transfer Robbo from one part of the world to the other. In the enter point Robbo dematerializes and in the out point — materializes back. If Robbo enters from the left — he would go out to the right.

But what if the exit is blocked? Or all exits are be blocked. Or exit teleport could be destroyed during the teleportation process.

Teleports can be connected not only in pairs, but also in triplets (actually any amount of teleports can be linked). In my implementation teleport can be connected to any point in space (even if there is no teleport from the other side).

I've tested all such cases in the emulator and wrote down the result. I've managed to write a formula of checking available cells without conditions. On each step I XOR the current direction with 11,10,11,10 (binary), and get direction where I should check cell for Emptiness. If all four cells are blocked — then Robbo returns to the teleportation start.

Explosion is an art

Bomb is a dangerous object! You should be careful when playing with it. Any bullet that collides with the bomb would cause a detonation!

So, what is complex? In original robbo bombs explosion really looked like an explosion. I've built long chains of bomb and looked how they collide. When I tried to reproduce that behavior — I captured a lot of videos and marked the state of explosion mist frame by frame. Two days I've been observing that numbers and got insane in my attempts to reproduce that sequence. That time I even tried to look in the GnuRobbo implementation, and found out that they just didn't give a shit, so I was on my own. Original bomb explosion have 5 steps. At the beginning the explosion intensifies and then gets weaker. It does not happen evenly, but some pattern appears. On the GnuRobbo forum people discussed the difference, and they assumed that originally it uses some kind of random.

After a while I figured out two explosion matrix. The first one applies to cells around the bomb at the moment of detonation. On the first step the bomb is still exists, and we can see it. On the second step the bomb disappears and the second matrix is applied. When matrix applies to the field it sets the state of explosion animation to some number. If that cell already contains an explosion then new value is added to an old, but no more than 5.

First matrix:

0 0 0
0 0 2
5 4 5

Second matrix:

5 4 5
4 3 2
0 0 0

For a single bomb the result is identical to the original game, but for sequence it still differs.

Each explosion is really an object that decrements its animation state on each step, and when animation step equals -1 — the callback is called. Generic callback can place another object in place of the explosion. Actually there are a lot of explosions in the game: the fog from a bullet, object destruction, bomb explosion, and teleportation are build atop of explosion.

Lava

Lava is an only thing that does not work as a single-cell object. It consumes everything on its way except walls. I tried to implement it as other objects with cell logic, but it was much easier to move the whole line of lava in one step. So the most-left item is making all logic and disable other cells to the right for the current game step.

Graphics

I didn't want to redraw the whole game field on each step because it may cause performance issues (on old PCs). The game holds previous sprites states, and stores all happened updates in an UpdateArray.

Tried to get all sprites from the GnuRobbo, but realized that they have upscaled them, partially redrawn, so decided to capture sprites from the emulator. Also, GnuRobbo didn't implement ~80% of different types of walls from the original game. So I understood that I choose the right way of doing it.

Animations also were not properly implemented in GnuRobbo. After a while I captured a lot of videos from the emulator and checked what's happening step by step. For example, when Robbo makes a step — he should move to the next cell in the original state, and only on the next cell sprite should be changed. It caused adding a new SpriteAnimationStep in the middle of WorldStep. In that step no moving logics are happening, but only sprite change logics.

Sprite Palette

In the middle of development I've noticed that each level have its own sprites colors.

A lot of old games used this solution, because they were limited in video memory, but wanted to implement some visual diversity at low cost.

Each level now contains a list of palette colors. All sprites actually have only 5 colors. One for background and four for images. On the level loading spriteSheet is getting colored in the level palette.

I've implemented the coloring process in a crazy way and made a separate project that it doing it. I do not color the image pixel by pixel, instead of it — I am using Indexed Palette PNG format. I split the png into sections, update only palette part and build it back, after that the image is getting immediately colored. It even works in IE7.

Updates

May 2023: there was final animation when you beat the original game. Recreated it frame-by-frame.

GitHub link