Javascript Bomberman 9524.dec 2010
    Here is my attempt to rewrite bomberman 95 game into javascript language. At first I wanted to learn the possibilities of HTML5+canvas element, but then I found out that canvas element is too slow to achieve at least 10 fps performance. This is not a fully working game, just a scratch done in two nights of programming. I tried to code everything in javascript OOP without any ballast in html code. The code is quite clear and easy to understand. The funny part is decoding the ".sch" file that can be found in original Bomberman 95 game.


Javascript source code
var ctx;
var nToLoad = 4;
var map = [];
var player = {x:0, y:0, dstx:0, dsty:0, 
  src:"res/player.gif", offsetX:0, offsetY:-42, keyUp:38, 
  keyLeft:37, keyRight:39, keyDown:40, keyDrop:32, bombs:3, fire:4};
var game;
var keys = [];

function Debug(src)
{
  this.element = document.createElement('img');
  this.element.style.position = "absolute";
  this.element.src = src;
  game.appendChild(this.element);
}

Debug.prototype.set = function(x, y) 
{  
  this.element.style.left = getxy(x, y).x;
  this.element.style.top = getxy(x, y).y;
}

// Bomb {{{
var Bomb = function(player)
{
  this.x = Math.floor( player.x + 0.5 );
  this.y = Math.floor( player.y + 0.5 );
  this.fire = player.fire;
  var time = 3;
  var timerId;
  var _this = this;
  var _player = player;
  this.blocking = _player;

  this.Start = function()
  {
    //alert("stimer");
    timerId = setInterval(
      function()
      { 
        _this.Tick();
      }, 500);
  };

  this.Tick = function()
  {
    if ( _this.blocking && ( Math.floor(_player.x + 0.5) != this.x || Math.floor(_player.y + 0.5) != this.y ) )
    {
      _this.blocking = false;
    }

    if ( time-- == 0 )
      this.Explode();
    else
      SetBlock(this.x, this.y, "res/bomb_"+time+".gif");
  };

  this.Explode = function() 
  {
    clearInterval( timerId );
    map[this.y][this.x] = false;
    SetBlock(this.x, this.y);
    new Explosion(this);
    _player.bombs++;
  }

  if ( !map[this.y][this.x] && player.bombs > 0 )
  {
    player.bombs--;
    map[this.y][this.x] = this;
    SetBlock(this.x, this.y, "res/bomb_"+time+".gif");
    _this.Start();
  }
};

var Explosion = function(bomb)
{
  this.len = bomb.fire;

  this.Scan = function(bx, by, dx, dy, r)
  {
    if ( map[by][bx] == false )
    {
      map[by][bx] = new Fire(bx, by);
    } else
    if ( map[by][bx].constructor == Fire )
    {
      // DOROBIT !
//      map[by][bx].Destroy(); 
    } else
    if ( map[by][bx].constructor == Bomb )
    {
      map[by][bx].Explode();
    } else
    if ( map[by][bx].constructor == Food )
    {
      map[by][bx].Destroy();
      map[by][bx] = new Fire(bx, by); // neni az tak pravda, prepali aj dve naraz potom
      return;
    } else
      return;

    if ( --r > 0 )
      this.Scan(bx+dx, by+dy, dx, dy, r);
  }
  this.Scan(bomb.x, bomb.y, +1, 0, this.len);
  this.Scan(bomb.x-1, bomb.y, -1, 0, this.len-1);
  this.Scan(bomb.x, bomb.y-1, 0, -1, this.len-1);
  this.Scan(bomb.x, bomb.y+1, 0, +1, this.len-1);
};

var Fire = function(x, y)
{
  this.x = x;
  this.y = y;
  var _this = this;
  SetBlock(x, y, "res/fire.gif");
  var _timer = setTimeout( function() { _this.Done(); }, 200 );
  this.Done = function()
  {
    SetBlock(this.x, this.y);
    map[this.y][this.x] = false;
  }
  this.Destroy = function()
  {
    clearTimeout(_timer); // toto funguje !?
    _this.Done();
  }
};

var Food = function(x, y)
{
  var _x = x;
  var _y = y;
  SetBlock(x, y, "res/food.gif");
  this.Destroy = function()
  {
    map[_y][_x] = false;
    SetBlock(_x, _y);
  }
};

var Wall = function(x, y)
{
  SetBlock(x, y, "res/wall.png");
};
// }}} Bomb

document.onkeydown = function(event)
{
  keys[event.keyCode] = 1;
}

document.onkeyup = function(event)
{
  keys[event.keyCode] = 0;
}

player.Setup = function(x, y)
{
  this.x = this.dstx = x;
  this.y = this.dsty = y;

  this.element = document.createElement('img');
  this.element.src = this.src;
  this.element.style.position = "absolute";
  game.appendChild( this.element );
}

player.Move = function()
{
  var curx = this.x;
  var cury = this.y;
  var alix = Math.floor(curx+0.5);
  var aliy = Math.floor(cury+0.5);

  debugrect.set(alix, aliy);
 
  var mX = keys[this.keyLeft] || keys[this.keyRight];
  var mY = keys[this.keyUp] || keys[this.keyDown];
  this.dstx = (!mX && mY) ? alix : curx;
  this.dsty = (!mY && mX) ? aliy : cury;

  var dx=0, dy=0;
  if ( keys[this.keyLeft] ) dx = -1;
  if ( keys[this.keyRight] ) dx = 1;
  if ( keys[this.keyUp] ) dy = -1;
  if ( keys[this.keyDown] ) dy = 1;
  if ( keys[this.keyDrop] ) new Bomb(this); 

  this.dstx += dx;
  this.dsty += dy;

  debugdot.set(Math.floor(this.dstx+0.5), Math.floor(this.dsty+0.5));

  var newx = curx;
  var newy = cury;
  var step = 0.02;

  if ( this.dstx > curx ) 
    newx += step;
  if ( this.dstx < curx ) 
    newx -= step;

  if ( this.dsty > cury ) 
    newy += step;
  if ( this.dsty < cury ) 
    newy -= step;

  if ( !checkCollisionBlock( newx, newy, this ) )
  {
    if ( checkCollisionBlock( newx, cury, this ) )
      newy = cury;
    else
    if ( checkCollisionBlock( curx, newy, this ) )
      newx = curx;
    else
    {
      newx = curx;
      newy = cury;
    }
  }
  this.x = newx;
  this.y = newy;
}

function SetBlock(x, y, item)
{
  var id = "i" + x.toString() + "_" + y.toString();
  var block = document.getElementById( id );
  if ( typeof(block) == "undefined")
    return;
  if (item)
    block.src = item;

  block.style.display = item ? "" : "none";
}
 
window.onload = function()
{
 // <div id="game" style="position:absolute; width:640px; height:480px; background:url(res/field0.png)"></div>
  game = document.createElement("div");
  game.style.position = "absolute";
  game.style.width = 640;
  game.style.height = 480;
  game.style.background = "url(res/Field0.png)";
  document.body.appendChild(game);

  for (y=0; y<11; y++)
    for (x=0; x<15; x++)
    {
      var block = document.createElement('img');
      block.id = "i" + x.toString() + "_" + y.toString();
      var pos = getxy(x, y);
      block.style.position = "absolute";
      block.style.left = pos.x;
      block.style.top = pos.y;
      block.style.display = "none";
      block.src = "";
      block.width = 40;
      block.height = 36;
      game.appendChild( block );
    }

  debugdot = new Debug("res/debug.gif");
  debugrect = new Debug("res/debug2.gif");
  LoadScheme();
  Run();
}

function LoadScheme()
{
  var client = new XMLHttpRequest();
  client.open('GET', 'res/PINGPONG.SCH');
  client.onreadystatechange = function() {
    if(this.readyState == 4)
    {
      ParseScheme(client.responseText.split("\n"));
      Run();
    }
  }
  client.send();
}

function Run()
{
  setInterval("Update()", 10);
}

function checkCollision(x, y, p)
{
  if ( map[Math.floor(y)][Math.floor(x)] == false )
    return true;
  if ( p && map[Math.floor(y)][Math.floor(x)].blocking == p )
    return true;
  return false;
}

function checkCollisionBlock(x, y, p)
{
  return checkCollision(x+0.2, y+0.2, p) &&
         checkCollision(x+0.8, y+0.2, p) &&
         checkCollision(x+0.8, y+0.8, p) &&
         checkCollision(x+0.2, y+0.8, p);
}

function Update()
{
  player.Move();
  player.element.style.left = getxy(player.x, player.y).x + player.offsetX;
  player.element.style.top = getxy(player.x, player.y).y + player.offsetY;
}           

function getxy(col, row)
{
  return {x:col * 40 + 20, y:row * 36 + 64};
}

function ParseScheme(f)
{
  for (var i=0; i<f.length; i++)
    if ( f[i].charAt(0) == "-" )
    {
      var params = f[i].substr(1).split(",");
      if ( params[0] == "R" )
      {
        var row = parseInt( params[1] );
        map[row] = [];
        for (var col=0; col<15; col++)
        {
          var item = false;
          switch ( params[2].charAt(col) )
          {
            case '.': break;
            case ':': item = new Wall(col, row); break;
            case '#': item = new Food(col, row); break;
          }
          map[row][col] = item;
        }
      }

      if ( params[0] == "S" )
      {
        if ( params[1] == "0" )
        {
          player.Setup( parseInt( params[2] ), parseInt( params[3] ) );
        }
      }
    }
}