Click here to Skip to main content
15,891,905 members
Articles / Web Development / CSS

Tom's Halls - A JavaScript Platform Game Engine

Rate me:
Please Sign up or sign in to vote.
4.91/5 (12 votes)
8 Apr 2010CPOL8 min read 79.3K   671   41  
A 2D platform game engine using JavaScript DOM manipulation and CSS
// Tom's Halls game
// Tom W Hall 2007 - 2008
// tomshalls@gmail.com

// Global variables
var gViewport;
var gPlayer;
var gLoopInterval;
var gLoopIntervalMs = 15;

// Force / direction constants
var forceNeutral = 0;
var forceLeft = 1;
var forceRight = 2;
var forceUp = 3;
var forceDown = 4;

addEvent(window, 'load', initialSetUp);

doPreLoadImages('images/grey-flower-tile.png', 'images/divider.png', 'images/loading.png');

preLoadImages();

// Initializes game
function initialSetUp()
{
	gConfig = new Config();
	gViewport = new Viewport('viewport');
	gPlayer = new Player(gViewport);
	gViewport.addPlayer(gPlayer);

	updateScreen();
	updateLives();
	updateManuscriptPages();
	
	addEvent(window.document, 'keypress', keyPress);
	addEvent(window.document, 'keydown', keyPress);
	addEvent(window.document, 'keyup', keyUp);	
	addEvent(document.getElementById('game-info-text'), 'click', gameInfoTextClick);
}

// Updates screen's Javascript reference etc
function updateScreen()
{
	clearInterval(gLoopInterval);

	gViewport.clear();

	var head = document.getElementsByTagName('head')[0];
	
	var script = document.getElementById('screen-script');
	if (script)
	{
		head.removeChild(script);
	}

	script = document.createElement('script');
	script.id = 'screen-script';
	script.setAttribute('type', 'text/javascript');
	script.setAttribute('src', 'js/screens/screen_' + gPlayer.screenX + '_' + gPlayer.screenY + '.js');
	head.appendChild(script);

	gPlayer.screenEntryLeft = gPlayer.left;
	gPlayer.screenEntryTop = gPlayer.top;
	
	gPlayer.updateDomElement();

	gViewport.domElement.focus();
}

// Main game loop
function loop()
{
	if (gPlayer.lostLife)
	{
		updateLives();
		gPlayer.lostLife = false;
	}
	else if (gPlayer.lostLifeCountdown > 0)
	{
		gPlayer.deathFade();
	}
	else
	{
		gViewport.updateSprites();
		gPlayer.applyPhysics();

		if (gPlayer.gotManuscriptPage)
		{
			updateManuscriptPages();
			gPlayer.gotManuscriptPage = false;
		}
	}
}

// Adds event listener to object
function addEvent(obj, evt, func)
{ 
	if (obj.addEventListener)
	{ 
		obj.addEventListener(evt, func, false); 
		return true; 
	}
	else if (obj.attachEvent)
	{ 
		return obj.attachEvent("on" + evt, func); 
	}
	else
	{
		obj[evt] = func;
	}
}

// Handles key down
function keyPress(e)
{
	var keyDownID = window.event ? event.keyCode : (e.keyCode != 0 ? e.keyCode : e.which);
	switch(keyDownID)
	{
		case 37:
			gPlayer.tryMoveLeft();
			break;
		
		case 39:
			gPlayer.tryMoveRight();
			break;
			
		case 38:
			gPlayer.tryMoveUp();
			break;
		
		case 40:
			gPlayer.tryMoveDown();
			break;
			
		case 32:
			gPlayer.tryJump();
			break;
	
		case 112:
			if (gPlayer.speedY < 0)
			{
				gPlayer.invincible = true;
			}
			break;
	}
}

// Handles key up
function keyUp(e)
{
	var keyDownID = window.event ? event.keyCode : (e.keyCode != 0 ? e.keyCode : e.which);
	switch(keyDownID)
	{
		case 37:
			gPlayer.endTryMoveLeft();
			break;
		
		case 39:
			gPlayer.endTryMoveRight();
			break;
			
		case 38:
			gPlayer.endTryMoveUp();
			break;
		
		case 40:
			gPlayer.endTryMoveDown();
			break;
	}
}

// Toggles display of the game info panel
function gameInfoTextClick()
{
	var gameInfo = document.getElementById('game-info');
	if (gameInfo.style.display == 'block')
	{
		gameInfo.style.display = 'none';
		gLoopInterval = setInterval('loop()', gLoopIntervalMs);
	}
	else
	{
		gameInfo.style.display = 'block';
		clearInterval(gLoopInterval);
	}
}

// Updates the current screen name
function updateScreenName(screenName)
{
	updateInnerText('screen-name-text', screenName);
}

// Updates the Manuscript Pages box
function updateManuscriptPages()
{
	updateInnerText('manuscript-pages', gPlayer.manuscriptPages);
}

// Updates the player lives box
function updateLives()
{
	if (gPlayer.lives < 1)
	{
		gPlayer.screenX = 100;
		gPlayer.screenY = 200;
		gPlayer.left = 352;
		gPlayer.top = 264;
		gPlayer.domClass = 'player-left';
		updateScreen();
	}
	
	var lives = document.getElementById('lives');
	while (lives.hasChildNodes())
	{
		lives.removeChild(lives.firstChild);
	}

	var numLives = gPlayer.lives;
	if (numLives > 0)
	{
		var i = numLives;
		do
		{
			var life = document.createElement('div');
			life.className = 'player-left';
			life.style.width = '32px';
			life.style.height = '64px';
			lives.appendChild(life);  
		}
		while (--i);
	}
}

// Updates the inner text of the supplied DOM element with the supplied text
function updateInnerText(elementId, text)
{
	var el = document.getElementById(elementId);
	
	while (el.hasChildNodes())
	{
		el.removeChild(el.firstChild);
	}
	
	var textNode = document.createTextNode(text);
	el.appendChild(textNode);
}

function preLoadImages()
{
	doPreLoadImages(
		'images/ascii-asterisc.gif',
		'images/ascii-hash.gif',
		'images/ascii-plus.gif',
		'images/black-bg.gif',
		'images/fire.gif',
		'images/bookshelf.jpg',	
		'images/border.jpg',
		'images/cdshelf.jpg',		
		'images/earth.jpg',
		'images/earth-bg.jpg',
		'images/email.jpg',
		'images/forest.jpg',
		'images/game-info.jpg',
		'images/grass.jpg',
		'images/hell.jpg',
		'images/lives.jpg',
		'images/logo.jpg',
		'images/manuscript-pages.jpg',
		'images/parchment.jpg',
		'images/plot.jpg',
		'images/saloon.jpg',
		'images/sand.jpg',
		'images/sky.jpg',
		'images/technical-background.jpg',
		'images/tom.jpg',
		'images/wood.jpg',
		'images/wood-pale.jpg',
		'images/bathtub-left.png',
		'images/bathtub-right.png',
		'images/bed.png',
		'images/bricks.png',
		'images/bricks-bg.png',
		'images/bricks-bg-dark.png',
		'images/ceiling.png',
		'images/crazy-robert-left.png',
		'images/crazy-robert-right.png',
		'images/desk.png',
		'images/devil-left.png',
		'images/devil-right.png',
		'images/gravy-jug-left.png',
		'images/gravy-jug-right.png',
		'images/gunslinger-left.png',
		'images/gunslinger-right.png',
		'images/hifi-rack.png',
		'images/ladder.png',
		'images/log.png',
		'images/manuscript.png',
		'images/ozymandias.png',
		'images/plant-pot.png',
		'images/skull-left.png',
		'images/skull-right.png',
		'images/player-left.png',
		'images/player-right.png',
		'images/player-updown.png',
		'images/plot.png',
		'images/speaker-left.png',
		'images/speaker-right.png',
		'images/spider.png',
		'images/spider-silk.png',
		'images/statue.png',
		'images/stodgeson-left.png',
		'images/stodgeson-right.png',
		'images/stove.png',
		'images/sunflower.png',
		'images/sunflower-stalk.png',
		'images/table-leg.png',
		'images/tile-blue.png',
		'images/tile-copper.png',
		'images/tile-green.png',
		'images/tile-grey.png',
		'images/tile-red.png',
		'images/toilet-left.png',
		'images/toilet-right.png',
		'images/tomato.png',
		'images/wallpaper.png'
	);
}

function doPreLoadImages()
{
	var images = new Array();

	var i = 0;
	do
	{
		var image = new Image();
		image.src = doPreLoadImages.arguments[i];
		images.push(image);
		i++;
	}
	while (i < arguments.length)
}

function Config()
{
	this.startScreenX = 101;
	this.startScreenY = 101;
	this.left = 50;
	this.top = 448;
	this.domClass = 'player-right';
	this.invincible = false;
}

// SCREEN CREATION METHODS ----------

function addElement(id, domClass, left, top, width, height)
{
	gViewport.addElement(new Element(id, domClass, left, top, width, height, gViewport));
}

function addSteps(topX, topY, width, height, stepHeight, domClass, isSolid)
{
	var stepWidth = Math.round(width / (height / stepHeight));
	var x = (width >= 0) ? topX : topX - Math.abs(stepWidth);
	var y = topY;
	do
	{
		if (isSolid)
		{
			if (width >= 0)
			{
				gViewport.addElement(new Element(null, domClass, topX, y, (x - topX) + stepWidth, stepHeight, gViewport));
			}
			else
			{
				gViewport.addElement(new Element(null, domClass, x, y, topX - x, stepHeight, gViewport));
			}
		}
		else
		{
			gViewport.addElement(new Element(null, domClass, x, y, Math.abs(stepWidth), stepHeight, gViewport));
		}

		x += stepWidth;
		y += stepHeight;
	}
	while (y < topY + height);
}

function addHorizontalMonster(id, left, top, width, height, leftLimit, rightLimit, force, movementIncrement, leftDomClass, rightDomClass)
{
	gViewport.addElement(new HorizontalMonster(id, left, top, width, height, leftLimit, rightLimit, force, movementIncrement, leftDomClass, rightDomClass, gViewport));
}

function addVerticalMonster(id, left, top, width, height, topLimit, bottomLimit, force, movementIncrement, upDomClass, downDomClass)
{
	gViewport.addElement(new VerticalMonster(id, left, top, width, height, topLimit, bottomLimit, force, movementIncrement, upDomClass, downDomClass, gViewport));
}

function addSpider(id, left, top, topLimit, bottomLimit, force, movementIncrement)
{
	gViewport.addSpider(new Spider(id, left, top, topLimit, bottomLimit, force, movementIncrement, gViewport));
}

function addSunflower(id, left, top, topLimit, bottomLimit, force, movementIncrement)
{
	gViewport.addSunflower(new Sunflower(id, left, top, topLimit, bottomLimit, force, movementIncrement, gViewport));
}

function addElevator(id, domClass, left, top, width, height, topLimit, bottomLimit, force, movementIncrement)
{
	gViewport.addElement(new Elevator(id, domClass, left, top, width, height, topLimit, bottomLimit, force, movementIncrement, gViewport));
}

function addZoomevator(id, domClass, left, top, width, height, leftLimit, rightLimit, force, movementIncrement)
{
	gViewport.addElement(new Zoomevator(id, domClass, left, top, width, height, leftLimit, rightLimit, force, movementIncrement, gViewport));
}

function addManuscriptPage(id, left, top)
{
	var manuscriptPg = null;
	if (gViewport.manuscriptPages.length > 0)
	{
		var i = gViewport.manuscriptPages.length -1;
		do
		{
			var mp = gViewport.manuscriptPages[i];
			if (mp.id == id)
			{
				manuscriptPg = mp;
				break;
			}
		}
		while (i--);
	}

	if (manuscriptPg == null)
	{
		manuscriptPg = new ManuscriptPage(id, left, top, gViewport);
		gViewport.addManuscriptPage(manuscriptPg);
	}
	
	if (manuscriptPg.collected == false)
	{
		gViewport.addElement(manuscriptPg);
		manuscriptPg.addDomElement();
	}
}

// ---------- SCREEN CREATION METHODS

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
New Zealand New Zealand
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions