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] ) ); } } } }